diff --git a/README.md b/README.md new file mode 100644 index 0000000..834f94b --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +test-update-doctrine-command +============================ + +A Symfony project created on January 26, 2016, 1:51 pm. diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..fb1de45 --- /dev/null +++ b/app/.htaccess @@ -0,0 +1,7 @@ + + Require all denied + + + Order deny,allow + Deny from all + diff --git a/app/AppCache.php b/app/AppCache.php new file mode 100644 index 0000000..639ec2c --- /dev/null +++ b/app/AppCache.php @@ -0,0 +1,7 @@ +getEnvironment(), ['dev', 'test'], true)) { + $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); + $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); + $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); + $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); + } + + return $bundles; + } + + public function getRootDir() + { + return __DIR__; + } + + public function getCacheDir() + { + return dirname(__DIR__).'/var/cache/'.$this->getEnvironment(); + } + + public function getLogDir() + { + return dirname(__DIR__).'/var/logs'; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml'); + } +} diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig new file mode 100644 index 0000000..bafd28d --- /dev/null +++ b/app/Resources/views/base.html.twig @@ -0,0 +1,13 @@ + + + + + {% block title %}Welcome!{% endblock %} + {% block stylesheets %}{% endblock %} + + + + {% block body %}{% endblock %} + {% block javascripts %}{% endblock %} + + diff --git a/app/Resources/views/default/index.html.twig b/app/Resources/views/default/index.html.twig new file mode 100644 index 0000000..75842eb --- /dev/null +++ b/app/Resources/views/default/index.html.twig @@ -0,0 +1,76 @@ +{% extends 'base.html.twig' %} + +{% block body %} +
+
+
+

Welcome to Symfony {{ constant('Symfony\\Component\\HttpKernel\\Kernel::VERSION') }}

+
+ +
+

+ + + Your application is ready to start working on it at: + {{ base_dir }}/ +

+
+ + + +
+
+{% endblock %} + +{% block stylesheets %} + +{% endblock %} diff --git a/app/autoload.php b/app/autoload.php new file mode 100644 index 0000000..fa582ec --- /dev/null +++ b/app/autoload.php @@ -0,0 +1,13 @@ +getParameterOption(['--env', '-e'], getenv('SYMFONY_ENV') ?: 'dev'); +$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['--no-debug', '']) && $env !== 'prod'; + +if ($debug) { + Debug::enable(); +} + +$kernel = new AppKernel($env, $debug); +$application = new Application($kernel); +$application->run($input); diff --git a/bin/symfony_requirements b/bin/symfony_requirements new file mode 100755 index 0000000..1eca671 --- /dev/null +++ b/bin/symfony_requirements @@ -0,0 +1,143 @@ +#!/usr/bin/env php +getPhpIniConfigPath(); + +echo_title('Symfony2 Requirements Checker'); + +echo '> PHP is using the following php.ini file:'.PHP_EOL; +if ($iniPath) { + echo_style('green', ' '.$iniPath); +} else { + echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!'); +} + +echo PHP_EOL.PHP_EOL; + +echo '> Checking Symfony requirements:'.PHP_EOL.' '; + +$messages = array(); +foreach ($symfonyRequirements->getRequirements() as $req) { + /** @var $req Requirement */ + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('red', 'E'); + $messages['error'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +$checkPassed = empty($messages['error']); + +foreach ($symfonyRequirements->getRecommendations() as $req) { + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('yellow', 'W'); + $messages['warning'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +if ($checkPassed) { + echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects'); +} else { + echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects'); + + echo_title('Fix the following mandatory requirements', 'red'); + + foreach ($messages['error'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +if (!empty($messages['warning'])) { + echo_title('Optional recommendations to improve your setup', 'yellow'); + + foreach ($messages['warning'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +echo PHP_EOL; +echo_style('title', 'Note'); +echo ' The command console could use a different php.ini file'.PHP_EOL; +echo_style('title', '~~~~'); +echo ' than the one used with your web server. To be on the'.PHP_EOL; +echo ' safe side, please check the requirements from your web'.PHP_EOL; +echo ' server using the '; +echo_style('yellow', 'web/config.php'); +echo ' script.'.PHP_EOL; +echo PHP_EOL; + +exit($checkPassed ? 0 : 1); + +function get_error_message(Requirement $requirement, $lineSize) +{ + if ($requirement->isFulfilled()) { + return; + } + + $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; + $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; + + return $errorMessage; +} + +function echo_title($title, $style = null) +{ + $style = $style ?: 'title'; + + echo PHP_EOL; + echo_style($style, $title.PHP_EOL); + echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); + echo PHP_EOL; +} + +function echo_style($style, $message) +{ + // ANSI color codes + $styles = array( + 'reset' => "\033[0m", + 'red' => "\033[31m", + 'green' => "\033[32m", + 'yellow' => "\033[33m", + 'error' => "\033[37;41m", + 'success' => "\033[37;42m", + 'title' => "\033[34m", + ); + $supports = has_color_support(); + + echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); +} + +function echo_block($style, $title, $message) +{ + $message = ' '.trim($message).' '; + $width = strlen($message); + + echo PHP_EOL.PHP_EOL; + + echo_style($style, str_repeat(' ', $width).PHP_EOL); + echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_repeat(' ', $width).PHP_EOL); +} + +function has_color_support() +{ + static $support; + + if (null === $support) { + if (DIRECTORY_SEPARATOR == '\\') { + $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } else { + $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); + } + } + + return $support; +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0a5a7e5 --- /dev/null +++ b/composer.json @@ -0,0 +1,64 @@ +{ + "name": "senaria/test.update.doctrine.command", + "license": "proprietary", + "type": "project", + "autoload": { + "psr-4": { + "": "src/" + }, + "classmap": [ + "app/AppKernel.php", + "app/AppCache.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "require": { + "php": ">=5.5.9", + "symfony/symfony": "3.0.*", + "doctrine/orm": "^2.5", + "doctrine/doctrine-bundle": "^1.6", + "doctrine/doctrine-cache-bundle": "^1.2", + "symfony/swiftmailer-bundle": "^2.3", + "symfony/monolog-bundle": "^2.8", + "sensio/distribution-bundle": "^5.0", + "sensio/framework-extra-bundle": "^3.0.2", + "incenteev/composer-parameter-handler": "^2.0" + }, + "require-dev": { + "sensio/generator-bundle": "^3.0", + "symfony/phpunit-bridge": "^2.7" + }, + "scripts": { + "post-install-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" + ], + "post-update-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" + ] + }, + "extra": { + "symfony-app-dir": "app", + "symfony-bin-dir": "bin", + "symfony-var-dir": "var", + "symfony-web-dir": "web", + "symfony-tests-dir": "tests", + "symfony-assets-install": "relative", + "incenteev-parameters": { + "file": "app/config/parameters.yml" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..5a9600b --- /dev/null +++ b/composer.lock @@ -0,0 +1,1949 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "93501e777053cbbced9d2432e3970393", + "content-hash": "2719813e18d1d68c78504382e9e81fb8", + "packages": [ + { + "name": "doctrine/annotations", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2015-08-31 12:32:49" + }, + { + "name": "doctrine/cache", + "version": "v1.5.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/47cdc76ceb95cc591d9c79a36dc3794975b5d136", + "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": ">=3.7", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2015-12-19 05:03:47" + }, + { + "name": "doctrine/collections", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2015-04-14 22:21:58" + }, + { + "name": "doctrine/common", + "version": "v2.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2015-12-25 13:18:31" + }, + { + "name": "doctrine/dbal", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/2fbcea96eae34a53183377cdbb0b9bec33974648", + "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.7-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2015-12-25 16:28:24" + }, + { + "name": "doctrine/doctrine-bundle", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "c4ffef2b2296e9d0179eb0b5248e5ae25c9bba3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/c4ffef2b2296e9d0179eb0b5248e5ae25c9bba3b", + "reference": "c4ffef2b2296e9d0179eb0b5248e5ae25c9bba3b", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.3", + "doctrine/doctrine-cache-bundle": "~1.0", + "jdorn/sql-formatter": "~1.1", + "php": ">=5.3.2", + "symfony/console": "~2.3|~3.0", + "symfony/doctrine-bridge": "~2.2|~3.0", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "phpunit/phpunit": "~4", + "satooshi/php-coveralls": "~0.6.1", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0", + "twig/twig": "~1.10" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "symfony/web-profiler-bundle": "to use the data collector" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ], + "time": "2015-11-16 17:11:46" + }, + { + "name": "doctrine/doctrine-cache-bundle", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineCacheBundle.git", + "reference": "030ff41ef1db66370b36467086bfb817a661fe6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/030ff41ef1db66370b36467086bfb817a661fe6a", + "reference": "030ff41ef1db66370b36467086bfb817a661fe6a", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.4.2", + "doctrine/inflector": "~1.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": "~2.2|~3.0" + }, + "require-dev": { + "instaclick/coding-standard": "~1.1", + "instaclick/object-calisthenics-sniffs": "dev-master", + "instaclick/symfony2-coding-standard": "dev-remaster", + "phpunit/phpunit": "~4", + "satooshi/php-coveralls": "~0.6.1", + "squizlabs/php_codesniffer": "~1.5", + "symfony/console": "~2.2|~3.0", + "symfony/finder": "~2.2|~3.0", + "symfony/framework-bundle": "~2.2|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/security-acl": "~2.3|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0" + }, + "suggest": { + "symfony/security-acl": "For using this bundle to cache ACLs" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineCacheBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Fabio B. Silva", + "email": "fabio.bat.silva@gmail.com" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Bundle for Doctrine Cache", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2015-11-27 04:59:07" + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2015-11-06 14:35:42" + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09 13:34:57" + }, + { + "name": "doctrine/orm", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "d9fc5388f1aa1751a0e148e76b4569bd207338e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/d9fc5388f1aa1751a0e148e76b4569bd207338e9", + "reference": "d9fc5388f1aa1751a0e148e76b4569bd207338e9", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.4", + "doctrine/collections": "~1.2", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "ext-pdo": "*", + "php": ">=5.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "symfony/yaml": "~2.3|~3.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\ORM\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ], + "time": "2015-12-25 15:50:05" + }, + { + "name": "incenteev/composer-parameter-handler", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/Incenteev/ParameterHandler.git", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpspec/prophecy-phpunit": "~1.0", + "symfony/filesystem": "~2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Incenteev\\ParameterHandler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Composer script handling your ignored parameter file", + "homepage": "https://github.com/Incenteev/ParameterHandler", + "keywords": [ + "parameters management" + ], + "time": "2015-11-10 17:04:01" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "time": "2014-01-12 16:20:24" + }, + { + "name": "monolog/monolog", + "version": "1.17.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "raven/raven": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "swiftmailer/swiftmailer": "~5.3", + "videlalvaro/php-amqplib": "~2.4" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "raven/raven": "Allow sending log messages to a Sentry server", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.16.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2015-10-14 12:51:02" + }, + { + "name": "paragonie/random_compat", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "d762ee5b099a29044603cd4649851e81aa66cb47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/d762ee5b099a29044603cd4649851e81aa66cb47", + "reference": "d762ee5b099a29044603cd4649851e81aa66cb47", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2015-12-10 14:48:13" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "sensio/distribution-bundle", + "version": "v5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", + "reference": "419c1824af940e2be0f833aca2327e1181a6b503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/419c1824af940e2be0f833aca2327e1181a6b503", + "reference": "419c1824af940e2be0f833aca2327e1181a6b503", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "sensiolabs/security-checker": "~3.0", + "symfony/class-loader": "~2.3|~3.0", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/filesystem": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/process": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sensio\\Bundle\\DistributionBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Base bundle for Symfony Distributions", + "keywords": [ + "configuration", + "distribution" + ], + "time": "2015-12-18 17:44:11" + }, + { + "name": "sensio/framework-extra-bundle", + "version": "v3.0.12", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "3e8936fe13aa4086644977d334d8fcd275f50357" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/3e8936fe13aa4086644977d334d8fcd275f50357", + "reference": "3e8936fe13aa4086644977d334d8fcd275f50357", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.2", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "symfony/expression-language": "~2.4|~3.0", + "symfony/security-bundle": "~2.4|~3.0" + }, + "suggest": { + "symfony/expression-language": "", + "symfony/psr-http-message-bridge": "To use the PSR-7 converters", + "symfony/security-bundle": "" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sensio\\Bundle\\FrameworkExtraBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": [ + "annotations", + "controllers" + ], + "time": "2015-12-18 17:39:27" + }, + { + "name": "sensiolabs/security-checker", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/security-checker.git", + "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/21696b0daa731064c23cfb694c60a2584a7b6e93", + "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93", + "shasum": "" + }, + "require": { + "symfony/console": "~2.0|~3.0" + }, + "bin": [ + "security-checker" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-0": { + "SensioLabs\\Security": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "A security checker for your composer.lock", + "time": "2015-11-07 08:07:40" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1,<0.9.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "email", + "mail", + "mailer" + ], + "time": "2015-06-06 14:19:39" + }, + { + "name": "symfony/monolog-bundle", + "version": "v2.8.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "84785c4d44801c4dd82829fa2e1820cacfe2c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/84785c4d44801c4dd82829fa2e1820cacfe2c46f", + "reference": "84785c4d44801c4dd82829fa2e1820cacfe2c46f", + "shasum": "" + }, + "require": { + "monolog/monolog": "~1.8", + "php": ">=5.3.2", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/monolog-bridge": "~2.3|~3.0" + }, + "require-dev": { + "symfony/console": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony MonologBundle", + "homepage": "http://symfony.com", + "keywords": [ + "log", + "logging" + ], + "time": "2015-11-17 10:02:29" + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "2deb44160e1c886241c06602b12b98779f728177" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/2deb44160e1c886241c06602b12b98779f728177", + "reference": "2deb44160e1c886241c06602b12b98779f728177", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-04 20:28:58" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/49ff736bd5d41f45240cec77b44967d76e0c3d25", + "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-20 09:19:13" + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "e2e77609a9e2328eb370fbb0e0d8b2000ebb488f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/e2e77609a9e2328eb370fbb0e0d8b2000ebb488f", + "reference": "e2e77609a9e2328eb370fbb0e0d8b2000ebb488f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2015-12-18 15:10:25" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "7f7f3c9c2b9f17722e0cd64fdb4f957330c53146" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/7f7f3c9c2b9f17722e0cd64fdb4f957330c53146", + "reference": "7f7f3c9c2b9f17722e0cd64fdb4f957330c53146", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-04 20:28:58" + }, + { + "name": "symfony/polyfill-util", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "4271c55cbc0a77b2641f861b978123e46b3da969" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4271c55cbc0a77b2641f861b978123e46b3da969", + "reference": "4271c55cbc0a77b2641f861b978123e46b3da969", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2015-11-04 20:28:58" + }, + { + "name": "symfony/swiftmailer-bundle", + "version": "v2.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/swiftmailer-bundle.git", + "reference": "3d21ada19f23631f558ad6df653b168e35362e78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/3d21ada19f23631f558ad6df653b168e35362e78", + "reference": "3d21ada19f23631f558ad6df653b168e35362e78", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "swiftmailer/swiftmailer": ">=4.2.0,~5.0", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7|~3.0" + }, + "suggest": { + "psr/log": "Allows logging" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SwiftmailerBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony SwiftmailerBundle", + "homepage": "http://symfony.com", + "time": "2015-11-28 10:59:29" + }, + { + "name": "symfony/symfony", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/symfony.git", + "reference": "979d7323716fec847508eac3e62d59b117612a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/symfony/zipball/979d7323716fec847508eac3e62d59b117612a6e", + "reference": "979d7323716fec847508eac3e62d59b117612a6e", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.4", + "php": ">=5.5.9", + "psr/log": "~1.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0", + "twig/twig": "~1.23|~2.0" + }, + "conflict": { + "phpdocumentor/reflection": "<1.0.7" + }, + "replace": { + "symfony/asset": "self.version", + "symfony/browser-kit": "self.version", + "symfony/class-loader": "self.version", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/css-selector": "self.version", + "symfony/debug": "self.version", + "symfony/debug-bundle": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/expression-language": "self.version", + "symfony/filesystem": "self.version", + "symfony/finder": "self.version", + "symfony/form": "self.version", + "symfony/framework-bundle": "self.version", + "symfony/http-foundation": "self.version", + "symfony/http-kernel": "self.version", + "symfony/intl": "self.version", + "symfony/ldap": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/property-access": "self.version", + "symfony/property-info": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/security-core": "self.version", + "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", + "symfony/security-http": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/templating": "self.version", + "symfony/translation": "self.version", + "symfony/twig-bridge": "self.version", + "symfony/twig-bundle": "self.version", + "symfony/validator": "self.version", + "symfony/var-dumper": "self.version", + "symfony/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.4", + "doctrine/doctrine-bundle": "~1.4", + "doctrine/orm": "~2.4,>=2.4.5", + "egulias/email-validator": "~1.2", + "monolog/monolog": "~1.11", + "ocramius/proxy-manager": "~0.4|~1.0", + "phpdocumentor/reflection": "^1.0.7", + "symfony/security-acl": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", + "Symfony\\Bundle\\": "src/Symfony/Bundle/", + "Symfony\\Component\\": "src/Symfony/Component/" + }, + "classmap": [ + "src/Symfony/Component/Intl/Resources/stubs" + ], + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "The Symfony PHP framework", + "homepage": "https://symfony.com", + "keywords": [ + "framework" + ], + "time": "2015-12-26 16:49:48" + }, + { + "name": "twig/twig", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "shasum": "" + }, + "require": { + "php": ">=5.2.7" + }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.23-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "http://twig.sensiolabs.org/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2015-11-05 12:49:06" + } + ], + "packages-dev": [ + { + "name": "sensio/generator-bundle", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git", + "reference": "525e078ff7d5e9f19b0ef912bb6d6753673b3c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/525e078ff7d5e9f19b0ef912bb6d6753673b3c66", + "reference": "525e078ff7d5e9f19b0ef912bb6d6753673b3c66", + "shasum": "" + }, + "require": { + "symfony/console": "~2.7|~3.0", + "symfony/framework-bundle": "~2.7|~3.0", + "symfony/process": "~2.7|~3.0", + "symfony/yaml": "~2.7|~3.0" + }, + "require-dev": { + "doctrine/orm": "~2.4", + "symfony/doctrine-bridge": "~2.7|~3.0", + "twig/twig": "~1.18" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sensio\\Bundle\\GeneratorBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle generates code for you", + "time": "2015-12-20 20:01:41" + }, + { + "name": "symfony/phpunit-bridge", + "version": "v2.8.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "2deb2ccbe184948ae4e85e7b99e962738d3d2d69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/2deb2ccbe184948ae4e85e7b99e962738d3d2d69", + "reference": "2deb2ccbe184948ae4e85e7b99e962738d3d2d69", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony PHPUnit Bridge", + "homepage": "https://symfony.com", + "time": "2015-12-11 08:57:52" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.5.9" + }, + "platform-dev": [] +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..a2a0910 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,34 @@ + + + + + + + + + + + tests + + + + + + + + + + src + + src/*Bundle/Resources + src/*/*Bundle/Resources + src/*/Bundle/*Bundle/Resources + + + + diff --git a/src/.htaccess b/src/.htaccess new file mode 100644 index 0000000..fb1de45 --- /dev/null +++ b/src/.htaccess @@ -0,0 +1,7 @@ + + Require all denied + + + Order deny,allow + Deny from all + diff --git a/src/AppBundle/AppBundle.php b/src/AppBundle/AppBundle.php new file mode 100644 index 0000000..05123b6 --- /dev/null +++ b/src/AppBundle/AppBundle.php @@ -0,0 +1,9 @@ +render('default/index.html.twig', [ + 'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'), + ]); + } +} diff --git a/src/AppBundle/Entity/FkIndexNameTest.php b/src/AppBundle/Entity/FkIndexNameTest.php new file mode 100644 index 0000000..44e584f --- /dev/null +++ b/src/AppBundle/Entity/FkIndexNameTest.php @@ -0,0 +1,26 @@ +request('GET', '/'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + $this->assertContains('Welcome to Symfony', $crawler->filter('#container h1')->text()); + } +} diff --git a/var/SymfonyRequirements.php b/var/SymfonyRequirements.php new file mode 100644 index 0000000..28b0dcd --- /dev/null +++ b/var/SymfonyRequirements.php @@ -0,0 +1,764 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Users of PHP 5.2 should be able to run the requirements checks. + * This is why the file and all classes must be compatible with PHP 5.2+ + * (e.g. not using namespaces and closures). + * + * ************** CAUTION ************** + * + * DO NOT EDIT THIS FILE as it will be overridden by Composer as part of + * the installation/update process. The original file resides in the + * SensioDistributionBundle. + * + * ************** CAUTION ************** + */ + +/** + * Represents a single PHP requirement, e.g. an installed extension. + * It can be a mandatory requirement or an optional recommendation. + * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration. + * + * @author Tobias Schultze + */ +class Requirement +{ + private $fulfilled; + private $testMessage; + private $helpText; + private $helpHtml; + private $optional; + + /** + * Constructor that initializes the requirement. + * + * @param bool $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) + { + $this->fulfilled = (bool) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (bool) $optional; + } + + /** + * Returns whether the requirement is fulfilled. + * + * @return bool true if fulfilled, otherwise false + */ + public function isFulfilled() + { + return $this->fulfilled; + } + + /** + * Returns the message for testing the requirement. + * + * @return string The test message + */ + public function getTestMessage() + { + return $this->testMessage; + } + + /** + * Returns the help text for resolving the problem. + * + * @return string The help text + */ + public function getHelpText() + { + return $this->helpText; + } + + /** + * Returns the help text formatted in HTML. + * + * @return string The HTML help + */ + public function getHelpHtml() + { + return $this->helpHtml; + } + + /** + * Returns whether this is only an optional recommendation and not a mandatory requirement. + * + * @return bool true if optional, false if mandatory + */ + public function isOptional() + { + return $this->optional; + } +} + +/** + * Represents a PHP requirement in form of a php.ini configuration. + * + * @author Tobias Schultze + */ +class PhpIniRequirement extends Requirement +{ + /** + * Constructor that initializes the requirement. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) + { + $cfgValue = ini_get($cfgName); + + if (is_callable($evaluation)) { + if (null === $testMessage || null === $helpHtml) { + throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.'); + } + + $fulfilled = call_user_func($evaluation, $cfgValue); + } else { + if (null === $testMessage) { + $testMessage = sprintf('%s %s be %s in php.ini', + $cfgName, + $optional ? 'should' : 'must', + $evaluation ? 'enabled' : 'disabled' + ); + } + + if (null === $helpHtml) { + $helpHtml = sprintf('Set %s to %s in php.ini*.', + $cfgName, + $evaluation ? 'on' : 'off' + ); + } + + $fulfilled = $evaluation == $cfgValue; + } + + parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional); + } +} + +/** + * A RequirementCollection represents a set of Requirement instances. + * + * @author Tobias Schultze + */ +class RequirementCollection implements IteratorAggregate +{ + private $requirements = array(); + + /** + * Gets the current RequirementCollection as an Iterator. + * + * @return Traversable A Traversable interface + */ + public function getIterator() + { + return new ArrayIterator($this->requirements); + } + + /** + * Adds a Requirement. + * + * @param Requirement $requirement A Requirement instance + */ + public function add(Requirement $requirement) + { + $this->requirements[] = $requirement; + } + + /** + * Adds a mandatory requirement. + * + * @param bool $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation. + * + * @param bool $fulfilled Whether the recommendation is fulfilled + * @param string $testMessage The message for testing the recommendation + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a mandatory requirement in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a requirement collection to the current set of requirements. + * + * @param RequirementCollection $collection A RequirementCollection instance + */ + public function addCollection(RequirementCollection $collection) + { + $this->requirements = array_merge($this->requirements, $collection->all()); + } + + /** + * Returns both requirements and recommendations. + * + * @return array Array of Requirement instances + */ + public function all() + { + return $this->requirements; + } + + /** + * Returns all mandatory requirements. + * + * @return array Array of Requirement instances + */ + public function getRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the mandatory requirements that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && !$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns all optional recommendations. + * + * @return array Array of Requirement instances + */ + public function getRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if ($req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the recommendations that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns whether a php.ini configuration is not correct. + * + * @return bool php.ini configuration problem? + */ + public function hasPhpIniConfigIssue() + { + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) { + return true; + } + } + + return false; + } + + /** + * Returns the PHP configuration file (php.ini) path. + * + * @return string|false php.ini file path + */ + public function getPhpIniConfigPath() + { + return get_cfg_var('cfg_file_path'); + } +} + +/** + * This class specifies all requirements and optional recommendations that + * are necessary to run the Symfony Standard Edition. + * + * @author Tobias Schultze + * @author Fabien Potencier + */ +class SymfonyRequirements extends RequirementCollection +{ + const REQUIRED_PHP_VERSION = '5.3.3'; + + /** + * Constructor that initializes the requirements. + */ + public function __construct() + { + /* mandatory requirements follow */ + + $installedPhpVersion = phpversion(); + + $this->addRequirement( + version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='), + sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion), + sprintf('You are running PHP version "%s", but Symfony needs at least PHP "%s" to run. + Before using Symfony, upgrade your PHP installation, preferably to the latest version.', + $installedPhpVersion, self::REQUIRED_PHP_VERSION), + sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion) + ); + + $this->addRequirement( + version_compare($installedPhpVersion, '5.3.16', '!='), + 'PHP version must not be 5.3.16 as Symfony won\'t work properly with it', + 'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)' + ); + + $this->addRequirement( + is_dir(__DIR__.'/../vendor/composer'), + 'Vendor libraries must be installed', + 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. '. + 'Then run "php composer.phar install" to install them.' + ); + + $cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache'; + + $this->addRequirement( + is_writable($cacheDir), + 'app/cache/ or var/cache/ directory must be writable', + 'Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.' + ); + + $logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs'; + + $this->addRequirement( + is_writable($logsDir), + 'app/logs/ or var/logs/ directory must be writable', + 'Change the permissions of either "app/logs/" or "var/logs/" directory so that the web server can write into it.' + ); + + $this->addPhpIniRequirement( + 'date.timezone', true, false, + 'date.timezone setting must be set', + 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).' + ); + + if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) { + $timezones = array(); + foreach (DateTimeZone::listAbbreviations() as $abbreviations) { + foreach ($abbreviations as $abbreviation) { + $timezones[$abbreviation['timezone_id']] = true; + } + } + + $this->addRequirement( + isset($timezones[@date_default_timezone_get()]), + sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()), + 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.' + ); + } + + $this->addRequirement( + function_exists('iconv'), + 'iconv() must be available', + 'Install and enable the iconv extension.' + ); + + $this->addRequirement( + function_exists('json_encode'), + 'json_encode() must be available', + 'Install and enable the JSON extension.' + ); + + $this->addRequirement( + function_exists('session_start'), + 'session_start() must be available', + 'Install and enable the session extension.' + ); + + $this->addRequirement( + function_exists('ctype_alpha'), + 'ctype_alpha() must be available', + 'Install and enable the ctype extension.' + ); + + $this->addRequirement( + function_exists('token_get_all'), + 'token_get_all() must be available', + 'Install and enable the Tokenizer extension.' + ); + + $this->addRequirement( + function_exists('simplexml_import_dom'), + 'simplexml_import_dom() must be available', + 'Install and enable the SimpleXML extension.' + ); + + if (function_exists('apc_store') && ini_get('apc.enabled')) { + if (version_compare($installedPhpVersion, '5.4.0', '>=')) { + $this->addRequirement( + version_compare(phpversion('apc'), '3.1.13', '>='), + 'APC version must be at least 3.1.13 when using PHP 5.4', + 'Upgrade your APC extension (3.1.13+).' + ); + } else { + $this->addRequirement( + version_compare(phpversion('apc'), '3.0.17', '>='), + 'APC version must be at least 3.0.17', + 'Upgrade your APC extension (3.0.17+).' + ); + } + } + + $this->addPhpIniRequirement('detect_unicode', false); + + if (extension_loaded('suhosin')) { + $this->addPhpIniRequirement( + 'suhosin.executor.include.whitelist', + create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'), + false, + 'suhosin.executor.include.whitelist must be configured correctly in php.ini', + 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.' + ); + } + + if (extension_loaded('xdebug')) { + $this->addPhpIniRequirement( + 'xdebug.show_exception_trace', false, true + ); + + $this->addPhpIniRequirement( + 'xdebug.scream', false, true + ); + + $this->addPhpIniRecommendation( + 'xdebug.max_nesting_level', + create_function('$cfgValue', 'return $cfgValue > 100;'), + true, + 'xdebug.max_nesting_level should be above 100 in php.ini', + 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.' + ); + } + + $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; + + $this->addRequirement( + null !== $pcreVersion, + 'PCRE extension must be available', + 'Install the PCRE extension (version 8.0+).' + ); + + if (extension_loaded('mbstring')) { + $this->addPhpIniRequirement( + 'mbstring.func_overload', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'string functions should not be overloaded', + 'Set "mbstring.func_overload" to 0 in php.ini* to disable function overloading by the mbstring extension.' + ); + } + + /* optional recommendations follow */ + + if (file_exists(__DIR__.'/../vendor/composer')) { + require_once __DIR__.'/../vendor/autoload.php'; + + try { + $r = new ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle'); + + $contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php'); + } catch (ReflectionException $e) { + $contents = ''; + } + $this->addRecommendation( + file_get_contents(__FILE__) === $contents, + 'Requirements file should be up-to-date', + 'Your requirements file is outdated. Run composer install and re-check your configuration.' + ); + } + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.4', '>='), + 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions', + 'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.8', '>='), + 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156', + 'Install PHP 5.3.8 or newer if your project uses annotations.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.4.0', '!='), + 'You should not use PHP 5.4.0 due to the PHP bug #61453', + 'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.4.11', '>='), + 'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)', + 'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.' + ); + + $this->addRecommendation( + (version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<')) + || + version_compare($installedPhpVersion, '5.4.8', '>='), + 'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909', + 'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.' + ); + + if (null !== $pcreVersion) { + $this->addRecommendation( + $pcreVersion >= 8.0, + sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion), + 'PCRE 8.0+ is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.' + ); + } + + $this->addRecommendation( + class_exists('DomDocument'), + 'PHP-DOM and PHP-XML modules should be installed', + 'Install and enable the PHP-DOM and the PHP-XML modules.' + ); + + $this->addRecommendation( + function_exists('mb_strlen'), + 'mb_strlen() should be available', + 'Install and enable the mbstring extension.' + ); + + $this->addRecommendation( + function_exists('iconv'), + 'iconv() should be available', + 'Install and enable the iconv extension.' + ); + + $this->addRecommendation( + function_exists('utf8_decode'), + 'utf8_decode() should be available', + 'Install and enable the XML extension.' + ); + + $this->addRecommendation( + function_exists('filter_var'), + 'filter_var() should be available', + 'Install and enable the filter extension.' + ); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->addRecommendation( + function_exists('posix_isatty'), + 'posix_isatty() should be available', + 'Install and enable the php_posix extension (used to colorize the CLI output).' + ); + } + + $this->addRecommendation( + extension_loaded('intl'), + 'intl extension should be available', + 'Install and enable the intl extension (used for validators).' + ); + + if (extension_loaded('intl')) { + // in some WAMP server installations, new Collator() returns null + $this->addRecommendation( + null !== new Collator('fr_FR'), + 'intl extension should be correctly configured', + 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' + ); + + // check for compatible ICU versions (only done when you have the intl extension) + if (defined('INTL_ICU_VERSION')) { + $version = INTL_ICU_VERSION; + } else { + $reflector = new ReflectionExtension('intl'); + + ob_start(); + $reflector->info(); + $output = strip_tags(ob_get_clean()); + + preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); + $version = $matches[1]; + } + + $this->addRecommendation( + version_compare($version, '4.0', '>='), + 'intl ICU version should be at least 4+', + 'Upgrade your intl extension with a newer ICU version (4+).' + ); + + $this->addPhpIniRecommendation( + 'intl.error_level', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'intl.error_level should be 0 in php.ini', + 'Set "intl.error_level" to "0" in php.ini* to inhibit the messages when an error occurs in ICU functions.' + ); + } + + $accelerator = + (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) + || + (extension_loaded('apc') && ini_get('apc.enabled')) + || + (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) + || + (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) + || + (extension_loaded('xcache') && ini_get('xcache.cacher')) + || + (extension_loaded('wincache') && ini_get('wincache.ocenabled')) + ; + + $this->addRecommendation( + $accelerator, + 'a PHP accelerator should be installed', + 'Install and/or enable a PHP accelerator (highly recommended).' + ); + + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $this->addRecommendation( + $this->getRealpathCacheSize() > 1000, + 'realpath_cache_size should be above 1024 in php.ini', + 'Set "realpath_cache_size" to e.g. "1024" in php.ini* to improve performance on windows.' + ); + } + + $this->addPhpIniRecommendation('short_open_tag', false); + + $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); + + $this->addPhpIniRecommendation('register_globals', false, true); + + $this->addPhpIniRecommendation('session.auto_start', false); + + $this->addRecommendation( + class_exists('PDO'), + 'PDO should be installed', + 'Install PDO (mandatory for Doctrine).' + ); + + if (class_exists('PDO')) { + $drivers = PDO::getAvailableDrivers(); + $this->addRecommendation( + count($drivers) > 0, + sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), + 'Install PDO drivers (mandatory for Doctrine).' + ); + } + } + + /** + * Loads realpath_cache_size from php.ini and converts it to int. + * + * (e.g. 16k is converted to 16384 int) + * + * @return int + */ + protected function getRealpathCacheSize() + { + $size = ini_get('realpath_cache_size'); + $size = trim($size); + $unit = strtolower(substr($size, -1, 1)); + switch ($unit) { + case 'g': + return $size * 1024 * 1024 * 1024; + case 'm': + return $size * 1024 * 1024; + case 'k': + return $size * 1024; + default: + return (int) $size; + } + } +} diff --git a/var/bootstrap.php.cache b/var/bootstrap.php.cache new file mode 100644 index 0000000..ac47d9a --- /dev/null +++ b/var/bootstrap.php.cache @@ -0,0 +1,3057 @@ +parameters = $parameters; +} +public function all() +{ +return $this->parameters; +} +public function keys() +{ +return array_keys($this->parameters); +} +public function replace(array $parameters = array()) +{ +$this->parameters = $parameters; +} +public function add(array $parameters = array()) +{ +$this->parameters = array_replace($this->parameters, $parameters); +} +public function get($key, $default = null) +{ +return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; +} +public function set($key, $value) +{ +$this->parameters[$key] = $value; +} +public function has($key) +{ +return array_key_exists($key, $this->parameters); +} +public function remove($key) +{ +unset($this->parameters[$key]); +} +public function getAlpha($key, $default ='') +{ +return preg_replace('/[^[:alpha:]]/','', $this->get($key, $default)); +} +public function getAlnum($key, $default ='') +{ +return preg_replace('/[^[:alnum:]]/','', $this->get($key, $default)); +} +public function getDigits($key, $default ='') +{ +return str_replace(array('-','+'),'', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT)); +} +public function getInt($key, $default = 0) +{ +return (int) $this->get($key, $default); +} +public function getBoolean($key, $default = false) +{ +return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN); +} +public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array()) +{ +$value = $this->get($key, $default); +if (!is_array($options) && $options) { +$options = array('flags'=> $options); +} +if (is_array($value) && !isset($options['flags'])) { +$options['flags'] = FILTER_REQUIRE_ARRAY; +} +return filter_var($value, $filter, $options); +} +public function getIterator() +{ +return new \ArrayIterator($this->parameters); +} +public function count() +{ +return count($this->parameters); +} +} +} +namespace Symfony\Component\HttpFoundation +{ +class HeaderBag implements \IteratorAggregate, \Countable +{ +protected $headers = array(); +protected $cacheControl = array(); +public function __construct(array $headers = array()) +{ +foreach ($headers as $key => $values) { +$this->set($key, $values); +} +} +public function __toString() +{ +if (!$this->headers) { +return''; +} +$max = max(array_map('strlen', array_keys($this->headers))) + 1; +$content =''; +ksort($this->headers); +foreach ($this->headers as $name => $values) { +$name = implode('-', array_map('ucfirst', explode('-', $name))); +foreach ($values as $value) { +$content .= sprintf("%-{$max}s %s\r\n", $name.':', $value); +} +} +return $content; +} +public function all() +{ +return $this->headers; +} +public function keys() +{ +return array_keys($this->headers); +} +public function replace(array $headers = array()) +{ +$this->headers = array(); +$this->add($headers); +} +public function add(array $headers) +{ +foreach ($headers as $key => $values) { +$this->set($key, $values); +} +} +public function get($key, $default = null, $first = true) +{ +$key = strtr(strtolower($key),'_','-'); +if (!array_key_exists($key, $this->headers)) { +if (null === $default) { +return $first ? null : array(); +} +return $first ? $default : array($default); +} +if ($first) { +return count($this->headers[$key]) ? $this->headers[$key][0] : $default; +} +return $this->headers[$key]; +} +public function set($key, $values, $replace = true) +{ +$key = strtr(strtolower($key),'_','-'); +$values = array_values((array) $values); +if (true === $replace || !isset($this->headers[$key])) { +$this->headers[$key] = $values; +} else { +$this->headers[$key] = array_merge($this->headers[$key], $values); +} +if ('cache-control'=== $key) { +$this->cacheControl = $this->parseCacheControl($values[0]); +} +} +public function has($key) +{ +return array_key_exists(strtr(strtolower($key),'_','-'), $this->headers); +} +public function contains($key, $value) +{ +return in_array($value, $this->get($key, null, false)); +} +public function remove($key) +{ +$key = strtr(strtolower($key),'_','-'); +unset($this->headers[$key]); +if ('cache-control'=== $key) { +$this->cacheControl = array(); +} +} +public function getDate($key, \DateTime $default = null) +{ +if (null === $value = $this->get($key)) { +return $default; +} +if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) { +throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); +} +return $date; +} +public function addCacheControlDirective($key, $value = true) +{ +$this->cacheControl[$key] = $value; +$this->set('Cache-Control', $this->getCacheControlHeader()); +} +public function hasCacheControlDirective($key) +{ +return array_key_exists($key, $this->cacheControl); +} +public function getCacheControlDirective($key) +{ +return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; +} +public function removeCacheControlDirective($key) +{ +unset($this->cacheControl[$key]); +$this->set('Cache-Control', $this->getCacheControlHeader()); +} +public function getIterator() +{ +return new \ArrayIterator($this->headers); +} +public function count() +{ +return count($this->headers); +} +protected function getCacheControlHeader() +{ +$parts = array(); +ksort($this->cacheControl); +foreach ($this->cacheControl as $key => $value) { +if (true === $value) { +$parts[] = $key; +} else { +if (preg_match('#[^a-zA-Z0-9._-]#', $value)) { +$value ='"'.$value.'"'; +} +$parts[] = "$key=$value"; +} +} +return implode(', ', $parts); +} +protected function parseCacheControl($header) +{ +$cacheControl = array(); +preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER); +foreach ($matches as $match) { +$cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true); +} +return $cacheControl; +} +} +} +namespace Symfony\Component\HttpFoundation +{ +use Symfony\Component\HttpFoundation\File\UploadedFile; +class FileBag extends ParameterBag +{ +private static $fileKeys = array('error','name','size','tmp_name','type'); +public function __construct(array $parameters = array()) +{ +$this->replace($parameters); +} +public function replace(array $files = array()) +{ +$this->parameters = array(); +$this->add($files); +} +public function set($key, $value) +{ +if (!is_array($value) && !$value instanceof UploadedFile) { +throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); +} +parent::set($key, $this->convertFileInformation($value)); +} +public function add(array $files = array()) +{ +foreach ($files as $key => $file) { +$this->set($key, $file); +} +} +protected function convertFileInformation($file) +{ +if ($file instanceof UploadedFile) { +return $file; +} +$file = $this->fixPhpFilesArray($file); +if (is_array($file)) { +$keys = array_keys($file); +sort($keys); +if ($keys == self::$fileKeys) { +if (UPLOAD_ERR_NO_FILE == $file['error']) { +$file = null; +} else { +$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); +} +} else { +$file = array_map(array($this,'convertFileInformation'), $file); +} +} +return $file; +} +protected function fixPhpFilesArray($data) +{ +if (!is_array($data)) { +return $data; +} +$keys = array_keys($data); +sort($keys); +if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { +return $data; +} +$files = $data; +foreach (self::$fileKeys as $k) { +unset($files[$k]); +} +foreach ($data['name'] as $key => $name) { +$files[$key] = $this->fixPhpFilesArray(array('error'=> $data['error'][$key],'name'=> $name,'type'=> $data['type'][$key],'tmp_name'=> $data['tmp_name'][$key],'size'=> $data['size'][$key], +)); +} +return $files; +} +} +} +namespace Symfony\Component\HttpFoundation +{ +class ServerBag extends ParameterBag +{ +public function getHeaders() +{ +$headers = array(); +$contentHeaders = array('CONTENT_LENGTH'=> true,'CONTENT_MD5'=> true,'CONTENT_TYPE'=> true); +foreach ($this->parameters as $key => $value) { +if (0 === strpos($key,'HTTP_')) { +$headers[substr($key, 5)] = $value; +} +elseif (isset($contentHeaders[$key])) { +$headers[$key] = $value; +} +} +if (isset($this->parameters['PHP_AUTH_USER'])) { +$headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER']; +$headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] :''; +} else { +$authorizationHeader = null; +if (isset($this->parameters['HTTP_AUTHORIZATION'])) { +$authorizationHeader = $this->parameters['HTTP_AUTHORIZATION']; +} elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) { +$authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION']; +} +if (null !== $authorizationHeader) { +if (0 === stripos($authorizationHeader,'basic ')) { +$exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2); +if (count($exploded) == 2) { +list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; +} +} elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader,'digest '))) { +$headers['PHP_AUTH_DIGEST'] = $authorizationHeader; +$this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader; +} elseif (0 === stripos($authorizationHeader,'bearer ')) { +$headers['AUTHORIZATION'] = $authorizationHeader; +} +} +} +if (isset($headers['PHP_AUTH_USER'])) { +$headers['AUTHORIZATION'] ='Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']); +} elseif (isset($headers['PHP_AUTH_DIGEST'])) { +$headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST']; +} +return $headers; +} +} +} +namespace Symfony\Component\HttpFoundation +{ +use Symfony\Component\HttpFoundation\Session\SessionInterface; +class Request +{ +const HEADER_FORWARDED ='forwarded'; +const HEADER_CLIENT_IP ='client_ip'; +const HEADER_CLIENT_HOST ='client_host'; +const HEADER_CLIENT_PROTO ='client_proto'; +const HEADER_CLIENT_PORT ='client_port'; +const METHOD_HEAD ='HEAD'; +const METHOD_GET ='GET'; +const METHOD_POST ='POST'; +const METHOD_PUT ='PUT'; +const METHOD_PATCH ='PATCH'; +const METHOD_DELETE ='DELETE'; +const METHOD_PURGE ='PURGE'; +const METHOD_OPTIONS ='OPTIONS'; +const METHOD_TRACE ='TRACE'; +const METHOD_CONNECT ='CONNECT'; +protected static $trustedProxies = array(); +protected static $trustedHostPatterns = array(); +protected static $trustedHosts = array(); +protected static $trustedHeaders = array( +self::HEADER_FORWARDED =>'FORWARDED', +self::HEADER_CLIENT_IP =>'X_FORWARDED_FOR', +self::HEADER_CLIENT_HOST =>'X_FORWARDED_HOST', +self::HEADER_CLIENT_PROTO =>'X_FORWARDED_PROTO', +self::HEADER_CLIENT_PORT =>'X_FORWARDED_PORT', +); +protected static $httpMethodParameterOverride = false; +public $attributes; +public $request; +public $query; +public $server; +public $files; +public $cookies; +public $headers; +protected $content; +protected $languages; +protected $charsets; +protected $encodings; +protected $acceptableContentTypes; +protected $pathInfo; +protected $requestUri; +protected $baseUrl; +protected $basePath; +protected $method; +protected $format; +protected $session; +protected $locale; +protected $defaultLocale ='en'; +protected static $formats; +protected static $requestFactory; +public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) +{ +$this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); +} +public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) +{ +$this->request = new ParameterBag($request); +$this->query = new ParameterBag($query); +$this->attributes = new ParameterBag($attributes); +$this->cookies = new ParameterBag($cookies); +$this->files = new FileBag($files); +$this->server = new ServerBag($server); +$this->headers = new HeaderBag($this->server->getHeaders()); +$this->content = $content; +$this->languages = null; +$this->charsets = null; +$this->encodings = null; +$this->acceptableContentTypes = null; +$this->pathInfo = null; +$this->requestUri = null; +$this->baseUrl = null; +$this->basePath = null; +$this->method = null; +$this->format = null; +} +public static function createFromGlobals() +{ +$server = $_SERVER; +if ('cli-server'=== php_sapi_name()) { +if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) { +$server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH']; +} +if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { +$server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE']; +} +} +$request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); +if (0 === strpos($request->headers->get('CONTENT_TYPE'),'application/x-www-form-urlencoded') +&& in_array(strtoupper($request->server->get('REQUEST_METHOD','GET')), array('PUT','DELETE','PATCH')) +) { +parse_str($request->getContent(), $data); +$request->request = new ParameterBag($data); +} +return $request; +} +public static function create($uri, $method ='GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) +{ +$server = array_replace(array('SERVER_NAME'=>'localhost','SERVER_PORT'=> 80,'HTTP_HOST'=>'localhost','HTTP_USER_AGENT'=>'Symfony/2.X','HTTP_ACCEPT'=>'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','HTTP_ACCEPT_LANGUAGE'=>'en-us,en;q=0.5','HTTP_ACCEPT_CHARSET'=>'ISO-8859-1,utf-8;q=0.7,*;q=0.7','REMOTE_ADDR'=>'127.0.0.1','SCRIPT_NAME'=>'','SCRIPT_FILENAME'=>'','SERVER_PROTOCOL'=>'HTTP/1.1','REQUEST_TIME'=> time(), +), $server); +$server['PATH_INFO'] =''; +$server['REQUEST_METHOD'] = strtoupper($method); +$components = parse_url($uri); +if (isset($components['host'])) { +$server['SERVER_NAME'] = $components['host']; +$server['HTTP_HOST'] = $components['host']; +} +if (isset($components['scheme'])) { +if ('https'=== $components['scheme']) { +$server['HTTPS'] ='on'; +$server['SERVER_PORT'] = 443; +} else { +unset($server['HTTPS']); +$server['SERVER_PORT'] = 80; +} +} +if (isset($components['port'])) { +$server['SERVER_PORT'] = $components['port']; +$server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port']; +} +if (isset($components['user'])) { +$server['PHP_AUTH_USER'] = $components['user']; +} +if (isset($components['pass'])) { +$server['PHP_AUTH_PW'] = $components['pass']; +} +if (!isset($components['path'])) { +$components['path'] ='/'; +} +switch (strtoupper($method)) { +case'POST': +case'PUT': +case'DELETE': +if (!isset($server['CONTENT_TYPE'])) { +$server['CONTENT_TYPE'] ='application/x-www-form-urlencoded'; +} +case'PATCH': +$request = $parameters; +$query = array(); +break; +default: +$request = array(); +$query = $parameters; +break; +} +$queryString =''; +if (isset($components['query'])) { +parse_str(html_entity_decode($components['query']), $qs); +if ($query) { +$query = array_replace($qs, $query); +$queryString = http_build_query($query,'','&'); +} else { +$query = $qs; +$queryString = $components['query']; +} +} elseif ($query) { +$queryString = http_build_query($query,'','&'); +} +$server['REQUEST_URI'] = $components['path'].(''!== $queryString ?'?'.$queryString :''); +$server['QUERY_STRING'] = $queryString; +return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content); +} +public static function setFactory($callable) +{ +self::$requestFactory = $callable; +} +public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) +{ +$dup = clone $this; +if ($query !== null) { +$dup->query = new ParameterBag($query); +} +if ($request !== null) { +$dup->request = new ParameterBag($request); +} +if ($attributes !== null) { +$dup->attributes = new ParameterBag($attributes); +} +if ($cookies !== null) { +$dup->cookies = new ParameterBag($cookies); +} +if ($files !== null) { +$dup->files = new FileBag($files); +} +if ($server !== null) { +$dup->server = new ServerBag($server); +$dup->headers = new HeaderBag($dup->server->getHeaders()); +} +$dup->languages = null; +$dup->charsets = null; +$dup->encodings = null; +$dup->acceptableContentTypes = null; +$dup->pathInfo = null; +$dup->requestUri = null; +$dup->baseUrl = null; +$dup->basePath = null; +$dup->method = null; +$dup->format = null; +if (!$dup->get('_format') && $this->get('_format')) { +$dup->attributes->set('_format', $this->get('_format')); +} +if (!$dup->getRequestFormat(null)) { +$dup->setRequestFormat($this->getRequestFormat(null)); +} +return $dup; +} +public function __clone() +{ +$this->query = clone $this->query; +$this->request = clone $this->request; +$this->attributes = clone $this->attributes; +$this->cookies = clone $this->cookies; +$this->files = clone $this->files; +$this->server = clone $this->server; +$this->headers = clone $this->headers; +} +public function __toString() +{ +try { +$content = $this->getContent(); +} catch (\LogicException $e) { +return trigger_error($e, E_USER_ERROR); +} +return +sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". +$this->headers."\r\n". +$content; +} +public function overrideGlobals() +{ +$this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), null,'&'))); +$_GET = $this->query->all(); +$_POST = $this->request->all(); +$_SERVER = $this->server->all(); +$_COOKIE = $this->cookies->all(); +foreach ($this->headers->all() as $key => $value) { +$key = strtoupper(str_replace('-','_', $key)); +if (in_array($key, array('CONTENT_TYPE','CONTENT_LENGTH'))) { +$_SERVER[$key] = implode(', ', $value); +} else { +$_SERVER['HTTP_'.$key] = implode(', ', $value); +} +} +$request = array('g'=> $_GET,'p'=> $_POST,'c'=> $_COOKIE); +$requestOrder = ini_get('request_order') ?: ini_get('variables_order'); +$requestOrder = preg_replace('#[^cgp]#','', strtolower($requestOrder)) ?:'gp'; +$_REQUEST = array(); +foreach (str_split($requestOrder) as $order) { +$_REQUEST = array_merge($_REQUEST, $request[$order]); +} +} +public static function setTrustedProxies(array $proxies) +{ +self::$trustedProxies = $proxies; +} +public static function getTrustedProxies() +{ +return self::$trustedProxies; +} +public static function setTrustedHosts(array $hostPatterns) +{ +self::$trustedHostPatterns = array_map(function ($hostPattern) { +return sprintf('#%s#i', $hostPattern); +}, $hostPatterns); +self::$trustedHosts = array(); +} +public static function getTrustedHosts() +{ +return self::$trustedHostPatterns; +} +public static function setTrustedHeaderName($key, $value) +{ +if (!array_key_exists($key, self::$trustedHeaders)) { +throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); +} +self::$trustedHeaders[$key] = $value; +} +public static function getTrustedHeaderName($key) +{ +if (!array_key_exists($key, self::$trustedHeaders)) { +throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key)); +} +return self::$trustedHeaders[$key]; +} +public static function normalizeQueryString($qs) +{ +if (''== $qs) { +return''; +} +$parts = array(); +$order = array(); +foreach (explode('&', $qs) as $param) { +if (''=== $param ||'='=== $param[0]) { +continue; +} +$keyValuePair = explode('=', $param, 2); +$parts[] = isset($keyValuePair[1]) ? +rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) : +rawurlencode(urldecode($keyValuePair[0])); +$order[] = urldecode($keyValuePair[0]); +} +array_multisort($order, SORT_ASC, $parts); +return implode('&', $parts); +} +public static function enableHttpMethodParameterOverride() +{ +self::$httpMethodParameterOverride = true; +} +public static function getHttpMethodParameterOverride() +{ +return self::$httpMethodParameterOverride; +} +public function get($key, $default = null) +{ +if ($this !== $result = $this->attributes->get($key, $this)) { +return $result; +} +if ($this !== $result = $this->query->get($key, $this)) { +return $result; +} +if ($this !== $result = $this->request->get($key, $this)) { +return $result; +} +return $default; +} +public function getSession() +{ +return $this->session; +} +public function hasPreviousSession() +{ +return $this->hasSession() && $this->cookies->has($this->session->getName()); +} +public function hasSession() +{ +return null !== $this->session; +} +public function setSession(SessionInterface $session) +{ +$this->session = $session; +} +public function getClientIps() +{ +$clientIps = array(); +$ip = $this->server->get('REMOTE_ADDR'); +if (!$this->isFromTrustedProxy()) { +return array($ip); +} +if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { +$forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); +preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches); +$clientIps = $matches[3]; +} elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { +$clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); +} +$clientIps[] = $ip; $ip = $clientIps[0]; +foreach ($clientIps as $key => $clientIp) { +if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { +$clientIps[$key] = $clientIp = $match[1]; +} +if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { +unset($clientIps[$key]); +} +} +return $clientIps ? array_reverse($clientIps) : array($ip); +} +public function getClientIp() +{ +$ipAddresses = $this->getClientIps(); +return $ipAddresses[0]; +} +public function getScriptName() +{ +return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME','')); +} +public function getPathInfo() +{ +if (null === $this->pathInfo) { +$this->pathInfo = $this->preparePathInfo(); +} +return $this->pathInfo; +} +public function getBasePath() +{ +if (null === $this->basePath) { +$this->basePath = $this->prepareBasePath(); +} +return $this->basePath; +} +public function getBaseUrl() +{ +if (null === $this->baseUrl) { +$this->baseUrl = $this->prepareBaseUrl(); +} +return $this->baseUrl; +} +public function getScheme() +{ +return $this->isSecure() ?'https':'http'; +} +public function getPort() +{ +if ($this->isFromTrustedProxy()) { +if (self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) { +return $port; +} +if (self::$trustedHeaders[self::HEADER_CLIENT_PROTO] &&'https'=== $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO],'http')) { +return 443; +} +} +if ($host = $this->headers->get('HOST')) { +if ($host[0] ==='[') { +$pos = strpos($host,':', strrpos($host,']')); +} else { +$pos = strrpos($host,':'); +} +if (false !== $pos) { +return (int) substr($host, $pos + 1); +} +return'https'=== $this->getScheme() ? 443 : 80; +} +return $this->server->get('SERVER_PORT'); +} +public function getUser() +{ +return $this->headers->get('PHP_AUTH_USER'); +} +public function getPassword() +{ +return $this->headers->get('PHP_AUTH_PW'); +} +public function getUserInfo() +{ +$userinfo = $this->getUser(); +$pass = $this->getPassword(); +if (''!= $pass) { +$userinfo .= ":$pass"; +} +return $userinfo; +} +public function getHttpHost() +{ +$scheme = $this->getScheme(); +$port = $this->getPort(); +if (('http'== $scheme && $port == 80) || ('https'== $scheme && $port == 443)) { +return $this->getHost(); +} +return $this->getHost().':'.$port; +} +public function getRequestUri() +{ +if (null === $this->requestUri) { +$this->requestUri = $this->prepareRequestUri(); +} +return $this->requestUri; +} +public function getSchemeAndHttpHost() +{ +return $this->getScheme().'://'.$this->getHttpHost(); +} +public function getUri() +{ +if (null !== $qs = $this->getQueryString()) { +$qs ='?'.$qs; +} +return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs; +} +public function getUriForPath($path) +{ +return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path; +} +public function getRelativeUriForPath($path) +{ +if (!isset($path[0]) ||'/'!== $path[0]) { +return $path; +} +if ($path === $basePath = $this->getPathInfo()) { +return''; +} +$sourceDirs = explode('/', isset($basePath[0]) &&'/'=== $basePath[0] ? substr($basePath, 1) : $basePath); +$targetDirs = explode('/', isset($path[0]) &&'/'=== $path[0] ? substr($path, 1) : $path); +array_pop($sourceDirs); +$targetFile = array_pop($targetDirs); +foreach ($sourceDirs as $i => $dir) { +if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { +unset($sourceDirs[$i], $targetDirs[$i]); +} else { +break; +} +} +$targetDirs[] = $targetFile; +$path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); +return !isset($path[0]) ||'/'=== $path[0] +|| false !== ($colonPos = strpos($path,':')) && ($colonPos < ($slashPos = strpos($path,'/')) || false === $slashPos) +? "./$path" : $path; +} +public function getQueryString() +{ +$qs = static::normalizeQueryString($this->server->get('QUERY_STRING')); +return''=== $qs ? null : $qs; +} +public function isSecure() +{ +if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) { +return in_array(strtolower(current(explode(',', $proto))), array('https','on','ssl','1')); +} +$https = $this->server->get('HTTPS'); +return !empty($https) &&'off'!== strtolower($https); +} +public function getHost() +{ +if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) { +$elements = explode(',', $host); +$host = $elements[count($elements) - 1]; +} elseif (!$host = $this->headers->get('HOST')) { +if (!$host = $this->server->get('SERVER_NAME')) { +$host = $this->server->get('SERVER_ADDR',''); +} +} +$host = strtolower(preg_replace('/:\d+$/','', trim($host))); +if ($host &&''!== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/','', $host)) { +throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host)); +} +if (count(self::$trustedHostPatterns) > 0) { +if (in_array($host, self::$trustedHosts)) { +return $host; +} +foreach (self::$trustedHostPatterns as $pattern) { +if (preg_match($pattern, $host)) { +self::$trustedHosts[] = $host; +return $host; +} +} +throw new \UnexpectedValueException(sprintf('Untrusted Host "%s"', $host)); +} +return $host; +} +public function setMethod($method) +{ +$this->method = null; +$this->server->set('REQUEST_METHOD', $method); +} +public function getMethod() +{ +if (null === $this->method) { +$this->method = strtoupper($this->server->get('REQUEST_METHOD','GET')); +if ('POST'=== $this->method) { +if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) { +$this->method = strtoupper($method); +} elseif (self::$httpMethodParameterOverride) { +$this->method = strtoupper($this->request->get('_method', $this->query->get('_method','POST'))); +} +} +} +return $this->method; +} +public function getRealMethod() +{ +return strtoupper($this->server->get('REQUEST_METHOD','GET')); +} +public function getMimeType($format) +{ +if (null === static::$formats) { +static::initializeFormats(); +} +return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; +} +public function getFormat($mimeType) +{ +if (false !== $pos = strpos($mimeType,';')) { +$mimeType = substr($mimeType, 0, $pos); +} +if (null === static::$formats) { +static::initializeFormats(); +} +foreach (static::$formats as $format => $mimeTypes) { +if (in_array($mimeType, (array) $mimeTypes)) { +return $format; +} +} +} +public function setFormat($format, $mimeTypes) +{ +if (null === static::$formats) { +static::initializeFormats(); +} +static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); +} +public function getRequestFormat($default ='html') +{ +if (null === $this->format) { +$this->format = $this->attributes->get('_format', $default); +} +return $this->format; +} +public function setRequestFormat($format) +{ +$this->format = $format; +} +public function getContentType() +{ +return $this->getFormat($this->headers->get('CONTENT_TYPE')); +} +public function setDefaultLocale($locale) +{ +$this->defaultLocale = $locale; +if (null === $this->locale) { +$this->setPhpDefaultLocale($locale); +} +} +public function getDefaultLocale() +{ +return $this->defaultLocale; +} +public function setLocale($locale) +{ +$this->setPhpDefaultLocale($this->locale = $locale); +} +public function getLocale() +{ +return null === $this->locale ? $this->defaultLocale : $this->locale; +} +public function isMethod($method) +{ +return $this->getMethod() === strtoupper($method); +} +public function isMethodSafe() +{ +return in_array($this->getMethod(), array('GET','HEAD')); +} +public function getContent($asResource = false) +{ +$currentContentIsResource = is_resource($this->content); +if (PHP_VERSION_ID < 50600 && false === $this->content) { +throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.'); +} +if (true === $asResource) { +if ($currentContentIsResource) { +rewind($this->content); +return $this->content; +} +if (is_string($this->content)) { +$resource = fopen('php://temp','r+'); +fwrite($resource, $this->content); +rewind($resource); +return $resource; +} +$this->content = false; +return fopen('php://input','rb'); +} +if ($currentContentIsResource) { +rewind($this->content); +return stream_get_contents($this->content); +} +if (null === $this->content) { +$this->content = file_get_contents('php://input'); +} +return $this->content; +} +public function getETags() +{ +return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); +} +public function isNoCache() +{ +return $this->headers->hasCacheControlDirective('no-cache') ||'no-cache'== $this->headers->get('Pragma'); +} +public function getPreferredLanguage(array $locales = null) +{ +$preferredLanguages = $this->getLanguages(); +if (empty($locales)) { +return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; +} +if (!$preferredLanguages) { +return $locales[0]; +} +$extendedPreferredLanguages = array(); +foreach ($preferredLanguages as $language) { +$extendedPreferredLanguages[] = $language; +if (false !== $position = strpos($language,'_')) { +$superLanguage = substr($language, 0, $position); +if (!in_array($superLanguage, $preferredLanguages)) { +$extendedPreferredLanguages[] = $superLanguage; +} +} +} +$preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales)); +return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; +} +public function getLanguages() +{ +if (null !== $this->languages) { +return $this->languages; +} +$languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); +$this->languages = array(); +foreach ($languages as $lang => $acceptHeaderItem) { +if (false !== strpos($lang,'-')) { +$codes = explode('-', $lang); +if ('i'=== $codes[0]) { +if (count($codes) > 1) { +$lang = $codes[1]; +} +} else { +for ($i = 0, $max = count($codes); $i < $max; ++$i) { +if ($i === 0) { +$lang = strtolower($codes[0]); +} else { +$lang .='_'.strtoupper($codes[$i]); +} +} +} +} +$this->languages[] = $lang; +} +return $this->languages; +} +public function getCharsets() +{ +if (null !== $this->charsets) { +return $this->charsets; +} +return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()); +} +public function getEncodings() +{ +if (null !== $this->encodings) { +return $this->encodings; +} +return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()); +} +public function getAcceptableContentTypes() +{ +if (null !== $this->acceptableContentTypes) { +return $this->acceptableContentTypes; +} +return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()); +} +public function isXmlHttpRequest() +{ +return'XMLHttpRequest'== $this->headers->get('X-Requested-With'); +} +protected function prepareRequestUri() +{ +$requestUri =''; +if ($this->headers->has('X_ORIGINAL_URL')) { +$requestUri = $this->headers->get('X_ORIGINAL_URL'); +$this->headers->remove('X_ORIGINAL_URL'); +$this->server->remove('HTTP_X_ORIGINAL_URL'); +$this->server->remove('UNENCODED_URL'); +$this->server->remove('IIS_WasUrlRewritten'); +} elseif ($this->headers->has('X_REWRITE_URL')) { +$requestUri = $this->headers->get('X_REWRITE_URL'); +$this->headers->remove('X_REWRITE_URL'); +} elseif ($this->server->get('IIS_WasUrlRewritten') =='1'&& $this->server->get('UNENCODED_URL') !='') { +$requestUri = $this->server->get('UNENCODED_URL'); +$this->server->remove('UNENCODED_URL'); +$this->server->remove('IIS_WasUrlRewritten'); +} elseif ($this->server->has('REQUEST_URI')) { +$requestUri = $this->server->get('REQUEST_URI'); +$schemeAndHttpHost = $this->getSchemeAndHttpHost(); +if (strpos($requestUri, $schemeAndHttpHost) === 0) { +$requestUri = substr($requestUri, strlen($schemeAndHttpHost)); +} +} elseif ($this->server->has('ORIG_PATH_INFO')) { +$requestUri = $this->server->get('ORIG_PATH_INFO'); +if (''!= $this->server->get('QUERY_STRING')) { +$requestUri .='?'.$this->server->get('QUERY_STRING'); +} +$this->server->remove('ORIG_PATH_INFO'); +} +$this->server->set('REQUEST_URI', $requestUri); +return $requestUri; +} +protected function prepareBaseUrl() +{ +$filename = basename($this->server->get('SCRIPT_FILENAME')); +if (basename($this->server->get('SCRIPT_NAME')) === $filename) { +$baseUrl = $this->server->get('SCRIPT_NAME'); +} elseif (basename($this->server->get('PHP_SELF')) === $filename) { +$baseUrl = $this->server->get('PHP_SELF'); +} elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { +$baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); } else { +$path = $this->server->get('PHP_SELF',''); +$file = $this->server->get('SCRIPT_FILENAME',''); +$segs = explode('/', trim($file,'/')); +$segs = array_reverse($segs); +$index = 0; +$last = count($segs); +$baseUrl =''; +do { +$seg = $segs[$index]; +$baseUrl ='/'.$seg.$baseUrl; +++$index; +} while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos); +} +$requestUri = $this->getRequestUri(); +if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { +return $prefix; +} +if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl),'/'.DIRECTORY_SEPARATOR).'/')) { +return rtrim($prefix,'/'.DIRECTORY_SEPARATOR); +} +$truncatedRequestUri = $requestUri; +if (false !== $pos = strpos($requestUri,'?')) { +$truncatedRequestUri = substr($requestUri, 0, $pos); +} +$basename = basename($baseUrl); +if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) { +return''; +} +if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && $pos !== 0) { +$baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); +} +return rtrim($baseUrl,'/'.DIRECTORY_SEPARATOR); +} +protected function prepareBasePath() +{ +$filename = basename($this->server->get('SCRIPT_FILENAME')); +$baseUrl = $this->getBaseUrl(); +if (empty($baseUrl)) { +return''; +} +if (basename($baseUrl) === $filename) { +$basePath = dirname($baseUrl); +} else { +$basePath = $baseUrl; +} +if ('\\'=== DIRECTORY_SEPARATOR) { +$basePath = str_replace('\\','/', $basePath); +} +return rtrim($basePath,'/'); +} +protected function preparePathInfo() +{ +$baseUrl = $this->getBaseUrl(); +if (null === ($requestUri = $this->getRequestUri())) { +return'/'; +} +$pathInfo ='/'; +if ($pos = strpos($requestUri,'?')) { +$requestUri = substr($requestUri, 0, $pos); +} +$pathInfo = substr($requestUri, strlen($baseUrl)); +if (null !== $baseUrl && (false === $pathInfo ||''=== $pathInfo)) { +return'/'; +} elseif (null === $baseUrl) { +return $requestUri; +} +return (string) $pathInfo; +} +protected static function initializeFormats() +{ +static::$formats = array('html'=> array('text/html','application/xhtml+xml'),'txt'=> array('text/plain'),'js'=> array('application/javascript','application/x-javascript','text/javascript'),'css'=> array('text/css'),'json'=> array('application/json','application/x-json'),'xml'=> array('text/xml','application/xml','application/x-xml'),'rdf'=> array('application/rdf+xml'),'atom'=> array('application/atom+xml'),'rss'=> array('application/rss+xml'),'form'=> array('application/x-www-form-urlencoded'), +); +} +private function setPhpDefaultLocale($locale) +{ +try { +if (class_exists('Locale', false)) { +\Locale::setDefault($locale); +} +} catch (\Exception $e) { +} +} +private function getUrlencodedPrefix($string, $prefix) +{ +if (0 !== strpos(rawurldecode($string), $prefix)) { +return false; +} +$len = strlen($prefix); +if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) { +return $match[0]; +} +return false; +} +private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) +{ +if (self::$requestFactory) { +$request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); +if (!$request instanceof self) { +throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); +} +return $request; +} +return new static($query, $request, $attributes, $cookies, $files, $server, $content); +} +private function isFromTrustedProxy() +{ +return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies); +} +} +} +namespace Symfony\Component\HttpFoundation +{ +class Response +{ +const HTTP_CONTINUE = 100; +const HTTP_SWITCHING_PROTOCOLS = 101; +const HTTP_PROCESSING = 102; const HTTP_OK = 200; +const HTTP_CREATED = 201; +const HTTP_ACCEPTED = 202; +const HTTP_NON_AUTHORITATIVE_INFORMATION = 203; +const HTTP_NO_CONTENT = 204; +const HTTP_RESET_CONTENT = 205; +const HTTP_PARTIAL_CONTENT = 206; +const HTTP_MULTI_STATUS = 207; const HTTP_ALREADY_REPORTED = 208; const HTTP_IM_USED = 226; const HTTP_MULTIPLE_CHOICES = 300; +const HTTP_MOVED_PERMANENTLY = 301; +const HTTP_FOUND = 302; +const HTTP_SEE_OTHER = 303; +const HTTP_NOT_MODIFIED = 304; +const HTTP_USE_PROXY = 305; +const HTTP_RESERVED = 306; +const HTTP_TEMPORARY_REDIRECT = 307; +const HTTP_PERMANENTLY_REDIRECT = 308; const HTTP_BAD_REQUEST = 400; +const HTTP_UNAUTHORIZED = 401; +const HTTP_PAYMENT_REQUIRED = 402; +const HTTP_FORBIDDEN = 403; +const HTTP_NOT_FOUND = 404; +const HTTP_METHOD_NOT_ALLOWED = 405; +const HTTP_NOT_ACCEPTABLE = 406; +const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; +const HTTP_REQUEST_TIMEOUT = 408; +const HTTP_CONFLICT = 409; +const HTTP_GONE = 410; +const HTTP_LENGTH_REQUIRED = 411; +const HTTP_PRECONDITION_FAILED = 412; +const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; +const HTTP_REQUEST_URI_TOO_LONG = 414; +const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; +const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; +const HTTP_EXPECTATION_FAILED = 417; +const HTTP_I_AM_A_TEAPOT = 418; const HTTP_UNPROCESSABLE_ENTITY = 422; const HTTP_LOCKED = 423; const HTTP_FAILED_DEPENDENCY = 424; const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; const HTTP_UPGRADE_REQUIRED = 426; const HTTP_PRECONDITION_REQUIRED = 428; const HTTP_TOO_MANY_REQUESTS = 429; const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; const HTTP_INTERNAL_SERVER_ERROR = 500; +const HTTP_NOT_IMPLEMENTED = 501; +const HTTP_BAD_GATEWAY = 502; +const HTTP_SERVICE_UNAVAILABLE = 503; +const HTTP_GATEWAY_TIMEOUT = 504; +const HTTP_VERSION_NOT_SUPPORTED = 505; +const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; const HTTP_INSUFFICIENT_STORAGE = 507; const HTTP_LOOP_DETECTED = 508; const HTTP_NOT_EXTENDED = 510; const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; +public $headers; +protected $content; +protected $version; +protected $statusCode; +protected $statusText; +protected $charset; +public static $statusTexts = array( +100 =>'Continue', +101 =>'Switching Protocols', +102 =>'Processing', 200 =>'OK', +201 =>'Created', +202 =>'Accepted', +203 =>'Non-Authoritative Information', +204 =>'No Content', +205 =>'Reset Content', +206 =>'Partial Content', +207 =>'Multi-Status', 208 =>'Already Reported', 226 =>'IM Used', 300 =>'Multiple Choices', +301 =>'Moved Permanently', +302 =>'Found', +303 =>'See Other', +304 =>'Not Modified', +305 =>'Use Proxy', +307 =>'Temporary Redirect', +308 =>'Permanent Redirect', 400 =>'Bad Request', +401 =>'Unauthorized', +402 =>'Payment Required', +403 =>'Forbidden', +404 =>'Not Found', +405 =>'Method Not Allowed', +406 =>'Not Acceptable', +407 =>'Proxy Authentication Required', +408 =>'Request Timeout', +409 =>'Conflict', +410 =>'Gone', +411 =>'Length Required', +412 =>'Precondition Failed', +413 =>'Payload Too Large', +414 =>'URI Too Long', +415 =>'Unsupported Media Type', +416 =>'Range Not Satisfiable', +417 =>'Expectation Failed', +418 =>'I\'m a teapot', 422 =>'Unprocessable Entity', 423 =>'Locked', 424 =>'Failed Dependency', 425 =>'Reserved for WebDAV advanced collections expired proposal', 426 =>'Upgrade Required', 428 =>'Precondition Required', 429 =>'Too Many Requests', 431 =>'Request Header Fields Too Large', 500 =>'Internal Server Error', +501 =>'Not Implemented', +502 =>'Bad Gateway', +503 =>'Service Unavailable', +504 =>'Gateway Timeout', +505 =>'HTTP Version Not Supported', +506 =>'Variant Also Negotiates (Experimental)', 507 =>'Insufficient Storage', 508 =>'Loop Detected', 510 =>'Not Extended', 511 =>'Network Authentication Required', ); +public function __construct($content ='', $status = 200, $headers = array()) +{ +$this->headers = new ResponseHeaderBag($headers); +$this->setContent($content); +$this->setStatusCode($status); +$this->setProtocolVersion('1.0'); +} +public static function create($content ='', $status = 200, $headers = array()) +{ +return new static($content, $status, $headers); +} +public function __toString() +{ +return +sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n". +$this->headers."\r\n". +$this->getContent(); +} +public function __clone() +{ +$this->headers = clone $this->headers; +} +public function prepare(Request $request) +{ +$headers = $this->headers; +if ($this->isInformational() || $this->isEmpty()) { +$this->setContent(null); +$headers->remove('Content-Type'); +$headers->remove('Content-Length'); +} else { +if (!$headers->has('Content-Type')) { +$format = $request->getRequestFormat(); +if (null !== $format && $mimeType = $request->getMimeType($format)) { +$headers->set('Content-Type', $mimeType); +} +} +$charset = $this->charset ?:'UTF-8'; +if (!$headers->has('Content-Type')) { +$headers->set('Content-Type','text/html; charset='.$charset); +} elseif (0 === stripos($headers->get('Content-Type'),'text/') && false === stripos($headers->get('Content-Type'),'charset')) { +$headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset); +} +if ($headers->has('Transfer-Encoding')) { +$headers->remove('Content-Length'); +} +if ($request->isMethod('HEAD')) { +$length = $headers->get('Content-Length'); +$this->setContent(null); +if ($length) { +$headers->set('Content-Length', $length); +} +} +} +if ('HTTP/1.0'!= $request->server->get('SERVER_PROTOCOL')) { +$this->setProtocolVersion('1.1'); +} +if ('1.0'== $this->getProtocolVersion() &&'no-cache'== $this->headers->get('Cache-Control')) { +$this->headers->set('pragma','no-cache'); +$this->headers->set('expires', -1); +} +$this->ensureIEOverSSLCompatibility($request); +return $this; +} +public function sendHeaders() +{ +if (headers_sent()) { +return $this; +} +if (!$this->headers->has('Date')) { +$this->setDate(\DateTime::createFromFormat('U', time())); +} +foreach ($this->headers->allPreserveCase() as $name => $values) { +foreach ($values as $value) { +header($name.': '.$value, false, $this->statusCode); +} +} +header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); +foreach ($this->headers->getCookies() as $cookie) { +setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); +} +return $this; +} +public function sendContent() +{ +echo $this->content; +return $this; +} +public function send() +{ +$this->sendHeaders(); +$this->sendContent(); +if (function_exists('fastcgi_finish_request')) { +fastcgi_finish_request(); +} elseif ('cli'!== PHP_SAPI) { +static::closeOutputBuffers(0, true); +} +return $this; +} +public function setContent($content) +{ +if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content,'__toString'))) { +throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content))); +} +$this->content = (string) $content; +return $this; +} +public function getContent() +{ +return $this->content; +} +public function setProtocolVersion($version) +{ +$this->version = $version; +return $this; +} +public function getProtocolVersion() +{ +return $this->version; +} +public function setStatusCode($code, $text = null) +{ +$this->statusCode = $code = (int) $code; +if ($this->isInvalid()) { +throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); +} +if (null === $text) { +$this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] :'unknown status'; +return $this; +} +if (false === $text) { +$this->statusText =''; +return $this; +} +$this->statusText = $text; +return $this; +} +public function getStatusCode() +{ +return $this->statusCode; +} +public function setCharset($charset) +{ +$this->charset = $charset; +return $this; +} +public function getCharset() +{ +return $this->charset; +} +public function isCacheable() +{ +if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { +return false; +} +if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) { +return false; +} +return $this->isValidateable() || $this->isFresh(); +} +public function isFresh() +{ +return $this->getTtl() > 0; +} +public function isValidateable() +{ +return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); +} +public function setPrivate() +{ +$this->headers->removeCacheControlDirective('public'); +$this->headers->addCacheControlDirective('private'); +return $this; +} +public function setPublic() +{ +$this->headers->addCacheControlDirective('public'); +$this->headers->removeCacheControlDirective('private'); +return $this; +} +public function mustRevalidate() +{ +return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); +} +public function getDate() +{ +if (!$this->headers->has('Date')) { +$this->setDate(\DateTime::createFromFormat('U', time())); +} +return $this->headers->getDate('Date'); +} +public function setDate(\DateTime $date) +{ +$date->setTimezone(new \DateTimeZone('UTC')); +$this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); +return $this; +} +public function getAge() +{ +if (null !== $age = $this->headers->get('Age')) { +return (int) $age; +} +return max(time() - $this->getDate()->format('U'), 0); +} +public function expire() +{ +if ($this->isFresh()) { +$this->headers->set('Age', $this->getMaxAge()); +} +return $this; +} +public function getExpires() +{ +try { +return $this->headers->getDate('Expires'); +} catch (\RuntimeException $e) { +return \DateTime::createFromFormat(DATE_RFC2822,'Sat, 01 Jan 00 00:00:00 +0000'); +} +} +public function setExpires(\DateTime $date = null) +{ +if (null === $date) { +$this->headers->remove('Expires'); +} else { +$date = clone $date; +$date->setTimezone(new \DateTimeZone('UTC')); +$this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); +} +return $this; +} +public function getMaxAge() +{ +if ($this->headers->hasCacheControlDirective('s-maxage')) { +return (int) $this->headers->getCacheControlDirective('s-maxage'); +} +if ($this->headers->hasCacheControlDirective('max-age')) { +return (int) $this->headers->getCacheControlDirective('max-age'); +} +if (null !== $this->getExpires()) { +return $this->getExpires()->format('U') - $this->getDate()->format('U'); +} +} +public function setMaxAge($value) +{ +$this->headers->addCacheControlDirective('max-age', $value); +return $this; +} +public function setSharedMaxAge($value) +{ +$this->setPublic(); +$this->headers->addCacheControlDirective('s-maxage', $value); +return $this; +} +public function getTtl() +{ +if (null !== $maxAge = $this->getMaxAge()) { +return $maxAge - $this->getAge(); +} +} +public function setTtl($seconds) +{ +$this->setSharedMaxAge($this->getAge() + $seconds); +return $this; +} +public function setClientTtl($seconds) +{ +$this->setMaxAge($this->getAge() + $seconds); +return $this; +} +public function getLastModified() +{ +return $this->headers->getDate('Last-Modified'); +} +public function setLastModified(\DateTime $date = null) +{ +if (null === $date) { +$this->headers->remove('Last-Modified'); +} else { +$date = clone $date; +$date->setTimezone(new \DateTimeZone('UTC')); +$this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); +} +return $this; +} +public function getEtag() +{ +return $this->headers->get('ETag'); +} +public function setEtag($etag = null, $weak = false) +{ +if (null === $etag) { +$this->headers->remove('Etag'); +} else { +if (0 !== strpos($etag,'"')) { +$etag ='"'.$etag.'"'; +} +$this->headers->set('ETag', (true === $weak ?'W/':'').$etag); +} +return $this; +} +public function setCache(array $options) +{ +if ($diff = array_diff(array_keys($options), array('etag','last_modified','max_age','s_maxage','private','public'))) { +throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff)))); +} +if (isset($options['etag'])) { +$this->setEtag($options['etag']); +} +if (isset($options['last_modified'])) { +$this->setLastModified($options['last_modified']); +} +if (isset($options['max_age'])) { +$this->setMaxAge($options['max_age']); +} +if (isset($options['s_maxage'])) { +$this->setSharedMaxAge($options['s_maxage']); +} +if (isset($options['public'])) { +if ($options['public']) { +$this->setPublic(); +} else { +$this->setPrivate(); +} +} +if (isset($options['private'])) { +if ($options['private']) { +$this->setPrivate(); +} else { +$this->setPublic(); +} +} +return $this; +} +public function setNotModified() +{ +$this->setStatusCode(304); +$this->setContent(null); +foreach (array('Allow','Content-Encoding','Content-Language','Content-Length','Content-MD5','Content-Type','Last-Modified') as $header) { +$this->headers->remove($header); +} +return $this; +} +public function hasVary() +{ +return null !== $this->headers->get('Vary'); +} +public function getVary() +{ +if (!$vary = $this->headers->get('Vary', null, false)) { +return array(); +} +$ret = array(); +foreach ($vary as $item) { +$ret = array_merge($ret, preg_split('/[\s,]+/', $item)); +} +return $ret; +} +public function setVary($headers, $replace = true) +{ +$this->headers->set('Vary', $headers, $replace); +return $this; +} +public function isNotModified(Request $request) +{ +if (!$request->isMethodSafe()) { +return false; +} +$notModified = false; +$lastModified = $this->headers->get('Last-Modified'); +$modifiedSince = $request->headers->get('If-Modified-Since'); +if ($etags = $request->getETags()) { +$notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags); +} +if ($modifiedSince && $lastModified) { +$notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified); +} +if ($notModified) { +$this->setNotModified(); +} +return $notModified; +} +public function isInvalid() +{ +return $this->statusCode < 100 || $this->statusCode >= 600; +} +public function isInformational() +{ +return $this->statusCode >= 100 && $this->statusCode < 200; +} +public function isSuccessful() +{ +return $this->statusCode >= 200 && $this->statusCode < 300; +} +public function isRedirection() +{ +return $this->statusCode >= 300 && $this->statusCode < 400; +} +public function isClientError() +{ +return $this->statusCode >= 400 && $this->statusCode < 500; +} +public function isServerError() +{ +return $this->statusCode >= 500 && $this->statusCode < 600; +} +public function isOk() +{ +return 200 === $this->statusCode; +} +public function isForbidden() +{ +return 403 === $this->statusCode; +} +public function isNotFound() +{ +return 404 === $this->statusCode; +} +public function isRedirect($location = null) +{ +return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); +} +public function isEmpty() +{ +return in_array($this->statusCode, array(204, 304)); +} +public static function closeOutputBuffers($targetLevel, $flush) +{ +$status = ob_get_status(true); +$level = count($status); +$flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1; +while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || $flags === ($s['flags'] & $flags) : $s['del'])) { +if ($flush) { +ob_end_flush(); +} else { +ob_end_clean(); +} +} +} +protected function ensureIEOverSSLCompatibility(Request $request) +{ +if (false !== stripos($this->headers->get('Content-Disposition'),'attachment') && preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) == 1 && true === $request->isSecure()) { +if ((int) preg_replace('/(MSIE )(.*?);/','$2', $match[0]) < 9) { +$this->headers->remove('Cache-Control'); +} +} +} +} +} +namespace Symfony\Component\HttpFoundation +{ +class ResponseHeaderBag extends HeaderBag +{ +const COOKIES_FLAT ='flat'; +const COOKIES_ARRAY ='array'; +const DISPOSITION_ATTACHMENT ='attachment'; +const DISPOSITION_INLINE ='inline'; +protected $computedCacheControl = array(); +protected $cookies = array(); +protected $headerNames = array(); +public function __construct(array $headers = array()) +{ +parent::__construct($headers); +if (!isset($this->headers['cache-control'])) { +$this->set('Cache-Control',''); +} +} +public function __toString() +{ +$cookies =''; +foreach ($this->getCookies() as $cookie) { +$cookies .='Set-Cookie: '.$cookie."\r\n"; +} +ksort($this->headerNames); +return parent::__toString().$cookies; +} +public function allPreserveCase() +{ +return array_combine($this->headerNames, $this->headers); +} +public function replace(array $headers = array()) +{ +$this->headerNames = array(); +parent::replace($headers); +if (!isset($this->headers['cache-control'])) { +$this->set('Cache-Control',''); +} +} +public function set($key, $values, $replace = true) +{ +parent::set($key, $values, $replace); +$uniqueKey = strtr(strtolower($key),'_','-'); +$this->headerNames[$uniqueKey] = $key; +if (in_array($uniqueKey, array('cache-control','etag','last-modified','expires'))) { +$computed = $this->computeCacheControlValue(); +$this->headers['cache-control'] = array($computed); +$this->headerNames['cache-control'] ='Cache-Control'; +$this->computedCacheControl = $this->parseCacheControl($computed); +} +} +public function remove($key) +{ +parent::remove($key); +$uniqueKey = strtr(strtolower($key),'_','-'); +unset($this->headerNames[$uniqueKey]); +if ('cache-control'=== $uniqueKey) { +$this->computedCacheControl = array(); +} +} +public function hasCacheControlDirective($key) +{ +return array_key_exists($key, $this->computedCacheControl); +} +public function getCacheControlDirective($key) +{ +return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; +} +public function setCookie(Cookie $cookie) +{ +$this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; +} +public function removeCookie($name, $path ='/', $domain = null) +{ +if (null === $path) { +$path ='/'; +} +unset($this->cookies[$domain][$path][$name]); +if (empty($this->cookies[$domain][$path])) { +unset($this->cookies[$domain][$path]); +if (empty($this->cookies[$domain])) { +unset($this->cookies[$domain]); +} +} +} +public function getCookies($format = self::COOKIES_FLAT) +{ +if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) { +throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY)))); +} +if (self::COOKIES_ARRAY === $format) { +return $this->cookies; +} +$flattenedCookies = array(); +foreach ($this->cookies as $path) { +foreach ($path as $cookies) { +foreach ($cookies as $cookie) { +$flattenedCookies[] = $cookie; +} +} +} +return $flattenedCookies; +} +public function clearCookie($name, $path ='/', $domain = null, $secure = false, $httpOnly = true) +{ +$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly)); +} +public function makeDisposition($disposition, $filename, $filenameFallback ='') +{ +if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) { +throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); +} +if (''== $filenameFallback) { +$filenameFallback = $filename; +} +if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) { +throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); +} +if (false !== strpos($filenameFallback,'%')) { +throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); +} +if (false !== strpos($filename,'/') || false !== strpos($filename,'\\') || false !== strpos($filenameFallback,'/') || false !== strpos($filenameFallback,'\\')) { +throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); +} +$output = sprintf('%s; filename="%s"', $disposition, str_replace('"','\\"', $filenameFallback)); +if ($filename !== $filenameFallback) { +$output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename)); +} +return $output; +} +protected function computeCacheControlValue() +{ +if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { +return'no-cache'; +} +if (!$this->cacheControl) { +return'private, must-revalidate'; +} +$header = $this->getCacheControlHeader(); +if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) { +return $header; +} +if (!isset($this->cacheControl['s-maxage'])) { +return $header.', private'; +} +return $header; +} +} +} +namespace Symfony\Component\DependencyInjection +{ +interface ContainerAwareInterface +{ +public function setContainer(ContainerInterface $container = null); +} +} +namespace Symfony\Component\DependencyInjection +{ +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +interface ContainerInterface +{ +const EXCEPTION_ON_INVALID_REFERENCE = 1; +const NULL_ON_INVALID_REFERENCE = 2; +const IGNORE_ON_INVALID_REFERENCE = 3; +public function set($id, $service); +public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); +public function has($id); +public function initialized($id); +public function getParameter($name); +public function hasParameter($name); +public function setParameter($name, $value); +} +} +namespace Symfony\Component\DependencyInjection +{ +interface ResettableContainerInterface extends ContainerInterface +{ +public function reset(); +} +} +namespace Symfony\Component\DependencyInjection +{ +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; +class Container implements ResettableContainerInterface +{ +protected $parameterBag; +protected $services = array(); +protected $methodMap = array(); +protected $aliases = array(); +protected $loading = array(); +private $underscoreMap = array('_'=>'','.'=>'_','\\'=>'_'); +public function __construct(ParameterBagInterface $parameterBag = null) +{ +$this->parameterBag = $parameterBag ?: new ParameterBag(); +} +public function compile() +{ +$this->parameterBag->resolve(); +$this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); +} +public function isFrozen() +{ +return $this->parameterBag instanceof FrozenParameterBag; +} +public function getParameterBag() +{ +return $this->parameterBag; +} +public function getParameter($name) +{ +return $this->parameterBag->get($name); +} +public function hasParameter($name) +{ +return $this->parameterBag->has($name); +} +public function setParameter($name, $value) +{ +$this->parameterBag->set($name, $value); +} +public function set($id, $service) +{ +$id = strtolower($id); +if ('service_container'=== $id) { +throw new InvalidArgumentException('You cannot set service "service_container".'); +} +$this->services[$id] = $service; +if (null === $service) { +unset($this->services[$id]); +} +} +public function has($id) +{ +for ($i = 2;;) { +if ('service_container'=== $id +|| isset($this->aliases[$id]) +|| isset($this->services[$id]) +|| array_key_exists($id, $this->services) +) { +return true; +} +if (--$i && $id !== $lcId = strtolower($id)) { +$id = $lcId; +} else { +return method_exists($this,'get'.strtr($id, $this->underscoreMap).'Service'); +} +} +} +public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) +{ +for ($i = 2;;) { +if ('service_container'=== $id) { +return $this; +} +if (isset($this->aliases[$id])) { +$id = $this->aliases[$id]; +} +if (isset($this->services[$id]) || array_key_exists($id, $this->services)) { +return $this->services[$id]; +} +if (isset($this->loading[$id])) { +throw new ServiceCircularReferenceException($id, array_keys($this->loading)); +} +if (isset($this->methodMap[$id])) { +$method = $this->methodMap[$id]; +} elseif (--$i && $id !== $lcId = strtolower($id)) { +$id = $lcId; +continue; +} elseif (method_exists($this, $method ='get'.strtr($id, $this->underscoreMap).'Service')) { +} else { +if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { +if (!$id) { +throw new ServiceNotFoundException($id); +} +$alternatives = array(); +foreach ($this->services as $key => $associatedService) { +$lev = levenshtein($id, $key); +if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) { +$alternatives[] = $key; +} +} +throw new ServiceNotFoundException($id, null, null, $alternatives); +} +return; +} +$this->loading[$id] = true; +try { +$service = $this->$method(); +} catch (\Exception $e) { +unset($this->services[$id]); +throw $e; +} finally { +unset($this->loading[$id]); +} +return $service; +} +} +public function initialized($id) +{ +$id = strtolower($id); +if ('service_container'=== $id) { +return false; +} +if (isset($this->aliases[$id])) { +$id = $this->aliases[$id]; +} +return isset($this->services[$id]) || array_key_exists($id, $this->services); +} +public function reset() +{ +$this->services = array(); +} +public function getServiceIds() +{ +$ids = array(); +$r = new \ReflectionClass($this); +foreach ($r->getMethods() as $method) { +if (preg_match('/^get(.+)Service$/', $method->name, $match)) { +$ids[] = self::underscore($match[1]); +} +} +$ids[] ='service_container'; +return array_unique(array_merge($ids, array_keys($this->services))); +} +public static function camelize($id) +{ +return strtr(ucwords(strtr($id, array('_'=>' ','.'=>'_ ','\\'=>'_ '))), array(' '=>'')); +} +public static function underscore($id) +{ +return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/','/([a-z\d])([A-Z])/'), array('\\1_\\2','\\1_\\2'), strtr($id,'_','.'))); +} +private function __clone() +{ +} +} +} +namespace Symfony\Component\HttpKernel +{ +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +interface HttpKernelInterface +{ +const MASTER_REQUEST = 1; +const SUB_REQUEST = 2; +public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); +} +} +namespace Symfony\Component\HttpKernel +{ +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\Config\Loader\LoaderInterface; +interface KernelInterface extends HttpKernelInterface, \Serializable +{ +public function registerBundles(); +public function registerContainerConfiguration(LoaderInterface $loader); +public function boot(); +public function shutdown(); +public function getBundles(); +public function getBundle($name, $first = true); +public function locateResource($name, $dir = null, $first = true); +public function getName(); +public function getEnvironment(); +public function isDebug(); +public function getRootDir(); +public function getContainer(); +public function getStartTime(); +public function getCacheDir(); +public function getLogDir(); +public function getCharset(); +} +} +namespace Symfony\Component\HttpKernel +{ +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +interface TerminableInterface +{ +public function terminate(Request $request, Response $response); +} +} +namespace Symfony\Component\HttpKernel +{ +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Config\EnvParametersResource; +use Symfony\Component\HttpKernel\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; +use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\ClassLoader\ClassCollectionLoader; +abstract class Kernel implements KernelInterface, TerminableInterface +{ +protected $bundles = array(); +protected $bundleMap; +protected $container; +protected $rootDir; +protected $environment; +protected $debug; +protected $booted = false; +protected $name; +protected $startTime; +protected $loadClassCache; +const VERSION ='3.0.1'; +const VERSION_ID = 30001; +const MAJOR_VERSION = 3; +const MINOR_VERSION = 0; +const RELEASE_VERSION = 1; +const EXTRA_VERSION =''; +const END_OF_MAINTENANCE ='07/2016'; +const END_OF_LIFE ='01/2017'; +public function __construct($environment, $debug) +{ +$this->environment = $environment; +$this->debug = (bool) $debug; +$this->rootDir = $this->getRootDir(); +$this->name = $this->getName(); +if ($this->debug) { +$this->startTime = microtime(true); +} +} +public function __clone() +{ +if ($this->debug) { +$this->startTime = microtime(true); +} +$this->booted = false; +$this->container = null; +} +public function boot() +{ +if (true === $this->booted) { +return; +} +if ($this->loadClassCache) { +$this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]); +} +$this->initializeBundles(); +$this->initializeContainer(); +foreach ($this->getBundles() as $bundle) { +$bundle->setContainer($this->container); +$bundle->boot(); +} +$this->booted = true; +} +public function terminate(Request $request, Response $response) +{ +if (false === $this->booted) { +return; +} +if ($this->getHttpKernel() instanceof TerminableInterface) { +$this->getHttpKernel()->terminate($request, $response); +} +} +public function shutdown() +{ +if (false === $this->booted) { +return; +} +$this->booted = false; +foreach ($this->getBundles() as $bundle) { +$bundle->shutdown(); +$bundle->setContainer(null); +} +$this->container = null; +} +public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) +{ +if (false === $this->booted) { +$this->boot(); +} +return $this->getHttpKernel()->handle($request, $type, $catch); +} +protected function getHttpKernel() +{ +return $this->container->get('http_kernel'); +} +public function getBundles() +{ +return $this->bundles; +} +public function getBundle($name, $first = true) +{ +if (!isset($this->bundleMap[$name])) { +throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, get_class($this))); +} +if (true === $first) { +return $this->bundleMap[$name][0]; +} +return $this->bundleMap[$name]; +} +public function locateResource($name, $dir = null, $first = true) +{ +if ('@'!== $name[0]) { +throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); +} +if (false !== strpos($name,'..')) { +throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); +} +$bundleName = substr($name, 1); +$path =''; +if (false !== strpos($bundleName,'/')) { +list($bundleName, $path) = explode('/', $bundleName, 2); +} +$isResource = 0 === strpos($path,'Resources') && null !== $dir; +$overridePath = substr($path, 9); +$resourceBundle = null; +$bundles = $this->getBundle($bundleName, false); +$files = array(); +foreach ($bundles as $bundle) { +if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { +if (null !== $resourceBundle) { +throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', +$file, +$resourceBundle, +$dir.'/'.$bundles[0]->getName().$overridePath +)); +} +if ($first) { +return $file; +} +$files[] = $file; +} +if (file_exists($file = $bundle->getPath().'/'.$path)) { +if ($first && !$isResource) { +return $file; +} +$files[] = $file; +$resourceBundle = $bundle->getName(); +} +} +if (count($files) > 0) { +return $first && $isResource ? $files[0] : $files; +} +throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); +} +public function getName() +{ +if (null === $this->name) { +$this->name = preg_replace('/[^a-zA-Z0-9_]+/','', basename($this->rootDir)); +} +return $this->name; +} +public function getEnvironment() +{ +return $this->environment; +} +public function isDebug() +{ +return $this->debug; +} +public function getRootDir() +{ +if (null === $this->rootDir) { +$r = new \ReflectionObject($this); +$this->rootDir = dirname($r->getFileName()); +} +return $this->rootDir; +} +public function getContainer() +{ +return $this->container; +} +public function loadClassCache($name ='classes', $extension ='.php') +{ +$this->loadClassCache = array($name, $extension); +} +public function setClassCache(array $classes) +{ +file_put_contents($this->getCacheDir().'/classes.map', sprintf('debug ? $this->startTime : -INF; +} +public function getCacheDir() +{ +return $this->rootDir.'/cache/'.$this->environment; +} +public function getLogDir() +{ +return $this->rootDir.'/logs'; +} +public function getCharset() +{ +return'UTF-8'; +} +protected function doLoadClassCache($name, $extension) +{ +if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) { +ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension); +} +} +protected function initializeBundles() +{ +$this->bundles = array(); +$topMostBundles = array(); +$directChildren = array(); +foreach ($this->registerBundles() as $bundle) { +$name = $bundle->getName(); +if (isset($this->bundles[$name])) { +throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); +} +$this->bundles[$name] = $bundle; +if ($parentName = $bundle->getParent()) { +if (isset($directChildren[$parentName])) { +throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); +} +if ($parentName == $name) { +throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name)); +} +$directChildren[$parentName] = $name; +} else { +$topMostBundles[$name] = $bundle; +} +} +if (!empty($directChildren) && count($diff = array_diff_key($directChildren, $this->bundles))) { +$diff = array_keys($diff); +throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); +} +$this->bundleMap = array(); +foreach ($topMostBundles as $name => $bundle) { +$bundleMap = array($bundle); +$hierarchy = array($name); +while (isset($directChildren[$name])) { +$name = $directChildren[$name]; +array_unshift($bundleMap, $this->bundles[$name]); +$hierarchy[] = $name; +} +foreach ($hierarchy as $bundle) { +$this->bundleMap[$bundle] = $bundleMap; +array_pop($bundleMap); +} +} +} +protected function getContainerClass() +{ +return $this->name.ucfirst($this->environment).($this->debug ?'Debug':'').'ProjectContainer'; +} +protected function getContainerBaseClass() +{ +return'Container'; +} +protected function initializeContainer() +{ +$class = $this->getContainerClass(); +$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); +$fresh = true; +if (!$cache->isFresh()) { +$container = $this->buildContainer(); +$container->compile(); +$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); +$fresh = false; +} +require_once $cache->getPath(); +$this->container = new $class(); +$this->container->set('kernel', $this); +if (!$fresh && $this->container->has('cache_warmer')) { +$this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); +} +} +protected function getKernelParameters() +{ +$bundles = array(); +foreach ($this->bundles as $name => $bundle) { +$bundles[$name] = get_class($bundle); +} +return array_merge( +array('kernel.root_dir'=> realpath($this->rootDir) ?: $this->rootDir,'kernel.environment'=> $this->environment,'kernel.debug'=> $this->debug,'kernel.name'=> $this->name,'kernel.cache_dir'=> realpath($this->getCacheDir()) ?: $this->getCacheDir(),'kernel.logs_dir'=> realpath($this->getLogDir()) ?: $this->getLogDir(),'kernel.bundles'=> $bundles,'kernel.charset'=> $this->getCharset(),'kernel.container_class'=> $this->getContainerClass(), +), +$this->getEnvParameters() +); +} +protected function getEnvParameters() +{ +$parameters = array(); +foreach ($_SERVER as $key => $value) { +if (0 === strpos($key,'SYMFONY__')) { +$parameters[strtolower(str_replace('__','.', substr($key, 9)))] = $value; +} +} +return $parameters; +} +protected function buildContainer() +{ +foreach (array('cache'=> $this->getCacheDir(),'logs'=> $this->getLogDir()) as $name => $dir) { +if (!is_dir($dir)) { +if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { +throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); +} +} elseif (!is_writable($dir)) { +throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); +} +} +$container = $this->getContainerBuilder(); +$container->addObjectResource($this); +$this->prepareContainer($container); +if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { +$container->merge($cont); +} +$container->addCompilerPass(new AddClassesToCachePass($this)); +$container->addResource(new EnvParametersResource('SYMFONY__')); +return $container; +} +protected function prepareContainer(ContainerBuilder $container) +{ +$extensions = array(); +foreach ($this->bundles as $bundle) { +if ($extension = $bundle->getContainerExtension()) { +$container->registerExtension($extension); +$extensions[] = $extension->getAlias(); +} +if ($this->debug) { +$container->addObjectResource($bundle); +} +} +foreach ($this->bundles as $bundle) { +$bundle->build($container); +} +$container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); +} +protected function getContainerBuilder() +{ +$container = new ContainerBuilder(new ParameterBag($this->getKernelParameters())); +if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { +$container->setProxyInstantiator(new RuntimeInstantiator()); +} +return $container; +} +protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) +{ +$dumper = new PhpDumper($container); +if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { +$dumper->setProxyDumper(new ProxyDumper(md5($cache->getPath()))); +} +$content = $dumper->dump(array('class'=> $class,'base_class'=> $baseClass,'file'=> $cache->getPath())); +if (!$this->debug) { +$content = static::stripComments($content); +} +$cache->write($content, $container->getResources()); +} +protected function getContainerLoader(ContainerInterface $container) +{ +$locator = new FileLocator($this); +$resolver = new LoaderResolver(array( +new XmlFileLoader($container, $locator), +new YamlFileLoader($container, $locator), +new IniFileLoader($container, $locator), +new PhpFileLoader($container, $locator), +new DirectoryLoader($container, $locator), +new ClosureLoader($container), +)); +return new DelegatingLoader($resolver); +} +public static function stripComments($source) +{ +if (!function_exists('token_get_all')) { +return $source; +} +$rawChunk =''; +$output =''; +$tokens = token_get_all($source); +$ignoreSpace = false; +for (reset($tokens); false !== $token = current($tokens); next($tokens)) { +if (is_string($token)) { +$rawChunk .= $token; +} elseif (T_START_HEREDOC === $token[0]) { +$output .= $rawChunk.$token[1]; +do { +$token = next($tokens); +$output .= $token[1]; +} while ($token[0] !== T_END_HEREDOC); +$rawChunk =''; +} elseif (T_WHITESPACE === $token[0]) { +if ($ignoreSpace) { +$ignoreSpace = false; +continue; +} +$rawChunk .= preg_replace(array('/\n{2,}/S'),"\n", $token[1]); +} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { +$ignoreSpace = true; +} else { +$rawChunk .= $token[1]; +if (T_OPEN_TAG === $token[0]) { +$ignoreSpace = true; +} +} +} +$output .= $rawChunk; +return $output; +} +public function serialize() +{ +return serialize(array($this->environment, $this->debug)); +} +public function unserialize($data) +{ +list($environment, $debug) = unserialize($data); +$this->__construct($environment, $debug); +} +} +} +namespace Symfony\Component\ClassLoader +{ +class ApcClassLoader +{ +private $prefix; +protected $decorated; +public function __construct($prefix, $decorated) +{ +if (!extension_loaded('apc')) { +throw new \RuntimeException('Unable to use ApcClassLoader as APC is not enabled.'); +} +if (!method_exists($decorated,'findFile')) { +throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); +} +$this->prefix = $prefix; +$this->decorated = $decorated; +} +public function register($prepend = false) +{ +spl_autoload_register(array($this,'loadClass'), true, $prepend); +} +public function unregister() +{ +spl_autoload_unregister(array($this,'loadClass')); +} +public function loadClass($class) +{ +if ($file = $this->findFile($class)) { +require $file; +return true; +} +} +public function findFile($class) +{ +if (false === $file = apc_fetch($this->prefix.$class)) { +apc_store($this->prefix.$class, $file = $this->decorated->findFile($class)); +} +return $file; +} +public function __call($method, $args) +{ +return call_user_func_array(array($this->decorated, $method), $args); +} +} +} +namespace Symfony\Component\HttpKernel\Bundle +{ +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +interface BundleInterface extends ContainerAwareInterface +{ +public function boot(); +public function shutdown(); +public function build(ContainerBuilder $container); +public function getContainerExtension(); +public function getParent(); +public function getName(); +public function getNamespace(); +public function getPath(); +} +} +namespace Symfony\Component\DependencyInjection +{ +trait ContainerAwareTrait +{ +protected $container; +public function setContainer(ContainerInterface $container = null) +{ +$this->container = $container; +} +} +} +namespace Symfony\Component\HttpKernel\Bundle +{ +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Console\Application; +use Symfony\Component\Finder\Finder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +abstract class Bundle implements BundleInterface +{ +use ContainerAwareTrait; +protected $name; +protected $extension; +protected $path; +public function boot() +{ +} +public function shutdown() +{ +} +public function build(ContainerBuilder $container) +{ +} +public function getContainerExtension() +{ +if (null === $this->extension) { +$extension = $this->createContainerExtension(); +if (null !== $extension) { +if (!$extension instanceof ExtensionInterface) { +throw new \LogicException(sprintf('Extension %s must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface.', get_class($extension))); +} +$basename = preg_replace('/Bundle$/','', $this->getName()); +$expectedAlias = Container::underscore($basename); +if ($expectedAlias != $extension->getAlias()) { +throw new \LogicException(sprintf('Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.', +$expectedAlias, $extension->getAlias() +)); +} +$this->extension = $extension; +} else { +$this->extension = false; +} +} +if ($this->extension) { +return $this->extension; +} +} +public function getNamespace() +{ +$class = get_class($this); +return substr($class, 0, strrpos($class,'\\')); +} +public function getPath() +{ +if (null === $this->path) { +$reflected = new \ReflectionObject($this); +$this->path = dirname($reflected->getFileName()); +} +return $this->path; +} +public function getParent() +{ +} +final public function getName() +{ +if (null !== $this->name) { +return $this->name; +} +$name = get_class($this); +$pos = strrpos($name,'\\'); +return $this->name = false === $pos ? $name : substr($name, $pos + 1); +} +public function registerCommands(Application $application) +{ +if (!is_dir($dir = $this->getPath().'/Command')) { +return; +} +if (!class_exists('Symfony\Component\Finder\Finder')) { +throw new \RuntimeException('You need the symfony/finder component to register bundle commands.'); +} +$finder = new Finder(); +$finder->files()->name('*Command.php')->in($dir); +$prefix = $this->getNamespace().'\\Command'; +foreach ($finder as $file) { +$ns = $prefix; +if ($relativePath = $file->getRelativePath()) { +$ns .='\\'.strtr($relativePath,'/','\\'); +} +$class = $ns.'\\'.$file->getBasename('.php'); +if ($this->container) { +$alias ='console.command.'.strtolower(str_replace('\\','_', $class)); +if ($this->container->has($alias)) { +continue; +} +} +$r = new \ReflectionClass($class); +if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { +$application->add($r->newInstance()); +} +} +} +protected function getContainerExtensionClass() +{ +$basename = preg_replace('/Bundle$/','', $this->getName()); +return $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; +} +protected function createContainerExtension() +{ +if (class_exists($class = $this->getContainerExtensionClass())) { +return new $class(); +} +} +} +} +namespace Symfony\Component\Config +{ +use Symfony\Component\Config\Resource\ResourceInterface; +interface ConfigCacheInterface +{ +public function getPath(); +public function isFresh(); +public function write($content, array $metadata = null); +} +} +namespace Symfony\Component\Config +{ +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; +class ResourceCheckerConfigCache implements ConfigCacheInterface +{ +private $file; +private $resourceCheckers; +public function __construct($file, array $resourceCheckers = array()) +{ +$this->file = $file; +$this->resourceCheckers = $resourceCheckers; +} +public function getPath() +{ +return $this->file; +} +public function isFresh() +{ +if (!is_file($this->file)) { +return false; +} +if (!$this->resourceCheckers) { +return true; } +$metadata = $this->getMetaFile(); +if (!is_file($metadata)) { +return true; +} +$time = filemtime($this->file); +$meta = unserialize(file_get_contents($metadata)); +foreach ($meta as $resource) { +foreach ($this->resourceCheckers as $checker) { +if (!$checker->supports($resource)) { +continue; } +if ($checker->isFresh($resource, $time)) { +break; } +return false; } +} +return true; +} +public function write($content, array $metadata = null) +{ +$mode = 0666; +$umask = umask(); +$filesystem = new Filesystem(); +$filesystem->dumpFile($this->file, $content, null); +try { +$filesystem->chmod($this->file, $mode, $umask); +} catch (IOException $e) { +} +if (null !== $metadata) { +$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null); +try { +$filesystem->chmod($this->getMetaFile(), $mode, $umask); +} catch (IOException $e) { +} +} +} +private function getMetaFile() +{ +return $this->file.'.meta'; +} +} +} +namespace Symfony\Component\Config +{ +use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; +class ConfigCache extends ResourceCheckerConfigCache +{ +private $debug; +public function __construct($file, $debug) +{ +parent::__construct($file, array( +new SelfCheckingResourceChecker(), +)); +$this->debug = (bool) $debug; +} +public function isFresh() +{ +if (!$this->debug && is_file($this->getPath())) { +return true; +} +return parent::isFresh(); +} +} +} +namespace Symfony\Component\HttpKernel +{ +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +class HttpKernel implements HttpKernelInterface, TerminableInterface +{ +protected $dispatcher; +protected $resolver; +protected $requestStack; +public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null) +{ +$this->dispatcher = $dispatcher; +$this->resolver = $resolver; +$this->requestStack = $requestStack ?: new RequestStack(); +} +public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) +{ +$request->headers->set('X-Php-Ob-Level', ob_get_level()); +try { +return $this->handleRaw($request, $type); +} catch (\Exception $e) { +if (false === $catch) { +$this->finishRequest($request, $type); +throw $e; +} +return $this->handleException($e, $request, $type); +} +} +public function terminate(Request $request, Response $response) +{ +$this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response)); +} +public function terminateWithException(\Exception $exception) +{ +if (!$request = $this->requestStack->getMasterRequest()) { +throw new \LogicException('Request stack is empty', 0, $exception); +} +$response = $this->handleException($exception, $request, self::MASTER_REQUEST); +$response->sendHeaders(); +$response->sendContent(); +$this->terminate($request, $response); +} +private function handleRaw(Request $request, $type = self::MASTER_REQUEST) +{ +$this->requestStack->push($request); +$event = new GetResponseEvent($this, $request, $type); +$this->dispatcher->dispatch(KernelEvents::REQUEST, $event); +if ($event->hasResponse()) { +return $this->filterResponse($event->getResponse(), $request, $type); +} +if (false === $controller = $this->resolver->getController($request)) { +throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo())); +} +$event = new FilterControllerEvent($this, $controller, $request, $type); +$this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event); +$controller = $event->getController(); +$arguments = $this->resolver->getArguments($request, $controller); +$response = call_user_func_array($controller, $arguments); +if (!$response instanceof Response) { +$event = new GetResponseForControllerResultEvent($this, $request, $type, $response); +$this->dispatcher->dispatch(KernelEvents::VIEW, $event); +if ($event->hasResponse()) { +$response = $event->getResponse(); +} +if (!$response instanceof Response) { +$msg = sprintf('The controller must return a response (%s given).', $this->varToString($response)); +if (null === $response) { +$msg .=' Did you forget to add a return statement somewhere in your controller?'; +} +throw new \LogicException($msg); +} +} +return $this->filterResponse($response, $request, $type); +} +private function filterResponse(Response $response, Request $request, $type) +{ +$event = new FilterResponseEvent($this, $request, $type, $response); +$this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); +$this->finishRequest($request, $type); +return $event->getResponse(); +} +private function finishRequest(Request $request, $type) +{ +$this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type)); +$this->requestStack->pop(); +} +private function handleException(\Exception $e, $request, $type) +{ +$event = new GetResponseForExceptionEvent($this, $request, $type, $e); +$this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); +$e = $event->getException(); +if (!$event->hasResponse()) { +$this->finishRequest($request, $type); +throw $e; +} +$response = $event->getResponse(); +if ($response->headers->has('X-Status-Code')) { +$response->setStatusCode($response->headers->get('X-Status-Code')); +$response->headers->remove('X-Status-Code'); +} elseif (!$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) { +if ($e instanceof HttpExceptionInterface) { +$response->setStatusCode($e->getStatusCode()); +$response->headers->add($e->getHeaders()); +} else { +$response->setStatusCode(500); +} +} +try { +return $this->filterResponse($response, $request, $type); +} catch (\Exception $e) { +return $response; +} +} +private function varToString($var) +{ +if (is_object($var)) { +return sprintf('Object(%s)', get_class($var)); +} +if (is_array($var)) { +$a = array(); +foreach ($var as $k => $v) { +$a[] = sprintf('%s => %s', $k, $this->varToString($v)); +} +return sprintf('Array(%s)', implode(', ', $a)); +} +if (is_resource($var)) { +return sprintf('Resource(%s)', get_resource_type($var)); +} +if (null === $var) { +return'null'; +} +if (false === $var) { +return'false'; +} +if (true === $var) { +return'true'; +} +return (string) $var; +} +} +} diff --git a/var/cache/.gitkeep b/var/cache/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/var/cache/dev/annotations/59/5b41707042756e646c655c456e746974795c466b496e6465784e616d6554657374405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/59/5b41707042756e646c655c456e746974795c466b496e6465784e616d6554657374405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..abd1dfe --- /dev/null +++ b/var/cache/dev/annotations/59/5b41707042756e646c655c456e746974795c466b496e6465784e616d6554657374405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +a:2:{i:0;O:27:"Doctrine\ORM\Mapping\Entity":2:{s:15:"repositoryClass";N;s:8:"readOnly";b:0;}i:1;O:26:"Doctrine\ORM\Mapping\Table":5:{s:4:"name";s:8:"test_one";s:6:"schema";N;s:7:"indexes";N;s:17:"uniqueConstraints";N;s:7:"options";a:0:{}}} \ No newline at end of file diff --git a/var/cache/dev/annotations/65/5b41707042756e646c655c456e746974795c466b496e6465784e616d655465737432246964405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/65/5b41707042756e646c655c456e746974795c466b496e6465784e616d655465737432246964405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..7da0e5c --- /dev/null +++ b/var/cache/dev/annotations/65/5b41707042756e646c655c456e746974795c466b496e6465784e616d655465737432246964405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +a:3:{i:0;O:23:"Doctrine\ORM\Mapping\Id":0:{}i:1;O:27:"Doctrine\ORM\Mapping\Column":9:{s:4:"name";N;s:4:"type";s:7:"integer";s:6:"length";N;s:9:"precision";i:0;s:5:"scale";i:0;s:6:"unique";b:0;s:8:"nullable";b:0;s:7:"options";a:0:{}s:16:"columnDefinition";N;}i:2;O:35:"Doctrine\ORM\Mapping\GeneratedValue":1:{s:8:"strategy";s:4:"AUTO";}} \ No newline at end of file diff --git a/var/cache/dev/annotations/6b/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d65546573742474657374405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/6b/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d65546573742474657374405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..6c7e3ae --- /dev/null +++ b/var/cache/dev/annotations/6b/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d65546573742474657374405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +i:1453823629; \ No newline at end of file diff --git a/var/cache/dev/annotations/6c/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d6554657374246964405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/6c/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d6554657374246964405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..6c7e3ae --- /dev/null +++ b/var/cache/dev/annotations/6c/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d6554657374246964405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +i:1453823629; \ No newline at end of file diff --git a/var/cache/dev/annotations/72/5b41707042756e646c655c456e746974795c466b496e6465784e616d6554657374246964405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/72/5b41707042756e646c655c456e746974795c466b496e6465784e616d6554657374246964405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..7da0e5c --- /dev/null +++ b/var/cache/dev/annotations/72/5b41707042756e646c655c456e746974795c466b496e6465784e616d6554657374246964405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +a:3:{i:0;O:23:"Doctrine\ORM\Mapping\Id":0:{}i:1;O:27:"Doctrine\ORM\Mapping\Column":9:{s:4:"name";N;s:4:"type";s:7:"integer";s:6:"length";N;s:9:"precision";i:0;s:5:"scale";i:0;s:6:"unique";b:0;s:8:"nullable";b:0;s:7:"options";a:0:{}s:16:"columnDefinition";N;}i:2;O:35:"Doctrine\ORM\Mapping\GeneratedValue":1:{s:8:"strategy";s:4:"AUTO";}} \ No newline at end of file diff --git a/var/cache/dev/annotations/7d/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d655465737432246964405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/7d/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d655465737432246964405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..a98a0ea --- /dev/null +++ b/var/cache/dev/annotations/7d/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d655465737432246964405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +i:1453818640; \ No newline at end of file diff --git a/var/cache/dev/annotations/ac/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d655465737432405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/ac/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d655465737432405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..93fba91 --- /dev/null +++ b/var/cache/dev/annotations/ac/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d655465737432405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +i:1453817023; \ No newline at end of file diff --git a/var/cache/dev/annotations/b0/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d6554657374405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/b0/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d6554657374405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..6c7e3ae --- /dev/null +++ b/var/cache/dev/annotations/b0/5b5b435d41707042756e646c655c456e746974795c466b496e6465784e616d6554657374405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +i:1453823629; \ No newline at end of file diff --git a/var/cache/dev/annotations/cf/5b41707042756e646c655c456e746974795c466b496e6465784e616d65546573742474657374405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/cf/5b41707042756e646c655c456e746974795c466b496e6465784e616d65546573742474657374405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..15ad3c1 --- /dev/null +++ b/var/cache/dev/annotations/cf/5b41707042756e646c655c456e746974795c466b496e6465784e616d65546573742474657374405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +a:1:{i:0;O:30:"Doctrine\ORM\Mapping\ManyToOne":4:{s:12:"targetEntity";s:16:"FkIndexNameTest2";s:7:"cascade";N;s:5:"fetch";s:4:"LAZY";s:10:"inversedBy";N;}} \ No newline at end of file diff --git a/var/cache/dev/annotations/de/5b41707042756e646c655c456e746974795c466b496e6465784e616d655465737432405b416e6e6f745d5d5b315d.doctrinecache.data b/var/cache/dev/annotations/de/5b41707042756e646c655c456e746974795c466b496e6465784e616d655465737432405b416e6e6f745d5d5b315d.doctrinecache.data new file mode 100644 index 0000000..5fe77a8 --- /dev/null +++ b/var/cache/dev/annotations/de/5b41707042756e646c655c456e746974795c466b496e6465784e616d655465737432405b416e6e6f745d5d5b315d.doctrinecache.data @@ -0,0 +1,2 @@ +0 +a:2:{i:0;O:27:"Doctrine\ORM\Mapping\Entity":2:{s:15:"repositoryClass";N;s:8:"readOnly";b:0;}i:1;O:26:"Doctrine\ORM\Mapping\Table":5:{s:4:"name";s:8:"test_two";s:6:"schema";N;s:7:"indexes";N;s:17:"uniqueConstraints";N;s:7:"options";a:0:{}}} \ No newline at end of file diff --git a/var/cache/dev/appDevDebugProjectContainer.php b/var/cache/dev/appDevDebugProjectContainer.php new file mode 100644 index 0000000..81de588 --- /dev/null +++ b/var/cache/dev/appDevDebugProjectContainer.php @@ -0,0 +1,3764 @@ +targetDirs[$i] = $dir = dirname($dir); + } + $this->parameters = $this->getDefaultParameters(); + + $this->services = array(); + $this->methodMap = array( + 'annotation_reader' => 'getAnnotationReaderService', + 'assets.context' => 'getAssets_ContextService', + 'assets.packages' => 'getAssets_PackagesService', + 'cache_clearer' => 'getCacheClearerService', + 'cache_warmer' => 'getCacheWarmerService', + 'config_cache_factory' => 'getConfigCacheFactoryService', + 'controller_name_converter' => 'getControllerNameConverterService', + 'data_collector.dump' => 'getDataCollector_DumpService', + 'data_collector.form' => 'getDataCollector_FormService', + 'data_collector.form.extractor' => 'getDataCollector_Form_ExtractorService', + 'data_collector.request' => 'getDataCollector_RequestService', + 'data_collector.router' => 'getDataCollector_RouterService', + 'debug.controller_resolver' => 'getDebug_ControllerResolverService', + 'debug.debug_handlers_listener' => 'getDebug_DebugHandlersListenerService', + 'debug.dump_listener' => 'getDebug_DumpListenerService', + 'debug.event_dispatcher' => 'getDebug_EventDispatcherService', + 'debug.stopwatch' => 'getDebug_StopwatchService', + 'doctrine' => 'getDoctrineService', + 'doctrine.dbal.connection_factory' => 'getDoctrine_Dbal_ConnectionFactoryService', + 'doctrine.dbal.default_connection' => 'getDoctrine_Dbal_DefaultConnectionService', + 'doctrine.dbal.logger.profiling.default' => 'getDoctrine_Dbal_Logger_Profiling_DefaultService', + 'doctrine.orm.default_entity_listener_resolver' => 'getDoctrine_Orm_DefaultEntityListenerResolverService', + 'doctrine.orm.default_entity_manager' => 'getDoctrine_Orm_DefaultEntityManagerService', + 'doctrine.orm.default_listeners.attach_entity_listeners' => 'getDoctrine_Orm_DefaultListeners_AttachEntityListenersService', + 'doctrine.orm.default_manager_configurator' => 'getDoctrine_Orm_DefaultManagerConfiguratorService', + 'doctrine.orm.validator.unique' => 'getDoctrine_Orm_Validator_UniqueService', + 'doctrine.orm.validator_initializer' => 'getDoctrine_Orm_ValidatorInitializerService', + 'doctrine_cache.providers.doctrine.orm.default_metadata_cache' => 'getDoctrineCache_Providers_Doctrine_Orm_DefaultMetadataCacheService', + 'doctrine_cache.providers.doctrine.orm.default_query_cache' => 'getDoctrineCache_Providers_Doctrine_Orm_DefaultQueryCacheService', + 'doctrine_cache.providers.doctrine.orm.default_result_cache' => 'getDoctrineCache_Providers_Doctrine_Orm_DefaultResultCacheService', + 'file_locator' => 'getFileLocatorService', + 'filesystem' => 'getFilesystemService', + 'form.factory' => 'getForm_FactoryService', + 'form.registry' => 'getForm_RegistryService', + 'form.resolved_type_factory' => 'getForm_ResolvedTypeFactoryService', + 'form.type.birthday' => 'getForm_Type_BirthdayService', + 'form.type.button' => 'getForm_Type_ButtonService', + 'form.type.checkbox' => 'getForm_Type_CheckboxService', + 'form.type.choice' => 'getForm_Type_ChoiceService', + 'form.type.collection' => 'getForm_Type_CollectionService', + 'form.type.country' => 'getForm_Type_CountryService', + 'form.type.currency' => 'getForm_Type_CurrencyService', + 'form.type.date' => 'getForm_Type_DateService', + 'form.type.datetime' => 'getForm_Type_DatetimeService', + 'form.type.email' => 'getForm_Type_EmailService', + 'form.type.entity' => 'getForm_Type_EntityService', + 'form.type.file' => 'getForm_Type_FileService', + 'form.type.form' => 'getForm_Type_FormService', + 'form.type.hidden' => 'getForm_Type_HiddenService', + 'form.type.integer' => 'getForm_Type_IntegerService', + 'form.type.language' => 'getForm_Type_LanguageService', + 'form.type.locale' => 'getForm_Type_LocaleService', + 'form.type.money' => 'getForm_Type_MoneyService', + 'form.type.number' => 'getForm_Type_NumberService', + 'form.type.password' => 'getForm_Type_PasswordService', + 'form.type.percent' => 'getForm_Type_PercentService', + 'form.type.radio' => 'getForm_Type_RadioService', + 'form.type.range' => 'getForm_Type_RangeService', + 'form.type.repeated' => 'getForm_Type_RepeatedService', + 'form.type.reset' => 'getForm_Type_ResetService', + 'form.type.search' => 'getForm_Type_SearchService', + 'form.type.submit' => 'getForm_Type_SubmitService', + 'form.type.text' => 'getForm_Type_TextService', + 'form.type.textarea' => 'getForm_Type_TextareaService', + 'form.type.time' => 'getForm_Type_TimeService', + 'form.type.timezone' => 'getForm_Type_TimezoneService', + 'form.type.url' => 'getForm_Type_UrlService', + 'form.type_extension.csrf' => 'getForm_TypeExtension_CsrfService', + 'form.type_extension.form.data_collector' => 'getForm_TypeExtension_Form_DataCollectorService', + 'form.type_extension.form.http_foundation' => 'getForm_TypeExtension_Form_HttpFoundationService', + 'form.type_extension.form.validator' => 'getForm_TypeExtension_Form_ValidatorService', + 'form.type_extension.repeated.validator' => 'getForm_TypeExtension_Repeated_ValidatorService', + 'form.type_extension.submit.validator' => 'getForm_TypeExtension_Submit_ValidatorService', + 'form.type_guesser.doctrine' => 'getForm_TypeGuesser_DoctrineService', + 'form.type_guesser.validator' => 'getForm_TypeGuesser_ValidatorService', + 'fragment.handler' => 'getFragment_HandlerService', + 'fragment.listener' => 'getFragment_ListenerService', + 'fragment.renderer.esi' => 'getFragment_Renderer_EsiService', + 'fragment.renderer.hinclude' => 'getFragment_Renderer_HincludeService', + 'fragment.renderer.inline' => 'getFragment_Renderer_InlineService', + 'fragment.renderer.ssi' => 'getFragment_Renderer_SsiService', + 'http_kernel' => 'getHttpKernelService', + 'kernel' => 'getKernelService', + 'kernel.class_cache.cache_warmer' => 'getKernel_ClassCache_CacheWarmerService', + 'locale_listener' => 'getLocaleListenerService', + 'logger' => 'getLoggerService', + 'monolog.handler.console' => 'getMonolog_Handler_ConsoleService', + 'monolog.handler.debug' => 'getMonolog_Handler_DebugService', + 'monolog.handler.main' => 'getMonolog_Handler_MainService', + 'monolog.logger.doctrine' => 'getMonolog_Logger_DoctrineService', + 'monolog.logger.event' => 'getMonolog_Logger_EventService', + 'monolog.logger.php' => 'getMonolog_Logger_PhpService', + 'monolog.logger.profiler' => 'getMonolog_Logger_ProfilerService', + 'monolog.logger.request' => 'getMonolog_Logger_RequestService', + 'monolog.logger.router' => 'getMonolog_Logger_RouterService', + 'monolog.logger.security' => 'getMonolog_Logger_SecurityService', + 'monolog.logger.templating' => 'getMonolog_Logger_TemplatingService', + 'monolog.logger.translation' => 'getMonolog_Logger_TranslationService', + 'profiler' => 'getProfilerService', + 'profiler_listener' => 'getProfilerListenerService', + 'property_accessor' => 'getPropertyAccessorService', + 'request_stack' => 'getRequestStackService', + 'response_listener' => 'getResponseListenerService', + 'router' => 'getRouterService', + 'router.request_context' => 'getRouter_RequestContextService', + 'router_listener' => 'getRouterListenerService', + 'routing.loader' => 'getRouting_LoaderService', + 'security.access.decision_manager' => 'getSecurity_Access_DecisionManagerService', + 'security.authentication.guard_handler' => 'getSecurity_Authentication_GuardHandlerService', + 'security.authentication.manager' => 'getSecurity_Authentication_ManagerService', + 'security.authentication.trust_resolver' => 'getSecurity_Authentication_TrustResolverService', + 'security.authentication_utils' => 'getSecurity_AuthenticationUtilsService', + 'security.authorization_checker' => 'getSecurity_AuthorizationCheckerService', + 'security.csrf.token_manager' => 'getSecurity_Csrf_TokenManagerService', + 'security.encoder_factory' => 'getSecurity_EncoderFactoryService', + 'security.firewall' => 'getSecurity_FirewallService', + 'security.firewall.map.context.dev' => 'getSecurity_Firewall_Map_Context_DevService', + 'security.firewall.map.context.main' => 'getSecurity_Firewall_Map_Context_MainService', + 'security.logout_url_generator' => 'getSecurity_LogoutUrlGeneratorService', + 'security.password_encoder' => 'getSecurity_PasswordEncoderService', + 'security.rememberme.response_listener' => 'getSecurity_Rememberme_ResponseListenerService', + 'security.role_hierarchy' => 'getSecurity_RoleHierarchyService', + 'security.token_storage' => 'getSecurity_TokenStorageService', + 'security.user_checker.main' => 'getSecurity_UserChecker_MainService', + 'security.validator.user_password' => 'getSecurity_Validator_UserPasswordService', + 'sensio_distribution.security_checker' => 'getSensioDistribution_SecurityCheckerService', + 'sensio_distribution.security_checker.command' => 'getSensioDistribution_SecurityChecker_CommandService', + 'sensio_framework_extra.cache.listener' => 'getSensioFrameworkExtra_Cache_ListenerService', + 'sensio_framework_extra.controller.listener' => 'getSensioFrameworkExtra_Controller_ListenerService', + 'sensio_framework_extra.converter.datetime' => 'getSensioFrameworkExtra_Converter_DatetimeService', + 'sensio_framework_extra.converter.doctrine.orm' => 'getSensioFrameworkExtra_Converter_Doctrine_OrmService', + 'sensio_framework_extra.converter.listener' => 'getSensioFrameworkExtra_Converter_ListenerService', + 'sensio_framework_extra.converter.manager' => 'getSensioFrameworkExtra_Converter_ManagerService', + 'sensio_framework_extra.security.listener' => 'getSensioFrameworkExtra_Security_ListenerService', + 'sensio_framework_extra.view.guesser' => 'getSensioFrameworkExtra_View_GuesserService', + 'sensio_framework_extra.view.listener' => 'getSensioFrameworkExtra_View_ListenerService', + 'service_container' => 'getServiceContainerService', + 'session' => 'getSessionService', + 'session.save_listener' => 'getSession_SaveListenerService', + 'session.storage.filesystem' => 'getSession_Storage_FilesystemService', + 'session.storage.metadata_bag' => 'getSession_Storage_MetadataBagService', + 'session.storage.native' => 'getSession_Storage_NativeService', + 'session.storage.php_bridge' => 'getSession_Storage_PhpBridgeService', + 'session_listener' => 'getSessionListenerService', + 'streamed_response_listener' => 'getStreamedResponseListenerService', + 'swiftmailer.email_sender.listener' => 'getSwiftmailer_EmailSender_ListenerService', + 'swiftmailer.mailer.default' => 'getSwiftmailer_Mailer_DefaultService', + 'swiftmailer.mailer.default.plugin.messagelogger' => 'getSwiftmailer_Mailer_Default_Plugin_MessageloggerService', + 'swiftmailer.mailer.default.spool' => 'getSwiftmailer_Mailer_Default_SpoolService', + 'swiftmailer.mailer.default.transport' => 'getSwiftmailer_Mailer_Default_TransportService', + 'swiftmailer.mailer.default.transport.eventdispatcher' => 'getSwiftmailer_Mailer_Default_Transport_EventdispatcherService', + 'swiftmailer.mailer.default.transport.real' => 'getSwiftmailer_Mailer_Default_Transport_RealService', + 'templating' => 'getTemplatingService', + 'templating.filename_parser' => 'getTemplating_FilenameParserService', + 'templating.helper.logout_url' => 'getTemplating_Helper_LogoutUrlService', + 'templating.helper.security' => 'getTemplating_Helper_SecurityService', + 'templating.loader' => 'getTemplating_LoaderService', + 'templating.locator' => 'getTemplating_LocatorService', + 'templating.name_parser' => 'getTemplating_NameParserService', + 'translation.dumper.csv' => 'getTranslation_Dumper_CsvService', + 'translation.dumper.ini' => 'getTranslation_Dumper_IniService', + 'translation.dumper.json' => 'getTranslation_Dumper_JsonService', + 'translation.dumper.mo' => 'getTranslation_Dumper_MoService', + 'translation.dumper.php' => 'getTranslation_Dumper_PhpService', + 'translation.dumper.po' => 'getTranslation_Dumper_PoService', + 'translation.dumper.qt' => 'getTranslation_Dumper_QtService', + 'translation.dumper.res' => 'getTranslation_Dumper_ResService', + 'translation.dumper.xliff' => 'getTranslation_Dumper_XliffService', + 'translation.dumper.yml' => 'getTranslation_Dumper_YmlService', + 'translation.extractor' => 'getTranslation_ExtractorService', + 'translation.extractor.php' => 'getTranslation_Extractor_PhpService', + 'translation.loader' => 'getTranslation_LoaderService', + 'translation.loader.csv' => 'getTranslation_Loader_CsvService', + 'translation.loader.dat' => 'getTranslation_Loader_DatService', + 'translation.loader.ini' => 'getTranslation_Loader_IniService', + 'translation.loader.json' => 'getTranslation_Loader_JsonService', + 'translation.loader.mo' => 'getTranslation_Loader_MoService', + 'translation.loader.php' => 'getTranslation_Loader_PhpService', + 'translation.loader.po' => 'getTranslation_Loader_PoService', + 'translation.loader.qt' => 'getTranslation_Loader_QtService', + 'translation.loader.res' => 'getTranslation_Loader_ResService', + 'translation.loader.xliff' => 'getTranslation_Loader_XliffService', + 'translation.loader.yml' => 'getTranslation_Loader_YmlService', + 'translation.writer' => 'getTranslation_WriterService', + 'translator' => 'getTranslatorService', + 'translator.default' => 'getTranslator_DefaultService', + 'translator.selector' => 'getTranslator_SelectorService', + 'translator_listener' => 'getTranslatorListenerService', + 'twig' => 'getTwigService', + 'twig.controller.exception' => 'getTwig_Controller_ExceptionService', + 'twig.controller.preview_error' => 'getTwig_Controller_PreviewErrorService', + 'twig.exception_listener' => 'getTwig_ExceptionListenerService', + 'twig.loader' => 'getTwig_LoaderService', + 'twig.profile' => 'getTwig_ProfileService', + 'twig.translation.extractor' => 'getTwig_Translation_ExtractorService', + 'uri_signer' => 'getUriSignerService', + 'validator' => 'getValidatorService', + 'validator.builder' => 'getValidator_BuilderService', + 'validator.email' => 'getValidator_EmailService', + 'validator.expression' => 'getValidator_ExpressionService', + 'var_dumper.cli_dumper' => 'getVarDumper_CliDumperService', + 'var_dumper.cloner' => 'getVarDumper_ClonerService', + 'web_profiler.controller.exception' => 'getWebProfiler_Controller_ExceptionService', + 'web_profiler.controller.profiler' => 'getWebProfiler_Controller_ProfilerService', + 'web_profiler.controller.router' => 'getWebProfiler_Controller_RouterService', + 'web_profiler.debug_toolbar' => 'getWebProfiler_DebugToolbarService', + ); + $this->aliases = array( + 'console.command.sensiolabs_security_command_securitycheckercommand' => 'sensio_distribution.security_checker.command', + 'database_connection' => 'doctrine.dbal.default_connection', + 'doctrine.orm.default_metadata_cache' => 'doctrine_cache.providers.doctrine.orm.default_metadata_cache', + 'doctrine.orm.default_query_cache' => 'doctrine_cache.providers.doctrine.orm.default_query_cache', + 'doctrine.orm.default_result_cache' => 'doctrine_cache.providers.doctrine.orm.default_result_cache', + 'doctrine.orm.entity_manager' => 'doctrine.orm.default_entity_manager', + 'event_dispatcher' => 'debug.event_dispatcher', + 'mailer' => 'swiftmailer.mailer.default', + 'session.storage' => 'session.storage.native', + 'swiftmailer.mailer' => 'swiftmailer.mailer.default', + 'swiftmailer.plugin.messagelogger' => 'swiftmailer.mailer.default.plugin.messagelogger', + 'swiftmailer.spool' => 'swiftmailer.mailer.default.spool', + 'swiftmailer.transport' => 'swiftmailer.mailer.default.transport', + 'swiftmailer.transport.real' => 'swiftmailer.mailer.default.transport.real', + ); + } + + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + + /** + * Gets the 'annotation_reader' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Common\Annotations\CachedReader A Doctrine\Common\Annotations\CachedReader instance. + */ + protected function getAnnotationReaderService() + { + return $this->services['annotation_reader'] = new \Doctrine\Common\Annotations\CachedReader(new \Doctrine\Common\Annotations\AnnotationReader(), new \Doctrine\Common\Cache\FilesystemCache((__DIR__.'/annotations')), true); + } + + /** + * Gets the 'assets.context' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Asset\Context\RequestStackContext A Symfony\Component\Asset\Context\RequestStackContext instance. + */ + protected function getAssets_ContextService() + { + return $this->services['assets.context'] = new \Symfony\Component\Asset\Context\RequestStackContext($this->get('request_stack')); + } + + /** + * Gets the 'assets.packages' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Asset\Packages A Symfony\Component\Asset\Packages instance. + */ + protected function getAssets_PackagesService() + { + return $this->services['assets.packages'] = new \Symfony\Component\Asset\Packages(new \Symfony\Component\Asset\PathPackage('', new \Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy(), $this->get('assets.context')), array()); + } + + /** + * Gets the 'cache_clearer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer A Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer instance. + */ + protected function getCacheClearerService() + { + return $this->services['cache_clearer'] = new \Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer(array()); + } + + /** + * Gets the 'cache_warmer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate A Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate instance. + */ + protected function getCacheWarmerService() + { + $a = $this->get('kernel'); + $b = $this->get('templating.filename_parser'); + + $c = new \Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinder($a, $b, ($this->targetDirs[3].'/app/Resources')); + + return $this->services['cache_warmer'] = new \Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate(array(0 => new \Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplatePathsCacheWarmer($c, $this->get('templating.locator')), 1 => $this->get('kernel.class_cache.cache_warmer'), 2 => new \Symfony\Bundle\FrameworkBundle\CacheWarmer\TranslationsCacheWarmer($this->get('translator')), 3 => new \Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer($this->get('router')), 4 => new \Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer($this, $c, array()), 5 => new \Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheWarmer($this->get('twig'), new \Symfony\Bundle\TwigBundle\TemplateIterator($a, ($this->targetDirs[3].'/app'), array())), 6 => new \Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer($this->get('doctrine')))); + } + + /** + * Gets the 'config_cache_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Config\ResourceCheckerConfigCacheFactory A Symfony\Component\Config\ResourceCheckerConfigCacheFactory instance. + */ + protected function getConfigCacheFactoryService() + { + return $this->services['config_cache_factory'] = new \Symfony\Component\Config\ResourceCheckerConfigCacheFactory(array(0 => new \Symfony\Component\Config\Resource\SelfCheckingResourceChecker())); + } + + /** + * Gets the 'data_collector.dump' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\DataCollector\DumpDataCollector A Symfony\Component\HttpKernel\DataCollector\DumpDataCollector instance. + */ + protected function getDataCollector_DumpService() + { + return $this->services['data_collector.dump'] = new \Symfony\Component\HttpKernel\DataCollector\DumpDataCollector($this->get('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE), NULL, 'UTF-8', $this->get('request_stack'), NULL); + } + + /** + * Gets the 'data_collector.form' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\DataCollector\FormDataCollector A Symfony\Component\Form\Extension\DataCollector\FormDataCollector instance. + */ + protected function getDataCollector_FormService() + { + return $this->services['data_collector.form'] = new \Symfony\Component\Form\Extension\DataCollector\FormDataCollector($this->get('data_collector.form.extractor')); + } + + /** + * Gets the 'data_collector.form.extractor' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\DataCollector\FormDataExtractor A Symfony\Component\Form\Extension\DataCollector\FormDataExtractor instance. + */ + protected function getDataCollector_Form_ExtractorService() + { + return $this->services['data_collector.form.extractor'] = new \Symfony\Component\Form\Extension\DataCollector\FormDataExtractor(); + } + + /** + * Gets the 'data_collector.request' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\DataCollector\RequestDataCollector A Symfony\Component\HttpKernel\DataCollector\RequestDataCollector instance. + */ + protected function getDataCollector_RequestService() + { + return $this->services['data_collector.request'] = new \Symfony\Component\HttpKernel\DataCollector\RequestDataCollector(); + } + + /** + * Gets the 'data_collector.router' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector A Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector instance. + */ + protected function getDataCollector_RouterService() + { + return $this->services['data_collector.router'] = new \Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector(); + } + + /** + * Gets the 'debug.controller_resolver' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Controller\TraceableControllerResolver A Symfony\Component\HttpKernel\Controller\TraceableControllerResolver instance. + */ + protected function getDebug_ControllerResolverService() + { + return $this->services['debug.controller_resolver'] = new \Symfony\Component\HttpKernel\Controller\TraceableControllerResolver(new \Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver($this, $this->get('controller_name_converter'), $this->get('monolog.logger.request', ContainerInterface::NULL_ON_INVALID_REFERENCE)), $this->get('debug.stopwatch')); + } + + /** + * Gets the 'debug.debug_handlers_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\DebugHandlersListener A Symfony\Component\HttpKernel\EventListener\DebugHandlersListener instance. + */ + protected function getDebug_DebugHandlersListenerService() + { + return $this->services['debug.debug_handlers_listener'] = new \Symfony\Component\HttpKernel\EventListener\DebugHandlersListener(NULL, $this->get('monolog.logger.php', ContainerInterface::NULL_ON_INVALID_REFERENCE), NULL, NULL, true, NULL); + } + + /** + * Gets the 'debug.dump_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\DumpListener A Symfony\Component\HttpKernel\EventListener\DumpListener instance. + */ + protected function getDebug_DumpListenerService() + { + return $this->services['debug.dump_listener'] = new \Symfony\Component\HttpKernel\EventListener\DumpListener($this->get('var_dumper.cloner'), $this->get('data_collector.dump')); + } + + /** + * Gets the 'debug.event_dispatcher' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher A Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher instance. + */ + protected function getDebug_EventDispatcherService() + { + $this->services['debug.event_dispatcher'] = $instance = new \Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher(new \Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher($this), $this->get('debug.stopwatch'), $this->get('monolog.logger.event', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + + $instance->addListenerService('kernel.controller', array(0 => 'data_collector.router', 1 => 'onKernelController'), 0); + $instance->addSubscriberService('response_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener'); + $instance->addSubscriberService('streamed_response_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\StreamedResponseListener'); + $instance->addSubscriberService('locale_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleListener'); + $instance->addSubscriberService('translator_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\TranslatorListener'); + $instance->addSubscriberService('session_listener', 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener'); + $instance->addSubscriberService('session.save_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\SaveSessionListener'); + $instance->addSubscriberService('fragment.listener', 'Symfony\\Component\\HttpKernel\\EventListener\\FragmentListener'); + $instance->addSubscriberService('profiler_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener'); + $instance->addSubscriberService('data_collector.request', 'Symfony\\Component\\HttpKernel\\DataCollector\\RequestDataCollector'); + $instance->addSubscriberService('router_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener'); + $instance->addSubscriberService('debug.debug_handlers_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\DebugHandlersListener'); + $instance->addSubscriberService('security.firewall', 'Symfony\\Component\\Security\\Http\\Firewall'); + $instance->addSubscriberService('security.rememberme.response_listener', 'Symfony\\Component\\Security\\Http\\RememberMe\\ResponseListener'); + $instance->addSubscriberService('twig.exception_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener'); + $instance->addSubscriberService('monolog.handler.console', 'Symfony\\Bridge\\Monolog\\Handler\\ConsoleHandler'); + $instance->addSubscriberService('swiftmailer.email_sender.listener', 'Symfony\\Bundle\\SwiftmailerBundle\\EventListener\\EmailSenderListener'); + $instance->addSubscriberService('sensio_framework_extra.controller.listener', 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ControllerListener'); + $instance->addSubscriberService('sensio_framework_extra.converter.listener', 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ParamConverterListener'); + $instance->addSubscriberService('sensio_framework_extra.view.listener', 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\TemplateListener'); + $instance->addSubscriberService('sensio_framework_extra.cache.listener', 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\HttpCacheListener'); + $instance->addSubscriberService('sensio_framework_extra.security.listener', 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\SecurityListener'); + $instance->addSubscriberService('debug.dump_listener', 'Symfony\\Component\\HttpKernel\\EventListener\\DumpListener'); + $instance->addSubscriberService('web_profiler.debug_toolbar', 'Symfony\\Bundle\\WebProfilerBundle\\EventListener\\WebDebugToolbarListener'); + + return $instance; + } + + /** + * Gets the 'debug.stopwatch' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Stopwatch\Stopwatch A Symfony\Component\Stopwatch\Stopwatch instance. + */ + protected function getDebug_StopwatchService() + { + return $this->services['debug.stopwatch'] = new \Symfony\Component\Stopwatch\Stopwatch(); + } + + /** + * Gets the 'doctrine' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Bundle\DoctrineBundle\Registry A Doctrine\Bundle\DoctrineBundle\Registry instance. + */ + protected function getDoctrineService() + { + return $this->services['doctrine'] = new \Doctrine\Bundle\DoctrineBundle\Registry($this, array('default' => 'doctrine.dbal.default_connection'), array('default' => 'doctrine.orm.default_entity_manager'), 'default', 'default'); + } + + /** + * Gets the 'doctrine.dbal.connection_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Bundle\DoctrineBundle\ConnectionFactory A Doctrine\Bundle\DoctrineBundle\ConnectionFactory instance. + */ + protected function getDoctrine_Dbal_ConnectionFactoryService() + { + return $this->services['doctrine.dbal.connection_factory'] = new \Doctrine\Bundle\DoctrineBundle\ConnectionFactory(array()); + } + + /** + * Gets the 'doctrine.dbal.default_connection' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\DBAL\Connection A Doctrine\DBAL\Connection instance. + */ + protected function getDoctrine_Dbal_DefaultConnectionService() + { + $a = new \Doctrine\DBAL\Logging\LoggerChain(); + $a->addLogger(new \Symfony\Bridge\Doctrine\Logger\DbalLogger($this->get('monolog.logger.doctrine', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE))); + $a->addLogger($this->get('doctrine.dbal.logger.profiling.default')); + + $b = new \Doctrine\DBAL\Configuration(); + $b->setSQLLogger($a); + + $c = new \Symfony\Bridge\Doctrine\ContainerAwareEventManager($this); + $c->addEventListener(array(0 => 'loadClassMetadata'), $this->get('doctrine.orm.default_listeners.attach_entity_listeners')); + + return $this->services['doctrine.dbal.default_connection'] = $this->get('doctrine.dbal.connection_factory')->createConnection(array('driver' => 'pdo_mysql', 'host' => '127.0.0.1', 'port' => 3306, 'dbname' => 'symfony', 'user' => 'root', 'password' => 'yolo', 'charset' => 'UTF8', 'driverOptions' => array(), 'defaultTableOptions' => array()), $b, $c, array()); + } + + /** + * Gets the 'doctrine.orm.default_entity_listener_resolver' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\ORM\Mapping\DefaultEntityListenerResolver A Doctrine\ORM\Mapping\DefaultEntityListenerResolver instance. + */ + protected function getDoctrine_Orm_DefaultEntityListenerResolverService() + { + return $this->services['doctrine.orm.default_entity_listener_resolver'] = new \Doctrine\ORM\Mapping\DefaultEntityListenerResolver(); + } + + /** + * Gets the 'doctrine.orm.default_entity_manager' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\ORM\EntityManager A Doctrine\ORM\EntityManager instance. + */ + protected function getDoctrine_Orm_DefaultEntityManagerService() + { + $a = new \Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain(); + $a->addDriver(new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($this->get('annotation_reader'), array(0 => ($this->targetDirs[3].'/src/AppBundle/Entity'))), 'AppBundle\\Entity'); + + $b = new \Doctrine\ORM\Configuration(); + $b->setEntityNamespaces(array('AppBundle' => 'AppBundle\\Entity')); + $b->setMetadataCacheImpl($this->get('doctrine_cache.providers.doctrine.orm.default_metadata_cache')); + $b->setQueryCacheImpl($this->get('doctrine_cache.providers.doctrine.orm.default_query_cache')); + $b->setResultCacheImpl($this->get('doctrine_cache.providers.doctrine.orm.default_result_cache')); + $b->setMetadataDriverImpl($a); + $b->setProxyDir((__DIR__.'/doctrine/orm/Proxies')); + $b->setProxyNamespace('Proxies'); + $b->setAutoGenerateProxyClasses(true); + $b->setClassMetadataFactoryName('Doctrine\\ORM\\Mapping\\ClassMetadataFactory'); + $b->setDefaultRepositoryClassName('Doctrine\\ORM\\EntityRepository'); + $b->setNamingStrategy(new \Doctrine\ORM\Mapping\UnderscoreNamingStrategy()); + $b->setQuoteStrategy(new \Doctrine\ORM\Mapping\DefaultQuoteStrategy()); + $b->setEntityListenerResolver($this->get('doctrine.orm.default_entity_listener_resolver')); + + $this->services['doctrine.orm.default_entity_manager'] = $instance = \Doctrine\ORM\EntityManager::create($this->get('doctrine.dbal.default_connection'), $b); + + $this->get('doctrine.orm.default_manager_configurator')->configure($instance); + + return $instance; + } + + /** + * Gets the 'doctrine.orm.default_listeners.attach_entity_listeners' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\ORM\Tools\AttachEntityListenersListener A Doctrine\ORM\Tools\AttachEntityListenersListener instance. + */ + protected function getDoctrine_Orm_DefaultListeners_AttachEntityListenersService() + { + return $this->services['doctrine.orm.default_listeners.attach_entity_listeners'] = new \Doctrine\ORM\Tools\AttachEntityListenersListener(); + } + + /** + * Gets the 'doctrine.orm.default_manager_configurator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Bundle\DoctrineBundle\ManagerConfigurator A Doctrine\Bundle\DoctrineBundle\ManagerConfigurator instance. + */ + protected function getDoctrine_Orm_DefaultManagerConfiguratorService() + { + return $this->services['doctrine.orm.default_manager_configurator'] = new \Doctrine\Bundle\DoctrineBundle\ManagerConfigurator(array(), array()); + } + + /** + * Gets the 'doctrine.orm.validator.unique' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator A Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator instance. + */ + protected function getDoctrine_Orm_Validator_UniqueService() + { + return $this->services['doctrine.orm.validator.unique'] = new \Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator($this->get('doctrine')); + } + + /** + * Gets the 'doctrine.orm.validator_initializer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Doctrine\Validator\DoctrineInitializer A Symfony\Bridge\Doctrine\Validator\DoctrineInitializer instance. + */ + protected function getDoctrine_Orm_ValidatorInitializerService() + { + return $this->services['doctrine.orm.validator_initializer'] = new \Symfony\Bridge\Doctrine\Validator\DoctrineInitializer($this->get('doctrine')); + } + + /** + * Gets the 'doctrine_cache.providers.doctrine.orm.default_metadata_cache' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Common\Cache\ArrayCache A Doctrine\Common\Cache\ArrayCache instance. + */ + protected function getDoctrineCache_Providers_Doctrine_Orm_DefaultMetadataCacheService() + { + $this->services['doctrine_cache.providers.doctrine.orm.default_metadata_cache'] = $instance = new \Doctrine\Common\Cache\ArrayCache(); + + $instance->setNamespace('sf2orm_default_15a331a5c940fc1fc66d9a8757c4c571ce907e1d260fc696d5250e7afd012142'); + + return $instance; + } + + /** + * Gets the 'doctrine_cache.providers.doctrine.orm.default_query_cache' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Common\Cache\ArrayCache A Doctrine\Common\Cache\ArrayCache instance. + */ + protected function getDoctrineCache_Providers_Doctrine_Orm_DefaultQueryCacheService() + { + $this->services['doctrine_cache.providers.doctrine.orm.default_query_cache'] = $instance = new \Doctrine\Common\Cache\ArrayCache(); + + $instance->setNamespace('sf2orm_default_15a331a5c940fc1fc66d9a8757c4c571ce907e1d260fc696d5250e7afd012142'); + + return $instance; + } + + /** + * Gets the 'doctrine_cache.providers.doctrine.orm.default_result_cache' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Doctrine\Common\Cache\ArrayCache A Doctrine\Common\Cache\ArrayCache instance. + */ + protected function getDoctrineCache_Providers_Doctrine_Orm_DefaultResultCacheService() + { + $this->services['doctrine_cache.providers.doctrine.orm.default_result_cache'] = $instance = new \Doctrine\Common\Cache\ArrayCache(); + + $instance->setNamespace('sf2orm_default_15a331a5c940fc1fc66d9a8757c4c571ce907e1d260fc696d5250e7afd012142'); + + return $instance; + } + + /** + * Gets the 'file_locator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Config\FileLocator A Symfony\Component\HttpKernel\Config\FileLocator instance. + */ + protected function getFileLocatorService() + { + return $this->services['file_locator'] = new \Symfony\Component\HttpKernel\Config\FileLocator($this->get('kernel'), ($this->targetDirs[3].'/app/Resources')); + } + + /** + * Gets the 'filesystem' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Filesystem\Filesystem A Symfony\Component\Filesystem\Filesystem instance. + */ + protected function getFilesystemService() + { + return $this->services['filesystem'] = new \Symfony\Component\Filesystem\Filesystem(); + } + + /** + * Gets the 'form.factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\FormFactory A Symfony\Component\Form\FormFactory instance. + */ + protected function getForm_FactoryService() + { + return $this->services['form.factory'] = new \Symfony\Component\Form\FormFactory($this->get('form.registry'), $this->get('form.resolved_type_factory')); + } + + /** + * Gets the 'form.registry' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\FormRegistry A Symfony\Component\Form\FormRegistry instance. + */ + protected function getForm_RegistryService() + { + return $this->services['form.registry'] = new \Symfony\Component\Form\FormRegistry(array(0 => new \Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension($this, array('Symfony\\Component\\Form\\Extension\\Core\\Type\\FormType' => 'form.type.form', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\BirthdayType' => 'form.type.birthday', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\CheckboxType' => 'form.type.checkbox', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\ChoiceType' => 'form.type.choice', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\CollectionType' => 'form.type.collection', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\CountryType' => 'form.type.country', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\DateType' => 'form.type.date', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\DateTimeType' => 'form.type.datetime', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\EmailType' => 'form.type.email', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\FileType' => 'form.type.file', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\HiddenType' => 'form.type.hidden', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\IntegerType' => 'form.type.integer', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\LanguageType' => 'form.type.language', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\LocaleType' => 'form.type.locale', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\MoneyType' => 'form.type.money', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\NumberType' => 'form.type.number', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\PasswordType' => 'form.type.password', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\PercentType' => 'form.type.percent', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\RadioType' => 'form.type.radio', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\RangeType' => 'form.type.range', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\RepeatedType' => 'form.type.repeated', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\SearchType' => 'form.type.search', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\TextareaType' => 'form.type.textarea', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType' => 'form.type.text', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\TimeType' => 'form.type.time', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\TimezoneType' => 'form.type.timezone', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\UrlType' => 'form.type.url', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\ButtonType' => 'form.type.button', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType' => 'form.type.submit', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\ResetType' => 'form.type.reset', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\CurrencyType' => 'form.type.currency', 'Symfony\\Bridge\\Doctrine\\Form\\Type\\EntityType' => 'form.type.entity'), array('Symfony\\Component\\Form\\Extension\\Core\\Type\\FormType' => array(0 => 'form.type_extension.form.http_foundation', 1 => 'form.type_extension.form.validator', 2 => 'form.type_extension.csrf', 3 => 'form.type_extension.form.data_collector'), 'Symfony\\Component\\Form\\Extension\\Core\\Type\\RepeatedType' => array(0 => 'form.type_extension.repeated.validator'), 'Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType' => array(0 => 'form.type_extension.submit.validator')), array(0 => 'form.type_guesser.validator', 1 => 'form.type_guesser.doctrine'))), $this->get('form.resolved_type_factory')); + } + + /** + * Gets the 'form.resolved_type_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy A Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy instance. + */ + protected function getForm_ResolvedTypeFactoryService() + { + return $this->services['form.resolved_type_factory'] = new \Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy(new \Symfony\Component\Form\ResolvedFormTypeFactory(), $this->get('data_collector.form')); + } + + /** + * Gets the 'form.type.birthday' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\BirthdayType A Symfony\Component\Form\Extension\Core\Type\BirthdayType instance. + */ + protected function getForm_Type_BirthdayService() + { + return $this->services['form.type.birthday'] = new \Symfony\Component\Form\Extension\Core\Type\BirthdayType(); + } + + /** + * Gets the 'form.type.button' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\ButtonType A Symfony\Component\Form\Extension\Core\Type\ButtonType instance. + */ + protected function getForm_Type_ButtonService() + { + return $this->services['form.type.button'] = new \Symfony\Component\Form\Extension\Core\Type\ButtonType(); + } + + /** + * Gets the 'form.type.checkbox' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\CheckboxType A Symfony\Component\Form\Extension\Core\Type\CheckboxType instance. + */ + protected function getForm_Type_CheckboxService() + { + return $this->services['form.type.checkbox'] = new \Symfony\Component\Form\Extension\Core\Type\CheckboxType(); + } + + /** + * Gets the 'form.type.choice' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\ChoiceType A Symfony\Component\Form\Extension\Core\Type\ChoiceType instance. + */ + protected function getForm_Type_ChoiceService() + { + return $this->services['form.type.choice'] = new \Symfony\Component\Form\Extension\Core\Type\ChoiceType(); + } + + /** + * Gets the 'form.type.collection' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\CollectionType A Symfony\Component\Form\Extension\Core\Type\CollectionType instance. + */ + protected function getForm_Type_CollectionService() + { + return $this->services['form.type.collection'] = new \Symfony\Component\Form\Extension\Core\Type\CollectionType(); + } + + /** + * Gets the 'form.type.country' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\CountryType A Symfony\Component\Form\Extension\Core\Type\CountryType instance. + */ + protected function getForm_Type_CountryService() + { + return $this->services['form.type.country'] = new \Symfony\Component\Form\Extension\Core\Type\CountryType(); + } + + /** + * Gets the 'form.type.currency' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\CurrencyType A Symfony\Component\Form\Extension\Core\Type\CurrencyType instance. + */ + protected function getForm_Type_CurrencyService() + { + return $this->services['form.type.currency'] = new \Symfony\Component\Form\Extension\Core\Type\CurrencyType(); + } + + /** + * Gets the 'form.type.date' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\DateType A Symfony\Component\Form\Extension\Core\Type\DateType instance. + */ + protected function getForm_Type_DateService() + { + return $this->services['form.type.date'] = new \Symfony\Component\Form\Extension\Core\Type\DateType(); + } + + /** + * Gets the 'form.type.datetime' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\DateTimeType A Symfony\Component\Form\Extension\Core\Type\DateTimeType instance. + */ + protected function getForm_Type_DatetimeService() + { + return $this->services['form.type.datetime'] = new \Symfony\Component\Form\Extension\Core\Type\DateTimeType(); + } + + /** + * Gets the 'form.type.email' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\EmailType A Symfony\Component\Form\Extension\Core\Type\EmailType instance. + */ + protected function getForm_Type_EmailService() + { + return $this->services['form.type.email'] = new \Symfony\Component\Form\Extension\Core\Type\EmailType(); + } + + /** + * Gets the 'form.type.entity' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Doctrine\Form\Type\EntityType A Symfony\Bridge\Doctrine\Form\Type\EntityType instance. + */ + protected function getForm_Type_EntityService() + { + return $this->services['form.type.entity'] = new \Symfony\Bridge\Doctrine\Form\Type\EntityType($this->get('doctrine')); + } + + /** + * Gets the 'form.type.file' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\FileType A Symfony\Component\Form\Extension\Core\Type\FileType instance. + */ + protected function getForm_Type_FileService() + { + return $this->services['form.type.file'] = new \Symfony\Component\Form\Extension\Core\Type\FileType(); + } + + /** + * Gets the 'form.type.form' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\FormType A Symfony\Component\Form\Extension\Core\Type\FormType instance. + */ + protected function getForm_Type_FormService() + { + return $this->services['form.type.form'] = new \Symfony\Component\Form\Extension\Core\Type\FormType($this->get('property_accessor')); + } + + /** + * Gets the 'form.type.hidden' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\HiddenType A Symfony\Component\Form\Extension\Core\Type\HiddenType instance. + */ + protected function getForm_Type_HiddenService() + { + return $this->services['form.type.hidden'] = new \Symfony\Component\Form\Extension\Core\Type\HiddenType(); + } + + /** + * Gets the 'form.type.integer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\IntegerType A Symfony\Component\Form\Extension\Core\Type\IntegerType instance. + */ + protected function getForm_Type_IntegerService() + { + return $this->services['form.type.integer'] = new \Symfony\Component\Form\Extension\Core\Type\IntegerType(); + } + + /** + * Gets the 'form.type.language' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\LanguageType A Symfony\Component\Form\Extension\Core\Type\LanguageType instance. + */ + protected function getForm_Type_LanguageService() + { + return $this->services['form.type.language'] = new \Symfony\Component\Form\Extension\Core\Type\LanguageType(); + } + + /** + * Gets the 'form.type.locale' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\LocaleType A Symfony\Component\Form\Extension\Core\Type\LocaleType instance. + */ + protected function getForm_Type_LocaleService() + { + return $this->services['form.type.locale'] = new \Symfony\Component\Form\Extension\Core\Type\LocaleType(); + } + + /** + * Gets the 'form.type.money' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\MoneyType A Symfony\Component\Form\Extension\Core\Type\MoneyType instance. + */ + protected function getForm_Type_MoneyService() + { + return $this->services['form.type.money'] = new \Symfony\Component\Form\Extension\Core\Type\MoneyType(); + } + + /** + * Gets the 'form.type.number' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\NumberType A Symfony\Component\Form\Extension\Core\Type\NumberType instance. + */ + protected function getForm_Type_NumberService() + { + return $this->services['form.type.number'] = new \Symfony\Component\Form\Extension\Core\Type\NumberType(); + } + + /** + * Gets the 'form.type.password' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\PasswordType A Symfony\Component\Form\Extension\Core\Type\PasswordType instance. + */ + protected function getForm_Type_PasswordService() + { + return $this->services['form.type.password'] = new \Symfony\Component\Form\Extension\Core\Type\PasswordType(); + } + + /** + * Gets the 'form.type.percent' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\PercentType A Symfony\Component\Form\Extension\Core\Type\PercentType instance. + */ + protected function getForm_Type_PercentService() + { + return $this->services['form.type.percent'] = new \Symfony\Component\Form\Extension\Core\Type\PercentType(); + } + + /** + * Gets the 'form.type.radio' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\RadioType A Symfony\Component\Form\Extension\Core\Type\RadioType instance. + */ + protected function getForm_Type_RadioService() + { + return $this->services['form.type.radio'] = new \Symfony\Component\Form\Extension\Core\Type\RadioType(); + } + + /** + * Gets the 'form.type.range' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\RangeType A Symfony\Component\Form\Extension\Core\Type\RangeType instance. + */ + protected function getForm_Type_RangeService() + { + return $this->services['form.type.range'] = new \Symfony\Component\Form\Extension\Core\Type\RangeType(); + } + + /** + * Gets the 'form.type.repeated' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\RepeatedType A Symfony\Component\Form\Extension\Core\Type\RepeatedType instance. + */ + protected function getForm_Type_RepeatedService() + { + return $this->services['form.type.repeated'] = new \Symfony\Component\Form\Extension\Core\Type\RepeatedType(); + } + + /** + * Gets the 'form.type.reset' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\ResetType A Symfony\Component\Form\Extension\Core\Type\ResetType instance. + */ + protected function getForm_Type_ResetService() + { + return $this->services['form.type.reset'] = new \Symfony\Component\Form\Extension\Core\Type\ResetType(); + } + + /** + * Gets the 'form.type.search' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\SearchType A Symfony\Component\Form\Extension\Core\Type\SearchType instance. + */ + protected function getForm_Type_SearchService() + { + return $this->services['form.type.search'] = new \Symfony\Component\Form\Extension\Core\Type\SearchType(); + } + + /** + * Gets the 'form.type.submit' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\SubmitType A Symfony\Component\Form\Extension\Core\Type\SubmitType instance. + */ + protected function getForm_Type_SubmitService() + { + return $this->services['form.type.submit'] = new \Symfony\Component\Form\Extension\Core\Type\SubmitType(); + } + + /** + * Gets the 'form.type.text' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\TextType A Symfony\Component\Form\Extension\Core\Type\TextType instance. + */ + protected function getForm_Type_TextService() + { + return $this->services['form.type.text'] = new \Symfony\Component\Form\Extension\Core\Type\TextType(); + } + + /** + * Gets the 'form.type.textarea' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\TextareaType A Symfony\Component\Form\Extension\Core\Type\TextareaType instance. + */ + protected function getForm_Type_TextareaService() + { + return $this->services['form.type.textarea'] = new \Symfony\Component\Form\Extension\Core\Type\TextareaType(); + } + + /** + * Gets the 'form.type.time' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\TimeType A Symfony\Component\Form\Extension\Core\Type\TimeType instance. + */ + protected function getForm_Type_TimeService() + { + return $this->services['form.type.time'] = new \Symfony\Component\Form\Extension\Core\Type\TimeType(); + } + + /** + * Gets the 'form.type.timezone' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\TimezoneType A Symfony\Component\Form\Extension\Core\Type\TimezoneType instance. + */ + protected function getForm_Type_TimezoneService() + { + return $this->services['form.type.timezone'] = new \Symfony\Component\Form\Extension\Core\Type\TimezoneType(); + } + + /** + * Gets the 'form.type.url' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Core\Type\UrlType A Symfony\Component\Form\Extension\Core\Type\UrlType instance. + */ + protected function getForm_Type_UrlService() + { + return $this->services['form.type.url'] = new \Symfony\Component\Form\Extension\Core\Type\UrlType(); + } + + /** + * Gets the 'form.type_extension.csrf' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension A Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension instance. + */ + protected function getForm_TypeExtension_CsrfService() + { + return $this->services['form.type_extension.csrf'] = new \Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension($this->get('security.csrf.token_manager'), true, '_token', $this->get('translator.default'), 'validators'); + } + + /** + * Gets the 'form.type_extension.form.data_collector' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension A Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension instance. + */ + protected function getForm_TypeExtension_Form_DataCollectorService() + { + return $this->services['form.type_extension.form.data_collector'] = new \Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension($this->get('data_collector.form')); + } + + /** + * Gets the 'form.type_extension.form.http_foundation' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension A Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension instance. + */ + protected function getForm_TypeExtension_Form_HttpFoundationService() + { + return $this->services['form.type_extension.form.http_foundation'] = new \Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension(new \Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler(new \Symfony\Component\Form\Util\ServerParams($this->get('request_stack')))); + } + + /** + * Gets the 'form.type_extension.form.validator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension A Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension instance. + */ + protected function getForm_TypeExtension_Form_ValidatorService() + { + return $this->services['form.type_extension.form.validator'] = new \Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension($this->get('validator')); + } + + /** + * Gets the 'form.type_extension.repeated.validator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension A Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension instance. + */ + protected function getForm_TypeExtension_Repeated_ValidatorService() + { + return $this->services['form.type_extension.repeated.validator'] = new \Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension(); + } + + /** + * Gets the 'form.type_extension.submit.validator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension A Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension instance. + */ + protected function getForm_TypeExtension_Submit_ValidatorService() + { + return $this->services['form.type_extension.submit.validator'] = new \Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension(); + } + + /** + * Gets the 'form.type_guesser.doctrine' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser A Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser instance. + */ + protected function getForm_TypeGuesser_DoctrineService() + { + return $this->services['form.type_guesser.doctrine'] = new \Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser($this->get('doctrine')); + } + + /** + * Gets the 'form.type_guesser.validator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser A Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser instance. + */ + protected function getForm_TypeGuesser_ValidatorService() + { + return $this->services['form.type_guesser.validator'] = new \Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser($this->get('validator')); + } + + /** + * Gets the 'fragment.handler' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler A Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler instance. + */ + protected function getFragment_HandlerService() + { + $this->services['fragment.handler'] = $instance = new \Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler($this, $this->get('request_stack'), true); + + $instance->addRendererService('inline', 'fragment.renderer.inline'); + $instance->addRendererService('hinclude', 'fragment.renderer.hinclude'); + $instance->addRendererService('hinclude', 'fragment.renderer.hinclude'); + $instance->addRendererService('esi', 'fragment.renderer.esi'); + $instance->addRendererService('ssi', 'fragment.renderer.ssi'); + + return $instance; + } + + /** + * Gets the 'fragment.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\FragmentListener A Symfony\Component\HttpKernel\EventListener\FragmentListener instance. + */ + protected function getFragment_ListenerService() + { + return $this->services['fragment.listener'] = new \Symfony\Component\HttpKernel\EventListener\FragmentListener($this->get('uri_signer'), '/_fragment'); + } + + /** + * Gets the 'fragment.renderer.esi' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer A Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer instance. + */ + protected function getFragment_Renderer_EsiService() + { + $this->services['fragment.renderer.esi'] = $instance = new \Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer(NULL, $this->get('fragment.renderer.inline'), $this->get('uri_signer')); + + $instance->setFragmentPath('/_fragment'); + + return $instance; + } + + /** + * Gets the 'fragment.renderer.hinclude' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer A Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer instance. + */ + protected function getFragment_Renderer_HincludeService() + { + $this->services['fragment.renderer.hinclude'] = $instance = new \Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer($this->get('twig'), $this->get('uri_signer'), NULL); + + $instance->setFragmentPath('/_fragment'); + + return $instance; + } + + /** + * Gets the 'fragment.renderer.inline' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer A Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer instance. + */ + protected function getFragment_Renderer_InlineService() + { + $this->services['fragment.renderer.inline'] = $instance = new \Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer($this->get('http_kernel'), $this->get('debug.event_dispatcher')); + + $instance->setFragmentPath('/_fragment'); + + return $instance; + } + + /** + * Gets the 'fragment.renderer.ssi' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer A Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer instance. + */ + protected function getFragment_Renderer_SsiService() + { + $this->services['fragment.renderer.ssi'] = $instance = new \Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer(NULL, $this->get('fragment.renderer.inline'), $this->get('uri_signer')); + + $instance->setFragmentPath('/_fragment'); + + return $instance; + } + + /** + * Gets the 'http_kernel' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\HttpKernel A Symfony\Component\HttpKernel\HttpKernel instance. + */ + protected function getHttpKernelService() + { + return $this->services['http_kernel'] = new \Symfony\Component\HttpKernel\HttpKernel($this->get('debug.event_dispatcher'), $this->get('debug.controller_resolver'), $this->get('request_stack')); + } + + /** + * Gets the 'kernel' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @throws RuntimeException always since this service is expected to be injected dynamically + */ + protected function getKernelService() + { + throw new RuntimeException('You have requested a synthetic service ("kernel"). The DIC does not know how to construct this service.'); + } + + /** + * Gets the 'kernel.class_cache.cache_warmer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassCacheCacheWarmer A Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassCacheCacheWarmer instance. + */ + protected function getKernel_ClassCache_CacheWarmerService() + { + return $this->services['kernel.class_cache.cache_warmer'] = new \Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassCacheCacheWarmer(); + } + + /** + * Gets the 'locale_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\LocaleListener A Symfony\Component\HttpKernel\EventListener\LocaleListener instance. + */ + protected function getLocaleListenerService() + { + return $this->services['locale_listener'] = new \Symfony\Component\HttpKernel\EventListener\LocaleListener($this->get('request_stack'), 'en', $this->get('router', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'logger' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getLoggerService() + { + $this->services['logger'] = $instance = new \Symfony\Bridge\Monolog\Logger('app'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.handler.console' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Handler\ConsoleHandler A Symfony\Bridge\Monolog\Handler\ConsoleHandler instance. + */ + protected function getMonolog_Handler_ConsoleService() + { + return $this->services['monolog.handler.console'] = new \Symfony\Bridge\Monolog\Handler\ConsoleHandler(NULL, false, array()); + } + + /** + * Gets the 'monolog.handler.debug' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Handler\DebugHandler A Symfony\Bridge\Monolog\Handler\DebugHandler instance. + */ + protected function getMonolog_Handler_DebugService() + { + return $this->services['monolog.handler.debug'] = new \Symfony\Bridge\Monolog\Handler\DebugHandler(100, true); + } + + /** + * Gets the 'monolog.handler.main' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Monolog\Handler\StreamHandler A Monolog\Handler\StreamHandler instance. + */ + protected function getMonolog_Handler_MainService() + { + return $this->services['monolog.handler.main'] = new \Monolog\Handler\StreamHandler(($this->targetDirs[2].'/logs/dev.log'), 100, true, NULL); + } + + /** + * Gets the 'monolog.logger.doctrine' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_DoctrineService() + { + $this->services['monolog.logger.doctrine'] = $instance = new \Symfony\Bridge\Monolog\Logger('doctrine'); + + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.event' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_EventService() + { + $this->services['monolog.logger.event'] = $instance = new \Symfony\Bridge\Monolog\Logger('event'); + + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.php' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_PhpService() + { + $this->services['monolog.logger.php'] = $instance = new \Symfony\Bridge\Monolog\Logger('php'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.profiler' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_ProfilerService() + { + $this->services['monolog.logger.profiler'] = $instance = new \Symfony\Bridge\Monolog\Logger('profiler'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.request' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_RequestService() + { + $this->services['monolog.logger.request'] = $instance = new \Symfony\Bridge\Monolog\Logger('request'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.router' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_RouterService() + { + $this->services['monolog.logger.router'] = $instance = new \Symfony\Bridge\Monolog\Logger('router'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.security' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_SecurityService() + { + $this->services['monolog.logger.security'] = $instance = new \Symfony\Bridge\Monolog\Logger('security'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.templating' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_TemplatingService() + { + $this->services['monolog.logger.templating'] = $instance = new \Symfony\Bridge\Monolog\Logger('templating'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'monolog.logger.translation' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Monolog\Logger A Symfony\Bridge\Monolog\Logger instance. + */ + protected function getMonolog_Logger_TranslationService() + { + $this->services['monolog.logger.translation'] = $instance = new \Symfony\Bridge\Monolog\Logger('translation'); + + $instance->pushHandler($this->get('monolog.handler.console')); + $instance->pushHandler($this->get('monolog.handler.main')); + $instance->pushHandler($this->get('monolog.handler.debug')); + + return $instance; + } + + /** + * Gets the 'profiler' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\Profiler\Profiler A Symfony\Component\HttpKernel\Profiler\Profiler instance. + */ + protected function getProfilerService() + { + $a = $this->get('monolog.logger.profiler', ContainerInterface::NULL_ON_INVALID_REFERENCE); + $b = $this->get('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE); + + $c = new \Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector($this->get('doctrine')); + $c->addLogger('default', $this->get('doctrine.dbal.logger.profiling.default')); + + $d = new \Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector(); + if ($this->has('kernel')) { + $d->setKernel($b); + } + + $this->services['profiler'] = $instance = new \Symfony\Component\HttpKernel\Profiler\Profiler(new \Symfony\Component\HttpKernel\Profiler\FileProfilerStorage(('file:'.__DIR__.'/profiler')), $a); + + $instance->add($this->get('data_collector.request')); + $instance->add(new \Symfony\Component\HttpKernel\DataCollector\TimeDataCollector($b, $this->get('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE))); + $instance->add(new \Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector()); + $instance->add(new \Symfony\Component\HttpKernel\DataCollector\AjaxDataCollector()); + $instance->add($this->get('data_collector.form')); + $instance->add(new \Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector()); + $instance->add(new \Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector($a)); + $instance->add(new \Symfony\Component\HttpKernel\DataCollector\EventDataCollector($this->get('debug.event_dispatcher', ContainerInterface::NULL_ON_INVALID_REFERENCE))); + $instance->add($this->get('data_collector.router')); + $instance->add(new \Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector($this->get('security.token_storage', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('security.role_hierarchy'), $this->get('security.logout_url_generator'))); + $instance->add(new \Symfony\Bridge\Twig\DataCollector\TwigDataCollector($this->get('twig.profile'))); + $instance->add($c); + $instance->add(new \Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector($this)); + $instance->add($this->get('data_collector.dump')); + $instance->add($d); + + return $instance; + } + + /** + * Gets the 'profiler_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\ProfilerListener A Symfony\Component\HttpKernel\EventListener\ProfilerListener instance. + */ + protected function getProfilerListenerService() + { + return $this->services['profiler_listener'] = new \Symfony\Component\HttpKernel\EventListener\ProfilerListener($this->get('profiler'), $this->get('request_stack'), NULL, false, false); + } + + /** + * Gets the 'property_accessor' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\PropertyAccess\PropertyAccessor A Symfony\Component\PropertyAccess\PropertyAccessor instance. + */ + protected function getPropertyAccessorService() + { + return $this->services['property_accessor'] = new \Symfony\Component\PropertyAccess\PropertyAccessor(false, false); + } + + /** + * Gets the 'request_stack' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpFoundation\RequestStack A Symfony\Component\HttpFoundation\RequestStack instance. + */ + protected function getRequestStackService() + { + return $this->services['request_stack'] = new \Symfony\Component\HttpFoundation\RequestStack(); + } + + /** + * Gets the 'response_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\ResponseListener A Symfony\Component\HttpKernel\EventListener\ResponseListener instance. + */ + protected function getResponseListenerService() + { + return $this->services['response_listener'] = new \Symfony\Component\HttpKernel\EventListener\ResponseListener('UTF-8'); + } + + /** + * Gets the 'router' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Routing\Router A Symfony\Bundle\FrameworkBundle\Routing\Router instance. + */ + protected function getRouterService() + { + $this->services['router'] = $instance = new \Symfony\Bundle\FrameworkBundle\Routing\Router($this, ($this->targetDirs[3].'/app/config/routing_dev.yml'), array('cache_dir' => __DIR__, 'debug' => true, 'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', 'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', 'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper', 'generator_cache_class' => 'appDevUrlGenerator', 'matcher_class' => 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', 'matcher_base_class' => 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper', 'matcher_cache_class' => 'appDevUrlMatcher', 'strict_requirements' => true), $this->get('router.request_context', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('monolog.logger.router', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + + $instance->setConfigCacheFactory($this->get('config_cache_factory')); + + return $instance; + } + + /** + * Gets the 'router_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\RouterListener A Symfony\Component\HttpKernel\EventListener\RouterListener instance. + */ + protected function getRouterListenerService() + { + return $this->services['router_listener'] = new \Symfony\Component\HttpKernel\EventListener\RouterListener($this->get('router'), $this->get('request_stack'), $this->get('router.request_context', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('monolog.logger.request', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'routing.loader' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader A Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader instance. + */ + protected function getRouting_LoaderService() + { + $a = $this->get('file_locator'); + $b = $this->get('annotation_reader'); + + $c = new \Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader($b); + + $d = new \Symfony\Component\Config\Loader\LoaderResolver(); + $d->addLoader(new \Symfony\Component\Routing\Loader\XmlFileLoader($a)); + $d->addLoader(new \Symfony\Component\Routing\Loader\YamlFileLoader($a)); + $d->addLoader(new \Symfony\Component\Routing\Loader\PhpFileLoader($a)); + $d->addLoader(new \Symfony\Component\Routing\Loader\DirectoryLoader($a)); + $d->addLoader(new \Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoader($this)); + $d->addLoader(new \Symfony\Component\Routing\Loader\AnnotationDirectoryLoader($a, $c)); + $d->addLoader(new \Symfony\Component\Routing\Loader\AnnotationFileLoader($a, $c)); + $d->addLoader($c); + + return $this->services['routing.loader'] = new \Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader($this->get('controller_name_converter'), $d); + } + + /** + * Gets the 'security.authentication.guard_handler' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Guard\GuardAuthenticatorHandler A Symfony\Component\Security\Guard\GuardAuthenticatorHandler instance. + */ + protected function getSecurity_Authentication_GuardHandlerService() + { + return $this->services['security.authentication.guard_handler'] = new \Symfony\Component\Security\Guard\GuardAuthenticatorHandler($this->get('security.token_storage'), $this->get('debug.event_dispatcher', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'security.authentication_utils' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Http\Authentication\AuthenticationUtils A Symfony\Component\Security\Http\Authentication\AuthenticationUtils instance. + */ + protected function getSecurity_AuthenticationUtilsService() + { + return $this->services['security.authentication_utils'] = new \Symfony\Component\Security\Http\Authentication\AuthenticationUtils($this->get('request_stack')); + } + + /** + * Gets the 'security.authorization_checker' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Core\Authorization\AuthorizationChecker A Symfony\Component\Security\Core\Authorization\AuthorizationChecker instance. + */ + protected function getSecurity_AuthorizationCheckerService() + { + return $this->services['security.authorization_checker'] = new \Symfony\Component\Security\Core\Authorization\AuthorizationChecker($this->get('security.token_storage'), $this->get('security.authentication.manager'), $this->get('security.access.decision_manager'), false); + } + + /** + * Gets the 'security.csrf.token_manager' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Csrf\CsrfTokenManager A Symfony\Component\Security\Csrf\CsrfTokenManager instance. + */ + protected function getSecurity_Csrf_TokenManagerService() + { + return $this->services['security.csrf.token_manager'] = new \Symfony\Component\Security\Csrf\CsrfTokenManager(new \Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator(), new \Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage($this->get('session'))); + } + + /** + * Gets the 'security.encoder_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Core\Encoder\EncoderFactory A Symfony\Component\Security\Core\Encoder\EncoderFactory instance. + */ + protected function getSecurity_EncoderFactoryService() + { + return $this->services['security.encoder_factory'] = new \Symfony\Component\Security\Core\Encoder\EncoderFactory(array()); + } + + /** + * Gets the 'security.firewall' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Http\Firewall A Symfony\Component\Security\Http\Firewall instance. + */ + protected function getSecurity_FirewallService() + { + return $this->services['security.firewall'] = new \Symfony\Component\Security\Http\Firewall(new \Symfony\Bundle\SecurityBundle\Security\FirewallMap($this, array('security.firewall.map.context.dev' => new \Symfony\Component\HttpFoundation\RequestMatcher('^/(_(profiler|wdt)|css|images|js)/'), 'security.firewall.map.context.main' => NULL)), $this->get('debug.event_dispatcher')); + } + + /** + * Gets the 'security.firewall.map.context.dev' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\SecurityBundle\Security\FirewallContext A Symfony\Bundle\SecurityBundle\Security\FirewallContext instance. + */ + protected function getSecurity_Firewall_Map_Context_DevService() + { + return $this->services['security.firewall.map.context.dev'] = new \Symfony\Bundle\SecurityBundle\Security\FirewallContext(array(), NULL); + } + + /** + * Gets the 'security.firewall.map.context.main' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\SecurityBundle\Security\FirewallContext A Symfony\Bundle\SecurityBundle\Security\FirewallContext instance. + */ + protected function getSecurity_Firewall_Map_Context_MainService() + { + $a = $this->get('monolog.logger.security', ContainerInterface::NULL_ON_INVALID_REFERENCE); + $b = $this->get('security.token_storage'); + $c = $this->get('security.authentication.manager'); + $d = $this->get('router', ContainerInterface::NULL_ON_INVALID_REFERENCE); + + $e = new \Symfony\Component\Security\Http\AccessMap(); + + return $this->services['security.firewall.map.context.main'] = new \Symfony\Bundle\SecurityBundle\Security\FirewallContext(array(0 => new \Symfony\Component\Security\Http\Firewall\ChannelListener($e, new \Symfony\Component\Security\Http\EntryPoint\RetryAuthenticationEntryPoint(80, 443), $a), 1 => new \Symfony\Component\Security\Http\Firewall\ContextListener($b, array(0 => new \Symfony\Component\Security\Core\User\InMemoryUserProvider()), 'main', $a, $this->get('debug.event_dispatcher', ContainerInterface::NULL_ON_INVALID_REFERENCE)), 2 => new \Symfony\Component\Security\Http\Firewall\AnonymousAuthenticationListener($b, '56a782448eafa8.89273161', $a, $c), 3 => new \Symfony\Component\Security\Http\Firewall\AccessListener($b, $this->get('security.access.decision_manager'), $e, $c)), new \Symfony\Component\Security\Http\Firewall\ExceptionListener($b, $this->get('security.authentication.trust_resolver'), new \Symfony\Component\Security\Http\HttpUtils($d, $d), 'main', NULL, NULL, NULL, $a, false)); + } + + /** + * Gets the 'security.password_encoder' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Core\Encoder\UserPasswordEncoder A Symfony\Component\Security\Core\Encoder\UserPasswordEncoder instance. + */ + protected function getSecurity_PasswordEncoderService() + { + return $this->services['security.password_encoder'] = new \Symfony\Component\Security\Core\Encoder\UserPasswordEncoder($this->get('security.encoder_factory')); + } + + /** + * Gets the 'security.rememberme.response_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Http\RememberMe\ResponseListener A Symfony\Component\Security\Http\RememberMe\ResponseListener instance. + */ + protected function getSecurity_Rememberme_ResponseListenerService() + { + return $this->services['security.rememberme.response_listener'] = new \Symfony\Component\Security\Http\RememberMe\ResponseListener(); + } + + /** + * Gets the 'security.token_storage' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage A Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage instance. + */ + protected function getSecurity_TokenStorageService() + { + return $this->services['security.token_storage'] = new \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage(); + } + + /** + * Gets the 'security.user_checker.main' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Core\User\UserChecker A Symfony\Component\Security\Core\User\UserChecker instance. + */ + protected function getSecurity_UserChecker_MainService() + { + return $this->services['security.user_checker.main'] = new \Symfony\Component\Security\Core\User\UserChecker(); + } + + /** + * Gets the 'security.validator.user_password' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator A Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator instance. + */ + protected function getSecurity_Validator_UserPasswordService() + { + return $this->services['security.validator.user_password'] = new \Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator($this->get('security.token_storage'), $this->get('security.encoder_factory')); + } + + /** + * Gets the 'sensio_distribution.security_checker' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \SensioLabs\Security\SecurityChecker A SensioLabs\Security\SecurityChecker instance. + */ + protected function getSensioDistribution_SecurityCheckerService() + { + return $this->services['sensio_distribution.security_checker'] = new \SensioLabs\Security\SecurityChecker(); + } + + /** + * Gets the 'sensio_distribution.security_checker.command' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \SensioLabs\Security\Command\SecurityCheckerCommand A SensioLabs\Security\Command\SecurityCheckerCommand instance. + */ + protected function getSensioDistribution_SecurityChecker_CommandService() + { + return $this->services['sensio_distribution.security_checker.command'] = new \SensioLabs\Security\Command\SecurityCheckerCommand($this->get('sensio_distribution.security_checker')); + } + + /** + * Gets the 'sensio_framework_extra.cache.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\EventListener\HttpCacheListener A Sensio\Bundle\FrameworkExtraBundle\EventListener\HttpCacheListener instance. + */ + protected function getSensioFrameworkExtra_Cache_ListenerService() + { + return $this->services['sensio_framework_extra.cache.listener'] = new \Sensio\Bundle\FrameworkExtraBundle\EventListener\HttpCacheListener(); + } + + /** + * Gets the 'sensio_framework_extra.controller.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener A Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener instance. + */ + protected function getSensioFrameworkExtra_Controller_ListenerService() + { + return $this->services['sensio_framework_extra.controller.listener'] = new \Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener($this->get('annotation_reader')); + } + + /** + * Gets the 'sensio_framework_extra.converter.datetime' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter A Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter instance. + */ + protected function getSensioFrameworkExtra_Converter_DatetimeService() + { + return $this->services['sensio_framework_extra.converter.datetime'] = new \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter(); + } + + /** + * Gets the 'sensio_framework_extra.converter.doctrine.orm' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter A Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter instance. + */ + protected function getSensioFrameworkExtra_Converter_Doctrine_OrmService() + { + return $this->services['sensio_framework_extra.converter.doctrine.orm'] = new \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter($this->get('doctrine', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'sensio_framework_extra.converter.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener A Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener instance. + */ + protected function getSensioFrameworkExtra_Converter_ListenerService() + { + return $this->services['sensio_framework_extra.converter.listener'] = new \Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener($this->get('sensio_framework_extra.converter.manager'), true); + } + + /** + * Gets the 'sensio_framework_extra.converter.manager' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager A Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager instance. + */ + protected function getSensioFrameworkExtra_Converter_ManagerService() + { + $this->services['sensio_framework_extra.converter.manager'] = $instance = new \Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager(); + + $instance->add($this->get('sensio_framework_extra.converter.doctrine.orm'), 0, 'doctrine.orm'); + $instance->add($this->get('sensio_framework_extra.converter.datetime'), 0, 'datetime'); + + return $instance; + } + + /** + * Gets the 'sensio_framework_extra.security.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener A Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener instance. + */ + protected function getSensioFrameworkExtra_Security_ListenerService() + { + return $this->services['sensio_framework_extra.security.listener'] = new \Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener(NULL, new \Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage(), $this->get('security.authentication.trust_resolver', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('security.role_hierarchy', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('security.token_storage', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('security.authorization_checker', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'sensio_framework_extra.view.guesser' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser A Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser instance. + */ + protected function getSensioFrameworkExtra_View_GuesserService() + { + return $this->services['sensio_framework_extra.view.guesser'] = new \Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser($this->get('kernel')); + } + + /** + * Gets the 'sensio_framework_extra.view.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener A Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener instance. + */ + protected function getSensioFrameworkExtra_View_ListenerService() + { + return $this->services['sensio_framework_extra.view.listener'] = new \Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener($this); + } + + /** + * Gets the 'service_container' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @throws RuntimeException always since this service is expected to be injected dynamically + */ + protected function getServiceContainerService() + { + throw new RuntimeException('You have requested a synthetic service ("service_container"). The DIC does not know how to construct this service.'); + } + + /** + * Gets the 'session' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpFoundation\Session\Session A Symfony\Component\HttpFoundation\Session\Session instance. + */ + protected function getSessionService() + { + return $this->services['session'] = new \Symfony\Component\HttpFoundation\Session\Session($this->get('session.storage.native'), new \Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag(), new \Symfony\Component\HttpFoundation\Session\Flash\FlashBag()); + } + + /** + * Gets the 'session.save_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\SaveSessionListener A Symfony\Component\HttpKernel\EventListener\SaveSessionListener instance. + */ + protected function getSession_SaveListenerService() + { + return $this->services['session.save_listener'] = new \Symfony\Component\HttpKernel\EventListener\SaveSessionListener(); + } + + /** + * Gets the 'session.storage.filesystem' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage A Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage instance. + */ + protected function getSession_Storage_FilesystemService() + { + return $this->services['session.storage.filesystem'] = new \Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage((__DIR__.'/sessions'), 'MOCKSESSID', $this->get('session.storage.metadata_bag')); + } + + /** + * Gets the 'session.storage.native' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage A Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage instance. + */ + protected function getSession_Storage_NativeService() + { + return $this->services['session.storage.native'] = new \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage(array('cookie_httponly' => true, 'gc_probability' => 1), NULL, $this->get('session.storage.metadata_bag')); + } + + /** + * Gets the 'session.storage.php_bridge' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage A Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage instance. + */ + protected function getSession_Storage_PhpBridgeService() + { + return $this->services['session.storage.php_bridge'] = new \Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage(NULL, $this->get('session.storage.metadata_bag')); + } + + /** + * Gets the 'session_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\EventListener\SessionListener A Symfony\Bundle\FrameworkBundle\EventListener\SessionListener instance. + */ + protected function getSessionListenerService() + { + return $this->services['session_listener'] = new \Symfony\Bundle\FrameworkBundle\EventListener\SessionListener($this); + } + + /** + * Gets the 'streamed_response_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\StreamedResponseListener A Symfony\Component\HttpKernel\EventListener\StreamedResponseListener instance. + */ + protected function getStreamedResponseListenerService() + { + return $this->services['streamed_response_listener'] = new \Symfony\Component\HttpKernel\EventListener\StreamedResponseListener(); + } + + /** + * Gets the 'swiftmailer.email_sender.listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener A Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener instance. + */ + protected function getSwiftmailer_EmailSender_ListenerService() + { + return $this->services['swiftmailer.email_sender.listener'] = new \Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener($this, $this->get('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'swiftmailer.mailer.default' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Swift_Mailer A Swift_Mailer instance. + */ + protected function getSwiftmailer_Mailer_DefaultService() + { + return $this->services['swiftmailer.mailer.default'] = new \Swift_Mailer($this->get('swiftmailer.mailer.default.transport')); + } + + /** + * Gets the 'swiftmailer.mailer.default.plugin.messagelogger' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Swift_Plugins_MessageLogger A Swift_Plugins_MessageLogger instance. + */ + protected function getSwiftmailer_Mailer_Default_Plugin_MessageloggerService() + { + return $this->services['swiftmailer.mailer.default.plugin.messagelogger'] = new \Swift_Plugins_MessageLogger(); + } + + /** + * Gets the 'swiftmailer.mailer.default.spool' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Swift_MemorySpool A Swift_MemorySpool instance. + */ + protected function getSwiftmailer_Mailer_Default_SpoolService() + { + return $this->services['swiftmailer.mailer.default.spool'] = new \Swift_MemorySpool(); + } + + /** + * Gets the 'swiftmailer.mailer.default.transport' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Swift_Transport_SpoolTransport A Swift_Transport_SpoolTransport instance. + */ + protected function getSwiftmailer_Mailer_Default_TransportService() + { + $this->services['swiftmailer.mailer.default.transport'] = $instance = new \Swift_Transport_SpoolTransport($this->get('swiftmailer.mailer.default.transport.eventdispatcher'), $this->get('swiftmailer.mailer.default.spool')); + + $instance->registerPlugin($this->get('swiftmailer.mailer.default.plugin.messagelogger')); + + return $instance; + } + + /** + * Gets the 'swiftmailer.mailer.default.transport.real' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Swift_Transport_EsmtpTransport A Swift_Transport_EsmtpTransport instance. + */ + protected function getSwiftmailer_Mailer_Default_Transport_RealService() + { + $a = new \Swift_Transport_Esmtp_AuthHandler(array(0 => new \Swift_Transport_Esmtp_Auth_CramMd5Authenticator(), 1 => new \Swift_Transport_Esmtp_Auth_LoginAuthenticator(), 2 => new \Swift_Transport_Esmtp_Auth_PlainAuthenticator())); + $a->setUsername(NULL); + $a->setPassword(NULL); + $a->setAuthMode(NULL); + + $this->services['swiftmailer.mailer.default.transport.real'] = $instance = new \Swift_Transport_EsmtpTransport(new \Swift_Transport_StreamBuffer(new \Swift_StreamFilters_StringReplacementFilterFactory()), array(0 => $a), $this->get('swiftmailer.mailer.default.transport.eventdispatcher')); + + $instance->setHost('127.0.0.1'); + $instance->setPort(25); + $instance->setEncryption(NULL); + $instance->setTimeout(30); + $instance->setSourceIp(NULL); + + return $instance; + } + + /** + * Gets the 'templating' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\TwigBundle\TwigEngine A Symfony\Bundle\TwigBundle\TwigEngine instance. + */ + protected function getTemplatingService() + { + return $this->services['templating'] = new \Symfony\Bundle\TwigBundle\TwigEngine($this->get('twig'), $this->get('templating.name_parser'), $this->get('templating.locator')); + } + + /** + * Gets the 'templating.filename_parser' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser A Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser instance. + */ + protected function getTemplating_FilenameParserService() + { + return $this->services['templating.filename_parser'] = new \Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser(); + } + + /** + * Gets the 'templating.helper.logout_url' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper A Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper instance. + */ + protected function getTemplating_Helper_LogoutUrlService() + { + return $this->services['templating.helper.logout_url'] = new \Symfony\Bundle\SecurityBundle\Templating\Helper\LogoutUrlHelper($this->get('security.logout_url_generator')); + } + + /** + * Gets the 'templating.helper.security' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\SecurityBundle\Templating\Helper\SecurityHelper A Symfony\Bundle\SecurityBundle\Templating\Helper\SecurityHelper instance. + */ + protected function getTemplating_Helper_SecurityService() + { + return $this->services['templating.helper.security'] = new \Symfony\Bundle\SecurityBundle\Templating\Helper\SecurityHelper($this->get('security.authorization_checker', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'templating.loader' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader A Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader instance. + */ + protected function getTemplating_LoaderService() + { + return $this->services['templating.loader'] = new \Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader($this->get('templating.locator')); + } + + /** + * Gets the 'templating.name_parser' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser A Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser instance. + */ + protected function getTemplating_NameParserService() + { + return $this->services['templating.name_parser'] = new \Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser($this->get('kernel')); + } + + /** + * Gets the 'translation.dumper.csv' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\CsvFileDumper A Symfony\Component\Translation\Dumper\CsvFileDumper instance. + */ + protected function getTranslation_Dumper_CsvService() + { + return $this->services['translation.dumper.csv'] = new \Symfony\Component\Translation\Dumper\CsvFileDumper(); + } + + /** + * Gets the 'translation.dumper.ini' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\IniFileDumper A Symfony\Component\Translation\Dumper\IniFileDumper instance. + */ + protected function getTranslation_Dumper_IniService() + { + return $this->services['translation.dumper.ini'] = new \Symfony\Component\Translation\Dumper\IniFileDumper(); + } + + /** + * Gets the 'translation.dumper.json' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\JsonFileDumper A Symfony\Component\Translation\Dumper\JsonFileDumper instance. + */ + protected function getTranslation_Dumper_JsonService() + { + return $this->services['translation.dumper.json'] = new \Symfony\Component\Translation\Dumper\JsonFileDumper(); + } + + /** + * Gets the 'translation.dumper.mo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\MoFileDumper A Symfony\Component\Translation\Dumper\MoFileDumper instance. + */ + protected function getTranslation_Dumper_MoService() + { + return $this->services['translation.dumper.mo'] = new \Symfony\Component\Translation\Dumper\MoFileDumper(); + } + + /** + * Gets the 'translation.dumper.php' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\PhpFileDumper A Symfony\Component\Translation\Dumper\PhpFileDumper instance. + */ + protected function getTranslation_Dumper_PhpService() + { + return $this->services['translation.dumper.php'] = new \Symfony\Component\Translation\Dumper\PhpFileDumper(); + } + + /** + * Gets the 'translation.dumper.po' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\PoFileDumper A Symfony\Component\Translation\Dumper\PoFileDumper instance. + */ + protected function getTranslation_Dumper_PoService() + { + return $this->services['translation.dumper.po'] = new \Symfony\Component\Translation\Dumper\PoFileDumper(); + } + + /** + * Gets the 'translation.dumper.qt' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\QtFileDumper A Symfony\Component\Translation\Dumper\QtFileDumper instance. + */ + protected function getTranslation_Dumper_QtService() + { + return $this->services['translation.dumper.qt'] = new \Symfony\Component\Translation\Dumper\QtFileDumper(); + } + + /** + * Gets the 'translation.dumper.res' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\IcuResFileDumper A Symfony\Component\Translation\Dumper\IcuResFileDumper instance. + */ + protected function getTranslation_Dumper_ResService() + { + return $this->services['translation.dumper.res'] = new \Symfony\Component\Translation\Dumper\IcuResFileDumper(); + } + + /** + * Gets the 'translation.dumper.xliff' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\XliffFileDumper A Symfony\Component\Translation\Dumper\XliffFileDumper instance. + */ + protected function getTranslation_Dumper_XliffService() + { + return $this->services['translation.dumper.xliff'] = new \Symfony\Component\Translation\Dumper\XliffFileDumper(); + } + + /** + * Gets the 'translation.dumper.yml' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Dumper\YamlFileDumper A Symfony\Component\Translation\Dumper\YamlFileDumper instance. + */ + protected function getTranslation_Dumper_YmlService() + { + return $this->services['translation.dumper.yml'] = new \Symfony\Component\Translation\Dumper\YamlFileDumper(); + } + + /** + * Gets the 'translation.extractor' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Extractor\ChainExtractor A Symfony\Component\Translation\Extractor\ChainExtractor instance. + */ + protected function getTranslation_ExtractorService() + { + $this->services['translation.extractor'] = $instance = new \Symfony\Component\Translation\Extractor\ChainExtractor(); + + $instance->addExtractor('php', $this->get('translation.extractor.php')); + $instance->addExtractor('twig', $this->get('twig.translation.extractor')); + + return $instance; + } + + /** + * Gets the 'translation.extractor.php' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor A Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor instance. + */ + protected function getTranslation_Extractor_PhpService() + { + return $this->services['translation.extractor.php'] = new \Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor(); + } + + /** + * Gets the 'translation.loader' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader A Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader instance. + */ + protected function getTranslation_LoaderService() + { + $a = $this->get('translation.loader.xliff'); + + $this->services['translation.loader'] = $instance = new \Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader(); + + $instance->addLoader('php', $this->get('translation.loader.php')); + $instance->addLoader('yml', $this->get('translation.loader.yml')); + $instance->addLoader('xlf', $a); + $instance->addLoader('xliff', $a); + $instance->addLoader('po', $this->get('translation.loader.po')); + $instance->addLoader('mo', $this->get('translation.loader.mo')); + $instance->addLoader('ts', $this->get('translation.loader.qt')); + $instance->addLoader('csv', $this->get('translation.loader.csv')); + $instance->addLoader('res', $this->get('translation.loader.res')); + $instance->addLoader('dat', $this->get('translation.loader.dat')); + $instance->addLoader('ini', $this->get('translation.loader.ini')); + $instance->addLoader('json', $this->get('translation.loader.json')); + + return $instance; + } + + /** + * Gets the 'translation.loader.csv' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\CsvFileLoader A Symfony\Component\Translation\Loader\CsvFileLoader instance. + */ + protected function getTranslation_Loader_CsvService() + { + return $this->services['translation.loader.csv'] = new \Symfony\Component\Translation\Loader\CsvFileLoader(); + } + + /** + * Gets the 'translation.loader.dat' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\IcuDatFileLoader A Symfony\Component\Translation\Loader\IcuDatFileLoader instance. + */ + protected function getTranslation_Loader_DatService() + { + return $this->services['translation.loader.dat'] = new \Symfony\Component\Translation\Loader\IcuDatFileLoader(); + } + + /** + * Gets the 'translation.loader.ini' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\IniFileLoader A Symfony\Component\Translation\Loader\IniFileLoader instance. + */ + protected function getTranslation_Loader_IniService() + { + return $this->services['translation.loader.ini'] = new \Symfony\Component\Translation\Loader\IniFileLoader(); + } + + /** + * Gets the 'translation.loader.json' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\JsonFileLoader A Symfony\Component\Translation\Loader\JsonFileLoader instance. + */ + protected function getTranslation_Loader_JsonService() + { + return $this->services['translation.loader.json'] = new \Symfony\Component\Translation\Loader\JsonFileLoader(); + } + + /** + * Gets the 'translation.loader.mo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\MoFileLoader A Symfony\Component\Translation\Loader\MoFileLoader instance. + */ + protected function getTranslation_Loader_MoService() + { + return $this->services['translation.loader.mo'] = new \Symfony\Component\Translation\Loader\MoFileLoader(); + } + + /** + * Gets the 'translation.loader.php' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\PhpFileLoader A Symfony\Component\Translation\Loader\PhpFileLoader instance. + */ + protected function getTranslation_Loader_PhpService() + { + return $this->services['translation.loader.php'] = new \Symfony\Component\Translation\Loader\PhpFileLoader(); + } + + /** + * Gets the 'translation.loader.po' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\PoFileLoader A Symfony\Component\Translation\Loader\PoFileLoader instance. + */ + protected function getTranslation_Loader_PoService() + { + return $this->services['translation.loader.po'] = new \Symfony\Component\Translation\Loader\PoFileLoader(); + } + + /** + * Gets the 'translation.loader.qt' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\QtFileLoader A Symfony\Component\Translation\Loader\QtFileLoader instance. + */ + protected function getTranslation_Loader_QtService() + { + return $this->services['translation.loader.qt'] = new \Symfony\Component\Translation\Loader\QtFileLoader(); + } + + /** + * Gets the 'translation.loader.res' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\IcuResFileLoader A Symfony\Component\Translation\Loader\IcuResFileLoader instance. + */ + protected function getTranslation_Loader_ResService() + { + return $this->services['translation.loader.res'] = new \Symfony\Component\Translation\Loader\IcuResFileLoader(); + } + + /** + * Gets the 'translation.loader.xliff' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\XliffFileLoader A Symfony\Component\Translation\Loader\XliffFileLoader instance. + */ + protected function getTranslation_Loader_XliffService() + { + return $this->services['translation.loader.xliff'] = new \Symfony\Component\Translation\Loader\XliffFileLoader(); + } + + /** + * Gets the 'translation.loader.yml' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Loader\YamlFileLoader A Symfony\Component\Translation\Loader\YamlFileLoader instance. + */ + protected function getTranslation_Loader_YmlService() + { + return $this->services['translation.loader.yml'] = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + } + + /** + * Gets the 'translation.writer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\Writer\TranslationWriter A Symfony\Component\Translation\Writer\TranslationWriter instance. + */ + protected function getTranslation_WriterService() + { + $this->services['translation.writer'] = $instance = new \Symfony\Component\Translation\Writer\TranslationWriter(); + + $instance->addDumper('php', $this->get('translation.dumper.php')); + $instance->addDumper('xlf', $this->get('translation.dumper.xliff')); + $instance->addDumper('po', $this->get('translation.dumper.po')); + $instance->addDumper('mo', $this->get('translation.dumper.mo')); + $instance->addDumper('yml', $this->get('translation.dumper.yml')); + $instance->addDumper('ts', $this->get('translation.dumper.qt')); + $instance->addDumper('csv', $this->get('translation.dumper.csv')); + $instance->addDumper('ini', $this->get('translation.dumper.ini')); + $instance->addDumper('json', $this->get('translation.dumper.json')); + $instance->addDumper('res', $this->get('translation.dumper.res')); + + return $instance; + } + + /** + * Gets the 'translator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Translation\IdentityTranslator A Symfony\Component\Translation\IdentityTranslator instance. + */ + protected function getTranslatorService() + { + return $this->services['translator'] = new \Symfony\Component\Translation\IdentityTranslator($this->get('translator.selector')); + } + + /** + * Gets the 'translator.default' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\FrameworkBundle\Translation\Translator A Symfony\Bundle\FrameworkBundle\Translation\Translator instance. + */ + protected function getTranslator_DefaultService() + { + $this->services['translator.default'] = $instance = new \Symfony\Bundle\FrameworkBundle\Translation\Translator($this, $this->get('translator.selector'), array('translation.loader.php' => array(0 => 'php'), 'translation.loader.yml' => array(0 => 'yml'), 'translation.loader.xliff' => array(0 => 'xlf', 1 => 'xliff'), 'translation.loader.po' => array(0 => 'po'), 'translation.loader.mo' => array(0 => 'mo'), 'translation.loader.qt' => array(0 => 'ts'), 'translation.loader.csv' => array(0 => 'csv'), 'translation.loader.res' => array(0 => 'res'), 'translation.loader.dat' => array(0 => 'dat'), 'translation.loader.ini' => array(0 => 'ini'), 'translation.loader.json' => array(0 => 'json')), array('cache_dir' => (__DIR__.'/translations'), 'debug' => true), array()); + + $instance->setConfigCacheFactory($this->get('config_cache_factory')); + + return $instance; + } + + /** + * Gets the 'translator_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\TranslatorListener A Symfony\Component\HttpKernel\EventListener\TranslatorListener instance. + */ + protected function getTranslatorListenerService() + { + return $this->services['translator_listener'] = new \Symfony\Component\HttpKernel\EventListener\TranslatorListener($this->get('translator'), $this->get('request_stack')); + } + + /** + * Gets the 'twig' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Twig_Environment A Twig_Environment instance. + */ + protected function getTwigService() + { + $a = $this->get('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE); + $b = $this->get('request_stack'); + + $c = new \Symfony\Bridge\Twig\AppVariable(); + $c->setEnvironment('dev'); + $c->setDebug(true); + if ($this->has('security.token_storage')) { + $c->setTokenStorage($this->get('security.token_storage', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + if ($this->has('request_stack')) { + $c->setRequestStack($b); + } + + $this->services['twig'] = $instance = new \Twig_Environment($this->get('twig.loader'), array('debug' => true, 'strict_variables' => true, 'exception_controller' => 'twig.controller.exception:showAction', 'form_themes' => array(0 => 'form_div_layout.html.twig'), 'autoescape' => 'filename', 'cache' => (__DIR__.'/twig'), 'charset' => 'UTF-8', 'paths' => array(), 'date' => array('format' => 'F j, Y H:i', 'interval_format' => '%d days', 'timezone' => NULL), 'number_format' => array('decimals' => 0, 'decimal_point' => '.', 'thousands_separator' => ','))); + + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\LogoutUrlExtension($this->get('security.logout_url_generator'))); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\SecurityExtension($this->get('security.authorization_checker', ContainerInterface::NULL_ON_INVALID_REFERENCE))); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\ProfilerExtension($this->get('twig.profile'), $a)); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\TranslationExtension($this->get('translator'))); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\AssetExtension($this->get('assets.packages'))); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\CodeExtension(NULL, ($this->targetDirs[3].'/app'), 'UTF-8')); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\RoutingExtension($this->get('router'))); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\YamlExtension()); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\StopwatchExtension($a, true)); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\ExpressionExtension()); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\HttpKernelExtension($this->get('fragment.handler'))); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\HttpFoundationExtension($b)); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\FormExtension(new \Symfony\Bridge\Twig\Form\TwigRenderer(new \Symfony\Bridge\Twig\Form\TwigRendererEngine(array(0 => 'form_div_layout.html.twig')), $this->get('security.csrf.token_manager', ContainerInterface::NULL_ON_INVALID_REFERENCE)))); + $instance->addExtension(new \Twig_Extension_Debug()); + $instance->addExtension(new \Doctrine\Bundle\DoctrineBundle\Twig\DoctrineExtension()); + $instance->addExtension(new \Symfony\Bridge\Twig\Extension\DumpExtension($this->get('var_dumper.cloner'))); + $instance->addExtension(new \Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension()); + $instance->addGlobal('app', $c); + call_user_func(array(new \Symfony\Bundle\TwigBundle\DependencyInjection\Configurator\EnvironmentConfigurator('F j, Y H:i', '%d days', NULL, 0, '.', ','), 'configure'), $instance); + + return $instance; + } + + /** + * Gets the 'twig.controller.exception' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\TwigBundle\Controller\ExceptionController A Symfony\Bundle\TwigBundle\Controller\ExceptionController instance. + */ + protected function getTwig_Controller_ExceptionService() + { + return $this->services['twig.controller.exception'] = new \Symfony\Bundle\TwigBundle\Controller\ExceptionController($this->get('twig'), true); + } + + /** + * Gets the 'twig.controller.preview_error' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\TwigBundle\Controller\PreviewErrorController A Symfony\Bundle\TwigBundle\Controller\PreviewErrorController instance. + */ + protected function getTwig_Controller_PreviewErrorService() + { + return $this->services['twig.controller.preview_error'] = new \Symfony\Bundle\TwigBundle\Controller\PreviewErrorController($this->get('http_kernel'), 'twig.controller.exception:showAction'); + } + + /** + * Gets the 'twig.exception_listener' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\EventListener\ExceptionListener A Symfony\Component\HttpKernel\EventListener\ExceptionListener instance. + */ + protected function getTwig_ExceptionListenerService() + { + return $this->services['twig.exception_listener'] = new \Symfony\Component\HttpKernel\EventListener\ExceptionListener('twig.controller.exception:showAction', $this->get('monolog.logger.request', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'twig.loader' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\TwigBundle\Loader\FilesystemLoader A Symfony\Bundle\TwigBundle\Loader\FilesystemLoader instance. + */ + protected function getTwig_LoaderService() + { + $this->services['twig.loader'] = $instance = new \Symfony\Bundle\TwigBundle\Loader\FilesystemLoader($this->get('templating.locator'), $this->get('templating.name_parser')); + + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views'), 'Framework'); + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views'), 'Security'); + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views'), 'Twig'); + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/swiftmailer-bundle/Resources/views'), 'Swiftmailer'); + $instance->addPath(($this->targetDirs[3].'/vendor/doctrine/doctrine-bundle/Resources/views'), 'Doctrine'); + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views'), 'Debug'); + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views'), 'WebProfiler'); + $instance->addPath(($this->targetDirs[3].'/app/Resources/views')); + $instance->addPath(($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form')); + + return $instance; + } + + /** + * Gets the 'twig.profile' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Twig_Profiler_Profile A Twig_Profiler_Profile instance. + */ + protected function getTwig_ProfileService() + { + return $this->services['twig.profile'] = new \Twig_Profiler_Profile(); + } + + /** + * Gets the 'twig.translation.extractor' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bridge\Twig\Translation\TwigExtractor A Symfony\Bridge\Twig\Translation\TwigExtractor instance. + */ + protected function getTwig_Translation_ExtractorService() + { + return $this->services['twig.translation.extractor'] = new \Symfony\Bridge\Twig\Translation\TwigExtractor($this->get('twig')); + } + + /** + * Gets the 'uri_signer' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\HttpKernel\UriSigner A Symfony\Component\HttpKernel\UriSigner instance. + */ + protected function getUriSignerService() + { + return $this->services['uri_signer'] = new \Symfony\Component\HttpKernel\UriSigner('a958d119a837605337f0224ff71592384a432e2f'); + } + + /** + * Gets the 'validator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Validator\Validator\ValidatorInterface A Symfony\Component\Validator\Validator\ValidatorInterface instance. + */ + protected function getValidatorService() + { + return $this->services['validator'] = $this->get('validator.builder')->getValidator(); + } + + /** + * Gets the 'validator.builder' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Validator\ValidatorBuilderInterface A Symfony\Component\Validator\ValidatorBuilderInterface instance. + */ + protected function getValidator_BuilderService() + { + $this->services['validator.builder'] = $instance = \Symfony\Component\Validator\Validation::createValidatorBuilder(); + + $instance->setConstraintValidatorFactory(new \Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory($this, array('validator.expression' => 'validator.expression', 'Symfony\\Component\\Validator\\Constraints\\EmailValidator' => 'validator.email', 'security.validator.user_password' => 'security.validator.user_password', 'doctrine.orm.validator.unique' => 'doctrine.orm.validator.unique'))); + $instance->setTranslator($this->get('translator')); + $instance->setTranslationDomain('validators'); + $instance->addXmlMappings(array(0 => ($this->targetDirs[3].'/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/config/validation.xml'))); + $instance->enableAnnotationMapping($this->get('annotation_reader')); + $instance->addMethodMapping('loadValidatorMetadata'); + $instance->addObjectInitializers(array(0 => $this->get('doctrine.orm.validator_initializer'))); + + return $instance; + } + + /** + * Gets the 'validator.email' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Validator\Constraints\EmailValidator A Symfony\Component\Validator\Constraints\EmailValidator instance. + */ + protected function getValidator_EmailService() + { + return $this->services['validator.email'] = new \Symfony\Component\Validator\Constraints\EmailValidator(false); + } + + /** + * Gets the 'validator.expression' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\Validator\Constraints\ExpressionValidator A Symfony\Component\Validator\Constraints\ExpressionValidator instance. + */ + protected function getValidator_ExpressionService() + { + return $this->services['validator.expression'] = new \Symfony\Component\Validator\Constraints\ExpressionValidator($this->get('property_accessor')); + } + + /** + * Gets the 'var_dumper.cli_dumper' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\VarDumper\Dumper\CliDumper A Symfony\Component\VarDumper\Dumper\CliDumper instance. + */ + protected function getVarDumper_CliDumperService() + { + return $this->services['var_dumper.cli_dumper'] = new \Symfony\Component\VarDumper\Dumper\CliDumper(NULL, 'UTF-8'); + } + + /** + * Gets the 'var_dumper.cloner' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Component\VarDumper\Cloner\VarCloner A Symfony\Component\VarDumper\Cloner\VarCloner instance. + */ + protected function getVarDumper_ClonerService() + { + $this->services['var_dumper.cloner'] = $instance = new \Symfony\Component\VarDumper\Cloner\VarCloner(); + + $instance->setMaxItems(2500); + $instance->setMaxString(-1); + + return $instance; + } + + /** + * Gets the 'web_profiler.controller.exception' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController A Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController instance. + */ + protected function getWebProfiler_Controller_ExceptionService() + { + return $this->services['web_profiler.controller.exception'] = new \Symfony\Bundle\WebProfilerBundle\Controller\ExceptionController($this->get('profiler', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('twig'), true); + } + + /** + * Gets the 'web_profiler.controller.profiler' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController A Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController instance. + */ + protected function getWebProfiler_Controller_ProfilerService() + { + return $this->services['web_profiler.controller.profiler'] = new \Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController($this->get('router', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('profiler', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('twig'), array('data_collector.request' => array(0 => 'request', 1 => '@WebProfiler/Collector/request.html.twig'), 'data_collector.time' => array(0 => 'time', 1 => '@WebProfiler/Collector/time.html.twig'), 'data_collector.memory' => array(0 => 'memory', 1 => '@WebProfiler/Collector/memory.html.twig'), 'data_collector.ajax' => array(0 => 'ajax', 1 => '@WebProfiler/Collector/ajax.html.twig'), 'data_collector.form' => array(0 => 'form', 1 => '@WebProfiler/Collector/form.html.twig'), 'data_collector.exception' => array(0 => 'exception', 1 => '@WebProfiler/Collector/exception.html.twig'), 'data_collector.logger' => array(0 => 'logger', 1 => '@WebProfiler/Collector/logger.html.twig'), 'data_collector.events' => array(0 => 'events', 1 => '@WebProfiler/Collector/events.html.twig'), 'data_collector.router' => array(0 => 'router', 1 => '@WebProfiler/Collector/router.html.twig'), 'data_collector.security' => array(0 => 'security', 1 => '@Security/Collector/security.html.twig'), 'data_collector.twig' => array(0 => 'twig', 1 => '@WebProfiler/Collector/twig.html.twig'), 'data_collector.doctrine' => array(0 => 'db', 1 => '@Doctrine/Collector/db.html.twig'), 'swiftmailer.data_collector' => array(0 => 'swiftmailer', 1 => '@Swiftmailer/Collector/swiftmailer.html.twig'), 'data_collector.dump' => array(0 => 'dump', 1 => '@Debug/Profiler/dump.html.twig'), 'data_collector.config' => array(0 => 'config', 1 => '@WebProfiler/Collector/config.html.twig')), 'bottom'); + } + + /** + * Gets the 'web_profiler.controller.router' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\WebProfilerBundle\Controller\RouterController A Symfony\Bundle\WebProfilerBundle\Controller\RouterController instance. + */ + protected function getWebProfiler_Controller_RouterService() + { + return $this->services['web_profiler.controller.router'] = new \Symfony\Bundle\WebProfilerBundle\Controller\RouterController($this->get('profiler', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('twig'), $this->get('router', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'web_profiler.debug_toolbar' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener A Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener instance. + */ + protected function getWebProfiler_DebugToolbarService() + { + return $this->services['web_profiler.debug_toolbar'] = new \Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener($this->get('twig'), false, 2, 'bottom', $this->get('router', ContainerInterface::NULL_ON_INVALID_REFERENCE), '^/(app(_[\\w]+)?\\.php/)?_wdt'); + } + + /** + * Gets the 'controller_name_converter' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser A Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser instance. + */ + protected function getControllerNameConverterService() + { + return $this->services['controller_name_converter'] = new \Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser($this->get('kernel')); + } + + /** + * Gets the 'doctrine.dbal.logger.profiling.default' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Doctrine\DBAL\Logging\DebugStack A Doctrine\DBAL\Logging\DebugStack instance. + */ + protected function getDoctrine_Dbal_Logger_Profiling_DefaultService() + { + return $this->services['doctrine.dbal.logger.profiling.default'] = new \Doctrine\DBAL\Logging\DebugStack(); + } + + /** + * Gets the 'router.request_context' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Routing\RequestContext A Symfony\Component\Routing\RequestContext instance. + */ + protected function getRouter_RequestContextService() + { + return $this->services['router.request_context'] = new \Symfony\Component\Routing\RequestContext('', 'GET', 'localhost', 'http', 80, 443); + } + + /** + * Gets the 'security.access.decision_manager' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Security\Core\Authorization\AccessDecisionManager A Symfony\Component\Security\Core\Authorization\AccessDecisionManager instance. + */ + protected function getSecurity_Access_DecisionManagerService() + { + $a = $this->get('security.authentication.trust_resolver'); + + $this->services['security.access.decision_manager'] = $instance = new \Symfony\Component\Security\Core\Authorization\AccessDecisionManager(array(), 'affirmative', false, true); + + $instance->setVoters(array(0 => new \Symfony\Component\Security\Core\Authorization\Voter\RoleVoter(), 1 => new \Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter(new \Symfony\Component\Security\Core\Authorization\ExpressionLanguage(), $a, $this->get('security.role_hierarchy', ContainerInterface::NULL_ON_INVALID_REFERENCE)), 2 => new \Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter($a))); + + return $instance; + } + + /** + * Gets the 'security.authentication.manager' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager A Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager instance. + */ + protected function getSecurity_Authentication_ManagerService() + { + $this->services['security.authentication.manager'] = $instance = new \Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager(array(0 => new \Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider('56a782448eafa8.89273161')), true); + + $instance->setEventDispatcher($this->get('debug.event_dispatcher')); + + return $instance; + } + + /** + * Gets the 'security.authentication.trust_resolver' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver A Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver instance. + */ + protected function getSecurity_Authentication_TrustResolverService() + { + return $this->services['security.authentication.trust_resolver'] = new \Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver('Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken', 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\RememberMeToken'); + } + + /** + * Gets the 'security.logout_url_generator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Security\Http\Logout\LogoutUrlGenerator A Symfony\Component\Security\Http\Logout\LogoutUrlGenerator instance. + */ + protected function getSecurity_LogoutUrlGeneratorService() + { + return $this->services['security.logout_url_generator'] = new \Symfony\Component\Security\Http\Logout\LogoutUrlGenerator($this->get('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('router', ContainerInterface::NULL_ON_INVALID_REFERENCE), $this->get('security.token_storage', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * Gets the 'security.role_hierarchy' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Security\Core\Role\RoleHierarchy A Symfony\Component\Security\Core\Role\RoleHierarchy instance. + */ + protected function getSecurity_RoleHierarchyService() + { + return $this->services['security.role_hierarchy'] = new \Symfony\Component\Security\Core\Role\RoleHierarchy(array()); + } + + /** + * Gets the 'session.storage.metadata_bag' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\HttpFoundation\Session\Storage\MetadataBag A Symfony\Component\HttpFoundation\Session\Storage\MetadataBag instance. + */ + protected function getSession_Storage_MetadataBagService() + { + return $this->services['session.storage.metadata_bag'] = new \Symfony\Component\HttpFoundation\Session\Storage\MetadataBag('_sf2_meta', '0'); + } + + /** + * Gets the 'swiftmailer.mailer.default.transport.eventdispatcher' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Swift_Events_SimpleEventDispatcher A Swift_Events_SimpleEventDispatcher instance. + */ + protected function getSwiftmailer_Mailer_Default_Transport_EventdispatcherService() + { + return $this->services['swiftmailer.mailer.default.transport.eventdispatcher'] = new \Swift_Events_SimpleEventDispatcher(); + } + + /** + * Gets the 'templating.locator' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator A Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator instance. + */ + protected function getTemplating_LocatorService() + { + return $this->services['templating.locator'] = new \Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator($this->get('file_locator'), __DIR__); + } + + /** + * Gets the 'translator.selector' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Symfony\Component\Translation\MessageSelector A Symfony\Component\Translation\MessageSelector instance. + */ + protected function getTranslator_SelectorService() + { + return $this->services['translator.selector'] = new \Symfony\Component\Translation\MessageSelector(); + } + + /** + * {@inheritdoc} + */ + public function getParameter($name) + { + $name = strtolower($name); + + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + + return $this->parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter($name) + { + $name = strtolower($name); + + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === $this->parameterBag) { + $this->parameterBag = new FrozenParameterBag($this->parameters); + } + + return $this->parameterBag; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'kernel.root_dir' => ($this->targetDirs[3].'/app'), + 'kernel.environment' => 'dev', + 'kernel.debug' => true, + 'kernel.name' => 'app', + 'kernel.cache_dir' => __DIR__, + 'kernel.logs_dir' => ($this->targetDirs[2].'/logs'), + 'kernel.bundles' => array( + 'FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', + 'SecurityBundle' => 'Symfony\\Bundle\\SecurityBundle\\SecurityBundle', + 'TwigBundle' => 'Symfony\\Bundle\\TwigBundle\\TwigBundle', + 'MonologBundle' => 'Symfony\\Bundle\\MonologBundle\\MonologBundle', + 'SwiftmailerBundle' => 'Symfony\\Bundle\\SwiftmailerBundle\\SwiftmailerBundle', + 'DoctrineBundle' => 'Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle', + 'SensioFrameworkExtraBundle' => 'Sensio\\Bundle\\FrameworkExtraBundle\\SensioFrameworkExtraBundle', + 'AppBundle' => 'AppBundle\\AppBundle', + 'DebugBundle' => 'Symfony\\Bundle\\DebugBundle\\DebugBundle', + 'WebProfilerBundle' => 'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle', + 'SensioDistributionBundle' => 'Sensio\\Bundle\\DistributionBundle\\SensioDistributionBundle', + 'SensioGeneratorBundle' => 'Sensio\\Bundle\\GeneratorBundle\\SensioGeneratorBundle', + ), + 'kernel.charset' => 'UTF-8', + 'kernel.container_class' => 'appDevDebugProjectContainer', + 'database_host' => '127.0.0.1', + 'database_port' => 3306, + 'database_name' => 'symfony', + 'database_user' => 'root', + 'database_password' => 'yolo', + 'mailer_transport' => 'smtp', + 'mailer_host' => '127.0.0.1', + 'mailer_user' => NULL, + 'mailer_password' => NULL, + 'secret' => 'a958d119a837605337f0224ff71592384a432e2f', + 'locale' => 'en', + 'fragment.renderer.hinclude.global_template' => NULL, + 'fragment.path' => '/_fragment', + 'kernel.secret' => 'a958d119a837605337f0224ff71592384a432e2f', + 'kernel.http_method_override' => true, + 'kernel.trusted_hosts' => array( + + ), + 'kernel.trusted_proxies' => array( + + ), + 'kernel.default_locale' => 'en', + 'session.metadata.storage_key' => '_sf2_meta', + 'session.storage.options' => array( + 'cookie_httponly' => true, + 'gc_probability' => 1, + ), + 'session.save_path' => ($this->targetDirs[3].'/app/../var/sessions/dev'), + 'session.metadata.update_threshold' => '0', + 'form.type_extension.csrf.enabled' => true, + 'form.type_extension.csrf.field_name' => '_token', + 'templating.helper.code.file_link_format' => NULL, + 'templating.loader.cache.path' => NULL, + 'templating.engines' => array( + 0 => 'twig', + ), + 'validator.mapping.cache.prefix' => '', + 'validator.translation_domain' => 'validators', + 'profiler_listener.only_exceptions' => false, + 'profiler_listener.only_master_requests' => false, + 'profiler.storage.dsn' => ('file:'.__DIR__.'/profiler'), + 'router.options.generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'router.options.generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'router.options.generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper', + 'router.options.matcher_class' => 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', + 'router.options.matcher_base_class' => 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', + 'router.options.matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper', + 'router.options.matcher.cache_class' => 'appDevUrlMatcher', + 'router.options.generator.cache_class' => 'appDevUrlGenerator', + 'router.request_context.host' => 'localhost', + 'router.request_context.scheme' => 'http', + 'router.request_context.base_url' => '', + 'router.resource' => ($this->targetDirs[3].'/app/config/routing_dev.yml'), + 'router.cache_class_prefix' => 'appDev', + 'request_listener.http_port' => 80, + 'request_listener.https_port' => 443, + 'debug.error_handler.throw_at' => -1, + 'debug.container.dump' => (__DIR__.'/appDevDebugProjectContainer.xml'), + 'security.authentication.trust_resolver.anonymous_class' => 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\AnonymousToken', + 'security.authentication.trust_resolver.rememberme_class' => 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\RememberMeToken', + 'security.role_hierarchy.roles' => array( + + ), + 'security.access.denied_url' => NULL, + 'security.authentication.manager.erase_credentials' => true, + 'security.authentication.session_strategy.strategy' => 'migrate', + 'security.access.always_authenticate_before_granting' => false, + 'security.authentication.hide_user_not_found' => true, + 'twig.exception_listener.controller' => 'twig.controller.exception:showAction', + 'twig.form.resources' => array( + 0 => 'form_div_layout.html.twig', + ), + 'monolog.logger.class' => 'Symfony\\Bridge\\Monolog\\Logger', + 'monolog.gelf.publisher.class' => 'Gelf\\MessagePublisher', + 'monolog.gelfphp.publisher.class' => 'Gelf\\Publisher', + 'monolog.handler.stream.class' => 'Monolog\\Handler\\StreamHandler', + 'monolog.handler.console.class' => 'Symfony\\Bridge\\Monolog\\Handler\\ConsoleHandler', + 'monolog.handler.group.class' => 'Monolog\\Handler\\GroupHandler', + 'monolog.handler.buffer.class' => 'Monolog\\Handler\\BufferHandler', + 'monolog.handler.rotating_file.class' => 'Monolog\\Handler\\RotatingFileHandler', + 'monolog.handler.syslog.class' => 'Monolog\\Handler\\SyslogHandler', + 'monolog.handler.syslogudp.class' => 'Monolog\\Handler\\SyslogUdpHandler', + 'monolog.handler.null.class' => 'Monolog\\Handler\\NullHandler', + 'monolog.handler.test.class' => 'Monolog\\Handler\\TestHandler', + 'monolog.handler.gelf.class' => 'Monolog\\Handler\\GelfHandler', + 'monolog.handler.rollbar.class' => 'Monolog\\Handler\\RollbarHandler', + 'monolog.handler.flowdock.class' => 'Monolog\\Handler\\FlowdockHandler', + 'monolog.handler.browser_console.class' => 'Monolog\\Handler\\BrowserConsoleHandler', + 'monolog.handler.firephp.class' => 'Symfony\\Bridge\\Monolog\\Handler\\FirePHPHandler', + 'monolog.handler.chromephp.class' => 'Symfony\\Bridge\\Monolog\\Handler\\ChromePhpHandler', + 'monolog.handler.debug.class' => 'Symfony\\Bridge\\Monolog\\Handler\\DebugHandler', + 'monolog.handler.swift_mailer.class' => 'Symfony\\Bridge\\Monolog\\Handler\\SwiftMailerHandler', + 'monolog.handler.native_mailer.class' => 'Monolog\\Handler\\NativeMailerHandler', + 'monolog.handler.socket.class' => 'Monolog\\Handler\\SocketHandler', + 'monolog.handler.pushover.class' => 'Monolog\\Handler\\PushoverHandler', + 'monolog.handler.raven.class' => 'Monolog\\Handler\\RavenHandler', + 'monolog.handler.newrelic.class' => 'Monolog\\Handler\\NewRelicHandler', + 'monolog.handler.hipchat.class' => 'Monolog\\Handler\\HipChatHandler', + 'monolog.handler.slack.class' => 'Monolog\\Handler\\SlackHandler', + 'monolog.handler.cube.class' => 'Monolog\\Handler\\CubeHandler', + 'monolog.handler.amqp.class' => 'Monolog\\Handler\\AmqpHandler', + 'monolog.handler.error_log.class' => 'Monolog\\Handler\\ErrorLogHandler', + 'monolog.handler.loggly.class' => 'Monolog\\Handler\\LogglyHandler', + 'monolog.handler.logentries.class' => 'Monolog\\Handler\\LogEntriesHandler', + 'monolog.handler.whatfailuregroup.class' => 'Monolog\\Handler\\WhatFailureGroupHandler', + 'monolog.activation_strategy.not_found.class' => 'Symfony\\Bundle\\MonologBundle\\NotFoundActivationStrategy', + 'monolog.handler.fingers_crossed.class' => 'Monolog\\Handler\\FingersCrossedHandler', + 'monolog.handler.fingers_crossed.error_level_activation_strategy.class' => 'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy', + 'monolog.handler.filter.class' => 'Monolog\\Handler\\FilterHandler', + 'monolog.handler.mongo.class' => 'Monolog\\Handler\\MongoDBHandler', + 'monolog.mongo.client.class' => 'MongoClient', + 'monolog.handler.elasticsearch.class' => 'Monolog\\Handler\\ElasticSearchHandler', + 'monolog.elastica.client.class' => 'Elastica\\Client', + 'monolog.swift_mailer.handlers' => array( + + ), + 'monolog.handlers_to_channels' => array( + 'monolog.handler.console' => array( + 'type' => 'exclusive', + 'elements' => array( + 0 => 'event', + 1 => 'doctrine', + ), + ), + 'monolog.handler.main' => array( + 'type' => 'exclusive', + 'elements' => array( + 0 => 'event', + ), + ), + ), + 'swiftmailer.class' => 'Swift_Mailer', + 'swiftmailer.transport.sendmail.class' => 'Swift_Transport_SendmailTransport', + 'swiftmailer.transport.mail.class' => 'Swift_Transport_MailTransport', + 'swiftmailer.transport.failover.class' => 'Swift_Transport_FailoverTransport', + 'swiftmailer.plugin.redirecting.class' => 'Swift_Plugins_RedirectingPlugin', + 'swiftmailer.plugin.impersonate.class' => 'Swift_Plugins_ImpersonatePlugin', + 'swiftmailer.plugin.messagelogger.class' => 'Swift_Plugins_MessageLogger', + 'swiftmailer.plugin.antiflood.class' => 'Swift_Plugins_AntiFloodPlugin', + 'swiftmailer.transport.smtp.class' => 'Swift_Transport_EsmtpTransport', + 'swiftmailer.plugin.blackhole.class' => 'Swift_Plugins_BlackholePlugin', + 'swiftmailer.spool.file.class' => 'Swift_FileSpool', + 'swiftmailer.spool.memory.class' => 'Swift_MemorySpool', + 'swiftmailer.email_sender.listener.class' => 'Symfony\\Bundle\\SwiftmailerBundle\\EventListener\\EmailSenderListener', + 'swiftmailer.data_collector.class' => 'Symfony\\Bundle\\SwiftmailerBundle\\DataCollector\\MessageDataCollector', + 'swiftmailer.mailer.default.transport.name' => 'smtp', + 'swiftmailer.mailer.default.delivery.enabled' => true, + 'swiftmailer.mailer.default.transport.smtp.encryption' => NULL, + 'swiftmailer.mailer.default.transport.smtp.port' => 25, + 'swiftmailer.mailer.default.transport.smtp.host' => '127.0.0.1', + 'swiftmailer.mailer.default.transport.smtp.username' => NULL, + 'swiftmailer.mailer.default.transport.smtp.password' => NULL, + 'swiftmailer.mailer.default.transport.smtp.auth_mode' => NULL, + 'swiftmailer.mailer.default.transport.smtp.timeout' => 30, + 'swiftmailer.mailer.default.transport.smtp.source_ip' => NULL, + 'swiftmailer.spool.default.memory.path' => (__DIR__.'/swiftmailer/spool/default'), + 'swiftmailer.mailer.default.spool.enabled' => true, + 'swiftmailer.mailer.default.plugin.impersonate' => NULL, + 'swiftmailer.mailer.default.single_address' => NULL, + 'swiftmailer.spool.enabled' => true, + 'swiftmailer.delivery.enabled' => true, + 'swiftmailer.single_address' => NULL, + 'swiftmailer.mailers' => array( + 'default' => 'swiftmailer.mailer.default', + ), + 'swiftmailer.default_mailer' => 'default', + 'doctrine_cache.apc.class' => 'Doctrine\\Common\\Cache\\ApcCache', + 'doctrine_cache.array.class' => 'Doctrine\\Common\\Cache\\ArrayCache', + 'doctrine_cache.chain.class' => 'Doctrine\\Common\\Cache\\ChainCache', + 'doctrine_cache.couchbase.class' => 'Doctrine\\Common\\Cache\\CouchbaseCache', + 'doctrine_cache.couchbase.connection.class' => 'Couchbase', + 'doctrine_cache.couchbase.hostnames' => 'localhost:8091', + 'doctrine_cache.file_system.class' => 'Doctrine\\Common\\Cache\\FilesystemCache', + 'doctrine_cache.php_file.class' => 'Doctrine\\Common\\Cache\\PhpFileCache', + 'doctrine_cache.memcache.class' => 'Doctrine\\Common\\Cache\\MemcacheCache', + 'doctrine_cache.memcache.connection.class' => 'Memcache', + 'doctrine_cache.memcache.host' => 'localhost', + 'doctrine_cache.memcache.port' => 11211, + 'doctrine_cache.memcached.class' => 'Doctrine\\Common\\Cache\\MemcachedCache', + 'doctrine_cache.memcached.connection.class' => 'Memcached', + 'doctrine_cache.memcached.host' => 'localhost', + 'doctrine_cache.memcached.port' => 11211, + 'doctrine_cache.mongodb.class' => 'Doctrine\\Common\\Cache\\MongoDBCache', + 'doctrine_cache.mongodb.collection.class' => 'MongoCollection', + 'doctrine_cache.mongodb.connection.class' => 'MongoClient', + 'doctrine_cache.mongodb.server' => 'localhost:27017', + 'doctrine_cache.redis.class' => 'Doctrine\\Common\\Cache\\RedisCache', + 'doctrine_cache.redis.connection.class' => 'Redis', + 'doctrine_cache.redis.host' => 'localhost', + 'doctrine_cache.redis.port' => 6379, + 'doctrine_cache.riak.class' => 'Doctrine\\Common\\Cache\\RiakCache', + 'doctrine_cache.riak.bucket.class' => 'Riak\\Bucket', + 'doctrine_cache.riak.connection.class' => 'Riak\\Connection', + 'doctrine_cache.riak.bucket_property_list.class' => 'Riak\\BucketPropertyList', + 'doctrine_cache.riak.host' => 'localhost', + 'doctrine_cache.riak.port' => 8087, + 'doctrine_cache.sqlite3.class' => 'Doctrine\\Common\\Cache\\SQLite3Cache', + 'doctrine_cache.sqlite3.connection.class' => 'SQLite3', + 'doctrine_cache.void.class' => 'Doctrine\\Common\\Cache\\VoidCache', + 'doctrine_cache.wincache.class' => 'Doctrine\\Common\\Cache\\WinCacheCache', + 'doctrine_cache.xcache.class' => 'Doctrine\\Common\\Cache\\XcacheCache', + 'doctrine_cache.zenddata.class' => 'Doctrine\\Common\\Cache\\ZendDataCache', + 'doctrine_cache.security.acl.cache.class' => 'Doctrine\\Bundle\\DoctrineCacheBundle\\Acl\\Model\\AclCache', + 'doctrine.dbal.logger.chain.class' => 'Doctrine\\DBAL\\Logging\\LoggerChain', + 'doctrine.dbal.logger.profiling.class' => 'Doctrine\\DBAL\\Logging\\DebugStack', + 'doctrine.dbal.logger.class' => 'Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger', + 'doctrine.dbal.configuration.class' => 'Doctrine\\DBAL\\Configuration', + 'doctrine.data_collector.class' => 'Doctrine\\Bundle\\DoctrineBundle\\DataCollector\\DoctrineDataCollector', + 'doctrine.dbal.connection.event_manager.class' => 'Symfony\\Bridge\\Doctrine\\ContainerAwareEventManager', + 'doctrine.dbal.connection_factory.class' => 'Doctrine\\Bundle\\DoctrineBundle\\ConnectionFactory', + 'doctrine.dbal.events.mysql_session_init.class' => 'Doctrine\\DBAL\\Event\\Listeners\\MysqlSessionInit', + 'doctrine.dbal.events.oracle_session_init.class' => 'Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit', + 'doctrine.class' => 'Doctrine\\Bundle\\DoctrineBundle\\Registry', + 'doctrine.entity_managers' => array( + 'default' => 'doctrine.orm.default_entity_manager', + ), + 'doctrine.default_entity_manager' => 'default', + 'doctrine.dbal.connection_factory.types' => array( + + ), + 'doctrine.connections' => array( + 'default' => 'doctrine.dbal.default_connection', + ), + 'doctrine.default_connection' => 'default', + 'doctrine.orm.configuration.class' => 'Doctrine\\ORM\\Configuration', + 'doctrine.orm.entity_manager.class' => 'Doctrine\\ORM\\EntityManager', + 'doctrine.orm.manager_configurator.class' => 'Doctrine\\Bundle\\DoctrineBundle\\ManagerConfigurator', + 'doctrine.orm.cache.array.class' => 'Doctrine\\Common\\Cache\\ArrayCache', + 'doctrine.orm.cache.apc.class' => 'Doctrine\\Common\\Cache\\ApcCache', + 'doctrine.orm.cache.memcache.class' => 'Doctrine\\Common\\Cache\\MemcacheCache', + 'doctrine.orm.cache.memcache_host' => 'localhost', + 'doctrine.orm.cache.memcache_port' => 11211, + 'doctrine.orm.cache.memcache_instance.class' => 'Memcache', + 'doctrine.orm.cache.memcached.class' => 'Doctrine\\Common\\Cache\\MemcachedCache', + 'doctrine.orm.cache.memcached_host' => 'localhost', + 'doctrine.orm.cache.memcached_port' => 11211, + 'doctrine.orm.cache.memcached_instance.class' => 'Memcached', + 'doctrine.orm.cache.redis.class' => 'Doctrine\\Common\\Cache\\RedisCache', + 'doctrine.orm.cache.redis_host' => 'localhost', + 'doctrine.orm.cache.redis_port' => 6379, + 'doctrine.orm.cache.redis_instance.class' => 'Redis', + 'doctrine.orm.cache.xcache.class' => 'Doctrine\\Common\\Cache\\XcacheCache', + 'doctrine.orm.cache.wincache.class' => 'Doctrine\\Common\\Cache\\WinCacheCache', + 'doctrine.orm.cache.zenddata.class' => 'Doctrine\\Common\\Cache\\ZendDataCache', + 'doctrine.orm.metadata.driver_chain.class' => 'Doctrine\\Common\\Persistence\\Mapping\\Driver\\MappingDriverChain', + 'doctrine.orm.metadata.annotation.class' => 'Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver', + 'doctrine.orm.metadata.xml.class' => 'Doctrine\\ORM\\Mapping\\Driver\\SimplifiedXmlDriver', + 'doctrine.orm.metadata.yml.class' => 'Doctrine\\ORM\\Mapping\\Driver\\SimplifiedYamlDriver', + 'doctrine.orm.metadata.php.class' => 'Doctrine\\ORM\\Mapping\\Driver\\PHPDriver', + 'doctrine.orm.metadata.staticphp.class' => 'Doctrine\\ORM\\Mapping\\Driver\\StaticPHPDriver', + 'doctrine.orm.proxy_cache_warmer.class' => 'Symfony\\Bridge\\Doctrine\\CacheWarmer\\ProxyCacheWarmer', + 'form.type_guesser.doctrine.class' => 'Symfony\\Bridge\\Doctrine\\Form\\DoctrineOrmTypeGuesser', + 'doctrine.orm.validator.unique.class' => 'Symfony\\Bridge\\Doctrine\\Validator\\Constraints\\UniqueEntityValidator', + 'doctrine.orm.validator_initializer.class' => 'Symfony\\Bridge\\Doctrine\\Validator\\DoctrineInitializer', + 'doctrine.orm.security.user.provider.class' => 'Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider', + 'doctrine.orm.listeners.resolve_target_entity.class' => 'Doctrine\\ORM\\Tools\\ResolveTargetEntityListener', + 'doctrine.orm.listeners.attach_entity_listeners.class' => 'Doctrine\\ORM\\Tools\\AttachEntityListenersListener', + 'doctrine.orm.naming_strategy.default.class' => 'Doctrine\\ORM\\Mapping\\DefaultNamingStrategy', + 'doctrine.orm.naming_strategy.underscore.class' => 'Doctrine\\ORM\\Mapping\\UnderscoreNamingStrategy', + 'doctrine.orm.quote_strategy.default.class' => 'Doctrine\\ORM\\Mapping\\DefaultQuoteStrategy', + 'doctrine.orm.quote_strategy.ansi.class' => 'Doctrine\\ORM\\Mapping\\AnsiQuoteStrategy', + 'doctrine.orm.entity_listener_resolver.class' => 'Doctrine\\ORM\\Mapping\\DefaultEntityListenerResolver', + 'doctrine.orm.second_level_cache.default_cache_factory.class' => 'Doctrine\\ORM\\Cache\\DefaultCacheFactory', + 'doctrine.orm.second_level_cache.default_region.class' => 'Doctrine\\ORM\\Cache\\Region\\DefaultRegion', + 'doctrine.orm.second_level_cache.filelock_region.class' => 'Doctrine\\ORM\\Cache\\Region\\FileLockRegion', + 'doctrine.orm.second_level_cache.logger_chain.class' => 'Doctrine\\ORM\\Cache\\Logging\\CacheLoggerChain', + 'doctrine.orm.second_level_cache.logger_statistics.class' => 'Doctrine\\ORM\\Cache\\Logging\\StatisticsCacheLogger', + 'doctrine.orm.second_level_cache.cache_configuration.class' => 'Doctrine\\ORM\\Cache\\CacheConfiguration', + 'doctrine.orm.second_level_cache.regions_configuration.class' => 'Doctrine\\ORM\\Cache\\RegionsConfiguration', + 'doctrine.orm.auto_generate_proxy_classes' => true, + 'doctrine.orm.proxy_dir' => (__DIR__.'/doctrine/orm/Proxies'), + 'doctrine.orm.proxy_namespace' => 'Proxies', + 'sensio_framework_extra.view.guesser.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\Templating\\TemplateGuesser', + 'sensio_framework_extra.controller.listener.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ControllerListener', + 'sensio_framework_extra.routing.loader.annot_dir.class' => 'Symfony\\Component\\Routing\\Loader\\AnnotationDirectoryLoader', + 'sensio_framework_extra.routing.loader.annot_file.class' => 'Symfony\\Component\\Routing\\Loader\\AnnotationFileLoader', + 'sensio_framework_extra.routing.loader.annot_class.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\Routing\\AnnotatedRouteControllerLoader', + 'sensio_framework_extra.converter.listener.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ParamConverterListener', + 'sensio_framework_extra.converter.manager.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterManager', + 'sensio_framework_extra.converter.doctrine.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DoctrineParamConverter', + 'sensio_framework_extra.converter.datetime.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DateTimeParamConverter', + 'sensio_framework_extra.view.listener.class' => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\TemplateListener', + 'web_profiler.debug_toolbar.position' => 'bottom', + 'web_profiler.debug_toolbar.intercept_redirects' => false, + 'web_profiler.debug_toolbar.mode' => 2, + 'data_collector.templates' => array( + 'data_collector.request' => array( + 0 => 'request', + 1 => '@WebProfiler/Collector/request.html.twig', + ), + 'data_collector.time' => array( + 0 => 'time', + 1 => '@WebProfiler/Collector/time.html.twig', + ), + 'data_collector.memory' => array( + 0 => 'memory', + 1 => '@WebProfiler/Collector/memory.html.twig', + ), + 'data_collector.ajax' => array( + 0 => 'ajax', + 1 => '@WebProfiler/Collector/ajax.html.twig', + ), + 'data_collector.form' => array( + 0 => 'form', + 1 => '@WebProfiler/Collector/form.html.twig', + ), + 'data_collector.exception' => array( + 0 => 'exception', + 1 => '@WebProfiler/Collector/exception.html.twig', + ), + 'data_collector.logger' => array( + 0 => 'logger', + 1 => '@WebProfiler/Collector/logger.html.twig', + ), + 'data_collector.events' => array( + 0 => 'events', + 1 => '@WebProfiler/Collector/events.html.twig', + ), + 'data_collector.router' => array( + 0 => 'router', + 1 => '@WebProfiler/Collector/router.html.twig', + ), + 'data_collector.security' => array( + 0 => 'security', + 1 => '@Security/Collector/security.html.twig', + ), + 'data_collector.twig' => array( + 0 => 'twig', + 1 => '@WebProfiler/Collector/twig.html.twig', + ), + 'data_collector.doctrine' => array( + 0 => 'db', + 1 => '@Doctrine/Collector/db.html.twig', + ), + 'swiftmailer.data_collector' => array( + 0 => 'swiftmailer', + 1 => '@Swiftmailer/Collector/swiftmailer.html.twig', + ), + 'data_collector.dump' => array( + 0 => 'dump', + 1 => '@Debug/Profiler/dump.html.twig', + ), + 'data_collector.config' => array( + 0 => 'config', + 1 => '@WebProfiler/Collector/config.html.twig', + ), + ), + 'console.command.ids' => array( + 0 => 'sensio_distribution.security_checker.command', + ), + ); + } +} diff --git a/var/cache/dev/appDevDebugProjectContainer.php.meta b/var/cache/dev/appDevDebugProjectContainer.php.meta new file mode 100644 index 0000000..f3b909c --- /dev/null +++ b/var/cache/dev/appDevDebugProjectContainer.php.meta @@ -0,0 +1 @@ +a:188:{i:0;C:46:"Symfony\Component\Config\Resource\FileResource":78:{s:70:"/Users/senaria/Projects/test-update-doctrine-command/app/AppKernel.php";}i:1;C:46:"Symfony\Component\Config\Resource\FileResource":128:{s:119:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php";}i:2;C:46:"Symfony\Component\Config\Resource\FileResource":139:{s:130:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php";}i:3;C:46:"Symfony\Component\Config\Resource\FileResource":135:{s:126:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/Bundle.php";}i:4;C:46:"Symfony\Component\Config\Resource\FileResource":137:{s:128:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php";}i:6;C:46:"Symfony\Component\Config\Resource\FileResource":129:{s:120:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigBundle.php";}i:8;C:46:"Symfony\Component\Config\Resource\FileResource":109:{s:100:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/MonologBundle.php";}i:10;C:46:"Symfony\Component\Config\Resource\FileResource":117:{s:108:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/SwiftmailerBundle.php";}i:12;C:46:"Symfony\Component\Config\Resource\FileResource":112:{s:103:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/DoctrineBundle.php";}i:14;C:46:"Symfony\Component\Config\Resource\FileResource":129:{s:120:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/SensioFrameworkExtraBundle.php";}i:16;C:46:"Symfony\Component\Config\Resource\FileResource":88:{s:80:"/Users/senaria/Projects/test-update-doctrine-command/src/AppBundle/AppBundle.php";}i:18;C:46:"Symfony\Component\Config\Resource\FileResource":131:{s:122:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DebugBundle.php";}i:20;C:46:"Symfony\Component\Config\Resource\FileResource":143:{s:134:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php";}i:22;C:46:"Symfony\Component\Config\Resource\FileResource":124:{s:115:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/distribution-bundle/SensioDistributionBundle.php";}i:24;C:46:"Symfony\Component\Config\Resource\FileResource":118:{s:109:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/generator-bundle/SensioGeneratorBundle.php";}i:26;C:46:"Symfony\Component\Config\Resource\FileResource":172:{s:163:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php";}i:27;C:46:"Symfony\Component\Config\Resource\FileResource":165:{s:156:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php";}i:28;C:46:"Symfony\Component\Config\Resource\FileResource":168:{s:159:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php";}i:29;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php";}i:30;C:46:"Symfony\Component\Config\Resource\FileResource":180:{s:171:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php";}i:31;C:46:"Symfony\Component\Config\Resource\FileResource":181:{s:172:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php";}i:32;C:46:"Symfony\Component\Config\Resource\FileResource":174:{s:165:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php";}i:33;C:46:"Symfony\Component\Config\Resource\FileResource":161:{s:152:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php";}i:34;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php";}i:35;C:46:"Symfony\Component\Config\Resource\FileResource":174:{s:165:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php";}i:36;C:46:"Symfony\Component\Config\Resource\FileResource":171:{s:162:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php";}i:37;C:46:"Symfony\Component\Config\Resource\FileResource":172:{s:163:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php";}i:38;C:46:"Symfony\Component\Config\Resource\FileResource":187:{s:178:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php";}i:39;C:46:"Symfony\Component\Config\Resource\FileResource":177:{s:168:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php";}i:40;C:46:"Symfony\Component\Config\Resource\FileResource":174:{s:165:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php";}i:41;C:46:"Symfony\Component\Config\Resource\FileResource":162:{s:153:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php";}i:42;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php";}i:43;C:46:"Symfony\Component\Config\Resource\FileResource":169:{s:160:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php";}i:44;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php";}i:45;C:46:"Symfony\Component\Config\Resource\FileResource":182:{s:173:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php";}i:46;C:46:"Symfony\Component\Config\Resource\FileResource":174:{s:165:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php";}i:47;C:46:"Symfony\Component\Config\Resource\FileResource":168:{s:159:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php";}i:48;C:46:"Symfony\Component\Config\Resource\FileResource":173:{s:164:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php";}i:49;C:46:"Symfony\Component\Config\Resource\FileResource":161:{s:152:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php";}i:50;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php";}i:51;C:46:"Symfony\Component\Config\Resource\FileResource":162:{s:153:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php";}i:52;C:46:"Symfony\Component\Config\Resource\FileResource":169:{s:160:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php";}i:53;C:46:"Symfony\Component\Config\Resource\FileResource":142:{s:133:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/LoggerChannelPass.php";}i:54;C:46:"Symfony\Component\Config\Resource\FileResource":141:{s:132:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/DebugHandlerPass.php";}i:55;C:46:"Symfony\Component\Config\Resource\FileResource":142:{s:133:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddProcessorsPass.php";}i:56;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddSwiftMailerTransportPass.php";}i:57;C:46:"Symfony\Component\Config\Resource\FileResource":148:{s:139:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/DependencyInjection/Compiler/RegisterPluginsPass.php";}i:58;C:46:"Symfony\Component\Config\Resource\FileResource":190:{s:181:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php";}i:59;C:46:"Symfony\Component\Config\Resource\FileResource":172:{s:163:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php";}i:60;C:46:"Symfony\Component\Config\Resource\FileResource":145:{s:136:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/EntityListenerPass.php";}i:61;C:46:"Symfony\Component\Config\Resource\FileResource":153:{s:144:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/AddParamConverterPass.php";}i:62;C:46:"Symfony\Component\Config\Resource\FileResource":142:{s:133:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/LegacyPass.php";}i:63;C:46:"Symfony\Component\Config\Resource\FileResource":170:{s:161:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php";}i:64;C:46:"Symfony\Component\Config\Resource\FileResource":86:{s:78:"/Users/senaria/Projects/test-update-doctrine-command/app/config/config_dev.yml";}i:65;C:46:"Symfony\Component\Config\Resource\FileResource":82:{s:74:"/Users/senaria/Projects/test-update-doctrine-command/app/config/config.yml";}i:66;C:46:"Symfony\Component\Config\Resource\FileResource":86:{s:78:"/Users/senaria/Projects/test-update-doctrine-command/app/config/parameters.yml";}i:67;C:46:"Symfony\Component\Config\Resource\FileResource":84:{s:76:"/Users/senaria/Projects/test-update-doctrine-command/app/config/security.yml";}i:68;C:46:"Symfony\Component\Config\Resource\FileResource":84:{s:76:"/Users/senaria/Projects/test-update-doctrine-command/app/config/services.yml";}i:69;C:46:"Symfony\Component\Config\Resource\FileResource":163:{s:154:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php";}i:70;C:57:"Symfony\Component\HttpKernel\Config\EnvParametersResource":57:{a:2:{s:6:"prefix";s:9:"SYMFONY__";s:9:"variables";a:0:{}}}i:71;C:46:"Symfony\Component\Config\Resource\FileResource":173:{s:164:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php";}i:72;C:46:"Symfony\Component\Config\Resource\FileResource":171:{s:162:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php";}i:107;C:46:"Symfony\Component\Config\Resource\FileResource":161:{s:152:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ExtensionCompilerPass.php";}i:108;C:46:"Symfony\Component\Config\Resource\FileResource":170:{s:161:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php";}i:109;C:46:"Symfony\Component\Config\Resource\FileResource":160:{s:151:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php";}i:110;C:46:"Symfony\Component\Config\Resource\FileResource":172:{s:163:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php";}i:111;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php";}i:112;C:46:"Symfony\Component\Config\Resource\FileResource":170:{s:161:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php";}i:113;C:46:"Symfony\Component\Config\Resource\FileResource":168:{s:159:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php";}i:114;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php";}i:115;C:46:"Symfony\Component\Config\Resource\FileResource":168:{s:159:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php";}i:116;C:46:"Symfony\Component\Config\Resource\FileResource":167:{s:158:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php";}i:117;C:46:"Symfony\Component\Config\Resource\FileResource":166:{s:157:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php";}i:119;C:46:"Symfony\Component\Config\Resource\FileResource":164:{s:155:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php";}i:120;C:46:"Symfony\Component\Config\Resource\FileResource":169:{s:160:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php";}i:121;C:46:"Symfony\Component\Config\Resource\FileResource":174:{s:165:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php";}i:122;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php";}i:123;C:46:"Symfony\Component\Config\Resource\FileResource":184:{s:175:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php";}i:128;C:46:"Symfony\Component\Config\Resource\FileResource":162:{s:153:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php";}i:129;C:46:"Symfony\Component\Config\Resource\FileResource":151:{s:142:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php";}i:130;C:46:"Symfony\Component\Config\Resource\FileResource":150:{s:141:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/Extension.php";}i:131;C:46:"Symfony\Component\Config\Resource\FileResource":144:{s:135:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml";}i:132;C:46:"Symfony\Component\Config\Resource\FileResource":149:{s:140:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml";}i:133;C:46:"Symfony\Component\Config\Resource\FileResource":158:{s:149:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml";}i:134;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml";}i:135;C:46:"Symfony\Component\Config\Resource\FileResource":156:{s:147:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml";}i:136;C:46:"Symfony\Component\Config\Resource\FileResource":148:{s:139:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml";}i:137;C:46:"Symfony\Component\Config\Resource\FileResource":145:{s:136:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml";}i:138;C:46:"Symfony\Component\Config\Resource\FileResource":150:{s:141:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml";}i:139;C:46:"Symfony\Component\Config\Resource\FileResource":154:{s:145:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml";}i:140;C:46:"Symfony\Component\Config\Resource\FileResource":147:{s:138:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml";}i:141;C:46:"Symfony\Component\Config\Resource\FileResource":151:{s:142:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml";}i:142;C:46:"Symfony\Component\Config\Resource\FileResource":150:{s:141:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml";}i:143;C:46:"Symfony\Component\Config\Resource\FileResource":143:{s:134:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/config/validation.xml";}i:144;C:46:"Symfony\Component\Config\Resource\FileResource":158:{s:149:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_listener.xml";}i:145;C:46:"Symfony\Component\Config\Resource\FileResource":150:{s:141:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml";}i:146;C:46:"Symfony\Component\Config\Resource\FileResource":151:{s:142:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml";}i:147;C:46:"Symfony\Component\Config\Resource\FileResource":151:{s:142:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml";}i:148;C:46:"Symfony\Component\Config\Resource\FileResource":148:{s:139:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml";}i:149;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml";}i:150;C:46:"Symfony\Component\Config\Resource\FileResource":151:{s:142:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml";}i:151;C:46:"Symfony\Component\Config\Resource\FileResource":146:{s:137:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml";}i:152;C:46:"Symfony\Component\Config\Resource\FileResource":160:{s:151:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php";}i:155;C:46:"Symfony\Component\Config\Resource\FileResource":148:{s:139:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml";}i:156;C:46:"Symfony\Component\Config\Resource\FileResource":158:{s:149:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml";}i:157;C:46:"Symfony\Component\Config\Resource\FileResource":159:{s:150:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml";}i:158;C:46:"Symfony\Component\Config\Resource\FileResource":154:{s:145:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml";}i:159;C:46:"Symfony\Component\Config\Resource\FileResource":155:{s:146:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml";}i:160;C:46:"Symfony\Component\Config\Resource\FileResource":150:{s:141:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml";}i:161;C:46:"Symfony\Component\Config\Resource\FileResource":145:{s:136:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml";}i:162;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php";}i:165;C:46:"Symfony\Component\Config\Resource\FileResource":140:{s:131:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml";}i:166;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php";}i:167;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":114:{a:2:{i:0;s:88:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/FrameworkBundle/views";i:1;b:0;}}i:168;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":153:{a:2:{i:0;s:126:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views";i:1;b:1;}}i:169;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":113:{a:2:{i:0;s:87:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/SecurityBundle/views";i:1;b:0;}}i:170;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":152:{a:2:{i:0;s:125:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views";i:1;b:1;}}i:171;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":109:{a:2:{i:0;s:83:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/TwigBundle/views";i:1;b:0;}}i:172;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":148:{a:2:{i:0;s:121:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views";i:1;b:1;}}i:173;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":112:{a:2:{i:0;s:86:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/MonologBundle/views";i:1;b:0;}}i:174;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":124:{a:2:{i:0;s:98:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/Resources/views";i:1;b:0;}}i:175;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":116:{a:2:{i:0;s:90:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/SwiftmailerBundle/views";i:1;b:0;}}i:176;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":129:{a:2:{i:0;s:102:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/Resources/views";i:1;b:1;}}i:177;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":113:{a:2:{i:0;s:87:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/DoctrineBundle/views";i:1;b:0;}}i:178;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":127:{a:2:{i:0;s:100:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/Resources/views";i:1;b:1;}}i:179;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":125:{a:2:{i:0;s:99:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/SensioFrameworkExtraBundle/views";i:1;b:0;}}i:180;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":132:{a:2:{i:0;s:105:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/views";i:1;b:0;}}i:181;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":108:{a:2:{i:0;s:82:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/AppBundle/views";i:1;b:0;}}i:182;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":108:{a:2:{i:0;s:82:"/Users/senaria/Projects/test-update-doctrine-command/src/AppBundle/Resources/views";i:1;b:0;}}i:183;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":110:{a:2:{i:0;s:84:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/DebugBundle/views";i:1;b:0;}}i:184;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":149:{a:2:{i:0;s:122:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views";i:1;b:1;}}i:185;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":116:{a:2:{i:0;s:90:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/WebProfilerBundle/views";i:1;b:0;}}i:186;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":155:{a:2:{i:0;s:128:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views";i:1;b:1;}}i:187;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":123:{a:2:{i:0;s:97:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/SensioDistributionBundle/views";i:1;b:0;}}i:188;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":129:{a:2:{i:0;s:102:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/distribution-bundle/Resources/views";i:1;b:0;}}i:189;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":120:{a:2:{i:0;s:94:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/SensioGeneratorBundle/views";i:1;b:0;}}i:190;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":125:{a:2:{i:0;s:99:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/generator-bundle/Resources/views";i:1;b:0;}}i:191;C:55:"Symfony\Component\Config\Resource\FileExistenceResource":98:{a:2:{i:0;s:72:"/Users/senaria/Projects/test-update-doctrine-command/app/Resources/views";i:1;b:1;}}i:192;C:46:"Symfony\Component\Config\Resource\FileResource":132:{s:123:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/DependencyInjection/MonologExtension.php";}i:195;C:46:"Symfony\Component\Config\Resource\FileResource":129:{s:120:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/DependencyInjection/Configuration.php";}i:196;C:46:"Symfony\Component\Config\Resource\FileResource":120:{s:111:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/Resources/config/monolog.xml";}i:197;C:46:"Symfony\Component\Config\Resource\FileResource":140:{s:131:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/DependencyInjection/SwiftmailerExtension.php";}i:200;C:46:"Symfony\Component\Config\Resource\FileResource":128:{s:119:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/Resources/config/swiftmailer.xml";}i:201;C:46:"Symfony\Component\Config\Resource\FileResource":135:{s:126:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/DependencyInjection/DoctrineExtension.php";}i:202;C:46:"Symfony\Component\Config\Resource\FileResource":162:{s:153:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php";}i:205;C:46:"Symfony\Component\Config\Resource\FileResource":129:{s:120:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-cache-bundle/Resources/config/services.xml";}i:206;C:46:"Symfony\Component\Config\Resource\FileResource":119:{s:110:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/Resources/config/dbal.xml";}i:207;C:46:"Symfony\Component\Config\Resource\FileResource":118:{s:109:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/Resources/config/orm.xml";}i:208;C:46:"Symfony\Component\Config\Resource\FileResource":136:{s:127:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config";}i:209;C:46:"Symfony\Component\Config\Resource\FileResource":119:{s:110:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle";}i:210;C:46:"Symfony\Component\Config\Resource\FileResource":135:{s:126:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config";}i:211;C:46:"Symfony\Component\Config\Resource\FileResource":118:{s:109:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle";}i:212;C:46:"Symfony\Component\Config\Resource\FileResource":131:{s:122:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config";}i:213;C:46:"Symfony\Component\Config\Resource\FileResource":114:{s:105:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle";}i:214;C:46:"Symfony\Component\Config\Resource\FileResource":107:{s:99:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle/Resources/config";}i:215;C:46:"Symfony\Component\Config\Resource\FileResource":90:{s:82:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/monolog-bundle";}i:216;C:46:"Symfony\Component\Config\Resource\FileResource":112:{s:103:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/Resources/config";}i:217;C:46:"Symfony\Component\Config\Resource\FileResource":94:{s:86:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle";}i:218;C:46:"Symfony\Component\Config\Resource\FileResource":110:{s:101:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/Resources/config";}i:219;C:46:"Symfony\Component\Config\Resource\FileResource":92:{s:84:"/Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle";}i:220;C:46:"Symfony\Component\Config\Resource\FileResource":115:{s:106:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config";}i:221;C:46:"Symfony\Component\Config\Resource\FileResource":97:{s:89:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle";}i:222;C:46:"Symfony\Component\Config\Resource\FileResource":74:{s:66:"/Users/senaria/Projects/test-update-doctrine-command/src/AppBundle";}i:223;C:46:"Symfony\Component\Config\Resource\FileResource":132:{s:123:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config";}i:224;C:46:"Symfony\Component\Config\Resource\FileResource":115:{s:106:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle";}i:225;C:46:"Symfony\Component\Config\Resource\FileResource":138:{s:129:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config";}i:226;C:46:"Symfony\Component\Config\Resource\FileResource":121:{s:112:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle";}i:227;C:46:"Symfony\Component\Config\Resource\FileResource":112:{s:103:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/distribution-bundle/Resources/config";}i:228;C:46:"Symfony\Component\Config\Resource\FileResource":94:{s:86:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/distribution-bundle";}i:229;C:46:"Symfony\Component\Config\Resource\FileResource":101:{s:93:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/generator-bundle/Resources";}i:230;C:46:"Symfony\Component\Config\Resource\FileResource":91:{s:83:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/generator-bundle";}i:231;C:46:"Symfony\Component\Config\Resource\FileResource":152:{s:143:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/DependencyInjection/SensioFrameworkExtraExtension.php";}i:234;C:46:"Symfony\Component\Config\Resource\FileResource":136:{s:127:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/DependencyInjection/Configuration.php";}i:235;C:46:"Symfony\Component\Config\Resource\FileResource":128:{s:119:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/services.xml";}i:236;C:46:"Symfony\Component\Config\Resource\FileResource":131:{s:122:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/annotations.xml";}i:237;C:46:"Symfony\Component\Config\Resource\FileResource":127:{s:118:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/routing.xml";}i:238;C:46:"Symfony\Component\Config\Resource\FileResource":130:{s:121:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/converters.xml";}i:239;C:46:"Symfony\Component\Config\Resource\FileResource":124:{s:115:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/view.xml";}i:240;C:46:"Symfony\Component\Config\Resource\FileResource":125:{s:116:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/cache.xml";}i:241;C:46:"Symfony\Component\Config\Resource\FileResource":128:{s:119:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/framework-extra-bundle/Resources/config/security.xml";}i:242;C:46:"Symfony\Component\Config\Resource\FileResource":154:{s:145:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php";}i:245;C:46:"Symfony\Component\Config\Resource\FileResource":145:{s:136:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml";}i:246;C:46:"Symfony\Component\Config\Resource\FileResource":166:{s:157:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php";}i:249;C:46:"Symfony\Component\Config\Resource\FileResource":159:{s:150:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php";}i:250;C:46:"Symfony\Component\Config\Resource\FileResource":151:{s:142:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml";}i:251;C:46:"Symfony\Component\Config\Resource\FileResource":150:{s:141:"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml";}i:252;C:46:"Symfony\Component\Config\Resource\FileResource":147:{s:138:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/distribution-bundle/DependencyInjection/SensioDistributionExtension.php";}i:255;C:46:"Symfony\Component\Config\Resource\FileResource":125:{s:116:"/Users/senaria/Projects/test-update-doctrine-command/vendor/sensio/distribution-bundle/Resources/config/security.xml";}} \ No newline at end of file diff --git a/var/cache/dev/appDevDebugProjectContainer.xml b/var/cache/dev/appDevDebugProjectContainer.xml new file mode 100644 index 0000000..0126e6c --- /dev/null +++ b/var/cache/dev/appDevDebugProjectContainer.xml @@ -0,0 +1,2497 @@ + + + + /Users/senaria/Projects/test-update-doctrine-command/app + dev + true + app + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev + /Users/senaria/Projects/test-update-doctrine-command/var/logs + + Symfony\Bundle\FrameworkBundle\FrameworkBundle + Symfony\Bundle\SecurityBundle\SecurityBundle + Symfony\Bundle\TwigBundle\TwigBundle + Symfony\Bundle\MonologBundle\MonologBundle + Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle + Doctrine\Bundle\DoctrineBundle\DoctrineBundle + Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle + AppBundle\AppBundle + Symfony\Bundle\DebugBundle\DebugBundle + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle + Sensio\Bundle\DistributionBundle\SensioDistributionBundle + Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle + + UTF-8 + appDevDebugProjectContainer + 127.0.0.1 + 3306 + symfony + root + yolo + smtp + 127.0.0.1 + null + null + a958d119a837605337f0224ff71592384a432e2f + en + null + /_fragment + a958d119a837605337f0224ff71592384a432e2f + true + + + en + _sf2_meta + + true + 1 + + /Users/senaria/Projects/test-update-doctrine-command/app/../var/sessions/dev + 0 + true + _token + null + null + + twig + + + validators + false + false + file:/Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/profiler + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper + appDevUrlMatcher + appDevUrlGenerator + localhost + http + + /Users/senaria/Projects/test-update-doctrine-command/app/config/routing_dev.yml + appDev + 80 + 443 + -1 + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/appDevDebugProjectContainer.xml + Symfony\Component\Security\Core\Authentication\Token\AnonymousToken + Symfony\Component\Security\Core\Authentication\Token\RememberMeToken + + null + true + migrate + false + true + twig.controller.exception:showAction + + form_div_layout.html.twig + + Symfony\Bridge\Monolog\Logger + Gelf\MessagePublisher + Gelf\Publisher + Monolog\Handler\StreamHandler + Symfony\Bridge\Monolog\Handler\ConsoleHandler + Monolog\Handler\GroupHandler + Monolog\Handler\BufferHandler + Monolog\Handler\RotatingFileHandler + Monolog\Handler\SyslogHandler + Monolog\Handler\SyslogUdpHandler + Monolog\Handler\NullHandler + Monolog\Handler\TestHandler + Monolog\Handler\GelfHandler + Monolog\Handler\RollbarHandler + Monolog\Handler\FlowdockHandler + Monolog\Handler\BrowserConsoleHandler + Symfony\Bridge\Monolog\Handler\FirePHPHandler + Symfony\Bridge\Monolog\Handler\ChromePhpHandler + Symfony\Bridge\Monolog\Handler\DebugHandler + Symfony\Bridge\Monolog\Handler\SwiftMailerHandler + Monolog\Handler\NativeMailerHandler + Monolog\Handler\SocketHandler + Monolog\Handler\PushoverHandler + Monolog\Handler\RavenHandler + Monolog\Handler\NewRelicHandler + Monolog\Handler\HipChatHandler + Monolog\Handler\SlackHandler + Monolog\Handler\CubeHandler + Monolog\Handler\AmqpHandler + Monolog\Handler\ErrorLogHandler + Monolog\Handler\LogglyHandler + Monolog\Handler\LogEntriesHandler + Monolog\Handler\WhatFailureGroupHandler + Symfony\Bundle\MonologBundle\NotFoundActivationStrategy + Monolog\Handler\FingersCrossedHandler + Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy + Monolog\Handler\FilterHandler + Monolog\Handler\MongoDBHandler + MongoClient + Monolog\Handler\ElasticSearchHandler + Elastica\Client + + + + exclusive + + event + doctrine + + + + exclusive + + event + + + + Swift_Mailer + Swift_Transport_SendmailTransport + Swift_Transport_MailTransport + Swift_Transport_FailoverTransport + Swift_Plugins_RedirectingPlugin + Swift_Plugins_ImpersonatePlugin + Swift_Plugins_MessageLogger + Swift_Plugins_AntiFloodPlugin + Swift_Transport_EsmtpTransport + Swift_Plugins_BlackholePlugin + Swift_FileSpool + Swift_MemorySpool + Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener + Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector + smtp + true + null + 25 + 127.0.0.1 + null + null + null + 30 + null + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/swiftmailer/spool/default + true + null + null + true + true + null + + swiftmailer.mailer.default + + default + Doctrine\Common\Cache\ApcCache + Doctrine\Common\Cache\ArrayCache + Doctrine\Common\Cache\ChainCache + Doctrine\Common\Cache\CouchbaseCache + Couchbase + localhost:8091 + Doctrine\Common\Cache\FilesystemCache + Doctrine\Common\Cache\PhpFileCache + Doctrine\Common\Cache\MemcacheCache + Memcache + localhost + 11211 + Doctrine\Common\Cache\MemcachedCache + Memcached + localhost + 11211 + Doctrine\Common\Cache\MongoDBCache + MongoCollection + MongoClient + localhost:27017 + Doctrine\Common\Cache\RedisCache + Redis + localhost + 6379 + Doctrine\Common\Cache\RiakCache + Riak\Bucket + Riak\Connection + Riak\BucketPropertyList + localhost + 8087 + Doctrine\Common\Cache\SQLite3Cache + SQLite3 + Doctrine\Common\Cache\VoidCache + Doctrine\Common\Cache\WinCacheCache + Doctrine\Common\Cache\XcacheCache + Doctrine\Common\Cache\ZendDataCache + Doctrine\Bundle\DoctrineCacheBundle\Acl\Model\AclCache + Doctrine\DBAL\Logging\LoggerChain + Doctrine\DBAL\Logging\DebugStack + Symfony\Bridge\Doctrine\Logger\DbalLogger + Doctrine\DBAL\Configuration + Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector + Symfony\Bridge\Doctrine\ContainerAwareEventManager + Doctrine\Bundle\DoctrineBundle\ConnectionFactory + Doctrine\DBAL\Event\Listeners\MysqlSessionInit + Doctrine\DBAL\Event\Listeners\OracleSessionInit + Doctrine\Bundle\DoctrineBundle\Registry + + doctrine.orm.default_entity_manager + + default + + + doctrine.dbal.default_connection + + default + Doctrine\ORM\Configuration + Doctrine\ORM\EntityManager + Doctrine\Bundle\DoctrineBundle\ManagerConfigurator + Doctrine\Common\Cache\ArrayCache + Doctrine\Common\Cache\ApcCache + Doctrine\Common\Cache\MemcacheCache + localhost + 11211 + Memcache + Doctrine\Common\Cache\MemcachedCache + localhost + 11211 + Memcached + Doctrine\Common\Cache\RedisCache + localhost + 6379 + Redis + Doctrine\Common\Cache\XcacheCache + Doctrine\Common\Cache\WinCacheCache + Doctrine\Common\Cache\ZendDataCache + Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain + Doctrine\ORM\Mapping\Driver\AnnotationDriver + Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver + Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver + Doctrine\ORM\Mapping\Driver\PHPDriver + Doctrine\ORM\Mapping\Driver\StaticPHPDriver + Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer + Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser + Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator + Symfony\Bridge\Doctrine\Validator\DoctrineInitializer + Symfony\Bridge\Doctrine\Security\User\EntityUserProvider + Doctrine\ORM\Tools\ResolveTargetEntityListener + Doctrine\ORM\Tools\AttachEntityListenersListener + Doctrine\ORM\Mapping\DefaultNamingStrategy + Doctrine\ORM\Mapping\UnderscoreNamingStrategy + Doctrine\ORM\Mapping\DefaultQuoteStrategy + Doctrine\ORM\Mapping\AnsiQuoteStrategy + Doctrine\ORM\Mapping\DefaultEntityListenerResolver + Doctrine\ORM\Cache\DefaultCacheFactory + Doctrine\ORM\Cache\Region\DefaultRegion + Doctrine\ORM\Cache\Region\FileLockRegion + Doctrine\ORM\Cache\Logging\CacheLoggerChain + Doctrine\ORM\Cache\Logging\StatisticsCacheLogger + Doctrine\ORM\Cache\CacheConfiguration + Doctrine\ORM\Cache\RegionsConfiguration + true + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/doctrine/orm/Proxies + Proxies + Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser + Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener + Symfony\Component\Routing\Loader\AnnotationDirectoryLoader + Symfony\Component\Routing\Loader\AnnotationFileLoader + Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader + Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter + Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener + bottom + false + 2 + + + request + @WebProfiler/Collector/request.html.twig + + + time + @WebProfiler/Collector/time.html.twig + + + memory + @WebProfiler/Collector/memory.html.twig + + + ajax + @WebProfiler/Collector/ajax.html.twig + + + form + @WebProfiler/Collector/form.html.twig + + + exception + @WebProfiler/Collector/exception.html.twig + + + logger + @WebProfiler/Collector/logger.html.twig + + + events + @WebProfiler/Collector/events.html.twig + + + router + @WebProfiler/Collector/router.html.twig + + + security + @Security/Collector/security.html.twig + + + twig + @WebProfiler/Collector/twig.html.twig + + + db + @Doctrine/Collector/db.html.twig + + + swiftmailer + @Swiftmailer/Collector/swiftmailer.html.twig + + + dump + @Debug/Profiler/dump.html.twig + + + config + @WebProfiler/Collector/config.html.twig + + + + sensio_distribution.security_checker.command + + + + + + + + + + UTF-8 + + + + + + + + en + + + + + + + + + + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/app/Resources + + + + + + + + + + + + + + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/app/Resources + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/app + + + + + + + + + + + + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/app/Resources + + + a958d119a837605337f0224ff71592384a432e2f + + + + + + + + + + + + + + true + + inline + fragment.renderer.inline + + + hinclude + fragment.renderer.hinclude + + + hinclude + fragment.renderer.hinclude + + + esi + fragment.renderer.esi + + + ssi + fragment.renderer.ssi + + + + + + + + /_fragment + + + + + + + + null + + /_fragment + + + + + null + + + + /_fragment + + + + + null + + + + /_fragment + + + + + + + + php + + + yml + + + xlf + xliff + + + po + + + mo + + + ts + + + csv + + + res + + + dat + + + ini + + + json + + + + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/translations + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + php + + + + yml + + + + xlf + + + + xliff + + + + po + + + + mo + + + + ts + + + + csv + + + + res + + + + dat + + + + ini + + + + json + + + + + + php + + + + twig + + + + + + php + + + + xlf + + + + po + + + + mo + + + + yml + + + + ts + + + + csv + + + + ini + + + + json + + + + res + + + + + false + false + + + + + + + + + + + + _sf2_meta + 0 + + + + true + 1 + + null + + + + null + + + + + + + + + + + + + + + + + + + + + + form.type.form + form.type.birthday + form.type.checkbox + form.type.choice + form.type.collection + form.type.country + form.type.date + form.type.datetime + form.type.email + form.type.file + form.type.hidden + form.type.integer + form.type.language + form.type.locale + form.type.money + form.type.number + form.type.password + form.type.percent + form.type.radio + form.type.range + form.type.repeated + form.type.search + form.type.textarea + form.type.text + form.type.time + form.type.timezone + form.type.url + form.type.button + form.type.submit + form.type.reset + form.type.currency + form.type.entity + + + + form.type_extension.form.http_foundation + form.type_extension.form.validator + form.type_extension.csrf + form.type_extension.form.data_collector + + + form.type_extension.repeated.validator + + + form.type_extension.submit.validator + + + + form.type_guesser.validator + form.type_guesser.doctrine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + _token + + validators + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev + + + + + + + + + + + validator.expression + validator.email + security.validator.user_password + doctrine.orm.validator.unique + + + + + + + + + validators + + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/config/validation.xml + + + + + + + loadValidatorMetadata + + + + + + + + + + + + + + + false + + + + + /_fragment + + + + + + file:/Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/profiler + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + default + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GET + localhost + http + 80 + 443 + + + + + + + + + + + + + null + + null + null + true + null + + + + + + + + + + + + + kernel.controller + + data_collector.router + onKernelController + + 0 + + + response_listener + Symfony\Component\HttpKernel\EventListener\ResponseListener + + + streamed_response_listener + Symfony\Component\HttpKernel\EventListener\StreamedResponseListener + + + locale_listener + Symfony\Component\HttpKernel\EventListener\LocaleListener + + + translator_listener + Symfony\Component\HttpKernel\EventListener\TranslatorListener + + + session_listener + Symfony\Bundle\FrameworkBundle\EventListener\SessionListener + + + session.save_listener + Symfony\Component\HttpKernel\EventListener\SaveSessionListener + + + fragment.listener + Symfony\Component\HttpKernel\EventListener\FragmentListener + + + profiler_listener + Symfony\Component\HttpKernel\EventListener\ProfilerListener + + + data_collector.request + Symfony\Component\HttpKernel\DataCollector\RequestDataCollector + + + router_listener + Symfony\Component\HttpKernel\EventListener\RouterListener + + + debug.debug_handlers_listener + Symfony\Component\HttpKernel\EventListener\DebugHandlersListener + + + security.firewall + Symfony\Component\Security\Http\Firewall + + + security.rememberme.response_listener + Symfony\Component\Security\Http\RememberMe\ResponseListener + + + twig.exception_listener + Symfony\Component\HttpKernel\EventListener\ExceptionListener + + + monolog.handler.console + Symfony\Bridge\Monolog\Handler\ConsoleHandler + + + swiftmailer.email_sender.listener + Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener + + + sensio_framework_extra.controller.listener + Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener + + + sensio_framework_extra.converter.listener + Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener + + + sensio_framework_extra.view.listener + Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener + + + sensio_framework_extra.cache.listener + Sensio\Bundle\FrameworkExtraBundle\EventListener\HttpCacheListener + + + sensio_framework_extra.security.listener + Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener + + + debug.dump_listener + Symfony\Component\HttpKernel\EventListener\DumpListener + + + web_profiler.debug_toolbar + Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener + + + + + + + + + + + + + + + + + + false + + + + + + + 56a782448eafa8.89273161 + + + + true + + + + + + Symfony\Component\Security\Core\Authentication\Token\AnonymousToken + Symfony\Component\Security\Core\Authentication\Token\RememberMeToken + + + + + + + affirmative + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ^/(_(profiler|wdt)|css|images|js)/ + + + null + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + + + + + + + + + + + + 80 + 443 + + + + + + + + + + + + + + main + + + + + + + + 56a782448eafa8.89273161 + + + + + + + + + + + + + + + + + + + + + + + + + + + main + null + null + null + + false + + + + + + + true + true + twig.controller.exception:showAction + + form_div_layout.html.twig + + filename + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/twig + UTF-8 + + + F j, Y H:i + %d days + null + + + 0 + . + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + /Users/senaria/Projects/test-update-doctrine-command/app + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + form_div_layout.html.twig + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app + + + + dev + + + true + + + + + + + + + + + + + F j, Y H:i + %d days + null + 0 + . + , + + + + + + + + + + + + twig.controller.exception:showAction + + + + + true + + + + twig.controller.exception:showAction + + + /Users/senaria/Projects/test-update-doctrine-command/var/logs/dev.log + 100 + true + null + + + + null + false + + + + + + + + + + + + + + + + + + + + + doctrine.dbal.default_connection + + + doctrine.orm.default_entity_manager + + default + default + + + + + pdo_mysql + 127.0.0.1 + 3306 + symfony + root + yolo + UTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + loadClassMetadata + + + + + + + + + + + + + + + + + + + + + + + + + + + sf2orm_default_15a331a5c940fc1fc66d9a8757c4c571ce907e1d260fc696d5250e7afd012142 + + + + + sf2orm_default_15a331a5c940fc1fc66d9a8757c4c571ce907e1d260fc696d5250e7afd012142 + + + + + sf2orm_default_15a331a5c940fc1fc66d9a8757c4c571ce907e1d260fc696d5250e7afd012142 + + + + + + + + + + + + + + + + + AppBundle\Entity + + + + + + + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/src/AppBundle/Entity + + + + AppBundle\Entity + + + + + + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/doctrine/orm/Proxies + + + Proxies + + + true + + + Doctrine\ORM\Mapping\ClassMetadataFactory + + + Doctrine\ORM\EntityRepository + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + 0 + doctrine.orm + + + + 0 + datetime + + + + + + + + + + + + + + + + + + + null + + + + + + + + + + + + null + UTF-8 + + null + + + + + + + + + 2500 + + + -1 + + + + null + UTF-8 + + + + + + + + request + @WebProfiler/Collector/request.html.twig + + + time + @WebProfiler/Collector/time.html.twig + + + memory + @WebProfiler/Collector/memory.html.twig + + + ajax + @WebProfiler/Collector/ajax.html.twig + + + form + @WebProfiler/Collector/form.html.twig + + + exception + @WebProfiler/Collector/exception.html.twig + + + logger + @WebProfiler/Collector/logger.html.twig + + + events + @WebProfiler/Collector/events.html.twig + + + router + @WebProfiler/Collector/router.html.twig + + + security + @Security/Collector/security.html.twig + + + twig + @WebProfiler/Collector/twig.html.twig + + + db + @Doctrine/Collector/db.html.twig + + + swiftmailer + @Swiftmailer/Collector/swiftmailer.html.twig + + + dump + @Debug/Profiler/dump.html.twig + + + config + @WebProfiler/Collector/config.html.twig + + + bottom + + + + + + + + + + true + + + + + false + 2 + bottom + + ^/(app(_[\w]+)?\.php/)?_wdt + + + + + + + + request + + + + + + + + + + + + translation + + + + + + + + + + + + templating + + + + + + + + + + + + profiler + + + + + + + + + + + + router + + + + + + + + + + + + php + + + + + + + + + + + + event + + + + + + security + + + + + + + + + + + + doctrine + + + + + + + + + 100 + true + + + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/sessions + MOCKSESSID + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/app/config/routing_dev.yml + + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev + true + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper + appDevUrlGenerator + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper + appDevUrlMatcher + true + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/var/cache/dev/annotations + + + true + + + + + + + + + + + + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views + Framework + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views + Security + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views + Twig + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/swiftmailer-bundle/Resources/views + Swiftmailer + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/doctrine/doctrine-bundle/Resources/views + Doctrine + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views + Debug + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views + WebProfiler + + + /Users/senaria/Projects/test-update-doctrine-command/app/Resources/views + + + /Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form + + + + app + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + + + null + + + null + + + + + + + 127.0.0.1 + + + 25 + + + null + + + 30 + + + null + + + + + + + + + + + + + + + + + + diff --git a/var/cache/dev/appDevDebugProjectContainerCompiler.log b/var/cache/dev/appDevDebugProjectContainerCompiler.log new file mode 100644 index 0000000..4bdccfe --- /dev/null +++ b/var/cache/dev/appDevDebugProjectContainerCompiler.log @@ -0,0 +1,379 @@ +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "assets._default_package" (parent: assets.path_package). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.authentication.listener.form" (parent: security.authentication.listener.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.authentication.listener.simple_form" (parent: security.authentication.listener.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.authentication.rememberme.services.persistent" (parent: security.authentication.rememberme.services.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.authentication.rememberme.services.simplehash" (parent: security.authentication.rememberme.services.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.user.provider.concrete.in_memory" (parent: security.user.provider.in_memory). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.firewall.map.context.dev" (parent: security.firewall.context). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.context_listener.0" (parent: security.context_listener). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.authentication.listener.anonymous.main" (parent: security.authentication.listener.anonymous). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.authentication.provider.anonymous.main" (parent: security.authentication.provider.anonymous). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.exception_listener.main" (parent: security.exception_listener). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "security.firewall.map.context.main" (parent: security.firewall.context). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.transport.eventdispatcher" (parent: swiftmailer.transport.eventdispatcher.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.transport.authhandler" (parent: swiftmailer.transport.authhandler.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.transport.buffer" (parent: swiftmailer.transport.buffer.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.transport.smtp" (parent: swiftmailer.transport.smtp.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default" (parent: swiftmailer.mailer.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.spool.memory" (parent: swiftmailer.spool.memory.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.transport.spool" (parent: swiftmailer.transport.spool.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "swiftmailer.mailer.default.plugin.messagelogger" (parent: swiftmailer.plugin.messagelogger.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.dbal.default_connection.configuration" (parent: doctrine.dbal.connection.configuration). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.dbal.logger.profiling.default" (parent: doctrine.dbal.logger.profiling). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.dbal.logger.chain.default" (parent: doctrine.dbal.logger.chain). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.dbal.default_connection.event_manager" (parent: doctrine.dbal.connection.event_manager). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.dbal.default_connection" (parent: doctrine.dbal.connection). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.orm.default_configuration" (parent: doctrine.orm.configuration). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine_cache.providers.doctrine.orm.default_metadata_cache" (parent: doctrine_cache.abstract.array). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine_cache.providers.doctrine.orm.default_result_cache" (parent: doctrine_cache.abstract.array). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine_cache.providers.doctrine.orm.default_query_cache" (parent: doctrine_cache.abstract.array). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.orm.default_manager_configurator" (parent: doctrine.orm.manager_configurator.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "doctrine.orm.default_entity_manager" (parent: doctrine.orm.entity_manager.abstract). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.request" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.translation" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.templating" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.profiler" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.router" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.php" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.event" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.security" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass: Resolving inheritance for "monolog.logger.doctrine" (parent: monolog.logger_prototype). +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "form.property_accessor"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "validator.mapping.class_metadata_factory"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "doctrine.dbal.event_manager"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "doctrine.orm.metadata.annotation_reader"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "doctrine.orm.default_entity_manager.event_manager"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "sensio_framework_extra.security.expression_language"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "assets.path_package"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "assets.url_package"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "assets.static_version_strategy"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.firewall.context"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.user.provider.in_memory"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.user.provider.in_memory.user"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.user.provider.ldap"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.user.provider.chain"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.logout_listener"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.logout.handler.cookie_clearing"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.logout.success_handler"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.form_entry_point"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.custom_success_handler"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.success_handler"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.custom_failure_handler"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.failure_handler"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.form"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.simple_form"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.simple_success_failure_handler"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.simple_preauth"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.x509"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.remote_user"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.basic"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.digest"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.provider.dao"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.provider.ldap_bind"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.provider.simple"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.provider.pre_authenticated"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.exception_listener"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.switchuser_listener"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.rememberme"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.provider.rememberme"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.rememberme.services.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.rememberme.services.persistent"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.rememberme.services.simplehash"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.provider.guard"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "security.authentication.listener.guard"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "monolog.logger_prototype"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.mailer.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.sendmail.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.mail.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.null.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.buffer.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.authhandler.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.eventdispatcher.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.plugin.redirecting.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.plugin.antiflood.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.plugin.impersonate.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.plugin.messagelogger.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.smtp.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.transport.spool.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.spool.file.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "swiftmailer.spool.memory.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.apc"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.array"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.chain"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.couchbase"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.file_system"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.php_file"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.memcache"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.memcached"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.mongodb"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.redis"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.riak"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.sqlite3"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.void"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.wincache"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.xcache"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine_cache.abstract.zenddata"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.dbal.logger.chain"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.dbal.logger.profiling"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.dbal.connection"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.dbal.connection.event_manager"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.dbal.connection.configuration"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.orm.configuration"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.orm.entity_manager.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.orm.manager_configurator.abstract"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\RemoveAbstractDefinitionsPass: Removed service "doctrine.orm.security.user.provider"; reason: abstract. +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "locale_listener" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "router.cache_warmer" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "router_listener" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "security.logout_url_generator" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "security.http_utils" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "security.http_utils" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "twig.extension.routing" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "web_profiler.controller.profiler" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "web_profiler.controller.router" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "web_profiler.debug_toolbar" previously pointing to "router.default" to "router". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "validator.builder" previously pointing to "annotations.cached_reader" to "annotation_reader". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "doctrine.orm.default_annotation_metadata_driver" previously pointing to "annotations.cached_reader" to "annotation_reader". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "sensio_framework_extra.controller.listener" previously pointing to "annotations.cached_reader" to "annotation_reader". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "sensio_framework_extra.routing.loader.annot_class" previously pointing to "annotations.cached_reader" to "annotation_reader". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "security.user_password_encoder.generic" previously pointing to "security.encoder_factory.generic" to "security.encoder_factory". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "security.validator.user_password" previously pointing to "security.encoder_factory.generic" to "security.encoder_factory". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "twig" previously pointing to "twig.loader.filesystem" to "twig.loader". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "swiftmailer.email_sender.listener" previously pointing to "monolog.logger" to "logger". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "swiftmailer.mailer.default" previously pointing to "swiftmailer.mailer.default.transport.spool" to "swiftmailer.mailer.default.transport". +Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass: Changed reference of service "swiftmailer.mailer.default.transport" previously pointing to "swiftmailer.mailer.default.spool.memory" to "swiftmailer.mailer.default.spool". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "templating.cache_warmer.template_paths" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "translation.warmer" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "router.cache_warmer" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.cache_warmer" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.template_cache_warmer" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.orm.proxy_cache_warmer" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "551040e836ac8aab5035db31d3e5f1b399565fb00ccee2fdb1a6889434d7d5c7_1" to "config_cache_factory". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "session.attribute_bag" to "session". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "session.flash_bag" to "session". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "1c00b65be2826f0b297ae9b62e2db33ab0ffbc4b3a6cf4673e688e199ce115ab_1" to "form.resolved_type_factory". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "form.extension" to "form.registry". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "form.type_extension.form.request_handler" to "form.type_extension.form.http_foundation". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "form.server_params" to "form.type_extension.form.request_handler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.csrf.token_generator" to "security.csrf.token_manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.csrf.token_storage" to "security.csrf.token_manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "assets._default_package" to "assets.packages". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "validator.validator_factory" to "validator.builder". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "profiler.storage" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.time" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.memory" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.ajax" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.exception" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.logger" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.events" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.security" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.twig" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.doctrine" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "swiftmailer.data_collector" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "data_collector.config" to "profiler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "routing.loader.xml" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "routing.loader.yml" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "routing.loader.php" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "routing.loader.directory" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "routing.loader.service" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "sensio_framework_extra.routing.loader.annot_dir" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "sensio_framework_extra.routing.loader.annot_file" to "routing.resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "routing.resolver" to "routing.loader". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "debug.event_dispatcher.parent" to "debug.event_dispatcher". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "controller_resolver" to "debug.controller_resolver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.authentication.provider.anonymous.main" to "security.authentication.manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.access.simple_role_voter" to "security.access.decision_manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.access.expression_voter" to "security.access.decision_manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.access.authenticated_voter" to "security.access.decision_manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.expression_language" to "security.access.expression_voter". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.firewall.map" to "security.firewall". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.request_matcher.5314eeb91110adf24b9b678372bb11bbe00e8858c519c088bfb65f525181ad3bf573fd1d" to "security.firewall.map". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.authentication.retry_entry_point" to "security.channel_listener". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.http_utils" to "security.exception_listener.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.channel_listener" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.context_listener.0" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.authentication.listener.anonymous.main" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.access_listener" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.exception_listener.main" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.logout_url" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.security" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.profiler" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.trans" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.assets" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.code" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.routing" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.yaml" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.debug.stopwatch" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.expression" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.httpkernel" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.httpfoundation" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.form" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.debug" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.twig.doctrine_extension" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.dump" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.extension.webprofiler" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.app_variable" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.configurator.environment" to "twig". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.template_iterator" to "twig.template_cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.form.renderer" to "twig.extension.form". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "twig.form.engine" to "twig.form.renderer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "04c0e291c8f52b7828f2dbfa1d961d160ef6e559cb1a9c45d42f6770da860910_1" to "swiftmailer.mailer.default.transport.authhandler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "04c0e291c8f52b7828f2dbfa1d961d160ef6e559cb1a9c45d42f6770da860910_2" to "swiftmailer.mailer.default.transport.authhandler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "04c0e291c8f52b7828f2dbfa1d961d160ef6e559cb1a9c45d42f6770da860910_3" to "swiftmailer.mailer.default.transport.authhandler". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "swiftmailer.transport.replacementfactory" to "swiftmailer.mailer.default.transport.buffer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.dbal.logger.chain.default" to "doctrine.dbal.default_connection.configuration". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.dbal.logger" to "doctrine.dbal.logger.chain.default". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.dbal.default_connection.configuration" to "doctrine.dbal.default_connection". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.dbal.default_connection.event_manager" to "doctrine.dbal.default_connection". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.orm.default_metadata_driver" to "doctrine.orm.default_configuration". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.orm.naming_strategy.underscore" to "doctrine.orm.default_configuration". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.orm.quote_strategy.default" to "doctrine.orm.default_configuration". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.orm.default_annotation_metadata_driver" to "doctrine.orm.default_metadata_driver". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "doctrine.orm.default_configuration" to "doctrine.orm.default_entity_manager". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "sensio_framework_extra.security.expression_language.default" to "sensio_framework_extra.security.listener". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "annotations.reader" to "annotation_reader". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "annotations.filesystem_cache" to "annotation_reader". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "swiftmailer.mailer.default.transport.buffer" to "swiftmailer.mailer.default.transport.real". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "swiftmailer.mailer.default.transport.authhandler" to "swiftmailer.mailer.default.transport.real". +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "controller_resolver"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "551040e836ac8aab5035db31d3e5f1b399565fb00ccee2fdb1a6889434d7d5c7_1"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "translator.logging"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "translation.warmer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "session.flash_bag"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "session.attribute_bag"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "session.handler.native_file"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "session.handler.write_check"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "form.extension"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "form.type_extension.form.request_handler"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "form.server_params"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.csrf.token_generator"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.csrf.token_storage"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "assets.empty_package"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "assets._default_package"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "templating.engine.delegating"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "templating.cache_warmer.template_paths"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "templating.loader.cache"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "templating.loader.chain"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "validator.mapping.cache.apc"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "validator.validator_factory"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "profiler.storage"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.config"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.ajax"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.exception"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.events"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.logger"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.time"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.memory"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "1c00b65be2826f0b297ae9b62e2db33ab0ffbc4b3a6cf4673e688e199ce115ab_1"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "routing.resolver"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "routing.loader.xml"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "routing.loader.yml"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "routing.loader.php"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "routing.loader.directory"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "routing.loader.service"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "router.cache_warmer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "annotations.reader"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "annotations.filesystem_cache"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "debug.event_dispatcher.parent"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.session_strategy"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.expression_language"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.access.simple_role_voter"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.access.authenticated_voter"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.access.expression_voter"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.firewall.map"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.http_utils"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.listener.anonymous"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.provider.anonymous"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.retry_entry_point"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.basic_entry_point"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.digest_entry_point"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.channel_listener"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.context_listener"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.logout.handler.session"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.access_listener"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.rememberme.token.provider.in_memory"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.logout_url"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.security"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.security"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.request_matcher.5314eeb91110adf24b9b678372bb11bbe00e8858c519c088bfb65f525181ad3bf573fd1d"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.context_listener.0"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.listener.anonymous.main"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.authentication.provider.anonymous.main"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.exception_listener.main"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.app_variable"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.cache_warmer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.template_iterator"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.template_cache_warmer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.loader.native_filesystem"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.loader.chain"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.profiler"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.twig"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.trans"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.assets"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.code"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.routing"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.yaml"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.debug.stopwatch"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.expression"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.httpkernel"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.httpfoundation"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.form"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.debug"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.form.engine"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.form.renderer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.configurator.environment"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.chrome_php"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.gelf_message"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.html"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.json"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.line"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.loggly"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.normalizer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.scalar"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.wildfire"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "monolog.formatter.logstash"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "04c0e291c8f52b7828f2dbfa1d961d160ef6e559cb1a9c45d42f6770da860910_3"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "04c0e291c8f52b7828f2dbfa1d961d160ef6e559cb1a9c45d42f6770da860910_2"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "04c0e291c8f52b7828f2dbfa1d961d160ef6e559cb1a9c45d42f6770da860910_1"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "swiftmailer.transport.failover"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "swiftmailer.transport.mailinvoker"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "swiftmailer.transport.replacementfactory"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "swiftmailer.data_collector"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "swiftmailer.mailer.default.transport.authhandler"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "swiftmailer.mailer.default.transport.buffer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.dbal.logger"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "data_collector.doctrine"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.twig.doctrine_extension"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.dbal.default_connection.configuration"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.dbal.logger.chain.default"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.dbal.default_connection.event_manager"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.proxy_cache_warmer"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.listeners.resolve_target_entity"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.naming_strategy.default"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.naming_strategy.underscore"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.quote_strategy.default"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.quote_strategy.ansi"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.default_configuration"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.default_annotation_metadata_driver"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "doctrine.orm.default_metadata_driver"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "sensio_framework_extra.routing.loader.annot_dir"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "sensio_framework_extra.routing.loader.annot_file"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "sensio_framework_extra.security.expression_language.default"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.dump"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "twig.extension.webprofiler"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "templating.finder" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "templating.finder" to "cache_warmer". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "assets.empty_version_strategy" to "assets.packages". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "sensio_framework_extra.routing.loader.annot_class" to "routing.loader". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "sensio_framework_extra.routing.loader.annot_class" to "routing.loader". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "sensio_framework_extra.routing.loader.annot_class" to "routing.loader". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.access_map" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.user.provider.concrete.in_memory" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "security.access_map" to "security.firewall.map.context.main". +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "assets.empty_version_strategy"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "templating.finder"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.access_map"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "security.user.provider.concrete.in_memory"; reason: unused. +Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "sensio_framework_extra.routing.loader.annot_class"; reason: unused. \ No newline at end of file diff --git a/var/cache/dev/classes.map b/var/cache/dev/classes.map new file mode 100644 index 0000000..f3ee75b --- /dev/null +++ b/var/cache/dev/classes.map @@ -0,0 +1,86 @@ + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener', + 1 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', + 2 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage', + 3 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler', + 4 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', + 5 => 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy', + 6 => 'Symfony\\Component\\HttpFoundation\\Session\\Session', + 8 => 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', + 9 => 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', + 10 => 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', + 11 => 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocator', + 12 => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 13 => 'Symfony\\Component\\Routing\\RequestContext', + 14 => 'Symfony\\Component\\Routing\\Router', + 15 => 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', + 16 => 'Symfony\\Bundle\\FrameworkBundle\\Routing\\Router', + 17 => 'Symfony\\Component\\Config\\FileLocator', + 18 => 'Symfony\\Component\\Debug\\ErrorHandler', + 19 => 'Symfony\\Component\\EventDispatcher\\Event', + 20 => 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', + 21 => 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', + 22 => 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', + 23 => 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', + 24 => 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', + 25 => 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', + 26 => 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', + 27 => 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', + 28 => 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', + 29 => 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', + 30 => 'Symfony\\Component\\HttpKernel\\KernelEvents', + 31 => 'Symfony\\Component\\HttpKernel\\Config\\FileLocator', + 32 => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', + 33 => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', + 34 => 'Symfony\\Component\\Security\\Http\\Firewall', + 35 => 'Symfony\\Component\\Security\\Core\\User\\UserProviderInterface', + 36 => 'Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager', + 37 => 'Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorage', + 38 => 'Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager', + 39 => 'Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationChecker', + 40 => 'Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface', + 41 => 'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallMap', + 42 => 'Symfony\\Bundle\\SecurityBundle\\Security\\FirewallContext', + 43 => 'Symfony\\Component\\HttpFoundation\\RequestMatcher', + 44 => 'Twig_Environment', + 45 => 'Twig_Extension', + 46 => 'Twig_Extension_Core', + 47 => 'Twig_Extension_Escaper', + 48 => 'Twig_Extension_Optimizer', + 49 => 'Twig_LoaderInterface', + 50 => 'Twig_Markup', + 51 => 'Twig_Template', + 52 => 'Monolog\\Formatter\\FormatterInterface', + 53 => 'Monolog\\Formatter\\LineFormatter', + 54 => 'Monolog\\Handler\\HandlerInterface', + 55 => 'Monolog\\Handler\\AbstractHandler', + 56 => 'Monolog\\Handler\\AbstractProcessingHandler', + 57 => 'Monolog\\Handler\\StreamHandler', + 58 => 'Monolog\\Handler\\FingersCrossedHandler', + 59 => 'Monolog\\Handler\\FilterHandler', + 60 => 'Monolog\\Handler\\TestHandler', + 61 => 'Monolog\\Logger', + 62 => 'Symfony\\Bridge\\Monolog\\Logger', + 63 => 'Symfony\\Bridge\\Monolog\\Handler\\DebugHandler', + 64 => 'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface', + 65 => 'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy', + 66 => 'Doctrine\\Common\\Annotations\\DocLexer', + 67 => 'Doctrine\\Common\\Annotations\\FileCacheReader', + 68 => 'Doctrine\\Common\\Annotations\\PhpParser', + 69 => 'Doctrine\\Common\\Annotations\\Reader', + 70 => 'Doctrine\\Common\\Lexer', + 71 => 'Doctrine\\Common\\Persistence\\ConnectionRegistry', + 72 => 'Doctrine\\Common\\Persistence\\Proxy', + 73 => 'Doctrine\\Common\\Util\\ClassUtils', + 74 => 'Doctrine\\Bundle\\DoctrineBundle\\Registry', + 75 => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ControllerListener', + 76 => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ParamConverterListener', + 77 => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DateTimeParamConverter', + 78 => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DoctrineParamConverter', + 79 => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterInterface', + 80 => 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterManager', + 81 => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\TemplateListener', + 82 => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\HttpCacheListener', + 83 => 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\SecurityListener', + 84 => 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\ConfigurationAnnotation', +); \ No newline at end of file diff --git a/var/logs/.gitkeep b/var/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/var/logs/dev.log b/var/logs/dev.log new file mode 100644 index 0000000..660c44b --- /dev/null +++ b/var/logs/dev.log @@ -0,0 +1,212 @@ +[2016-01-26 15:30:40] doctrine.DEBUG: CREATE TABLE test_one (id INT AUTO_INCREMENT NOT NULL, wtf INT NOT NULL, INDEX IDX_3115A36417DDF58 (wtf), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB [] [] +[2016-01-26 15:30:40] doctrine.DEBUG: CREATE TABLE test_two (id INT AUTO_INCREMENT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB [] [] +[2016-01-26 15:30:40] doctrine.DEBUG: ALTER TABLE test_one ADD CONSTRAINT FK_3115A36417DDF58 FOREIGN KEY (wtf) REFERENCES test_two (id) [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:39:07] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:40:40] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:43:28] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:46:36] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:46:56] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:48:28] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:49:10] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:50:10] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:50:49] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:50:49] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:50:49] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:50:49] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:50:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:50:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:50:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:50:53] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:51:13] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:51:13] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:51:13] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:51:13] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:51:14] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:51:14] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:51:14] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:51:41] php.DEBUG: fsockopen(): unable to connect to 127.0.0.1:8000 (Connection refused) {"type":2,"file":"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php","line":59,"level":28928} [] +[2016-01-26 15:54:40] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 15:54:40] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 15:54:40] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:54:40] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 15:54:40] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 15:54:40] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 15:54:40] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:00:00] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:00:00] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:00:01] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:00:01] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:00:01] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:00:01] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:00:01] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:01:26] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:01:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:02:42] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:03:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:34:45] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:34:45] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:34:46] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:34:46] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:34:46] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:34:46] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:34:46] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:35:06] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:35:38] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:35:58] php.DEBUG: fsockopen(): unable to connect to 127.0.0.1:8000 (Connection refused) {"type":2,"file":"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php","line":59,"level":28928} [] +[2016-01-26 16:36:18] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:36:18] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:36:18] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:36:18] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:36:19] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:36:19] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:36:19] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:36:30] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:36:49] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:40:36] php.DEBUG: fsockopen(): unable to connect to 127.0.0.1:8000 (Connection refused) {"type":2,"file":"/Users/senaria/Projects/test-update-doctrine-command/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php","line":59,"level":28928} [] +[2016-01-26 16:50:15] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:50:15] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:50:15] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:50:15] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:50:15] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:50:15] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:50:15] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:52:36] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:53:49] doctrine.DEBUG: CREATE TABLE test_one (id INT AUTO_INCREMENT NOT NULL, test_id INT DEFAULT NULL, INDEX IDX_3115A361E5D0459 (test_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB [] [] +[2016-01-26 16:53:49] doctrine.DEBUG: CREATE TABLE test_two (id INT AUTO_INCREMENT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB [] [] +[2016-01-26 16:53:49] doctrine.DEBUG: ALTER TABLE test_one ADD CONSTRAINT FK_3115A361E5D0459 FOREIGN KEY (test_id) REFERENCES test_two (id) [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:54:30] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:56:20] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:58:41] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SHOW FULL TABLES WHERE Table_type = 'BASE TABLE' [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_one' [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_one' */ WHERE k.table_name = 'test_one' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_one' AND TABLE_SCHEMA = 'symfony' [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'symfony' AND TABLE_NAME = 'test_two' [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.constraint_name = k.constraint_name AND c.table_name = 'test_two' */ WHERE k.table_name = 'test_two' AND k.table_schema = 'symfony' /*!50116 AND c.constraint_schema = 'symfony' */ AND k.`REFERENCED_COLUMN_NAME` is not NULL [] [] +[2016-01-26 16:58:50] doctrine.DEBUG: SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment FROM information_schema.STATISTICS WHERE TABLE_NAME = 'test_two' AND TABLE_SCHEMA = 'symfony' [] [] diff --git a/var/sessions/.gitkeep b/var/sessions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..8fe2e65 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-0 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000..c8d57af --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) 2015 Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..2414d2c --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,22 @@ + $baseDir . '/app/AppCache.php', + 'AppKernel' => $baseDir . '/app/AppKernel.php', + 'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', + 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', + 'Collator' => $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/Collator.php', + 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', + 'IntlDateFormatter' => $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/IntlDateFormatter.php', + 'Locale' => $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/Locale.php', + 'NumberFormatter' => $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php', + 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', + 'SqlFormatter' => $vendorDir . '/jdorn/sql-formatter/lib/SqlFormatter.php', + 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 0000000..d6d06aa --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,16 @@ + array($vendorDir . '/twig/twig/lib'), + 'SensioLabs\\Security' => array($vendorDir . '/sensiolabs/security-checker'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log'), + 'Doctrine\\ORM\\' => array($vendorDir . '/doctrine/orm/lib'), + 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/lib'), + 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'), + 'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib'), + 'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'), + 'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib'), +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 0000000..9528296 --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,35 @@ + array($baseDir . '/tests'), + 'Symfony\\Polyfill\\Util\\' => array($vendorDir . '/symfony/polyfill-util'), + 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), + 'Symfony\\Polyfill\\Php56\\' => array($vendorDir . '/symfony/polyfill-php56'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Component'), + 'Symfony\\Bundle\\SwiftmailerBundle\\' => array($vendorDir . '/symfony/swiftmailer-bundle'), + 'Symfony\\Bundle\\MonologBundle\\' => array($vendorDir . '/symfony/monolog-bundle'), + 'Symfony\\Bundle\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bundle'), + 'Symfony\\Bridge\\Twig\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Twig'), + 'Symfony\\Bridge\\Swiftmailer\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Swiftmailer'), + 'Symfony\\Bridge\\ProxyManager\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/ProxyManager'), + 'Symfony\\Bridge\\PhpUnit\\' => array($vendorDir . '/symfony/phpunit-bridge'), + 'Symfony\\Bridge\\Monolog\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Monolog'), + 'Symfony\\Bridge\\Doctrine\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Doctrine'), + 'Sensio\\Bundle\\GeneratorBundle\\' => array($vendorDir . '/sensio/generator-bundle'), + 'Sensio\\Bundle\\FrameworkExtraBundle\\' => array($vendorDir . '/sensio/framework-extra-bundle'), + 'Sensio\\Bundle\\DistributionBundle\\' => array($vendorDir . '/sensio/distribution-bundle'), + 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), + 'Incenteev\\ParameterHandler\\' => array($vendorDir . '/incenteev/composer-parameter-handler'), + 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), + 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), + 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'), + 'Doctrine\\Bundle\\DoctrineCacheBundle\\' => array($vendorDir . '/doctrine/doctrine-cache-bundle'), + 'Doctrine\\Bundle\\DoctrineBundle\\' => array($vendorDir . '/doctrine/doctrine-bundle'), + '' => array($baseDir . '/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..12bc1d4 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,55 @@ + $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + $includeFiles = require __DIR__ . '/autoload_files.php'; + foreach ($includeFiles as $file) { + composerRequiredea5e600c850af581d53037a1c45eafa($file); + } + + return $loader; + } +} + +function composerRequiredea5e600c850af581d53037a1c45eafa($file) +{ + require $file; +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..f361ddc --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,1991 @@ +[ + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "time": "2014-09-09 13:34:57", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ] + }, + { + "name": "doctrine/annotations", + "version": "v1.2.7", + "version_normalized": "1.2.7.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "time": "2015-08-31 12:32:49", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ] + }, + { + "name": "twig/twig", + "version": "v1.23.1", + "version_normalized": "1.23.1.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "shasum": "" + }, + "require": { + "php": ">=5.2.7" + }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, + "time": "2015-11-05 12:49:06", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.23-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "http://twig.sensiolabs.org/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ] + }, + { + "name": "symfony/polyfill-util", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "4271c55cbc0a77b2641f861b978123e46b3da969" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4271c55cbc0a77b2641f861b978123e46b3da969", + "reference": "4271c55cbc0a77b2641f861b978123e46b3da969", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2015-11-04 20:28:58", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ] + }, + { + "name": "paragonie/random_compat", + "version": "1.1.4", + "version_normalized": "1.1.4.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "d762ee5b099a29044603cd4649851e81aa66cb47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/d762ee5b099a29044603cd4649851e81aa66cb47", + "reference": "d762ee5b099a29044603cd4649851e81aa66cb47", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2015-12-10 14:48:13", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ] + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "7f7f3c9c2b9f17722e0cd64fdb4f957330c53146" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/7f7f3c9c2b9f17722e0cd64fdb4f957330c53146", + "reference": "7f7f3c9c2b9f17722e0cd64fdb4f957330c53146", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0", + "php": ">=5.3.3" + }, + "time": "2015-11-04 20:28:58", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "e2e77609a9e2328eb370fbb0e0d8b2000ebb488f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/e2e77609a9e2328eb370fbb0e0d8b2000ebb488f", + "reference": "e2e77609a9e2328eb370fbb0e0d8b2000ebb488f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "time": "2015-12-18 15:10:25", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/49ff736bd5d41f45240cec77b44967d76e0c3d25", + "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2015-11-20 09:19:13", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/symfony", + "version": "v3.0.1", + "version_normalized": "3.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/symfony.git", + "reference": "979d7323716fec847508eac3e62d59b117612a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/symfony/zipball/979d7323716fec847508eac3e62d59b117612a6e", + "reference": "979d7323716fec847508eac3e62d59b117612a6e", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.4", + "php": ">=5.5.9", + "psr/log": "~1.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0", + "twig/twig": "~1.23|~2.0" + }, + "conflict": { + "phpdocumentor/reflection": "<1.0.7" + }, + "replace": { + "symfony/asset": "self.version", + "symfony/browser-kit": "self.version", + "symfony/class-loader": "self.version", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/css-selector": "self.version", + "symfony/debug": "self.version", + "symfony/debug-bundle": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/expression-language": "self.version", + "symfony/filesystem": "self.version", + "symfony/finder": "self.version", + "symfony/form": "self.version", + "symfony/framework-bundle": "self.version", + "symfony/http-foundation": "self.version", + "symfony/http-kernel": "self.version", + "symfony/intl": "self.version", + "symfony/ldap": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/property-access": "self.version", + "symfony/property-info": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/security-core": "self.version", + "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", + "symfony/security-http": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/templating": "self.version", + "symfony/translation": "self.version", + "symfony/twig-bridge": "self.version", + "symfony/twig-bundle": "self.version", + "symfony/validator": "self.version", + "symfony/var-dumper": "self.version", + "symfony/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.4", + "doctrine/doctrine-bundle": "~1.4", + "doctrine/orm": "~2.4,>=2.4.5", + "egulias/email-validator": "~1.2", + "monolog/monolog": "~1.11", + "ocramius/proxy-manager": "~0.4|~1.0", + "phpdocumentor/reflection": "^1.0.7", + "symfony/security-acl": "~2.8|~3.0" + }, + "time": "2015-12-26 16:49:48", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", + "Symfony\\Bundle\\": "src/Symfony/Bundle/", + "Symfony\\Component\\": "src/Symfony/Component/" + }, + "classmap": [ + "src/Symfony/Component/Intl/Resources/stubs" + ], + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "The Symfony PHP framework", + "homepage": "https://symfony.com", + "keywords": [ + "framework" + ] + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "2deb44160e1c886241c06602b12b98779f728177" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/2deb44160e1c886241c06602b12b98779f728177", + "reference": "2deb44160e1c886241c06602b12b98779f728177", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0" + }, + "time": "2015-11-04 20:28:58", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "psr/log", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "time": "2012-12-21 11:40:51", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ] + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "time": "2015-11-06 14:35:42", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ] + }, + { + "name": "doctrine/collections", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "time": "2015-04-14 22:21:58", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ] + }, + { + "name": "doctrine/cache", + "version": "v1.5.4", + "version_normalized": "1.5.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/47cdc76ceb95cc591d9c79a36dc3794975b5d136", + "reference": "47cdc76ceb95cc591d9c79a36dc3794975b5d136", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": ">=3.7", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "time": "2015-12-19 05:03:47", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ] + }, + { + "name": "doctrine/common", + "version": "v2.6.1", + "version_normalized": "2.6.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "time": "2015-12-25 13:18:31", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ] + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "version_normalized": "1.2.17.0", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "time": "2014-01-12 16:20:24", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ] + }, + { + "name": "doctrine/doctrine-cache-bundle", + "version": "1.2.2", + "version_normalized": "1.2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineCacheBundle.git", + "reference": "030ff41ef1db66370b36467086bfb817a661fe6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/030ff41ef1db66370b36467086bfb817a661fe6a", + "reference": "030ff41ef1db66370b36467086bfb817a661fe6a", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.4.2", + "doctrine/inflector": "~1.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": "~2.2|~3.0" + }, + "require-dev": { + "instaclick/coding-standard": "~1.1", + "instaclick/object-calisthenics-sniffs": "dev-master", + "instaclick/symfony2-coding-standard": "dev-remaster", + "phpunit/phpunit": "~4", + "satooshi/php-coveralls": "~0.6.1", + "squizlabs/php_codesniffer": "~1.5", + "symfony/console": "~2.2|~3.0", + "symfony/finder": "~2.2|~3.0", + "symfony/framework-bundle": "~2.2|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/security-acl": "~2.3|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0" + }, + "suggest": { + "symfony/security-acl": "For using this bundle to cache ACLs" + }, + "time": "2015-11-27 04:59:07", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineCacheBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Fabio B. Silva", + "email": "fabio.bat.silva@gmail.com" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Bundle for Doctrine Cache", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ] + }, + { + "name": "doctrine/dbal", + "version": "v2.5.3", + "version_normalized": "2.5.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/2fbcea96eae34a53183377cdbb0b9bec33974648", + "reference": "2fbcea96eae34a53183377cdbb0b9bec33974648", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.7-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "time": "2015-12-25 16:28:24", + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ] + }, + { + "name": "doctrine/doctrine-bundle", + "version": "1.6.1", + "version_normalized": "1.6.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "c4ffef2b2296e9d0179eb0b5248e5ae25c9bba3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/c4ffef2b2296e9d0179eb0b5248e5ae25c9bba3b", + "reference": "c4ffef2b2296e9d0179eb0b5248e5ae25c9bba3b", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.3", + "doctrine/doctrine-cache-bundle": "~1.0", + "jdorn/sql-formatter": "~1.1", + "php": ">=5.3.2", + "symfony/console": "~2.3|~3.0", + "symfony/doctrine-bridge": "~2.2|~3.0", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "phpunit/phpunit": "~4", + "satooshi/php-coveralls": "~0.6.1", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0", + "twig/twig": "~1.10" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "symfony/web-profiler-bundle": "to use the data collector" + }, + "time": "2015-11-16 17:11:46", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ] + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "version_normalized": "1.0.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "time": "2015-06-14 21:17:01", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ] + }, + { + "name": "doctrine/orm", + "version": "v2.5.3", + "version_normalized": "2.5.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "d9fc5388f1aa1751a0e148e76b4569bd207338e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/d9fc5388f1aa1751a0e148e76b4569bd207338e9", + "reference": "d9fc5388f1aa1751a0e148e76b4569bd207338e9", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.4", + "doctrine/collections": "~1.2", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "ext-pdo": "*", + "php": ">=5.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "symfony/yaml": "~2.3|~3.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "time": "2015-12-25 15:50:05", + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\ORM\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ] + }, + { + "name": "incenteev/composer-parameter-handler", + "version": "v2.1.2", + "version_normalized": "2.1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Incenteev/ParameterHandler.git", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpspec/prophecy-phpunit": "~1.0", + "symfony/filesystem": "~2.2" + }, + "time": "2015-11-10 17:04:01", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Incenteev\\ParameterHandler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Composer script handling your ignored parameter file", + "homepage": "https://github.com/Incenteev/ParameterHandler", + "keywords": [ + "parameters management" + ] + }, + { + "name": "sensiolabs/security-checker", + "version": "v3.0.2", + "version_normalized": "3.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/security-checker.git", + "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/21696b0daa731064c23cfb694c60a2584a7b6e93", + "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93", + "shasum": "" + }, + "require": { + "symfony/console": "~2.0|~3.0" + }, + "time": "2015-11-07 08:07:40", + "bin": [ + "security-checker" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "SensioLabs\\Security": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "A security checker for your composer.lock" + }, + { + "name": "sensio/distribution-bundle", + "version": "v5.0.3", + "version_normalized": "5.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", + "reference": "419c1824af940e2be0f833aca2327e1181a6b503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/419c1824af940e2be0f833aca2327e1181a6b503", + "reference": "419c1824af940e2be0f833aca2327e1181a6b503", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "sensiolabs/security-checker": "~3.0", + "symfony/class-loader": "~2.3|~3.0", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/filesystem": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/process": "~2.3|~3.0" + }, + "time": "2015-12-18 17:44:11", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sensio\\Bundle\\DistributionBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Base bundle for Symfony Distributions", + "keywords": [ + "configuration", + "distribution" + ] + }, + { + "name": "sensio/framework-extra-bundle", + "version": "v3.0.12", + "version_normalized": "3.0.12.0", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "3e8936fe13aa4086644977d334d8fcd275f50357" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/3e8936fe13aa4086644977d334d8fcd275f50357", + "reference": "3e8936fe13aa4086644977d334d8fcd275f50357", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.2", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "symfony/expression-language": "~2.4|~3.0", + "symfony/security-bundle": "~2.4|~3.0" + }, + "suggest": { + "symfony/expression-language": "", + "symfony/psr-http-message-bridge": "To use the PSR-7 converters", + "symfony/security-bundle": "" + }, + "time": "2015-12-18 17:39:27", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sensio\\Bundle\\FrameworkExtraBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": [ + "annotations", + "controllers" + ] + }, + { + "name": "monolog/monolog", + "version": "1.17.2", + "version_normalized": "1.17.2.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "raven/raven": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "swiftmailer/swiftmailer": "~5.3", + "videlalvaro/php-amqplib": "~2.4" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "raven/raven": "Allow sending log messages to a Sentry server", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" + }, + "time": "2015-10-14 12:51:02", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.16.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ] + }, + { + "name": "symfony/monolog-bundle", + "version": "v2.8.2", + "version_normalized": "2.8.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "84785c4d44801c4dd82829fa2e1820cacfe2c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/84785c4d44801c4dd82829fa2e1820cacfe2c46f", + "reference": "84785c4d44801c4dd82829fa2e1820cacfe2c46f", + "shasum": "" + }, + "require": { + "monolog/monolog": "~1.8", + "php": ">=5.3.2", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/monolog-bridge": "~2.3|~3.0" + }, + "require-dev": { + "symfony/console": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "time": "2015-11-17 10:02:29", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.8.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony MonologBundle", + "homepage": "http://symfony.com", + "keywords": [ + "log", + "logging" + ] + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.1", + "version_normalized": "5.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1,<0.9.4" + }, + "time": "2015-06-06 14:19:39", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "email", + "mail", + "mailer" + ] + }, + { + "name": "symfony/swiftmailer-bundle", + "version": "v2.3.9", + "version_normalized": "2.3.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/swiftmailer-bundle.git", + "reference": "3d21ada19f23631f558ad6df653b168e35362e78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/3d21ada19f23631f558ad6df653b168e35362e78", + "reference": "3d21ada19f23631f558ad6df653b168e35362e78", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "swiftmailer/swiftmailer": ">=4.2.0,~5.0", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7|~3.0" + }, + "suggest": { + "psr/log": "Allows logging" + }, + "time": "2015-11-28 10:59:29", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SwiftmailerBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony SwiftmailerBundle", + "homepage": "http://symfony.com" + }, + { + "name": "sensio/generator-bundle", + "version": "v3.0.3", + "version_normalized": "3.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git", + "reference": "525e078ff7d5e9f19b0ef912bb6d6753673b3c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/525e078ff7d5e9f19b0ef912bb6d6753673b3c66", + "reference": "525e078ff7d5e9f19b0ef912bb6d6753673b3c66", + "shasum": "" + }, + "require": { + "symfony/console": "~2.7|~3.0", + "symfony/framework-bundle": "~2.7|~3.0", + "symfony/process": "~2.7|~3.0", + "symfony/yaml": "~2.7|~3.0" + }, + "require-dev": { + "doctrine/orm": "~2.4", + "symfony/doctrine-bridge": "~2.7|~3.0", + "twig/twig": "~1.18" + }, + "time": "2015-12-20 20:01:41", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sensio\\Bundle\\GeneratorBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle generates code for you" + }, + { + "name": "symfony/phpunit-bridge", + "version": "v2.8.1", + "version_normalized": "2.8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "2deb2ccbe184948ae4e85e7b99e962738d3d2d69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/2deb2ccbe184948ae4e85e7b99e962738d3d2d69", + "reference": "2deb2ccbe184948ae4e85e7b99e962738d3d2d69", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "time": "2015-12-11 08:57:52", + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony PHPUnit Bridge", + "homepage": "https://symfony.com" + } +] diff --git a/vendor/doctrine/annotations/LICENSE b/vendor/doctrine/annotations/LICENSE new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/vendor/doctrine/annotations/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/annotations/README.md b/vendor/doctrine/annotations/README.md new file mode 100644 index 0000000..ebb30e0 --- /dev/null +++ b/vendor/doctrine/annotations/README.md @@ -0,0 +1,19 @@ +# Doctrine Annotations + +[![Build Status](https://travis-ci.org/doctrine/annotations.png?branch=master)](https://travis-ci.org/doctrine/annotations) + +Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)). + +## Changelog + +### v1.2.0 + + * HHVM support + * Allowing dangling comma in annotations + * Excluded annotations are no longer autoloaded + * Importing namespaces also in traits + * Added support for `::class` 5.5-style constant, works also in 5.3 and 5.4 + +### v1.1 + + * Add Exception when ZendOptimizer+ or Opcache is configured to drop comments diff --git a/vendor/doctrine/annotations/composer.json b/vendor/doctrine/annotations/composer.json new file mode 100644 index 0000000..1c65f6c --- /dev/null +++ b/vendor/doctrine/annotations/composer.json @@ -0,0 +1,31 @@ +{ + "name": "doctrine/annotations", + "type": "library", + "description": "Docblock Annotations Parser", + "keywords": ["annotations", "docblock", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2", + "doctrine/lexer": "1.*" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Annotations\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php new file mode 100644 index 0000000..a79a0f8 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Annotations class. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Annotation +{ + /** + * Value property. Common among all derived classes. + * + * @var string + */ + public $value; + + /** + * Constructor. + * + * @param array $data Key-value for properties to be defined in this class. + */ + public final function __construct(array $data) + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + /** + * Error handler for unknown property accessor in Annotation class. + * + * @param string $name Unknown property name. + * + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } + + /** + * Error handler for unknown property mutator in Annotation class. + * + * @param string $name Unknown property name. + * @param mixed $value Property value. + * + * @throws \BadMethodCallException + */ + public function __set($name, $value) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php new file mode 100644 index 0000000..dbef6df --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the attribute type during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Attribute +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var boolean + */ + public $required = false; +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php new file mode 100644 index 0000000..53134e3 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the types of all declared attributes during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Attributes +{ + /** + * @var array + */ + public $value; +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php new file mode 100644 index 0000000..e122a75 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the available values during the parsing process. + * + * @since 2.4 + * @author Fabio B. Silva + * + * @Annotation + * @Attributes({ + * @Attribute("value", required = true, type = "array"), + * @Attribute("literal", required = false, type = "array") + * }) + */ +final class Enum +{ + /** + * @var array + */ + public $value; + + /** + * Literal target declaration. + * + * @var array + */ + public $literal; + + /** + * Annotation constructor. + * + * @param array $values + * + * @throws \InvalidArgumentException + */ + public function __construct(array $values) + { + if ( ! isset($values['literal'])) { + $values['literal'] = array(); + } + + foreach ($values['value'] as $var) { + if( ! is_scalar($var)) { + throw new \InvalidArgumentException(sprintf( + '@Enum supports only scalar values "%s" given.', + is_object($var) ? get_class($var) : gettype($var) + )); + } + } + + foreach ($values['literal'] as $key => $var) { + if( ! in_array($key, $values['value'])) { + throw new \InvalidArgumentException(sprintf( + 'Undefined enumerator value "%s" for literal "%s".', + $key , $var + )); + } + } + + $this->value = $values['value']; + $this->literal = $values['literal']; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php new file mode 100644 index 0000000..175226a --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser to ignore specific + * annotations during the parsing process. + * + * @Annotation + * @author Johannes M. Schmitt + */ +final class IgnoreAnnotation +{ + /** + * @var array + */ + public $names; + + /** + * Constructor. + * + * @param array $values + * + * @throws \RuntimeException + */ + public function __construct(array $values) + { + if (is_string($values['value'])) { + $values['value'] = array($values['value']); + } + if (!is_array($values['value'])) { + throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value']))); + } + + $this->names = $values['value']; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php new file mode 100644 index 0000000..d67f960 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check if that attribute is required during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Required +{ +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php new file mode 100644 index 0000000..f6c5445 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the annotation target during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Target +{ + const TARGET_CLASS = 1; + const TARGET_METHOD = 2; + const TARGET_PROPERTY = 4; + const TARGET_ANNOTATION = 8; + const TARGET_ALL = 15; + + /** + * @var array + */ + private static $map = array( + 'ALL' => self::TARGET_ALL, + 'CLASS' => self::TARGET_CLASS, + 'METHOD' => self::TARGET_METHOD, + 'PROPERTY' => self::TARGET_PROPERTY, + 'ANNOTATION' => self::TARGET_ANNOTATION, + ); + + /** + * @var array + */ + public $value; + + /** + * Targets as bitmask. + * + * @var integer + */ + public $targets; + + /** + * Literal target declaration. + * + * @var integer + */ + public $literal; + + /** + * Annotation constructor. + * + * @param array $values + * + * @throws \InvalidArgumentException + */ + public function __construct(array $values) + { + if (!isset($values['value'])){ + $values['value'] = null; + } + if (is_string($values['value'])){ + $values['value'] = array($values['value']); + } + if (!is_array($values['value'])){ + throw new \InvalidArgumentException( + sprintf('@Target expects either a string value, or an array of strings, "%s" given.', + is_object($values['value']) ? get_class($values['value']) : gettype($values['value']) + ) + ); + } + + $bitmask = 0; + foreach ($values['value'] as $literal) { + if(!isset(self::$map[$literal])){ + throw new \InvalidArgumentException( + sprintf('Invalid Target "%s". Available targets: [%s]', + $literal, implode(', ', array_keys(self::$map))) + ); + } + $bitmask |= self::$map[$literal]; + } + + $this->targets = $bitmask; + $this->value = $values['value']; + $this->literal = implode(', ', $this->value); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php new file mode 100644 index 0000000..d06fe66 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -0,0 +1,197 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Description of AnnotationException + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AnnotationException extends \Exception +{ + /** + * Creates a new AnnotationException describing a Syntax error. + * + * @param string $message Exception message + * + * @return AnnotationException + */ + public static function syntaxError($message) + { + return new self('[Syntax Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a Semantical error. + * + * @param string $message Exception message + * + * @return AnnotationException + */ + public static function semanticalError($message) + { + return new self('[Semantical Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing an error which occurred during + * the creation of the annotation. + * + * @since 2.2 + * + * @param string $message + * + * @return AnnotationException + */ + public static function creationError($message) + { + return new self('[Creation Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a type error. + * + * @since 1.1 + * + * @param string $message + * + * @return AnnotationException + */ + public static function typeError($message) + { + return new self('[Type Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a constant semantical error. + * + * @since 2.3 + * + * @param string $identifier + * @param string $context + * + * @return AnnotationException + */ + public static function semanticalErrorConstants($identifier, $context = null) + { + return self::semanticalError(sprintf( + "Couldn't find constant %s%s.", + $identifier, + $context ? ', ' . $context : '' + )); + } + + /** + * Creates a new AnnotationException describing an type error of an attribute. + * + * @since 2.2 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param string $expected + * @param mixed $actual + * + * @return AnnotationException + */ + public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual) + { + return self::typeError(sprintf( + 'Attribute "%s" of @%s declared on %s expects %s, but got %s.', + $attributeName, + $annotationName, + $context, + $expected, + is_object($actual) ? 'an instance of ' . get_class($actual) : gettype($actual) + )); + } + + /** + * Creates a new AnnotationException describing an required error of an attribute. + * + * @since 2.2 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param string $expected + * + * @return AnnotationException + */ + public static function requiredError($attributeName, $annotationName, $context, $expected) + { + return self::typeError(sprintf( + 'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', + $attributeName, + $annotationName, + $context, + $expected + )); + } + + /** + * Creates a new AnnotationException describing a invalid enummerator. + * + * @since 2.4 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param array $available + * @param mixed $given + * + * @return AnnotationException + */ + public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) + { + return new self(sprintf( + '[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.', + $attributeName, + $annotationName, + $context, + implode(', ', $available), + is_object($given) ? get_class($given) : $given + )); + } + + /** + * @return AnnotationException + */ + public static function optimizerPlusSaveComments() + { + return new self( + "You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1." + ); + } + + /** + * @return AnnotationException + */ + public static function optimizerPlusLoadComments() + { + return new self( + "You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1." + ); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php new file mode 100644 index 0000000..4ebd1fb --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -0,0 +1,394 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; +use Doctrine\Common\Annotations\Annotation\Target; +use ReflectionClass; +use ReflectionMethod; +use ReflectionProperty; + +/** + * A reader for docblock annotations. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + */ +class AnnotationReader implements Reader +{ + /** + * Global map for imports. + * + * @var array + */ + private static $globalImports = array( + 'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation', + ); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names are case sensitive. + * + * @var array + */ + private static $globalIgnoredNames = array( + // Annotation tags + 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, + /* Can we enable this? 'Enum' => true, */ + 'Required' => true, + 'Target' => true, + // Widely used tags (but not existent in phpdoc) + 'fix' => true , 'fixme' => true, + 'override' => true, + // PHPDocumentor 1 tags + 'abstract'=> true, 'access'=> true, + 'code' => true, + 'deprec'=> true, + 'endcode' => true, 'exception'=> true, + 'final'=> true, + 'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true, + 'magic' => true, + 'name'=> true, + 'toc' => true, 'tutorial'=> true, + 'private' => true, + 'static'=> true, 'staticvar'=> true, 'staticVar'=> true, + 'throw' => true, + // PHPDocumentor 2 tags. + 'api' => true, 'author'=> true, + 'category'=> true, 'copyright'=> true, + 'deprecated'=> true, + 'example'=> true, + 'filesource'=> true, + 'global'=> true, + 'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true, + 'license'=> true, 'link'=> true, + 'method' => true, + 'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true, + 'return'=> true, + 'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true, + 'throws'=> true, 'todo'=> true, 'TODO'=> true, + 'usedby'=> true, 'uses' => true, + 'var'=> true, 'version'=> true, + // PHPUnit tags + 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, + // PHPCheckStyle + 'SuppressWarnings' => true, + // PHPStorm + 'noinspection' => true, + // PEAR + 'package_version' => true, + // PlantUML + 'startuml' => true, 'enduml' => true, + ); + + /** + * Add a new annotation to the globally ignored annotation names with regard to exception handling. + * + * @param string $name + */ + static public function addGlobalIgnoredName($name) + { + self::$globalIgnoredNames[$name] = true; + } + + /** + * Annotations parser. + * + * @var \Doctrine\Common\Annotations\DocParser + */ + private $parser; + + /** + * Annotations parser used to collect parsing metadata. + * + * @var \Doctrine\Common\Annotations\DocParser + */ + private $preParser; + + /** + * PHP parser used to collect imports. + * + * @var \Doctrine\Common\Annotations\PhpParser + */ + private $phpParser; + + /** + * In-memory cache mechanism to store imported annotations per class. + * + * @var array + */ + private $imports = array(); + + /** + * In-memory cache mechanism to store ignored annotations per class. + * + * @var array + */ + private $ignoredAnnotationNames = array(); + + /** + * Constructor. + * + * Initializes a new AnnotationReader. + */ + public function __construct() + { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (PHP_VERSION_ID < 70000) { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.load_comments') === "0" || ini_get('opcache.load_comments') === "0")) { + throw AnnotationException::optimizerPlusLoadComments(); + } + + if (extension_loaded('Zend OPcache') && ini_get('opcache.load_comments') == 0) { + throw AnnotationException::optimizerPlusLoadComments(); + } + } + + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); + + $this->parser = new DocParser; + $this->preParser = new DocParser; + + $this->preParser->setImports(self::$globalImports); + $this->preParser->setIgnoreNotImportedAnnotations(true); + + $this->phpParser = new PhpParser; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->parser->setTarget(Target::TARGET_CLASS); + $this->parser->setImports($this->getClassImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + + $this->parser->setTarget(Target::TARGET_PROPERTY); + $this->parser->setImports($this->getPropertyImports($property)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($property->getDocComment(), $context); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + + $this->parser->setTarget(Target::TARGET_METHOD); + $this->parser->setImports($this->getMethodImports($method)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + + return $this->parser->parse($method->getDocComment(), $context); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Returns the ignored annotations for the given class. + * + * @param \ReflectionClass $class + * + * @return array + */ + private function getIgnoredAnnotationNames(ReflectionClass $class) + { + if (isset($this->ignoredAnnotationNames[$name = $class->getName()])) { + return $this->ignoredAnnotationNames[$name]; + } + + $this->collectParsingMetadata($class); + + return $this->ignoredAnnotationNames[$name]; + } + + /** + * Retrieves imports. + * + * @param \ReflectionClass $class + * + * @return array + */ + private function getClassImports(ReflectionClass $class) + { + if (isset($this->imports[$name = $class->getName()])) { + return $this->imports[$name]; + } + + $this->collectParsingMetadata($class); + + return $this->imports[$name]; + } + + /** + * Retrieves imports for methods. + * + * @param \ReflectionMethod $method + * + * @return array + */ + private function getMethodImports(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $classImports = $this->getClassImports($class); + if (!method_exists($class, 'getTraits')) { + return $classImports; + } + + $traitImports = array(); + + foreach ($class->getTraits() as $trait) { + if ($trait->hasMethod($method->getName()) + && $trait->getFileName() === $method->getFileName() + ) { + $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); + } + } + + return array_merge($classImports, $traitImports); + } + + /** + * Retrieves imports for properties. + * + * @param \ReflectionProperty $property + * + * @return array + */ + private function getPropertyImports(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $classImports = $this->getClassImports($class); + if (!method_exists($class, 'getTraits')) { + return $classImports; + } + + $traitImports = array(); + + foreach ($class->getTraits() as $trait) { + if ($trait->hasProperty($property->getName())) { + $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); + } + } + + return array_merge($classImports, $traitImports); + } + + /** + * Collects parsing metadata for a given class. + * + * @param \ReflectionClass $class + */ + private function collectParsingMetadata(ReflectionClass $class) + { + $ignoredAnnotationNames = self::$globalIgnoredNames; + $annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name); + + foreach ($annotations as $annotation) { + if ($annotation instanceof IgnoreAnnotation) { + foreach ($annotation->names AS $annot) { + $ignoredAnnotationNames[$annot] = true; + } + } + } + + $name = $class->getName(); + + $this->imports[$name] = array_merge( + self::$globalImports, + $this->phpParser->parseClass($class), + array('__NAMESPACE__' => $class->getNamespaceName()) + ); + + $this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php new file mode 100644 index 0000000..13ceb63 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * AnnotationRegistry. + */ +final class AnnotationRegistry +{ + /** + * A map of namespaces to use for autoloading purposes based on a PSR-0 convention. + * + * Contains the namespace as key and an array of directories as value. If the value is NULL + * the include path is used for checking for the corresponding file. + * + * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own. + * + * @var array + */ + static private $autoloadNamespaces = array(); + + /** + * A map of autoloader callables. + * + * @var array + */ + static private $loaders = array(); + + /** + * @return void + */ + static public function reset() + { + self::$autoloadNamespaces = array(); + self::$loaders = array(); + } + + /** + * Registers file. + * + * @param string $file + * + * @return void + */ + static public function registerFile($file) + { + require_once $file; + } + + /** + * Adds a namespace with one or many directories to look for files or null for the include path. + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param string $namespace + * @param string|array|null $dirs + * + * @return void + */ + static public function registerAutoloadNamespace($namespace, $dirs = null) + { + self::$autoloadNamespaces[$namespace] = $dirs; + } + + /** + * Registers multiple namespaces. + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param array $namespaces + * + * @return void + */ + static public function registerAutoloadNamespaces(array $namespaces) + { + self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); + } + + /** + * Registers an autoloading callable for annotations, much like spl_autoload_register(). + * + * NOTE: These class loaders HAVE to be silent when a class was not found! + * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class. + * + * @param callable $callable + * + * @return void + * + * @throws \InvalidArgumentException + */ + static public function registerLoader($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader()."); + } + self::$loaders[] = $callable; + } + + /** + * Autoloads an annotation class silently. + * + * @param string $class + * + * @return boolean + */ + static public function loadAnnotationClass($class) + { + foreach (self::$autoloadNamespaces AS $namespace => $dirs) { + if (strpos($class, $namespace) === 0) { + $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php"; + if ($dirs === null) { + if ($path = stream_resolve_include_path($file)) { + require $path; + return true; + } + } else { + foreach((array)$dirs AS $dir) { + if (is_file($dir . DIRECTORY_SEPARATOR . $file)) { + require $dir . DIRECTORY_SEPARATOR . $file; + return true; + } + } + } + } + } + + foreach (self::$loaders AS $loader) { + if (call_user_func($loader, $class) === true) { + return true; + } + } + return false; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php new file mode 100644 index 0000000..e6dc593 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php @@ -0,0 +1,235 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Cache\Cache; + +/** + * A cache aware annotation reader. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +final class CachedReader implements Reader +{ + /** + * @var string + */ + private static $CACHE_SALT = '@[Annot]'; + + /** + * @var Reader + */ + private $delegate; + + /** + * @var Cache + */ + private $cache; + + /** + * @var boolean + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * Constructor. + * + * @param Reader $reader + * @param Cache $cache + * @param bool $debug + */ + public function __construct(Reader $reader, Cache $cache, $debug = false) + { + $this->delegate = $reader; + $this->cache = $cache; + $this->debug = (boolean) $debug; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $cacheKey = $class->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getClassAnnotations($class); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $cacheKey = $class->getName().'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getPropertyAnnotations($property); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $cacheKey = $class->getName().'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getMethodAnnotations($method); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Clears loaded annotations. + * + * @return void + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } + + /** + * Fetches a value from the cache. + * + * @param string $rawCacheKey The cache key. + * @param \ReflectionClass $class The related class. + * + * @return mixed The cached value or false when the value is not in cache. + */ + private function fetchFromCache($rawCacheKey, \ReflectionClass $class) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + if (($data = $this->cache->fetch($cacheKey)) !== false) { + if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { + return $data; + } + } + + return false; + } + + /** + * Saves a value to the cache. + * + * @param string $rawCacheKey The cache key. + * @param mixed $value The value. + * + * @return void + */ + private function saveToCache($rawCacheKey, $value) + { + $cacheKey = $rawCacheKey . self::$CACHE_SALT; + $this->cache->save($cacheKey, $value); + if ($this->debug) { + $this->cache->save('[C]'.$cacheKey, time()); + } + } + + /** + * Checks if the cache is fresh. + * + * @param string $cacheKey + * @param \ReflectionClass $class + * + * @return boolean + */ + private function isCacheFresh($cacheKey, \ReflectionClass $class) + { + if (false === $filename = $class->getFilename()) { + return true; + } + + return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php new file mode 100644 index 0000000..d864540 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Lexer\AbstractLexer; + +/** + * Simple lexer for docblock annotations. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + */ +final class DocLexer extends AbstractLexer +{ + const T_NONE = 1; + const T_INTEGER = 2; + const T_STRING = 3; + const T_FLOAT = 4; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; + const T_AT = 101; + const T_CLOSE_CURLY_BRACES = 102; + const T_CLOSE_PARENTHESIS = 103; + const T_COMMA = 104; + const T_EQUALS = 105; + const T_FALSE = 106; + const T_NAMESPACE_SEPARATOR = 107; + const T_OPEN_CURLY_BRACES = 108; + const T_OPEN_PARENTHESIS = 109; + const T_TRUE = 110; + const T_NULL = 111; + const T_COLON = 112; + + /** + * @var array + */ + protected $noCase = array( + '@' => self::T_AT, + ',' => self::T_COMMA, + '(' => self::T_OPEN_PARENTHESIS, + ')' => self::T_CLOSE_PARENTHESIS, + '{' => self::T_OPEN_CURLY_BRACES, + '}' => self::T_CLOSE_CURLY_BRACES, + '=' => self::T_EQUALS, + ':' => self::T_COLON, + '\\' => self::T_NAMESPACE_SEPARATOR + ); + + /** + * @var array + */ + protected $withCase = array( + 'true' => self::T_TRUE, + 'false' => self::T_FALSE, + 'null' => self::T_NULL + ); + + /** + * {@inheritdoc} + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*', + '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', + '"(?:""|[^"])*+"', + ); + } + + /** + * {@inheritdoc} + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '\*+', '(.)'); + } + + /** + * {@inheritdoc} + */ + protected function getType(&$value) + { + $type = self::T_NONE; + + if ($value[0] === '"') { + $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + } + + if (isset($this->noCase[$value])) { + return $this->noCase[$value]; + } + + if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) { + return self::T_IDENTIFIER; + } + + $lowerValue = strtolower($value); + + if (isset($this->withCase[$lowerValue])) { + return $this->withCase[$lowerValue]; + } + + // Checking numeric value + if (is_numeric($value)) { + return (strpos($value, '.') !== false || stripos($value, 'e') !== false) + ? self::T_FLOAT : self::T_INTEGER; + } + + return $type; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php new file mode 100644 index 0000000..db66846 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php @@ -0,0 +1,1138 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\Attribute; +use ReflectionClass; +use Doctrine\Common\Annotations\Annotation\Enum; +use Doctrine\Common\Annotations\Annotation\Target; +use Doctrine\Common\Annotations\Annotation\Attributes; + +/** + * A parser for docblock annotations. + * + * It is strongly discouraged to change the default annotation parsing process. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + * @author Fabio B. Silva + */ +final class DocParser +{ + /** + * An array of all valid tokens for a class name. + * + * @var array + */ + private static $classIdentifiers = array( + DocLexer::T_IDENTIFIER, + DocLexer::T_TRUE, + DocLexer::T_FALSE, + DocLexer::T_NULL + ); + + /** + * The lexer. + * + * @var \Doctrine\Common\Annotations\DocLexer + */ + private $lexer; + + /** + * Current target context. + * + * @var string + */ + private $target; + + /** + * Doc parser used to collect annotation target. + * + * @var \Doctrine\Common\Annotations\DocParser + */ + private static $metadataParser; + + /** + * Flag to control if the current annotation is nested or not. + * + * @var boolean + */ + private $isNestedAnnotation = false; + + /** + * Hashmap containing all use-statements that are to be used when parsing + * the given doc block. + * + * @var array + */ + private $imports = array(); + + /** + * This hashmap is used internally to cache results of class_exists() + * look-ups. + * + * @var array + */ + private $classExists = array(); + + /** + * Whether annotations that have not been imported should be ignored. + * + * @var boolean + */ + private $ignoreNotImportedAnnotations = false; + + /** + * An array of default namespaces if operating in simple mode. + * + * @var array + */ + private $namespaces = array(); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names must be the raw names as used in the class, not the fully qualified + * class names. + * + * @var array + */ + private $ignoredAnnotationNames = array(); + + /** + * @var string + */ + private $context = ''; + + /** + * Hash-map for caching annotation metadata. + * + * @var array + */ + private static $annotationMetadata = array( + 'Doctrine\Common\Annotations\Annotation\Target' => array( + 'is_annotation' => true, + 'has_constructor' => true, + 'properties' => array(), + 'targets_literal' => 'ANNOTATION_CLASS', + 'targets' => Target::TARGET_CLASS, + 'default_property' => 'value', + 'attribute_types' => array( + 'value' => array( + 'required' => false, + 'type' =>'array', + 'array_type'=>'string', + 'value' =>'array' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Attribute' => array( + 'is_annotation' => true, + 'has_constructor' => false, + 'targets_literal' => 'ANNOTATION_ANNOTATION', + 'targets' => Target::TARGET_ANNOTATION, + 'default_property' => 'name', + 'properties' => array( + 'name' => 'name', + 'type' => 'type', + 'required' => 'required' + ), + 'attribute_types' => array( + 'value' => array( + 'required' => true, + 'type' =>'string', + 'value' =>'string' + ), + 'type' => array( + 'required' =>true, + 'type' =>'string', + 'value' =>'string' + ), + 'required' => array( + 'required' =>false, + 'type' =>'boolean', + 'value' =>'boolean' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Attributes' => array( + 'is_annotation' => true, + 'has_constructor' => false, + 'targets_literal' => 'ANNOTATION_CLASS', + 'targets' => Target::TARGET_CLASS, + 'default_property' => 'value', + 'properties' => array( + 'value' => 'value' + ), + 'attribute_types' => array( + 'value' => array( + 'type' =>'array', + 'required' =>true, + 'array_type'=>'Doctrine\Common\Annotations\Annotation\Attribute', + 'value' =>'array' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Enum' => array( + 'is_annotation' => true, + 'has_constructor' => true, + 'targets_literal' => 'ANNOTATION_PROPERTY', + 'targets' => Target::TARGET_PROPERTY, + 'default_property' => 'value', + 'properties' => array( + 'value' => 'value' + ), + 'attribute_types' => array( + 'value' => array( + 'type' => 'array', + 'required' => true, + ), + 'literal' => array( + 'type' => 'array', + 'required' => false, + ), + ), + ), + ); + + /** + * Hash-map for handle types declaration. + * + * @var array + */ + private static $typeMap = array( + 'float' => 'double', + 'bool' => 'boolean', + // allow uppercase Boolean in honor of George Boole + 'Boolean' => 'boolean', + 'int' => 'integer', + ); + + /** + * Constructs a new DocParser. + */ + public function __construct() + { + $this->lexer = new DocLexer; + } + + /** + * Sets the annotation names that are ignored during the parsing process. + * + * The names are supposed to be the raw names as used in the class, not the + * fully qualified class names. + * + * @param array $names + * + * @return void + */ + public function setIgnoredAnnotationNames(array $names) + { + $this->ignoredAnnotationNames = $names; + } + + /** + * Sets ignore on not-imported annotations. + * + * @param boolean $bool + * + * @return void + */ + public function setIgnoreNotImportedAnnotations($bool) + { + $this->ignoreNotImportedAnnotations = (boolean) $bool; + } + + /** + * Sets the default namespaces. + * + * @param array $namespace + * + * @return void + * + * @throws \RuntimeException + */ + public function addNamespace($namespace) + { + if ($this->imports) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + + $this->namespaces[] = $namespace; + } + + /** + * Sets the imports. + * + * @param array $imports + * + * @return void + * + * @throws \RuntimeException + */ + public function setImports(array $imports) + { + if ($this->namespaces) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + + $this->imports = $imports; + } + + /** + * Sets current target context as bitmask. + * + * @param integer $target + * + * @return void + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Parses the given docblock string for annotations. + * + * @param string $input The docblock string to parse. + * @param string $context The parsing context. + * + * @return array Array of annotations. If no annotations are found, an empty array is returned. + */ + public function parse($input, $context = '') + { + $pos = $this->findInitialTokenPosition($input); + if ($pos === null) { + return array(); + } + + $this->context = $context; + + $this->lexer->setInput(trim(substr($input, $pos), '* /')); + $this->lexer->moveNext(); + + return $this->Annotations(); + } + + /** + * Finds the first valid annotation + * + * @param string $input The docblock string to parse + * + * @return int|null + */ + private function findInitialTokenPosition($input) + { + $pos = 0; + + // search for first valid annotation + while (($pos = strpos($input, '@', $pos)) !== false) { + // if the @ is preceded by a space or * it is valid + if ($pos === 0 || $input[$pos - 1] === ' ' || $input[$pos - 1] === '*') { + return $pos; + } + + $pos++; + } + + return null; + } + + /** + * Attempts to match the given token with the current lookahead token. + * If they match, updates the lookahead token; otherwise raises a syntax error. + * + * @param integer $token Type of token. + * + * @return boolean True if tokens match; false otherwise. + */ + private function match($token) + { + if ( ! $this->lexer->isNextToken($token) ) { + $this->syntaxError($this->lexer->getLiteral($token)); + } + + return $this->lexer->moveNext(); + } + + /** + * Attempts to match the current lookahead token with any of the given tokens. + * + * If any of them matches, this method updates the lookahead token; otherwise + * a syntax error is raised. + * + * @param array $tokens + * + * @return boolean + */ + private function matchAny(array $tokens) + { + if ( ! $this->lexer->isNextTokenAny($tokens)) { + $this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens))); + } + + return $this->lexer->moveNext(); + } + + /** + * Generates a new syntax error. + * + * @param string $expected Expected string. + * @param array|null $token Optional token. + * + * @return void + * + * @throws AnnotationException + */ + private function syntaxError($expected, $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $message = sprintf('Expected %s, got ', $expected); + $message .= ($this->lexer->lookahead === null) + ? 'end of string' + : sprintf("'%s' at position %s", $token['value'], $token['position']); + + if (strlen($this->context)) { + $message .= ' in ' . $this->context; + } + + $message .= '.'; + + throw AnnotationException::syntaxError($message); + } + + /** + * Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism + * but uses the {@link AnnotationRegistry} to load classes. + * + * @param string $fqcn + * + * @return boolean + */ + private function classExists($fqcn) + { + if (isset($this->classExists[$fqcn])) { + return $this->classExists[$fqcn]; + } + + // first check if the class already exists, maybe loaded through another AnnotationReader + if (class_exists($fqcn, false)) { + return $this->classExists[$fqcn] = true; + } + + // final check, does this class exist? + return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn); + } + + /** + * Collects parsing metadata for a given annotation class + * + * @param string $name The annotation name + * + * @return void + */ + private function collectAnnotationMetadata($name) + { + if (self::$metadataParser === null) { + self::$metadataParser = new self(); + + self::$metadataParser->setIgnoreNotImportedAnnotations(true); + self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames); + self::$metadataParser->setImports(array( + 'enum' => 'Doctrine\Common\Annotations\Annotation\Enum', + 'target' => 'Doctrine\Common\Annotations\Annotation\Target', + 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute', + 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes' + )); + + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Enum.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php'); + } + + $class = new \ReflectionClass($name); + $docComment = $class->getDocComment(); + + // Sets default values for annotation metadata + $metadata = array( + 'default_property' => null, + 'has_constructor' => (null !== $constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0, + 'properties' => array(), + 'property_types' => array(), + 'attribute_types' => array(), + 'targets_literal' => null, + 'targets' => Target::TARGET_ALL, + 'is_annotation' => false !== strpos($docComment, '@Annotation'), + ); + + // verify that the class is really meant to be an annotation + if ($metadata['is_annotation']) { + self::$metadataParser->setTarget(Target::TARGET_CLASS); + + foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { + if ($annotation instanceof Target) { + $metadata['targets'] = $annotation->targets; + $metadata['targets_literal'] = $annotation->literal; + + continue; + } + + if ($annotation instanceof Attributes) { + foreach ($annotation->value as $attribute) { + $this->collectAttributeTypeMetadata($metadata, $attribute); + } + } + } + + // if not has a constructor will inject values into public properties + if (false === $metadata['has_constructor']) { + // collect all public properties + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $metadata['properties'][$property->name] = $property->name; + + if (false === ($propertyComment = $property->getDocComment())) { + continue; + } + + $attribute = new Attribute(); + + $attribute->required = (false !== strpos($propertyComment, '@Required')); + $attribute->name = $property->name; + $attribute->type = (false !== strpos($propertyComment, '@var') && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) + ? $matches[1] + : 'mixed'; + + $this->collectAttributeTypeMetadata($metadata, $attribute); + + // checks if the property has @Enum + if (false !== strpos($propertyComment, '@Enum')) { + $context = 'property ' . $class->name . "::\$" . $property->name; + + self::$metadataParser->setTarget(Target::TARGET_PROPERTY); + + foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) { + if ( ! $annotation instanceof Enum) { + continue; + } + + $metadata['enum'][$property->name]['value'] = $annotation->value; + $metadata['enum'][$property->name]['literal'] = ( ! empty($annotation->literal)) + ? $annotation->literal + : $annotation->value; + } + } + } + + // choose the first property as default property + $metadata['default_property'] = reset($metadata['properties']); + } + } + + self::$annotationMetadata[$name] = $metadata; + } + + /** + * Collects parsing metadata for a given attribute. + * + * @param array $metadata + * @param Attribute $attribute + * + * @return void + */ + private function collectAttributeTypeMetadata(&$metadata, Attribute $attribute) + { + // handle internal type declaration + $type = isset(self::$typeMap[$attribute->type]) + ? self::$typeMap[$attribute->type] + : $attribute->type; + + // handle the case if the property type is mixed + if ('mixed' === $type) { + return; + } + + // Evaluate type + switch (true) { + // Checks if the property has array + case (false !== $pos = strpos($type, '<')): + $arrayType = substr($type, $pos + 1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; + break; + + // Checks if the property has type[] + case (false !== $pos = strrpos($type, '[')): + $arrayType = substr($type, 0, $pos); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; + break; + } + + $metadata['attribute_types'][$attribute->name]['type'] = $type; + $metadata['attribute_types'][$attribute->name]['value'] = $attribute->type; + $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required; + } + + /** + * Annotations ::= Annotation {[ "*" ]* [Annotation]}* + * + * @return array + */ + private function Annotations() + { + $annotations = array(); + + while (null !== $this->lexer->lookahead) { + if (DocLexer::T_AT !== $this->lexer->lookahead['type']) { + $this->lexer->moveNext(); + continue; + } + + // make sure the @ is preceded by non-catchable pattern + if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) { + $this->lexer->moveNext(); + continue; + } + + // make sure the @ is followed by either a namespace separator, or + // an identifier token + if ((null === $peek = $this->lexer->glimpse()) + || (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true)) + || $peek['position'] !== $this->lexer->lookahead['position'] + 1) { + $this->lexer->moveNext(); + continue; + } + + $this->isNestedAnnotation = false; + if (false !== $annot = $this->Annotation()) { + $annotations[] = $annot; + } + } + + return $annotations; + } + + /** + * Annotation ::= "@" AnnotationName MethodCall + * AnnotationName ::= QualifiedName | SimpleName + * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName + * NameSpacePart ::= identifier | null | false | true + * SimpleName ::= identifier | null | false | true + * + * @return mixed False if it is not a valid annotation. + * + * @throws AnnotationException + */ + private function Annotation() + { + $this->match(DocLexer::T_AT); + + // check if we have an annotation + $name = $this->Identifier(); + + // only process names which are not fully qualified, yet + // fully qualified names must start with a \ + $originalName = $name; + + if ('\\' !== $name[0]) { + $alias = (false === $pos = strpos($name, '\\'))? $name : substr($name, 0, $pos); + $found = false; + + if ($this->namespaces) { + foreach ($this->namespaces as $namespace) { + if ($this->classExists($namespace.'\\'.$name)) { + $name = $namespace.'\\'.$name; + $found = true; + break; + } + } + } elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) { + $found = true; + $name = (false !== $pos) + ? $this->imports[$loweredAlias] . substr($name, $pos) + : $this->imports[$loweredAlias]; + } elseif ( ! isset($this->ignoredAnnotationNames[$name]) + && isset($this->imports['__NAMESPACE__']) + && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name) + ) { + $name = $this->imports['__NAMESPACE__'].'\\'.$name; + $found = true; + } elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) { + $found = true; + } + + if ( ! $found) { + if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) { + return false; + } + + throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context)); + } + } + + if ( ! $this->classExists($name)) { + throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context)); + } + + // at this point, $name contains the fully qualified class name of the + // annotation, and it is also guaranteed that this class exists, and + // that it is loaded + + + // collects the metadata annotation only if there is not yet + if ( ! isset(self::$annotationMetadata[$name])) { + $this->collectAnnotationMetadata($name); + } + + // verify that the class is really meant to be an annotation and not just any ordinary class + if (self::$annotationMetadata[$name]['is_annotation'] === false) { + if (isset($this->ignoredAnnotationNames[$originalName])) { + return false; + } + + throw AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context)); + } + + //if target is nested annotation + $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target; + + // Next will be nested + $this->isNestedAnnotation = true; + + //if annotation does not support current target + if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) { + throw AnnotationException::semanticalError( + sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.', + $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal']) + ); + } + + $values = $this->MethodCall(); + + if (isset(self::$annotationMetadata[$name]['enum'])) { + // checks all declared attributes + foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) { + // checks if the attribute is a valid enumerator + if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) { + throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]); + } + } + } + + // checks all declared attributes + foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) { + if ($property === self::$annotationMetadata[$name]['default_property'] + && !isset($values[$property]) && isset($values['value'])) { + $property = 'value'; + } + + // handle a not given attribute or null value + if (!isset($values[$property])) { + if ($type['required']) { + throw AnnotationException::requiredError($property, $originalName, $this->context, 'a(n) '.$type['value']); + } + + continue; + } + + if ($type['type'] === 'array') { + // handle the case of a single value + if ( ! is_array($values[$property])) { + $values[$property] = array($values[$property]); + } + + // checks if the attribute has array type declaration, such as "array" + if (isset($type['array_type'])) { + foreach ($values[$property] as $item) { + if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) { + throw AnnotationException::attributeTypeError($property, $originalName, $this->context, 'either a(n) '.$type['array_type'].', or an array of '.$type['array_type'].'s', $item); + } + } + } + } elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) { + throw AnnotationException::attributeTypeError($property, $originalName, $this->context, 'a(n) '.$type['value'], $values[$property]); + } + } + + // check if the annotation expects values via the constructor, + // or directly injected into public properties + if (self::$annotationMetadata[$name]['has_constructor'] === true) { + return new $name($values); + } + + $instance = new $name(); + + foreach ($values as $property => $value) { + if (!isset(self::$annotationMetadata[$name]['properties'][$property])) { + if ('value' !== $property) { + throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties']))); + } + + // handle the case if the property has no annotations + if ( ! $property = self::$annotationMetadata[$name]['default_property']) { + throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values))); + } + } + + $instance->{$property} = $value; + } + + return $instance; + } + + /** + * MethodCall ::= ["(" [Values] ")"] + * + * @return array + */ + private function MethodCall() + { + $values = array(); + + if ( ! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { + return $values; + } + + $this->match(DocLexer::T_OPEN_PARENTHESIS); + + if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + $values = $this->Values(); + } + + $this->match(DocLexer::T_CLOSE_PARENTHESIS); + + return $values; + } + + /** + * Values ::= Array | Value {"," Value}* [","] + * + * @return array + */ + private function Values() + { + $values = array($this->Value()); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + break; + } + + $token = $this->lexer->lookahead; + $value = $this->Value(); + + if ( ! is_object($value) && ! is_array($value)) { + $this->syntaxError('Value', $token); + } + + $values[] = $value; + } + + foreach ($values as $k => $value) { + if (is_object($value) && $value instanceof \stdClass) { + $values[$value->name] = $value->value; + } else if ( ! isset($values['value'])){ + $values['value'] = $value; + } else { + if ( ! is_array($values['value'])) { + $values['value'] = array($values['value']); + } + + $values['value'][] = $value; + } + + unset($values[$k]); + } + + return $values; + } + + /** + * Constant ::= integer | string | float | boolean + * + * @return mixed + * + * @throws AnnotationException + */ + private function Constant() + { + $identifier = $this->Identifier(); + + if ( ! defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) { + list($className, $const) = explode('::', $identifier); + + $alias = (false === $pos = strpos($className, '\\')) ? $className : substr($className, 0, $pos); + $found = false; + + switch (true) { + case !empty ($this->namespaces): + foreach ($this->namespaces as $ns) { + if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) { + $className = $ns.'\\'.$className; + $found = true; + break; + } + } + break; + + case isset($this->imports[$loweredAlias = strtolower($alias)]): + $found = true; + $className = (false !== $pos) + ? $this->imports[$loweredAlias] . substr($className, $pos) + : $this->imports[$loweredAlias]; + break; + + default: + if(isset($this->imports['__NAMESPACE__'])) { + $ns = $this->imports['__NAMESPACE__']; + + if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) { + $className = $ns.'\\'.$className; + $found = true; + } + } + break; + } + + if ($found) { + $identifier = $className . '::' . $const; + } + } + + // checks if identifier ends with ::class, \strlen('::class') === 7 + $classPos = stripos($identifier, '::class'); + if ($classPos === strlen($identifier) - 7) { + return substr($identifier, 0, $classPos); + } + + if (!defined($identifier)) { + throw AnnotationException::semanticalErrorConstants($identifier, $this->context); + } + + return constant($identifier); + } + + /** + * Identifier ::= string + * + * @return string + */ + private function Identifier() + { + // check if we have an annotation + if ( ! $this->lexer->isNextTokenAny(self::$classIdentifiers)) { + $this->syntaxError('namespace separator or identifier'); + } + + $this->lexer->moveNext(); + + $className = $this->lexer->token['value']; + + while ($this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value'])) + && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) { + + $this->match(DocLexer::T_NAMESPACE_SEPARATOR); + $this->matchAny(self::$classIdentifiers); + + $className .= '\\' . $this->lexer->token['value']; + } + + return $className; + } + + /** + * Value ::= PlainValue | FieldAssignment + * + * @return mixed + */ + private function Value() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type']) { + return $this->FieldAssignment(); + } + + return $this->PlainValue(); + } + + /** + * PlainValue ::= integer | string | float | boolean | Array | Annotation + * + * @return mixed + */ + private function PlainValue() + { + if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { + return $this->Arrayx(); + } + + if ($this->lexer->isNextToken(DocLexer::T_AT)) { + return $this->Annotation(); + } + + if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { + return $this->Constant(); + } + + switch ($this->lexer->lookahead['type']) { + case DocLexer::T_STRING: + $this->match(DocLexer::T_STRING); + return $this->lexer->token['value']; + + case DocLexer::T_INTEGER: + $this->match(DocLexer::T_INTEGER); + return (int)$this->lexer->token['value']; + + case DocLexer::T_FLOAT: + $this->match(DocLexer::T_FLOAT); + return (float)$this->lexer->token['value']; + + case DocLexer::T_TRUE: + $this->match(DocLexer::T_TRUE); + return true; + + case DocLexer::T_FALSE: + $this->match(DocLexer::T_FALSE); + return false; + + case DocLexer::T_NULL: + $this->match(DocLexer::T_NULL); + return null; + + default: + $this->syntaxError('PlainValue'); + } + } + + /** + * FieldAssignment ::= FieldName "=" PlainValue + * FieldName ::= identifier + * + * @return array + */ + private function FieldAssignment() + { + $this->match(DocLexer::T_IDENTIFIER); + $fieldName = $this->lexer->token['value']; + + $this->match(DocLexer::T_EQUALS); + + $item = new \stdClass(); + $item->name = $fieldName; + $item->value = $this->PlainValue(); + + return $item; + } + + /** + * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}" + * + * @return array + */ + private function Arrayx() + { + $array = $values = array(); + + $this->match(DocLexer::T_OPEN_CURLY_BRACES); + + // If the array is empty, stop parsing and return. + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { + $this->match(DocLexer::T_CLOSE_CURLY_BRACES); + + return $array; + } + + $values[] = $this->ArrayEntry(); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + + // optional trailing comma + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { + break; + } + + $values[] = $this->ArrayEntry(); + } + + $this->match(DocLexer::T_CLOSE_CURLY_BRACES); + + foreach ($values as $value) { + list ($key, $val) = $value; + + if ($key !== null) { + $array[$key] = $val; + } else { + $array[] = $val; + } + } + + return $array; + } + + /** + * ArrayEntry ::= Value | KeyValuePair + * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant + * Key ::= string | integer | Constant + * + * @return array + */ + private function ArrayEntry() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type'] + || DocLexer::T_COLON === $peek['type']) { + + if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { + $key = $this->Constant(); + } else { + $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING)); + $key = $this->lexer->token['value']; + } + + $this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON)); + + return array($key, $this->PlainValue()); + } + + return array(null, $this->Value()); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php new file mode 100644 index 0000000..24add1b --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php @@ -0,0 +1,288 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * File cache reader for annotations. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + * + * @deprecated the FileCacheReader is deprecated and will be removed + * in version 2.0.0 of doctrine/annotations. Please use the + * {@see \Doctrine\Common\Annotations\CachedReader} instead. + */ +class FileCacheReader implements Reader +{ + /** + * @var Reader + */ + private $reader; + + /** + * @var string + */ + private $dir; + + /** + * @var bool + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * @var array + */ + private $classNameHashes = array(); + + /** + * @var int + */ + private $umask; + + /** + * Constructor. + * + * @param Reader $reader + * @param string $cacheDir + * @param boolean $debug + * + * @throws \InvalidArgumentException + */ + public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002) + { + if ( ! is_int($umask)) { + throw new \InvalidArgumentException(sprintf( + 'The parameter umask must be an integer, was: %s', + gettype($umask) + )); + } + + $this->reader = $reader; + $this->umask = $umask; + + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777 & (~$this->umask), true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir)); + } + + $this->dir = rtrim($cacheDir, '\\/'); + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name]; + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($path)) { + $annot = $this->reader->getClassAnnotations($class); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getClassAnnotations($class); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name].'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($path)) { + $annot = $this->reader->getPropertyAnnotations($property); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getPropertyAnnotations($property); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name].'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($path)) { + $annot = $this->reader->getMethodAnnotations($method); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getMethodAnnotations($method); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + /** + * Saves the cache file. + * + * @param string $path + * @param mixed $data + * + * @return void + */ + private function saveCacheFile($path, $data) + { + if (!is_writable($this->dir)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $this->dir)); + } + + $tempfile = tempnam($this->dir, uniqid('', true)); + + if (false === $tempfile) { + throw new \RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir)); + } + + $written = file_put_contents($tempfile, 'umask)); + + if (false === rename($tempfile, $path)) { + @unlink($tempfile); + throw new \RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path)); + } + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Clears loaded annotations. + * + * @return void + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php new file mode 100644 index 0000000..bf7fbdc --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Allows the reader to be used in-place of Doctrine's reader. + * + * @author Johannes M. Schmitt + */ +class IndexedReader implements Reader +{ + /** + * @var Reader + */ + private $delegate; + + /** + * Constructor. + * + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->delegate = $reader; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $annotations = array(); + foreach ($this->delegate->getClassAnnotations($class) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotation) + { + return $this->delegate->getClassAnnotation($class, $annotation); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $annotations = array(); + foreach ($this->delegate->getMethodAnnotations($method) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotation) + { + return $this->delegate->getMethodAnnotation($method, $annotation); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $annotations = array(); + foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotation) + { + return $this->delegate->getPropertyAnnotation($property, $annotation); + } + + /** + * Proxies all methods to the delegate. + * + * @param string $method + * @param array $args + * + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->delegate, $method), $args); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php new file mode 100644 index 0000000..21ee7cc --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php @@ -0,0 +1,91 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use SplFileObject; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +final class PhpParser +{ + /** + * Parses a class. + * + * @param \ReflectionClass $class A ReflectionClass object. + * + * @return array A list with use statements in the form (Alias => FQN). + */ + public function parseClass(\ReflectionClass $class) + { + if (method_exists($class, 'getUseStatements')) { + return $class->getUseStatements(); + } + + if (false === $filename = $class->getFilename()) { + return array(); + } + + $content = $this->getFileContent($filename, $class->getStartLine()); + + if (null === $content) { + return array(); + } + + $namespace = preg_quote($class->getNamespaceName()); + $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); + $tokenizer = new TokenParser('parseUseStatements($class->getNamespaceName()); + + return $statements; + } + + /** + * Gets the content of the file right up to the given line number. + * + * @param string $filename The name of the file to load. + * @param integer $lineNumber The number of lines to read from file. + * + * @return string The content of the file. + */ + private function getFileContent($filename, $lineNumber) + { + if ( ! is_file($filename)) { + return null; + } + + $content = ''; + $lineCnt = 0; + $file = new SplFileObject($filename); + while (!$file->eof()) { + if ($lineCnt++ == $lineNumber) { + break; + } + + $content .= $file->fgets(); + } + + return $content; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php new file mode 100644 index 0000000..4774f87 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Interface for annotation readers. + * + * @author Johannes M. Schmitt + */ +interface Reader +{ + /** + * Gets the annotations applied to a class. + * + * @param \ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * + * @return array An array of Annotations. + */ + function getClassAnnotations(\ReflectionClass $class); + + /** + * Gets a class annotation. + * + * @param \ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getClassAnnotation(\ReflectionClass $class, $annotationName); + + /** + * Gets the annotations applied to a method. + * + * @param \ReflectionMethod $method The ReflectionMethod of the method from which + * the annotations should be read. + * + * @return array An array of Annotations. + */ + function getMethodAnnotations(\ReflectionMethod $method); + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method The ReflectionMethod to read the annotations from. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getMethodAnnotation(\ReflectionMethod $method, $annotationName); + + /** + * Gets the annotations applied to a property. + * + * @param \ReflectionProperty $property The ReflectionProperty of the property + * from which the annotations should be read. + * + * @return array An array of Annotations. + */ + function getPropertyAnnotations(\ReflectionProperty $property); + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property The ReflectionProperty to read the annotations from. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getPropertyAnnotation(\ReflectionProperty $property, $annotationName); +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php new file mode 100644 index 0000000..d4757ee --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -0,0 +1,127 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Simple Annotation Reader. + * + * This annotation reader is intended to be used in projects where you have + * full-control over all annotations that are available. + * + * @since 2.2 + * @author Johannes M. Schmitt + * @author Fabio B. Silva + */ +class SimpleAnnotationReader implements Reader +{ + /** + * @var DocParser + */ + private $parser; + + /** + * Constructor. + * + * Initializes a new SimpleAnnotationReader. + */ + public function __construct() + { + $this->parser = new DocParser(); + $this->parser->setIgnoreNotImportedAnnotations(true); + } + + /** + * Adds a namespace in which we will look for annotations. + * + * @param string $namespace + * + * @return void + */ + public function addNamespace($namespace) + { + $this->parser->addNamespace($namespace); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + return $this->parser->parse($class->getDocComment(), 'class '.$class->getName()); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()'); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName()); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php new file mode 100644 index 0000000..9bdccce --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php @@ -0,0 +1,187 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +class TokenParser +{ + /** + * The token list. + * + * @var array + */ + private $tokens; + + /** + * The number of tokens. + * + * @var int + */ + private $numTokens; + + /** + * The current array pointer. + * + * @var int + */ + private $pointer = 0; + + /** + * @param string $contents + */ + public function __construct($contents) + { + $this->tokens = token_get_all($contents); + + // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it + // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored + // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a + // docblock. If the first thing in the file is a class without a doc block this would cause calls to + // getDocBlock() on said class to return our long lost doc_comment. Argh. + // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least + // it's harmless to us. + token_get_all("numTokens = count($this->tokens); + } + + /** + * Gets the next non whitespace and non comment token. + * + * @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped. + * If FALSE then only whitespace and normal comments are skipped. + * + * @return array|null The token if exists, null otherwise. + */ + public function next($docCommentIsComment = TRUE) + { + for ($i = $this->pointer; $i < $this->numTokens; $i++) { + $this->pointer++; + if ($this->tokens[$i][0] === T_WHITESPACE || + $this->tokens[$i][0] === T_COMMENT || + ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) { + + continue; + } + + return $this->tokens[$i]; + } + + return null; + } + + /** + * Parses a single use statement. + * + * @return array A list with all found class names for a use statement. + */ + public function parseUseStatement() + { + $class = ''; + $alias = ''; + $statements = array(); + $explicitAlias = false; + while (($token = $this->next())) { + $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR; + if (!$explicitAlias && $isNameToken) { + $class .= $token[1]; + $alias = $token[1]; + } else if ($explicitAlias && $isNameToken) { + $alias .= $token[1]; + } else if ($token[0] === T_AS) { + $explicitAlias = true; + $alias = ''; + } else if ($token === ',') { + $statements[strtolower($alias)] = $class; + $class = ''; + $alias = ''; + $explicitAlias = false; + } else if ($token === ';') { + $statements[strtolower($alias)] = $class; + break; + } else { + break; + } + } + + return $statements; + } + + /** + * Gets all use statements. + * + * @param string $namespaceName The namespace name of the reflected class. + * + * @return array A list with all found use statements. + */ + public function parseUseStatements($namespaceName) + { + $statements = array(); + while (($token = $this->next())) { + if ($token[0] === T_USE) { + $statements = array_merge($statements, $this->parseUseStatement()); + continue; + } + if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) { + continue; + } + + // Get fresh array for new namespace. This is to prevent the parser to collect the use statements + // for a previous namespace with the same name. This is the case if a namespace is defined twice + // or if a namespace with the same name is commented out. + $statements = array(); + } + + return $statements; + } + + /** + * Gets the namespace. + * + * @return string The found namespace. + */ + public function parseNamespace() + { + $name = ''; + while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) { + $name .= $token[1]; + } + + return $name; + } + + /** + * Gets the class name. + * + * @return string The found class name. + */ + public function parseClass() + { + // Namespaces and class names are tokenized the same: T_STRINGs + // separated by T_NS_SEPARATOR so we can use one function to provide + // both. + return $this->parseNamespace(); + } +} diff --git a/vendor/doctrine/cache/.coveralls.yml b/vendor/doctrine/cache/.coveralls.yml new file mode 100644 index 0000000..0c08233 --- /dev/null +++ b/vendor/doctrine/cache/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +src_dir: lib +coverage_clover: build/logs/clover.xml diff --git a/vendor/doctrine/cache/.travis.yml b/vendor/doctrine/cache/.travis.yml new file mode 100644 index 0000000..62a0ec1 --- /dev/null +++ b/vendor/doctrine/cache/.travis.yml @@ -0,0 +1,44 @@ +language: php + +sudo: false + +cache: + directories: + - vendor + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +services: + - riak + - mongodb + - memcached + - redis-server + +before_install: + - if [[ $TRAVIS_PHP_VERSION != 'hhvm' && $TRAVIS_PHP_VERSION != '7.0' ]]; then pecl install riak-beta; fi; + - if [[ $TRAVIS_PHP_VERSION =~ 5.[34] ]] ; then echo 'extension="apc.so"' >> ./tests/travis/php.ini; fi; + - if [[ $TRAVIS_PHP_VERSION =~ 5.[56] ]] ; then echo yes | pecl install apcu-4.0.10; fi; + - if [[ $TRAVIS_PHP_VERSION = 7.* ]] ; then pecl config-set preferred_state beta; echo yes | pecl install apcu; fi; + - if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]]; then phpenv config-add ./tests/travis/php.ini; fi; + +install: + - travis_retry composer install + +script: + - ./vendor/bin/phpunit -c ./tests/travis/phpunit.travis.xml -v + +after_script: + - php vendor/bin/coveralls -v + +matrix: + fast_finish: true + allow_failures: + - php: hhvm + - php: 7.0 diff --git a/vendor/doctrine/cache/LICENSE b/vendor/doctrine/cache/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/cache/README.md b/vendor/doctrine/cache/README.md new file mode 100644 index 0000000..94f80a3 --- /dev/null +++ b/vendor/doctrine/cache/README.md @@ -0,0 +1,14 @@ +# Doctrine Cache + +Master: [![Build Status](https://secure.travis-ci.org/doctrine/cache.png?branch=master)](http://travis-ci.org/doctrine/cache) [![Coverage Status](https://coveralls.io/repos/doctrine/cache/badge.png?branch=master)](https://coveralls.io/r/doctrine/cache?branch=master) + +[![Latest Stable Version](https://poser.pugx.org/doctrine/cache/v/stable.png)](https://packagist.org/packages/doctrine/cache) [![Total Downloads](https://poser.pugx.org/doctrine/cache/downloads.png)](https://packagist.org/packages/doctrine/cache) + +Cache component extracted from the Doctrine Common project. + +## Changelog + +### v1.2 + +* Added support for MongoDB as Cache Provider +* Fix namespace version reset diff --git a/vendor/doctrine/cache/UPGRADE.md b/vendor/doctrine/cache/UPGRADE.md new file mode 100644 index 0000000..e1f8a50 --- /dev/null +++ b/vendor/doctrine/cache/UPGRADE.md @@ -0,0 +1,16 @@ +# Upgrade to 1.4 + +## Minor BC Break: `Doctrine\Common\Cache\FileCache#$extension` is now `private`. + +If you need to override the value of `Doctrine\Common\Cache\FileCache#$extension`, then use the +second parameter of `Doctrine\Common\Cache\FileCache#__construct()` instead of overriding +the property in your own implementation. + +## Minor BC Break: file based caches paths changed + +`Doctrine\Common\Cache\FileCache`, `Doctrine\Common\Cache\PhpFileCache` and +`Doctrine\Common\Cache\FilesystemCache` are using a different cache paths structure. + +If you rely on warmed up caches for deployments, consider that caches generated +with `doctrine/cache` `<1.4` are not compatible with the new directory structure, +and will be ignored. diff --git a/vendor/doctrine/cache/build.properties b/vendor/doctrine/cache/build.properties new file mode 100644 index 0000000..2d98c36 --- /dev/null +++ b/vendor/doctrine/cache/build.properties @@ -0,0 +1,3 @@ +# Version class and file +project.version_class = Doctrine\\Common\\Cache\\Version +project.version_file = lib/Doctrine/Common/Cache/Version.php diff --git a/vendor/doctrine/cache/build.xml b/vendor/doctrine/cache/build.xml new file mode 100644 index 0000000..a7c52e3 --- /dev/null +++ b/vendor/doctrine/cache/build.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/cache/composer.json b/vendor/doctrine/cache/composer.json new file mode 100644 index 0000000..bd7a7ec --- /dev/null +++ b/vendor/doctrine/cache/composer.json @@ -0,0 +1,37 @@ +{ + "name": "doctrine/cache", + "type": "library", + "description": "Caching library offering an object-oriented API for many cache backends", + "keywords": ["cache", "caching"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": ">=3.7", + "satooshi/php-coveralls": "~0.6", + "predis/predis": "~1.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "autoload": { + "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } + }, + "autoload-dev": { + "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php new file mode 100644 index 0000000..036583e --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * APC cache provider. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ApcCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return apc_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return apc_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return apc_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + // apc_delete returns false if the id does not exist + return apc_delete($id) || ! apc_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return apc_clear_cache() && apc_clear_cache('user'); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return apc_fetch($keys); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = apc_cache_info('', true); + $sma = apc_sma_info(); + + // @TODO - Temporary fix @see https://github.com/krakjoe/apcu/pull/42 + if (PHP_VERSION_ID >= 50500) { + $info['num_hits'] = isset($info['num_hits']) ? $info['num_hits'] : $info['nhits']; + $info['num_misses'] = isset($info['num_misses']) ? $info['num_misses'] : $info['nmisses']; + $info['start_time'] = isset($info['start_time']) ? $info['start_time'] : $info['stime']; + } + + return array( + Cache::STATS_HITS => $info['num_hits'], + Cache::STATS_MISSES => $info['num_misses'], + Cache::STATS_UPTIME => $info['start_time'], + Cache::STATS_MEMORY_USAGE => $info['mem_size'], + Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php new file mode 100644 index 0000000..31a0729 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Array cache driver. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class ArrayCache extends CacheProvider +{ + /** + * @var array $data + */ + private $data = array(); + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->doContains($id) ? $this->data[$id] : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + // isset() is required for performance optimizations, to avoid unnecessary function calls to array_key_exists. + return isset($this->data[$id]) || array_key_exists($id, $this->data); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = $data; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->data = array(); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php new file mode 100644 index 0000000..89fe323 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,116 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + * @author Kévin Dunglas + */ +interface Cache +{ + const STATS_HITS = 'hits'; + const STATS_MISSES = 'misses'; + const STATS_UPTIME = 'uptime'; + const STATS_MEMORY_USAGE = 'memory_usage'; + const STATS_MEMORY_AVAILABLE = 'memory_available'; + /** + * Only for backward compatibility (may be removed in next major release) + * + * @deprecated + */ + const STATS_MEMORY_AVAILIABLE = 'memory_available'; + + /** + * Fetches an entry from the cache. + * + * @param string $id The id of the cache entry to fetch. + * + * @return mixed The cached data or FALSE, if no cache entry exists for the given id. + */ + public function fetch($id); + + /** + * Tests if an entry exists in the cache. + * + * @param string $id The cache id of the entry to check for. + * + * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + public function contains($id); + + /** + * Puts data into the cache. + * + * If a cache entry with the given id already exists, its data will be replaced. + * + * @param string $id The cache id. + * @param mixed $data The cache entry/data. + * @param int $lifeTime The lifetime in number of seconds for this cache entry. + * If zero (the default), the entry never expires (although it may be deleted from the cache + * to make place for other entries). + * + * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + public function save($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id The cache id. + * + * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. + * Deleting a non-existing entry is considered successful. + */ + public function delete($id); + + /** + * Retrieves cached information from the data store. + * + * The server's statistics array has the following values: + * + * - hits + * Number of keys that have been requested and found present. + * + * - misses + * Number of items that have been requested and not found. + * + * - uptime + * Time that the server is running. + * + * - memory_usage + * Memory used by this server to store items. + * + * - memory_available + * Memory allowed to use for storage. + * + * @since 2.2 + * + * @return array|null An associative array with server's statistics if available, NULL otherwise. + */ + public function getStats(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php new file mode 100644 index 0000000..b1d4d67 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,277 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base class for cache provider implementations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Fabio B. Silva + */ +abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache +{ + const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; + + /** + * The namespace to prefix all cache ids with. + * + * @var string + */ + private $namespace = ''; + + /** + * The namespace version. + * + * @var integer|null + */ + private $namespaceVersion; + + /** + * Sets the namespace to prefix all cache ids with. + * + * @param string $namespace + * + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = (string) $namespace; + $this->namespaceVersion = null; + } + + /** + * Retrieves the namespace that prefixes all cache ids. + * + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * {@inheritdoc} + */ + public function fetch($id) + { + return $this->doFetch($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function fetchMultiple(array $keys) + { + if (empty($keys)) { + return array(); + } + + // note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys + $namespacedKeys = array_combine($keys, array_map(array($this, 'getNamespacedId'), $keys)); + $items = $this->doFetchMultiple($namespacedKeys); + $foundItems = array(); + + // no internal array function supports this sort of mapping: needs to be iterative + // this filters and combines keys in one pass + foreach ($namespacedKeys as $requestedKey => $namespacedKey) { + if (isset($items[$namespacedKey]) || array_key_exists($namespacedKey, $items)) { + $foundItems[$requestedKey] = $items[$namespacedKey]; + } + } + + return $foundItems; + } + + /** + * {@inheritdoc} + */ + public function contains($id) + { + return $this->doContains($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = 0) + { + return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + public function delete($id) + { + return $this->doDelete($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function getStats() + { + return $this->doGetStats(); + } + + /** + * {@inheritDoc} + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * {@inheritDoc} + */ + public function deleteAll() + { + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->getNamespaceVersion() + 1; + + if ($this->doSave($namespaceCacheKey, $namespaceVersion)) { + $this->namespaceVersion = $namespaceVersion; + + return true; + } + + return false; + } + + /** + * Prefixes the passed id with the configured namespace value. + * + * @param string $id The id to namespace. + * + * @return string The namespaced id. + */ + private function getNamespacedId($id) + { + $namespaceVersion = $this->getNamespaceVersion(); + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Returns the namespace cache key. + * + * @return string + */ + private function getNamespaceCacheKey() + { + return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + } + + /** + * Returns the namespace version. + * + * @return integer + */ + private function getNamespaceVersion() + { + if (null !== $this->namespaceVersion) { + return $this->namespaceVersion; + } + + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1; + + return $this->namespaceVersion; + } + + /** + * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it. + * + * @param array $keys Array of keys to retrieve from cache + * @return array Array of values retrieved for the given keys. + */ + protected function doFetchMultiple(array $keys) + { + $returnValues = array(); + + foreach ($keys as $key) { + if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) { + $returnValues[$key] = $item; + } + } + + return $returnValues; + } + + /** + * Fetches an entry from the cache. + * + * @param string $id The id of the cache entry to fetch. + * + * @return mixed|false The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Tests if an entry exists in the cache. + * + * @param string $id The cache id of the entry to check for. + * + * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + abstract protected function doContains($id); + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this + * cache entry (0 => infinite lifeTime). + * + * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = 0); + + /** + * Deletes a cache entry. + * + * @param string $id The cache id. + * + * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Flushes all cache entries. + * + * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from the data store. + * + * @since 2.2 + * + * @return array|null An associative array with server's statistics if available, NULL otherwise. + */ + abstract protected function doGetStats(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php new file mode 100644 index 0000000..96c9b54 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php @@ -0,0 +1,147 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Cache provider that allows to easily chain multiple cache providers + * + * @author Michaël Gallego + */ +class ChainCache extends CacheProvider +{ + /** + * @var CacheProvider[] + */ + private $cacheProviders = array(); + + /** + * Constructor + * + * @param CacheProvider[] $cacheProviders + */ + public function __construct($cacheProviders = array()) + { + $this->cacheProviders = $cacheProviders; + } + + /** + * {@inheritDoc} + */ + public function setNamespace($namespace) + { + parent::setNamespace($namespace); + + foreach ($this->cacheProviders as $cacheProvider) { + $cacheProvider->setNamespace($namespace); + } + } + + /** + * {@inheritDoc} + */ + protected function doFetch($id) + { + foreach ($this->cacheProviders as $key => $cacheProvider) { + if ($cacheProvider->doContains($id)) { + $value = $cacheProvider->doFetch($id); + + // We populate all the previous cache layers (that are assumed to be faster) + for ($subKey = $key - 1 ; $subKey >= 0 ; $subKey--) { + $this->cacheProviders[$subKey]->doSave($id, $value); + } + + return $value; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function doContains($id) + { + foreach ($this->cacheProviders as $cacheProvider) { + if ($cacheProvider->doContains($id)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $stored = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $stored = $cacheProvider->doSave($id, $data, $lifeTime) && $stored; + } + + return $stored; + } + + /** + * {@inheritDoc} + */ + protected function doDelete($id) + { + $deleted = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $deleted = $cacheProvider->doDelete($id) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritDoc} + */ + protected function doFlush() + { + $flushed = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $flushed = $cacheProvider->doFlush() && $flushed; + } + + return $flushed; + } + + /** + * {@inheritDoc} + */ + protected function doGetStats() + { + // We return all the stats from all adapters + $stats = array(); + + foreach ($this->cacheProviders as $cacheProvider) { + $stats[] = $cacheProvider->doGetStats(); + } + + return $stats; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php new file mode 100644 index 0000000..3a91eaf --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache that can be flushed. + * + * Intended to be used for partial clearing of a cache namespace. For a more + * global "flushing", see {@see FlushableCache}. + * + * @link www.doctrine-project.org + * @since 1.4 + * @author Adirelle + */ +interface ClearableCache +{ + /** + * Deletes all cache entries in the current cache namespace. + * + * @return bool TRUE if the cache entries were successfully deleted, FALSE otherwise. + */ + public function deleteAll(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php new file mode 100644 index 0000000..c21691d --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Couchbase; + +/** + * Couchbase cache provider. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Michael Nitschinger + */ +class CouchbaseCache extends CacheProvider +{ + /** + * @var Couchbase|null + */ + private $couchbase; + + /** + * Sets the Couchbase instance to use. + * + * @param Couchbase $couchbase + * + * @return void + */ + public function setCouchbase(Couchbase $couchbase) + { + $this->couchbase = $couchbase; + } + + /** + * Gets the Couchbase instance used by the cache. + * + * @return Couchbase|null + */ + public function getCouchbase() + { + return $this->couchbase; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->couchbase->get($id) ?: false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (null !== $this->couchbase->get($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->couchbase->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->couchbase->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->couchbase->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->couchbase->getStats(); + $servers = $this->couchbase->getServers(); + $server = explode(":", $servers[0]); + $key = $server[0] . ":" . "11210"; + $stats = $stats[$key]; + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php new file mode 100644 index 0000000..b2e0427 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php @@ -0,0 +1,286 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Base file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + * @author Tobias Schultze + */ +abstract class FileCache extends CacheProvider +{ + /** + * The cache directory. + * + * @var string + */ + protected $directory; + + /** + * The cache file extension. + * + * @var string + */ + private $extension; + + /** + * @var int + */ + private $umask; + + /** + * @var int + */ + private $directoryStringLength; + + /** + * @var int + */ + private $extensionStringLength; + + /** + * @var bool + */ + private $isRunningOnWindows; + + /** + * Constructor. + * + * @param string $directory The cache directory. + * @param string $extension The cache file extension. + * + * @throws \InvalidArgumentException + */ + public function __construct($directory, $extension = '', $umask = 0002) + { + // YES, this needs to be *before* createPathIfNeeded() + if ( ! is_int($umask)) { + throw new \InvalidArgumentException(sprintf( + 'The umask parameter is required to be integer, was: %s', + gettype($umask) + )); + } + $this->umask = $umask; + + if ( ! $this->createPathIfNeeded($directory)) { + throw new \InvalidArgumentException(sprintf( + 'The directory "%s" does not exist and could not be created.', + $directory + )); + } + + if ( ! is_writable($directory)) { + throw new \InvalidArgumentException(sprintf( + 'The directory "%s" is not writable.', + $directory + )); + } + + // YES, this needs to be *after* createPathIfNeeded() + $this->directory = realpath($directory); + $this->extension = (string) $extension; + + $this->directoryStringLength = strlen($this->directory); + $this->extensionStringLength = strlen($this->extension); + $this->isRunningOnWindows = defined('PHP_WINDOWS_VERSION_BUILD'); + } + + /** + * Gets the cache directory. + * + * @return string + */ + public function getDirectory() + { + return $this->directory; + } + + /** + * Gets the cache file extension. + * + * @return string + */ + public function getExtension() + { + return $this->extension; + } + + /** + * @param string $id + * + * @return string + */ + protected function getFilename($id) + { + $hash = hash('sha256', $id); + + // This ensures that the filename is unique and that there are no invalid chars in it. + if ( + '' === $id + || ((strlen($id) * 2 + $this->extensionStringLength) > 255) + || ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258) + ) { + // Most filesystems have a limit of 255 chars for each path component. On Windows the the whole path is limited + // to 260 chars (including terminating null char). Using long UNC ("\\?\" prefix) does not work with the PHP API. + // And there is a bug in PHP (https://bugs.php.net/bug.php?id=70943) with path lengths of 259. + // So if the id in hex representation would surpass the limit, we use the hash instead. The prefix prevents + // collisions between the hash and bin2hex. + $filename = '_' . $hash; + } else { + $filename = bin2hex($id); + } + + return $this->directory + . DIRECTORY_SEPARATOR + . substr($hash, 0, 2) + . DIRECTORY_SEPARATOR + . $filename + . $this->extension; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $filename = $this->getFilename($id); + + return @unlink($filename) || ! file_exists($filename); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + foreach ($this->getIterator() as $name => $file) { + if ($file->isDir()) { + // Remove the intermediate directories which have been created to balance the tree. It only takes effect + // if the directory is empty. If several caches share the same directory but with different file extensions, + // the other ones are not removed. + @rmdir($name); + } elseif ($this->isFilenameEndingWithExtension($name)) { + // If an extension is set, only remove files which end with the given extension. + // If no extension is set, we have no other choice than removing everything. + @unlink($name); + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $usage = 0; + foreach ($this->getIterator() as $name => $file) { + if (! $file->isDir() && $this->isFilenameEndingWithExtension($name)) { + $usage += $file->getSize(); + } + } + + $free = disk_free_space($this->directory); + + return array( + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $usage, + Cache::STATS_MEMORY_AVAILABLE => $free, + ); + } + + /** + * Create path if needed. + * + * @param string $path + * @return bool TRUE on success or if path already exists, FALSE if path cannot be created. + */ + private function createPathIfNeeded($path) + { + if ( ! is_dir($path)) { + if (false === @mkdir($path, 0777 & (~$this->umask), true) && !is_dir($path)) { + return false; + } + } + + return true; + } + + /** + * Writes a string content to file in an atomic way. + * + * @param string $filename Path to the file where to write the data. + * @param string $content The content to write + * + * @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error. + */ + protected function writeFile($filename, $content) + { + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if ( ! $this->createPathIfNeeded($filepath)) { + return false; + } + + if ( ! is_writable($filepath)) { + return false; + } + + $tmpFile = tempnam($filepath, 'swap'); + @chmod($tmpFile, 0666 & (~$this->umask)); + + if (file_put_contents($tmpFile, $content) !== false) { + if (@rename($tmpFile, $filename)) { + return true; + } + + @unlink($tmpFile); + } + + return false; + } + + /** + * @return \Iterator + */ + private function getIterator() + { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + } + + /** + * @param string $name The filename + * + * @return bool + */ + private function isFilenameEndingWithExtension($name) + { + return '' === $this->extension + || strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php new file mode 100644 index 0000000..d988294 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Filesystem cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class FilesystemCache extends FileCache +{ + const EXTENSION = '.doctrinecache.data'; + + /** + * {@inheritdoc} + */ + public function __construct($directory, $extension = self::EXTENSION, $umask = 0002) + { + parent::__construct($directory, $extension, $umask); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $data = ''; + $lifetime = -1; + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (int) $line; + } + + if ($lifetime !== 0 && $lifetime < time()) { + fclose($resource); + + return false; + } + + while (false !== ($line = fgets($resource))) { + $data .= $line; + } + + fclose($resource); + + return unserialize($data); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $lifetime = -1; + $filename = $this->getFilename($id); + + if ( ! is_file($filename)) { + return false; + } + + $resource = fopen($filename, "r"); + + if (false !== ($line = fgets($resource))) { + $lifetime = (int) $line; + } + + fclose($resource); + + return $lifetime === 0 || $lifetime > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + $data = serialize($data); + $filename = $this->getFilename($id); + + return $this->writeFile($filename, $lifeTime . PHP_EOL . $data); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php new file mode 100644 index 0000000..4311d4f --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache that can be flushed. + * + * @link www.doctrine-project.org + * @since 1.4 + * @author Adirelle + */ +interface FlushableCache +{ + /** + * Flushes all cache entries, globally. + * + * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + public function flushAll(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php new file mode 100644 index 0000000..8afaeea --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcache; + +/** + * Memcache cache provider. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcacheCache extends CacheProvider +{ + /** + * @var Memcache|null + */ + private $memcache; + + /** + * Sets the memcache instance to use. + * + * @param Memcache $memcache + * + * @return void + */ + public function setMemcache(Memcache $memcache) + { + $this->memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache|null + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $flags = null; + $this->memcache->get($id, $flags); + + //if memcache has changed the value of "flags", it means the value exists + return ($flags !== null); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->memcache->set($id, $data, 0, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + // Memcache::delete() returns false if entry does not exist + return $this->memcache->delete($id) || ! $this->doContains($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcache->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcache->getStats(); + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php new file mode 100644 index 0000000..deebe5a --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use \Memcached; + +/** + * Memcached cache provider. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class MemcachedCache extends CacheProvider +{ + /** + * @var Memcached|null + */ + private $memcached; + + /** + * Sets the memcache instance to use. + * + * @param Memcached $memcached + * + * @return void + */ + public function setMemcached(Memcached $memcached) + { + $this->memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached|null + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return $this->memcached->getMulti($keys); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return false !== $this->memcached->get($id) + || $this->memcached->getResultCode() !== Memcached::RES_NOTFOUND; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->memcached->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->memcached->delete($id) + || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcached->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcached->getStats(); + $servers = $this->memcached->getServerList(); + $key = $servers[0]['host'] . ':' . $servers[0]['port']; + $stats = $stats[$key]; + return array( + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php new file mode 100644 index 0000000..75fe0ca --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php @@ -0,0 +1,197 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use MongoBinData; +use MongoCollection; +use MongoCursorException; +use MongoDate; + +/** + * MongoDB cache provider. + * + * @since 1.1 + * @author Jeremy Mikola + */ +class MongoDBCache extends CacheProvider +{ + /** + * The data field will store the serialized PHP value. + */ + const DATA_FIELD = 'd'; + + /** + * The expiration field will store a MongoDate value indicating when the + * cache entry should expire. + * + * With MongoDB 2.2+, entries can be automatically deleted by MongoDB by + * indexing this field with the "expireAfterSeconds" option equal to zero. + * This will direct MongoDB to regularly query for and delete any entries + * whose date is older than the current time. Entries without a date value + * in this field will be ignored. + * + * The cache provider will also check dates on its own, in case expired + * entries are fetched before MongoDB's TTLMonitor pass can expire them. + * + * @see http://docs.mongodb.org/manual/tutorial/expire-data/ + */ + const EXPIRATION_FIELD = 'e'; + + /** + * @var MongoCollection + */ + private $collection; + + /** + * Constructor. + * + * This provider will default to the write concern and read preference + * options set on the MongoCollection instance (or inherited from MongoDB or + * MongoClient). Using an unacknowledged write concern (< 1) may make the + * return values of delete() and save() unreliable. Reading from secondaries + * may make contain() and fetch() unreliable. + * + * @see http://www.php.net/manual/en/mongo.readpreferences.php + * @see http://www.php.net/manual/en/mongo.writeconcerns.php + * @param MongoCollection $collection + */ + public function __construct(MongoCollection $collection) + { + $this->collection = $collection; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $document = $this->collection->findOne(array('_id' => $id), array(self::DATA_FIELD, self::EXPIRATION_FIELD)); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->doDelete($id); + return false; + } + + return unserialize($document[self::DATA_FIELD]->bin); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $document = $this->collection->findOne(array('_id' => $id), array(self::EXPIRATION_FIELD)); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->doDelete($id); + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $result = $this->collection->update( + array('_id' => $id), + array('$set' => array( + self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null), + self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY), + )), + array('upsert' => true, 'multiple' => false) + ); + } catch (MongoCursorException $e) { + return false; + } + + return isset($result['ok']) ? $result['ok'] == 1 : true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $result = $this->collection->remove(array('_id' => $id)); + + return isset($result['ok']) ? $result['ok'] == 1 : true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + // Use remove() in lieu of drop() to maintain any collection indexes + $result = $this->collection->remove(); + + return isset($result['ok']) ? $result['ok'] == 1 : true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $serverStatus = $this->collection->db->command(array( + 'serverStatus' => 1, + 'locks' => 0, + 'metrics' => 0, + 'recordStats' => 0, + 'repl' => 0, + )); + + $collStats = $this->collection->db->command(array('collStats' => 1)); + + return array( + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (int) $serverStatus['uptime'] : null), + Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (int) $collStats['size'] : null), + Cache::STATS_MEMORY_AVAILABLE => null, + ); + } + + /** + * Check if the document is expired. + * + * @param array $document + * + * @return bool + */ + private function isExpired(array $document) + { + return isset($document[self::EXPIRATION_FIELD]) && + $document[self::EXPIRATION_FIELD] instanceof MongoDate && + $document[self::EXPIRATION_FIELD]->sec < time(); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php new file mode 100644 index 0000000..df7146d --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Interface for cache drivers that allows to get many items at once. + * + * @link www.doctrine-project.org + * @since 1.4 + * @author Asmir Mustafic + */ +interface MultiGetCache +{ + /** + * Returns an associative array of values for keys is found in cache. + * + * @param string[] $keys Array of keys to retrieve from cache + * @return mixed[] Array of retrieved values, indexed by the specified keys. + * Values that couldn't be retrieved are not contained in this array. + */ + function fetchMultiple(array $keys); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php new file mode 100644 index 0000000..5e75196 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Php file cache driver. + * + * @since 2.3 + * @author Fabio B. Silva + */ +class PhpFileCache extends FileCache +{ + const EXTENSION = '.doctrinecache.php'; + + /** + * {@inheritdoc} + */ + public function __construct($directory, $extension = self::EXTENSION, $umask = 0002) + { + parent::__construct($directory, $extension, $umask); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $value = $this->includeFileForId($id); + + if (! $value) { + return false; + } + + if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { + return false; + } + + return $value['data']; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $value = $this->includeFileForId($id); + + if (! $value) { + return false; + } + + return $value['lifetime'] === 0 || $value['lifetime'] > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + if (is_object($data) && ! method_exists($data, '__set_state')) { + throw new \InvalidArgumentException( + "Invalid argument given, PhpFileCache only allows objects that implement __set_state() " . + "and fully support var_export(). You can use the FilesystemCache to save arbitrary object " . + "graphs using serialize()/deserialize()." + ); + } + + $filename = $this->getFilename($id); + + $value = array( + 'lifetime' => $lifeTime, + 'data' => $data + ); + + $value = var_export($value, true); + $code = sprintf('writeFile($filename, $code); + } + + /** + * @param string $id + * + * @return array|false + */ + private function includeFileForId($id) + { + $fileName = $this->getFilename($id); + + // note: error suppression is still faster than `file_exists`, `is_file` and `is_readable` + $value = @include $fileName; + + if (! isset($value['lifetime'])) { + return false; + } + + return $value; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php new file mode 100644 index 0000000..e7da2a6 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php @@ -0,0 +1,108 @@ + + */ +class PredisCache extends CacheProvider +{ + /** + * @var ClientInterface + */ + private $client; + + /** + * @param ClientInterface $client + * + * @return void + */ + public function __construct(ClientInterface $client) + { + $this->client = $client; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $result = $this->client->get($id); + if (null === $result) { + return false; + } + + return unserialize($result); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + $fetchedItems = call_user_func_array(array($this->client, 'mget'), $keys); + + return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems))); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->client->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $data = serialize($data); + if ($lifeTime > 0) { + $response = $this->client->setex($id, $lifeTime, $data); + } else { + $response = $this->client->set($id, $data); + } + + return $response === true || $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->client->del($id) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $response = $this->client->flushdb(); + + return $response === true || $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->client->info(); + + return array( + Cache::STATS_HITS => $info['Stats']['keyspace_hits'], + Cache::STATS_MISSES => $info['Stats']['keyspace_misses'], + Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'], + Cache::STATS_MEMORY_AVAILABLE => false + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php new file mode 100644 index 0000000..9b182c9 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php @@ -0,0 +1,153 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use Redis; + +/** + * Redis cache provider. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Osman Ungur + */ +class RedisCache extends CacheProvider +{ + /** + * @var Redis|null + */ + private $redis; + + /** + * Sets the redis instance to use. + * + * @param Redis $redis + * + * @return void + */ + public function setRedis(Redis $redis) + { + $redis->setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue()); + $this->redis = $redis; + } + + /** + * Gets the redis instance used by the cache. + * + * @return Redis|null + */ + public function getRedis() + { + return $this->redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->redis->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + $fetchedItems = array_combine($keys, $this->redis->mget($keys)); + + // Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data. + $foundItems = array(); + + foreach ($fetchedItems as $key => $value) { + if (false !== $value || $this->redis->exists($key)) { + $foundItems[$key] = $value; + } + } + + return $foundItems; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + return $this->redis->setex($id, $lifeTime, $data); + } + + return $this->redis->set($id, $data); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->redis->delete($id) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->redis->flushDB(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->redis->info(); + return array( + Cache::STATS_HITS => $info['keyspace_hits'], + Cache::STATS_MISSES => $info['keyspace_misses'], + Cache::STATS_UPTIME => $info['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['used_memory'], + Cache::STATS_MEMORY_AVAILABLE => false + ); + } + + /** + * Returns the serializer constant to use. If Redis is compiled with + * igbinary support, that is used. Otherwise the default PHP serializer is + * used. + * + * @return integer One of the Redis::SERIALIZER_* constants + */ + protected function getSerializerValue() + { + if (defined('HHVM_VERSION')) { + return Redis::SERIALIZER_PHP; + } + return defined('Redis::SERIALIZER_IGBINARY') ? Redis::SERIALIZER_IGBINARY : Redis::SERIALIZER_PHP; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php new file mode 100644 index 0000000..0baa3f2 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php @@ -0,0 +1,250 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use Riak\Bucket; +use Riak\Connection; +use Riak\Input; +use Riak\Exception; +use Riak\Object; + +/** + * Riak cache provider. + * + * @link www.doctrine-project.org + * @since 1.1 + * @author Guilherme Blanco + */ +class RiakCache extends CacheProvider +{ + const EXPIRES_HEADER = 'X-Riak-Meta-Expires'; + + /** + * @var \Riak\Bucket + */ + private $bucket; + + /** + * Sets the riak bucket instance to use. + * + * @param \Riak\Bucket $bucket + */ + public function __construct(Bucket $bucket) + { + $this->bucket = $bucket; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + try { + $response = $this->bucket->get($id); + + // No objects found + if ( ! $response->hasObject()) { + return false; + } + + // Check for attempted siblings + $object = ($response->hasSiblings()) + ? $this->resolveConflict($id, $response->getVClock(), $response->getObjectList()) + : $response->getFirstObject(); + + // Check for expired object + if ($this->isExpired($object)) { + $this->bucket->delete($object); + + return false; + } + + return unserialize($object->getContent()); + } catch (Exception\RiakException $e) { + // Covers: + // - Riak\ConnectionException + // - Riak\CommunicationException + // - Riak\UnexpectedResponseException + // - Riak\NotFoundException + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + try { + // We only need the HEAD, not the entire object + $input = new Input\GetInput(); + + $input->setReturnHead(true); + + $response = $this->bucket->get($id, $input); + + // No objects found + if ( ! $response->hasObject()) { + return false; + } + + $object = $response->getFirstObject(); + + // Check for expired object + if ($this->isExpired($object)) { + $this->bucket->delete($object); + + return false; + } + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $object = new Object($id); + + $object->setContent(serialize($data)); + + if ($lifeTime > 0) { + $object->addMetadata(self::EXPIRES_HEADER, (string) (time() + $lifeTime)); + } + + $this->bucket->put($object); + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + try { + $this->bucket->delete($id); + + return true; + } catch (Exception\BadArgumentsException $e) { + // Key did not exist on cluster already + } catch (Exception\RiakException $e) { + // Covers: + // - Riak\Exception\ConnectionException + // - Riak\Exception\CommunicationException + // - Riak\Exception\UnexpectedResponseException + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + try { + $keyList = $this->bucket->getKeyList(); + + foreach ($keyList as $key) { + $this->bucket->delete($key); + } + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + // Only exposed through HTTP stats API, not Protocol Buffers API + return null; + } + + /** + * Check if a given Riak Object have expired. + * + * @param \Riak\Object $object + * + * @return bool + */ + private function isExpired(Object $object) + { + $metadataMap = $object->getMetadataMap(); + + return isset($metadataMap[self::EXPIRES_HEADER]) + && $metadataMap[self::EXPIRES_HEADER] < time(); + } + + /** + * On-read conflict resolution. Applied approach here is last write wins. + * Specific needs may override this method to apply alternate conflict resolutions. + * + * {@internal Riak does not attempt to resolve a write conflict, and store + * it as sibling of conflicted one. By following this approach, it is up to + * the next read to resolve the conflict. When this happens, your fetched + * object will have a list of siblings (read as a list of objects). + * In our specific case, we do not care about the intermediate ones since + * they are all the same read from storage, and we do apply a last sibling + * (last write) wins logic. + * If by any means our resolution generates another conflict, it'll up to + * next read to properly solve it.} + * + * @param string $id + * @param string $vClock + * @param array $objectList + * + * @return \Riak\Object + */ + protected function resolveConflict($id, $vClock, array $objectList) + { + // Our approach here is last-write wins + $winner = $objectList[count($objectList)]; + + $putInput = new Input\PutInput(); + $putInput->setVClock($vClock); + + $mergedObject = new Object($id); + $mergedObject->setContent($winner->getContent()); + + $this->bucket->put($mergedObject, $putInput); + + return $mergedObject; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php new file mode 100644 index 0000000..0bf6e4d --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php @@ -0,0 +1,220 @@ +. + */ + +namespace Doctrine\Common\Cache; + +use SQLite3; +use SQLite3Result; + +/** + * SQLite3 cache provider. + * + * @since 1.4 + * @author Jake Bell + */ +class SQLite3Cache extends CacheProvider +{ + /** + * The ID field will store the cache key. + */ + const ID_FIELD = 'k'; + + /** + * The data field will store the serialized PHP value. + */ + const DATA_FIELD = 'd'; + + /** + * The expiration field will store a date value indicating when the + * cache entry should expire. + */ + const EXPIRATION_FIELD = 'e'; + + /** + * @var SQLite3 + */ + private $sqlite; + + /** + * @var string + */ + private $table; + + /** + * Constructor. + * + * Calling the constructor will ensure that the database file and table + * exist and will create both if they don't. + * + * @param SQLite3 $sqlite + * @param string $table + */ + public function __construct(SQLite3 $sqlite, $table) + { + $this->sqlite = $sqlite; + $this->table = (string) $table; + + list($id, $data, $exp) = $this->getFields(); + + return $this->sqlite->exec(sprintf( + 'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)', + $table, + $id, + $data, + $exp + )); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + if ($item = $this->findById($id)) { + return unserialize($item[self::DATA_FIELD]); + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return null !== $this->findById($id, false); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $statement = $this->sqlite->prepare(sprintf( + 'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)', + $this->table, + implode(',', $this->getFields()) + )); + + $statement->bindValue(':id', $id); + $statement->bindValue(':data', serialize($data), SQLITE3_BLOB); + $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null); + + return $statement->execute() instanceof SQLite3Result; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + list($idField) = $this->getFields(); + + $statement = $this->sqlite->prepare(sprintf( + 'DELETE FROM %s WHERE %s = :id', + $this->table, + $idField + )); + + $statement->bindValue(':id', $id); + + return $statement->execute() instanceof SQLite3Result; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table)); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + // no-op. + } + + /** + * Find a single row by ID. + * + * @param mixed $id + * @param bool $includeData + * + * @return array|null + */ + private function findById($id, $includeData = true) + { + list($idField) = $fields = $this->getFields(); + + if (!$includeData) { + $key = array_search(static::DATA_FIELD, $fields); + unset($fields[$key]); + } + + $statement = $this->sqlite->prepare(sprintf( + 'SELECT %s FROM %s WHERE %s = :id LIMIT 1', + implode(',', $fields), + $this->table, + $idField + )); + + $statement->bindValue(':id', $id, SQLITE3_TEXT); + + $item = $statement->execute()->fetchArray(SQLITE3_ASSOC); + + if ($item === false) { + return null; + } + + if ($this->isExpired($item)) { + $this->doDelete($id); + + return null; + } + + return $item; + } + + /** + * Gets an array of the fields in our table. + * + * @return array + */ + private function getFields() + { + return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD); + } + + /** + * Check if the item is expired. + * + * @param array $item + * + * @return bool + */ + private function isExpired(array $item) + { + return isset($item[static::EXPIRATION_FIELD]) && + $item[self::EXPIRATION_FIELD] !== null && + $item[self::EXPIRATION_FIELD] < time(); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php new file mode 100644 index 0000000..ac45238 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php @@ -0,0 +1,25 @@ +. + */ + +namespace Doctrine\Common\Cache; + +class Version +{ + const VERSION = '1.5.4'; +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php new file mode 100644 index 0000000..65e8456 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Void cache driver. The cache could be of use in tests where you don`t need to cache anything. + * + * @link www.doctrine-project.org + * @since 1.5 + * @author Kotlyar Maksim + */ +class VoidCache extends CacheProvider +{ + /** + * {@inheritDoc} + */ + protected function doFetch($id) + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function doContains($id) + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return true; + } + + /** + * {@inheritDoc} + */ + protected function doDelete($id) + { + return true; + } + + /** + * {@inheritDoc} + */ + protected function doFlush() + { + return true; + } + + /** + * {@inheritDoc} + */ + protected function doGetStats() + { + return; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php new file mode 100644 index 0000000..6f563aa --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * WinCache cache provider. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class WinCacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return wincache_ucache_get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return wincache_ucache_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return wincache_ucache_set($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return wincache_ucache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return wincache_ucache_clear(); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return wincache_ucache_get($keys); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = wincache_ucache_info(); + $meminfo = wincache_ucache_meminfo(); + + return array( + Cache::STATS_HITS => $info['total_hit_count'], + Cache::STATS_MISSES => $info['total_miss_count'], + Cache::STATS_UPTIME => $info['total_cache_uptime'], + Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], + Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php new file mode 100644 index 0000000..a2c4ca5 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Xcache cache driver. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author David Abdemoulaie + */ +class XcacheCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->doContains($id) ? unserialize(xcache_get($id)) : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return xcache_isset($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return xcache_set($id, serialize($data), (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return xcache_unset($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->checkAuthorization(); + + xcache_clear_cache(XC_TYPE_VAR); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off. + * + * @return void + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On. + */ + protected function checkAuthorization() + { + if (ini_get('xcache.admin.enable_auth')) { + throw new \BadMethodCallException( + 'To use all features of \Doctrine\Common\Cache\XcacheCache, ' + . 'you must set "xcache.admin.enable_auth" to "Off" in your php.ini.' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $this->checkAuthorization(); + + $info = xcache_info(XC_TYPE_VAR, 0); + return array( + Cache::STATS_HITS => $info['hits'], + Cache::STATS_MISSES => $info['misses'], + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $info['size'], + Cache::STATS_MEMORY_AVAILABLE => $info['avail'], + ); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php new file mode 100644 index 0000000..6e35ac8 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Cache; + +/** + * Zend Data Cache cache driver. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Ralph Schindler + * @author Guilherme Blanco + */ +class ZendDataCache extends CacheProvider +{ + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return zend_shm_cache_fetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (false !== zend_shm_cache_fetch($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return zend_shm_cache_store($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return zend_shm_cache_delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $namespace = $this->getNamespace(); + if (empty($namespace)) { + return zend_shm_cache_clear(); + } + return zend_shm_cache_clear($namespace); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} diff --git a/vendor/doctrine/cache/phpunit.xml.dist b/vendor/doctrine/cache/phpunit.xml.dist new file mode 100644 index 0000000..40cc24d --- /dev/null +++ b/vendor/doctrine/cache/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php new file mode 100644 index 0000000..29af44d --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ApcCacheTest.php @@ -0,0 +1,21 @@ +markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084'); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php new file mode 100644 index 0000000..cf00185 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ArrayCacheTest.php @@ -0,0 +1,31 @@ +_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + public function testLifetime() + { + $this->markTestSkipped('ArrayCache does not implement TTL currently.'); + } + + protected function isSharedStorage() + { + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/BaseFileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/BaseFileCacheTest.php new file mode 100644 index 0000000..689452f --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/BaseFileCacheTest.php @@ -0,0 +1,145 @@ +directory = sys_get_temp_dir() . '/doctrine_cache_'. uniqid(); + } while (file_exists($this->directory)); + } + + protected function tearDown() + { + if ( ! is_dir($this->directory)) { + return; + } + + $iterator = new RecursiveDirectoryIterator($this->directory); + + foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST) as $file) { + if ($file->isFile()) { + @unlink($file->getRealPath()); + } elseif ($file->isDir()) { + @rmdir($file->getRealPath()); + } + } + + @rmdir($this->directory); + } + + public function testFlushAllRemovesBalancingDirectories() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->flushAll()); + + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST); + + $this->assertCount(0, $iterator); + } + + protected function isSharedStorage() + { + return false; + } + + public function getPathLengthsToTest() + { + // Windows officially supports 260 bytes including null terminator + // 258 bytes available to use due to php bug #70943 + // Windows officially supports 260 bytes including null terminator + // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943) + // 260 characters is too large - null terminator is included in allowable length + return array( + array(257, false), + array(258, false), + array(259, true), + array(260, true) + ); + } + + private static function getBasePathForWindowsPathLengthTests($pathLength) + { + return FileCacheTest::getBasePathForWindowsPathLengthTests($pathLength); + } + + private static function getKeyAndPathFittingLength($length) + { + $basePath = self::getBasePathForWindowsPathLengthTests($length); + + $baseDirLength = strlen($basePath); + $extensionLength = strlen('.doctrine.cache'); + $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR); + $namespaceAndBracketLength = strlen(bin2hex("[][1]")); + $keyLength = $length + - ($baseDirLength + + $extensionLength + + $directoryLength + + $namespaceAndBracketLength); + + $key = str_repeat('a', floor($keyLength / 2)); + $namespacedKey = '[' . $key . '][1]'; + + $keyHash = hash('sha256', $namespacedKey); + + $keyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . bin2hex($namespacedKey) + . '.doctrine.cache'; + + $hashedKeyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . '_' . $keyHash + . '.doctrine.cache'; + + return array($key, $keyPath, $hashedKeyPath); + } + + /** + * @dataProvider getPathLengthsToTest + */ + public function testWindowsPathLengthLimitIsCorrectlyHandled($length, $pathShouldBeHashed) + { + $this->directory = self::getBasePathForWindowsPathLengthTests($length); + + list($key, $keyPath, $hashedKeyPath) = self::getKeyAndPathFittingLength($length); + + $this->assertEquals($length, strlen($keyPath), "Unhashed path should be of correct length."); + + $cacheClass = get_class($this->_getCacheDriver()); + $cache = new $cacheClass($this->directory, '.doctrine.cache'); + + // Trick it into thinking this is windows. + $reflClass = new \ReflectionClass('\Doctrine\Common\Cache\FileCache'); + $reflProp = $reflClass->getProperty('isRunningOnWindows'); + $reflProp->setAccessible(true); + $reflProp->setValue($cache, true); + $reflProp->setAccessible(false); + + $cache->save($key, $length); + $fetched = $cache->fetch($key); + $this->assertEquals($length, $fetched); + + if ($pathShouldBeHashed) { + $this->assertFileExists($hashedKeyPath, "Path generated for key should be hashed."); + unlink($hashedKeyPath); + } else { + $this->assertFileExists($keyPath, "Path generated for key should not be hashed."); + unlink($keyPath); + } + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheProviderTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheProviderTest.php new file mode 100644 index 0000000..2b32592 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheProviderTest.php @@ -0,0 +1,72 @@ +getMockForAbstractClass( + 'Doctrine\Common\Cache\CacheProvider', + array(), + '', + true, + true, + true, + array('doFetchMultiple') + ); + + $cache + ->expects($this->once()) + ->method('doFetchMultiple') + ->will($this->returnValue(array( + '[foo][1]' => 'bar', + '[bar][1]' => 'baz', + '[baz][1]' => 'tab', + ))); + + $this->assertEquals( + array('foo' => 'bar', 'bar' => 'baz'), + $cache->fetchMultiple(array('foo', 'bar')) + ); + } + + public function testFailedDeleteAllDoesNotChangeNamespaceVersion() + { + /* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */ + $cache = $this->getMockForAbstractClass( + 'Doctrine\Common\Cache\CacheProvider', + array(), + '', + true, + true, + true, + array('doFetch', 'doSave', 'doContains') + ); + + $cache + ->expects($this->once()) + ->method('doFetch') + ->with('DoctrineNamespaceCacheKey[]') + ->will($this->returnValue(false)); + + // doSave is only called once from deleteAll as we do not need to persist the default version in getNamespaceVersion() + $cache + ->expects($this->once()) + ->method('doSave') + ->with('DoctrineNamespaceCacheKey[]') + ->will($this->returnValue(false)); + + // After a failed deleteAll() the local namespace version is not increased (still 1). Otherwise all data written afterwards + // would be lost outside the current instance. + $cache + ->expects($this->once()) + ->method('doContains') + ->with('[key][1]') + ->will($this->returnValue(true)); + + $this->assertFalse($cache->deleteAll(), 'deleteAll() returns false when saving the namespace version fails'); + $cache->contains('key'); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php new file mode 100644 index 0000000..dccd2a5 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CacheTest.php @@ -0,0 +1,447 @@ +_getCacheDriver(); + + // Test saving a value, checking if it exists, and fetching it back + $this->assertTrue($cache->save('key', $value)); + $this->assertTrue($cache->contains('key')); + if (is_object($value)) { + $this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference'); + } else { + $this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type'); + } + + // Test deleting a value + $this->assertTrue($cache->delete('key')); + $this->assertFalse($cache->contains('key')); + $this->assertFalse($cache->fetch('key')); + } + + /** + * @dataProvider provideDataToCache + */ + public function testUpdateExistingEntry($value) + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key', 'old-value')); + $this->assertTrue($cache->contains('key')); + + $this->assertTrue($cache->save('key', $value)); + $this->assertTrue($cache->contains('key')); + if (is_object($value)) { + $this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference'); + } else { + $this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type'); + } + } + + public function testCacheKeyIsCaseSensitive() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key', 'value')); + $this->assertTrue($cache->contains('key')); + $this->assertSame('value', $cache->fetch('key')); + + $this->assertFalse($cache->contains('KEY')); + $this->assertFalse($cache->fetch('KEY')); + + $cache->delete('KEY'); + $this->assertTrue($cache->contains('key', 'Deleting cache item with different case must not affect other cache item')); + } + + public function testFetchMultiple() + { + $cache = $this->_getCacheDriver(); + $values = $this->provideDataToCache(); + $saved = array(); + + foreach ($values as $key => $value) { + $cache->save($key, $value[0]); + + $saved[$key] = $value[0]; + } + + $keys = array_keys($saved); + + $this->assertEquals( + $saved, + $cache->fetchMultiple($keys), + 'Testing fetchMultiple with different data types' + ); + $this->assertEquals( + array_slice($saved, 0, 1), + $cache->fetchMultiple(array_slice($keys, 0, 1)), + 'Testing fetchMultiple with a single key' + ); + + $keysWithNonExisting = array(); + $keysWithNonExisting[] = 'non_existing1'; + $keysWithNonExisting[] = $keys[0]; + $keysWithNonExisting[] = 'non_existing2'; + $keysWithNonExisting[] = $keys[1]; + $keysWithNonExisting[] = 'non_existing3'; + + $this->assertEquals( + array_slice($saved, 0, 2), + $cache->fetchMultiple($keysWithNonExisting), + 'Testing fetchMultiple with a subset of keys and mixed with non-existing ones' + ); + } + + public function testFetchMultipleWithNoKeys() + { + $cache = $this->_getCacheDriver(); + + $this->assertSame(array(), $cache->fetchMultiple(array())); + } + + public function provideDataToCache() + { + $obj = new \stdClass(); + $obj->foo = 'bar'; + $obj2 = new \stdClass(); + $obj2->bar = 'foo'; + $obj2->obj = $obj; + $obj->obj2 = $obj2; + + return array( + 'array' => array(array('one', 2, 3.01)), + 'string' => array('value'), + 'string_invalid_utf8' => array("\xc3\x28"), + 'string_null_byte' => array('with'."\0".'null char'), + 'integer' => array(1), + 'float' => array(1.5), + 'object' => array(new ArrayObject(array('one', 2, 3.01))), + 'object_recursive' => array($obj), + 'true' => array(true), + // the following are considered FALSE in boolean context, but caches should still recognize their existence + 'null' => array(null), + 'false' => array(false), + 'array_empty' => array(array()), + 'string_zero' => array('0'), + 'integer_zero' => array(0), + 'float_zero' => array(0.0), + 'string_empty' => array(''), + ); + } + + public function testDeleteIsSuccessfulWhenKeyDoesNotExist() + { + $cache = $this->_getCacheDriver(); + + $cache->delete('key'); + $this->assertFalse($cache->contains('key')); + $this->assertTrue($cache->delete('key')); + } + + public function testDeleteAll() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->deleteAll()); + $this->assertFalse($cache->contains('key1')); + $this->assertFalse($cache->contains('key2')); + } + + /** + * @dataProvider provideCacheIds + */ + public function testCanHandleSpecialCacheIds($id) + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save($id, 'value')); + $this->assertTrue($cache->contains($id)); + $this->assertEquals('value', $cache->fetch($id)); + + $this->assertTrue($cache->delete($id)); + $this->assertFalse($cache->contains($id)); + $this->assertFalse($cache->fetch($id)); + } + + public function testNoCacheIdCollisions() + { + $cache = $this->_getCacheDriver(); + + $ids = $this->provideCacheIds(); + + // fill cache with each id having a different value + foreach ($ids as $index => $id) { + $cache->save($id[0], $index); + } + + // then check value of each cache id + foreach ($ids as $index => $id) { + $value = $cache->fetch($id[0]); + $this->assertNotFalse($value, sprintf('Failed to retrieve data for cache id "%s".', $id[0])); + if ($index !== $value) { + $this->fail(sprintf('Cache id "%s" collides with id "%s".', $id[0], $ids[$value][0])); + } + } + } + + /** + * Returns cache ids with special characters that should still work. + * + * For example, the characters :\/<>"*?| are not valid in Windows filenames. So they must be encoded properly. + * Each cache id should be considered different from the others. + * + * @return array + */ + public function provideCacheIds() + { + return array( + array(':'), + array('\\'), + array('/'), + array('<'), + array('>'), + array('"'), + array('*'), + array('?'), + array('|'), + array('['), + array(']'), + array('ä'), + array('a'), + array('é'), + array('e'), + array('.'), // directory traversal + array('..'), // directory traversal + array('-'), + array('_'), + array('$'), + array('%'), + array(' '), + array("\0"), + array(''), + array(str_repeat('a', 300)), // long key + array(str_repeat('a', 113)), + ); + } + + public function testLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('expire', 'value', 1); + $this->assertTrue($cache->contains('expire'), 'Data should not be expired yet'); + // @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead + sleep(2); + $this->assertFalse($cache->contains('expire'), 'Data should be expired'); + } + + public function testNoExpire() + { + $cache = $this->_getCacheDriver(); + $cache->save('noexpire', 'value', 0); + // @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead + sleep(1); + $this->assertTrue($cache->contains('noexpire'), 'Data with lifetime of zero should not expire'); + } + + public function testLongLifetime() + { + $cache = $this->_getCacheDriver(); + $cache->save('longlifetime', 'value', 30 * 24 * 3600 + 1); + $this->assertTrue($cache->contains('longlifetime'), 'Data with lifetime > 30 days should be accepted'); + } + + public function testDeleteAllAndNamespaceVersioningBetweenCaches() + { + if ( ! $this->isSharedStorage()) { + $this->markTestSkipped('The cache storage needs to be shared.'); + } + + $cache1 = $this->_getCacheDriver(); + $cache2 = $this->_getCacheDriver(); + + $this->assertTrue($cache1->save('key1', 1)); + $this->assertTrue($cache2->save('key2', 2)); + + /* Both providers are initialized with the same namespace version, so + * they can see entries set by each other. + */ + $this->assertTrue($cache1->contains('key1')); + $this->assertTrue($cache1->contains('key2')); + $this->assertTrue($cache2->contains('key1')); + $this->assertTrue($cache2->contains('key2')); + + /* Deleting all entries through one provider will only increment the + * namespace version on that object (and in the cache itself, which new + * instances will use to initialize). The second provider will retain + * its original version and still see stale data. + */ + $this->assertTrue($cache1->deleteAll()); + $this->assertFalse($cache1->contains('key1')); + $this->assertFalse($cache1->contains('key2')); + $this->assertTrue($cache2->contains('key1')); + $this->assertTrue($cache2->contains('key2')); + + /* A new cache provider should not see the deleted entries, since its + * namespace version will be initialized. + */ + $cache3 = $this->_getCacheDriver(); + $this->assertFalse($cache3->contains('key1')); + $this->assertFalse($cache3->contains('key2')); + } + + public function testFlushAll() + { + $cache = $this->_getCacheDriver(); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->flushAll()); + $this->assertFalse($cache->contains('key1')); + $this->assertFalse($cache->contains('key2')); + } + + public function testFlushAllAndNamespaceVersioningBetweenCaches() + { + if ( ! $this->isSharedStorage()) { + $this->markTestSkipped('The cache storage needs to be shared.'); + } + + $cache1 = $this->_getCacheDriver(); + $cache2 = $this->_getCacheDriver(); + + /* Deleting all elements from the first provider should increment its + * namespace version before saving the first entry. + */ + $cache1->deleteAll(); + $this->assertTrue($cache1->save('key1', 1)); + + /* The second provider will be initialized with the same namespace + * version upon its first save operation. + */ + $this->assertTrue($cache2->save('key2', 2)); + + /* Both providers have the same namespace version and can see entries + * set by each other. + */ + $this->assertTrue($cache1->contains('key1')); + $this->assertTrue($cache1->contains('key2')); + $this->assertTrue($cache2->contains('key1')); + $this->assertTrue($cache2->contains('key2')); + + /* Flushing all entries through one cache will remove all entries from + * the cache but leave their namespace version as-is. + */ + $this->assertTrue($cache1->flushAll()); + $this->assertFalse($cache1->contains('key1')); + $this->assertFalse($cache1->contains('key2')); + $this->assertFalse($cache2->contains('key1')); + $this->assertFalse($cache2->contains('key2')); + + /* Inserting a new entry will use the same, incremented namespace + * version, and it will be visible to both providers. + */ + $this->assertTrue($cache1->save('key1', 1)); + $this->assertTrue($cache1->contains('key1')); + $this->assertTrue($cache2->contains('key1')); + + /* A new cache provider will be initialized with the original namespace + * version and not share any visibility with the first two providers. + */ + $cache3 = $this->_getCacheDriver(); + $this->assertFalse($cache3->contains('key1')); + $this->assertFalse($cache3->contains('key2')); + $this->assertTrue($cache3->save('key3', 3)); + $this->assertTrue($cache3->contains('key3')); + } + + public function testNamespace() + { + $cache = $this->_getCacheDriver(); + + $cache->setNamespace('ns1_'); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('ns2_'); + + $this->assertFalse($cache->contains('key1')); + } + + public function testDeleteAllNamespace() + { + $cache = $this->_getCacheDriver(); + + $cache->setNamespace('ns1'); + $this->assertFalse($cache->contains('key1')); + $cache->save('key1', 'test'); + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('ns2'); + $this->assertFalse($cache->contains('key1')); + $cache->save('key1', 'test'); + $this->assertTrue($cache->contains('key1')); + + $cache->setNamespace('ns1'); + $this->assertTrue($cache->contains('key1')); + $cache->deleteAll(); + $this->assertFalse($cache->contains('key1')); + + $cache->setNamespace('ns2'); + $this->assertTrue($cache->contains('key1')); + $cache->deleteAll(); + $this->assertFalse($cache->contains('key1')); + } + + /** + * @group DCOM-43 + */ + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertArrayHasKey(Cache::STATS_HITS, $stats); + $this->assertArrayHasKey(Cache::STATS_MISSES, $stats); + $this->assertArrayHasKey(Cache::STATS_UPTIME, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats); + $this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILABLE, $stats); + } + + public function testSaveReturnsTrueWithAndWithoutTTlSet() + { + $cache = $this->_getCacheDriver(); + $cache->deleteAll(); + $this->assertTrue($cache->save('without_ttl', 'without_ttl')); + $this->assertTrue($cache->save('with_ttl', 'with_ttl', 3600)); + } + + /** + * Return whether multiple cache providers share the same storage. + * + * This is used for skipping certain tests for shared storage behavior. + * + * @return bool + */ + protected function isSharedStorage() + { + return true; + } + + /** + * @return \Doctrine\Common\Cache\CacheProvider + */ + abstract protected function _getCacheDriver(); +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ChainCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ChainCacheTest.php new file mode 100644 index 0000000..a3c013b --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/ChainCacheTest.php @@ -0,0 +1,99 @@ +markTestSkipped('The ChainCache test uses ArrayCache which does not implement TTL currently.'); + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertInternalType('array', $stats); + } + + public function testOnlyFetchFirstOne() + { + $cache1 = new ArrayCache(); + $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $cache2->expects($this->never())->method('doFetch'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->save('id', 'bar'); + + $this->assertEquals('bar', $chainCache->fetch('id')); + } + + public function testFetchPropagateToFastestCache() + { + $cache1 = new ArrayCache(); + $cache2 = new ArrayCache(); + + $cache2->save('bar', 'value'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + + $this->assertFalse($cache1->contains('bar')); + + $result = $chainCache->fetch('bar'); + + $this->assertEquals('value', $result); + $this->assertTrue($cache2->contains('bar')); + } + + public function testNamespaceIsPropagatedToAllProviders() + { + $cache1 = new ArrayCache(); + $cache2 = new ArrayCache(); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->setNamespace('bar'); + + $this->assertEquals('bar', $cache1->getNamespace()); + $this->assertEquals('bar', $cache2->getNamespace()); + } + + public function testDeleteToAllProviders() + { + $cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $cache1->expects($this->once())->method('doDelete'); + $cache2->expects($this->once())->method('doDelete'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->delete('bar'); + } + + public function testFlushToAllProviders() + { + $cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $cache1->expects($this->once())->method('doFlush'); + $cache2->expects($this->once())->method('doFlush'); + + $chainCache = new ChainCache(array($cache1, $cache2)); + $chainCache->flushAll(); + } + + protected function isSharedStorage() + { + return false; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php new file mode 100644 index 0000000..f42b3a7 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/CouchbaseCacheTest.php @@ -0,0 +1,30 @@ +couchbase = new Couchbase('127.0.0.1', 'Administrator', 'password', 'default'); + } catch(Exception $ex) { + $this->markTestSkipped('Could not instantiate the Couchbase cache because of: ' . $ex); + } + } + + protected function _getCacheDriver() + { + $driver = new CouchbaseCache(); + $driver->setCouchbase($this->couchbase); + return $driver; + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FileCacheTest.php new file mode 100644 index 0000000..c93b509 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FileCacheTest.php @@ -0,0 +1,256 @@ +driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(), '', false + ); + } + + public function testFilenameShouldCreateThePathWithOneSubDirectory() + { + $cache = $this->driver; + $method = new \ReflectionMethod($cache, 'getFilename'); + $key = 'item-key'; + $expectedDir = array( + '84', + ); + $expectedDir = implode(DIRECTORY_SEPARATOR, $expectedDir); + + $method->setAccessible(true); + + $path = $method->invoke($cache, $key); + $dirname = pathinfo($path, PATHINFO_DIRNAME); + + $this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir, $dirname); + } + + public function testFileExtensionCorrectlyEscaped() + { + $driver1 = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(__DIR__, '.*') + ); + $driver2 = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(__DIR__, '.php') + ); + + $doGetStats = new \ReflectionMethod($driver1, 'doGetStats'); + + $doGetStats->setAccessible(true); + + $stats1 = $doGetStats->invoke($driver1); + $stats2 = $doGetStats->invoke($driver2); + + $this->assertSame(0, $stats1[Cache::STATS_MEMORY_USAGE]); + $this->assertGreaterThan(0, $stats2[Cache::STATS_MEMORY_USAGE]); + } + + /** + * @group DCOM-266 + */ + public function testFileExtensionSlashCorrectlyEscaped() + { + $driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array(__DIR__ . '/../', DIRECTORY_SEPARATOR . basename(__FILE__)) + ); + + $doGetStats = new \ReflectionMethod($driver, 'doGetStats'); + + $doGetStats->setAccessible(true); + + $stats = $doGetStats->invoke($driver); + + $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_USAGE]); + } + + public function testNonIntUmaskThrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + + $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array('', '', 'invalid') + ); + } + + public function testGetDirectoryReturnsRealpathDirectoryString() + { + $directory = __DIR__ . '/../'; + $driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array($directory) + ); + + $doGetDirectory = new \ReflectionMethod($driver, 'getDirectory'); + + $actualDirectory = $doGetDirectory->invoke($driver); + $expectedDirectory = realpath($directory); + + $this->assertEquals($expectedDirectory, $actualDirectory); + } + + public function testGetExtensionReturnsExtensionString() + { + $directory = __DIR__ . '/../'; + $extension = DIRECTORY_SEPARATOR . basename(__FILE__); + $driver = $this->getMock( + 'Doctrine\Common\Cache\FileCache', + array('doFetch', 'doContains', 'doSave'), + array($directory, $extension) + ); + + $doGetExtension = new \ReflectionMethod($driver, 'getExtension'); + + $actualExtension = $doGetExtension->invoke($driver); + + $this->assertEquals($extension, $actualExtension); + } + + const WIN_MAX_PATH_LEN = 258; + + public static function getBasePathForWindowsPathLengthTests($pathLength) + { + // Not using __DIR__ because it can get screwed up when xdebug debugger is attached. + $basePath = realpath(sys_get_temp_dir()); + + // Test whether the desired path length is odd or even. + $desiredPathLengthIsOdd = ($pathLength % 2) == 1; + + // If the cache key is not too long, the filecache codepath will add + // a slash and bin2hex($key). The length of the added portion will be an odd number. + // len(desired) = len(base path) + len(slash . bin2hex($key)) + // odd = even + odd + // even = odd + odd + $basePathLengthShouldBeOdd = !$desiredPathLengthIsOdd; + + $basePathLengthIsOdd = (strlen($basePath) % 2) == 1; + + // If the base path needs to be odd or even where it is not, we add an odd number of + // characters as a pad. In this case, we're adding '\aa' (or '/aa' depending on platform) + // This is all to make it so that the key we're testing would result in + // a path that is exactly the length we want to test IF the path length limit + // were not in place in FileCache. + if ($basePathLengthIsOdd != $basePathLengthShouldBeOdd) { + $basePath .= DIRECTORY_SEPARATOR . "aa"; + } + + return $basePath; + } + + public static function getKeyAndPathFittingLength($length) + { + $basePath = self::getBasePathForWindowsPathLengthTests($length); + + $baseDirLength = strlen($basePath); + $extensionLength = strlen('.doctrine.cache'); + $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR); + $keyLength = $length - ($baseDirLength + $extensionLength + $directoryLength); // - 1 because of slash + + $key = str_repeat('a', floor($keyLength / 2)); + + $keyHash = hash('sha256', $key); + + $keyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . bin2hex($key) + . '.doctrine.cache'; + + $hashedKeyPath = $basePath + . DIRECTORY_SEPARATOR + . substr($keyHash, 0, 2) + . DIRECTORY_SEPARATOR + . '_' . $keyHash + . '.doctrine.cache'; + + return array($key, $keyPath, $hashedKeyPath); + } + + public function getPathLengthsToTest() + { + // Windows officially supports 260 bytes including null terminator + // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943) + // 260 characters is too large - null terminator is included in allowable length + return array( + array(257, false), + array(258, false), + array(259, true), + array(260, true) + ); + } + + /** + * @runInSeparateProcess + * @dataProvider getPathLengthsToTest + * + * @covers \Doctrine\Common\Cache\FileCache::getFilename + */ + public function testWindowsPathLengthLimitationsAreCorrectlyRespected($length, $pathShouldBeHashed) + { + if (! defined('PHP_WINDOWS_VERSION_BUILD')) { + define('PHP_WINDOWS_VERSION_BUILD', 'Yes, this is the "usual suspect", with the usual limitations'); + } + + $basePath = $this->getBasePathForWindowsPathLengthTests($length); + + $fileCache = $this->getMockForAbstractClass( + 'Doctrine\Common\Cache\FileCache', + array($basePath, '.doctrine.cache') + ); + + list($key, $keyPath, $hashedKeyPath) = $this->getKeyAndPathFittingLength($length); + + $getFileName = new \ReflectionMethod($fileCache, 'getFilename'); + + $getFileName->setAccessible(true); + + $this->assertEquals( + $length, + strlen($keyPath), + sprintf('Path expected to be %d characters long is %d characters long', $length, strlen($keyPath)) + ); + + if ($pathShouldBeHashed) { + $keyPath = $hashedKeyPath; + } + + if ($pathShouldBeHashed) { + $this->assertSame( + $hashedKeyPath, + $getFileName->invoke($fileCache, $key), + 'Keys should be hashed correctly if they are over the limit.' + ); + } else { + $this->assertSame( + $keyPath, + $getFileName->invoke($fileCache, $key), + 'Keys below limit of the allowed length are used directly, unhashed' + ); + } + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php new file mode 100644 index 0000000..9e7c9e8 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/FilesystemCacheTest.php @@ -0,0 +1,61 @@ +_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats[Cache::STATS_HITS]); + $this->assertNull($stats[Cache::STATS_MISSES]); + $this->assertNull($stats[Cache::STATS_UPTIME]); + $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); + $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]); + } + + public function testCacheInSharedDirectoryIsPerExtension() + { + $cache1 = new FilesystemCache($this->directory, '.foo'); + $cache2 = new FilesystemCache($this->directory, '.bar'); + + $this->assertTrue($cache1->save('key1', 11)); + $this->assertTrue($cache1->save('key2', 12)); + + $this->assertTrue($cache2->save('key1', 21)); + $this->assertTrue($cache2->save('key2', 22)); + + $this->assertSame(11, $cache1->fetch('key1'), 'Cache value must not be influenced by a different cache in the same directory but different extension'); + $this->assertSame(12, $cache1->fetch('key2')); + $this->assertTrue($cache1->flushAll()); + $this->assertFalse($cache1->fetch('key1'), 'flushAll() must delete all items with the current extension'); + $this->assertFalse($cache1->fetch('key2')); + + $this->assertSame(21, $cache2->fetch('key1'), 'flushAll() must not remove items with a different extension in a shared directory'); + $this->assertSame(22, $cache2->fetch('key2')); + } + + public function testFlushAllWithNoExtension() + { + $cache = new FilesystemCache($this->directory, ''); + + $this->assertTrue($cache->save('key1', 1)); + $this->assertTrue($cache->save('key2', 2)); + $this->assertTrue($cache->flushAll()); + $this->assertFalse($cache->contains('key1')); + $this->assertFalse($cache->contains('key2')); + } + + protected function _getCacheDriver() + { + return new FilesystemCache($this->directory); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php new file mode 100644 index 0000000..74853b8 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcacheCacheTest.php @@ -0,0 +1,59 @@ +memcache = new Memcache(); + + if (@$this->memcache->connect('localhost', 11211) === false) { + unset($this->memcache); + $this->markTestSkipped('Cannot connect to Memcache.'); + } + } + + protected function tearDown() + { + if ($this->memcache instanceof Memcache) { + $this->memcache->flush(); + } + } + + /** + * {@inheritdoc} + * + * Memcache does not support " " and null byte as key so we remove them from the tests. + */ + public function provideCacheIds() + { + $ids = parent::provideCacheIds(); + unset($ids[21], $ids[22]); + + return $ids; + } + + public function testGetMemcacheReturnsInstanceOfMemcache() + { + $this->assertInstanceOf('Memcache', $this->_getCacheDriver()->getMemcache()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + $driver = new MemcacheCache(); + $driver->setMemcache($this->memcache); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php new file mode 100644 index 0000000..fbcf5c3 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MemcachedCacheTest.php @@ -0,0 +1,61 @@ +memcached = new Memcached(); + $this->memcached->setOption(Memcached::OPT_COMPRESSION, false); + $this->memcached->addServer('127.0.0.1', 11211); + + if (@fsockopen('127.0.0.1', 11211) === false) { + unset($this->memcached); + $this->markTestSkipped('Cannot connect to Memcached.'); + } + } + + protected function tearDown() + { + if ($this->memcached instanceof Memcached) { + $this->memcached->flush(); + } + } + + /** + * {@inheritdoc} + * + * Memcached does not support " ", null byte and very long keys so we remove them from the tests. + */ + public function provideCacheIds() + { + $ids = parent::provideCacheIds(); + unset($ids[21], $ids[22], $ids[24]); + + return $ids; + } + + public function testGetMemcachedReturnsInstanceOfMemcached() + { + $this->assertInstanceOf('Memcached', $this->_getCacheDriver()->getMemcached()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + $driver = new MemcachedCache(); + $driver->setMemcached($this->memcached); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MongoDBCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MongoDBCacheTest.php new file mode 100644 index 0000000..02f6205 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/MongoDBCacheTest.php @@ -0,0 +1,68 @@ +=')) { + $this->markTestSkipped('Mongo >= 1.3.0 is required.'); + } + + $mongo = new MongoClient(); + $this->collection = $mongo->selectCollection('doctrine_common_cache', 'test'); + } + + protected function tearDown() + { + if ($this->collection instanceof MongoCollection) { + $this->collection->drop(); + } + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats[Cache::STATS_HITS]); + $this->assertNull($stats[Cache::STATS_MISSES]); + $this->assertGreaterThan(0, $stats[Cache::STATS_UPTIME]); + $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); + $this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]); + } + + /** + * @group 108 + */ + public function testMongoCursorExceptionsDoNotBubbleUp() + { + /* @var $collection \MongoCollection|\PHPUnit_Framework_MockObject_MockObject */ + $collection = $this->getMock('MongoCollection'); + + $collection->expects(self::once())->method('update')->willThrowException(new \MongoCursorException()); + + $cache = new MongoDBCache($collection); + + self::assertFalse($cache->save('foo', 'bar')); + } + + protected function _getCacheDriver() + { + return new MongoDBCache($this->collection); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php new file mode 100644 index 0000000..32eec6e --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PhpFileCacheTest.php @@ -0,0 +1,95 @@ +_getCacheDriver(); + + // Test save + $cache->save('test_set_state', new SetStateClass(array(1,2,3))); + + //Test __set_state call + $this->assertCount(0, SetStateClass::$values); + + // Test fetch + $value = $cache->fetch('test_set_state'); + $this->assertInstanceOf('Doctrine\Tests\Common\Cache\SetStateClass', $value); + $this->assertEquals(array(1,2,3), $value->getValue()); + + //Test __set_state call + $this->assertCount(1, SetStateClass::$values); + + // Test contains + $this->assertTrue($cache->contains('test_set_state')); + } + + public function testNotImplementsSetState() + { + $cache = $this->_getCacheDriver(); + + $this->setExpectedException('InvalidArgumentException'); + $cache->save('test_not_set_state', new NotSetStateClass(array(1,2,3))); + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats[Cache::STATS_HITS]); + $this->assertNull($stats[Cache::STATS_MISSES]); + $this->assertNull($stats[Cache::STATS_UPTIME]); + $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); + $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]); + } + + protected function _getCacheDriver() + { + return new PhpFileCache($this->directory); + } +} + +class NotSetStateClass +{ + private $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } +} + +class SetStateClass extends NotSetStateClass +{ + public static $values = array(); + + public static function __set_state($data) + { + self::$values = $data; + return new self($data['value']); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PredisCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PredisCacheTest.php new file mode 100644 index 0000000..e20839d --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/PredisCacheTest.php @@ -0,0 +1,87 @@ +markTestSkipped('Predis\Client is missing. Make sure to "composer install" to have all dev dependencies.'); + } + + $this->client = new Client(); + + try { + $this->client->connect(); + } catch (ConnectionException $e) { + $this->markTestSkipped('Cannot connect to Redis because of: ' . $e); + } + } + + public function testHitMissesStatsAreProvided() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNotNull($stats[Cache::STATS_HITS]); + $this->assertNotNull($stats[Cache::STATS_MISSES]); + } + + /** + * @return PredisCache + */ + protected function _getCacheDriver() + { + return new PredisCache($this->client); + } + + /** + * {@inheritDoc} + * + * @dataProvider provideDataToCache + */ + public function testSetContainsFetchDelete($value) + { + if (array() === $value) { + $this->markTestIncomplete( + 'Predis currently doesn\'t support saving empty array values. ' + . 'See https://github.com/nrk/predis/issues/241' + ); + } + + parent::testSetContainsFetchDelete($value); + } + + /** + * {@inheritDoc} + * + * @dataProvider provideDataToCache + */ + public function testUpdateExistingEntry($value) + { + if (array() === $value) { + $this->markTestIncomplete( + 'Predis currently doesn\'t support saving empty array values. ' + . 'See https://github.com/nrk/predis/issues/241' + ); + } + + parent::testUpdateExistingEntry($value); + } + + public function testAllowsGenericPredisClient() + { + /* @var $predisClient \Predis\ClientInterface */ + $predisClient = $this->getMock('Predis\\ClientInterface'); + + $this->assertInstanceOf('Doctrine\\Common\\Cache\\PredisCache', new PredisCache($predisClient)); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php new file mode 100644 index 0000000..caaaa06 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RedisCacheTest.php @@ -0,0 +1,47 @@ +_redis = new \Redis(); + $ok = @$this->_redis->connect('127.0.0.1'); + if (!$ok) { + $this->markTestSkipped('Cannot connect to Redis.'); + } + } + + public function testHitMissesStatsAreProvided() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNotNull($stats[Cache::STATS_HITS]); + $this->assertNotNull($stats[Cache::STATS_MISSES]); + } + + public function testGetRedisReturnsInstanceOfRedis() + { + $this->assertInstanceOf('Redis', $this->_getCacheDriver()->getRedis()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + $driver = new RedisCache(); + $driver->setRedis($this->_redis); + return $driver; + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RiakCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RiakCacheTest.php new file mode 100644 index 0000000..e7d5f3d --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/RiakCacheTest.php @@ -0,0 +1,58 @@ +connection = new Connection('127.0.0.1', 8087); + $this->bucket = new Bucket($this->connection, 'test'); + } catch (Exception\RiakException $e) { + $this->markTestSkipped('Cannot connect to Riak.'); + } + } + + /** + * {@inheritdoc} + */ + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + /** + * Retrieve RiakCache instance. + * + * @return \Doctrine\Common\Cache\RiakCache + */ + protected function _getCacheDriver() + { + return new RiakCache($this->bucket); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/SQLite3CacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/SQLite3CacheTest.php new file mode 100644 index 0000000..f0c5b0b --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/SQLite3CacheTest.php @@ -0,0 +1,42 @@ +file = tempnam(null, 'doctrine-cache-test-'); + unlink($this->file); + $this->sqlite = new SQLite3($this->file); + } + + protected function tearDown() + { + $this->sqlite = null; // DB must be closed before + unlink($this->file); + } + + public function testGetStats() + { + $this->assertNull($this->_getCacheDriver()->getStats()); + } + + /** + * {@inheritDoc} + */ + protected function _getCacheDriver() + { + return new SQLite3Cache($this->sqlite, 'test_table'); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/VoidCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/VoidCacheTest.php new file mode 100644 index 0000000..8ab7e5b --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/VoidCacheTest.php @@ -0,0 +1,58 @@ +assertFalse($cache->contains('foo')); + $this->assertFalse($cache->contains('bar')); + } + + public function testShouldAlwaysReturnFalseOnFetch() + { + $cache = new VoidCache(); + + $this->assertFalse($cache->fetch('foo')); + $this->assertFalse($cache->fetch('bar')); + } + + public function testShouldAlwaysReturnTrueOnSaveButNotStoreAnything() + { + $cache = new VoidCache(); + + $this->assertTrue($cache->save('foo', 'fooVal')); + + $this->assertFalse($cache->contains('foo')); + $this->assertFalse($cache->fetch('foo')); + } + + public function testShouldAlwaysReturnTrueOnDelete() + { + $cache = new VoidCache(); + + $this->assertTrue($cache->delete('foo')); + } + + public function testShouldAlwaysReturnNullOnGetStatus() + { + $cache = new VoidCache(); + + $this->assertNull($cache->getStats()); + } + + public function testShouldAlwaysReturnTrueOnFlush() + { + $cache = new VoidCache(); + + $this->assertTrue($cache->flushAll()); + } +} diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php new file mode 100644 index 0000000..1dded42 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/Common/Cache/WinCacheCacheTest.php @@ -0,0 +1,16 @@ +markTestSkipped('Zend Data Cache only works in apache2handler SAPI.'); + } + } + + public function testGetStats() + { + $cache = $this->_getCacheDriver(); + $stats = $cache->getStats(); + + $this->assertNull($stats); + } + + protected function _getCacheDriver() + { + return new ZendDataCache(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 0000000..e8323d2 --- /dev/null +++ b/vendor/doctrine/cache/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ + + + + + + + + + + + + + + ../Doctrine/ + + + + + + ../../lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/collections/.travis.yml b/vendor/doctrine/collections/.travis.yml new file mode 100644 index 0000000..60f4792 --- /dev/null +++ b/vendor/doctrine/collections/.travis.yml @@ -0,0 +1,21 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + - hhvm-nightly + +matrix: + fast_finish: true + allow_failures: + - php: 7.0 + +before_script: + - composer --prefer-source install + +script: + - phpunit diff --git a/vendor/doctrine/collections/LICENSE b/vendor/doctrine/collections/LICENSE new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/vendor/doctrine/collections/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/collections/README.md b/vendor/doctrine/collections/README.md new file mode 100644 index 0000000..161cab6 --- /dev/null +++ b/vendor/doctrine/collections/README.md @@ -0,0 +1,25 @@ +# Doctrine Collections + +[![Build Status](https://travis-ci.org/doctrine/collections.svg?branch=master)](https://travis-ci.org/doctrine/collections) + +Collections Abstraction library + +## Changelog + +### v1.3.0 + +* [Explicit casting of first and max results in criteria API](https://github.com/doctrine/collections/pull/26) +* [Keep keys when using `ArrayCollection#matching()` with sorting](https://github.com/doctrine/collections/pull/49) +* [Made `AbstractLazyCollection#$initialized` protected for extensibility](https://github.com/doctrine/collections/pull/52) + +### v1.2.0 + +* Add a new ``AbstractLazyCollection`` + +### v1.1.0 + +* Deprecated ``Comparison::IS``, because it's only there for SQL semantics. + These are fixed in the ORM instead. +* Add ``Comparison::CONTAINS`` to perform partial string matches: + + $criteria->andWhere($criteria->expr()->contains('property', 'Foo')); diff --git a/vendor/doctrine/collections/composer.json b/vendor/doctrine/collections/composer.json new file mode 100644 index 0000000..155cac4 --- /dev/null +++ b/vendor/doctrine/collections/composer.json @@ -0,0 +1,29 @@ +{ + "name": "doctrine/collections", + "type": "library", + "description": "Collections Abstraction library", + "keywords": ["collections", "array", "iterator"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php new file mode 100644 index 0000000..b907f8b --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php @@ -0,0 +1,343 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure; + +/** + * Lazy collection that is backed by a concrete collection + * + * @author Michaël Gallego + * @since 1.2 + */ +abstract class AbstractLazyCollection implements Collection +{ + /** + * The backed collection to use + * + * @var Collection + */ + protected $collection; + + /** + * @var boolean + */ + protected $initialized = false; + + /** + * {@inheritDoc} + */ + public function count() + { + $this->initialize(); + return $this->collection->count(); + } + + /** + * {@inheritDoc} + */ + public function add($element) + { + $this->initialize(); + return $this->collection->add($element); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->initialize(); + $this->collection->clear(); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + $this->initialize(); + return $this->collection->contains($element); + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + $this->initialize(); + return $this->collection->isEmpty(); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + $this->initialize(); + return $this->collection->remove($key); + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $this->initialize(); + return $this->collection->removeElement($element); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + $this->initialize(); + return $this->collection->containsKey($key); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + $this->initialize(); + return $this->collection->get($key); + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + $this->initialize(); + return $this->collection->getKeys(); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + $this->initialize(); + return $this->collection->getValues(); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->initialize(); + $this->collection->set($key, $value); + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + $this->initialize(); + return $this->collection->toArray(); + } + + /** + * {@inheritDoc} + */ + public function first() + { + $this->initialize(); + return $this->collection->first(); + } + + /** + * {@inheritDoc} + */ + public function last() + { + $this->initialize(); + return $this->collection->last(); + } + + /** + * {@inheritDoc} + */ + public function key() + { + $this->initialize(); + return $this->collection->key(); + } + + /** + * {@inheritDoc} + */ + public function current() + { + $this->initialize(); + return $this->collection->current(); + } + + /** + * {@inheritDoc} + */ + public function next() + { + $this->initialize(); + return $this->collection->next(); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + return $this->collection->exists($p); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + return $this->collection->filter($p); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + return $this->collection->forAll($p); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + $this->initialize(); + return $this->collection->map($func); + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + return $this->collection->partition($p); + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + $this->initialize(); + return $this->collection->indexOf($element); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + $this->initialize(); + return $this->collection->slice($offset, $length); + } + + /** + * {@inheritDoc} + */ + public function getIterator() + { + $this->initialize(); + return $this->collection->getIterator(); + } + + /** + * {@inheritDoc} + */ + public function offsetExists($offset) + { + $this->initialize(); + return $this->collection->offsetExists($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetGet($offset) + { + $this->initialize(); + return $this->collection->offsetGet($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + $this->initialize(); + $this->collection->offsetSet($offset, $value); + } + + /** + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + $this->initialize(); + $this->collection->offsetUnset($offset); + } + + /** + * Is the lazy collection already initialized? + * + * @return bool + */ + public function isInitialized() + { + return $this->initialized; + } + + /** + * Initialize the collection + * + * @return void + */ + protected function initialize() + { + if ( ! $this->initialized) { + $this->doInitialize(); + $this->initialized = true; + } + } + + /** + * Do the initialization logic + * + * @return void + */ + abstract protected function doInitialize(); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php new file mode 100644 index 0000000..bce5751 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,387 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use ArrayIterator; +use Closure; +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; + +/** + * An ArrayCollection is a Collection implementation that wraps a regular PHP array. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArrayCollection implements Collection, Selectable +{ + /** + * An array containing the entries of this collection. + * + * @var array + */ + private $elements; + + /** + * Initializes a new ArrayCollection. + * + * @param array $elements + */ + public function __construct(array $elements = array()) + { + $this->elements = $elements; + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + return $this->elements; + } + + /** + * {@inheritDoc} + */ + public function first() + { + return reset($this->elements); + } + + /** + * {@inheritDoc} + */ + public function last() + { + return end($this->elements); + } + + /** + * {@inheritDoc} + */ + public function key() + { + return key($this->elements); + } + + /** + * {@inheritDoc} + */ + public function next() + { + return next($this->elements); + } + + /** + * {@inheritDoc} + */ + public function current() + { + return current($this->elements); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + if ( ! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $key = array_search($element, $this->elements, true); + + if ($key === false) { + return false; + } + + unset($this->elements[$key]); + + return true; + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + $this->set($offset, $value); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + return in_array($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + return array_search($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + return isset($this->elements[$key]) ? $this->elements[$key] : null; + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + return array_keys($this->elements); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + return array_values($this->elements); + } + + /** + * {@inheritDoc} + */ + public function count() + { + return count($this->elements); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->elements[$key] = $value; + } + + /** + * {@inheritDoc} + */ + public function add($value) + { + $this->elements[] = $value; + + return true; + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + return empty($this->elements); + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + return new static(array_map($func, $this->elements)); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + return new static(array_filter($this->elements, $p)); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ( ! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $matches = $noMatches = array(); + + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + $matches[$key] = $element; + } else { + $noMatches[$key] = $element; + } + } + + return array(new static($matches), new static($noMatches)); + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->elements = array(); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + return array_slice($this->elements, $offset, $length, true); + } + + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + $filtered = $this->elements; + + if ($expr) { + $visitor = new ClosureExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $filtered = array_filter($filtered, $filter); + } + + if ($orderings = $criteria->getOrderings()) { + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1); + } + + uasort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return new static($filtered); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php new file mode 100644 index 0000000..8792f7a --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,263 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use ArrayAccess; +use Closure; +use Countable; +use IteratorAggregate; + +/** + * The missing (SPL) Collection/Array/OrderedMap interface. + * + * A Collection resembles the nature of a regular PHP array. That is, + * it is essentially an ordered map that can also be used + * like a list. + * + * A Collection has an internal iterator just like a PHP array. In addition, + * a Collection can be iterated with external iterators, which is preferable. + * To use an external iterator simply use the foreach language construct to + * iterate over the collection (which calls {@link getIterator()} internally) or + * explicitly retrieve an iterator though {@link getIterator()} which can then be + * used to iterate over the collection. + * You can not rely on the internal iterator of the collection being at a certain + * position unless you explicitly positioned it before. Prefer iteration with + * external iterators. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface Collection extends Countable, IteratorAggregate, ArrayAccess +{ + /** + * Adds an element at the end of the collection. + * + * @param mixed $element The element to add. + * + * @return boolean Always TRUE. + */ + public function add($element); + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + public function clear(); + + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param mixed $element The element to search for. + * + * @return boolean TRUE if the collection contains the element, FALSE otherwise. + */ + public function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty(); + + /** + * Removes the element at the specified index from the collection. + * + * @param string|integer $key The kex/index of the element to remove. + * + * @return mixed The removed element or NULL, if the collection did not contain the element. + */ + public function remove($key); + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * + * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. + */ + public function removeElement($element); + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|integer $key The key/index to check for. + * + * @return boolean TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + public function containsKey($key); + + /** + * Gets the element at the specified key/index. + * + * @param string|integer $key The key/index of the element to retrieve. + * + * @return mixed + */ + public function get($key); + + /** + * Gets all keys/indices of the collection. + * + * @return array The keys/indices of the collection, in the order of the corresponding + * elements in the collection. + */ + public function getKeys(); + + /** + * Gets all values of the collection. + * + * @return array The values of all elements in the collection, in the order they + * appear in the collection. + */ + public function getValues(); + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|integer $key The key/index of the element to set. + * @param mixed $value The element to set. + * + * @return void + */ + public function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + public function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + */ + public function first(); + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + */ + public function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string + */ + public function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + */ + public function current(); + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + */ + public function next(); + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + public function exists(Closure $p); + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + * + * @param Closure $p The predicate used for filtering. + * + * @return Collection A collection with the results of the filter operation. + */ + public function filter(Closure $p); + + /** + * Tests whether the given predicate p holds for all elements of this collection. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function forAll(Closure $p); + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @param Closure $func + * + * @return Collection + */ + public function map(Closure $func); + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * + * @return array An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + */ + public function partition(Closure $p); + + /** + * Gets the index/key of a given element. The comparison of two elements is strict, + * that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * + * @return int|string|bool The key/index of the element or FALSE if the element was not found. + */ + public function indexOf($element); + + /** + * Extracts a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset The offset to start from. + * @param int|null $length The maximum number of elements to return, or null for no limit. + * + * @return array + */ + public function slice($offset, $length = null); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php new file mode 100644 index 0000000..3f9d43a --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php @@ -0,0 +1,259 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Expression; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Criteria for filtering Selectable collections. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Criteria +{ + /** + * @var string + */ + const ASC = 'ASC'; + + /** + * @var string + */ + const DESC = 'DESC'; + + /** + * @var \Doctrine\Common\Collections\ExpressionBuilder|null + */ + private static $expressionBuilder; + + /** + * @var \Doctrine\Common\Collections\Expr\Expression|null + */ + private $expression; + + /** + * @var string[] + */ + private $orderings = array(); + + /** + * @var int|null + */ + private $firstResult; + + /** + * @var int|null + */ + private $maxResults; + + /** + * Creates an instance of the class. + * + * @return Criteria + */ + public static function create() + { + return new static(); + } + + /** + * Returns the expression builder. + * + * @return \Doctrine\Common\Collections\ExpressionBuilder + */ + public static function expr() + { + if (self::$expressionBuilder === null) { + self::$expressionBuilder = new ExpressionBuilder(); + } + + return self::$expressionBuilder; + } + + /** + * Construct a new Criteria. + * + * @param Expression $expression + * @param string[]|null $orderings + * @param int|null $firstResult + * @param int|null $maxResults + */ + public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) + { + $this->expression = $expression; + + $this->setFirstResult($firstResult); + $this->setMaxResults($maxResults); + + if (null !== $orderings) { + $this->orderBy($orderings); + } + } + + /** + * Sets the where expression to evaluate when this Criteria is searched for. + * + * @param Expression $expression + * + * @return Criteria + */ + public function where(Expression $expression) + { + $this->expression = $expression; + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an AND with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function andWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array( + $this->expression, $expression + )); + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an OR with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function orWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array( + $this->expression, $expression + )); + + return $this; + } + + /** + * Gets the expression attached to this Criteria. + * + * @return Expression|null + */ + public function getWhereExpression() + { + return $this->expression; + } + + /** + * Gets the current orderings of this Criteria. + * + * @return string[] + */ + public function getOrderings() + { + return $this->orderings; + } + + /** + * Sets the ordering of the result of this Criteria. + * + * Keys are field and values are the order, being either ASC or DESC. + * + * @see Criteria::ASC + * @see Criteria::DESC + * + * @param string[] $orderings + * + * @return Criteria + */ + public function orderBy(array $orderings) + { + $this->orderings = array_map( + function ($ordering) { + return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC; + }, + $orderings + ); + + return $this; + } + + /** + * Gets the current first result option of this Criteria. + * + * @return int|null + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Set the number of first result that this Criteria should return. + * + * @param int|null $firstResult The value to set. + * + * @return Criteria + */ + public function setFirstResult($firstResult) + { + $this->firstResult = null === $firstResult ? null : (int) $firstResult; + + return $this; + } + + /** + * Gets maxResults. + * + * @return int|null + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Sets maxResults. + * + * @param int|null $maxResults The value to set. + * + * @return Criteria + */ + public function setMaxResults($maxResults) + { + $this->maxResults = null === $maxResults ? null : (int) $maxResults; + + return $this; + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php new file mode 100644 index 0000000..994085f --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,227 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Walks an expression graph and turns it into a PHP closure. + * + * This closure can be used with {@Collection#filter()} and is used internally + * by {@ArrayCollection#select()}. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ClosureExpressionVisitor extends ExpressionVisitor +{ + /** + * Accesses the field of a given object. This field has to be public + * directly or indirectly (through an accessor get*, is*, or a magic + * method, __get, __call). + * + * @param object $object + * @param string $field + * + * @return mixed + */ + public static function getObjectFieldValue($object, $field) + { + if (is_array($object)) { + return $object[$field]; + } + + $accessors = array('get', 'is'); + + foreach ($accessors as $accessor) { + $accessor .= $field; + + if ( ! method_exists($object, $accessor)) { + continue; + } + + return $object->$accessor(); + } + + // __call should be triggered for get. + $accessor = $accessors[0] . $field; + + if (method_exists($object, '__call')) { + return $object->$accessor(); + } + + if ($object instanceof \ArrayAccess) { + return $object[$field]; + } + + return $object->$field; + } + + /** + * Helper for sorting arrays of objects based on multiple fields + orientations. + * + * @param string $name + * @param int $orientation + * @param \Closure $next + * + * @return \Closure + */ + public static function sortByField($name, $orientation = 1, \Closure $next = null) + { + if ( ! $next) { + $next = function() { + return 0; + }; + } + + return function ($a, $b) use ($name, $next, $orientation) { + $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); + $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); + + if ($aValue === $bValue) { + return $next($a, $b); + } + + return (($aValue > $bValue) ? 1 : -1) * $orientation; + }; + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + switch ($comparison->getOperator()) { + case Comparison::EQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; + }; + + case Comparison::NEQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; + }; + + case Comparison::LT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; + }; + + case Comparison::LTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; + }; + + case Comparison::GT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; + }; + + case Comparison::GTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; + }; + + case Comparison::IN: + return function ($object) use ($field, $value) { + return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::NIN: + return function ($object) use ($field, $value) { + return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::CONTAINS: + return function ($object) use ($field, $value) { + return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + default: + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return $this->andExpressions($expressionList); + + case CompositeExpression::TYPE_OR: + return $this->orExpressions($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * @param array $expressions + * + * @return callable + */ + private function andExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ( ! $expression($object)) { + return false; + } + } + return true; + }; + } + + /** + * @param array $expressions + * + * @return callable + */ + private function orExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ($expression($object)) { + return true; + } + } + return false; + }; + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php new file mode 100644 index 0000000..d54ecf2 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Comparison of a field with a value by the given operator. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Comparison implements Expression +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + const IS = '='; // no difference with EQ + const IN = 'IN'; + const NIN = 'NIN'; + const CONTAINS = 'CONTAINS'; + + /** + * @var string + */ + private $field; + + /** + * @var string + */ + private $op; + + /** + * @var Value + */ + private $value; + + /** + * @param string $field + * @param string $operator + * @param mixed $value + */ + public function __construct($field, $operator, $value) + { + if ( ! ($value instanceof Value)) { + $value = new Value($value); + } + + $this->field = $field; + $this->op = $operator; + $this->value = $value; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * @return Value + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->op; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkComparison($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php new file mode 100644 index 0000000..3613c02 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression of Expressions combined by AND or OR operation. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class CompositeExpression implements Expression +{ + const TYPE_AND = 'AND'; + const TYPE_OR = 'OR'; + + /** + * @var string + */ + private $type; + + /** + * @var Expression[] + */ + private $expressions = array(); + + /** + * @param string $type + * @param array $expressions + * + * @throws \RuntimeException + */ + public function __construct($type, array $expressions) + { + $this->type = $type; + + foreach ($expressions as $expr) { + if ($expr instanceof Value) { + throw new \RuntimeException("Values are not supported expressions as children of and/or expressions."); + } + if ( ! ($expr instanceof Expression)) { + throw new \RuntimeException("No expression given to CompositeExpression."); + } + + $this->expressions[] = $expr; + } + } + + /** + * Returns the list of expressions nested in this composite. + * + * @return Expression[] + */ + public function getExpressionList() + { + return $this->expressions; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkCompositeExpression($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php new file mode 100644 index 0000000..68db767 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression for the {@link Selectable} interface. + * + * @author Benjamin Eberlei + */ +interface Expression +{ + /** + * @param ExpressionVisitor $visitor + * + * @return mixed + */ + public function visit(ExpressionVisitor $visitor); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php new file mode 100644 index 0000000..080afdc --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * An Expression visitor walks a graph of expressions and turns them into a + * query for the underlying implementation. + * + * @author Benjamin Eberlei + */ +abstract class ExpressionVisitor +{ + /** + * Converts a comparison expression into the target query language output. + * + * @param Comparison $comparison + * + * @return mixed + */ + abstract public function walkComparison(Comparison $comparison); + + /** + * Converts a value expression into the target query language part. + * + * @param Value $value + * + * @return mixed + */ + abstract public function walkValue(Value $value); + + /** + * Converts a composite expression into the target query language output. + * + * @param CompositeExpression $expr + * + * @return mixed + */ + abstract public function walkCompositeExpression(CompositeExpression $expr); + + /** + * Dispatches walking an expression to the appropriate handler. + * + * @param Expression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function dispatch(Expression $expr) + { + switch (true) { + case ($expr instanceof Comparison): + return $this->walkComparison($expr); + + case ($expr instanceof Value): + return $this->walkValue($expr); + + case ($expr instanceof CompositeExpression): + return $this->walkCompositeExpression($expr); + + default: + throw new \RuntimeException("Unknown Expression " . get_class($expr)); + } + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php new file mode 100644 index 0000000..7f6e831 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +class Value implements Expression +{ + /** + * @var mixed + */ + private $value; + + /** + * @param mixed $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkValue($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php new file mode 100644 index 0000000..6539e3c --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +/** + * Builder for Expressions in the {@link Selectable} interface. + * + * Important Notice for interoperable code: You have to use scalar + * values only for comparisons, otherwise the behavior of the comparision + * may be different between implementations (Array vs ORM vs ODM). + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ExpressionBuilder +{ + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function eq($field, $value) + { + return new Comparison($field, Comparison::EQ, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gt($field, $value) + { + return new Comparison($field, Comparison::GT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lt($field, $value) + { + return new Comparison($field, Comparison::LT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gte($field, $value) + { + return new Comparison($field, Comparison::GTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lte($field, $value) + { + return new Comparison($field, Comparison::LTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function neq($field, $value) + { + return new Comparison($field, Comparison::NEQ, new Value($value)); + } + + /** + * @param string $field + * + * @return Comparison + */ + public function isNull($field) + { + return new Comparison($field, Comparison::EQ, new Value(null)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function in($field, array $values) + { + return new Comparison($field, Comparison::IN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function notIn($field, array $values) + { + return new Comparison($field, Comparison::NIN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function contains($field, $value) + { + return new Comparison($field, Comparison::CONTAINS, new Value($value)); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php new file mode 100644 index 0000000..57660ed --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Collections; + +/** + * Interface for collections that allow efficient filtering with an expression API. + * + * Goal of this interface is a backend independent method to fetch elements + * from a collections. {@link Expression} is crafted in a way that you can + * implement queries from both in-memory and database-backed collections. + * + * For database backed collections this allows very efficient access by + * utilizing the query APIs, for example SQL in the ORM. Applications using + * this API can implement efficient database access without having to ask the + * EntityManager or Repositories. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +interface Selectable +{ + /** + * Selects all elements from a selectable that match the expression and + * returns a new collection containing these elements. + * + * @param Criteria $criteria + * + * @return Collection + */ + public function matching(Criteria $criteria); +} diff --git a/vendor/doctrine/collections/phpunit.xml.dist b/vendor/doctrine/collections/phpunit.xml.dist new file mode 100644 index 0000000..36968e9 --- /dev/null +++ b/vendor/doctrine/collections/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/AbstractLazyCollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/AbstractLazyCollectionTest.php new file mode 100644 index 0000000..4de82cc --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/AbstractLazyCollectionTest.php @@ -0,0 +1,20 @@ +assertFalse($collection->isInitialized()); + $this->assertCount(3, $collection); + + $collection->add('bar'); + $this->assertTrue($collection->isInitialized()); + $this->assertCount(4, $collection); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ArrayCollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ArrayCollectionTest.php new file mode 100644 index 0000000..1b8a906 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ArrayCollectionTest.php @@ -0,0 +1,296 @@ +. + */ + +namespace Doctrine\Tests\Common\Collections; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; + +/** + * Tests for {@see \Doctrine\Common\Collections\ArrayCollection} + * + * @covers \Doctrine\Common\Collections\ArrayCollection + */ +class ArrayCollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideDifferentElements + */ + public function testToArray($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame($elements, $collection->toArray()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testFirst($elements) + { + $collection = new ArrayCollection($elements); + $this->assertSame(reset($elements), $collection->first()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testLast($elements) + { + $collection = new ArrayCollection($elements); + $this->assertSame(end($elements), $collection->last()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testKey($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(key($elements), $collection->key()); + + next($elements); + $collection->next(); + + $this->assertSame(key($elements), $collection->key()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testNext($elements) + { + $collection = new ArrayCollection($elements); + + while (true) { + $collectionNext = $collection->next(); + $arrayNext = next($elements); + + if(!$collectionNext || !$arrayNext) { + break; + } + + $this->assertSame($arrayNext, $collectionNext, "Returned value of ArrayCollection::next() and next() not match"); + $this->assertSame(key($elements), $collection->key(), "Keys not match"); + $this->assertSame(current($elements), $collection->current(), "Current values not match"); + } + } + + /** + * @dataProvider provideDifferentElements + */ + public function testCurrent($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(current($elements), $collection->current()); + + next($elements); + $collection->next(); + + $this->assertSame(current($elements), $collection->current()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testGetKeys($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(array_keys($elements), $collection->getKeys()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testGetValues($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(array_values($elements), $collection->getValues()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testCount($elements) + { + $collection = new ArrayCollection($elements); + + $this->assertSame(count($elements), $collection->count()); + } + + /** + * @dataProvider provideDifferentElements + */ + public function testIterator($elements) + { + $collection = new ArrayCollection($elements); + + $iterations = 0; + foreach($collection->getIterator() as $key => $item) { + $this->assertSame($elements[$key], $item, "Item {$key} not match"); + $iterations++; + } + + $this->assertEquals(count($elements), $iterations, "Number of iterations not match"); + } + + /** + * @return array + */ + public function provideDifferentElements() + { + return array( + 'indexed' => array(array(1, 2, 3, 4, 5)), + 'associative' => array(array('A' => 'a', 'B' => 'b', 'C' => 'c')), + 'mixed' => array(array('A' => 'a', 1, 'B' => 'b', 2, 3)), + ); + } + + public function testRemove() + { + $elements = array(1, 'A' => 'a', 2, 'B' => 'b', 3); + $collection = new ArrayCollection($elements); + + $this->assertEquals(1, $collection->remove(0)); + unset($elements[0]); + + $this->assertEquals(null, $collection->remove('non-existent')); + unset($elements['non-existent']); + + $this->assertEquals(2, $collection->remove(1)); + unset($elements[1]); + + $this->assertEquals('a', $collection->remove('A')); + unset($elements['A']); + + $this->assertEquals($elements, $collection->toArray()); + } + + public function testRemoveElement() + { + $elements = array(1, 'A' => 'a', 2, 'B' => 'b', 3, 'A2' => 'a', 'B2' => 'b'); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->removeElement(1)); + unset($elements[0]); + + $this->assertFalse($collection->removeElement('non-existent')); + + $this->assertTrue($collection->removeElement('a')); + unset($elements['A']); + + $this->assertTrue($collection->removeElement('a')); + unset($elements['A2']); + + $this->assertEquals($elements, $collection->toArray()); + } + + public function testContainsKey() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'B2' => 'b'); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->containsKey(0), "Contains index 0"); + $this->assertTrue($collection->containsKey('A'), "Contains key \"A\""); + $this->assertTrue($collection->containsKey('null'), "Contains key \"null\", with value null"); + $this->assertFalse($collection->containsKey('non-existent'), "Doesn't contain key"); + } + + public function testEmpty() + { + $collection = new ArrayCollection(); + $this->assertTrue($collection->isEmpty(), "Empty collection"); + + $collection->add(1); + $this->assertFalse($collection->isEmpty(), "Not empty collection"); + } + + public function testContains() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->contains(0), "Contains Zero"); + $this->assertTrue($collection->contains('a'), "Contains \"a\""); + $this->assertTrue($collection->contains(null), "Contains Null"); + $this->assertFalse($collection->contains('non-existent'), "Doesn't contain an element"); + } + + public function testExists() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertTrue($collection->exists(function($key, $element) { + return $key == 'A' && $element == 'a'; + }), "Element exists"); + + $this->assertFalse($collection->exists(function($key, $element) { + return $key == 'non-existent' && $element == 'non-existent'; + }), "Element not exists"); + } + + public function testIndexOf() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertSame(array_search(2, $elements, true), $collection->indexOf(2), 'Index of 2'); + $this->assertSame(array_search(null, $elements, true), $collection->indexOf(null), 'Index of null'); + $this->assertSame(array_search('non-existent', $elements, true), $collection->indexOf('non-existent'), 'Index of non existent'); + } + + public function testGet() + { + $elements = array(1, 'A' => 'a', 2, 'null' => null, 3, 'A2' => 'a', 'zero' => 0); + $collection = new ArrayCollection($elements); + + $this->assertSame(2, $collection->get(1), 'Get element by index'); + $this->assertSame('a', $collection->get('A'), 'Get element by name'); + $this->assertSame(null, $collection->get('non-existent'), 'Get non existent element'); + } + + public function testMatchingWithSortingPreservesyKeys() + { + $object1 = new \stdClass(); + $object2 = new \stdClass(); + + $object1->sortField = 2; + $object2->sortField = 1; + + $collection = new ArrayCollection(array( + 'object1' => $object1, + 'object2' => $object2, + )); + + $this->assertSame( + array( + 'object2' => $object2, + 'object1' => $object1, + ), + $collection + ->matching(new Criteria(null, array('sortField' => Criteria::ASC))) + ->toArray() + ); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php new file mode 100644 index 0000000..21c2e3d --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ClosureExpressionVisitorTest.php @@ -0,0 +1,250 @@ +. + */ + +namespace Doctrine\Tests\Common\Collections; + +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; +use Doctrine\Common\Collections\ExpressionBuilder; + +/** + * @group DDC-1637 + */ +class ClosureExpressionVisitorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ClosureExpressionVisitor + */ + private $visitor; + + /** + * @var ExpressionBuilder + */ + private $builder; + + protected function setUp() + { + $this->visitor = new ClosureExpressionVisitor(); + $this->builder = new ExpressionBuilder(); + } + + public function testGetObjectFieldValueIsAccessor() + { + $object = new TestObject(1, 2, true); + + $this->assertTrue($this->visitor->getObjectFieldValue($object, 'baz')); + } + + public function testGetObjectFieldValueMagicCallMethod() + { + $object = new TestObject(1, 2, true, 3); + + $this->assertEquals(3, $this->visitor->getObjectFieldValue($object, 'qux')); + } + + public function testWalkEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->eq("foo", 1)); + + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(2))); + } + + public function testWalkNotEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->neq("foo", 1)); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(2))); + } + + public function testWalkLessThanComparison() + { + $closure = $this->visitor->walkComparison($this->builder->lt("foo", 1)); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(0))); + } + + public function testWalkLessThanEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->lte("foo", 1)); + + $this->assertFalse($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertTrue($closure(new TestObject(0))); + } + + public function testWalkGreaterThanEqualsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->gte("foo", 1)); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkGreaterThanComparison() + { + $closure = $this->visitor->walkComparison($this->builder->gt("foo", 1)); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertFalse($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkInComparison() + { + $closure = $this->visitor->walkComparison($this->builder->in("foo", array(1, 2, 3))); + + $this->assertTrue($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(0))); + } + + public function testWalkNotInComparison() + { + $closure = $this->visitor->walkComparison($this->builder->notIn("foo", array(1, 2, 3))); + + $this->assertFalse($closure(new TestObject(1))); + $this->assertFalse($closure(new TestObject(2))); + $this->assertTrue($closure(new TestObject(0))); + $this->assertTrue($closure(new TestObject(4))); + } + + public function testWalkContainsComparison() + { + $closure = $this->visitor->walkComparison($this->builder->contains('foo', 'hello')); + + $this->assertTrue($closure(new TestObject('hello world'))); + $this->assertFalse($closure(new TestObject('world'))); + } + + public function testWalkAndCompositeExpression() + { + $closure = $this->visitor->walkCompositeExpression( + $this->builder->andX( + $this->builder->eq("foo", 1), + $this->builder->eq("bar", 1) + ) + ); + + $this->assertTrue($closure(new TestObject(1, 1))); + $this->assertFalse($closure(new TestObject(1, 0))); + $this->assertFalse($closure(new TestObject(0, 1))); + $this->assertFalse($closure(new TestObject(0, 0))); + } + + public function testWalkOrCompositeExpression() + { + $closure = $this->visitor->walkCompositeExpression( + $this->builder->orX( + $this->builder->eq("foo", 1), + $this->builder->eq("bar", 1) + ) + ); + + $this->assertTrue($closure(new TestObject(1, 1))); + $this->assertTrue($closure(new TestObject(1, 0))); + $this->assertTrue($closure(new TestObject(0, 1))); + $this->assertFalse($closure(new TestObject(0, 0))); + } + + public function testSortByFieldAscending() + { + $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); + $sort = ClosureExpressionVisitor::sortByField("foo"); + + usort($objects, $sort); + + $this->assertEquals("a", $objects[0]->getFoo()); + $this->assertEquals("b", $objects[1]->getFoo()); + $this->assertEquals("c", $objects[2]->getFoo()); + } + + public function testSortByFieldDescending() + { + $objects = array(new TestObject("b"), new TestObject("a"), new TestObject("c")); + $sort = ClosureExpressionVisitor::sortByField("foo", -1); + + usort($objects, $sort); + + $this->assertEquals("c", $objects[0]->getFoo()); + $this->assertEquals("b", $objects[1]->getFoo()); + $this->assertEquals("a", $objects[2]->getFoo()); + } + + public function testSortDelegate() + { + $objects = array(new TestObject("a", "c"), new TestObject("a", "b"), new TestObject("a", "a")); + $sort = ClosureExpressionVisitor::sortByField("bar", 1); + $sort = ClosureExpressionVisitor::sortByField("foo", 1, $sort); + + usort($objects, $sort); + + $this->assertEquals("a", $objects[0]->getBar()); + $this->assertEquals("b", $objects[1]->getBar()); + $this->assertEquals("c", $objects[2]->getBar()); + } + + public function testArrayComparison() + { + $closure = $this->visitor->walkComparison($this->builder->eq("foo", 42)); + + $this->assertTrue($closure(array('foo' => 42))); + } +} + +class TestObject +{ + private $foo; + private $bar; + private $baz; + private $qux; + + public function __construct($foo = null, $bar = null, $baz = null, $qux = null) + { + $this->foo = $foo; + $this->bar = $bar; + $this->baz = $baz; + $this->qux = $qux; + } + + public function __call($name, $arguments) + { + if ('getqux' === $name) { + return $this->qux; + } + } + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } + + public function isBaz() + { + return $this->baz; + } +} + diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php new file mode 100644 index 0000000..f3f91ad --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CollectionTest.php @@ -0,0 +1,265 @@ +collection = new ArrayCollection(); + } + + public function testIssetAndUnset() + { + $this->assertFalse(isset($this->collection[0])); + $this->collection->add('testing'); + $this->assertTrue(isset($this->collection[0])); + unset($this->collection[0]); + $this->assertFalse(isset($this->collection[0])); + } + + public function testToString() + { + $this->collection->add('testing'); + $this->assertTrue(is_string((string) $this->collection)); + } + + public function testRemovingNonExistentEntryReturnsNull() + { + $this->assertEquals(null, $this->collection->remove('testing_does_not_exist')); + } + + public function testExists() + { + $this->collection->add("one"); + $this->collection->add("two"); + $exists = $this->collection->exists(function($k, $e) { return $e == "one"; }); + $this->assertTrue($exists); + $exists = $this->collection->exists(function($k, $e) { return $e == "other"; }); + $this->assertFalse($exists); + } + + public function testMap() + { + $this->collection->add(1); + $this->collection->add(2); + $res = $this->collection->map(function($e) { return $e * 2; }); + $this->assertEquals(array(2, 4), $res->toArray()); + } + + public function testFilter() + { + $this->collection->add(1); + $this->collection->add("foo"); + $this->collection->add(3); + $res = $this->collection->filter(function($e) { return is_numeric($e); }); + $this->assertEquals(array(0 => 1, 2 => 3), $res->toArray()); + } + + public function testFirstAndLast() + { + $this->collection->add('one'); + $this->collection->add('two'); + + $this->assertEquals($this->collection->first(), 'one'); + $this->assertEquals($this->collection->last(), 'two'); + } + + public function testArrayAccess() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + + $this->assertEquals($this->collection[0], 'one'); + $this->assertEquals($this->collection[1], 'two'); + + unset($this->collection[0]); + $this->assertEquals($this->collection->count(), 1); + } + + public function testContainsKey() + { + $this->collection[5] = 'five'; + $this->assertTrue($this->collection->containsKey(5)); + } + + public function testContains() + { + $this->collection[0] = 'test'; + $this->assertTrue($this->collection->contains('test')); + } + + public function testSearch() + { + $this->collection[0] = 'test'; + $this->assertEquals(0, $this->collection->indexOf('test')); + } + + public function testGet() + { + $this->collection[0] = 'test'; + $this->assertEquals('test', $this->collection->get(0)); + } + + public function testGetKeys() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals(array(0, 1), $this->collection->getKeys()); + } + + public function testGetValues() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals(array('one', 'two'), $this->collection->getValues()); + } + + public function testCount() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals($this->collection->count(), 2); + $this->assertEquals(count($this->collection), 2); + } + + public function testForAll() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->assertEquals($this->collection->forAll(function($k, $e) { return is_string($e); }), true); + $this->assertEquals($this->collection->forAll(function($k, $e) { return is_array($e); }), false); + } + + public function testPartition() + { + $this->collection[] = true; + $this->collection[] = false; + $partition = $this->collection->partition(function($k, $e) { return $e == true; }); + $this->assertEquals($partition[0][0], true); + $this->assertEquals($partition[1][0], false); + } + + public function testClear() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->collection->clear(); + $this->assertEquals($this->collection->isEmpty(), true); + } + + public function testRemove() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $el = $this->collection->remove(0); + + $this->assertEquals('one', $el); + $this->assertEquals($this->collection->contains('one'), false); + $this->assertNull($this->collection->remove(0)); + } + + public function testRemoveElement() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + + $this->assertTrue($this->collection->removeElement('two')); + $this->assertFalse($this->collection->contains('two')); + $this->assertFalse($this->collection->removeElement('two')); + } + + public function testSlice() + { + $this->collection[] = 'one'; + $this->collection[] = 'two'; + $this->collection[] = 'three'; + + $slice = $this->collection->slice(0, 1); + $this->assertInternalType('array', $slice); + $this->assertEquals(array('one'), $slice); + + $slice = $this->collection->slice(1); + $this->assertEquals(array(1 => 'two', 2 => 'three'), $slice); + + $slice = $this->collection->slice(1, 1); + $this->assertEquals(array(1 => 'two'), $slice); + } + + public function fillMatchingFixture() + { + $std1 = new \stdClass(); + $std1->foo = "bar"; + $this->collection[] = $std1; + + $std2 = new \stdClass(); + $std2->foo = "baz"; + $this->collection[] = $std2; + } + + /** + * @group DDC-1637 + */ + public function testMatching() + { + $this->fillMatchingFixture(); + + $col = $this->collection->matching(new Criteria(Criteria::expr()->eq("foo", "bar"))); + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->collection); + $this->assertEquals(1, count($col)); + } + + /** + * @group DDC-1637 + */ + public function testMatchingOrdering() + { + $this->fillMatchingFixture(); + + $col = $this->collection->matching(new Criteria(null, array('foo' => 'DESC'))); + + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->collection); + $this->assertEquals(2, count($col)); + $this->assertEquals('baz', $col->first()->foo); + $this->assertEquals('bar', $col->last()->foo); + } + + /** + * @group DDC-1637 + */ + public function testMatchingSlice() + { + $this->fillMatchingFixture(); + + $col = $this->collection->matching(new Criteria(null, null, 1, 1)); + + $this->assertInstanceOf('Doctrine\Common\Collections\Collection', $col); + $this->assertNotSame($col, $this->collection); + $this->assertEquals(1, count($col)); + $this->assertEquals('baz', $col[0]->foo); + } + + public function testCanRemoveNullValuesByKey() + { + $this->collection->add(null); + $this->collection->remove(0); + $this->assertTrue($this->collection->isEmpty()); + } + + public function testCanVerifyExistingKeysWithNullValues() + { + $this->collection->set('key', null); + $this->assertTrue($this->collection->containsKey('key')); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php new file mode 100644 index 0000000..1ab058a --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/CriteriaTest.php @@ -0,0 +1,83 @@ +assertInstanceOf('Doctrine\Common\Collections\Criteria', $criteria); + } + + public function testConstructor() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria($expr, array("foo" => "ASC"), 10, 20); + + $this->assertSame($expr, $criteria->getWhereExpression()); + $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); + $this->assertEquals(10, $criteria->getFirstResult()); + $this->assertEquals(20, $criteria->getMaxResults()); + } + + public function testWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + + $this->assertSame($expr, $criteria->getWhereExpression()); + } + + public function testAndWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + $expr = $criteria->getWhereExpression(); + $criteria->andWhere($expr); + + $where = $criteria->getWhereExpression(); + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); + + $this->assertEquals(CompositeExpression::TYPE_AND, $where->getType()); + $this->assertSame(array($expr, $expr), $where->getExpressionList()); + } + + public function testOrWhere() + { + $expr = new Comparison("field", "=", "value"); + $criteria = new Criteria(); + + $criteria->where($expr); + $expr = $criteria->getWhereExpression(); + $criteria->orWhere($expr); + + $where = $criteria->getWhereExpression(); + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $where); + + $this->assertEquals(CompositeExpression::TYPE_OR, $where->getType()); + $this->assertSame(array($expr, $expr), $where->getExpressionList()); + } + + public function testOrderings() + { + $criteria = Criteria::create() + ->orderBy(array("foo" => "ASC")); + + $this->assertEquals(array("foo" => "ASC"), $criteria->getOrderings()); + } + + public function testExpr() + { + $this->assertInstanceOf('Doctrine\Common\Collections\ExpressionBuilder', Criteria::expr()); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php new file mode 100644 index 0000000..5f6b4ce --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/Common/Collections/ExpressionBuilderTest.php @@ -0,0 +1,125 @@ +builder = new ExpressionBuilder(); + } + + public function testAndX() + { + $expr = $this->builder->andX($this->builder->eq("a", "b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $expr); + $this->assertEquals(CompositeExpression::TYPE_AND, $expr->getType()); + } + + public function testOrX() + { + $expr = $this->builder->orX($this->builder->eq("a", "b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\CompositeExpression', $expr); + $this->assertEquals(CompositeExpression::TYPE_OR, $expr->getType()); + } + + public function testInvalidAndXArgument() + { + $this->setExpectedException("RuntimeException"); + $this->builder->andX("foo"); + } + + public function testEq() + { + $expr = $this->builder->eq("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::EQ, $expr->getOperator()); + } + + public function testNeq() + { + $expr = $this->builder->neq("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::NEQ, $expr->getOperator()); + } + + public function testLt() + { + $expr = $this->builder->lt("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::LT, $expr->getOperator()); + } + + public function testGt() + { + $expr = $this->builder->gt("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::GT, $expr->getOperator()); + } + + public function testGte() + { + $expr = $this->builder->gte("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::GTE, $expr->getOperator()); + } + + public function testLte() + { + $expr = $this->builder->lte("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::LTE, $expr->getOperator()); + } + + public function testIn() + { + $expr = $this->builder->in("a", array("b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::IN, $expr->getOperator()); + } + + public function testNotIn() + { + $expr = $this->builder->notIn("a", array("b")); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::NIN, $expr->getOperator()); + } + + public function testIsNull() + { + $expr = $this->builder->isNull("a"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::EQ, $expr->getOperator()); + } + + public function testContains() + { + $expr = $this->builder->contains("a", "b"); + + $this->assertInstanceOf('Doctrine\Common\Collections\Expr\Comparison', $expr); + $this->assertEquals(Comparison::CONTAINS, $expr->getOperator()); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/LazyArrayCollection.php b/vendor/doctrine/collections/tests/Doctrine/Tests/LazyArrayCollection.php new file mode 100644 index 0000000..56736b8 --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/LazyArrayCollection.php @@ -0,0 +1,22 @@ +collection = new ArrayCollection(array('a', 'b', 'c')); + } +} diff --git a/vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php b/vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php new file mode 100644 index 0000000..973b5fe --- /dev/null +++ b/vendor/doctrine/collections/tests/Doctrine/Tests/TestInit.php @@ -0,0 +1,21 @@ +setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + // new code necessary starting here + $reader->setIgnoreNotImportedAnnotations(true); + $reader->setEnableParsePhpImports(false); + $reader = new \Doctrine\Common\Annotations\CachedReader( + new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache() + ); + +## Annotation Base class or @Annotation + +Beginning after 2.1-RC2 you have to either extend ``Doctrine\Common\Annotations\Annotation`` or add @Annotation to your annotations class-level docblock, otherwise the class will simply be ignored. + +## Removed methods on AnnotationReader + +* AnnotationReader::setAutoloadAnnotations() +* AnnotationReader::getAutoloadAnnotations() +* AnnotationReader::isAutoloadAnnotations() + +## AnnotationRegistry + +Autoloading through the PHP autoloader is removed from the 2.1 AnnotationReader. Instead you have to use the global AnnotationRegistry for loading purposes: + + \Doctrine\Common\Annotations\AnnotationRegistry::registerFile($fileWithAnnotations); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace($namespace, $dirs = null); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespaces($namespaces); + \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader($callable); + +The $callable for registering a loader accepts a class as first and only parameter and must try to silently autoload it. On success true has to be returned. +The registerAutoloadNamespace function registers a PSR-0 compatible silent autoloader for all classes with the given namespace in the given directories. +If null is passed as directory the include path will be used. + diff --git a/vendor/doctrine/common/UPGRADE_TO_2_2 b/vendor/doctrine/common/UPGRADE_TO_2_2 new file mode 100644 index 0000000..1d93a13 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_2 @@ -0,0 +1,61 @@ +This document details all the possible changes that you should investigate when +updating your project from Doctrine Common 2.1 to 2.2: + +## Annotation Changes + +- AnnotationReader::setIgnoreNotImportedAnnotations has been removed, you need to + add ignore annotation names which are supposed to be ignored via + AnnotationReader::addGlobalIgnoredName + +- AnnotationReader::setAutoloadAnnotations was deprecated by the AnnotationRegistry + in 2.1 and has been removed in 2.2 + +- AnnotationReader::setEnableParsePhpImports was added to ease transition to the new + annotation mechanism in 2.1 and is removed in 2.2 + +- AnnotationReader::isParsePhpImportsEnabled is removed (see above) + +- AnnotationReader::setDefaultAnnotationNamespace was deprecated in favor of explicit + configuration in 2.1 and will be removed in 2.2 (for isolated projects where you + have full-control over _all_ available annotations, we offer a dedicated reader + class ``SimpleAnnotationReader``) + +- AnnotationReader::setAnnotationCreationFunction was deprecated in 2.1 and will be + removed in 2.2. We only offer two creation mechanisms which cannot be changed + anymore to allow the same reader instance to work with all annotations regardless + of which library they are coming from. + +- AnnotationReader::setAnnotationNamespaceAlias was deprecated in 2.1 and will be + removed in 2.2 (see setDefaultAnnotationNamespace) + +- If you use a class as annotation which has not the @Annotation marker in it's + class block, we will now throw an exception instead of silently ignoring it. You + can however still achieve the previous behavior using the @IgnoreAnnotation, or + AnnotationReader::addGlobalIgnoredName (the exception message will contain detailed + instructions when you run into this problem). + +## Cache Changes + +- Renamed old AbstractCache to CacheProvider + +- Dropped the support to the following functions of all cache providers: + + - CacheProvider::deleteByWildcard + + - CacheProvider::deleteByRegEx + + - CacheProvider::deleteByPrefix + + - CacheProvider::deleteBySuffix + +- CacheProvider::deleteAll will not remove ALL entries, it will only mark them as invalid + +- CacheProvider::flushAll will remove ALL entries, namespaced or not + +- Added support to MemcachedCache + +- Added support to WincacheCache + +## ClassLoader Changes + +- ClassLoader::fileExistsInIncludePath() no longer exists. Use the native stream_resolve_include_path() PHP function \ No newline at end of file diff --git a/vendor/doctrine/common/composer.json b/vendor/doctrine/common/composer.json new file mode 100644 index 0000000..a12495a --- /dev/null +++ b/vendor/doctrine/common/composer.json @@ -0,0 +1,36 @@ +{ + "name": "doctrine/common", + "type": "library", + "description": "Common Library for Doctrine projects", + "keywords": ["collections", "spl", "eventmanager", "annotations", "persistence"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": "~5.5|~7.0", + "doctrine/inflector": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/lexer": "1.*", + "doctrine/annotations": "1.*" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php new file mode 100644 index 0000000..78eeb35 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php @@ -0,0 +1,280 @@ +. + */ + +namespace Doctrine\Common; + +/** + * A ClassLoader is an autoloader for class files that can be + * installed on the SPL autoload stack. It is a class loader that either loads only classes + * of a specific namespace or all namespaces and it is suitable for working together + * with other autoloaders in the SPL autoload stack. + * + * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader + * relies on the PHP include_path. + * + * @author Roman Borschel + * @since 2.0 + * + * @deprecated the ClassLoader is deprecated and will be removed in version 3.0 of doctrine/common. + */ +class ClassLoader +{ + /** + * PHP file extension. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Current namespace. + * + * @var string|null + */ + protected $namespace; + + /** + * Current include path. + * + * @var string|null + */ + protected $includePath; + + /** + * PHP namespace separator. + * + * @var string + */ + protected $namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace from the specified include path. + * + * If no include path is given, the ClassLoader relies on the PHP include_path. + * If neither a namespace nor an include path is given, the ClassLoader will + * be responsible for loading all classes, thereby relying on the PHP include_path. + * + * @param string|null $ns The namespace of the classes to load. + * @param string|null $includePath The base include path to use. + */ + public function __construct($ns = null, $includePath = null) + { + $this->namespace = $ns; + $this->includePath = $includePath; + } + + /** + * Sets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @param string $sep The separator to use. + * + * @return void + */ + public function setNamespaceSeparator($sep) + { + $this->namespaceSeparator = $sep; + } + + /** + * Gets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @return string + */ + public function getNamespaceSeparator() + { + return $this->namespaceSeparator; + } + + /** + * Sets the base include path for all class files in the namespace of this ClassLoader. + * + * @param string|null $includePath + * + * @return void + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string|null + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Registers this ClassLoader on the SPL autoload stack. + * + * @return void + */ + public function register() + { + spl_autoload_register([$this, 'loadClass']); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $className The name of the class to load. + * + * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. + */ + public function loadClass($className) + { + if (self::typeExists($className)) { + return true; + } + + if (! $this->canLoadClass($className)) { + return false; + } + + require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->fileExtension; + + return self::typeExists($className); + } + + /** + * Asks this ClassLoader whether it can potentially load the class (file) with + * the given name. + * + * @param string $className The fully-qualified name of the class. + * + * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. + */ + public function canLoadClass($className) + { + if ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { + return false; + } + + $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; + + if ($this->includePath !== null) { + return is_file($this->includePath . DIRECTORY_SEPARATOR . $file); + } + + return (false !== stream_resolve_include_path($file)); + } + + /** + * Checks whether a class with a given name exists. A class "exists" if it is either + * already defined in the current request or if there is an autoloader on the SPL + * autoload stack that is a) responsible for the class in question and b) is able to + * load a class file in which the class definition resides. + * + * If the class is not already defined, each autoloader in the SPL autoload stack + * is asked whether it is able to tell if the class exists. If the autoloader is + * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload + * function of the autoloader is invoked and expected to return a value that + * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports + * that the class exists, TRUE is returned. + * + * Note that, depending on what kinds of autoloaders are installed on the SPL + * autoload stack, the class (file) might already be loaded as a result of checking + * for its existence. This is not the case with a ClassLoader, who separates + * these responsibilities. + * + * @param string $className The fully-qualified name of the class. + * + * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. + */ + public static function classExists($className) + { + return self::typeExists($className, true); + } + + /** + * Gets the ClassLoader from the SPL autoload stack that is responsible + * for (and is able to load) the class with the given name. + * + * @param string $className The name of the class. + * + * @return ClassLoader The ClassLoader for the class or NULL if no such ClassLoader exists. + */ + public static function getClassLoader($className) + { + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader) + && ($classLoader = reset($loader)) + && $classLoader instanceof ClassLoader + && $classLoader->canLoadClass($className) + ) { + return $classLoader; + } + } + + return null; + } + + /** + * Checks whether a given type exists + * + * @param string $type + * @param bool $autoload + * + * @return bool + */ + private static function typeExists($type, $autoload = false) + { + return class_exists($type, $autoload) + || interface_exists($type, $autoload) + || trait_exists($type, $autoload); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php new file mode 100644 index 0000000..2a1a08e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Base exception class for package Doctrine\Common. + * + * @author heinrich + */ +class CommonException extends \Exception +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php new file mode 100644 index 0000000..8cd02c9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Comparable interface that allows to compare two value objects to each other for similarity. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +interface Comparable +{ + /** + * Compares the current object to the passed $other. + * + * Returns 0 if they are semantically equal, 1 if the other object + * is less than the current one, or -1 if its more than the current one. + * + * This method should not check for identity using ===, only for semantical equality for example + * when two different DateTime instances point to the exact same Date + TZ. + * + * @param mixed $other + * + * @return int + */ + public function compareTo($other); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php new file mode 100644 index 0000000..75506e6 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventArgs.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\Common; + +/** + * EventArgs is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass state + * information to an event handler when an event is raised. The single empty EventArgs + * instance can be obtained through {@link getEmptyInstance}. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventArgs +{ + /** + * Single instance of EventArgs. + * + * @var EventArgs + */ + private static $_emptyEventArgsInstance; + + /** + * Gets the single, empty and immutable EventArgs instance. + * + * This instance will be used when events are dispatched without any parameter, + * like this: EventManager::dispatchEvent('eventname'); + * + * The benefit from this is that only one empty instance is instantiated and shared + * (otherwise there would be instances for every dispatched in the abovementioned form). + * + * @see EventManager::dispatchEvent + * + * @link http://msdn.microsoft.com/en-us/library/system.eventargs.aspx + * + * @return EventArgs + */ + public static function getEmptyInstance() + { + if ( ! self::$_emptyEventArgsInstance) { + self::$_emptyEventArgsInstance = new EventArgs; + } + + return self::$_emptyEventArgsInstance; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php new file mode 100644 index 0000000..0ee04a1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php @@ -0,0 +1,154 @@ +. + */ + +namespace Doctrine\Common; + +/** + * The EventManager is the central point of Doctrine's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EventManager +{ + /** + * Map of registered listeners. + * => + * + * @var array + */ + private $_listeners = []; + + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of the event is + * the name of the method that is invoked on listeners. + * @param EventArgs|null $eventArgs The event arguments to pass to the event handlers/listeners. + * If not supplied, the single empty EventArgs instance is used. + * + * @return boolean + */ + public function dispatchEvent($eventName, EventArgs $eventArgs = null) + { + if (isset($this->_listeners[$eventName])) { + $eventArgs = $eventArgs === null ? EventArgs::getEmptyInstance() : $eventArgs; + + foreach ($this->_listeners[$eventName] as $listener) { + $listener->$eventName($eventArgs); + } + } + } + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string|null $event The name of the event. + * + * @return array The event listeners for the specified event, or all event listeners. + */ + public function getListeners($event = null) + { + return $event ? $this->_listeners[$event] : $this->_listeners; + } + + /** + * Checks whether an event has any registered listeners. + * + * @param string $event + * + * @return boolean TRUE if the specified event has any listeners, FALSE otherwise. + */ + public function hasListeners($event) + { + return isset($this->_listeners[$event]) && $this->_listeners[$event]; + } + + /** + * Adds an event listener that listens on the specified events. + * + * @param string|array $events The event(s) to listen on. + * @param object $listener The listener object. + * + * @return void + */ + public function addEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Overrides listener if a previous one was associated already + // Prevents duplicate listeners on same event (same instance only) + $this->_listeners[$event][$hash] = $listener; + } + } + + /** + * Removes an event listener from the specified events. + * + * @param string|array $events + * @param object $listener + * + * @return void + */ + public function removeEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Check if actually have this listener associated + if (isset($this->_listeners[$event][$hash])) { + unset($this->_listeners[$event][$hash]); + } + } + } + + /** + * Adds an EventSubscriber. The subscriber is asked for all the events it is + * interested in and added as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function addEventSubscriber(EventSubscriber $subscriber) + { + $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); + } + + /** + * Removes an EventSubscriber. The subscriber is asked for all the events it is + * interested in and removed as a listener for these events. + * + * @param \Doctrine\Common\EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function removeEventSubscriber(EventSubscriber $subscriber) + { + $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php new file mode 100644 index 0000000..55d0f7d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/EventSubscriber.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventManager, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface EventSubscriber +{ + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + public function getSubscribedEvents(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php new file mode 100644 index 0000000..0aa07f8 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common; + +use Doctrine\Common\Lexer\AbstractLexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * Lexer moved into its own Component Doctrine\Common\Lexer. This class + * only stays for being BC. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Lexer extends AbstractLexer +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php new file mode 100644 index 0000000..e25e999 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that provide the service of notifying listeners of + * changes to their properties. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface NotifyPropertyChanged +{ + /** + * Adds a listener that wants to be notified about property changes. + * + * @param PropertyChangedListener $listener + * + * @return void + */ + public function addPropertyChangedListener(PropertyChangedListener $listener); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php new file mode 100644 index 0000000..12de462 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -0,0 +1,262 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Abstract implementation of the ManagerRegistry contract. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +abstract class AbstractManagerRegistry implements ManagerRegistry +{ + /** + * @var string + */ + private $name; + + /** + * @var array + */ + private $connections; + + /** + * @var array + */ + private $managers; + + /** + * @var string + */ + private $defaultConnection; + + /** + * @var string + */ + private $defaultManager; + + /** + * @var string + */ + private $proxyInterfaceName; + + /** + * Constructor. + * + * @param string $name + * @param array $connections + * @param array $managers + * @param string $defaultConnection + * @param string $defaultManager + * @param string $proxyInterfaceName + */ + public function __construct($name, array $connections, array $managers, $defaultConnection, $defaultManager, $proxyInterfaceName) + { + $this->name = $name; + $this->connections = $connections; + $this->managers = $managers; + $this->defaultConnection = $defaultConnection; + $this->defaultManager = $defaultManager; + $this->proxyInterfaceName = $proxyInterfaceName; + } + + /** + * Fetches/creates the given services. + * + * A service in this context is connection or a manager instance. + * + * @param string $name The name of the service. + * + * @return object The instance of the given service. + */ + abstract protected function getService($name); + + /** + * Resets the given services. + * + * A service in this context is connection or a manager instance. + * + * @param string $name The name of the service. + * + * @return void + */ + abstract protected function resetService($name); + + /** + * Gets the name of the registry. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getConnection($name = null) + { + if (null === $name) { + $name = $this->defaultConnection; + } + + if (!isset($this->connections[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Connection named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->connections[$name]); + } + + /** + * {@inheritdoc} + */ + public function getConnectionNames() + { + return $this->connections; + } + + /** + * {@inheritdoc} + */ + public function getConnections() + { + $connections = []; + foreach ($this->connections as $name => $id) { + $connections[$name] = $this->getService($id); + } + + return $connections; + } + + /** + * {@inheritdoc} + */ + public function getDefaultConnectionName() + { + return $this->defaultConnection; + } + + /** + * {@inheritdoc} + */ + public function getDefaultManagerName() + { + return $this->defaultManager; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException + */ + public function getManager($name = null) + { + if (null === $name) { + $name = $this->defaultManager; + } + + if (!isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->managers[$name]); + } + + /** + * {@inheritdoc} + */ + public function getManagerForClass($class) + { + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $proxyClass = new \ReflectionClass($class); + + if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { + if (! $parentClass = $proxyClass->getParentClass()) { + return null; + } + + $class = $parentClass->getName(); + } + + foreach ($this->managers as $id) { + $manager = $this->getService($id); + + if (!$manager->getMetadataFactory()->isTransient($class)) { + return $manager; + } + } + } + + /** + * {@inheritdoc} + */ + public function getManagerNames() + { + return $this->managers; + } + + /** + * {@inheritdoc} + */ + public function getManagers() + { + $dms = []; + foreach ($this->managers as $name => $id) { + $dms[$name] = $this->getService($id); + } + + return $dms; + } + + /** + * {@inheritdoc} + */ + public function getRepository($persistentObjectName, $persistentManagerName = null) + { + return $this->getManager($persistentManagerName)->getRepository($persistentObjectName); + } + + /** + * {@inheritdoc} + */ + public function resetManager($name = null) + { + if (null === $name) { + $name = $this->defaultManager; + } + + if (!isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + // force the creation of a new document manager + // if the current one is closed + $this->resetService($this->managers[$name]); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php new file mode 100644 index 0000000..7c25e98 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering connection for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +interface ConnectionRegistry +{ + /** + * Gets the default connection name. + * + * @return string The default connection name. + */ + public function getDefaultConnectionName(); + + /** + * Gets the named connection. + * + * @param string $name The connection name (null for the default one). + * + * @return object + */ + public function getConnection($name = null); + + /** + * Gets an array of all registered connections. + * + * @return array An array of Connection instances. + */ + public function getConnections(); + + /** + * Gets all connection names. + * + * @return array An array of connection names. + */ + public function getConnectionNames(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php new file mode 100644 index 0000000..52f41c0 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LifecycleEventArgs extends EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var object + */ + private $object; + + /** + * Constructor. + * + * @param object $object + * @param ObjectManager $objectManager + */ + public function __construct($object, ObjectManager $objectManager) + { + $this->object = $object; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated entity. + * + * @deprecated + * + * @return object + */ + public function getEntity() + { + return $this->object; + } + + /** + * Retrieves the associated object. + * + * @return object + */ + public function getObject() + { + return $this->object; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 0000000..3d8abbe --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.2 + */ +class LoadClassMetadataEventArgs extends EventArgs +{ + /** + * @var ClassMetadata + */ + private $classMetadata; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ClassMetadata $classMetadata + * @param ObjectManager $objectManager + */ + public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager) + { + $this->classMetadata = $classMetadata; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ClassMetadata. + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php new file mode 100644 index 0000000..5527d4d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the preFlush event. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ManagerEventArgs extends EventArgs +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * Constructor. + * + * @param ObjectManager $objectManager + */ + public function __construct(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php new file mode 100644 index 0000000..b78bad9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Provides event arguments for the onClear event. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends EventArgs +{ + /** + * @var \Doctrine\Common\Persistence\ObjectManager + */ + private $objectManager; + + /** + * @var string|null + */ + private $entityClass; + + /** + * Constructor. + * + * @param ObjectManager $objectManager The object manager. + * @param string|null $entityClass The optional entity class. + */ + public function __construct($objectManager, $entityClass = null) + { + $this->objectManager = $objectManager; + $this->entityClass = $entityClass; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Returns the name of the entity class that is cleared, or null if all are cleared. + * + * @return string|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Returns whether this event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php new file mode 100644 index 0000000..facce64 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\Common\Persistence\Event; + +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a preUpdate event. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.2 + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $entityChangeSet; + + /** + * Constructor. + * + * @param object $entity + * @param ObjectManager $objectManager + * @param array $changeSet + */ + public function __construct($entity, ObjectManager $objectManager, array &$changeSet) + { + parent::__construct($entity, $objectManager); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieves the entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Gets the old value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][0]; + } + + /** + * Gets the new value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][1]; + } + + /** + * Sets the new value of this field. + * + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Asserts the field exists in changeset. + * + * @param string $field + * + * @return void + * + * @throws \InvalidArgumentException + */ + private function assertValidField($field) + { + if ( ! isset($this->entityChangeSet[$field])) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', + $field, + get_class($this->getEntity()) + )); + } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php new file mode 100644 index 0000000..fce854b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ManagerRegistry.php @@ -0,0 +1,111 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract covering object managers for a Doctrine persistence layer ManagerRegistry class to implement. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Lukas Kahwe Smith + */ +interface ManagerRegistry extends ConnectionRegistry +{ + /** + * Gets the default object manager name. + * + * @return string The default object manager name. + */ + public function getDefaultManagerName(); + + /** + * Gets a named object manager. + * + * @param string $name The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getManager($name = null); + + /** + * Gets an array of all registered object managers. + * + * @return \Doctrine\Common\Persistence\ObjectManager[] An array of ObjectManager instances + */ + public function getManagers(); + + /** + * Resets a named object manager. + * + * This method is useful when an object manager has been closed + * because of a rollbacked transaction AND when you think that + * it makes sense to get a new one to replace the closed one. + * + * Be warned that you will get a brand new object manager as + * the existing one is not useable anymore. This means that any + * other object with a dependency on this object manager will + * hold an obsolete reference. You can inject the registry instead + * to avoid this problem. + * + * @param string|null $name The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function resetManager($name = null); + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered object managers. + * + * @param string $alias The alias. + * + * @return string The full namespace. + */ + public function getAliasNamespace($alias); + + /** + * Gets all connection names. + * + * @return array An array of connection names. + */ + public function getManagerNames(); + + /** + * Gets the ObjectRepository for an persistent object. + * + * @param string $persistentObject The name of the persistent object. + * @param string $persistentManagerName The object manager name (null for the default one). + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public function getRepository($persistentObject, $persistentManagerName = null); + + /** + * Gets the object manager associated with a given class. + * + * @param string $class A persistent object class name. + * + * @return \Doctrine\Common\Persistence\ObjectManager|null + */ + public function getManagerForClass($class); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php new file mode 100644 index 0000000..586b2da --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -0,0 +1,429 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Util\ClassUtils; +use ReflectionException; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping informations of a class which describes how a class should be mapped + * to a relational database. + * + * This class was abstracted from the ORM ClassMetadataFactory. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractClassMetadataFactory implements ClassMetadataFactory +{ + /** + * Salt used by specific Object Manager implementation. + * + * @var string + */ + protected $cacheSalt = '$CLASSMETADATA'; + + /** + * @var \Doctrine\Common\Cache\Cache|null + */ + private $cacheDriver; + + /** + * @var ClassMetadata[] + */ + private $loadedMetadata = []; + + /** + * @var bool + */ + protected $initialized = false; + + /** + * @var ReflectionService|null + */ + private $reflectionService = null; + + /** + * Sets the cache driver used by the factory to cache ClassMetadata instances. + * + * @param \Doctrine\Common\Cache\Cache $cacheDriver + * + * @return void + */ + public function setCacheDriver(Cache $cacheDriver = null) + { + $this->cacheDriver = $cacheDriver; + } + + /** + * Gets the cache driver used by the factory to cache ClassMetadata instances. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + /** + * Returns an array of all the loaded metadata currently in memory. + * + * @return ClassMetadata[] + */ + public function getLoadedMetadata() + { + return $this->loadedMetadata; + } + + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata() + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $driver = $this->getDriver(); + $metadata = []; + foreach ($driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + * + * @return void + */ + abstract protected function initialize(); + + /** + * Gets the fully qualified class-name from the namespace alias. + * + * @param string $namespaceAlias + * @param string $simpleClassName + * + * @return string + */ + abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); + + /** + * Returns the mapping driver implementation. + * + * @return \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakes up reflection after ClassMetadata gets unserialized from cache. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * + * @return void + */ + abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Initializes Reflection after ClassMetadata was constructed. + * + * @param ClassMetadata $class + * @param ReflectionService $reflService + * + * @return void + */ + abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Checks whether the class metadata is an entity. + * + * This method should return false for mapped superclasses or embedded classes. + * + * @param ClassMetadata $class + * + * @return boolean + */ + abstract protected function isEntity(ClassMetadata $class); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + * + * @throws ReflectionException + * @throws MappingException + */ + public function getMetadataFor($className) + { + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className, 2); + + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } else { + $realClassName = ClassUtils::getRealClass($className); + } + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + $loadingException = null; + + try { + if ($this->cacheDriver) { + if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) !== false) { + $this->loadedMetadata[$realClassName] = $cached; + + $this->wakeupReflection($cached, $this->getReflectionService()); + } else { + foreach ($this->loadMetadata($realClassName) as $loadedClassName) { + $this->cacheDriver->save( + $loadedClassName . $this->cacheSalt, + $this->loadedMetadata[$loadedClassName], + null + ); + } + } + } else { + $this->loadMetadata($realClassName); + } + } catch (MappingException $loadingException) { + if (! $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName)) { + throw $loadingException; + } + + $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; + } + + if ($className !== $realClassName) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + return $this->loadedMetadata[$className]; + } + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className) + { + return isset($this->loadedMetadata[$className]); + } + + /** + * Sets the metadata descriptor for a specific class. + * + * NOTE: This is only useful in very special cases, like when generating proxy classes. + * + * @param string $className + * @param ClassMetadata $class + * + * @return void + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Gets an array of parent classes for the given entity class. + * + * @param string $name + * + * @return array + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = []; + foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { + if ( ! $this->getDriver()->isTransient($parentClass)) { + $parentClasses[] = $parentClass; + } + } + return $parentClasses; + } + + /** + * Loads the metadata of the class in question and all it's ancestors whose metadata + * is still not loaded. + * + * Important: The class $name does not necesarily exist at this point here. + * Scenarios in a code-generation setup might have access to XML/YAML + * Mapping files without the actual PHP code existing here. That is why the + * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface + * should be used for reflection. + * + * @param string $name The name of the class for which the metadata should get loaded. + * + * @return array + */ + protected function loadMetadata($name) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + $loaded = []; + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = []; + $reflService = $this->getReflectionService(); + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + if ($this->isEntity($parent)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $reflService); + + $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if ($this->isEntity($class)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + + $this->wakeupReflection($class, $reflService); + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions + * + * Override this method to implement a fallback strategy for failed metadata loading + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null + */ + protected function onNotFoundMetadata($className) + { + return null; + } + + /** + * Actually loads the metadata from the underlying metadata. + * + * @param ClassMetadata $class + * @param ClassMetadata|null $parent + * @param bool $rootEntityFound + * @param array $nonSuperclassParents All parent class names + * that are not marked as mapped superclasses. + * + * @return void + */ + abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * + * @return ClassMetadata + */ + abstract protected function newClassMetadataInstance($className); + + /** + * {@inheritDoc} + */ + public function isTransient($class) + { + if ( ! $this->initialized) { + $this->initialize(); + } + + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + return $this->getDriver()->isTransient($class); + } + + /** + * Sets the reflectionService. + * + * @param ReflectionService $reflectionService + * + * @return void + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Gets the reflection service associated with this metadata factory. + * + * @return ReflectionService + */ + public function getReflectionService() + { + if ($this->reflectionService === null) { + $this->reflectionService = new RuntimeReflectionService(); + } + return $this->reflectionService; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php new file mode 100644 index 0000000..b8445f1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadata +{ + /** + * Gets the fully-qualified class name of this persistent class. + * + * @return string + */ + public function getName(); + + /** + * Gets the mapped identifier field name. + * + * The returned structure is an array of the identifier field names. + * + * @return array + */ + public function getIdentifier(); + + /** + * Gets the ReflectionClass instance for this mapped class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); + + /** + * Checks if the given field name is a mapped identifier for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isIdentifier($fieldName); + + /** + * Checks if the given field is a mapped property for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function hasField($fieldName); + + /** + * Checks if the given field is a mapped association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function hasAssociation($fieldName); + + /** + * Checks if the given field is a mapped single valued association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isSingleValuedAssociation($fieldName); + + /** + * Checks if the given field is a mapped collection valued association for this class. + * + * @param string $fieldName + * + * @return boolean + */ + public function isCollectionValuedAssociation($fieldName); + + /** + * A numerically indexed list of field names of this persistent class. + * + * This array includes identifier fields if present on this class. + * + * @return array + */ + public function getFieldNames(); + + /** + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + public function getIdentifierFieldNames(); + + /** + * Returns a numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + public function getAssociationNames(); + + /** + * Returns a type name of this field. + * + * This type names can be implementation specific but should at least include the php types: + * integer, string, boolean, float/double, datetime. + * + * @param string $fieldName + * + * @return string + */ + public function getTypeOfField($fieldName); + + /** + * Returns the target class name of the given association. + * + * @param string $assocName + * + * @return string + */ + public function getAssociationTargetClass($assocName); + + /** + * Checks if the association is the inverse side of a bidirectional association. + * + * @param string $assocName + * + * @return boolean + */ + public function isAssociationInverseSide($assocName); + + /** + * Returns the target field of the owning side of the association. + * + * @param string $assocName + * + * @return string + */ + public function getAssociationMappedByTargetField($assocName); + + /** + * Returns the identifier of this object as an array with field name as key. + * + * Has to return an empty array if no identifier isset. + * + * @param object $object + * + * @return array + */ + public function getIdentifierValues($object); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php new file mode 100644 index 0000000..3d82881 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Contract for a Doctrine persistence layer ClassMetadata class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ClassMetadataFactory +{ + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return array The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata(); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + */ + public function getMetadataFor($className); + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * + * @return boolean TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className); + + /** + * Sets the metadata descriptor for a specific class. + * + * @param string $className + * + * @param ClassMetadata $class + */ + public function setMetadataFor($className, $class); + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped directly or as a MappedSuperclass. + * + * @param string $className + * + * @return boolean + */ + public function isTransient($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 0000000..deb82c0 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,256 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class AnnotationDriver implements MappingDriver +{ + /** + * The AnnotationReader. + * + * @var AnnotationReader + */ + protected $reader; + + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = []; + + /** + * The paths excluded from path where to look for mapping files. + * + * @var array + */ + protected $excludePaths = []; + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Cache for AnnotationDriver#getAllClassNames(). + * + * @var array|null + */ + protected $classNames; + + /** + * Name of the entity annotations as keys. + * + * @var array + */ + protected $entityAnnotationClasses = []; + + /** + * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading + * docblock annotations. + * + * @param AnnotationReader $reader The AnnotationReader to use, duck-typed. + * @param string|array|null $paths One or multiple paths where mapping classes can be found. + */ + public function __construct($reader, $paths = null) + { + $this->reader = $reader; + if ($paths) { + $this->addPaths((array) $paths); + } + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Append exclude lookup paths to metadata driver. + * + * @param array $paths + */ + public function addExcludePaths(array $paths) + { + $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); + } + + /** + * Retrieve the defined metadata lookup exclude paths. + * + * @return array + */ + public function getExcludePaths() + { + return $this->excludePaths; + } + + /** + * Retrieve the current annotation reader + * + * @return AnnotationReader + */ + public function getReader() + { + return $this->reader; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Returns whether the class with the specified name is transient. Only non-transient + * classes, that is entities and mapped superclasses, should have their metadata loaded. + * + * A class is non-transient if it is annotated with an annotation + * from the {@see AnnotationDriver::entityAnnotationClasses}. + * + * @param string $className + * + * @return boolean + */ + public function isTransient($className) + { + $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); + + foreach ($classAnnotations as $annot) { + if (isset($this->entityAnnotationClasses[get_class($annot)])) { + return false; + } + } + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (!$this->paths) { + throw MappingException::pathRequired(); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RegexIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + '/^.+' . preg_quote($this->fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = $file[0]; + + if ( ! preg_match('(^phar:)i', $sourceFile)) { + $sourceFile = realpath($sourceFile); + } + + foreach ($this->excludePaths as $excludePath) { + $exclude = str_replace('\\', '/', realpath($excludePath)); + $current = str_replace('\\', '/', $sourceFile); + + if (strpos($current, $exclude) !== false) { + continue 2; + } + } + + require_once $sourceFile; + + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->classNames = $classes; + + return $classes; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php new file mode 100644 index 0000000..6ed7f6d --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,173 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Locates the file that contains the metadata information for a given class name. + * + * This behavior is independent of the actual content of the file. It just detects + * the file which is responsible for the given class name. + * + * @author Benjamin Eberlei + * @author Johannes M. Schmitt + */ +class DefaultFileLocator implements FileLocator +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = []; + + /** + * The file extension of mapping documents. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array $paths One or multiple paths where mapping documents can be found. + * @param string|null $fileExtension The file extension of mapping documents, usually prefixed with a dot. + */ + public function __construct($paths, $fileExtension = null) + { + $this->addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string|null + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string|null $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename) + { + $classes = []; + + if ($this->paths) { + foreach ($this->paths as $path) { + if ( ! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ((array) $this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php new file mode 100644 index 0000000..dc799d5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php @@ -0,0 +1,214 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * Base driver for file-based metadata drivers. + * + * A file driver operates in a mode where it loads the mapping files of individual + * classes on demand. This requires the user to adhere to the convention of 1 mapping + * file per class and the file names of the mapping files must correspond to the full + * class name, including namespace, with the namespace delimiters '\', replaced by dots '.'. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +abstract class FileDriver implements MappingDriver +{ + /** + * @var FileLocator + */ + protected $locator; + + /** + * @var array|null + */ + protected $classCache; + + /** + * @var string|null + */ + protected $globalBasename; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array|FileLocator $locator A FileLocator or one/multiple paths + * where mapping documents can be found. + * @param string|null $fileExtension + */ + public function __construct($locator, $fileExtension = null) + { + if ($locator instanceof FileLocator) { + $this->locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array)$locator, $fileExtension); + } + } + + /** + * Sets the global basename. + * + * @param string $file + * + * @return void + */ + public function setGlobalBasename($file) + { + $this->globalBasename = $file; + } + + /** + * Retrieves the global basename. + * + * @return string|null + */ + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Gets the element of schema meta data for the class from the mapping file. + * This will lazily load the mapping file if it is not loaded yet. + * + * @param string $className + * + * @return array The element of schema meta data. + * + * @throws MappingException + */ + public function getElement($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return $this->classCache[$className]; + } + + $result = $this->loadMappingFile($this->locator->findMappingFile($className)); + if (!isset($result[$className])) { + throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->locator->getFileExtension()); + } + + return $result[$className]; + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return !$this->locator->fileExists($className); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (! $this->classCache) { + return (array) $this->locator->getAllClassNames($this->globalBasename); + } + + return array_merge( + array_keys($this->classCache), + (array) $this->locator->getAllClassNames($this->globalBasename) + ); + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding file driver elements. + * + * @param string $file The mapping file to load. + * + * @return array + */ + abstract protected function loadMappingFile($file); + + /** + * Initializes the class cache from all the global files. + * + * Using this feature adds a substantial performance hit to file drivers as + * more metadata has to be loaded into memory than might actually be + * necessary. This may not be relevant to scenarios where caching of + * metadata is in place, however hits very hard in scenarios where no + * caching is used. + * + * @return void + */ + protected function initialize() + { + $this->classCache = []; + if (null !== $this->globalBasename) { + foreach ($this->locator->getPaths() as $path) { + $file = $path.'/'.$this->globalBasename.$this->locator->getFileExtension(); + if (is_file($file)) { + $this->classCache = array_merge( + $this->classCache, + $this->loadMappingFile($file) + ); + } + } + } + } + + /** + * Retrieves the locator used to discover mapping files by className. + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Sets the locator used to discover mapping files by className. + * + * @param FileLocator $locator + */ + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php new file mode 100644 index 0000000..f622856 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +/** + * Locates the file that contains the metadata information for a given class name. + * + * This behavior is independent of the actual content of the file. It just detects + * the file which is responsible for the given class name. + * + * @author Benjamin Eberlei + * @author Johannes M. Schmitt + */ +interface FileLocator +{ + /** + * Locates mapping file for the given class name. + * + * @param string $className + * + * @return string + */ + public function findMappingFile($className); + + /** + * Gets all class names that are found with this file locator. + * + * @param string $globalBasename Passed to allow excluding the basename. + * + * @return array + */ + public function getAllClassNames($globalBasename); + + /** + * Checks if a file can be found for this class name. + * + * @param string $className + * + * @return bool + */ + public function fileExists($className); + + /** + * Gets all the paths that this file locator looks for mapping files. + * + * @return array + */ + public function getPaths(); + + /** + * Gets the file extension that mapping files are suffixed with. + * + * @return string + */ + public function getFileExtension(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php new file mode 100644 index 0000000..b5d7ec0 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Contract for metadata drivers. + * + * @since 2.2 + * @author Jonathan H. Wage + */ +interface MappingDriver +{ + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadata $metadata + * + * @return void + */ + public function loadMetadataForClass($className, ClassMetadata $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames(); + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a MappedSuperclass. + * + * @param string $className + * + * @return boolean + */ + public function isTransient($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php new file mode 100644 index 0000000..50d9785 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The DriverChain allows you to add multiple other mapping drivers for + * certain namespaces. + * + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class MappingDriverChain implements MappingDriver +{ + /** + * The default driver. + * + * @var MappingDriver|null + */ + private $defaultDriver; + + /** + * @var array + */ + private $drivers = []; + + /** + * Gets the default driver. + * + * @return MappingDriver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @param MappingDriver $driver + * + * @return void + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * Adds a nested driver. + * + * @param MappingDriver $nestedDriver + * @param string $namespace + * + * @return void + */ + public function addDriver(MappingDriver $nestedDriver, $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Gets the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $driver MappingDriver */ + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + return; + } + } + + if (null !== $this->defaultDriver) { + $this->defaultDriver->loadMetadataForClass($className, $metadata); + return; + } + + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $classNames = []; + $driverClasses = []; + + /* @var $driver MappingDriver */ + foreach ($this->drivers AS $namespace => $driver) { + $oid = spl_object_hash($driver); + + if (!isset($driverClasses[$oid])) { + $driverClasses[$oid] = $driver->getAllClassNames(); + } + + foreach ($driverClasses[$oid] AS $className) { + if (strpos($className, $namespace) === 0) { + $classNames[$className] = true; + } + } + } + + if (null !== $this->defaultDriver) { + foreach ($this->defaultDriver->getAllClassNames() as $className) { + $classNames[$className] = true; + } + } + + return array_keys($classNames); + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + /* @var $driver MappingDriver */ + foreach ($this->drivers AS $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + if ($this->defaultDriver !== null) { + return $this->defaultDriver->isTransient($className); + } + + return true; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php new file mode 100644 index 0000000..efffa6a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * The PHPDriver includes php files which just populate ClassMetadataInfo + * instances with plain PHP code. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class PHPDriver extends FileDriver +{ + /** + * @var ClassMetadata + */ + protected $metadata; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = null) + { + parent::__construct($locator, '.php'); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->metadata = $metadata; + + $this->loadMappingFile($this->locator->findMappingFile($className)); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + $metadata = $this->metadata; + include $file; + + return [$metadata->getName() => $metadata]; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 0000000..8e37e5a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,142 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The StaticPHPDriver calls a static loadMetadata() method on your entity + * classes where you can manually populate the ClassMetadata instance. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class StaticPHPDriver implements MappingDriver +{ + /** + * Paths of entity directories. + * + * @var array + */ + private $paths = []; + + /** + * Map of all class names. + * + * @var array + */ + private $classNames; + + /** + * Constructor. + * + * @param array|string $paths + */ + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + /** + * Adds paths. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $className::loadMetadata($metadata); + } + + /** + * {@inheritDoc} + * @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it? + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (!$this->paths) { + throw MappingException::pathRequired(); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if ($file->getBasename('.php') == $file->getBasename()) { + continue; + } + + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (in_array($sourceFile, $includedFiles) && !$this->isTransient($className)) { + $classes[] = $className; + } + } + + $this->classNames = $classes; + + return $classes; + } + + /** + * {@inheritdoc} + */ + public function isTransient($className) + { + return ! method_exists($className, 'loadMetadata'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php new file mode 100644 index 0000000..4588cfe --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,239 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\MappingException; + +/** + * The Symfony File Locator makes a simplifying assumptions compared + * to the DefaultFileLocator. By assuming paths only contain entities of a certain + * namespace the mapping files consists of the short classname only. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SymfonyFileLocator implements FileLocator +{ + /** + * The paths where to look for mapping files. + * + * @var array + */ + protected $paths = []; + + /** + * A map of mapping directory path to namespace prefix used to expand class shortnames. + * + * @var array + */ + protected $prefixes = []; + + /** + * File extension that is searched for. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Represents PHP namespace delimiters when looking for files + * + * @var string + */ + private $nsSeparator; + + /** + * Constructor. + * + * @param array $prefixes + * @param string|null $fileExtension + * @param string $nsSeparator String which would be used when converting FQCN to filename and vice versa. Should not be empty + */ + public function __construct(array $prefixes, $fileExtension = null, $nsSeparator = '.') + { + $this->addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + + if (empty($nsSeparator)) { + throw new \InvalidArgumentException('Namespace separator should not be empty'); + } + + $this->nsSeparator = (string) $nsSeparator; + } + + /** + * Adds Namespace Prefixes. + * + * @param array $prefixes + * + * @return void + */ + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + /** + * Gets Namespace Prefixes. + * + * @return array + */ + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className).$this->fileExtension; + foreach ($this->paths as $path) { + if (!isset($this->prefixes[$path])) { + // global namespace class + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return true; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', $this->nsSeparator).$this->fileExtension; + if (is_file($filename)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename = null) + { + $classes = []; + + if ($this->paths) { + foreach ((array) $this->paths as $path) { + if (!is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName == $file->getBasename() || $fileName == $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->prefixes[$path])) { + + // Calculate namespace suffix for given prefix as a relative path from basepath to file path + $nsSuffix = strtr( + substr(realpath($file->getPath()), strlen(realpath($path))), + $this->nsSeparator, + '\\' + ); + + $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' .str_replace($this->nsSeparator, '\\', $fileName); + } else { + $classes[] = str_replace($this->nsSeparator, '\\', $fileName); + } + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className).$this->fileExtension; + foreach ($this->paths as $path) { + if (!isset($this->prefixes[$path])) { + if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) { + return $path.DIRECTORY_SEPARATOR.$defaultFileName; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (0 !== strpos($className, $prefix.'\\')) { + continue; + } + + $filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', $this->nsSeparator ).$this->fileExtension; + if (is_file($filename)) { + return $filename; + } + } + + throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->fileExtension); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php new file mode 100644 index 0000000..6e97485 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.2 + */ +class MappingException extends \Exception +{ + /** + * @param string $className + * @param array $namespaces + * + * @return self + */ + public static function classNotFoundInNamespaces($className, $namespaces) + { + return new self("The class '" . $className . "' was not found in the ". + "chain configured namespaces " . implode(", ", $namespaces)); + } + + /** + * @return self + */ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + /** + * @param string|null $path + * + * @return self + */ + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + { + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return self + */ + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return self + */ + public static function invalidMappingFile($entityName, $fileName) + { + return new self("Invalid mapping file '$fileName' for class '$entityName'."); + } + + /** + * @param string $className + * + * @return self + */ + public static function nonExistingClass($className) + { + return new self("Class '$className' does not exist"); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php new file mode 100644 index 0000000..0088ed5 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php @@ -0,0 +1,87 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * Very simple reflection service abstraction. + * + * This is required inside metadata layers that may require either + * static or runtime reflection. + * + * @author Benjamin Eberlei + */ +interface ReflectionService +{ + /** + * Returns an array of the parent classes (not interfaces) for the given class. + * + * @param string $class + * + * @throws \Doctrine\Common\Persistence\Mapping\MappingException + * + * @return array + */ + public function getParentClasses($class); + + /** + * Returns the shortname of a class. + * + * @param string $class + * + * @return string + */ + public function getClassShortName($class); + + /** + * @param string $class + * + * @return string + */ + public function getClassNamespace($class); + + /** + * Returns a reflection class instance or null. + * + * @param string $class + * + * @return \ReflectionClass|null + */ + public function getClass($class); + + /** + * Returns an accessible property (setAccessible(true)) or null. + * + * @param string $class + * @param string $property + * + * @return \ReflectionProperty|null + */ + public function getAccessibleProperty($class, $property); + + /** + * Checks if the class have a public method with the given name. + * + * @param mixed $class + * @param mixed $method + * + * @return bool + */ + public function hasPublicMethod($class, $method); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php new file mode 100644 index 0000000..4598d5a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +use Doctrine\Common\Reflection\RuntimePublicReflectionProperty; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Service. + * + * @author Benjamin Eberlei + */ +class RuntimeReflectionService implements ReflectionService +{ + /** + * {@inheritDoc} + */ + public function getParentClasses($class) + { + if ( ! class_exists($class)) { + throw MappingException::nonExistingClass($class); + } + + return class_parents($class); + } + + /** + * {@inheritDoc} + */ + public function getClassShortName($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getShortName(); + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return new ReflectionClass($class); + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + $reflectionProperty = new ReflectionProperty($class, $property); + + if ($reflectionProperty->isPublic()) { + $reflectionProperty = new RuntimePublicReflectionProperty($class, $property); + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + try { + $reflectionMethod = new ReflectionMethod($class, $method); + } catch (ReflectionException $e) { + return false; + } + + return $reflectionMethod->isPublic(); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php new file mode 100644 index 0000000..7d01766 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\Common\Persistence\Mapping; + +/** + * PHP Runtime Reflection Service. + * + * @author Benjamin Eberlei + */ +class StaticReflectionService implements ReflectionService +{ + /** + * {@inheritDoc} + */ + public function getParentClasses($class) + { + return []; + } + + /** + * {@inheritDoc} + */ + public function getClassShortName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, "\\")+1); + } + return $className; + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($className) + { + $namespace = ''; + if (strpos($className, '\\') !== false) { + $namespace = strrev(substr( strrev($className), strpos(strrev($className), '\\')+1 )); + } + return $namespace; + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + return true; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php new file mode 100644 index 0000000..02208c9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManager.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectManager class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectManager +{ + /** + * Finds an object by its identifier. + * + * This is just a convenient shortcut for getRepository($className)->find($id). + * + * @param string $className The class name of the object to find. + * @param mixed $id The identity of the object to find. + * + * @return object The found object. + */ + public function find($className, $id); + + /** + * Tells the ObjectManager to make an instance managed and persistent. + * + * The object will be entered into the database as a result of the flush operation. + * + * NOTE: The persist operation always considers objects that are not yet known to + * this ObjectManager as NEW. Do not pass detached objects to the persist operation. + * + * @param object $object The instance to make managed and persistent. + * + * @return void + */ + public function persist($object); + + /** + * Removes an object instance. + * + * A removed object will be removed from the database as a result of the flush operation. + * + * @param object $object The object instance to remove. + * + * @return void + */ + public function remove($object); + + /** + * Merges the state of a detached object into the persistence context + * of this ObjectManager and returns the managed copy of the object. + * The object passed to merge will not become associated/managed with this ObjectManager. + * + * @param object $object + * + * @return object + */ + public function merge($object); + + /** + * Clears the ObjectManager. All objects that are currently managed + * by this ObjectManager become detached. + * + * @param string|null $objectName if given, only objects of this type will get detached. + * + * @return void + */ + public function clear($objectName = null); + + /** + * Detaches an object from the ObjectManager, causing a managed object to + * become detached. Unflushed changes made to the object if any + * (including removal of the object), will not be synchronized to the database. + * Objects which previously referenced the detached object will continue to + * reference it. + * + * @param object $object The object to detach. + * + * @return void + */ + public function detach($object); + + /** + * Refreshes the persistent state of an object from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $object The object to refresh. + * + * @return void + */ + public function refresh($object); + + /** + * Flushes all changes to objects that have been queued up to now to the database. + * This effectively synchronizes the in-memory state of managed objects with the + * database. + * + * @return void + */ + public function flush(); + + /** + * Gets the repository for a class. + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public function getRepository($className); + + /** + * Returns the ClassMetadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)). + * + * @param string $className + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata + */ + public function getClassMetadata($className); + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + public function getMetadataFactory(); + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects. + * + * @param object $obj + * + * @return void + */ + public function initializeObject($obj); + + /** + * Checks if the object is part of the current UnitOfWork and therefore managed. + * + * @param object $object + * + * @return bool + */ + public function contains($object); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php new file mode 100644 index 0000000..9bc248a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * Makes a Persistent Objects aware of its own object-manager. + * + * Using this interface the managing object manager and class metadata instances + * are injected into the persistent object after construction. This allows + * you to implement ActiveRecord functionality on top of the persistence-ignorance + * that Doctrine propagates. + * + * Word of Warning: This is a very powerful hook to change how you can work with your domain models. + * Using this hook will break the Single Responsibility Principle inside your Domain Objects + * and increase the coupling of database and objects. + * + * Every ObjectManager has to implement this functionality itself. + * + * @author Benjamin Eberlei + */ +interface ObjectManagerAware +{ + /** + * Injects responsible ObjectManager and the ClassMetadata into this persistent object. + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + * + * @return void + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php new file mode 100644 index 0000000..8946475 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php @@ -0,0 +1,140 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Base class to simplify ObjectManager decorators + * + * @license http://opensource.org/licenses/MIT MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +abstract class ObjectManagerDecorator implements ObjectManager +{ + /** + * @var ObjectManager + */ + protected $wrapped; + + /** + * {@inheritdoc} + */ + public function find($className, $id) + { + return $this->wrapped->find($className, $id); + } + + /** + * {@inheritdoc} + */ + public function persist($object) + { + return $this->wrapped->persist($object); + } + + /** + * {@inheritdoc} + */ + public function remove($object) + { + return $this->wrapped->remove($object); + } + + /** + * {@inheritdoc} + */ + public function merge($object) + { + return $this->wrapped->merge($object); + } + + /** + * {@inheritdoc} + */ + public function clear($objectName = null) + { + return $this->wrapped->clear($objectName); + } + + /** + * {@inheritdoc} + */ + public function detach($object) + { + return $this->wrapped->detach($object); + } + + /** + * {@inheritdoc} + */ + public function refresh($object) + { + return $this->wrapped->refresh($object); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + return $this->wrapped->flush(); + } + + /** + * {@inheritdoc} + */ + public function getRepository($className) + { + return $this->wrapped->getRepository($className); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata($className) + { + return $this->wrapped->getClassMetadata($className); + } + + /** + * {@inheritdoc} + */ + public function getMetadataFactory() + { + return $this->wrapped->getMetadataFactory(); + } + + /** + * {@inheritdoc} + */ + public function initializeObject($obj) + { + return $this->wrapped->initializeObject($obj); + } + + /** + * {@inheritdoc} + */ + public function contains($object) + { + return $this->wrapped->contains($object); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php new file mode 100644 index 0000000..f607219 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/ObjectRepository.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Contract for a Doctrine persistence layer ObjectRepository class to implement. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + * @author Jonathan Wage + */ +interface ObjectRepository +{ + /** + * Finds an object by its primary key / identifier. + * + * @param mixed $id The identifier. + * + * @return object The object. + */ + public function find($id); + + /** + * Finds all objects in the repository. + * + * @return array The objects. + */ + public function findAll(); + + /** + * Finds objects by a set of criteria. + * + * Optionally sorting and limiting details can be passed. An implementation may throw + * an UnexpectedValueException if certain values of the sorting or limiting details are + * not supported. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array The objects. + * + * @throws \UnexpectedValueException + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null); + + /** + * Finds a single object by a set of criteria. + * + * @param array $criteria The criteria. + * + * @return object The object. + */ + public function findOneBy(array $criteria); + + /** + * Returns the class name of the object managed by the repository. + * + * @return string + */ + public function getClassName(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php new file mode 100644 index 0000000..990642e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/PersistentObject.php @@ -0,0 +1,254 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; + +/** + * PersistentObject base class that implements getter/setter methods for all mapped fields and associations + * by overriding __call. + * + * This class is a forward compatible implementation of the PersistentObject trait. + * + * Limitations: + * + * 1. All persistent objects have to be associated with a single ObjectManager, multiple + * ObjectManagers are not supported. You can set the ObjectManager with `PersistentObject#setObjectManager()`. + * 2. Setters and getters only work if a ClassMetadata instance was injected into the PersistentObject. + * This is either done on `postLoad` of an object or by accessing the global object manager. + * 3. There are no hooks for setters/getters. Just implement the method yourself instead of relying on __call(). + * 4. Slower than handcoded implementations: An average of 7 method calls per access to a field and 11 for an association. + * 5. Only the inverse side associations get autoset on the owning side as well. Setting objects on the owning side + * will not set the inverse side associations. + * + * @example + * + * PersistentObject::setObjectManager($em); + * + * class Foo extends PersistentObject + * { + * private $id; + * } + * + * $foo = new Foo(); + * $foo->getId(); // method exists through __call + * + * @author Benjamin Eberlei + */ +abstract class PersistentObject implements ObjectManagerAware +{ + /** + * @var ObjectManager|null + */ + private static $objectManager = null; + + /** + * @var ClassMetadata|null + */ + private $cm = null; + + /** + * Sets the object manager responsible for all persistent object base classes. + * + * @param ObjectManager|null $objectManager + * + * @return void + */ + static public function setObjectManager(ObjectManager $objectManager = null) + { + self::$objectManager = $objectManager; + } + + /** + * @return ObjectManager|null + */ + static public function getObjectManager() + { + return self::$objectManager; + } + + /** + * Injects the Doctrine Object Manager. + * + * @param ObjectManager $objectManager + * @param ClassMetadata $classMetadata + * + * @return void + * + * @throws \RuntimeException + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) + { + if ($objectManager !== self::$objectManager) { + throw new \RuntimeException("Trying to use PersistentObject with different ObjectManager instances. " . + "Was PersistentObject::setObjectManager() called?"); + } + + $this->cm = $classMetadata; + } + + /** + * Sets a persistent fields value. + * + * @param string $field + * @param array $args + * + * @return void + * + * @throws \BadMethodCallException When no persistent field exists by that name. + * @throws \InvalidArgumentException When the wrong target object type is passed to an association. + */ + private function set($field, $args) + { + $this->initializeDoctrine(); + + if ($this->cm->hasField($field) && !$this->cm->isIdentifier($field)) { + $this->$field = $args[0]; + } else if ($this->cm->hasAssociation($field) && $this->cm->isSingleValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (!($args[0] instanceof $targetClass) && $args[0] !== null) { + throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'"); + } + $this->$field = $args[0]; + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->cm->getName()."'"); + } + } + + /** + * Gets a persistent field value. + * + * @param string $field + * + * @return mixed + * + * @throws \BadMethodCallException When no persistent field exists by that name. + */ + private function get($field) + { + $this->initializeDoctrine(); + + if ( $this->cm->hasField($field) || $this->cm->hasAssociation($field) ) { + return $this->$field; + } else { + throw new \BadMethodCallException("no field with name '".$field."' exists on '".$this->cm->getName()."'"); + } + } + + /** + * If this is an inverse side association, completes the owning side. + * + * @param string $field + * @param ClassMetadata $targetClass + * @param object $targetObject + * + * @return void + */ + private function completeOwningSide($field, $targetClass, $targetObject) + { + // add this object on the owning side as well, for obvious infinite recursion + // reasons this is only done when called on the inverse side. + if ($this->cm->isAssociationInverseSide($field)) { + $mappedByField = $this->cm->getAssociationMappedByTargetField($field); + $targetMetadata = self::$objectManager->getClassMetadata($targetClass); + + $setter = ($targetMetadata->isCollectionValuedAssociation($mappedByField) ? "add" : "set").$mappedByField; + $targetObject->$setter($this); + } + } + + /** + * Adds an object to a collection. + * + * @param string $field + * @param array $args + * + * @return void + * + * @throws \BadMethodCallException + * @throws \InvalidArgumentException + */ + private function add($field, $args) + { + $this->initializeDoctrine(); + + if ($this->cm->hasAssociation($field) && $this->cm->isCollectionValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (!($args[0] instanceof $targetClass)) { + throw new \InvalidArgumentException("Expected persistent object of type '".$targetClass."'"); + } + if (!($this->$field instanceof Collection)) { + $this->$field = new ArrayCollection($this->$field ?: []); + } + $this->$field->add($args[0]); + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("There is no method add".$field."() on ".$this->cm->getName()); + } + } + + /** + * Initializes Doctrine Metadata for this class. + * + * @return void + * + * @throws \RuntimeException + */ + private function initializeDoctrine() + { + if ($this->cm !== null) { + return; + } + + if (!self::$objectManager) { + throw new \RuntimeException("No runtime object manager set. Call PersistentObject#setObjectManager()."); + } + + $this->cm = self::$objectManager->getClassMetadata(get_class($this)); + } + + /** + * Magic methods. + * + * @param string $method + * @param array $args + * + * @return mixed + * + * @throws \BadMethodCallException + */ + public function __call($method, $args) + { + $command = substr($method, 0, 3); + $field = lcfirst(substr($method, 3)); + if ($command == "set") { + $this->set($field, $args); + } else if ($command == "get") { + return $this->get($field); + } else if ($command == "add") { + $this->add($field, $args); + } else { + throw new \BadMethodCallException("There is no method ".$method." on ".$this->cm->getName()); + } + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php new file mode 100644 index 0000000..3369eb9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Proxy.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\Common\Persistence; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @since 2.2 + */ +interface Proxy +{ + /** + * Marker for Proxy class names. + * + * @var string + */ + const MARKER = '__CG__'; + + /** + * Length of the proxy marker. + * + * @var integer + */ + const MARKER_LENGTH = 6; + + /** + * Initializes this proxy if its not yet initialized. + * + * Acts as a no-op if already initialized. + * + * @return void + */ + public function __load(); + + /** + * Returns whether this proxy is initialized or not. + * + * @return bool + */ + public function __isInitialized(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php new file mode 100644 index 0000000..1a59cd4 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Contract for classes that are potential listeners of a NotifyPropertyChanged + * implementor. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface PropertyChangedListener +{ + /** + * Notifies the listener of a property change. + * + * @param object $sender The object on which the property changed. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property that changed. + * @param mixed $newValue The new value of the property that changed. + * + * @return void + */ + function propertyChanged($sender, $propertyName, $oldValue, $newValue); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php new file mode 100644 index 0000000..7eb216b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php @@ -0,0 +1,248 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\OutOfBoundsException; +use Doctrine\Common\Util\ClassUtils; + +/** + * Abstract factory for proxy objects. + * + * @author Benjamin Eberlei + */ +abstract class AbstractProxyFactory +{ + /** + * Never autogenerate a proxy and rely that it was generated by some + * process before deployment. + * + * @var integer + */ + const AUTOGENERATE_NEVER = 0; + + /** + * Always generates a new proxy in every request. + * + * This is only sane during development. + * + * @var integer + */ + const AUTOGENERATE_ALWAYS = 1; + + /** + * Autogenerate the proxy class when the proxy file does not exist. + * + * This strategy causes a file exists call whenever any proxy is used the + * first time in a request. + * + * @var integer + */ + const AUTOGENERATE_FILE_NOT_EXISTS = 2; + + /** + * Generate the proxy classes using eval(). + * + * This strategy is only sane for development, and even then it gives me + * the creeps a little. + * + * @var integer + */ + const AUTOGENERATE_EVAL = 3; + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * @var \Doctrine\Common\Proxy\ProxyGenerator the proxy generator responsible for creating the proxy classes/files. + */ + private $proxyGenerator; + + /** + * @var bool Whether to automatically (re)generate proxy classes. + */ + private $autoGenerate; + + /** + * @var \Doctrine\Common\Proxy\ProxyDefinition[] + */ + private $definitions = []; + + /** + * @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory + * @param bool|int $autoGenerate + */ + public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) + { + $this->proxyGenerator = $proxyGenerator; + $this->metadataFactory = $metadataFactory; + $this->autoGenerate = (int)$autoGenerate; + } + + /** + * Gets a reference proxy instance for the entity of the given type and identified by + * the given identifier. + * + * @param string $className + * @param array $identifier + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\OutOfBoundsException + */ + public function getProxy($className, array $identifier) + { + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + $fqcn = $definition->proxyClassName; + $proxy = new $fqcn($definition->initializer, $definition->cloner); + + foreach ($definition->identifierFields as $idField) { + if (! isset($identifier[$idField])) { + throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); + } + + $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); + } + + return $proxy; + } + + /** + * Generates proxy classes for all given classes. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata[] $classes The classes (ClassMetadata instances) + * for which to generate proxies. + * @param string $proxyDir The target directory of the proxy classes. If not specified, the + * directory configured on the Configuration of the EntityManager used + * by this factory is used. + * @return int Number of generated proxies. + */ + public function generateProxyClasses(array $classes, $proxyDir = null) + { + $generated = 0; + + foreach ($classes as $class) { + if ($this->skipClass($class)) { + continue; + } + + $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); + + $this->proxyGenerator->generateProxyClass($class, $proxyFileName); + + $generated += 1; + } + + return $generated; + } + + /** + * Reset initialization/cloning logic for an un-initialized proxy + * + * @param \Doctrine\Common\Proxy\Proxy $proxy + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException + */ + public function resetUninitializedProxy(Proxy $proxy) + { + if ($proxy->__isInitialized()) { + throw InvalidArgumentException::unitializedProxyExpected($proxy); + } + + $className = ClassUtils::getClass($proxy); + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + + $proxy->__setInitializer($definition->initializer); + $proxy->__setCloner($definition->cloner); + + return $proxy; + } + + /** + * Get a proxy definition for the given class name. + * + * @param string $className + * + * @return ProxyDefinition + */ + private function getProxyDefinition($className) + { + $classMetadata = $this->metadataFactory->getMetadataFor($className); + $className = $classMetadata->getName(); // aliases and case sensitivity + + $this->definitions[$className] = $this->createProxyDefinition($className); + $proxyClassName = $this->definitions[$className]->proxyClassName; + + if ( ! class_exists($proxyClassName, false)) { + $fileName = $this->proxyGenerator->getProxyFileName($className); + + switch ($this->autoGenerate) { + case self::AUTOGENERATE_NEVER: + require $fileName; + break; + + case self::AUTOGENERATE_FILE_NOT_EXISTS: + if ( ! file_exists($fileName)) { + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + } + require $fileName; + break; + + case self::AUTOGENERATE_ALWAYS: + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + require $fileName; + break; + + case self::AUTOGENERATE_EVAL: + $this->proxyGenerator->generateProxyClass($classMetadata, false); + break; + } + } + + return $this->definitions[$className]; + } + + /** + * Determine if this class should be skipped during proxy generation. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $metadata + * + * @return bool + */ + abstract protected function skipClass(ClassMetadata $metadata); + + /** + * @param string $className + * + * @return ProxyDefinition + */ + abstract protected function createProxyDefinition($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php new file mode 100644 index 0000000..0aa930b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; + +/** + * Special Autoloader for Proxy classes, which are not PSR-0 compliant. + * + * @author Benjamin Eberlei + */ +class Autoloader +{ + /** + * Resolves proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name. + * 2. Remove namespace separators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param string $className + * + * @return string + * + * @throws InvalidArgumentException + */ + public static function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw InvalidArgumentException::notProxyClass($className, $proxyNamespace); + } + + $className = str_replace('\\', '', substr($className, strlen($proxyNamespace) + 1)); + + return $proxyDir . DIRECTORY_SEPARATOR . $className . '.php'; + } + + /** + * Registers and returns autoloader callback for the given proxy dir and namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param callable|null $notFoundCallback Invoked when the proxy file is not found. + * + * @return \Closure + * + * @throws InvalidArgumentException + */ + public static function register($proxyDir, $proxyNamespace, $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, '\\'); + + if ( ! (null === $notFoundCallback || is_callable($notFoundCallback))) { + throw InvalidArgumentException::invalidClassNotFoundCallback($notFoundCallback); + } + + $autoloader = function ($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + call_user_func($notFoundCallback, $proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + + spl_autoload_register($autoloader); + + return $autoloader; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..dff54d9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use Doctrine\Common\Persistence\Proxy; +use InvalidArgumentException as BaseInvalidArgumentException; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ProxyException +{ + /** + * @return self + */ + public static function proxyDirectoryRequired() + { + return new self('You must configure a proxy directory. See docs for details'); + } + + /** + * @param string $className + * @param string $proxyNamespace + * + * @return self + */ + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf('The class "%s" is not part of the proxy namespace "%s"', $className, $proxyNamespace)); + } + + /** + * @param string $name + * + * @return self + */ + public static function invalidPlaceholder($name) + { + return new self(sprintf('Provided placeholder for "%s" must be either a string or a valid callable', $name)); + } + + /** + * @return self + */ + public static function proxyNamespaceRequired() + { + return new self('You must configure a proxy namespace'); + } + + /** + * @param Proxy $proxy + * + * @return self + */ + public static function unitializedProxyExpected(Proxy $proxy) + { + return new self(sprintf('Provided proxy of type "%s" must not be initialized.', get_class($proxy))); + } + + /** + * @param mixed $callback + * + * @return self + */ + public static function invalidClassNotFoundCallback($callback) + { + $type = is_object($callback) ? get_class($callback) : gettype($callback); + + return new self(sprintf('Invalid \$notFoundCallback given: must be a callable, "%s" given', $type)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..3c7415c --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use OutOfBoundsException as BaseOutOfBoundsException; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + * @author Fredrik Wendel + */ +class OutOfBoundsException extends BaseOutOfBoundsException implements ProxyException +{ + /** + * @param string $className + * @param string $idField + * + * @return self + */ + public static function missingPrimaryKeyValue($className, $idField) + { + return new self(sprintf("Missing value for primary key %s on %s", $idField, $className)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php new file mode 100644 index 0000000..0d1ff14 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +/** + * Base exception interface for proxy exceptions. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +interface ProxyException +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php new file mode 100644 index 0000000..154b917 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\Common\Proxy\Exception; + +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Proxy Unexpected Value Exception. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException +{ + /** + * @param string $proxyDirectory + * + * @return self + */ + public static function proxyDirectoryNotWritable($proxyDirectory) + { + return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory)); + } + + /** + * @param string $className + * @param string $methodName + * @param string $parameterName + * @param \Exception $previous + * + * @return self + */ + public static function invalidParameterTypeHint($className, $methodName, $parameterName, \Exception $previous) + { + return new self( + sprintf( + 'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', + $parameterName, + $methodName, + $className + ), + 0, + $previous + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php new file mode 100644 index 0000000..bd2b088 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Closure; +use Doctrine\Common\Persistence\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @author Marco Pivetta + * @since 2.4 + */ +interface Proxy extends BaseProxy +{ + /** + * Marks the proxy as initialized or not. + * + * @param boolean $initialized + * + * @return void + */ + public function __setInitialized($initialized); + + /** + * Sets the initializer callback to be used when initializing the proxy. That + * initializer should accept 3 parameters: $proxy, $method and $params. Those + * are respectively the proxy object that is being initialized, the method name + * that triggered initialization and the parameters passed to that method. + * + * @param Closure|null $initializer + * + * @return void + */ + public function __setInitializer(Closure $initializer = null); + + /** + * Retrieves the initializer callback used to initialize the proxy. + * + * @see __setInitializer + * + * @return Closure|null + */ + public function __getInitializer(); + + /** + * Sets the callback to be used when cloning the proxy. That initializer should accept + * a single parameter, which is the cloned proxy instance itself. + * + * @param Closure|null $cloner + * + * @return void + */ + public function __setCloner(Closure $cloner = null); + + /** + * Retrieves the callback to be used when cloning the proxy. + * + * @see __setCloner + * + * @return Closure|null + */ + public function __getCloner(); + + /** + * Retrieves the list of lazy loaded properties for a given proxy + * + * @return array Keys are the property names, and values are the default values + * for those properties. + */ + public function __getLazyProperties(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php new file mode 100644 index 0000000..48b149a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +/** + * Definition structure how to create a proxy. + * + * @author Benjamin Eberlei + */ +class ProxyDefinition +{ + /** + * @var string + */ + public $proxyClassName; + + /** + * @var array + */ + public $identifierFields; + + /** + * @var \ReflectionProperty[] + */ + public $reflectionFields; + + /** + * @var callable + */ + public $initializer; + + /** + * @var callable + */ + public $cloner; + + /** + * @param string $proxyClassName + * @param array $identifierFields + * @param array $reflectionFields + * @param callable $initializer + * @param callable $cloner + */ + public function __construct($proxyClassName, array $identifierFields, array $reflectionFields, $initializer, $cloner) + { + $this->proxyClassName = $proxyClassName; + $this->identifierFields = $identifierFields; + $this->reflectionFields = $reflectionFields; + $this->initializer = $initializer; + $this->cloner = $cloner; + } +} + diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php new file mode 100644 index 0000000..925dcd1 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -0,0 +1,1037 @@ +. + */ + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\Common\Util\ClassUtils; + +/** + * This factory is used to generate proxy classes. + * It builds proxies from given parameters, a template and class metadata. + * + * @author Marco Pivetta + * @since 2.4 + */ +class ProxyGenerator +{ + /** + * Used to match very simple id methods that don't need + * to be decorated since the identifier is known. + */ + const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*{\s*return\s*\$this->%s;\s*})i'; + + /** + * The namespace that contains all proxy classes. + * + * @var string + */ + private $proxyNamespace; + + /** + * The directory that contains all proxy classes. + * + * @var string + */ + private $proxyDirectory; + + /** + * Map of callables used to fill in placeholders set in the template. + * + * @var string[]|callable[] + */ + protected $placeholders = [ + 'baseProxyInterface' => Proxy::class, + 'additionalProperties' => '', + ]; + + /** + * Template used as a blueprint to generate proxies. + * + * @var string + */ + protected $proxyClassTemplate = '; + +/** + * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR + */ +class extends \ implements \ +{ + /** + * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with + * three parameters, being respectively the proxy object to be initialized, the method that triggered the + * initialization process and an array of ordered parameters that were passed to that method. + * + * @see \Doctrine\Common\Persistence\Proxy::__setInitializer + */ + public $__initializer__; + + /** + * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object + * + * @see \Doctrine\Common\Persistence\Proxy::__setCloner + */ + public $__cloner__; + + /** + * @var boolean flag indicating if this object was already initialized + * + * @see \Doctrine\Common\Persistence\Proxy::__isInitialized + */ + public $__isInitialized__ = false; + + /** + * @var array properties to be lazy loaded, with keys being the property + * names and values being their default values + * + * @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties + */ + public static $lazyPropertiesDefaults = []; + + + + + + + + + + + + + + + + + + /** + * Forces initialization of the proxy + */ + public function __load() + { + $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitialized($initialized) + { + $this->__isInitialized__ = $initialized; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitializer(\Closure $initializer = null) + { + $this->__initializer__ = $initializer; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __getInitializer() + { + return $this->__initializer__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setCloner(\Closure $cloner = null) + { + $this->__cloner__ = $cloner; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific cloning logic + */ + public function __getCloner() + { + return $this->__cloner__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + * @static + */ + public function __getLazyProperties() + { + return self::$lazyPropertiesDefaults; + } + + +} +'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param string $proxyDirectory The directory to use for the proxy classes. It must exist. + * @param string $proxyNamespace The namespace to use for the proxy classes. + * + * @throws InvalidArgumentException + */ + public function __construct($proxyDirectory, $proxyNamespace) + { + if ( ! $proxyDirectory) { + throw InvalidArgumentException::proxyDirectoryRequired(); + } + + if ( ! $proxyNamespace) { + throw InvalidArgumentException::proxyNamespaceRequired(); + } + + $this->proxyDirectory = $proxyDirectory; + $this->proxyNamespace = $proxyNamespace; + } + + /** + * Sets a placeholder to be replaced in the template. + * + * @param string $name + * @param string|callable $placeholder + * + * @throws InvalidArgumentException + */ + public function setPlaceholder($name, $placeholder) + { + if ( ! is_string($placeholder) && ! is_callable($placeholder)) { + throw InvalidArgumentException::invalidPlaceholder($name); + } + + $this->placeholders[$name] = $placeholder; + } + + /** + * Sets the base template used to create proxy classes. + * + * @param string $proxyClassTemplate + */ + public function setProxyClassTemplate($proxyClassTemplate) + { + $this->proxyClassTemplate = (string) $proxyClassTemplate; + } + + /** + * Generates a proxy class file. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class. + * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. + * + * @throws UnexpectedValueException + */ + public function generateProxyClass(ClassMetadata $class, $fileName = false) + { + preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); + + $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); + $placeholders = []; + + foreach ($placeholderMatches as $placeholder => $name) { + $placeholders[$placeholder] = isset($this->placeholders[$name]) + ? $this->placeholders[$name] + : [$this, 'generate' . $name]; + } + + foreach ($placeholders as & $placeholder) { + if (is_callable($placeholder)) { + $placeholder = call_user_func($placeholder, $class); + } + } + + $proxyCode = strtr($this->proxyClassTemplate, $placeholders); + + if ( ! $fileName) { + $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); + + if ( ! class_exists($proxyClassName)) { + eval(substr($proxyCode, 5)); + } + + return; + } + + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + if ( ! is_writable($parentDirectory)) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + $tmpFileName = $fileName . '.' . uniqid('', true); + + file_put_contents($tmpFileName, $proxyCode); + @chmod($tmpFileName, 0664); + rename($tmpFileName, $fileName); + } + + /** + * Generates the proxy short class name to be used in the template. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateProxyShortClassName(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[0]); + } + + /** + * Generates the proxy namespace. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateNamespace(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[1]); + } + + /** + * Generates the original class name. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateClassName(ClassMetadata $class) + { + return ltrim($class->getName(), '\\'); + } + + /** + * Generates the array representation of lazy loaded public properties and their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateLazyPropertiesDefaults(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $values = []; + + foreach ($lazyPublicProperties as $key => $value) { + $values[] = var_export($key, true) . ' => ' . var_export($value, true); + } + + return implode(', ', $values); + } + + /** + * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateConstructorImpl(ClassMetadata $class) + { + $constructorImpl = <<<'EOT' + /** + * @param \Closure $initializer + * @param \Closure $cloner + */ + public function __construct($initializer = null, $cloner = null) + { + +EOT; + $toUnset = []; + + foreach ($this->getLazyLoadedPublicProperties($class) as $lazyPublicProperty => $unused) { + $toUnset[] = '$this->' . $lazyPublicProperty; + } + + $constructorImpl .= (empty($toUnset) ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") + . <<<'EOT' + + $this->__initializer__ = $initializer; + $this->__cloner__ = $cloner; + } +EOT; + + return $constructorImpl; + } + + /** + * Generates the magic getter invoked when lazy loaded public properties are requested. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicGet(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $reflectionClass = $class->getReflectionClass(); + $hasParentGet = false; + $returnReference = ''; + $inheritDoc = ''; + + if ($reflectionClass->hasMethod('__get')) { + $hasParentGet = true; + $inheritDoc = '{@inheritDoc}'; + + if ($reflectionClass->getMethod('__get')->returnsReference()) { + $returnReference = '& '; + } + } + + if (empty($lazyPublicProperties) && ! $hasParentGet) { + return ''; + } + + $magicGet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return $this->$name; + } + + +EOT; + } + + if ($hasParentGet) { + $magicGet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return parent::__get($name); + +EOT; + } else { + $magicGet .= <<<'EOT' + trigger_error(sprintf('Undefined property: %s::$%s', __CLASS__, $name), E_USER_NOTICE); + +EOT; + } + + $magicGet .= " }"; + + return $magicGet; + } + + /** + * Generates the magic setter (currently unused). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicSet(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $hasParentSet = $class->getReflectionClass()->hasMethod('__set'); + + if (empty($lazyPublicProperties) && ! $hasParentSet) { + return ''; + } + + $inheritDoc = $hasParentSet ? '{@inheritDoc}' : ''; + $magicSet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + $this->$name = $value; + + return; + } + + +EOT; + } + + if ($hasParentSet) { + $magicSet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + return parent::__set($name, $value); +EOT; + } else { + $magicSet .= " \$this->\$name = \$value;"; + } + + $magicSet .= "\n }"; + + return $magicSet; + } + + /** + * Generates the magic issetter invoked when lazy loaded public properties are checked against isset(). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicIsset(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset'); + + if (empty($lazyPublicProperties) && ! $hasParentIsset) { + return ''; + } + + $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : ''; + $magicIsset = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return isset($this->$name); + } + + +EOT; + } + + if ($hasParentIsset) { + $magicIsset .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return parent::__isset($name); + +EOT; + } else { + $magicIsset .= " return false;"; + } + + return $magicIsset . "\n }"; + } + + /** + * Generates implementation for the `__sleep` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateSleepImpl(ClassMetadata $class) + { + $hasParentSleep = $class->getReflectionClass()->hasMethod('__sleep'); + $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : ''; + $sleepImpl = <<__isInitialized__) { + $properties = array_diff($properties, array_keys($this->__getLazyProperties())); + } + + return $properties; + } +EOT; + } + + $allProperties = ['__isInitialized__']; + + /* @var $prop \ReflectionProperty */ + foreach ($class->getReflectionClass()->getProperties() as $prop) { + if ($prop->isStatic()) { + continue; + } + + $allProperties[] = $prop->isPrivate() + ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName() + : $prop->getName(); + } + + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $protectedProperties = array_diff($allProperties, $lazyPublicProperties); + + foreach ($allProperties as &$property) { + $property = var_export($property, true); + } + + foreach ($protectedProperties as &$property) { + $property = var_export($property, true); + } + + $allProperties = implode(', ', $allProperties); + $protectedProperties = implode(', ', $protectedProperties); + + return $sleepImpl . <<__isInitialized__) { + return [$allProperties]; + } + + return [$protectedProperties]; + } +EOT; + } + + /** + * Generates implementation for the `__wakeup` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateWakeupImpl(ClassMetadata $class) + { + $unsetPublicProperties = []; + $hasWakeup = $class->getReflectionClass()->hasMethod('__wakeup'); + + foreach (array_keys($this->getLazyLoadedPublicProperties($class)) as $lazyPublicProperty) { + $unsetPublicProperties[] = '$this->' . $lazyPublicProperty; + } + + $shortName = $this->generateProxyShortClassName($class); + $inheritDoc = $hasWakeup ? '{@inheritDoc}' : ''; + $wakeupImpl = <<__isInitialized__) { + \$this->__initializer__ = function ($shortName \$proxy) { + \$proxy->__setInitializer(null); + \$proxy->__setCloner(null); + + \$existingProperties = get_object_vars(\$proxy); + + foreach (\$proxy->__getLazyProperties() as \$property => \$defaultValue) { + if ( ! array_key_exists(\$property, \$existingProperties)) { + \$proxy->\$property = \$defaultValue; + } + } + }; + +EOT; + + if ( ! empty($unsetPublicProperties)) { + $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ");"; + } + + $wakeupImpl .= "\n }"; + + if ($hasWakeup) { + $wakeupImpl .= "\n parent::__wakeup();"; + } + + $wakeupImpl .= "\n }"; + + return $wakeupImpl; + } + + /** + * Generates implementation for the `__clone` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateCloneImpl(ClassMetadata $class) + { + $hasParentClone = $class->getReflectionClass()->hasMethod('__clone'); + $inheritDoc = $hasParentClone ? '{@inheritDoc}' : ''; + $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : ''; + + return <<__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []); +$callParentClone } +EOT; + } + + /** + * Generates decorated methods by picking those available in the parent class. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMethods(ClassMetadata $class) + { + $methods = ''; + $methodNames = []; + $reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC); + $skippedMethods = [ + '__sleep' => true, + '__clone' => true, + '__wakeup' => true, + '__get' => true, + '__set' => true, + '__isset' => true, + ]; + + foreach ($reflectionMethods as $method) { + $name = $method->getName(); + + if ( + $method->isConstructor() || + isset($skippedMethods[strtolower($name)]) || + isset($methodNames[$name]) || + $method->isFinal() || + $method->isStatic() || + ( ! $method->isPublic()) + ) { + continue; + } + + $methodNames[$name] = true; + $methods .= "\n /**\n" + . " * {@inheritDoc}\n" + . " */\n" + . ' public function '; + + if ($method->returnsReference()) { + $methods .= '&'; + } + + $methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')'; + $methods .= $this->getMethodReturnType($method); + $methods .= "\n" . ' {' . "\n"; + + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($name, 3)); + $fieldType = $class->getTypeOfField($identifier); + $cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' return ' . $cast . ' parent::' . $method->getName() . "();\n"; + $methods .= ' }' . "\n\n"; + } + + $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters())); + $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters())); + + $methods .= "\n \$this->__initializer__ " + . "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true) + . ", [" . $invokeParamsString . "]);" + . "\n\n return parent::" . $name . '(' . $callParamsString . ');' + . "\n" . ' }' . "\n"; + } + + return $methods; + } + + /** + * Generates the Proxy file name. + * + * @param string $className + * @param string $baseDirectory Optional base directory for proxy file name generation. + * If not specified, the directory configured on the Configuration of the + * EntityManager will be used by this factory. + * + * @return string + */ + public function getProxyFileName($className, $baseDirectory = null) + { + $baseDirectory = $baseDirectory ?: $this->proxyDirectory; + + return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER + . str_replace('\\', '', $className) . '.php'; + } + + /** + * Checks if the method is a short identifier getter. + * + * What does this mean? For proxy objects the identifier is already known, + * however accessing the getter for this identifier usually triggers the + * lazy loading, leading to a query that may not be necessary if only the + * ID is interesting for the userland code (for example in views that + * generate links to the entity, but do not display anything else). + * + * @param \ReflectionMethod $method + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return boolean + */ + private function isShortIdentifierGetter($method, ClassMetadata $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $startLine = $method->getStartLine(); + $endLine = $method->getEndLine(); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 + && substr($method->getName(), 0, 3) == 'get' + && in_array($identifier, $class->getIdentifier(), true) + && $class->hasField($identifier) + && (($endLine - $startLine) <= 4) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + + return false; + } + + /** + * Generates the list of public properties to be lazy loaded, with their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return mixed[] + */ + private function getLazyLoadedPublicProperties(ClassMetadata $class) + { + $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); + $properties = []; + + foreach ($class->getReflectionClass()->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + + if (($class->hasField($name) || $class->hasAssociation($name)) && ! $class->isIdentifier($name)) { + $properties[$name] = $defaultProperties[$name]; + } + } + + return $properties; + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter[] $parameters + * + * @return string + */ + private function buildParametersString(ClassMetadata $class, \ReflectionMethod $method, array $parameters) + { + $parameterDefinitions = []; + + /* @var $param \ReflectionParameter */ + foreach ($parameters as $param) { + $parameterDefinition = ''; + + if ($parameterType = $this->getParameterType($class, $method, $param)) { + $parameterDefinition .= $parameterType . ' '; + } + + if ($param->isPassedByReference()) { + $parameterDefinition .= '&'; + } + + if (method_exists($param, 'isVariadic') && $param->isVariadic()) { + $parameterDefinition .= '...'; + } + + $parameters[] = '$' . $param->getName(); + $parameterDefinition .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true); + } + + $parameterDefinitions[] = $parameterDefinition; + } + + return implode(', ', $parameterDefinitions); + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter $parameter + * + * @return string|null + */ + private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter) + { + + // We need to pick the type hint class too + if ($parameter->isArray()) { + return 'array'; + } + + if ($parameter->isCallable()) { + return 'callable'; + } + + if (method_exists($parameter, 'hasType') && $parameter->hasType() && $parameter->getType()->isBuiltin()) { + return (string) $parameter->getType(); + } + + try { + $parameterClass = $parameter->getClass(); + + if ($parameterClass) { + return '\\' . $parameterClass->getName(); + } + } catch (\ReflectionException $previous) { + throw UnexpectedValueException::invalidParameterTypeHint( + $class->getName(), + $method->getName(), + $parameter->getName(), + $previous + ); + } + + return null; + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForInvoke(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + return '$' . $parameter->getName(); + }, + $parameters + ); + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForParentCall(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + $name = ''; + + if (method_exists($parameter, 'isVariadic') && $parameter->isVariadic()) { + $name .= '...'; + } + + $name .= '$' . $parameter->getName(); + + return $name; + }, + $parameters + ); + } + + /** + * @Param \ReflectionMethod $method + * + * @return string + */ + private function getMethodReturnType(\ReflectionMethod $method) + { + if (! (method_exists($method, 'hasReturnType') && $method->hasReturnType())) { + return ''; + } + + $returnType = $method->getReturnType(); + + if ($returnType->isBuiltin()) { + return ': ' . $returnType; + } + + $nameLower = strtolower((string) $returnType); + + if ('self' === $nameLower) { + return ': \\' . $method->getDeclaringClass()->getName(); + } + + if ('parent' === $nameLower) { + return ': \\' . $method->getDeclaringClass()->getParentClass()->getName(); + } + + return ': \\' . (string) $returnType; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php new file mode 100644 index 0000000..639fd69 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ClassFinderInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +interface ClassFinderInterface +{ + /** + * Finds a class. + * + * @param string $class The name of the class. + * + * @return string|null The name of the class or NULL if not found. + */ + public function findFile($class); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php new file mode 100644 index 0000000..418bb0f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/Psr0FindFile.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +/** + * Finds a class in a PSR-0 structure. + * + * @author Karoly Negyesi + */ +class Psr0FindFile implements ClassFinderInterface +{ + /** + * The PSR-0 prefixes. + * + * @var array + */ + protected $prefixes; + + /** + * @param array $prefixes An array of prefixes. Each key is a PHP namespace and each value is + * a list of directories. + */ + public function __construct($prefixes) + { + $this->prefixes = $prefixes; + } + + /** + * {@inheritDoc} + */ + public function findFile($class) + { + $lastNsPos = strrpos($class, '\\'); + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $lastNsPos) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $lastNsPos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $lastNsPos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (is_file($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + } + + return null; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php new file mode 100644 index 0000000..3d970ee --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +interface ReflectionProviderInterface +{ + /** + * Gets the ReflectionClass equivalent for this class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); + + /** + * Gets the ReflectionMethod equivalent for this class. + * + * @param string $name + * + * @return \ReflectionMethod + */ + public function getReflectionMethod($name); + + /** + * Gets the ReflectionProperty equivalent for this class. + * + * @param string $name + * + * @return \ReflectionProperty + */ + public function getReflectionProperty($name); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php new file mode 100644 index 0000000..4e8ef53 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use Doctrine\Common\Proxy\Proxy; +use ReflectionProperty; + +/** + * PHP Runtime Reflection Public Property - special overrides for public properties. + * + * @author Marco Pivetta + * @since 2.4 + */ +class RuntimePublicReflectionProperty extends ReflectionProperty +{ + /** + * {@inheritDoc} + * + * Checks is the value actually exist before fetching it. + * This is to avoid calling `__get` on the provided $object if it + * is a {@see \Doctrine\Common\Proxy\Proxy}. + */ + public function getValue($object = null) + { + $name = $this->getName(); + + if ($object instanceof Proxy && ! $object->__isInitialized()) { + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + $val = isset($object->$name) ? $object->$name : null; + $object->__setInitializer($originalInitializer); + + return $val; + } + + return isset($object->$name) ? parent::getValue($object) : null; + } + + /** + * {@inheritDoc} + * + * Avoids triggering lazy loading via `__set` if the provided object + * is a {@see \Doctrine\Common\Proxy\Proxy}. + * @link https://bugs.php.net/bug.php?id=63463 + */ + public function setValue($object, $value = null) + { + if ( ! ($object instanceof Proxy && ! $object->__isInitialized())) { + parent::setValue($object, $value); + + return; + } + + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + parent::setValue($object, $value); + $object->__setInitializer($originalInitializer); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php new file mode 100644 index 0000000..2d0f5b0 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionClass.php @@ -0,0 +1,433 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionClass; +use ReflectionException; + +class StaticReflectionClass extends ReflectionClass +{ + /** + * The static reflection parser object. + * + * @var StaticReflectionParser + */ + private $staticReflectionParser; + + /** + * @param StaticReflectionParser $staticReflectionParser + */ + public function __construct(StaticReflectionParser $staticReflectionParser) + { + $this->staticReflectionParser = $staticReflectionParser; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->staticReflectionParser->getClassName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->staticReflectionParser->getDocComment(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->staticReflectionParser->getNamespaceName(); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public function getMethod($name) + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + /** + * {@inheritDoc} + */ + public function getProperty($name) + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + /** + * {@inheritDoc} + */ + public static function export($argument, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstants() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getDefaultProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaceNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaces() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getMethods($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParentClass() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getProperties($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticPropertyValue($name, $default = '') + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitAliases() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraits() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasMethod($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasProperty($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function implementsInterface($interface) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isCloneable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstance($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstantiable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInterface() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isIterateable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isSubclassOf($class) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isTrait() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstance($args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceArgs(array $args = []) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceWithoutConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setStaticPropertyValue($name, $value) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php new file mode 100644 index 0000000..8d5380b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php @@ -0,0 +1,362 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use ReflectionMethod; + +class StaticReflectionMethod extends ReflectionMethod +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the method. + * + * @var string + */ + protected $methodName; + + /** + * @param StaticReflectionParser $staticReflectionParser + * @param string $methodName + */ + public function __construct(StaticReflectionParser $staticReflectionParser, $methodName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->methodName = $methodName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->methodName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('method', $this->methodName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->getStaticReflectionParser()->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('method', $this->methodName); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosure($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getPrototype() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invoke($object, $parameter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invokeArgs($object, array $args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDestructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosureThis() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfRequiredParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticVariables() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isClosure() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDeprecated() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function returnsReference() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php new file mode 100644 index 0000000..c48e9ba --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php @@ -0,0 +1,307 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use Doctrine\Common\Annotations\TokenParser; +use ReflectionException; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Karoly Negyesi + */ +class StaticReflectionParser implements ReflectionProviderInterface +{ + /** + * The fully qualified class name. + * + * @var string + */ + protected $className; + + /** + * The short class name. + * + * @var string + */ + protected $shortClassName; + + /** + * Whether the caller only wants class annotations. + * + * @var boolean. + */ + protected $classAnnotationOptimize; + + /** + * Whether the parser has run. + * + * @var boolean + */ + protected $parsed = false; + + /** + * The namespace of the class. + * + * @var string + */ + protected $namespace = ''; + + /** + * The use statements of the class. + * + * @var array + */ + protected $useStatements = []; + + /** + * The docComment of the class. + * + * @var string + */ + protected $docComment = [ + 'class' => '', + 'property' => [], + 'method' => [] + ]; + + /** + * The name of the class this class extends, if any. + * + * @var string + */ + protected $parentClassName = ''; + + /** + * The parent PSR-0 Parser. + * + * @var \Doctrine\Common\Reflection\StaticReflectionParser + */ + protected $parentStaticReflectionParser; + + /** + * Parses a class residing in a PSR-0 hierarchy. + * + * @param string $className The full, namespaced class name. + * @param ClassFinderInterface $finder A ClassFinder object which finds the class. + * @param boolean $classAnnotationOptimize Only retrieve the class docComment. + * Presumes there is only one statement per line. + */ + public function __construct($className, $finder, $classAnnotationOptimize = false) + { + $this->className = ltrim($className, '\\'); + $lastNsPos = strrpos($this->className, '\\'); + + if ($lastNsPos !== false) { + $this->namespace = substr($this->className, 0, $lastNsPos); + $this->shortClassName = substr($this->className, $lastNsPos + 1); + } else { + $this->shortClassName = $this->className; + } + + $this->finder = $finder; + $this->classAnnotationOptimize = $classAnnotationOptimize; + } + + /** + * @return void + */ + protected function parse() + { + if ($this->parsed || !$fileName = $this->finder->findFile($this->className)) { + return; + } + $this->parsed = true; + $contents = file_get_contents($fileName); + if ($this->classAnnotationOptimize) { + if (preg_match("/\A.*^\s*((abstract|final)\s+)?class\s+{$this->shortClassName}\s+/sm", $contents, $matches)) { + $contents = $matches[0]; + } + } + $tokenParser = new TokenParser($contents); + $docComment = ''; + while ($token = $tokenParser->next(false)) { + if (is_array($token)) { + switch ($token[0]) { + case T_USE: + $this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement()); + break; + case T_DOC_COMMENT: + $docComment = $token[1]; + break; + case T_CLASS: + $this->docComment['class'] = $docComment; + $docComment = ''; + break; + case T_VAR: + case T_PRIVATE: + case T_PROTECTED: + case T_PUBLIC: + $token = $tokenParser->next(); + if ($token[0] === T_VARIABLE) { + $propertyName = substr($token[1], 1); + $this->docComment['property'][$propertyName] = $docComment; + continue 2; + } + if ($token[0] !== T_FUNCTION) { + // For example, it can be T_FINAL. + continue 2; + } + // No break. + case T_FUNCTION: + // The next string after function is the name, but + // there can be & before the function name so find the + // string. + while (($token = $tokenParser->next()) && $token[0] !== T_STRING); + $methodName = $token[1]; + $this->docComment['method'][$methodName] = $docComment; + $docComment = ''; + break; + case T_EXTENDS: + $this->parentClassName = $tokenParser->parseClass(); + $nsPos = strpos($this->parentClassName, '\\'); + $fullySpecified = false; + if ($nsPos === 0) { + $fullySpecified = true; + } else { + if ($nsPos) { + $prefix = strtolower(substr($this->parentClassName, 0, $nsPos)); + $postfix = substr($this->parentClassName, $nsPos); + } else { + $prefix = strtolower($this->parentClassName); + $postfix = ''; + } + foreach ($this->useStatements as $alias => $use) { + if ($alias == $prefix) { + $this->parentClassName = '\\' . $use . $postfix; + $fullySpecified = true; + } + } + } + if (!$fullySpecified) { + $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName; + } + break; + } + } + } + } + + /** + * @return StaticReflectionParser + */ + protected function getParentStaticReflectionParser() + { + if (empty($this->parentStaticReflectionParser)) { + $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder); + } + + return $this->parentStaticReflectionParser; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + /** + * @return string + */ + public function getNamespaceName() + { + return $this->namespace; + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return new StaticReflectionClass($this); + } + + /** + * {@inheritDoc} + */ + public function getReflectionMethod($methodName) + { + return new StaticReflectionMethod($this, $methodName); + } + + /** + * {@inheritDoc} + */ + public function getReflectionProperty($propertyName) + { + return new StaticReflectionProperty($this, $propertyName); + } + + /** + * Gets the use statements from this file. + * + * @return array + */ + public function getUseStatements() + { + $this->parse(); + + return $this->useStatements; + } + + /** + * Gets the doc comment. + * + * @param string $type The type: 'class', 'property' or 'method'. + * @param string $name The name of the property or method, not needed for 'class'. + * + * @return string The doc comment, empty string if none. + */ + public function getDocComment($type = 'class', $name = '') + { + $this->parse(); + + return $name ? $this->docComment[$type][$name] : $this->docComment[$type]; + } + + /** + * Gets the PSR-0 parser for the declaring class. + * + * @param string $type The type: 'property' or 'method'. + * @param string $name The name of the property or method. + * + * @return StaticReflectionParser A static reflection parser for the declaring class. + * + * @throws ReflectionException + */ + public function getStaticReflectionParserForDeclaringClass($type, $name) + { + $this->parse(); + if (isset($this->docComment[$type][$name])) { + return $this; + } + if (!empty($this->parentClassName)) { + return $this->getParentStaticReflectionParser()->getStaticReflectionParserForDeclaringClass($type, $name); + } + throw new ReflectionException('Invalid ' . $type . ' "' . $name . '"'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php new file mode 100644 index 0000000..7f6522b --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\Common\Reflection; + +use ReflectionException; +use ReflectionProperty; + +class StaticReflectionProperty extends ReflectionProperty +{ + /** + * The PSR-0 parser object. + * + * @var StaticReflectionParser + */ + protected $staticReflectionParser; + + /** + * The name of the property. + * + * @var string|null + */ + protected $propertyName; + + /** + * @param StaticReflectionParser $staticReflectionParser + * @param string|null $propertyName + */ + public function __construct(StaticReflectionParser $staticReflectionParser, $propertyName) + { + $this->staticReflectionParser = $staticReflectionParser; + $this->propertyName = $propertyName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->propertyName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', $this->propertyName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('property', $this->propertyName); + } + + /** + * @return array + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getValue($object = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDefault() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php new file mode 100644 index 0000000..49dc7bb --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Persistence\Proxy; + +/** + * Class and reflection related functionality for objects that + * might or not be proxy objects at the moment. + * + * @author Benjamin Eberlei + * @author Johannes Schmitt + */ +class ClassUtils +{ + /** + * Gets the real class name of a class name that could be a proxy. + * + * @param string $class + * + * @return string + */ + public static function getRealClass($class) + { + if (false === $pos = strrpos($class, '\\'.Proxy::MARKER.'\\')) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } + + /** + * Gets the real class name of an object (even if its a proxy). + * + * @param object $object + * + * @return string + */ + public static function getClass($object) + { + return self::getRealClass(get_class($object)); + } + + /** + * Gets the real parent class name of a class or object. + * + * @param string $className + * + * @return string + */ + public static function getParentClass($className) + { + return get_parent_class( self::getRealClass( $className ) ); + } + + /** + * Creates a new reflection class. + * + * @param string $class + * + * @return \ReflectionClass + */ + public static function newReflectionClass($class) + { + return new \ReflectionClass( self::getRealClass( $class ) ); + } + + /** + * Creates a new reflection object. + * + * @param object $object + * + * @return \ReflectionObject + */ + public static function newReflectionObject($object) + { + return self::newReflectionClass( self::getClass( $object ) ); + } + + /** + * Given a class name and a proxy namespace returns the proxy name. + * + * @param string $className + * @param string $proxyNamespace + * + * @return string + */ + public static function generateProxyClassName($className, $proxyNamespace) + { + return rtrim($proxyNamespace, '\\') . '\\'.Proxy::MARKER.'\\' . ltrim($className, '\\'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php new file mode 100644 index 0000000..7f67532 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -0,0 +1,158 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Persistence\Proxy; + +/** + * Static class containing most used debug methods. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Giorgio Sironi + */ +final class Debug +{ + /** + * Private constructor (prevents instantiation). + */ + private function __construct() + { + } + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @link http://xdebug.org/ + * + * @param mixed $var The variable to dump. + * @param integer $maxDepth The maximum nesting level for object properties. + * @param boolean $stripTags Whether output should strip HTML tags. + * @param boolean $echo Send the dumped value to the output buffer + * + * @return string + */ + public static function dump($var, $maxDepth = 2, $stripTags = true, $echo = true) + { + $html = ini_get('html_errors'); + + if ($html !== true) { + ini_set('html_errors', true); + } + + if (extension_loaded('xdebug')) { + ini_set('xdebug.var_display_max_depth', $maxDepth); + } + + $var = self::export($var, $maxDepth++); + + ob_start(); + var_dump($var); + + $dump = ob_get_contents(); + + ob_end_clean(); + + $dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', $html); + + if ($echo) { + echo $dumpText; + } + + return $dumpText; + } + + /** + * @param mixed $var + * @param int $maxDepth + * + * @return mixed + */ + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($var instanceof Collection) { + $var = $var->toArray(); + } + + if ($maxDepth) { + if (is_array($var)) { + $return = []; + + foreach ($var as $k => $v) { + $return[$k] = self::export($v, $maxDepth - 1); + } + } else if ($isObj) { + $return = new \stdclass(); + if ($var instanceof \DateTime) { + $return->__CLASS__ = "DateTime"; + $return->date = $var->format('c'); + $return->timezone = $var->getTimeZone()->getName(); + } else { + $reflClass = ClassUtils::newReflectionObject($var); + $return->__CLASS__ = ClassUtils::getClass($var); + + if ($var instanceof Proxy) { + $return->__IS_PROXY__ = true; + $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); + } + + if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) { + $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); + } + + foreach ($reflClass->getProperties() as $reflProperty) { + $name = $reflProperty->getName(); + + $reflProperty->setAccessible(true); + $return->$name = self::export($reflProperty->getValue($var), $maxDepth - 1); + } + } + } else { + $return = $var; + } + } else { + $return = is_object($var) ? get_class($var) + : (is_array($var) ? 'Array(' . count($var) . ')' : $var); + } + + return $return; + } + + /** + * Returns a string representation of an object. + * + * @param object $obj + * + * @return string + */ + public static function toString($obj) + { + return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php new file mode 100644 index 0000000..082dc78 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\Common\Util; + +use Doctrine\Common\Inflector\Inflector as BaseInflector; + +/** + * Doctrine inflector has static methods for inflecting text. + * + * Kept for backwards compatibility reasons, was moved to its own component. + */ +class Inflector extends BaseInflector +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Version.php b/vendor/doctrine/common/lib/Doctrine/Common/Version.php new file mode 100644 index 0000000..7baa654 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Version.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\Common; + +/** + * Class to store and retrieve the version of Doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.6.1'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * + * @return int -1 if older, 0 if it is the same, 1 if version passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/doctrine/dbal/LICENSE b/vendor/doctrine/dbal/LICENSE new file mode 100644 index 0000000..4a91f0b --- /dev/null +++ b/vendor/doctrine/dbal/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/dbal/README.md b/vendor/doctrine/dbal/README.md new file mode 100644 index 0000000..b995630 --- /dev/null +++ b/vendor/doctrine/dbal/README.md @@ -0,0 +1,14 @@ +# Doctrine DBAL + +Powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction. + +* Master: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=master)](http://travis-ci.org/doctrine/dbal) [![Dependency Status](https://www.versioneye.com/php/doctrine:dbal/dev-master/badge.png)](https://www.versioneye.com/php/doctrine:dbal/dev-master) +* 2.4: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.4)](http://travis-ci.org/doctrine/dbal) [![Dependency Status](https://www.versioneye.com/php/doctrine:dbal/2.4.2/badge.png)](https://www.versioneye.com/php/doctrine:dbal/2.4.2) +* 2.3: [![Build Status](https://secure.travis-ci.org/doctrine/dbal.png?branch=2.3)](http://travis-ci.org/doctrine/dbal) [![Dependency Status](https://www.versioneye.com/php/doctrine:dbal/2.3.4/badge.png)](https://www.versioneye.com/php/doctrine:dbal/2.3.4) + +## More resources: + +* [Website](http://www.doctrine-project.org/projects/dbal.html) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DBAL) +* [Downloads](http://github.com/doctrine/dbal/downloads) diff --git a/vendor/doctrine/dbal/SECURITY.md b/vendor/doctrine/dbal/SECURITY.md new file mode 100644 index 0000000..e18f0dd --- /dev/null +++ b/vendor/doctrine/dbal/SECURITY.md @@ -0,0 +1,14 @@ +Security +======== + +The Doctrine library is operating very close to your database and as such needs +to handle and make assumptions about SQL injection vulnerabilities. + +It is vital that you understand how Doctrine approaches security, because +we cannot protect you from SQL injection. + +Please read the documentation chapter on Security in Doctrine DBAL to +understand the assumptions we make. + +- [Latest security.rst page on Github](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst) +- [Security Page in rendered documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html) diff --git a/vendor/doctrine/dbal/UPGRADE.md b/vendor/doctrine/dbal/UPGRADE.md new file mode 100644 index 0000000..1d628ee --- /dev/null +++ b/vendor/doctrine/dbal/UPGRADE.md @@ -0,0 +1,244 @@ +# Upgrade to 2.5.1 + +## MINOR BC BREAK: Doctrine\DBAL\Schema\Table + +When adding indexes to ``Doctrine\DBAL\Schema\Table`` via ``addIndex()`` or ``addUniqueIndex()``, +duplicate indexes are not silently ignored/dropped anymore (based on semantics, not naming!). +Duplicate indexes are considered indexes that pass ``isFullfilledBy()`` or ``overrules()`` +in ``Doctrine\DBAL\Schema\Index``. +This is required to make the index renaming feature introduced in 2.5.0 work properly and avoid +issues in the ORM schema tool / DBAL schema manager which pretends users from updating +their schemas and migrate to DBAL 2.5.*. +Additionally it offers more flexibility in declaring indexes for the user and potentially fixes +related issues in the ORM. +With this change, the responsibility to decide which index is a "duplicate" is completely deferred +to the user. +Please also note that adding foreign key constraints to a table via ``addForeignKeyConstraint()``, +``addUnnamedForeignKeyConstraint()`` or ``addNamedForeignKeyConstraint()`` now first checks if an +appropriate index is already present and avoids adding an additional auto-generated one eventually. + +# Upgrade to 2.5 + +## BC BREAK: time type resets date fields to UNIX epoch + +When mapping `time` type field to PHP's `DateTime` instance all unused date fields are +reset to UNIX epoch (i.e. 1970-01-01). This might break any logic which relies on comparing +`DateTime` instances with date fields set to the current date. + +Use `!` format prefix (see http://php.net/manual/en/datetime.createfromformat.php) for parsing +time strings to prevent having different date fields when comparing user input and `DateTime` +instances as mapped by Doctrine. + +## BC BREAK: Doctrine\DBAL\Schema\Table + +The methods ``addIndex()`` and ``addUniqueIndex()`` in ``Doctrine\DBAL\Schema\Table`` +have an additional, optional parameter. If you override these methods, you should +add this new parameter to the declaration of your overridden methods. + +## BC BREAK: Doctrine\DBAL\Connection + +The visibility of the property ``$_platform`` in ``Doctrine\DBAL\Connection`` +was changed from protected to private. If you have subclassed ``Doctrine\DBAL\Connection`` +in your application and accessed ``$_platform`` directly, you have to change the code +portions to use ``getDatabasePlatform()`` instead to retrieve the underlying database +platform. +The reason for this change is the new automatic platform version detection feature, +which lazily evaluates the appropriate platform class to use for the underlying database +server version at runtime. +Please also note, that calling ``getDatabasePlatform()`` now needs to establish a connection +in order to evaluate the appropriate platform class if ``Doctrine\DBAL\Connection`` is not +already connected. Under the following circumstances, it is not possible anymore to retrieve +the platform instance from the connection object without having to do a real connect: + +1. ``Doctrine\DBAL\Connection`` was instantiated without the ``platform`` connection parameter. +2. ``Doctrine\DBAL\Connection`` was instantiated without the ``serverVersion`` connection parameter. +3. The underlying driver is "version aware" and can provide different platform instances + for different versions. +4. The underlying driver connection is "version aware" and can provide the database server + version without having to query for it. + +If one of the above conditions is NOT met, there is no need for ``Doctrine\DBAL\Connection`` +to do a connect when calling ``getDatabasePlatform()``. + +## datetime Type uses date_create() as fallback + +Before 2.5 the DateTime type always required a specific format, defined in +`$platform->getDateTimeFormatString()`, which could cause quite some troubles +on platforms that had various microtime precision formats. Starting with 2.5 +whenever the parsing of a date fails with the predefined platform format, +the `date_create()` function will be used to parse the date. + +This could cause some troubles when your date format is weird and not parsed +correctly by `date_create`, however since databases are rather strict on dates +there should be no problem. + +## Support for pdo_ibm driver removed + +The ``pdo_ibm`` driver is buggy and does not work well with Doctrine. Therefore it will no +longer be supported and has been removed from the ``Doctrine\DBAL\DriverManager`` drivers +map. It is highly encouraged to to use `ibm_db2` driver instead if you want to connect +to an IBM DB2 database as it is much more stable and secure. + +If for some reason you have to utilize the ``pdo_ibm`` driver you can still use the `driverClass` +connection parameter to explicitly specify the ``Doctrine\DBAL\Driver\PDOIbm\Driver`` class. +However be aware that you are doing this at your own risk and it will not be guaranteed that +Doctrine will work as expected. + +# Upgrade to 2.4 + +## Doctrine\DBAL\Schema\Constraint + +If you have custom classes that implement the constraint interface, you have to implement +an additional method ``getQuotedColumns`` now. This method is used to build proper constraint +SQL for columns that need to be quoted, like keywords reserved by the specific platform used. +The method has to return the same values as ``getColumns`` only that those column names that +need quotation have to be returned quoted for the given platform. + +# Upgrade to 2.3 + +## Oracle Session Init now sets Numeric Character + +Before 2.3 the Oracle Session Init did not care about the numeric character of the Session. +This could lead to problems on non english locale systems that required a comma as a floating +point seperator in Oracle. Since 2.3, using the Oracle Session Init on connection start the +client session will be altered to set the numeric character to ".,": + + ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,' + +See [DBAL-345](http://www.doctrine-project.org/jira/browse/DBAL-345) for more details. + +## Doctrine\DBAL\Connection and Doctrine\DBAL\Statement + +The query related methods including but not limited to executeQuery, exec, query, and executeUpdate +now wrap the driver exceptions such as PDOException with DBALException to add more debugging +information such as the executed SQL statement, and any bound parameters. + +If you want to retrieve the driver specific exception, you can retrieve it by calling the +``getPrevious()`` method on DBALException. + +Before: + + catch(\PDOException $ex) { + // ... + } + +After: + + catch(\Doctrine\DBAL\DBALException $ex) { + $pdoException = $ex->getPrevious(); + // ... + } + +## Doctrine\DBAL\Connection#setCharsetSQL() removed + +This method only worked on MySQL and it is considered unsafe on MySQL to use SET NAMES UTF-8 instead +of setting the charset directly on connection already. Replace this behavior with the +connection charset option: + +Before: + + $conn = DriverManager::getConnection(array(..)); + $conn->setCharset('UTF8'); + +After: + + $conn = DriverManager::getConnection(array('charset' => 'UTF8', ..)); + +## Doctrine\DBAL\Schema\Table#renameColumn() removed + +Doctrine\DBAL\Schema\Table#renameColumn() was removed, because it drops and recreates +the column instead. There is no fix available, because a schema diff +cannot reliably detect if a column was renamed or one column was created +and another one dropped. + +You should use explicit SQL ALTER TABLE statements to change columns names. + +## Schema Filter paths + +The Filter Schema assets expression is not wrapped in () anymore for the regexp automatically. + +Before: + + $config->setFilterSchemaAssetsExpression('foo'); + +After: + + $config->setFilterSchemaAssetsExpression('(foo)'); + +## Creating MySQL Tables now defaults to UTF-8 + +If you are creating a new MySQL Table through the Doctrine API, charset/collate are +now set to 'utf8'/'utf8_unicode_ci' by default. Previously the MySQL server defaults were used. + +# Upgrade to 2.2 + +## Doctrine\DBAL\Connection#insert and Doctrine\DBAL\Connection#update + +Both methods now accept an optional last parameter $types with binding types of the values passed. +This can potentially break child classes that have overwritten one of these methods. + +## Doctrine\DBAL\Connection#executeQuery + +Doctrine\DBAL\Connection#executeQuery() got a new last parameter "QueryCacheProfile $qcp" + +## Doctrine\DBAL\Driver\Statement split + +The Driver statement was split into a ResultStatement and the normal statement extending from it. +This separates the configuration and the retrieval API from a statement. + +## MsSql Platform/SchemaManager renamed + +The MsSqlPlatform was renamed to SQLServerPlatform, the MsSqlSchemaManager was renamed +to SQLServerSchemaManager. + +## Cleanup SQLServer Platform version mess + +DBAL 2.1 and before were actually only compatible to SQL Server 2008, not earlier versions. +Still other parts of the platform did use old features instead of newly introduced datatypes +in SQL Server 2005. Starting with DBAL 2.2 you can pick the Doctrine abstraction exactly +matching your SQL Server version. + +The PDO SqlSrv driver now uses the new `SQLServer2008Platform` as default platform. +This platform uses new features of SQL Server as of version 2008. This also includes a switch +in the used fields for "text" and "blob" field types to: + + "text" => "VARCHAR(MAX)" + "blob" => "VARBINARY(MAX)" + +Additionally `SQLServerPlatform` in DBAL 2.1 and before used "DATE", "TIME" and "DATETIME2" for dates. +This types are only available since version 2008 and the introduction of an explicit +SQLServer 2008 platform makes this dependency explicit. + +An `SQLServer2005Platform` was also introduced to differentiate the features between +versions 2003, earlier and 2005. + +With this change the `SQLServerPlatform` now throws an exception for using limit queries +with an offset, since SQLServer 2003 and lower do not support this feature. + +To use the old SQL Server Platform, because you are using SQL Server 2003 and below use +the following configuration code: + + use Doctrine\DBAL\DriverManager; + use Doctrine\DBAL\Platforms\SQLServerPlatform; + use Doctrine\DBAL\Platforms\SQLServer2005Platform; + + // You are using SQL Server 2003 or earlier + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + 'platform' => new SQLServerPlatform() + // .. additional parameters + )); + + // You are using SQL Server 2005 + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + 'platform' => new SQLServer2005Platform() + // .. additional parameters + )); + + // You are using SQL Server 2008 + $conn = DriverManager::getConnection(array( + 'driver' => 'pdo_sqlsrv', + // 2008 is default platform + // .. additional parameters + )); diff --git a/vendor/doctrine/dbal/bin/doctrine-dbal b/vendor/doctrine/dbal/bin/doctrine-dbal new file mode 100755 index 0000000..0531527 --- /dev/null +++ b/vendor/doctrine/dbal/bin/doctrine-dbal @@ -0,0 +1,4 @@ +#!/usr/bin/env php +. + */ + +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\DBAL\Tools\Console\ConsoleRunner; + +$files = array(__DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php'); +$loader = null; +$cwd = getcwd(); +$directories = array($cwd, $cwd . DIRECTORY_SEPARATOR . 'config'); +$configFile = null; + +foreach ($files as $file) { + if (file_exists($file)) { + $loader = require $file; + + break; + } +} + +if ( ! $loader) { + throw new RuntimeException('vendor/autoload.php could not be found. Did you run `php composer.phar install`?'); +} + +foreach ($directories as $directory) { + $configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php'; + + if (file_exists($configFile)) { + break; + } +} + +if ( ! file_exists($configFile)) { + ConsoleRunner::printCliConfigTemplate(); + + exit(1); +} + +if ( ! is_readable($configFile)) { + echo 'Configuration file [' . $configFile . '] does not have read permission.' . PHP_EOL; + + exit(1); +} + +$commands = array(); +$helperSet = require $configFile; + +if ( ! $helperSet instanceof HelperSet) { + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof HelperSet) { + $helperSet = $helperSetCandidate; + + break; + } + } +} + +ConsoleRunner::run($helperSet, $commands); diff --git a/vendor/doctrine/dbal/composer.json b/vendor/doctrine/dbal/composer.json new file mode 100644 index 0000000..049019e --- /dev/null +++ b/vendor/doctrine/dbal/composer.json @@ -0,0 +1,37 @@ +{ + "name": "doctrine/dbal", + "type": "library", + "description": "Database Abstraction Layer", + "keywords": ["dbal", "database", "persistence", "queryobject"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} + ], + "require": { + "php": ">=5.3.2", + "doctrine/common": ">=2.4,<2.7-dev" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": ["bin/doctrine-dbal"], + "autoload": { + "psr-0": { "Doctrine\\DBAL\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "archive": { + "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar"] + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php new file mode 100644 index 0000000..0752dda --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\ResultStatement; +use PDO; + +class ArrayStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var array + */ + private $data; + + /** + * @var integer + */ + private $columnCount = 0; + + /** + * @var integer + */ + private $num = 0; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param array $data + */ + public function __construct(array $data) + { + $this->data = $data; + if (count($data)) { + $this->columnCount = count($data[0]); + } + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + unset ($this->data); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->columnCount; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 !== null || $arg3 !== null) { + throw new \InvalidArgumentException("Caching layer does not support 2nd/3rd argument to setFetchMode()"); + } + + $this->defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + if (isset($this->data[$this->num])) { + $row = $this->data[$this->num++]; + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + if ($fetchMode === PDO::FETCH_ASSOC) { + return $row; + } elseif ($fetchMode === PDO::FETCH_NUM) { + return array_values($row); + } elseif ($fetchMode === PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } elseif ($fetchMode === PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for fetching result."); + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + + return $row[$columnIndex]; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php new file mode 100644 index 0000000..6b3b539 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/CacheException.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +/** + * @author Benjamin Eberlei + * @since 2.2 + */ +class CacheException extends \Doctrine\DBAL\DBALException +{ + /** + * @return \Doctrine\DBAL\Cache\CacheException + */ + static public function noCacheKey() + { + return new self("No cache key was set."); + } + + /** + * @return \Doctrine\DBAL\Cache\CacheException + */ + static public function noResultDriverConfigured() + { + return new self("Trying to cache a query but no result driver is configured."); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php new file mode 100644 index 0000000..330eec9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/QueryCacheProfile.php @@ -0,0 +1,141 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\Common\Cache\Cache; + +/** + * Query Cache Profile handles the data relevant for query caching. + * + * It is a value object, setter methods return NEW instances. + * + * @author Benjamin Eberlei + */ +class QueryCacheProfile +{ + /** + * @var \Doctrine\Common\Cache\Cache|null + */ + private $resultCacheDriver; + + /** + * @var integer + */ + private $lifetime = 0; + + /** + * @var string|null + */ + private $cacheKey; + + /** + * @param integer $lifetime + * @param string|null $cacheKey + * @param \Doctrine\Common\Cache\Cache|null $resultCache + */ + public function __construct($lifetime = 0, $cacheKey = null, Cache $resultCache = null) + { + $this->lifetime = $lifetime; + $this->cacheKey = $cacheKey; + $this->resultCacheDriver = $resultCache; + } + + /** + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getResultCacheDriver() + { + return $this->resultCacheDriver; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\Cache\CacheException + */ + public function getCacheKey() + { + if ($this->cacheKey === null) { + throw CacheException::noCacheKey(); + } + + return $this->cacheKey; + } + + /** + * Generates the real cache key from query, params and types. + * + * @param string $query + * @param array $params + * @param array $types + * + * @return array + */ + public function generateCacheKeys($query, $params, $types) + { + $realCacheKey = $query . "-" . serialize($params) . "-" . serialize($types); + // should the key be automatically generated using the inputs or is the cache key set? + if ($this->cacheKey === null) { + $cacheKey = sha1($realCacheKey); + } else { + $cacheKey = $this->cacheKey; + } + + return array($cacheKey, $realCacheKey); + } + + /** + * @param \Doctrine\Common\Cache\Cache $cache + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setResultCacheDriver(Cache $cache) + { + return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); + } + + /** + * @param string|null $cacheKey + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setCacheKey($cacheKey) + { + return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver); + } + + /** + * @param integer $lifetime + * + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function setLifetime($lifetime) + { + return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php new file mode 100644 index 0000000..0062255 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -0,0 +1,221 @@ +. + */ + +namespace Doctrine\DBAL\Cache; + +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\Common\Cache\Cache; +use PDO; + +/** + * Cache statement for SQL results. + * + * A result is saved in multiple cache keys, there is the originally specified + * cache key which is just pointing to result rows by key. The following things + * have to be ensured: + * + * 1. lifetime of the original key has to be longer than that of all the individual rows keys + * 2. if any one row key is missing the query has to be re-executed. + * + * Also you have to realize that the cache will load the whole result into memory at once to ensure 2. + * This means that the memory usage for cached results might increase by using this feature. + */ +class ResultCacheStatement implements \IteratorAggregate, ResultStatement +{ + /** + * @var \Doctrine\Common\Cache\Cache + */ + private $resultCache; + + /** + * + * @var string + */ + private $cacheKey; + + /** + * @var string + */ + private $realKey; + + /** + * @var integer + */ + private $lifetime; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $statement; + + /** + * Did we reach the end of the statement? + * + * @var boolean + */ + private $emptied = false; + + /** + * @var array + */ + private $data; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\Common\Cache\Cache $resultCache + * @param string $cacheKey + * @param string $realKey + * @param integer $lifetime + */ + public function __construct(Statement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime) + { + $this->statement = $stmt; + $this->resultCache = $resultCache; + $this->cacheKey = $cacheKey; + $this->realKey = $realKey; + $this->lifetime = $lifetime; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + $this->statement->closeCursor(); + if ($this->emptied && $this->data !== null) { + $data = $this->resultCache->fetch($this->cacheKey); + if ( ! $data) { + $data = array(); + } + $data[$this->realKey] = $this->data; + + $this->resultCache->save($this->cacheKey, $data, $this->lifetime); + unset($this->data); + } + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->statement->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + if ($this->data === null) { + $this->data = array(); + } + + $row = $this->statement->fetch(PDO::FETCH_ASSOC); + if ($row) { + $this->data[] = $row; + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($fetchMode == PDO::FETCH_ASSOC) { + return $row; + } elseif ($fetchMode == PDO::FETCH_NUM) { + return array_values($row); + } elseif ($fetchMode == PDO::FETCH_BOTH) { + return array_merge($row, array_values($row)); + } elseif ($fetchMode == PDO::FETCH_COLUMN) { + return reset($row); + } else { + throw new \InvalidArgumentException("Invalid fetch-style given for caching result."); + } + } + $this->emptied = true; + + return false; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (!isset($row[$columnIndex])) { + // TODO: verify this is correct behavior + return false; + } + + return $row[$columnIndex]; + } + + /** + * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer The number of rows. + */ + public function rowCount() + { + return $this->statement->rowCount(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php new file mode 100644 index 0000000..ec7c130 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Logging\SQLLogger; +use Doctrine\Common\Cache\Cache; + +/** + * Configuration container for the Doctrine DBAL. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @internal When adding a new configuration option just write a getter/setter + * pair and add the option to the _attributes array with a proper default value. + */ +class Configuration +{ + /** + * The attributes that are contained in the configuration. + * Values are default values. + * + * @var array + */ + protected $_attributes = array(); + + /** + * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. + * + * @param \Doctrine\DBAL\Logging\SQLLogger|null $logger + * + * @return void + */ + public function setSQLLogger(SQLLogger $logger = null) + { + $this->_attributes['sqlLogger'] = $logger; + } + + /** + * Gets the SQL logger that is used. + * + * @return \Doctrine\DBAL\Logging\SQLLogger|null + */ + public function getSQLLogger() + { + return isset($this->_attributes['sqlLogger']) ? + $this->_attributes['sqlLogger'] : null; + } + + /** + * Gets the cache driver implementation that is used for query result caching. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getResultCacheImpl() + { + return isset($this->_attributes['resultCacheImpl']) ? + $this->_attributes['resultCacheImpl'] : null; + } + + /** + * Sets the cache driver implementation that is used for query result caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setResultCacheImpl(Cache $cacheImpl) + { + $this->_attributes['resultCacheImpl'] = $cacheImpl; + } + + /** + * Sets the filter schema assets expression. + * + * Only include tables/sequences matching the filter expression regexp in + * schema instances generated for the active connection when calling + * {AbstractSchemaManager#createSchema()}. + * + * @param string $filterExpression + * + * @return void + */ + public function setFilterSchemaAssetsExpression($filterExpression) + { + $this->_attributes['filterSchemaAssetsExpression'] = $filterExpression; + } + + /** + * Returns filter schema assets expression. + * + * @return string|null + */ + public function getFilterSchemaAssetsExpression() + { + if (isset($this->_attributes['filterSchemaAssetsExpression'])) { + return $this->_attributes['filterSchemaAssetsExpression']; + } + + return null; + } + + /** + * Sets the default auto-commit mode for connections. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * @param boolean $autoCommit True to enable auto-commit mode; false to disable it. + * + * @see getAutoCommit + */ + public function setAutoCommit($autoCommit) + { + $this->_attributes['autoCommit'] = (boolean) $autoCommit; + } + + /** + * Returns the default auto-commit mode for connections. + * + * @return boolean True if auto-commit mode is enabled by default for connections, false otherwise. + * + * @see setAutoCommit + */ + public function getAutoCommit() + { + if (isset($this->_attributes['autoCommit'])) { + return $this->_attributes['autoCommit']; + } + + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php new file mode 100644 index 0000000..8612540 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php @@ -0,0 +1,1607 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Exception\InvalidArgumentException; +use PDO; +use Closure; +use Exception; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Cache\ResultCacheStatement; +use Doctrine\DBAL\Cache\QueryCacheProfile; +use Doctrine\DBAL\Cache\ArrayStatement; +use Doctrine\DBAL\Cache\CacheException; +use Doctrine\DBAL\Driver\PingableConnection; + +/** + * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like + * events, transaction isolation levels, configuration, emulated transaction nesting, + * lazy connecting and more. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + * @author Lukas Smith (MDB2 library) + * @author Benjamin Eberlei + */ +class Connection implements DriverConnection +{ + /** + * Constant for transaction isolation level READ UNCOMMITTED. + */ + const TRANSACTION_READ_UNCOMMITTED = 1; + + /** + * Constant for transaction isolation level READ COMMITTED. + */ + const TRANSACTION_READ_COMMITTED = 2; + + /** + * Constant for transaction isolation level REPEATABLE READ. + */ + const TRANSACTION_REPEATABLE_READ = 3; + + /** + * Constant for transaction isolation level SERIALIZABLE. + */ + const TRANSACTION_SERIALIZABLE = 4; + + /** + * Represents an array of ints to be expanded by Doctrine SQL parsing. + * + * @var integer + */ + const PARAM_INT_ARRAY = 101; + + /** + * Represents an array of strings to be expanded by Doctrine SQL parsing. + * + * @var integer + */ + const PARAM_STR_ARRAY = 102; + + /** + * Offset by which PARAM_* constants are detected as arrays of the param type. + * + * @var integer + */ + const ARRAY_PARAM_OFFSET = 100; + + /** + * The wrapped driver connection. + * + * @var \Doctrine\DBAL\Driver\Connection + */ + protected $_conn; + + /** + * @var \Doctrine\DBAL\Configuration + */ + protected $_config; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * @var \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + protected $_expr; + + /** + * Whether or not a connection has been established. + * + * @var boolean + */ + private $_isConnected = false; + + /** + * The current auto-commit mode of this connection. + * + * @var boolean + */ + private $autoCommit = true; + + /** + * The transaction nesting level. + * + * @var integer + */ + private $_transactionNestingLevel = 0; + + /** + * The currently active transaction isolation level. + * + * @var integer + */ + private $_transactionIsolationLevel; + + /** + * If nested transactions should use savepoints. + * + * @var boolean + */ + private $_nestTransactionsWithSavepoints = false; + + /** + * The parameters used during creation of the Connection instance. + * + * @var array + */ + private $_params = array(); + + /** + * The DatabasePlatform object that provides information about the + * database platform used by the connection. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The schema manager. + * + * @var \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + protected $_schemaManager; + + /** + * The used DBAL driver. + * + * @var \Doctrine\DBAL\Driver + */ + protected $_driver; + + /** + * Flag that indicates whether the current transaction is marked for rollback only. + * + * @var boolean + */ + private $_isRollbackOnly = false; + + /** + * @var integer + */ + protected $defaultFetchMode = PDO::FETCH_ASSOC; + + /** + * Initializes a new instance of the Connection class. + * + * @param array $params The connection parameters. + * @param \Doctrine\DBAL\Driver $driver The driver to use. + * @param \Doctrine\DBAL\Configuration|null $config The configuration, optional. + * @param \Doctrine\Common\EventManager|null $eventManager The event manager, optional. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, + EventManager $eventManager = null) + { + $this->_driver = $driver; + $this->_params = $params; + + if (isset($params['pdo'])) { + $this->_conn = $params['pdo']; + $this->_isConnected = true; + unset($this->_params['pdo']); + } + + // Create default config and event manager if none given + if ( ! $config) { + $config = new Configuration(); + } + + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + $this->_config = $config; + $this->_eventManager = $eventManager; + + $this->_expr = new Query\Expression\ExpressionBuilder($this); + + $this->autoCommit = $config->getAutoCommit(); + } + + /** + * Gets the parameters used during instantiation. + * + * @return array + */ + public function getParams() + { + return $this->_params; + } + + /** + * Gets the name of the database this Connection is connected to. + * + * @return string + */ + public function getDatabase() + { + return $this->_driver->getDatabase($this); + } + + /** + * Gets the hostname of the currently connected database. + * + * @return string|null + */ + public function getHost() + { + return isset($this->_params['host']) ? $this->_params['host'] : null; + } + + /** + * Gets the port of the currently connected database. + * + * @return mixed + */ + public function getPort() + { + return isset($this->_params['port']) ? $this->_params['port'] : null; + } + + /** + * Gets the username used by this connection. + * + * @return string|null + */ + public function getUsername() + { + return isset($this->_params['user']) ? $this->_params['user'] : null; + } + + /** + * Gets the password used by this connection. + * + * @return string|null + */ + public function getPassword() + { + return isset($this->_params['password']) ? $this->_params['password'] : null; + } + + /** + * Gets the DBAL driver instance. + * + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_driver; + } + + /** + * Gets the Configuration used by the Connection. + * + * @return \Doctrine\DBAL\Configuration + */ + public function getConfiguration() + { + return $this->_config; + } + + /** + * Gets the EventManager used by the Connection. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Gets the DatabasePlatform for the connection. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + if (null == $this->platform) { + $this->detectDatabasePlatform(); + } + + return $this->platform; + } + + /** + * Gets the ExpressionBuilder for the connection. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + public function getExpressionBuilder() + { + return $this->_expr; + } + + /** + * Establishes the connection with the database. + * + * @return boolean TRUE if the connection was successfully established, FALSE if + * the connection is already open. + */ + public function connect() + { + if ($this->_isConnected) return false; + + $driverOptions = isset($this->_params['driverOptions']) ? + $this->_params['driverOptions'] : array(); + $user = isset($this->_params['user']) ? $this->_params['user'] : null; + $password = isset($this->_params['password']) ? + $this->_params['password'] : null; + + $this->_conn = $this->_driver->connect($this->_params, $user, $password, $driverOptions); + $this->_isConnected = true; + + if (null === $this->platform) { + $this->detectDatabasePlatform(); + } + + if (false === $this->autoCommit) { + $this->beginTransaction(); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new Event\ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Detects and sets the database platform. + * + * Evaluates custom platform class and version in order to set the correct platform. + * + * @throws DBALException if an invalid platform was specified for this connection. + */ + private function detectDatabasePlatform() + { + if ( ! isset($this->_params['platform'])) { + $version = $this->getDatabasePlatformVersion(); + + if (null !== $version) { + $this->platform = $this->_driver->createDatabasePlatformForVersion($version); + } else { + $this->platform = $this->_driver->getDatabasePlatform(); + } + } elseif ($this->_params['platform'] instanceof Platforms\AbstractPlatform) { + $this->platform = $this->_params['platform']; + } else { + throw DBALException::invalidPlatformSpecified(); + } + + $this->platform->setEventManager($this->_eventManager); + } + + /** + * Returns the version of the related platform if applicable. + * + * Returns null if either the driver is not capable to create version + * specific platform instances, no explicit server version was specified + * or the underlying driver connection cannot determine the platform + * version without having to query it (performance reasons). + * + * @return string|null + */ + private function getDatabasePlatformVersion() + { + // Driver does not support version specific platforms. + if ( ! $this->_driver instanceof VersionAwarePlatformDriver) { + return null; + } + + // Explicit platform version requested (supersedes auto-detection). + if (isset($this->_params['serverVersion'])) { + return $this->_params['serverVersion']; + } + + // If not connected, we need to connect now to determine the platform version. + if (null === $this->_conn) { + $this->connect(); + } + + // Automatic platform version detection. + if ($this->_conn instanceof ServerInfoAwareConnection && + ! $this->_conn->requiresQueryForServerVersion() + ) { + return $this->_conn->getServerVersion(); + } + + // Unable to detect platform version. + return null; + } + + /** + * Returns the current auto-commit mode for this connection. + * + * @return boolean True if auto-commit mode is currently enabled for this connection, false otherwise. + * + * @see setAutoCommit + */ + public function isAutoCommit() + { + return true === $this->autoCommit; + } + + /** + * Sets auto-commit mode for this connection. + * + * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual + * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either + * the method commit or the method rollback. By default, new connections are in auto-commit mode. + * + * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is + * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. + * + * @param boolean $autoCommit True to enable auto-commit mode; false to disable it. + * + * @see isAutoCommit + */ + public function setAutoCommit($autoCommit) + { + $autoCommit = (boolean) $autoCommit; + + // Mode not changed, no-op. + if ($autoCommit === $this->autoCommit) { + return; + } + + $this->autoCommit = $autoCommit; + + // Commit all currently active transactions if any when switching auto-commit mode. + if (true === $this->_isConnected && 0 !== $this->_transactionNestingLevel) { + $this->commitAll(); + } + } + + /** + * Sets the fetch mode. + * + * @param integer $fetchMode + * + * @return void + */ + public function setFetchMode($fetchMode) + { + $this->defaultFetchMode = $fetchMode; + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as an associative array. + * + * @param string $statement The SQL query. + * @param array $params The query parameters. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchAssoc($statement, array $params = array(), array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetch(PDO::FETCH_ASSOC); + } + + /** + * Prepares and executes an SQL query and returns the first row of the result + * as a numerically indexed array. + * + * @param string $statement The SQL query to be executed. + * @param array $params The prepared statement params. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchArray($statement, array $params = array(), array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetch(PDO::FETCH_NUM); + } + + /** + * Prepares and executes an SQL query and returns the value of a single column + * of the first row of the result. + * + * @param string $statement The SQL query to be executed. + * @param array $params The prepared statement params. + * @param integer $column The 0-indexed column number to retrieve. + * @param array $types The query parameter types. + * + * @return mixed + */ + public function fetchColumn($statement, array $params = array(), $column = 0, array $types = array()) + { + return $this->executeQuery($statement, $params, $types)->fetchColumn($column); + } + + /** + * Whether an actual connection to the database is established. + * + * @return boolean + */ + public function isConnected() + { + return $this->_isConnected; + } + + /** + * Checks whether a transaction is currently active. + * + * @return boolean TRUE if a transaction is currently active, FALSE otherwise. + */ + public function isTransactionActive() + { + return $this->_transactionNestingLevel > 0; + } + + /** + * Executes an SQL DELETE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table on which to delete. + * @param array $identifier The deletion criteria. An associative array containing column-value pairs. + * @param array $types The types of identifiers. + * + * @return integer The number of affected rows. + * + * @throws InvalidArgumentException + */ + public function delete($tableExpression, array $identifier, array $types = array()) + { + if (empty($identifier)) { + throw InvalidArgumentException::fromEmptyCriteria(); + } + + $this->connect(); + + $criteria = array(); + + foreach (array_keys($identifier) as $columnName) { + $criteria[] = $columnName . ' = ?'; + } + + return $this->executeUpdate( + 'DELETE FROM ' . $tableExpression . ' WHERE ' . implode(' AND ', $criteria), + array_values($identifier), + is_string(key($types)) ? $this->extractTypeValues($identifier, $types) : $types + ); + } + + /** + * Closes the connection. + * + * @return void + */ + public function close() + { + $this->_conn = null; + + $this->_isConnected = false; + } + + /** + * Sets the transaction isolation level. + * + * @param integer $level The level to set. + * + * @return integer + */ + public function setTransactionIsolation($level) + { + $this->_transactionIsolationLevel = $level; + + return $this->executeUpdate($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); + } + + /** + * Gets the currently active transaction isolation level. + * + * @return integer The current transaction isolation level. + */ + public function getTransactionIsolation() + { + if (null === $this->_transactionIsolationLevel) { + $this->_transactionIsolationLevel = $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); + } + + return $this->_transactionIsolationLevel; + } + + /** + * Executes an SQL UPDATE statement on a table. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table to update quoted or unquoted. + * @param array $data An associative array containing column-value pairs. + * @param array $identifier The update criteria. An associative array containing column-value pairs. + * @param array $types Types of the merged $data and $identifier arrays in that order. + * + * @return integer The number of affected rows. + */ + public function update($tableExpression, array $data, array $identifier, array $types = array()) + { + $this->connect(); + $set = array(); + + foreach ($data as $columnName => $value) { + $set[] = $columnName . ' = ?'; + } + + if (is_string(key($types))) { + $types = $this->extractTypeValues(array_merge($data, $identifier), $types); + } + + $params = array_merge(array_values($data), array_values($identifier)); + + $sql = 'UPDATE ' . $tableExpression . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; + + return $this->executeUpdate($sql, $params, $types); + } + + /** + * Inserts a table row with specified data. + * + * Table expression and columns are not escaped and are not safe for user-input. + * + * @param string $tableExpression The expression of the table to insert data into, quoted or unquoted. + * @param array $data An associative array containing column-value pairs. + * @param array $types Types of the inserted data. + * + * @return integer The number of affected rows. + */ + public function insert($tableExpression, array $data, array $types = array()) + { + $this->connect(); + + if (empty($data)) { + return $this->executeUpdate('INSERT INTO ' . $tableExpression . ' ()' . ' VALUES ()'); + } + + return $this->executeUpdate( + 'INSERT INTO ' . $tableExpression . ' (' . implode(', ', array_keys($data)) . ')' . + ' VALUES (' . implode(', ', array_fill(0, count($data), '?')) . ')', + array_values($data), + is_string(key($types)) ? $this->extractTypeValues($data, $types) : $types + ); + } + + /** + * Extract ordered type list from two associate key lists of data and types. + * + * @param array $data + * @param array $types + * + * @return array + */ + private function extractTypeValues(array $data, array $types) + { + $typeValues = array(); + + foreach ($data as $k => $_) { + $typeValues[] = isset($types[$k]) + ? $types[$k] + : \PDO::PARAM_STR; + } + + return $typeValues; + } + + /** + * Quotes a string so it can be safely used as a table or column name, even if + * it is a reserved name. + * + * Delimiting style depends on the underlying database platform that is being used. + * + * NOTE: Just because you CAN use quoted identifiers does not mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The name to be quoted. + * + * @return string The quoted name. + */ + public function quoteIdentifier($str) + { + return $this->getDatabasePlatform()->quoteIdentifier($str); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param string|null $type The type of the parameter. + * + * @return string The quoted parameter. + */ + public function quote($input, $type = null) + { + $this->connect(); + + list($value, $bindingType) = $this->getBindingInfo($input, $type); + return $this->_conn->quote($value, $bindingType); + } + + /** + * Prepares and executes an SQL query and returns the result as an associative array. + * + * @param string $sql The SQL query. + * @param array $params The query parameters. + * @param array $types The query parameter types. + * + * @return array + */ + public function fetchAll($sql, array $params = array(), $types = array()) + { + return $this->executeQuery($sql, $params, $types)->fetchAll(); + } + + /** + * Prepares an SQL statement. + * + * @param string $statement The SQL statement to prepare. + * + * @return \Doctrine\DBAL\Driver\Statement The prepared statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function prepare($statement) + { + $this->connect(); + + try { + $stmt = new Statement($statement, $this); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query. + * + * If the query is parametrized, a prepared statement is used. + * If an SQLLogger is configured, the execution is logged. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param \Doctrine\DBAL\Cache\QueryCacheProfile|null $qcp The query cache profile, optional. + * + * @return \Doctrine\DBAL\Driver\Statement The executed statement. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + if ($qcp !== null) { + return $this->executeCacheQuery($query, $params, $types, $qcp); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + } else { + $stmt = $this->_conn->query($query); + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $stmt; + } + + /** + * Executes a caching query. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query, if any. + * @param array $types The types the previous parameters are in. + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $qcp The query cache profile. + * + * @return \Doctrine\DBAL\Driver\ResultStatement + * + * @throws \Doctrine\DBAL\Cache\CacheException + */ + public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp) + { + $resultCache = $qcp->getResultCacheDriver() ?: $this->_config->getResultCacheImpl(); + if ( ! $resultCache) { + throw CacheException::noResultDriverConfigured(); + } + + list($cacheKey, $realKey) = $qcp->generateCacheKeys($query, $params, $types); + + // fetch the row pointers entry + if ($data = $resultCache->fetch($cacheKey)) { + // is the real key part of this row pointers map or is the cache only pointing to other cache keys? + if (isset($data[$realKey])) { + $stmt = new ArrayStatement($data[$realKey]); + } elseif (array_key_exists($realKey, $data)) { + $stmt = new ArrayStatement(array()); + } + } + + if (!isset($stmt)) { + $stmt = new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime()); + } + + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * Executes an, optionally parametrized, SQL query and returns the result, + * applying a given projection/transformation function on each row of the result. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters, if any. + * @param \Closure $function The transformation function that is applied on each row. + * The function receives a single parameter, an array, that + * represents a row of the result set. + * + * @return array The projected result of the query. + */ + public function project($query, array $params, Closure $function) + { + $result = array(); + $stmt = $this->executeQuery($query, $params); + + while ($row = $stmt->fetch()) { + $result[] = $function($row); + } + + $stmt->closeCursor(); + + return $result; + } + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement + * + * @throws \Doctrine\DBAL\DBALException + */ + public function query() + { + $this->connect(); + + $args = func_get_args(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + try { + switch (func_num_args()) { + case 1: + $statement = $this->_conn->query($args[0]); + break; + case 2: + $statement = $this->_conn->query($args[0], $args[1]); + break; + default: + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + break; + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $args[0]); + } + + $statement->setFetchMode($this->defaultFetchMode); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters + * and returns the number of affected rows. + * + * This method supports PDO binding types as well as DBAL mapping types. + * + * @param string $query The SQL query. + * @param array $params The query parameters. + * @param array $types The parameter types. + * + * @return integer The number of affected rows. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($query, $params, $types); + } + + try { + if ($params) { + list($query, $params, $types) = SQLParserUtils::expandListParameters($query, $params, $types); + + $stmt = $this->_conn->prepare($query); + if ($types) { + $this->_bindTypedValues($stmt, $params, $types); + $stmt->execute(); + } else { + $stmt->execute($params); + } + $result = $stmt->rowCount(); + } else { + $result = $this->_conn->exec($query); + } + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Executes an SQL statement and return the number of affected rows. + * + * @param string $statement + * + * @return integer The number of affected rows. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function exec($statement) + { + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + if ($logger) { + $logger->startQuery($statement); + } + + try { + $result = $this->_conn->exec($statement); + } catch (\Exception $ex) { + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + } + + if ($logger) { + $logger->stopQuery(); + } + + return $result; + } + + /** + * Returns the current transaction nesting level. + * + * @return integer The nesting level. A value of 0 means there's no active transaction. + */ + public function getTransactionNestingLevel() + { + return $this->_transactionNestingLevel; + } + + /** + * Fetches the SQLSTATE associated with the last database operation. + * + * @return integer The last error code. + */ + public function errorCode() + { + $this->connect(); + + return $this->_conn->errorCode(); + } + + /** + * Fetches extended error information associated with the last database operation. + * + * @return array The last error information. + */ + public function errorInfo() + { + $this->connect(); + + return $this->_conn->errorInfo(); + } + + /** + * Returns the ID of the last inserted row, or the last value from a sequence object, + * depending on the underlying driver. + * + * Note: This method may not return a meaningful or consistent result across different drivers, + * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY + * columns or sequences. + * + * @param string|null $seqName Name of the sequence object from which the ID should be returned. + * + * @return string A string representation of the last inserted ID. + */ + public function lastInsertId($seqName = null) + { + $this->connect(); + + return $this->_conn->lastInsertId($seqName); + } + + /** + * Executes a function in a transaction. + * + * The function gets passed this Connection instance as an (optional) parameter. + * + * If an exception occurs during execution of the function or transaction commit, + * the transaction is rolled back and the exception re-thrown. + * + * @param \Closure $func The function to execute transactionally. + * + * @return void + * + * @throws \Exception + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $func($this); + $this->commit(); + } catch (Exception $e) { + $this->rollback(); + throw $e; + } + } + + /** + * Sets if nested transactions should use savepoints. + * + * @param boolean $nestTransactionsWithSavepoints + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + { + if ($this->_transactionNestingLevel > 0) { + throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); + } + + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; + } + + /** + * Gets if nested transactions should use savepoints. + * + * @return boolean + */ + public function getNestTransactionsWithSavepoints() + { + return $this->_nestTransactionsWithSavepoints; + } + + /** + * Returns the savepoint name to use for nested transactions are false if they are not supported + * "savepointFormat" parameter is not set + * + * @return mixed A string with the savepoint name or false. + */ + protected function _getNestedTransactionSavePointName() + { + return 'DOCTRINE2_SAVEPOINT_'.$this->_transactionNestingLevel; + } + + /** + * Starts a transaction by suspending auto-commit mode. + * + * @return void + */ + public function beginTransaction() + { + $this->connect(); + + ++$this->_transactionNestingLevel; + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"START TRANSACTION"'); + } + $this->_conn->beginTransaction(); + if ($logger) { + $logger->stopQuery(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"SAVEPOINT"'); + } + $this->createSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + } + + /** + * Commits the current transaction. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If the commit failed due to no active transaction or + * because the transaction was marked for rollback only. + */ + public function commit() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + if ($this->_isRollbackOnly) { + throw ConnectionException::commitFailedRollbackOnly(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"COMMIT"'); + } + $this->_conn->commit(); + if ($logger) { + $logger->stopQuery(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"RELEASE SAVEPOINT"'); + } + $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); + if ($logger) { + $logger->stopQuery(); + } + } + + --$this->_transactionNestingLevel; + + if (false === $this->autoCommit && 0 === $this->_transactionNestingLevel) { + $this->beginTransaction(); + } + } + + /** + * Commits all current nesting transactions. + */ + private function commitAll() + { + while (0 !== $this->_transactionNestingLevel) { + if (false === $this->autoCommit && 1 === $this->_transactionNestingLevel) { + // When in no auto-commit mode, the last nesting commit immediately starts a new transaction. + // Therefore we need to do the final commit here and then leave to avoid an infinite loop. + $this->commit(); + + return; + } + + $this->commit(); + } + } + + /** + * Cancels any database changes done during the current transaction. + * + * This method can be listened with onPreTransactionRollback and onTransactionRollback + * eventlistener methods. + * + * @throws \Doctrine\DBAL\ConnectionException If the rollback operation failed. + */ + public function rollBack() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + $this->connect(); + + $logger = $this->_config->getSQLLogger(); + + if ($this->_transactionNestingLevel == 1) { + if ($logger) { + $logger->startQuery('"ROLLBACK"'); + } + $this->_transactionNestingLevel = 0; + $this->_conn->rollback(); + $this->_isRollbackOnly = false; + if ($logger) { + $logger->stopQuery(); + } + + if (false === $this->autoCommit) { + $this->beginTransaction(); + } + } elseif ($this->_nestTransactionsWithSavepoints) { + if ($logger) { + $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); + } + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); + --$this->_transactionNestingLevel; + if ($logger) { + $logger->stopQuery(); + } + } else { + $this->_isRollbackOnly = true; + --$this->_transactionNestingLevel; + } + } + + /** + * Creates a new savepoint. + * + * @param string $savepoint The name of the savepoint to create. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function createSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->platform->createSavePoint($savepoint)); + } + + /** + * Releases the given savepoint. + * + * @param string $savepoint The name of the savepoint to release. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function releaseSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + if ($this->platform->supportsReleaseSavepoints()) { + $this->_conn->exec($this->platform->releaseSavePoint($savepoint)); + } + } + + /** + * Rolls back to the given savepoint. + * + * @param string $savepoint The name of the savepoint to rollback to. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException + */ + public function rollbackSavepoint($savepoint) + { + if ( ! $this->getDatabasePlatform()->supportsSavepoints()) { + throw ConnectionException::savepointsNotSupported(); + } + + $this->_conn->exec($this->platform->rollbackSavePoint($savepoint)); + } + + /** + * Gets the wrapped driver connection. + * + * @return \Doctrine\DBAL\Driver\Connection + */ + public function getWrappedConnection() + { + $this->connect(); + + return $this->_conn; + } + + /** + * Gets the SchemaManager that can be used to inspect or change the + * database schema through the connection. + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + if ( ! $this->_schemaManager) { + $this->_schemaManager = $this->_driver->getSchemaManager($this); + } + + return $this->_schemaManager; + } + + /** + * Marks the current transaction so that the only possible + * outcome for the transaction to be rolled back. + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If no transaction is active. + */ + public function setRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + $this->_isRollbackOnly = true; + } + + /** + * Checks whether the current transaction is marked for rollback only. + * + * @return boolean + * + * @throws \Doctrine\DBAL\ConnectionException If no transaction is active. + */ + public function isRollbackOnly() + { + if ($this->_transactionNestingLevel == 0) { + throw ConnectionException::noActiveTransaction(); + } + + return $this->_isRollbackOnly; + } + + /** + * Converts a given value to its database representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * + * @return mixed The converted value. + */ + public function convertToDatabaseValue($value, $type) + { + return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform()); + } + + /** + * Converts a given value to its PHP representation according to the conversion + * rules of a specific DBAL mapping type. + * + * @param mixed $value The value to convert. + * @param string $type The name of the DBAL mapping type. + * + * @return mixed The converted type. + */ + public function convertToPHPValue($value, $type) + { + return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform()); + } + + /** + * Binds a set of parameters, some or all of which are typed with a PDO binding type + * or DBAL mapping type, to a given statement. + * + * @param \Doctrine\DBAL\Driver\Statement $stmt The statement to bind the values to. + * @param array $params The map/list of named/positional parameters. + * @param array $types The parameter types (PDO binding types or DBAL mapping types). + * + * @return void + * + * @internal Duck-typing used on the $stmt parameter to support driver statements as well as + * raw PDOStatement instances. + */ + private function _bindTypedValues($stmt, array $params, array $types) + { + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($bindIndex, $value, $bindingType); + } else { + $stmt->bindValue($bindIndex, $value); + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value, $bindingType) = $this->getBindingInfo($value, $type); + $stmt->bindValue($name, $value, $bindingType); + } else { + $stmt->bindValue($name, $value); + } + } + } + } + + /** + * Gets the binding type of a given type. The given type can be a PDO or DBAL mapping type. + * + * @param mixed $value The value to bind. + * @param mixed $type The type to bind (PDO or DBAL). + * + * @return array [0] => the (escaped) value, [1] => the binding type. + */ + private function getBindingInfo($value, $type) + { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform()); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + + return array($value, $bindingType); + } + + /** + * Resolves the parameters to a format which can be displayed. + * + * @internal This is a purely internal method. If you rely on this method, you are advised to + * copy/paste the code as this method may change, or be removed without prior notice. + * + * @param array $params + * @param array $types + * + * @return array + */ + public function resolveParams(array $params, array $types) + { + $resolvedParams = array(); + + // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. + if (is_int(key($params))) { + // Positional parameters + $typeOffset = array_key_exists(0, $types) ? -1 : 0; + $bindIndex = 1; + foreach ($params as $value) { + $typeIndex = $bindIndex + $typeOffset; + if (isset($types[$typeIndex])) { + $type = $types[$typeIndex]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$bindIndex] = $value; + } else { + $resolvedParams[$bindIndex] = $value; + } + ++$bindIndex; + } + } else { + // Named parameters + foreach ($params as $name => $value) { + if (isset($types[$name])) { + $type = $types[$name]; + list($value,) = $this->getBindingInfo($value, $type); + $resolvedParams[$name] = $value; + } else { + $resolvedParams[$name] = $value; + } + } + } + + return $resolvedParams; + } + + /** + * Creates a new instance of a SQL query builder. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + public function createQueryBuilder() + { + return new Query\QueryBuilder($this); + } + + /** + * Ping the server + * + * When the server is not available the method returns FALSE. + * It is responsibility of the developer to handle this case + * and abort the request or reconnect manually: + * + * @example + * + * if ($conn->ping() === false) { + * $conn->close(); + * $conn->connect(); + * } + * + * It is undefined if the underlying driver attempts to reconnect + * or disconnect when the connection is not available anymore + * as long it returns TRUE when a reconnect succeeded and + * FALSE when the connection was dropped. + * + * @return bool + */ + public function ping() + { + $this->connect(); + + if ($this->_conn instanceof PingableConnection) { + return $this->_conn->ping(); + } + + try { + $this->query($this->getDatabasePlatform()->getDummySelectSQL()); + + return true; + } catch (DBALException $e) { + return false; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php new file mode 100644 index 0000000..86494b0 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/ConnectionException.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan H. Wage . + */ + +namespace Doctrine\DBAL\Connections; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Configuration; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; + +/** + * Master-Slave Connection + * + * Connection can be used with master-slave setups. + * + * Important for the understanding of this connection should be how and when + * it picks the slave or master. + * + * 1. Slave if master was never picked before and ONLY if 'getWrappedConnection' + * or 'executeQuery' is used. + * 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint', + * 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or + * 'prepare' is called. + * 3. If master was picked once during the lifetime of the connection it will always get picked afterwards. + * 4. One slave connection is randomly picked ONCE during a request. + * + * ATTENTION: You can write to the slave with this connection if you execute a write query without + * opening up a transaction. For example: + * + * $conn = DriverManager::getConnection(...); + * $conn->executeQuery("DELETE FROM table"); + * + * Be aware that Connection#executeQuery is a method specifically for READ + * operations only. + * + * This connection is limited to slave operations using the + * Connection#executeQuery operation only, because it wouldn't be compatible + * with the ORM or SchemaManager code otherwise. Both use all the other + * operations in a context where writes could happen to a slave, which makes + * this restricted approach necessary. + * + * You can manually connect to the master at any time by calling: + * + * $conn->connect('master'); + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection', + * 'driver' => 'pdo_mysql', + * 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'slaves' => array( + * array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ) + * )); + * + * You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information. + * + * @author Lars Strojny + * @author Benjamin Eberlei + */ +class MasterSlaveConnection extends Connection +{ + /** + * Master and slave connection (one of the randomly picked slaves). + * + * @var \Doctrine\DBAL\Driver\Connection[] + */ + protected $connections = array('master' => null, 'slave' => null); + + /** + * You can keep the slave connection and then switch back to it + * during the request if you know what you are doing. + * + * @var boolean + */ + protected $keepSlave = false; + + /** + * Creates Master Slave Connection. + * + * @param array $params + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration|null $config + * @param \Doctrine\Common\EventManager|null $eventManager + * + * @throws \InvalidArgumentException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['slaves']) || !isset($params['master'])) { + throw new \InvalidArgumentException('master or slaves configuration missing'); + } + if (count($params['slaves']) == 0) { + throw new \InvalidArgumentException('You have to configure at least one slaves.'); + } + + $params['master']['driver'] = $params['driver']; + foreach ($params['slaves'] as $slaveKey => $slave) { + $params['slaves'][$slaveKey]['driver'] = $params['driver']; + } + + $this->keepSlave = isset($params['keepSlave']) ? (bool) $params['keepSlave'] : false; + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Checks if the connection is currently towards the master or not. + * + * @return boolean + */ + public function isConnectedToMaster() + { + return $this->_conn !== null && $this->_conn === $this->connections['master']; + } + + /** + * {@inheritDoc} + */ + public function connect($connectionName = null) + { + $requestedConnectionChange = ($connectionName !== null); + $connectionName = $connectionName ?: 'slave'; + + if ($connectionName !== 'slave' && $connectionName !== 'master') { + throw new \InvalidArgumentException("Invalid option to connect(), only master or slave allowed."); + } + + // If we have a connection open, and this is not an explicit connection + // change request, then abort right here, because we are already done. + // This prevents writes to the slave in case of "keepSlave" option enabled. + if ($this->_conn && !$requestedConnectionChange) { + return false; + } + + $forceMasterAsSlave = false; + + if ($this->getTransactionNestingLevel() > 0) { + $connectionName = 'master'; + $forceMasterAsSlave = true; + } + + if ($this->connections[$connectionName]) { + $this->_conn = $this->connections[$connectionName]; + + if ($forceMasterAsSlave && ! $this->keepSlave) { + $this->connections['slave'] = $this->_conn; + } + + return false; + } + + if ($connectionName === 'master') { + // Set slave connection to master to avoid invalid reads + if ($this->connections['slave'] && ! $this->keepSlave) { + unset($this->connections['slave']); + } + + $this->connections['master'] = $this->_conn = $this->connectTo($connectionName); + + if ( ! $this->keepSlave) { + $this->connections['slave'] = $this->connections['master']; + } + } else { + $this->connections['slave'] = $this->_conn = $this->connectTo($connectionName); + } + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connects to a specific connection. + * + * @param string $connectionName + * + * @return \Doctrine\DBAL\Driver + */ + protected function connectTo($connectionName) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); + + $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + /** + * @param string $connectionName + * @param array $params + * + * @return mixed + */ + protected function chooseConnectionConfiguration($connectionName, $params) + { + if ($connectionName === 'master') { + return $params['master']; + } + + return $params['slaves'][array_rand($params['slaves'])]; + } + + /** + * {@inheritDoc} + */ + public function executeUpdate($query, array $params = array(), array $types = array()) + { + $this->connect('master'); + + return parent::executeUpdate($query, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->connect('master'); + + parent::beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->connect('master'); + + parent::commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + $this->connect('master'); + + return parent::rollBack(); + } + + /** + * {@inheritDoc} + */ + public function delete($tableName, array $identifier, array $types = array()) + { + $this->connect('master'); + + return parent::delete($tableName, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function close() + { + unset($this->connections['master']); + unset($this->connections['slave']); + + parent::close(); + + $this->_conn = null; + $this->connections = array('master' => null, 'slave' => null); + } + + /** + * {@inheritDoc} + */ + public function update($tableName, array $data, array $identifier, array $types = array()) + { + $this->connect('master'); + + return parent::update($tableName, $data, $identifier, $types); + } + + /** + * {@inheritDoc} + */ + public function insert($tableName, array $data, array $types = array()) + { + $this->connect('master'); + + return parent::insert($tableName, $data, $types); + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $this->connect('master'); + + return parent::exec($statement); + } + + /** + * {@inheritDoc} + */ + public function createSavepoint($savepoint) + { + $this->connect('master'); + + parent::createSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function releaseSavepoint($savepoint) + { + $this->connect('master'); + + parent::releaseSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function rollbackSavepoint($savepoint) + { + $this->connect('master'); + + parent::rollbackSavepoint($savepoint); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $this->connect('master'); + + $args = func_get_args(); + + $logger = $this->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($args[0]); + } + + $statement = call_user_func_array(array($this->_conn, 'query'), $args); + + if ($logger) { + $logger->stopQuery(); + } + + return $statement; + } + + /** + * {@inheritDoc} + */ + public function prepare($statement) + { + $this->connect('master'); + + return parent::prepare($statement); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php new file mode 100644 index 0000000..d85dec8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php @@ -0,0 +1,247 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Driver\ExceptionConverterDriver; + +class DBALException extends \Exception +{ + /** + * @param string $method + * + * @return \Doctrine\DBAL\DBALException + */ + public static function notSupported($method) + { + return new self("Operation '$method' is not supported by platform."); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidPlatformSpecified() + { + return new self( + "Invalid 'platform' option specified, need to give an instance of ". + "\Doctrine\DBAL\Platforms\AbstractPlatform."); + } + + /** + * Returns a new instance for an invalid specified platform version. + * + * @param string $version The invalid platform version given. + * @param string $expectedFormat The expected platform version format. + * + * @return DBALException + */ + public static function invalidPlatformVersionSpecified($version, $expectedFormat) + { + return new self( + sprintf( + 'Invalid platform version "%s" specified. ' . + 'The platform version has to be specified in the format: "%s".', + $version, + $expectedFormat + ) + ); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidPdoInstance() + { + return new self( + "The 'pdo' option was used in DriverManager::getConnection() but no ". + "instance of PDO was given." + ); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function driverRequired() + { + return new self("The options 'driver' or 'driverClass' are mandatory if no PDO ". + "instance is given to DriverManager::getConnection()."); + } + + /** + * @param string $unknownDriverName + * @param array $knownDrivers + * + * @return \Doctrine\DBAL\DBALException + */ + public static function unknownDriver($unknownDriverName, array $knownDrivers) + { + return new self("The given 'driver' ".$unknownDriverName." is unknown, ". + "Doctrine currently supports only the following drivers: ".implode(", ", $knownDrivers)); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * @param string $sql + * @param array $params + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverExceptionDuringQuery(Driver $driver, \Exception $driverEx, $sql, array $params = array()) + { + $msg = "An exception occurred while executing '".$sql."'"; + if ($params) { + $msg .= " with params " . self::formatParameters($params); + } + $msg .= ":\n\n".$driverEx->getMessage(); + + if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverException) { + return $driver->convertException($msg, $driverEx); + } + + return new self($msg, 0, $driverEx); + } + + /** + * @param \Doctrine\DBAL\Driver $driver + * @param \Exception $driverEx + * + * @return \Doctrine\DBAL\DBALException + */ + public static function driverException(Driver $driver, \Exception $driverEx) + { + $msg = "An exception occured in driver: " . $driverEx->getMessage(); + + if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverException) { + return $driver->convertException($msg, $driverEx); + } + + return new self($msg, 0, $driverEx); + } + + /** + * Returns a human-readable representation of an array of parameters. + * This properly handles binary data by returning a hex representation. + * + * @param array $params + * + * @return string + */ + private static function formatParameters(array $params) + { + return '[' . implode(', ', array_map(function ($param) { + $json = @json_encode($param); + + if (! is_string($json) || $json == 'null' && is_string($param)) { + // JSON encoding failed, this is not a UTF-8 string. + return '"\x' . implode('\x', str_split(bin2hex($param), 2)) . '"'; + } + + return $json; + }, $params)) . ']'; + } + + /** + * @param string $wrapperClass + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidWrapperClass($wrapperClass) + { + return new self("The given 'wrapperClass' ".$wrapperClass." has to be a ". + "subtype of \Doctrine\DBAL\Connection."); + } + + /** + * @param string $driverClass + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidDriverClass($driverClass) + { + return new self("The given 'driverClass' ".$driverClass." has to implement the ". + "\Doctrine\DBAL\Driver interface."); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\DBALException + */ + public static function invalidTableName($tableName) + { + return new self("Invalid table name specified: ".$tableName); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\DBALException + */ + public static function noColumnsSpecifiedForTable($tableName) + { + return new self("No columns specified for table ".$tableName); + } + + /** + * @return \Doctrine\DBAL\DBALException + */ + public static function limitOffsetInvalid() + { + return new self("Invalid Offset in Limit Query, it has to be larger or equal to 0."); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function typeExists($name) + { + return new self('Type '.$name.' already exists.'); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function unknownColumnType($name) + { + return new self('Unknown column type "'.$name.'" requested. Any Doctrine type that you use has ' . + 'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' . + 'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' . + 'introspection then you might have forgot to register all database types for a Doctrine Type. Use ' . + 'AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement ' . + 'Type#getMappedDatabaseTypes(). If the type name is empty you might ' . + 'have a problem with the cache or forgot some mapping information.' + ); + } + + /** + * @param string $name + * + * @return \Doctrine\DBAL\DBALException + */ + public static function typeNotFound($name) + { + return new self('Type to be overwritten '.$name.' does not exist.'); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php new file mode 100644 index 0000000..21d6064 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Driver interface. + * Interface that all DBAL drivers must implement. + * + * @since 2.0 + */ +interface Driver +{ + /** + * Attempts to create a connection with the database. + * + * @param array $params All connection parameters passed by the user. + * @param string|null $username The username to use when connecting. + * @param string|null $password The password to use when connecting. + * @param array $driverOptions The driver options to use when connecting. + * + * @return \Doctrine\DBAL\Driver\Connection The database connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()); + + /** + * Gets the DatabasePlatform instance that provides all the metadata about + * the platform this driver connects to. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform. + */ + public function getDatabasePlatform(); + + /** + * Gets the SchemaManager that can be used to inspect and change the underlying + * database schema of the platform this driver connects to. + * + * @param \Doctrine\DBAL\Connection $conn + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager(Connection $conn); + + /** + * Gets the name of the driver. + * + * @return string The name of the driver. + */ + public function getName(); + + /** + * Gets the name of the database connected to for this driver. + * + * @param \Doctrine\DBAL\Connection $conn + * + * @return string The name of the database. + */ + public function getDatabase(Connection $conn); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php new file mode 100644 index 0000000..baa0816 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDB2Driver.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\DB2Platform; +use Doctrine\DBAL\Schema\DB2SchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for IBM DB2 based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractDB2Driver implements Driver +{ + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return $params['dbname']; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new DB2Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new DB2SchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php new file mode 100644 index 0000000..c5eee5a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractDriverException.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Abstract base implementation of the {@link DriverException} interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractDriverException extends \Exception implements DriverException +{ + /** + * The driver specific error code. + * + * @var integer|string|null + */ + private $errorCode; + + /** + * The SQLSTATE of the driver. + * + * @var string|null + */ + private $sqlState; + + /** + * Constructor. + * + * @param string $message The driver error message. + * @param string|null $sqlState The SQLSTATE the driver is in at the time the error occured, if any. + * @param integer|string|null $errorCode The driver specific error code if any. + */ + public function __construct($message, $sqlState = null, $errorCode = null) + { + parent::__construct($message); + + $this->errorCode = $errorCode; + $this->sqlState = $sqlState; + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php new file mode 100644 index 0000000..eff7957 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -0,0 +1,175 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\MySQL57Platform; +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Schema\MySqlSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for MySQL based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html + * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '1050': + return new Exception\TableExistsException($message, $exception); + + case '1051': + case '1146': + return new Exception\TableNotFoundException($message, $exception); + + case '1216': + case '1217': + case '1451': + case '1452': + case '1701': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + + case '1062': + case '1557': + case '1569': + case '1586': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '1054': + case '1166': + case '1611': + return new Exception\InvalidFieldNameException($message, $exception); + + case '1052': + case '1060': + case '1110': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '1064': + case '1149': + case '1287': + case '1341': + case '1342': + case '1343': + case '1344': + case '1382': + case '1479': + case '1541': + case '1554': + case '1626': + return new Exception\SyntaxErrorException($message, $exception); + + case '1044': + case '1045': + case '1046': + case '1049': + case '1095': + case '1142': + case '1143': + case '1227': + case '1370': + case '2002': + case '2005': + return new Exception\ConnectionException($message, $exception); + + case '1048': + case '1121': + case '1138': + case '1171': + case '1252': + case '1263': + case '1566': + return new Exception\NotNullConstraintViolationException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + if (false !== stripos($version, 'mariadb')) { + return $this->getDatabasePlatform(); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + if (version_compare($version, '5.7', '>=')) { + return new MySQL57Platform(); + } + + return $this->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DATABASE()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new MySqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new MySqlSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php new file mode 100644 index 0000000..1a0d977 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractOracleDriver.php @@ -0,0 +1,151 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Schema\OracleSchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Oracle based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver +{ + /** + * {@inheritdoc} + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '1': + case '2299': + case '38911': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '904': + return new Exception\InvalidFieldNameException($message, $exception); + + case '918': + case '960': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '923': + return new Exception\SyntaxErrorException($message, $exception); + + case '942': + return new Exception\TableNotFoundException($message, $exception); + + case '955': + return new Exception\TableExistsException($message, $exception); + + case '1017': + case '12545': + return new Exception\ConnectionException($message, $exception); + + case '1400': + return new Exception\NotNullConstraintViolationException($message, $exception); + + case '2266': + case '2291': + case '2292': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return $params['user']; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new OraclePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new OracleSchemaManager($conn); + } + + /** + * Returns an appropriate Easy Connect String for the given parameters. + * + * @param array $params The connection parameters to return the Easy Connect STring for. + * + * @return string + * + * @link http://download.oracle.com/docs/cd/E11882_01/network.112/e10836/naming.htm + */ + protected function getEasyConnectString(array $params) + { + if ( ! empty($params['host'])) { + if ( ! isset($params['port'])) { + $params['port'] = 1521; + } + + $serviceName = $params['dbname']; + + if ( ! empty($params['servicename'])) { + $serviceName = $params['servicename']; + } + + $service = 'SID=' . $serviceName; + $pooled = ''; + $instance = ''; + + if (isset($params['service']) && $params['service'] == true) { + $service = 'SERVICE_NAME=' . $serviceName; + } + + if (isset($params['instancename']) && ! empty($params['instancename'])) { + $instance = '(INSTANCE_NAME = ' . $params['instancename'] . ')'; + } + + if (isset($params['pooled']) && $params['pooled'] == true) { + $pooled = '(SERVER=POOLED)'; + } + + return '(DESCRIPTION=' . + '(ADDRESS=(PROTOCOL=TCP)(HOST=' . $params['host'] . ')(PORT=' . $params['port'] . '))' . + '(CONNECT_DATA=(' . $service . ')' . $instance . $pooled . '))'; + + } + + return isset($params['dbname']) ? $params['dbname'] : ''; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php new file mode 100644 index 0000000..4ad7cbe --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\PostgreSQL91Platform; +use Doctrine\DBAL\Platforms\PostgreSQL92Platform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Schema\PostgreSqlSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for PostgreSQL based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getSQLState()) { + case '0A000': + // Foreign key constraint violations during a TRUNCATE operation + // are considered "feature not supported" in PostgreSQL. + if (strpos($exception->getMessage(), 'truncate') !== false) { + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + } + + break; + case '23502': + return new Exception\NotNullConstraintViolationException($message, $exception); + + case '23503': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + + case '23505': + return new Exception\UniqueConstraintViolationException($message, $exception); + + case '42601': + return new Exception\SyntaxErrorException($message, $exception); + + case '42702': + return new Exception\NonUniqueFieldNameException($message, $exception); + + case '42703': + return new Exception\InvalidFieldNameException($message, $exception); + + case '42P01': + return new Exception\TableNotFoundException($message, $exception); + + case '42P07': + return new Exception\TableExistsException($message, $exception); + + case '7': + // In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code. + // The exception code is always set to 7 here. + // We have to match against the SQLSTATE in the error message in these cases. + if (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { + return new Exception\ConnectionException($message, $exception); + } + + break; + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '..' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + + switch(true) { + case version_compare($version, '9.2', '>='): + return new PostgreSQL92Platform(); + case version_compare($version, '9.1', '>='): + return new PostgreSQL91Platform(); + default: + return new PostgreSqlPlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return (isset($params['dbname'])) + ? $params['dbname'] + : $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new PostgreSqlPlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new PostgreSqlSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php new file mode 100644 index 0000000..0527555 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php @@ -0,0 +1,141 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SQLAnywhere11Platform; +use Doctrine\DBAL\Platforms\SQLAnywhere12Platform; +use Doctrine\DBAL\Platforms\SQLAnywhere16Platform; +use Doctrine\DBAL\Platforms\SQLAnywherePlatform; +use Doctrine\DBAL\Schema\SQLAnywhereSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SAP Sybase SQL Anywhere based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html + */ + public function convertException($message, DriverException $exception) + { + switch ($exception->getErrorCode()) { + case '-100': + case '-103': + case '-832': + return new Exception\ConnectionException($message, $exception); + case '-143': + return new Exception\InvalidFieldNameException($message, $exception); + case '-193': + case '-196': + return new Exception\UniqueConstraintViolationException($message, $exception); + case '-194': + case '-198': + return new Exception\ForeignKeyConstraintViolationException($message, $exception); + case '-144': + return new Exception\NonUniqueFieldNameException($message, $exception); + case '-184': + case '-195': + return new Exception\NotNullConstraintViolationException($message, $exception); + case '-131': + return new Exception\SyntaxErrorException($message, $exception); + case '-110': + return new Exception\TableExistsException($message, $exception); + case '-141': + case '-1041': + return new Exception\TableNotFoundException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', + $version, + $versionParts + )) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '...' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $buildVersion = isset($versionParts['build']) ? $versionParts['build'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; + + switch(true) { + case version_compare($version, '16', '>='): + return new SQLAnywhere16Platform(); + case version_compare($version, '12', '>='): + return new SQLAnywhere12Platform(); + case version_compare($version, '11', '>='): + return new SQLAnywhere11Platform(); + default: + return new SQLAnywherePlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DB_NAME()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SQLAnywhere12Platform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SQLAnywhereSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php new file mode 100644 index 0000000..53a4dd5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Platforms\SQLServer2005Platform; +use Doctrine\DBAL\Platforms\SQLServer2008Platform; +use Doctrine\DBAL\Platforms\SQLServer2012Platform; +use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Doctrine\DBAL\Schema\SQLServerSchemaManager; +use Doctrine\DBAL\VersionAwarePlatformDriver; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Microsoft SQL Server based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDriver +{ + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?)?/', + $version, + $versionParts + )) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '...' + ); + } + + $majorVersion = $versionParts['major']; + $minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0; + $patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : 0; + $buildVersion = isset($versionParts['build']) ? $versionParts['build'] : 0; + $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; + + switch(true) { + case version_compare($version, '11.00.2100', '>='): + return new SQLServer2012Platform(); + case version_compare($version, '10.00.1600', '>='): + return new SQLServer2008Platform(); + case version_compare($version, '9.00.1399', '>='): + return new SQLServer2005Platform(); + default: + return new SQLServerPlatform(); + } + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + if (isset($params['dbname'])) { + return $params['dbname']; + } + + return $conn->query('SELECT DB_NAME()')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SQLServer2008Platform(); + } + + /** + * {@inheritdoc} + */ + + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SQLServerSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php new file mode 100644 index 0000000..0facc9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Schema\SqliteSchemaManager; + +/** + * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver +{ + /** + * {@inheritdoc} + * + * @link http://www.sqlite.org/c3ref/c_abort.html + */ + public function convertException($message, DriverException $exception) + { + if (strpos($exception->getMessage(), 'must be unique') !== false || + strpos($exception->getMessage(), 'is not unique') !== false || + strpos($exception->getMessage(), 'are not unique') !== false || + strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false + ) { + return new Exception\UniqueConstraintViolationException($message, $exception); + } + + if (strpos($exception->getMessage(), 'may not be NULL') !== false || + strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false + ) { + return new Exception\NotNullConstraintViolationException($message, $exception); + } + + if (strpos($exception->getMessage(), 'no such table:') !== false) { + return new Exception\TableNotFoundException($message, $exception); + } + + if (strpos($exception->getMessage(), 'already exists') !== false) { + return new Exception\TableExistsException($message, $exception); + } + + if (strpos($exception->getMessage(), 'has no column named') !== false) { + return new Exception\InvalidFieldNameException($message, $exception); + } + + if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { + return new Exception\NonUniqueFieldNameException($message, $exception); + } + + if (strpos($exception->getMessage(), 'syntax error') !== false) { + return new Exception\SyntaxErrorException($message, $exception); + } + + if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { + return new Exception\ReadOnlyException($message, $exception); + } + + if (strpos($exception->getMessage(), 'unable to open database file') !== false) { + return new Exception\ConnectionException($message, $exception); + } + + return new Exception\DriverException($message, $exception); + } + + /** + * {@inheritdoc} + */ + public function getDatabase(\Doctrine\DBAL\Connection $conn) + { + $params = $conn->getParams(); + + return isset($params['path']) ? $params['path'] : null; + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new SqlitePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new SqliteSchemaManager($conn); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php new file mode 100644 index 0000000..401f217 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php @@ -0,0 +1,110 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Connection interface. + * Driver connections must implement this interface. + * + * This resembles (a subset of) the PDO interface. + * + * @since 2.0 + */ +interface Connection +{ + /** + * Prepares a statement for execution and returns a Statement object. + * + * @param string $prepareString + * + * @return \Doctrine\DBAL\Driver\Statement + */ + function prepare($prepareString); + + /** + * Executes an SQL statement, returning a result set as a Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + function query(); + + /** + * Quotes a string for use in a query. + * + * @param string $input + * @param integer $type + * + * @return string + */ + function quote($input, $type=\PDO::PARAM_STR); + + /** + * Executes an SQL statement and return the number of affected rows. + * + * @param string $statement + * + * @return integer + */ + function exec($statement); + + /** + * Returns the ID of the last inserted row or sequence value. + * + * @param string|null $name + * + * @return string + */ + function lastInsertId($name = null); + + /** + * Initiates a transaction. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function beginTransaction(); + + /** + * Commits a transaction. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function commit(); + + /** + * Rolls back the current transaction, as initiated by beginTransaction(). + * + * @return boolean TRUE on success or FALSE on failure. + */ + function rollBack(); + + /** + * Returns the error code associated with the last operation on the database handle. + * + * @return string|null The error code, or null if no operation has been run on the database handle. + */ + function errorCode(); + + /** + * Returns extended error information associated with the last operation on the database handle. + * + * @return array + */ + function errorInfo(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php new file mode 100644 index 0000000..f11146b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a driver exception. + * + * Driver exceptions provide the SQLSTATE of the driver + * and the driver specific error code at the time the error occurred. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface DriverException +{ + /** + * Returns the driver specific error code if available. + * + * Returns null if no driver specific error code is available + * for the error raised by the driver. + * + * @return integer|string|null + */ + public function getErrorCode(); + + /** + * Returns the driver error message. + * + * @return string + */ + public function getMessage(); + + /** + * Returns the SQLSTATE the driver was in at the time the error occurred. + * + * Returns null if the driver does not provide a SQLSTATE for the error occurred. + * + * @return string|null + */ + public function getSQLState(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php new file mode 100644 index 0000000..92ca660 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +/** + * @author Kim Hemsø Rasmussen + */ +class Connection extends \Doctrine\DBAL\Driver\PDOConnection +{ + /** + * {@inheritdoc} + */ + public function quote($value, $type = \PDO::PARAM_STR) + { + if (\PDO::PARAM_BOOL === $type) { + if ($value) { + return 'true'; + } else { + return 'false'; + } + } + + return parent::quote($value, $type); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php new file mode 100644 index 0000000..ff4cf3b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; + +use Doctrine\DBAL\Platforms\DrizzlePlatform; +use Doctrine\DBAL\Schema\DrizzleSchemaManager; + +/** + * Drizzle driver using PDO MySql. + * + * @author Kim Hemsø Rasmussen + */ +class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new Connection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + return $conn; + } + + /** + * {@inheritdoc} + */ + public function createDatabasePlatformForVersion($version) + { + return $this->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getDatabasePlatform() + { + return new DrizzlePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + { + return new DrizzleSchemaManager($conn); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle_pdo_mysql'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php new file mode 100644 index 0000000..0bd8d51 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a driver that is capable of converting DBAL driver exceptions into standardized DBAL driver exceptions. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface ExceptionConverterDriver +{ + /** + * Converts a given DBAL driver exception into a standardized DBAL driver exception. + * + * It evaluates the vendor specific error code and SQLSTATE and transforms + * it into a unified {@link Doctrine\DBAL\Exception\DriverException} subclass. + * + * @param string $message The DBAL exception message to use. + * @param \Doctrine\DBAL\Driver\DriverException $exception The DBAL driver exception to convert. + * + * @return \Doctrine\DBAL\Exception\DriverException An instance of one of the DriverException subclasses. + */ + public function convertException($message, DriverException $exception); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php new file mode 100644 index 0000000..0e809e2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +class DB2Connection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + private $_conn = null; + + /** + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * + * @throws \Doctrine\DBAL\Driver\IBMDB2\DB2Exception + */ + public function __construct(array $params, $username, $password, $driverOptions = array()) + { + $isPersistant = (isset($params['persistent']) && $params['persistent'] == true); + + if ($isPersistant) { + $this->_conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions); + } else { + $this->_conn = db2_connect($params['dbname'], $username, $password, $driverOptions); + } + if ( ! $this->_conn) { + throw new DB2Exception(db2_conn_errormsg()); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = db2_server_info($this->_conn); + + return $serverInfo->DBMS_VER; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($sql) + { + $stmt = @db2_prepare($this->_conn, $sql); + if ( ! $stmt) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return new DB2Statement($stmt); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type=\PDO::PARAM_STR) + { + $input = db2_escape_string($input); + if ($type == \PDO::PARAM_INT) { + return $input; + } else { + return "'".$input."'"; + } + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return db2_last_insert_id($this->_conn); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_OFF); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (!db2_commit($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (!db2_rollback($this->_conn)) { + throw new DB2Exception(db2_conn_errormsg($this->_conn)); + } + db2_autocommit($this->_conn, DB2_AUTOCOMMIT_ON); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_conn_error($this->_conn); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return array( + 0 => db2_conn_errormsg($this->_conn), + 1 => $this->errorCode(), + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php new file mode 100644 index 0000000..8ed82e4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Driver.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\AbstractDB2Driver; + +/** + * IBM DB2 Driver. + * + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Driver extends AbstractDB2Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if ( ! isset($params['protocol'])) { + $params['protocol'] = 'TCPIP'; + } + + if ($params['host'] !== 'localhost' && $params['host'] != '127.0.0.1') { + // if the host isn't localhost, use extended connection params + $params['dbname'] = 'DRIVER={IBM DB2 ODBC DRIVER}' . + ';DATABASE=' . $params['dbname'] . + ';HOSTNAME=' . $params['host'] . + ';PROTOCOL=' . $params['protocol'] . + ';UID=' . $username . + ';PWD=' . $password .';'; + if (isset($params['port'])) { + $params['dbname'] .= 'PORT=' . $params['port']; + } + + $username = null; + $password = null; + } + + return new DB2Connection($params, $username, $password, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ibm_db2'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php new file mode 100644 index 0000000..72775cb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +class DB2Exception extends \Exception +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php new file mode 100644 index 0000000..b8de9ea --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -0,0 +1,344 @@ +. + */ + +namespace Doctrine\DBAL\Driver\IBMDB2; + +use Doctrine\DBAL\Driver\Statement; + +class DB2Statement implements \IteratorAggregate, Statement +{ + /** + * @var resource + */ + private $_stmt = null; + + /** + * @var array + */ + private $_bindParam = array(); + + /** + * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClass = '\stdClass'; + + /** + * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClassCtorArgs = array(); + + /** + * @var integer + */ + private $_defaultFetchMode = \PDO::FETCH_BOTH; + + /** + * DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG + * + * @var array + */ + static private $_typeMap = array( + \PDO::PARAM_INT => DB2_LONG, + \PDO::PARAM_STR => DB2_CHAR, + ); + + /** + * @param resource $stmt + */ + public function __construct($stmt) + { + $this->_stmt = $stmt; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + $this->_bindParam[$column] =& $variable; + + if ($type && isset(self::$_typeMap[$type])) { + $type = self::$_typeMap[$type]; + } else { + $type = DB2_CHAR; + } + + if (!db2_bind_param($this->_stmt, $column, "variable", DB2_PARAM_IN, $type)) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + if ( ! $this->_stmt) { + return false; + } + + $this->_bindParam = array(); + db2_free_result($this->_stmt); + $ret = db2_free_stmt($this->_stmt); + $this->_stmt = false; + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + if ( ! $this->_stmt) { + return false; + } + + return db2_num_fields($this->_stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return db2_stmt_error(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return array( + 0 => db2_stmt_errormsg(), + 1 => db2_stmt_error(), + ); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ( ! $this->_stmt) { + return false; + } + + /*$retval = true; + if ($params !== null) { + $retval = @db2_execute($this->_stmt, $params); + } else { + $retval = @db2_execute($this->_stmt); + }*/ + if ($params === null) { + ksort($this->_bindParam); + $params = array_values($this->_bindParam); + } + $retval = @db2_execute($this->_stmt, $params); + + if ($retval === false) { + throw new DB2Exception(db2_stmt_errormsg()); + } + + return $retval; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + switch ($fetchMode) { + case \PDO::FETCH_BOTH: + return db2_fetch_both($this->_stmt); + case \PDO::FETCH_ASSOC: + return db2_fetch_assoc($this->_stmt); + case \PDO::FETCH_CLASS: + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : array(); + } + + $result = db2_fetch_object($this->_stmt); + + if ($result instanceof \stdClass) { + $result = $this->castObject($result, $className, $ctorArgs); + } + + return $result; + case \PDO::FETCH_NUM: + return db2_fetch_array($this->_stmt); + case \PDO::FETCH_OBJ: + return db2_fetch_object($this->_stmt); + default: + throw new DB2Exception("Given Fetch-Style " . $fetchMode . " is not supported."); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + + switch ($fetchMode) { + case \PDO::FETCH_CLASS: + while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) { + $rows[] = $row; + } + break; + case \PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(\PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return (@db2_num_rows($this->_stmt))?:0; + } + + /** + * Casts a stdClass object to the given class name mapping its' properties. + * + * @param \stdClass $sourceObject Object to cast from. + * @param string|object $destinationClass Name of the class or class instance to cast to. + * @param array $ctorArgs Arguments to use for constructing the destination class instance. + * + * @return object + * + * @throws DB2Exception + */ + private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = array()) + { + if ( ! is_string($destinationClass)) { + if ( ! is_object($destinationClass)) { + throw new DB2Exception(sprintf( + 'Destination class has to be of type string or object, %s given.', gettype($destinationClass) + )); + } + } else { + $destinationClass = new \ReflectionClass($destinationClass); + $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); + } + + $sourceReflection = new \ReflectionObject($sourceObject); + $destinationClassReflection = new \ReflectionObject($destinationClass); + /** @var \ReflectionProperty[] $destinationProperties */ + $destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), \CASE_LOWER); + + foreach ($sourceReflection->getProperties() as $sourceProperty) { + $sourceProperty->setAccessible(true); + + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + + // Try to find a case-matching property. + if ($destinationClassReflection->hasProperty($name)) { + $destinationProperty = $destinationClassReflection->getProperty($name); + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + + continue; + } + + $name = strtolower($name); + + // Try to find a property without matching case. + // Fallback for the driver returning either all uppercase or all lowercase column names. + if (isset($destinationProperties[$name])) { + $destinationProperty = $destinationProperties[$name]; + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + + continue; + } + + $destinationClass->$name = $value; + } + + return $destinationClass; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php new file mode 100644 index 0000000..8949d2b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/Driver.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\DBALException; + +/** + * @author Kim Hemsø Rasmussen + */ +class Driver extends AbstractMySQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new MysqliConnection($params, $username, $password, $driverOptions); + } catch (MysqliException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'mysqli'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php new file mode 100644 index 0000000..ae3502c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -0,0 +1,266 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Connection as Connection; +use Doctrine\DBAL\Driver\PingableConnection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * @author Kim Hemsø Rasmussen + * @author Till Klampaeckel + */ +class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection +{ + /** + * Name of the option to set connection flags + */ + const OPTION_FLAGS = 'flags'; + + /** + * @var \mysqli + */ + private $_conn; + + /** + * @param array $params + * @param string $username + * @param string $password + * @param array $driverOptions + * + * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException + */ + public function __construct(array $params, $username, $password, array $driverOptions = array()) + { + $port = isset($params['port']) ? $params['port'] : ini_get('mysqli.default_port'); + + // Fallback to default MySQL port if not given. + if ( ! $port) { + $port = 3306; + } + + $socket = isset($params['unix_socket']) ? $params['unix_socket'] : ini_get('mysqli.default_socket'); + $dbname = isset($params['dbname']) ? $params['dbname'] : null; + + $flags = isset($driverOptions[static::OPTION_FLAGS]) ? $driverOptions[static::OPTION_FLAGS] : null; + + $this->_conn = mysqli_init(); + + $this->setDriverOptions($driverOptions); + + set_error_handler(function () {}); + + if ( ! $this->_conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) { + restore_error_handler(); + + throw new MysqliException($this->_conn->connect_error, @$this->_conn->sqlstate ?: 'HY000', $this->_conn->connect_errno); + } + + restore_error_handler(); + + if (isset($params['charset'])) { + $this->_conn->set_charset($params['charset']); + } + } + + /** + * Retrieves mysqli native resource handle. + * + * Could be used if part of your application is not using DBAL. + * + * @return \mysqli + */ + public function getWrappedResourceHandle() + { + return $this->_conn; + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $majorVersion = floor($this->_conn->server_version / 10000); + $minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100); + $patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100); + + return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new MysqliStatement($this->_conn, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type=\PDO::PARAM_STR) + { + return "'". $this->_conn->escape_string($input) ."'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + if (false === $this->_conn->query($statement)) { + throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); + } + + return $this->_conn->affected_rows; + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return $this->_conn->insert_id; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->_conn->query('START TRANSACTION'); + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->_conn->commit(); + } + + /** + * {@inheritdoc}non-PHPdoc) + */ + public function rollBack() + { + return $this->_conn->rollback(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_conn->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_conn->error; + } + + /** + * Apply the driver options to the connection. + * + * @param array $driverOptions + * + * @throws MysqliException When one of of the options is not supported. + * @throws MysqliException When applying doesn't work - e.g. due to incorrect value. + */ + private function setDriverOptions(array $driverOptions = array()) + { + $supportedDriverOptions = array( + \MYSQLI_OPT_CONNECT_TIMEOUT, + \MYSQLI_OPT_LOCAL_INFILE, + \MYSQLI_INIT_COMMAND, + \MYSQLI_READ_DEFAULT_FILE, + \MYSQLI_READ_DEFAULT_GROUP, + ); + + if (defined('MYSQLI_SERVER_PUBLIC_KEY')) { + $supportedDriverOptions[] = \MYSQLI_SERVER_PUBLIC_KEY; + } + + $exceptionMsg = "%s option '%s' with value '%s'"; + + foreach ($driverOptions as $option => $value) { + + if ($option === static::OPTION_FLAGS) { + continue; + } + + if (!in_array($option, $supportedDriverOptions, true)) { + throw new MysqliException( + sprintf($exceptionMsg, 'Unsupported', $option, $value) + ); + } + + if (@mysqli_options($this->_conn, $option, $value)) { + continue; + } + + $msg = sprintf($exceptionMsg, 'Failed to set', $option, $value); + $msg .= sprintf(', error: %s (%d)', mysqli_error($this->_conn), mysqli_errno($this->_conn)); + + throw new MysqliException( + $msg, + $this->_conn->sqlstate, + $this->_conn->errno + ); + } + } + + /** + * Pings the server and re-connects when `mysqli.reconnect = 1` + * + * @return bool + */ + public function ping() + { + return $this->_conn->ping(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php new file mode 100644 index 0000000..23728b1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +/** + * Exception thrown in case the mysqli driver errors. + * + * @author Kim Hemsø Rasmussen + * @author Steve Müller + */ +class MysqliException extends AbstractDriverException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php new file mode 100644 index 0000000..edc02df --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -0,0 +1,371 @@ +. + */ + +namespace Doctrine\DBAL\Driver\Mysqli; + +use Doctrine\DBAL\Driver\Statement; +use PDO; + +/** + * @author Kim Hemsø Rasmussen + */ +class MysqliStatement implements \IteratorAggregate, Statement +{ + /** + * @var array + */ + protected static $_paramTypeMap = array( + PDO::PARAM_STR => 's', + PDO::PARAM_BOOL => 'i', + PDO::PARAM_NULL => 's', + PDO::PARAM_INT => 'i', + PDO::PARAM_LOB => 's' // TODO Support LOB bigger then max package size. + ); + + /** + * @var \mysqli + */ + protected $_conn; + + /** + * @var \mysqli_stmt + */ + protected $_stmt; + + /** + * @var null|boolean|array + */ + protected $_columnNames; + + /** + * @var null|array + */ + protected $_rowBindedValues; + + /** + * @var array + */ + protected $_bindedValues; + + /** + * @var string + */ + protected $types; + + /** + * Contains ref values for bindValue(). + * + * @var array + */ + protected $_values = array(); + + /** + * @var integer + */ + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @param \mysqli $conn + * @param string $prepareString + * + * @throws \Doctrine\DBAL\Driver\Mysqli\MysqliException + */ + public function __construct(\mysqli $conn, $prepareString) + { + $this->_conn = $conn; + $this->_stmt = $conn->prepare($prepareString); + if (false === $this->_stmt) { + throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno); + } + + $paramCount = $this->_stmt->param_count; + if (0 < $paramCount) { + $this->types = str_repeat('s', $paramCount); + $this->_bindedValues = array_fill(1, $paramCount, null); + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_bindedValues[$column] =& $variable; + $this->types[$column - 1] = $type; + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + if (null === $type) { + $type = 's'; + } else { + if (isset(self::$_paramTypeMap[$type])) { + $type = self::$_paramTypeMap[$type]; + } else { + throw new MysqliException("Unknown type: '{$type}'"); + } + } + + $this->_values[$param] = $value; + $this->_bindedValues[$param] =& $this->_values[$param]; + $this->types[$param - 1] = $type; + + return true; + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if (null !== $this->_bindedValues) { + if (null !== $params) { + if ( ! $this->_bindValues($params)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->errno); + } + } else { + if (!call_user_func_array(array($this->_stmt, 'bind_param'), array($this->types) + $this->_bindedValues)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + } + } + + if ( ! $this->_stmt->execute()) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + + if (null === $this->_columnNames) { + $meta = $this->_stmt->result_metadata(); + if (false !== $meta) { + // We have a result. + $this->_stmt->store_result(); + + $columnNames = array(); + foreach ($meta->fetch_fields() as $col) { + $columnNames[] = $col->name; + } + $meta->free(); + + $this->_columnNames = $columnNames; + $this->_rowBindedValues = array_fill(0, count($columnNames), null); + + $refs = array(); + foreach ($this->_rowBindedValues as $key => &$value) { + $refs[$key] =& $value; + } + + if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + } else { + $this->_columnNames = false; + } + } + + return true; + } + + /** + * Binds a array of values to bound parameters. + * + * @param array $values + * + * @return boolean + */ + private function _bindValues($values) + { + $params = array(); + $types = str_repeat('s', count($values)); + $params[0] = $types; + + foreach ($values as &$v) { + $params[] =& $v; + } + + return call_user_func_array(array($this->_stmt, 'bind_param'), $params); + } + + /** + * @return boolean|array + */ + private function _fetch() + { + $ret = $this->_stmt->fetch(); + + if (true === $ret) { + $values = array(); + foreach ($this->_rowBindedValues as $v) { + $values[] = $v; + } + + return $values; + } + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $values = $this->_fetch(); + if (null === $values) { + return null; + } + + if (false === $values) { + throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno); + } + + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + switch ($fetchMode) { + case PDO::FETCH_NUM: + return $values; + + case PDO::FETCH_ASSOC: + return array_combine($this->_columnNames, $values); + + case PDO::FETCH_BOTH: + $ret = array_combine($this->_columnNames, $values); + $ret += $values; + + return $ret; + + default: + throw new MysqliException("Unknown fetch type '{$fetchMode}'"); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + + $rows = array(); + if (PDO::FETCH_COLUMN == $fetchMode) { + while (($row = $this->fetchColumn()) !== false) { + $rows[] = $row; + } + } else { + while (($row = $this->fetch($fetchMode)) !== null) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + if (null === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->_stmt->errno; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->_stmt->error; + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + $this->_stmt->free_result(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + if (false === $this->_columnNames) { + return $this->_stmt->affected_rows; + } + + return $this->_stmt->num_rows; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->_stmt->field_count; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php new file mode 100644 index 0000000..1aa1b87 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/Driver.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractOracleDriver; + +/** + * A Doctrine DBAL driver for the Oracle OCI8 PHP extensions. + * + * @author Roman Borschel + * @since 2.0 + */ +class Driver extends AbstractOracleDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new OCI8Connection( + $username, + $password, + $this->_constructDsn($params), + isset($params['charset']) ? $params['charset'] : null, + isset($params['sessionMode']) ? $params['sessionMode'] : OCI_DEFAULT, + isset($params['persistent']) ? $params['persistent'] : false + ); + } catch (OCI8Exception $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Oracle DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function _constructDsn(array $params) + { + return $this->getEasyConnectString($params); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'oci8'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php new file mode 100644 index 0000000..a919dda --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -0,0 +1,233 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Platforms\OraclePlatform; + +/** + * OCI8 implementation of the Connection interface. + * + * @since 2.0 + */ +class OCI8Connection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + protected $dbh; + + /** + * @var integer + */ + protected $executeMode = OCI_COMMIT_ON_SUCCESS; + + /** + * Creates a Connection to an Oracle Database using oci8 extension. + * + * @param string $username + * @param string $password + * @param string $db + * @param string|null $charset + * @param integer $sessionMode + * @param boolean $persistent + * + * @throws OCI8Exception + */ + public function __construct($username, $password, $db, $charset = null, $sessionMode = OCI_DEFAULT, $persistent = false) + { + if (!defined('OCI_NO_AUTO_COMMIT')) { + define('OCI_NO_AUTO_COMMIT', 0); + } + + $this->dbh = $persistent + ? @oci_pconnect($username, $password, $db, $charset, $sessionMode) + : @oci_connect($username, $password, $db, $charset, $sessionMode); + + if ( ! $this->dbh) { + throw OCI8Exception::fromErrorInfo(oci_error()); + } + } + + /** + * {@inheritdoc} + * + * @throws \UnexpectedValueException if the version string returned by the database server + * does not contain a parsable version number. + */ + public function getServerVersion() + { + if ( ! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', oci_server_version($this->dbh), $version)) { + throw new \UnexpectedValueException( + sprintf( + 'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' . + 'Please report this database version string to the Doctrine team.', + oci_server_version($this->dbh) + ) + ); + } + + return $version[1]; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new OCI8Statement($this->dbh, $prepareString, $this); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + //$fetchMode = $args[1]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value) || is_float($value)) { + return $value; + } + $value = str_replace("'", "''", $value); + + return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return false; + } + + OraclePlatform::assertValidIdentifier($name); + + $sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL'; + $stmt = $this->query($sql); + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + if ($result === false || !isset($result['CURRVAL'])) { + throw new OCI8Exception("lastInsertId failed: Query was executed but no result was returned."); + } + + return (int) $result['CURRVAL']; + } + + /** + * Returns the current execution mode. + * + * @return integer + */ + public function getExecuteMode() + { + return $this->executeMode; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->executeMode = OCI_NO_AUTO_COMMIT; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + if (!oci_commit($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + + return true; + } + + /** + * {@inheritdoc} + */ + public function rollBack() + { + if (!oci_rollback($this->dbh)) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + $this->executeMode = OCI_COMMIT_ON_SUCCESS; + + return true; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->dbh); + if ($error !== false) { + $error = $error['code']; + } + + return $error; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return oci_error($this->dbh); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php new file mode 100644 index 0000000..1cd08b8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +class OCI8Exception extends AbstractDriverException +{ + /** + * @param array $error + * + * @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception + */ + public static function fromErrorInfo($error) + { + return new self($error['message'], null, $error['code']); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php new file mode 100644 index 0000000..3270a0b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -0,0 +1,306 @@ +. + */ + +namespace Doctrine\DBAL\Driver\OCI8; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * The OCI8 implementation of the Statement interface. + * + * @since 2.0 + * @author Roman Borschel + */ +class OCI8Statement implements \IteratorAggregate, Statement +{ + /** + * @var resource + */ + protected $_dbh; + + /** + * @var resource + */ + protected $_sth; + + /** + * @var \Doctrine\DBAL\Driver\OCI8\OCI8Connection + */ + protected $_conn; + + /** + * @var string + */ + protected static $_PARAM = ':param'; + + /** + * @var array + */ + protected static $fetchModeMap = array( + PDO::FETCH_BOTH => OCI_BOTH, + PDO::FETCH_ASSOC => OCI_ASSOC, + PDO::FETCH_NUM => OCI_NUM, + PDO::FETCH_COLUMN => OCI_NUM, + ); + + /** + * @var integer + */ + protected $_defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var array + */ + protected $_paramMap = array(); + + /** + * Creates a new OCI8Statement that uses the given connection handle and SQL statement. + * + * @param resource $dbh The connection handle. + * @param string $statement The SQL statement. + * @param \Doctrine\DBAL\Driver\OCI8\OCI8Connection $conn + */ + public function __construct($dbh, $statement, OCI8Connection $conn) + { + list($statement, $paramMap) = self::convertPositionalToNamedPlaceholders($statement); + $this->_sth = oci_parse($dbh, $statement); + $this->_dbh = $dbh; + $this->_paramMap = $paramMap; + $this->_conn = $conn; + } + + /** + * Converts positional (?) into named placeholders (:param). + * + * Oracle does not support positional parameters, hence this method converts all + * positional parameters into artificially named parameters. Note that this conversion + * is not perfect. All question marks (?) in the original statement are treated as + * placeholders and converted to a named parameter. + * + * The algorithm uses a state machine with two possible states: InLiteral and NotInLiteral. + * Question marks inside literal strings are therefore handled correctly by this method. + * This comes at a cost, the whole sql statement has to be looped over. + * + * @todo extract into utility class in Doctrine\DBAL\Util namespace + * @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements. + * + * @param string $statement The SQL statement to convert. + * + * @return string + */ + static public function convertPositionalToNamedPlaceholders($statement) + { + $count = 1; + $inLiteral = false; // a valid query never starts with quotes + $stmtLen = strlen($statement); + $paramMap = array(); + for ($i = 0; $i < $stmtLen; $i++) { + if ($statement[$i] == '?' && !$inLiteral) { + // real positional parameter detected + $paramMap[$count] = ":param$count"; + $len = strlen($paramMap[$count]); + $statement = substr_replace($statement, ":param$count", $i, 1); + $i += $len-1; // jump ahead + $stmtLen = strlen($statement); // adjust statement length + ++$count; + } elseif ($statement[$i] == "'" || $statement[$i] == '"') { + $inLiteral = ! $inLiteral; // switch state! + } + } + + return array($statement, $paramMap); + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + $column = isset($this->_paramMap[$column]) ? $this->_paramMap[$column] : $column; + + if ($type == \PDO::PARAM_LOB) { + $lob = oci_new_descriptor($this->_dbh, OCI_D_LOB); + $lob->writeTemporary($variable, OCI_TEMP_BLOB); + + return oci_bind_by_name($this->_sth, $column, $lob, -1, OCI_B_BLOB); + } elseif ($length !== null) { + return oci_bind_by_name($this->_sth, $column, $variable, $length); + } + + return oci_bind_by_name($this->_sth, $column, $variable); + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + return oci_free_statement($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return oci_num_fields($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $error = oci_error($this->_sth); + if ($error !== false) { + $error = $error['code']; + } + + return $error; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return oci_error($this->_sth); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + if ($hasZeroIndex && is_numeric($key)) { + $this->bindValue($key + 1, $val); + } else { + $this->bindValue($key, $val); + } + } + } + + $ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode()); + if ( ! $ret) { + throw OCI8Exception::fromErrorInfo($this->errorInfo()); + } + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->_defaultFetchMode = $fetchMode; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + return oci_fetch_array($this->_sth, self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->_defaultFetchMode; + if ( ! isset(self::$fetchModeMap[$fetchMode])) { + throw new \InvalidArgumentException("Invalid fetch style: " . $fetchMode); + } + + $result = array(); + if (self::$fetchModeMap[$fetchMode] === OCI_BOTH) { + while ($row = $this->fetch($fetchMode)) { + $result[] = $row; + } + } else { + $fetchStructure = OCI_FETCHSTATEMENT_BY_ROW; + if ($fetchMode == PDO::FETCH_COLUMN) { + $fetchStructure = OCI_FETCHSTATEMENT_BY_COLUMN; + } + + oci_fetch_all($this->_sth, $result, 0, -1, + self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS); + + if ($fetchMode == PDO::FETCH_COLUMN) { + $result = $result[0]; + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return oci_num_rows($this->_sth); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php new file mode 100644 index 0000000..8960de1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -0,0 +1,133 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +use PDO; + +/** + * PDO implementation of the Connection interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection +{ + /** + * @param string $dsn + * @param string|null $user + * @param string|null $password + * @param array|null $options + * + * @throws PDOException in case of an error. + */ + public function __construct($dsn, $user = null, $password = null, array $options = null) + { + try { + parent::__construct($dsn, $user, $password, $options); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Doctrine\DBAL\Driver\PDOStatement', array())); + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + try { + return parent::exec($statement); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return PDO::getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString, $driverOptions = array()) + { + try { + return parent::prepare($prepareString, $driverOptions); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $argsCount = count($args); + + try { + if ($argsCount == 4) { + return parent::query($args[0], $args[1], $args[2], $args[3]); + } + + if ($argsCount == 3) { + return parent::query($args[0], $args[1], $args[2]); + } + + if ($argsCount == 2) { + return parent::query($args[0], $args[1]); + } + + return parent::query($args[0]); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type = \PDO::PARAM_STR) + { + return parent::quote($input, $type); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + return parent::lastInsertId($name); + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php new file mode 100644 index 0000000..7a1a02a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Tiny wrapper for PDOException instances to implement the {@link DriverException} interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PDOException extends \PDOException implements DriverException +{ + /** + * The driver specific error code. + * + * @var integer|string|null + */ + private $errorCode; + + /** + * The SQLSTATE of the driver. + * + * @var string|null + */ + private $sqlState; + + /** + * Constructor. + * + * @param \PDOException $exception The PDO exception to wrap. + */ + public function __construct(\PDOException $exception) + { + parent::__construct($exception->getMessage(), 0, $exception); + + $this->code = $exception->getCode(); + $this->errorInfo = $exception->errorInfo; + $this->errorCode = isset($exception->errorInfo[1]) ? $exception->errorInfo[1] : $exception->getCode(); + $this->sqlState = isset($exception->errorInfo[0]) ? $exception->errorInfo[0] : $exception->getCode(); + } + + /** + * {@inheritdoc} + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * {@inheritdoc} + */ + public function getSQLState() + { + return $this->sqlState; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php new file mode 100644 index 0000000..0b929f7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOIbm; + +use Doctrine\DBAL\Driver\AbstractDB2Driver; +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * Driver for the PDO IBM extension. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Driver extends AbstractDB2Driver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + $conn = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + return $conn; + } + + /** + * Constructs the IBM PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'ibm:'; + if (isset($params['host'])) { + $dsn .= 'HOSTNAME=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'PORT=' . $params['port'] . ';'; + } + $dsn .= 'PROTOCOL=TCPIP;'; + if (isset($params['dbname'])) { + $dsn .= 'DATABASE=' . $params['dbname'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_ibm'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php new file mode 100644 index 0000000..7e3632d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOMySql; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use PDOException; + +/** + * PDO MySql driver. + * + * @since 2.0 + */ +class Driver extends AbstractMySQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + $conn = new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (PDOException $e) { + throw DBALException::driverException($this, $e); + } + + return $conn; + } + + /** + * Constructs the MySql PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function constructPdoDsn(array $params) + { + $dsn = 'mysql:'; + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ';'; + } + if (isset($params['port'])) { + $dsn .= 'port=' . $params['port'] . ';'; + } + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ';'; + } + if (isset($params['unix_socket'])) { + $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; + } + if (isset($params['charset'])) { + $dsn .= 'charset=' . $params['charset'] . ';'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_mysql'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php new file mode 100644 index 0000000..6e958c7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOOracle/Driver.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOOracle; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractOracleDriver; +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * PDO Oracle driver. + * + * WARNING: This driver gives us segfaults in our testsuites on CLOB and other + * stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community, + * which leads us to the recommendation to use the "oci8" driver to connect + * to Oracle instead. + */ +class Driver extends AbstractOracleDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (\PDOException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Oracle PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function constructPdoDsn(array $params) + { + $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); + + if (isset($params['charset'])) { + $dsn .= ';charset=' . $params['charset']; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_oracle'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php new file mode 100644 index 0000000..7ebfb4e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOPgSql; + +use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\DBALException; +use PDOException; +use PDO; + +/** + * Driver that connects through pdo_pgsql. + * + * @since 2.0 + */ +class Driver extends AbstractPostgreSQLDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + $pdo = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + + if (PHP_VERSION_ID >= 50600 + && (! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) + || true === $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] + ) + ) { + $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + } + + /* defining client_encoding via SET NAMES to avoid inconsistent DSN support + * - the 'client_encoding' connection param only works with postgres >= 9.1 + * - passing client_encoding via the 'options' param breaks pgbouncer support + */ + if (isset($params['charset'])) { + $pdo->query('SET NAMES \''.$params['charset'].'\''); + } + + return $pdo; + } catch (PDOException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * Constructs the Postgres PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'pgsql:'; + + if (isset($params['host']) && $params['host'] != '') { + $dsn .= 'host=' . $params['host'] . ' '; + } + + if (isset($params['port']) && $params['port'] != '') { + $dsn .= 'port=' . $params['port'] . ' '; + } + + if (isset($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ' '; + } else { + // Used for temporary connections to allow operations like dropping the database currently connected to. + // Connecting without an explicit database does not work, therefore "template1" database is used + // as it is certainly present in every server setup. + $dsn .= 'dbname=template1' . ' '; + } + + if (isset($params['sslmode'])) { + $dsn .= 'sslmode=' . $params['sslmode'] . ' '; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_pgsql'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php new file mode 100644 index 0000000..3c5382f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlite/Driver.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlite; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractSQLiteDriver; +use Doctrine\DBAL\Driver\PDOConnection; +use PDOException; + +/** + * The PDO Sqlite driver. + * + * @since 2.0 + */ +class Driver extends AbstractSQLiteDriver +{ + /** + * @var array + */ + protected $_userDefinedFunctions = array( + 'sqrt' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfSqrt'), 'numArgs' => 1), + 'mod' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfMod'), 'numArgs' => 2), + 'locate' => array('callback' => array('Doctrine\DBAL\Platforms\SqlitePlatform', 'udfLocate'), 'numArgs' => -1), + ); + + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if (isset($driverOptions['userDefinedFunctions'])) { + $this->_userDefinedFunctions = array_merge( + $this->_userDefinedFunctions, $driverOptions['userDefinedFunctions']); + unset($driverOptions['userDefinedFunctions']); + } + + try { + $pdo = new PDOConnection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } catch (PDOException $ex) { + throw DBALException::driverException($this, $ex); + } + + foreach ($this->_userDefinedFunctions as $fn => $data) { + $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); + } + + return $pdo; + } + + /** + * Constructs the Sqlite PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + protected function _constructPdoDsn(array $params) + { + $dsn = 'sqlite:'; + if (isset($params['path'])) { + $dsn .= $params['path']; + } elseif (isset($params['memory'])) { + $dsn .= ':memory:'; + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlite'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php new file mode 100644 index 0000000..4c04bfb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -0,0 +1,45 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\PDOConnection; + +/** + * Sqlsrv Connection implementation. + * + * @since 2.0 + */ +class Connection extends PDOConnection implements \Doctrine\DBAL\Driver\Connection +{ + /** + * @override + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + $val = parent::quote($value, $type); + + // Fix for a driver version terminating all values with null byte + if (strpos($val, "\0") !== false) { + $val = substr($val, 0, -1); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php new file mode 100644 index 0000000..64b91f1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Driver.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Driver\PDOSqlsrv; + +use Doctrine\DBAL\Driver\AbstractSQLServerDriver; + +/** + * The PDO-based Sqlsrv driver. + * + * @since 2.0 + */ +class Driver extends AbstractSQLServerDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new Connection( + $this->_constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Sqlsrv PDO DSN. + * + * @param array $params + * + * @return string The DSN. + */ + private function _constructPdoDsn(array $params) + { + $dsn = 'sqlsrv:server='; + + if (isset($params['host'])) { + $dsn .= $params['host']; + } + + if (isset($params['port']) && !empty($params['port'])) { + $dsn .= ',' . $params['port']; + } + + if (isset($params['dbname'])) { + $dsn .= ';Database=' . $params['dbname']; + } + + if (isset($params['MultipleActiveResultSets'])) { + $dsn .= '; MultipleActiveResultSets=' . ($params['MultipleActiveResultSets'] ? 'true' : 'false'); + } + + return $dsn; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlsrv'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php new file mode 100644 index 0000000..b6a8cb4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -0,0 +1,156 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * The PDO implementation of the Statement interface. + * Used by all PDO-based drivers. + * + * @since 2.0 + */ +class PDOStatement extends \PDOStatement implements Statement +{ + /** + * Protected constructor. + */ + protected function __construct() + { + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + // This thin wrapper is necessary to shield against the weird signature + // of PDOStatement::setFetchMode(): even if the second and third + // parameters are optional, PHP will not let us remove it from this + // declaration. + try { + if ($arg2 === null && $arg3 === null) { + return parent::setFetchMode($fetchMode); + } + + if ($arg3 === null) { + return parent::setFetchMode($fetchMode, $arg2); + } + + return parent::setFetchMode($fetchMode, $arg2, $arg3); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = \PDO::PARAM_STR) + { + try { + return parent::bindValue($param, $value, $type); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = \PDO::PARAM_STR, $length = null, $driverOptions = null) + { + try { + return parent::bindParam($column, $variable, $type, $length, $driverOptions); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + try { + return parent::execute($params); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, $cursorOrientation = null, $cursorOffset = null) + { + try { + if ($fetchMode === null && $cursorOrientation === null && $cursorOffset === null) { + return parent::fetch(); + } + + if ($cursorOrientation === null && $cursorOffset === null) { + return parent::fetch($fetchMode); + } + + if ($cursorOffset === null) { + return parent::fetch($fetchMode, $cursorOrientation); + } + + return parent::fetch($fetchMode, $cursorOrientation, $cursorOffset); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + { + try { + if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) { + return parent::fetchAll(); + } + + if ($fetchArgument === null && $ctorArgs === null) { + return parent::fetchAll($fetchMode); + } + + if ($ctorArgs === null) { + return parent::fetchAll($fetchMode, $fetchArgument); + } + + return parent::fetchAll($fetchMode, $fetchArgument, $ctorArgs); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + try { + return parent::fetchColumn($columnIndex); + } catch (\PDOException $exception) { + throw new PDOException($exception); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php new file mode 100644 index 0000000..8c85a82 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PingableConnection.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * An interface for connections which support a "native" ping method. + * + * @link www.doctrine-project.org + * @since 2.5 + * @author Till Klampaeckel + * @author Benjamin Eberlei + */ +interface PingableConnection +{ + /** + * Pings the database server to determine if the connection is still + * available. Return true/false based on if that was successful or not. + * + * @return bool + */ + public function ping(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php new file mode 100644 index 0000000..ad76ddb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Interface for the reading part of a prepare statement only. + * + * @author Benjamin Eberlei + */ +interface ResultStatement extends \Traversable +{ + /** + * Closes the cursor, enabling the statement to be executed again. + * + * @return boolean TRUE on success or FALSE on failure. + */ + public function closeCursor(); + + /** + * Returns the number of columns in the result set + * + * @return integer The number of columns in the result set represented + * by the PDOStatement object. If there is no result set, + * this method should return 0. + */ + public function columnCount(); + + /** + * Sets the fetch mode to use while iterating this statement. + * + * @param integer $fetchMode The fetch mode must be one of the PDO::FETCH_* constants. + * @param mixed $arg2 + * @param mixed $arg3 + * + * @return boolean + * + * @see PDO::FETCH_* constants. + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); + + /** + * Returns the next row of a result set. + * + * @param integer|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the PDO::FETCH_* constants, + * defaulting to PDO::FETCH_BOTH. + * + * @return mixed The return value of this method on success depends on the fetch mode. In all cases, FALSE is + * returned on failure. + * + * @see PDO::FETCH_* constants. + */ + public function fetch($fetchMode = null); + + /** + * Returns an array containing all of the result set rows. + * + * @param integer|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the PDO::FETCH_* constants, + * defaulting to PDO::FETCH_BOTH. + * + * @return array + * + * @see PDO::FETCH_* constants. + */ + public function fetchAll($fetchMode = null); + + /** + * Returns a single column from the next row of a result set or FALSE if there are no more rows. + * + * @param integer $columnIndex 0-indexed number of the column you wish to retrieve from the row. + * If no value is supplied, PDOStatement->fetchColumn() + * fetches the first column. + * + * @return string|boolean A single column in the next row of a result set, or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php new file mode 100644 index 0000000..cb694a3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/Driver.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver; + +/** + * A Doctrine DBAL driver for the SAP Sybase SQL Anywhere PHP extension. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class Driver extends AbstractSQLAnywhereDriver +{ + /** + * {@inheritdoc} + * + * @throws \Doctrine\DBAL\DBALException if there was a problem establishing the connection. + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + try { + return new SQLAnywhereConnection( + $this->buildDsn( + isset($params['host']) ? $params['host'] : null, + isset($params['port']) ? $params['port'] : null, + isset($params['server']) ? $params['server'] : null, + isset($params['dbname']) ? $params['dbname'] : null, + $username, + $password, + $driverOptions + ), + isset($params['persistent']) ? $params['persistent'] : false + ); + } catch (SQLAnywhereException $e) { + throw DBALException::driverException($this, $e); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlanywhere'; + } + + /** + * Build the connection string for given connection parameters and driver options. + * + * @param string $host Host address to connect to. + * @param integer $port Port to use for the connection (default to SQL Anywhere standard port 2638). + * @param string $server Database server name on the host to connect to. + * SQL Anywhere allows multiple database server instances on the same host, + * therefore specifying the server instance name to use is mandatory. + * @param string $dbname Name of the database on the server instance to connect to. + * @param string $username User name to use for connection authentication. + * @param string $password Password to use for connection authentication. + * @param array $driverOptions Additional parameters to use for the connection. + * + * @return string + */ + private function buildDsn($host, $port, $server, $dbname, $username = null, $password = null, array $driverOptions = array()) + { + $host = $host ?: 'localhost'; + $port = $port ?: 2638; + + if (! empty($server)) { + $server = ';ServerName=' . $server; + } + + return + 'HOST=' . $host . ':' . $port . + $server . + ';DBN=' . $dbname . + ';UID=' . $username . + ';PWD=' . $password . + ';' . implode( + ';', + array_map(function ($key, $value) { + return $key . '=' . $value; + }, array_keys($driverOptions), $driverOptions) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php new file mode 100644 index 0000000..79fbb36 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php @@ -0,0 +1,223 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * SAP Sybase SQL Anywhere implementation of the Connection interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource The SQL Anywhere connection resource. + */ + private $connection; + + /** + * Constructor. + * + * Connects to database with given connection string. + * + * @param string $dsn The connection string. + * @param boolean $persistent Whether or not to establish a persistent connection. + * + * @throws SQLAnywhereException + */ + public function __construct($dsn, $persistent = false) + { + $this->connection = $persistent ? @sasql_pconnect($dsn) : @sasql_connect($dsn); + + if ( ! is_resource($this->connection) || get_resource_type($this->connection) !== 'SQLAnywhere connection') { + throw SQLAnywhereException::fromSQLAnywhereError(); + } + + // Disable PHP warnings on error. + if ( ! sasql_set_option($this->connection, 'verbose_errors', false)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + // Enable auto committing by default. + if ( ! sasql_set_option($this->connection, 'auto_commit', 'on')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + // Enable exact, non-approximated row count retrieval. + if ( ! sasql_set_option($this->connection, 'row_counts', true)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function beginTransaction() + { + if ( ! sasql_set_option($this->connection, 'auto_commit', 'off')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function commit() + { + if ( ! sasql_commit($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + $this->endTransaction(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return sasql_errorcode($this->connection); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sasql_error($this->connection); + } + + /** + * {@inheritdoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + return $this->query("SELECT PROPERTY('ProductVersion')")->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function lastInsertId($name = null) + { + if (null === $name) { + return sasql_insert_id($this->connection); + } + + return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function prepare($prepareString) + { + return new SQLAnywhereStatement($this->connection, $prepareString); + } + + /** + * {@inheritdoc} + */ + public function query() + { + $args = func_get_args(); + $stmt = $this->prepare($args[0]); + + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function quote($input, $type = \PDO::PARAM_STR) + { + if (is_int($input) || is_float($input)) { + return $input; + } + + return "'" . sasql_escape_string($this->connection, $input) . "'"; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function rollBack() + { + if ( ! sasql_rollback($this->connection)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + $this->endTransaction(); + + return true; + } + + /** + * Ends transactional mode and enables auto commit again. + * + * @throws SQLAnywhereException + * + * @return boolean Whether or not ending transactional mode succeeded. + */ + private function endTransaction() + { + if ( ! sasql_set_option($this->connection, 'auto_commit', 'on')) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php new file mode 100644 index 0000000..b68777d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereException.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use Doctrine\DBAL\Driver\AbstractDriverException; + +/** + * SAP Sybase SQL Anywhere driver exception. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereException extends AbstractDriverException +{ + /** + * Helper method to turn SQL Anywhere error into exception. + * + * @param resource|null $conn The SQL Anywhere connection resource to retrieve the last error from. + * @param resource|null $stmt The SQL Anywhere statement resource to retrieve the last error from. + * + * @return SQLAnywhereException + * + * @throws \InvalidArgumentException + */ + public static function fromSQLAnywhereError($conn = null, $stmt = null) + { + if (null !== $conn && ! (is_resource($conn) && get_resource_type($conn) === 'SQLAnywhere connection')) { + throw new \InvalidArgumentException('Invalid SQL Anywhere connection resource given: ' . $conn); + } + + if (null !== $stmt && ! (is_resource($stmt) && get_resource_type($stmt) === 'SQLAnywhere statement')) { + throw new \InvalidArgumentException('Invalid SQL Anywhere statement resource given: ' . $stmt); + } + + $state = $conn ? sasql_sqlstate($conn) : sasql_sqlstate(); + $code = null; + $message = null; + + /** + * Try retrieving the last error from statement resource if given + */ + if ($stmt) { + $code = sasql_stmt_errno($stmt); + $message = sasql_stmt_error($stmt); + } + + /** + * Try retrieving the last error from the connection resource + * if either the statement resource is not given or the statement + * resource is given but the last error could not be retrieved from it (fallback). + * Depending on the type of error, it is sometimes necessary to retrieve + * it from the connection resource even though it occurred during + * a prepared statement. + */ + if ($conn && ! $code) { + $code = sasql_errorcode($conn); + $message = sasql_error($conn); + } + + /** + * Fallback mode if either no connection resource is given + * or the last error could not be retrieved from the given + * connection / statement resource. + */ + if ( ! $conn || ! $code) { + $code = sasql_errorcode(); + $message = sasql_error(); + } + + if ($message) { + return new self('SQLSTATE [' . $state . '] [' . $code . '] ' . $message, $state, $code); + } + + return new self('SQL Anywhere error occurred but no error message was retrieved from driver.', $state, $code); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php new file mode 100644 index 0000000..7a3b120 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -0,0 +1,347 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLAnywhere; + +use IteratorAggregate; +use PDO; +use Doctrine\DBAL\Driver\Statement; + +/** + * SAP SQL Anywhere implementation of the Statement interface. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereStatement implements IteratorAggregate, Statement +{ + /** + * @var resource The connection resource. + */ + private $conn; + + /** + * @var string Name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClass = '\stdClass'; + + /** + * @var string Constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + */ + private $defaultFetchClassCtorArgs = array(); + + /** + * @var int Default fetch mode to use. + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * @var resource The result set resource to fetch. + */ + private $result; + + /** + * @var resource The prepared SQL statement to execute. + */ + private $stmt; + + /** + * Constructor. + * + * Prepares given statement for given connection. + * + * @param resource $conn The connection resource to use. + * @param string $sql The SQL statement to prepare. + * + * @throws SQLAnywhereException + */ + public function __construct($conn, $sql) + { + if ( ! is_resource($conn) || get_resource_type($conn) !== 'SQLAnywhere connection') { + throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn); + } + + $this->conn = $conn; + $this->stmt = sasql_prepare($conn, $sql); + + if ( ! is_resource($this->stmt) || get_resource_type($this->stmt) !== 'SQLAnywhere statement') { + throw SQLAnywhereException::fromSQLAnywhereError($conn); + } + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + switch ($type) { + case PDO::PARAM_INT: + case PDO::PARAM_BOOL: + $type = 'i'; + break; + case PDO::PARAM_LOB: + $type = 'b'; + break; + case PDO::PARAM_NULL: + case PDO::PARAM_STR: + $type = 's'; + break; + default: + throw new SQLAnywhereException('Unknown type: ' . $type); + } + + if ( ! sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type); + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function closeCursor() + { + if ( ! sasql_stmt_free_result($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return sasql_stmt_field_count($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return sasql_stmt_errno($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sasql_stmt_error($this->stmt); + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function execute($params = null) + { + if (is_array($params)) { + $hasZeroIndex = array_key_exists(0, $params); + + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + + $this->bindValue($key, $val); + } + } + + if ( ! sasql_stmt_execute($this->stmt)) { + throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); + } + + $this->result = sasql_stmt_result_metadata($this->stmt); + + return true; + } + + /** + * {@inheritdoc} + * + * @throws SQLAnywhereException + */ + public function fetch($fetchMode = null) + { + if ( ! is_resource($this->result) || get_resource_type($this->result) !== 'SQLAnywhere result') { + return false; + } + + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + switch ($fetchMode) { + case PDO::FETCH_ASSOC: + return sasql_fetch_assoc($this->result); + case PDO::FETCH_BOTH: + return sasql_fetch_array($this->result, SASQL_BOTH); + case PDO::FETCH_CLASS: + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (func_num_args() >= 2) { + $args = func_get_args(); + $className = $args[1]; + $ctorArgs = isset($args[2]) ? $args[2] : array(); + } + + $result = sasql_fetch_object($this->result); + + if ($result instanceof \stdClass) { + $result = $this->castObject($result, $className, $ctorArgs); + } + + return $result; + case PDO::FETCH_NUM: + return sasql_fetch_row($this->result); + case PDO::FETCH_OBJ: + return sasql_fetch_object($this->result); + default: + throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode); + } + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + + switch ($fetchMode) { + case PDO::FETCH_CLASS: + while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) { + $rows[] = $row; + } + break; + case PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->fetchAll()); + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sasql_stmt_affected_rows($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ? $arg2 : $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + } + + /** + * Casts a stdClass object to the given class name mapping its' properties. + * + * @param \stdClass $sourceObject Object to cast from. + * @param string|object $destinationClass Name of the class or class instance to cast to. + * @param array $ctorArgs Arguments to use for constructing the destination class instance. + * + * @return object + * + * @throws SQLAnywhereException + */ + private function castObject(\stdClass $sourceObject, $destinationClass, array $ctorArgs = array()) + { + if ( ! is_string($destinationClass)) { + if ( ! is_object($destinationClass)) { + throw new SQLAnywhereException(sprintf( + 'Destination class has to be of type string or object, %s given.', gettype($destinationClass) + )); + } + } else { + $destinationClass = new \ReflectionClass($destinationClass); + $destinationClass = $destinationClass->newInstanceArgs($ctorArgs); + } + + $sourceReflection = new \ReflectionObject($sourceObject); + $destinationClassReflection = new \ReflectionObject($destinationClass); + + foreach ($sourceReflection->getProperties() as $sourceProperty) { + $sourceProperty->setAccessible(true); + + $name = $sourceProperty->getName(); + $value = $sourceProperty->getValue($sourceObject); + + if ($destinationClassReflection->hasProperty($name)) { + $destinationProperty = $destinationClassReflection->getProperty($name); + + $destinationProperty->setAccessible(true); + $destinationProperty->setValue($destinationClass, $value); + } else { + $destinationClass->$name = $value; + } + } + + return $destinationClass; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php new file mode 100644 index 0000000..f942397 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/Driver.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\Driver\AbstractSQLServerDriver; + +/** + * Driver for ext/sqlsrv. + */ +class Driver extends AbstractSQLServerDriver +{ + /** + * {@inheritdoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + if (!isset($params['host'])) { + throw new SQLSrvException("Missing 'host' in configuration for sqlsrv driver."); + } + + $serverName = $params['host']; + if (isset($params['port'])) { + $serverName .= ', ' . $params['port']; + } + + if (isset($params['dbname'])) { + $driverOptions['Database'] = $params['dbname']; + } + + if (isset($params['charset'])) { + $driverOptions['CharacterSet'] = $params['charset']; + } + + $driverOptions['UID'] = $username; + $driverOptions['PWD'] = $password; + + if (!isset($driverOptions['ReturnDatesAsStrings'])) { + $driverOptions['ReturnDatesAsStrings'] = 1; + } + + return new SQLSrvConnection($serverName, $driverOptions); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlsrv'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php new file mode 100644 index 0000000..67c7bfa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/LastInsertId.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +/** + * Last Id Data Container. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class LastInsertId +{ + /** + * @var integer + */ + private $id; + + /** + * @param integer $id + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * @return integer + */ + public function getId() + { + return $this->id; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php new file mode 100644 index 0000000..514ae54 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -0,0 +1,193 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; + +/** + * SQL Server implementation for the Connection interface. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvConnection implements Connection, ServerInfoAwareConnection +{ + /** + * @var resource + */ + protected $conn; + + /** + * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId + */ + protected $lastInsertId; + + /** + * @param string $serverName + * @param array $connectionOptions + * + * @throws \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException + */ + public function __construct($serverName, $connectionOptions) + { + if ( ! sqlsrv_configure('WarningsReturnAsErrors', 0)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + $this->conn = sqlsrv_connect($serverName, $connectionOptions); + if ( ! $this->conn) { + throw SQLSrvException::fromSqlSrvErrors(); + } + $this->lastInsertId = new LastInsertId(); + } + + /** + * {@inheritdoc} + */ + public function getServerVersion() + { + $serverInfo = sqlsrv_server_info($this->conn); + + return $serverInfo['SQLServerVersion']; + } + + /** + * {@inheritdoc} + */ + public function requiresQueryForServerVersion() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function prepare($sql) + { + return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId); + } + + /** + * {@inheritDoc} + */ + public function query() + { + $args = func_get_args(); + $sql = $args[0]; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt; + } + + /** + * {@inheritDoc} + * @license New BSD, code from Zend Framework + */ + public function quote($value, $type=\PDO::PARAM_STR) + { + if (is_int($value)) { + return $value; + } elseif (is_float($value)) { + return sprintf('%F', $value); + } + + return "'" . str_replace("'", "''", $value) . "'"; + } + + /** + * {@inheritDoc} + */ + public function exec($statement) + { + $stmt = $this->prepare($statement); + $stmt->execute(); + + return $stmt->rowCount(); + } + + /** + * {@inheritDoc} + */ + public function lastInsertId($name = null) + { + if ($name !== null) { + $sql = "SELECT IDENT_CURRENT(".$this->quote($name).") AS LastInsertId"; + $stmt = $this->prepare($sql); + $stmt->execute(); + + return $stmt->fetchColumn(); + } + + return $this->lastInsertId->getId(); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + if ( ! sqlsrv_begin_transaction($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function commit() + { + if ( ! sqlsrv_commit($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function rollBack() + { + if ( ! sqlsrv_rollback($this->conn)) { + throw SQLSrvException::fromSqlSrvErrors(); + } + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php new file mode 100644 index 0000000..50705ad --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvException.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use Doctrine\DBAL\DBALException; + +class SQLSrvException extends DBALException +{ + /** + * Helper method to turn sql server errors into exception. + * + * @return \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException + */ + static public function fromSqlSrvErrors() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + $message = ""; + foreach ($errors as $error) { + $message .= "SQLSTATE [".$error['SQLSTATE'].", ".$error['code']."]: ". $error['message']."\n"; + } + if ( ! $message) { + $message = "SQL Server error occurred but no error message was retrieved from driver."; + } + + return new self(rtrim($message)); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php new file mode 100644 index 0000000..ead250c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -0,0 +1,309 @@ +. + */ + +namespace Doctrine\DBAL\Driver\SQLSrv; + +use PDO; +use IteratorAggregate; +use Doctrine\DBAL\Driver\Statement; + +/** + * SQL Server Statement. + * + * @since 2.3 + * @author Benjamin Eberlei + */ +class SQLSrvStatement implements IteratorAggregate, Statement +{ + /** + * The SQLSRV Resource. + * + * @var resource + */ + private $conn; + + /** + * The SQL statement to execute. + * + * @var string + */ + private $sql; + + /** + * The SQLSRV statement resource. + * + * @var resource + */ + private $stmt; + + /** + * Parameters to bind. + * + * @var array + */ + private $params = array(); + + /** + * Translations. + * + * @var array + */ + private static $fetchMap = array( + PDO::FETCH_BOTH => SQLSRV_FETCH_BOTH, + PDO::FETCH_ASSOC => SQLSRV_FETCH_ASSOC, + PDO::FETCH_NUM => SQLSRV_FETCH_NUMERIC, + ); + + /** + * The name of the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + * + * @var string + */ + private $defaultFetchClass = '\stdClass'; + + /** + * The constructor arguments for the default class to instantiate when fetch mode is \PDO::FETCH_CLASS. + * + * @var string + */ + private $defaultFetchClassCtorArgs = array(); + + /** + * The fetch style. + * + * @param integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * The last insert ID. + * + * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId|null + */ + private $lastInsertId; + + /** + * Append to any INSERT query to retrieve the last insert id. + * + * @var string + */ + const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; + + /** + * @param resource $conn + * @param string $sql + * @param \Doctrine\DBAL\Driver\SQLSrv\LastInsertId|null $lastInsertId + */ + public function __construct($conn, $sql, LastInsertId $lastInsertId = null) + { + $this->conn = $conn; + $this->sql = $sql; + + if (stripos($sql, 'INSERT INTO ') === 0) { + $this->sql .= self::LAST_INSERT_ID_SQL; + $this->lastInsertId = $lastInsertId; + } + } + + /** + * {@inheritdoc} + */ + public function bindValue($param, $value, $type = null) + { + return $this->bindParam($param, $value, $type, null); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + if (!is_numeric($column)) { + throw new SQLSrvException("sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead."); + } + + if ($type === \PDO::PARAM_LOB) { + $this->params[$column-1] = array($variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max')); + } else { + $this->params[$column-1] = $variable; + } + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + if ($this->stmt) { + sqlsrv_free_stmt($this->stmt); + } + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return sqlsrv_num_fields($this->stmt); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if ($errors) { + return $errors[0]['code']; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return sqlsrv_errors(SQLSRV_ERR_ERRORS); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + if ($params) { + $hasZeroIndex = array_key_exists(0, $params); + foreach ($params as $key => $val) { + $key = ($hasZeroIndex && is_numeric($key)) ? $key + 1 : $key; + $this->bindValue($key, $val); + } + } + + $this->stmt = sqlsrv_query($this->conn, $this->sql, $this->params); + if ( ! $this->stmt) { + throw SQLSrvException::fromSqlSrvErrors(); + } + + if ($this->lastInsertId) { + sqlsrv_next_result($this->stmt); + sqlsrv_fetch($this->stmt); + $this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0)); + } + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + $this->defaultFetchMode = $fetchMode; + $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; + $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + + return true; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $args = func_get_args(); + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if (isset(self::$fetchMap[$fetchMode])) { + return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]) ?: false; + } + + if ($fetchMode == PDO::FETCH_OBJ || $fetchMode == PDO::FETCH_CLASS) { + $className = $this->defaultFetchClass; + $ctorArgs = $this->defaultFetchClassCtorArgs; + + if (count($args) >= 2) { + $className = $args[1]; + $ctorArgs = (isset($args[2])) ? $args[2] : array(); + } + + return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?: false; + } + + throw new SQLSrvException("Fetch mode is not supported!"); + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null) + { + $rows = array(); + + switch ($fetchMode) { + case PDO::FETCH_CLASS: + while ($row = call_user_func_array(array($this, 'fetch'), func_get_args())) { + $rows[] = $row; + } + break; + case PDO::FETCH_COLUMN: + while ($row = $this->fetchColumn()) { + $rows[] = $row; + } + break; + default: + while ($row = $this->fetch($fetchMode)) { + $rows[] = $row; + } + } + + return $rows; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $row = $this->fetch(PDO::FETCH_NUM); + + if (false === $row) { + return false; + } + + return isset($row[$columnIndex]) ? $row[$columnIndex] : null; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return sqlsrv_rows_affected($this->stmt); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php new file mode 100644 index 0000000..f15ffef --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Contract for a connection that is able to provide information about the server it is connected to. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface ServerInfoAwareConnection +{ + /** + * Returns the version number of the database server connected to. + * + * @return string + */ + public function getServerVersion(); + + /** + * Checks whether a query is required to retrieve the database server version. + * + * @return boolean True if a query is required to retrieve the database server version, false otherwise. + */ + public function requiresQueryForServerVersion(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php new file mode 100644 index 0000000..aedf47b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\DBAL\Driver; + +/** + * Statement interface. + * Drivers must implement this interface. + * + * This resembles (a subset of) the PDOStatement interface. + * + * @author Konsta Vesterinen + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +interface Statement extends ResultStatement +{ + /** + * Binds a value to a corresponding named (not supported by mysqli driver, see comment below) or positional + * placeholder in the SQL statement that was used to prepare the statement. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * @param mixed $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position of the parameter. + * @param mixed $value The value to bind to the parameter. + * @param integer $type Explicit data type for the parameter using the PDO::PARAM_* constants. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function bindValue($param, $value, $type = null); + + + /** + * Binds a PHP variable to a corresponding named (not supported by mysqli driver, see comment below) or question + * mark placeholder in the SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(), + * the variable is bound as a reference and will only be evaluated at the time + * that PDOStatement->execute() is called. + * + * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), + * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. + * + * Most parameters are input parameters, that is, parameters that are + * used in a read-only fashion to build up the query. Some drivers support the invocation + * of stored procedures that return data as output parameters, and some also as input/output + * parameters that both send in data and are updated to receive it. + * + * @param mixed $column Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement using + * question mark placeholders, this will be the 1-indexed position of the parameter. + * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. + * @param integer|null $type Explicit data type for the parameter using the PDO::PARAM_* constants. To return + * an INOUT parameter from a stored procedure, use the bitwise OR operator to set the + * PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * @param integer|null $length You must specify maxlength when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function bindParam($column, &$variable, $type = null, $length = null); + + /** + * Fetches the SQLSTATE associated with the last operation on the statement handle. + * + * @see Doctrine_Adapter_Interface::errorCode() + * + * @return string The error code string. + */ + function errorCode(); + + /** + * Fetches extended error information associated with the last operation on the statement handle. + * + * @see Doctrine_Adapter_Interface::errorInfo() + * + * @return array The error info array. + */ + function errorInfo(); + + /** + * Executes a prepared statement + * + * If the prepared statement included parameter markers, you must either: + * call PDOStatement->bindParam() to bind PHP variables to the parameter markers: + * bound variables pass their value as input and receive the output value, + * if any, of their associated parameter markers or pass an array of input-only + * parameter values. + * + * + * @param array|null $params An array of values with as many elements as there are + * bound parameters in the SQL statement being executed. + * + * @return boolean TRUE on success or FALSE on failure. + */ + function execute($params = null); + + /** + * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement + * executed by the corresponding object. + * + * If the last SQL statement executed by the associated Statement object was a SELECT statement, + * some databases may return the number of rows returned by that statement. However, + * this behaviour is not guaranteed for all databases and should not be + * relied on for portable applications. + * + * @return integer The number of rows. + */ + function rowCount(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php new file mode 100644 index 0000000..75004a4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/DriverManager.php @@ -0,0 +1,276 @@ +. + */ + +namespace Doctrine\DBAL; + +use Doctrine\Common\EventManager; + +/** + * Factory for creating Doctrine\DBAL\Connection instances. + * + * @author Roman Borschel + * @since 2.0 + */ +final class DriverManager +{ + /** + * List of supported drivers and their mappings to the driver classes. + * + * To add your own driver use the 'driverClass' parameter to + * {@link DriverManager::getConnection()}. + * + * @var array + */ + private static $_driverMap = array( + 'pdo_mysql' => 'Doctrine\DBAL\Driver\PDOMySql\Driver', + 'pdo_sqlite' => 'Doctrine\DBAL\Driver\PDOSqlite\Driver', + 'pdo_pgsql' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver', + 'pdo_oci' => 'Doctrine\DBAL\Driver\PDOOracle\Driver', + 'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver', + 'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver', + 'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver', + 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', + 'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver', + 'sqlanywhere' => 'Doctrine\DBAL\Driver\SQLAnywhere\Driver', + 'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver', + ); + + /** + * List of URL schemes from a database URL and their mappings to driver. + */ + private static $driverSchemeAliases = array( + 'db2' => 'ibm_db2', + 'mssql' => 'pdo_sqlsrv', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'pdo_sqlite', + ); + + /** + * Private constructor. This class cannot be instantiated. + */ + private function __construct() + { + } + + /** + * Creates a connection object based on the specified parameters. + * This method returns a Doctrine\DBAL\Connection which wraps the underlying + * driver connection. + * + * $params must contain at least one of the following. + * + * Either 'driver' with one of the following values: + * + * pdo_mysql + * pdo_sqlite + * pdo_pgsql + * pdo_oci (unstable) + * pdo_sqlsrv + * pdo_sqlsrv + * mysqli + * sqlanywhere + * sqlsrv + * ibm_db2 (unstable) + * drizzle_pdo_mysql + * + * OR 'driverClass' that contains the full class name (with namespace) of the + * driver class to instantiate. + * + * Other (optional) parameters: + * + * user (string): + * The username to use when connecting. + * + * password (string): + * The password to use when connecting. + * + * driverOptions (array): + * Any additional driver-specific options for the driver. These are just passed + * through to the driver. + * + * pdo: + * You can pass an existing PDO instance through this parameter. The PDO + * instance will be wrapped in a Doctrine\DBAL\Connection. + * + * wrapperClass: + * You may specify a custom wrapper class through the 'wrapperClass' + * parameter but this class MUST inherit from Doctrine\DBAL\Connection. + * + * driverClass: + * The driver class to use. + * + * @param array $params The parameters. + * @param \Doctrine\DBAL\Configuration|null $config The configuration to use. + * @param \Doctrine\Common\EventManager|null $eventManager The event manager to use. + * + * @return \Doctrine\DBAL\Connection + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function getConnection( + array $params, + Configuration $config = null, + EventManager $eventManager = null) + { + // create default config and event manager, if not set + if ( ! $config) { + $config = new Configuration(); + } + if ( ! $eventManager) { + $eventManager = new EventManager(); + } + + $params = self::parseDatabaseUrl($params); + + // check for existing pdo object + if (isset($params['pdo']) && ! $params['pdo'] instanceof \PDO) { + throw DBALException::invalidPdoInstance(); + } elseif (isset($params['pdo'])) { + $params['pdo']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $params['driver'] = 'pdo_' . $params['pdo']->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + self::_checkParams($params); + } + if (isset($params['driverClass'])) { + $className = $params['driverClass']; + } else { + $className = self::$_driverMap[$params['driver']]; + } + + $driver = new $className(); + + $wrapperClass = 'Doctrine\DBAL\Connection'; + if (isset($params['wrapperClass'])) { + if (is_subclass_of($params['wrapperClass'], $wrapperClass)) { + $wrapperClass = $params['wrapperClass']; + } else { + throw DBALException::invalidWrapperClass($params['wrapperClass']); + } + } + + return new $wrapperClass($params, $driver, $config, $eventManager); + } + + /** + * Returns the list of supported drivers. + * + * @return array + */ + public static function getAvailableDrivers() + { + return array_keys(self::$_driverMap); + } + + /** + * Checks the list of parameters. + * + * @param array $params The list of parameters. + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + private static function _checkParams(array $params) + { + // check existence of mandatory parameters + + // driver + if ( ! isset($params['driver']) && ! isset($params['driverClass'])) { + throw DBALException::driverRequired(); + } + + // check validity of parameters + + // driver + if (isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) { + throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap)); + } + + if (isset($params['driverClass']) && ! in_array('Doctrine\DBAL\Driver', class_implements($params['driverClass'], true))) { + throw DBALException::invalidDriverClass($params['driverClass']); + } + } + + /** + * Extracts parts from a database URL, if present, and returns an + * updated list of parameters. + * + * @param array $params The list of parameters. + * + * @param array A modified list of parameters with info from a database + * URL extracted into indidivual parameter parts. + * + */ + private static function parseDatabaseUrl(array $params) + { + if (!isset($params['url'])) { + return $params; + } + + // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid + $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']); + + $url = parse_url($url); + + if ($url === false) { + throw new DBALException('Malformed parameter "url".'); + } + + if (isset($url['scheme'])) { + $params['driver'] = str_replace('-', '_', $url['scheme']); // URL schemes must not contain underscores, but dashes are ok + if (isset(self::$driverSchemeAliases[$params['driver']])) { + $params['driver'] = self::$driverSchemeAliases[$params['driver']]; // use alias like "postgres", else we just let checkParams decide later if the driver exists (for literal "pdo-pgsql" etc) + } + } + + if (isset($url['host'])) { + $params['host'] = $url['host']; + } + if (isset($url['port'])) { + $params['port'] = $url['port']; + } + if (isset($url['user'])) { + $params['user'] = $url['user']; + } + if (isset($url['pass'])) { + $params['password'] = $url['pass']; + } + + if (isset($url['path'])) { + if (!isset($url['scheme']) || (strpos($url['scheme'], 'sqlite') !== false && $url['path'] == ':memory:')) { + $params['dbname'] = $url['path']; // if the URL was just "sqlite::memory:", which parses to scheme and path only + } else { + $params['dbname'] = substr($url['path'], 1); // strip the leading slash from the URL + } + } + + if (isset($url['query'])) { + $query = array(); + parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode + $params = array_merge($params, $query); // parse_str wipes existing array elements + } + + return $params; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php new file mode 100644 index 0000000..fd82638 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/ConnectionEventArgs.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Connection; + +/** + * Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class ConnectionEventArgs extends EventArgs +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Driver + */ + public function getDriver() + { + return $this->_connection->getDriver(); + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } + + /** + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getSchemaManager() + { + return $this->_connection->getSchemaManager(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php new file mode 100644 index 0000000..bd362c4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/MysqlSessionInit.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * MySQL Session Init Event Subscriber which allows to set the Client Encoding of the Connection. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @deprecated Use "charset" option to PDO MySQL Connection instead. + */ +class MysqlSessionInit implements EventSubscriber +{ + /** + * The charset. + * + * @var string + */ + private $_charset; + + /** + * The collation, or FALSE if no collation. + * + * @var string|boolean + */ + private $_collation; + + /** + * Configure Charset and Collation options of MySQL Client for each Connection. + * + * @param string $charset The charset. + * @param string|boolean $collation The collation, or FALSE if no collation. + */ + public function __construct($charset = 'utf8', $collation = false) + { + $this->_charset = $charset; + $this->_collation = $collation; + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $collation = ($this->_collation) ? " COLLATE ".$this->_collation : ""; + $args->getConnection()->executeUpdate("SET NAMES ".$this->_charset . $collation); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php new file mode 100644 index 0000000..e19540e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/OracleSessionInit.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Should be used when Oracle Server default environment does not match the Doctrine requirements. + * + * The following environment variables are required for the Doctrine default date format: + * + * NLS_TIME_FORMAT="HH24:MI:SS" + * NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS" + * NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS TZH:TZM" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class OracleSessionInit implements EventSubscriber +{ + /** + * @var array + */ + protected $_defaultSessionVars = array( + 'NLS_TIME_FORMAT' => "HH24:MI:SS", + 'NLS_DATE_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_FORMAT' => "YYYY-MM-DD HH24:MI:SS", + 'NLS_TIMESTAMP_TZ_FORMAT' => "YYYY-MM-DD HH24:MI:SS TZH:TZM", + 'NLS_NUMERIC_CHARACTERS' => ".,", + ); + + /** + * @param array $oracleSessionVars + */ + public function __construct(array $oracleSessionVars = array()) + { + $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + if (count($this->_defaultSessionVars)) { + array_change_key_case($this->_defaultSessionVars, \CASE_UPPER); + $vars = array(); + foreach ($this->_defaultSessionVars as $option => $value) { + $vars[] = $option." = '".$value."'"; + } + $sql = "ALTER SESSION SET ".implode(" ", $vars); + $args->getConnection()->executeUpdate($sql); + } + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php new file mode 100644 index 0000000..cfa4299 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/Listeners/SQLSessionInit.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\DBAL\Event\Listeners; + +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventSubscriber; + +/** + * Session init listener for executing a single SQL statement right after a connection is opened. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Benjamin Eberlei + */ +class SQLSessionInit implements EventSubscriber +{ + /** + * @var string + */ + protected $sql; + + /** + * @param string $sql + */ + public function __construct($sql) + { + $this->sql = $sql; + } + + /** + * @param \Doctrine\DBAL\Event\ConnectionEventArgs $args + * + * @return void + */ + public function postConnect(ConnectionEventArgs $args) + { + $conn = $args->getConnection(); + $conn->exec($this->sql); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array(Events::postConnect); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php new file mode 100644 index 0000000..c4477ad --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableAddColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php new file mode 100644 index 0000000..7249b63 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableChangeColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\ColumnDiff + */ + private $_columnDiff; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_columnDiff = $columnDiff; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\ColumnDiff + */ + public function getColumnDiff() + { + return $this->_columnDiff; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php new file mode 100644 index 0000000..384315d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableEventArgs.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php new file mode 100644 index 0000000..06f3788 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRemoveColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php new file mode 100644 index 0000000..94a274c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaAlterTableRenameColumnEventArgs.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs +{ + /** + * @var string + */ + private $_oldColumnName; + + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\TableDiff + */ + private $_tableDiff; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param string $oldColumnName + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct($oldColumnName, Column $column, TableDiff $tableDiff, AbstractPlatform $platform) + { + $this->_oldColumnName = $oldColumnName; + $this->_column = $column; + $this->_tableDiff = $tableDiff; + $this->_platform = $platform; + } + + /** + * @return string + */ + public function getOldColumnName() + { + return $this->_oldColumnName; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\TableDiff + */ + public function getTableDiff() + { + return $this->_tableDiff; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php new file mode 100644 index 0000000..14bb8df --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaColumnDefinitionEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Column; + +/** + * Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaColumnDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column|null + */ + private $_column = null; + + /** + * Raw column data as fetched from the database. + * + * @var array + */ + private $_tableColumn; + + /** + * @var string + */ + private $_table; + + /** + * @var string + */ + private $_database; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param array $tableColumn + * @param string $table + * @param string $database + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(array $tableColumn, $table, $database, Connection $connection) + { + $this->_tableColumn = $tableColumn; + $this->_table = $table; + $this->_database = $database; + $this->_connection = $connection; + } + + /** + * Allows to clear the column which means the column will be excluded from + * tables column list. + * + * @param null|\Doctrine\DBAL\Schema\Column $column + * + * @return \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs + */ + public function setColumn(Column $column = null) + { + $this->_column = $column; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Column|null + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return array + */ + public function getTableColumn() + { + return $this->_tableColumn; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return string + */ + public function getDatabase() + { + return $this->_database; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php new file mode 100644 index 0000000..38b2182 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableColumnEventArgs.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableColumnEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Column + */ + private $_column; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Column $column, Table $table, AbstractPlatform $platform) + { + $this->_column = $column; + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Column + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php new file mode 100644 index 0000000..175b0e9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaCreateTableEventArgs.php @@ -0,0 +1,128 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaCreateTableEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var array + */ + private $_columns; + + /** + * @var array + */ + private $_options; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var array + */ + private $_sql = array(); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param array $columns + * @param array $options + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + */ + public function __construct(Table $table, array $columns, array $options, AbstractPlatform $platform) + { + $this->_table = $table; + $this->_columns = $columns; + $this->_options = $options; + $this->_platform = $platform; + } + + /** + * @return \Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return array + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string|array $sql + * + * @return \Doctrine\DBAL\Event\SchemaCreateTableEventArgs + */ + public function addSql($sql) + { + if (is_array($sql)) { + $this->_sql = array_merge($this->_sql, $sql); + } else { + $this->_sql[] = $sql; + } + + return $this; + } + + /** + * @return array + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php new file mode 100644 index 0000000..6ffb644 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaDropTableEventArgs.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaDropTableEventArgs extends SchemaEventArgs +{ + /** + * @var string|\Doctrine\DBAL\Schema\Table + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $_platform; + + /** + * @var string|null + */ + private $_sql = null; + + /** + * @param string|\Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @throws \InvalidArgumentException + */ + public function __construct($table, AbstractPlatform $platform) + { + if ( ! $table instanceof Table && !is_string($table)) { + throw new \InvalidArgumentException('SchemaDropTableEventArgs expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + $this->_table = $table; + $this->_platform = $platform; + } + + /** + * @return string|\Doctrine\DBAL\Schema\Table + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getPlatform() + { + return $this->_platform; + } + + /** + * @param string $sql + * + * @return \Doctrine\DBAL\Event\SchemaDropTableEventArgs + */ + public function setSql($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * @return string|null + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php new file mode 100644 index 0000000..d9648b6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaEventArgs.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\Common\EventArgs; + +/** + * Base class for schema related events. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaEventArgs extends EventArgs +{ + /** + * @var boolean + */ + private $_preventDefault = false; + + /** + * @return \Doctrine\DBAL\Event\SchemaEventArgs + */ + public function preventDefault() + { + $this->_preventDefault = true; + + return $this; + } + + /** + * @return boolean + */ + public function isDefaultPrevented() + { + return $this->_preventDefault; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php new file mode 100644 index 0000000..3fd1d4b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Event/SchemaIndexDefinitionEventArgs.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\DBAL\Event; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Index; + +/** + * Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Jan Sorgalla + */ +class SchemaIndexDefinitionEventArgs extends SchemaEventArgs +{ + /** + * @var \Doctrine\DBAL\Schema\Index|null + */ + private $_index = null; + + /** + * Raw index data as fetched from the database. + * + * @var array + */ + private $_tableIndex; + + /** + * @var string + */ + private $_table; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $_connection; + + /** + * @param array $tableIndex + * @param string $table + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(array $tableIndex, $table, Connection $connection) + { + $this->_tableIndex = $tableIndex; + $this->_table = $table; + $this->_connection = $connection; + } + + /** + * Allows to clear the index which means the index will be excluded from tables index list. + * + * @param null|\Doctrine\DBAL\Schema\Index $index + * + * @return SchemaIndexDefinitionEventArgs + */ + public function setIndex(Index $index = null) + { + $this->_index = $index; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Schema\Index|null + */ + public function getIndex() + { + return $this->_index; + } + + /** + * @return array + */ + public function getTableIndex() + { + return $this->_tableIndex; + } + + /** + * @return string + */ + public function getTable() + { + return $this->_table; + } + + /** + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_connection->getDatabasePlatform(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php new file mode 100644 index 0000000..0d31504 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Container for all DBAL events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + /** + * Private constructor. This class cannot be instantiated. + */ + private function __construct() + { + } + + const postConnect = 'postConnect'; + + const onSchemaCreateTable = 'onSchemaCreateTable'; + const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn'; + const onSchemaDropTable = 'onSchemaDropTable'; + const onSchemaAlterTable = 'onSchemaAlterTable'; + const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn'; + const onSchemaAlterTableRemoveColumn = 'onSchemaAlterTableRemoveColumn'; + const onSchemaAlterTableChangeColumn = 'onSchemaAlterTableChangeColumn'; + const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn'; + const onSchemaColumnDefinition = 'onSchemaColumnDefinition'; + const onSchemaIndexDefinition = 'onSchemaIndexDefinition'; +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConnectionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConnectionException.php new file mode 100644 index 0000000..8d976dc --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConnectionException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all connection related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ConnectionException extends DriverException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConstraintViolationException.php new file mode 100644 index 0000000..8dd1cb6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ConstraintViolationException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all constraint violation related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ConstraintViolationException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php new file mode 100644 index 0000000..279aae9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectExistsException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all already existing database object related errors detected in the driver. + * + * A database object is considered any asset that can be created in a database + * such as schemas, tables, views, sequences, triggers, constraints, indexes, + * functions, stored procedures etc. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DatabaseObjectExistsException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php new file mode 100644 index 0000000..fbca22d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DatabaseObjectNotFoundException.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all unknown database object related errors detected in the driver. + * + * A database object is considered any asset that can be created in a database + * such as schemas, tables, views, sequences, triggers, constraints, indexes, + * functions, stored procedures etc. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DatabaseObjectNotFoundException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DriverException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DriverException.php new file mode 100644 index 0000000..17c9f3a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/DriverException.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +use Doctrine\DBAL\DBALException; + +/** + * Base class for all errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class DriverException extends DBALException +{ + /** + * The previous DBAL driver exception. + * + * @var \Doctrine\DBAL\Driver\DriverException + */ + private $driverException; + + /** + * Constructor. + * + * @param string $message The exception message. + * @param \Doctrine\DBAL\Driver\DriverException $driverException The DBAL driver exception to chain. + */ + public function __construct($message, \Doctrine\DBAL\Driver\DriverException $driverException) + { + $exception = null; + + if ($driverException instanceof \Exception) { + $exception = $driverException; + } + + parent::__construct($message, 0, $exception); + + $this->driverException = $driverException; + } + + /** + * Returns the driver specific error code if given. + * + * Returns null if no error code was given by the driver. + * + * @return integer|string|null + */ + public function getErrorCode() + { + return $this->driverException->getErrorCode(); + } + + /** + * Returns the SQLSTATE the driver was in at the time the error occurred, if given. + * + * Returns null if no SQLSTATE was given by the driver. + * + * @return string|null + */ + public function getSQLState() + { + return $this->driverException->getSQLState(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php new file mode 100644 index 0000000..d244855 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ForeignKeyConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a foreign key constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ForeignKeyConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..5b81122 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidArgumentException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +use Doctrine\DBAL\DBALException; + +/** + * Exception to be thrown when invalid arguments are passed to any DBAL API + * + * @author Marco Pivetta + * @link www.doctrine-project.org + * @since 2.5 + */ +class InvalidArgumentException extends DBALException +{ + /** + * @return self + */ + public static function fromEmptyCriteria() + { + return new self('Empty criteria was used, expected non-empty criteria'); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php new file mode 100644 index 0000000..e0b1013 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/InvalidFieldNameException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an invalid specified field name in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class InvalidFieldNameException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php new file mode 100644 index 0000000..7b214e3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NonUniqueFieldNameException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a non-unique/ambiguous specified field name in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class NonUniqueFieldNameException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php new file mode 100644 index 0000000..a1f45b9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/NotNullConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a NOT NULL constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class NotNullConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ReadOnlyException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ReadOnlyException.php new file mode 100644 index 0000000..0345e88 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ReadOnlyException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a write operation attempt on a read-only database element detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ReadOnlyException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ServerException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ServerException.php new file mode 100644 index 0000000..36460ee --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/ServerException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Base class for all server related errors detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class ServerException extends DriverException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/SyntaxErrorException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/SyntaxErrorException.php new file mode 100644 index 0000000..97b8fd2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/SyntaxErrorException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a syntax error in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SyntaxErrorException extends ServerException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableExistsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableExistsException.php new file mode 100644 index 0000000..c90bfb4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableExistsException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an already existing table referenced in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class TableExistsException extends DatabaseObjectExistsException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableNotFoundException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableNotFoundException.php new file mode 100644 index 0000000..90793b9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/TableNotFoundException.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for an unknown table referenced in a statement detected in the driver. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class TableNotFoundException extends DatabaseObjectNotFoundException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php new file mode 100644 index 0000000..cd11773 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Exception/UniqueConstraintViolationException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\DBAL\Exception; + +/** + * Exception for a unique constraint violation detected in the driver. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class UniqueConstraintViolationException extends ConstraintViolationException +{ +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php new file mode 100644 index 0000000..62d6fcb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGenerator.php @@ -0,0 +1,165 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Connection; + +/** + * Table ID Generator for those poor languages that are missing sequences. + * + * WARNING: The Table Id Generator clones a second independent database + * connection to work correctly. This means using the generator requests that + * generate IDs will have two open database connections. This is necessary to + * be safe from transaction failures in the main connection. Make sure to only + * ever use one TableGenerator otherwise you end up with many connections. + * + * TableID Generator does not work with SQLite. + * + * The TableGenerator does not take care of creating the SQL Table itself. You + * should look at the `TableGeneratorSchemaVisitor` to do this for you. + * Otherwise the schema for a table looks like: + * + * CREATE sequences ( + * sequence_name VARCHAR(255) NOT NULL, + * sequence_value INT NOT NULL DEFAULT '1', + * sequence_increment_by INT NOT NULL DEFAULT '1', + * PRIMARY KEY (table_name) + * ); + * + * Technically this generator works as follows: + * + * 1. Use a robust transaction serialization level. + * 2. Open transaction + * 3. Acquire a read lock on the table row (SELECT .. FOR UPDATE) + * 4. Increment current value by one and write back to database + * 5. Commit transaction + * + * If you are using a sequence_increment_by value that is larger than one the + * ID Generator will keep incrementing values until it hits the incrementation + * gap before issuing another query. + * + * If no row is present for a given sequence a new one will be created with the + * default values 'value' = 1 and 'increment_by' = 1 + * + * @author Benjamin Eberlei + */ +class TableGenerator +{ + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $generatorTableName; + + /** + * @var array + */ + private $sequences = array(); + + /** + * @param \Doctrine\DBAL\Connection $conn + * @param string $generatorTableName + * + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(Connection $conn, $generatorTableName = 'sequences') + { + $params = $conn->getParams(); + if ($params['driver'] == 'pdo_sqlite') { + throw new \Doctrine\DBAL\DBALException("Cannot use TableGenerator with SQLite."); + } + $this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager()); + $this->generatorTableName = $generatorTableName; + } + + /** + * Generates the next unused value for the given sequence name. + * + * @param string $sequenceName + * + * @return integer + * + * @throws \Doctrine\DBAL\DBALException + */ + public function nextValue($sequenceName) + { + if (isset($this->sequences[$sequenceName])) { + $value = $this->sequences[$sequenceName]['value']; + $this->sequences[$sequenceName]['value']++; + if ($this->sequences[$sequenceName]['value'] >= $this->sequences[$sequenceName]['max']) { + unset ($this->sequences[$sequenceName]); + } + + return $value; + } + + $this->conn->beginTransaction(); + + try { + $platform = $this->conn->getDatabasePlatform(); + $sql = "SELECT sequence_value, sequence_increment_by " . + "FROM " . $platform->appendLockHint($this->generatorTableName, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) . " " . + "WHERE sequence_name = ? " . $platform->getWriteLockSQL(); + $stmt = $this->conn->executeQuery($sql, array($sequenceName)); + + if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $row = array_change_key_case($row, CASE_LOWER); + + $value = $row['sequence_value']; + $value++; + + if ($row['sequence_increment_by'] > 1) { + $this->sequences[$sequenceName] = array( + 'value' => $value, + 'max' => $row['sequence_value'] + $row['sequence_increment_by'] + ); + } + + $sql = "UPDATE " . $this->generatorTableName . " ". + "SET sequence_value = sequence_value + sequence_increment_by " . + "WHERE sequence_name = ? AND sequence_value = ?"; + $rows = $this->conn->executeUpdate($sql, array($sequenceName, $row['sequence_value'])); + + if ($rows != 1) { + throw new \Doctrine\DBAL\DBALException("Race-condition detected while updating sequence. Aborting generation"); + } + } else { + $this->conn->insert( + $this->generatorTableName, + array('sequence_name' => $sequenceName, 'sequence_value' => 1, 'sequence_increment_by' => 1) + ); + $value = 1; + } + + $this->conn->commit(); + + } catch (\Exception $e) { + $this->conn->rollback(); + throw new \Doctrine\DBAL\DBALException("Error occurred while generating ID with TableGenerator, aborted generation: " . $e->getMessage(), 0, $e); + } + + return $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php new file mode 100644 index 0000000..25b06ac --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Id/TableGeneratorSchemaVisitor.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\DBAL\Id; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class TableGeneratorSchemaVisitor implements \Doctrine\DBAL\Schema\Visitor\Visitor +{ + /** + * @var string + */ + private $generatorTableName; + + /** + * @param string $generatorTableName + */ + public function __construct($generatorTableName = 'sequences') + { + $this->generatorTableName = $generatorTableName; + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $table = $schema->createTable($this->generatorTableName); + $table->addColumn('sequence_name', 'string'); + $table->addColumn('sequence_value', 'integer', array('default' => 1)); + $table->addColumn('sequence_increment_by', 'integer', array('default' => 1)); + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php new file mode 100644 index 0000000..8d78180 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/LockMode.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Contains all DBAL LockModes. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class LockMode +{ + const NONE = 0; + const OPTIMISTIC = 1; + const PESSIMISTIC_READ = 2; + const PESSIMISTIC_WRITE = 4; + + /** + * Private constructor. This class cannot be instantiated. + */ + final private function __construct() + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php new file mode 100644 index 0000000..b143a3e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/DebugStack.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Includes executed SQLs in a Debug Stack. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DebugStack implements SQLLogger +{ + /** + * Executed SQL queries. + * + * @var array + */ + public $queries = array(); + + /** + * If Debug Stack is enabled (log queries) or not. + * + * @var boolean + */ + public $enabled = true; + + /** + * @var float|null + */ + public $start = null; + + /** + * @var integer + */ + public $currentQuery = 0; + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + if ($this->enabled) { + $this->start = microtime(true); + $this->queries[++$this->currentQuery] = array('sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + if ($this->enabled) { + $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php new file mode 100644 index 0000000..8ed0478 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * A SQL logger that logs to the standard output using echo/var_dump. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EchoSQLLogger implements SQLLogger +{ + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + echo $sql . PHP_EOL; + + if ($params) { + var_dump($params); + } + + if ($types) { + var_dump($types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php new file mode 100644 index 0000000..81845b1 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Chains multiple SQLLogger. + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Christophe Coevoet + */ +class LoggerChain implements SQLLogger +{ + /** + * @var \Doctrine\DBAL\Logging\SQLLogger[] + */ + private $loggers = array(); + + /** + * Adds a logger in the chain. + * + * @param \Doctrine\DBAL\Logging\SQLLogger $logger + * + * @return void + */ + public function addLogger(SQLLogger $logger) + { + $this->loggers[] = $logger; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + foreach ($this->loggers as $logger) { + $logger->startQuery($sql, $params, $types); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + foreach ($this->loggers as $logger) { + $logger->stopQuery(); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php new file mode 100644 index 0000000..52491e5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/SQLLogger.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Logging; + +/** + * Interface for SQL loggers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface SQLLogger +{ + /** + * Logs a SQL statement somewhere. + * + * @param string $sql The SQL to be executed. + * @param array|null $params The SQL parameters. + * @param array|null $types The SQL parameter types. + * + * @return void + */ + public function startQuery($sql, array $params = null, array $types = null); + + /** + * Marks the last started query as stopped. This can be used for timing of queries. + * + * @return void + */ + public function stopQuery(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php new file mode 100644 index 0000000..f0ace99 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -0,0 +1,3528 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Types; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Events; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Event\SchemaCreateTableEventArgs; +use Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs; +use Doctrine\DBAL\Event\SchemaDropTableEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs; +use Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs; + +/** + * Base class for all DatabasePlatforms. The DatabasePlatforms are the central + * point of abstraction of platform-specific behaviors, features and SQL dialects. + * They are a passive source of information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Remove any unnecessary methods. + */ +abstract class AbstractPlatform +{ + /** + * @var integer + */ + const CREATE_INDEXES = 1; + + /** + * @var integer + */ + const CREATE_FOREIGNKEYS = 2; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_SECOND = 'SECOND'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_MINUTE = 'MINUTE'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_HOUR = 'HOUR'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_DAY = 'DAY'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_WEEK = 'WEEK'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_MONTH = 'MONTH'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_QUARTER = 'QUARTER'; + + /** + * @var string + */ + const DATE_INTERVAL_UNIT_YEAR = 'YEAR'; + + /** + * @var integer + */ + const TRIM_UNSPECIFIED = 0; + + /** + * @var integer + */ + const TRIM_LEADING = 1; + + /** + * @var integer + */ + const TRIM_TRAILING = 2; + + /** + * @var integer + */ + const TRIM_BOTH = 3; + + /** + * @var array|null + */ + protected $doctrineTypeMapping = null; + + /** + * Contains a list of all columns that should generate parseable column comments for type-detection + * in reverse engineering scenarios. + * + * @var array|null + */ + protected $doctrineTypeComments = null; + + /** + * @var \Doctrine\Common\EventManager + */ + protected $_eventManager; + + /** + * Holds the KeywordList instance for the current platform. + * + * @var \Doctrine\DBAL\Platforms\Keywords\KeywordList + */ + protected $_keywords; + + /** + * Constructor. + */ + public function __construct() + { + } + + /** + * Sets the EventManager used by the Platform. + * + * @param \Doctrine\Common\EventManager $eventManager + */ + public function setEventManager(EventManager $eventManager) + { + $this->_eventManager = $eventManager; + } + + /** + * Gets the EventManager used by the Platform. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager() + { + return $this->_eventManager; + } + + /** + * Returns the SQL snippet that declares a boolean column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBooleanTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares a 4 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares an 8 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getBigIntTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares a 2 byte integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract public function getSmallIntTypeDeclarationSQL(array $columnDef); + + /** + * Returns the SQL snippet that declares common properties of an integer column. + * + * @param array $columnDef + * + * @return string + */ + abstract protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef); + + /** + * Lazy load Doctrine Type Mappings. + * + * @return void + */ + abstract protected function initializeDoctrineTypeMappings(); + + /** + * Initializes Doctrine Type Mappings with the platform defaults + * and with all additional type mappings. + * + * @return void + */ + private function initializeAllDoctrineTypeMappings() + { + $this->initializeDoctrineTypeMappings(); + + foreach (Type::getTypesMap() as $typeName => $className) { + foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { + $this->doctrineTypeMapping[$dbType] = $typeName; + } + } + } + + /** + * Returns the SQL snippet used to declare a VARCHAR column type. + * + * @param array $field + * + * @return string + */ + public function getVarcharTypeDeclarationSQL(array $field) + { + if ( !isset($field['length'])) { + $field['length'] = $this->getVarcharDefaultLength(); + } + + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + if ($field['length'] > $this->getVarcharMaxLength()) { + return $this->getClobTypeDeclarationSQL($field); + } + + return $this->getVarcharTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. + * + * @param array $field The column definition. + * + * @return string + */ + public function getBinaryTypeDeclarationSQL(array $field) + { + if ( ! isset($field['length'])) { + $field['length'] = $this->getBinaryDefaultLength(); + } + + $fixed = isset($field['fixed']) ? $field['fixed'] : false; + + if ($field['length'] > $this->getBinaryMaxLength()) { + return $this->getBlobTypeDeclarationSQL($field); + } + + return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed); + } + + /** + * Returns the SQL snippet to declare a GUID/UUID field. + * + * By default this maps directly to a CHAR(36) and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getGuidTypeDeclarationSQL(array $field) + { + $field['length'] = 36; + $field['fixed'] = true; + + return $this->getVarcharTypeDeclarationSQL($field); + } + + /** + * Returns the SQL snippet to declare a JSON field. + * + * By default this maps directly to a CLOB and only maps to more + * special datatypes when the underlying databases support this datatype. + * + * @param array $field + * + * @return string + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return $this->getClobTypeDeclarationSQL($field); + } + + /** + * @param integer $length + * @param boolean $fixed + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('VARCHARs not supported by Platform.'); + } + + /** + * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. + * + * @param integer $length The length of the column. + * @param boolean $fixed Whether the column length is fixed. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + throw DBALException::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); + } + + /** + * Returns the SQL snippet used to declare a CLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getClobTypeDeclarationSQL(array $field); + + /** + * Returns the SQL Snippet used to declare a BLOB column type. + * + * @param array $field + * + * @return string + */ + abstract public function getBlobTypeDeclarationSQL(array $field); + + /** + * Gets the name of the platform. + * + * @return string + */ + abstract public function getName(); + + /** + * Registers a doctrine type to be used in conjunction with a column type of this platform. + * + * @param string $dbType + * @param string $doctrineType + * + * @throws \Doctrine\DBAL\DBALException If the type is not found. + */ + public function registerDoctrineTypeMapping($dbType, $doctrineType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + if (!Types\Type::hasType($doctrineType)) { + throw DBALException::typeNotFound($doctrineType); + } + + $dbType = strtolower($dbType); + $this->doctrineTypeMapping[$dbType] = $doctrineType; + } + + /** + * Gets the Doctrine type that is mapped for the given database column type. + * + * @param string $dbType + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException + */ + public function getDoctrineTypeMapping($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + if (!isset($this->doctrineTypeMapping[$dbType])) { + throw new \Doctrine\DBAL\DBALException("Unknown database type ".$dbType." requested, " . get_class($this) . " may not support it."); + } + + return $this->doctrineTypeMapping[$dbType]; + } + + /** + * Checks if a database type is currently supported by this platform. + * + * @param string $dbType + * + * @return boolean + */ + public function hasDoctrineTypeMappingFor($dbType) + { + if ($this->doctrineTypeMapping === null) { + $this->initializeAllDoctrineTypeMappings(); + } + + $dbType = strtolower($dbType); + + return isset($this->doctrineTypeMapping[$dbType]); + } + + /** + * Initializes the Doctrine Type comments instance variable for in_array() checks. + * + * @return void + */ + protected function initializeCommentedDoctrineTypes() + { + $this->doctrineTypeComments = array(); + + foreach (Type::getTypesMap() as $typeName => $className) { + $type = Type::getType($typeName); + + if ($type->requiresSQLCommentHint($this)) { + $this->doctrineTypeComments[] = $typeName; + } + } + } + + /** + * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? + * + * @param \Doctrine\DBAL\Types\Type $doctrineType + * + * @return boolean + */ + public function isCommentedDoctrineType(Type $doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + return in_array($doctrineType->getName(), $this->doctrineTypeComments); + } + + /** + * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements. + * + * @param string|\Doctrine\DBAL\Types\Type $doctrineType + * + * @return void + */ + public function markDoctrineTypeCommented($doctrineType) + { + if ($this->doctrineTypeComments === null) { + $this->initializeCommentedDoctrineTypes(); + } + + $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; + } + + /** + * Gets the comment to append to a column comment that helps parsing this type in reverse engineering. + * + * @param \Doctrine\DBAL\Types\Type $doctrineType + * + * @return string + */ + public function getDoctrineTypeComment(Type $doctrineType) + { + return '(DC2Type:' . $doctrineType->getName() . ')'; + } + + /** + * Gets the comment of a passed column modified by potential doctrine type comment hints. + * + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return string + */ + protected function getColumnComment(Column $column) + { + $comment = $column->getComment(); + + if ($this->isCommentedDoctrineType($column->getType())) { + $comment .= $this->getDoctrineTypeComment($column->getType()); + } + + return $comment; + } + + /** + * Gets the character used for identifier quoting. + * + * @return string + */ + public function getIdentifierQuoteCharacter() + { + return '"'; + } + + /** + * Gets the string portion that starts an SQL comment. + * + * @return string + */ + public function getSqlCommentStartString() + { + return "--"; + } + + /** + * Gets the string portion that ends an SQL comment. + * + * @return string + */ + public function getSqlCommentEndString() + { + return "\n"; + } + + /** + * Gets the maximum length of a varchar field. + * + * @return integer + */ + public function getVarcharMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a varchar field. + * + * @return integer + */ + public function getVarcharDefaultLength() + { + return 255; + } + + /** + * Gets the maximum length of a binary field. + * + * @return integer + */ + public function getBinaryMaxLength() + { + return 4000; + } + + /** + * Gets the default length of a binary field. + * + * @return integer + */ + public function getBinaryDefaultLength() + { + return 255; + } + + /** + * Gets all SQL wildcard characters of the platform. + * + * @return array + */ + public function getWildcards() + { + return array('%', '_'); + } + + /** + * Returns the regular expression operator. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getRegexpExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the global unique identifier expression. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getGuidExpression() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to get the average value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including an AVG aggregate function. + */ + public function getAvgExpression($column) + { + return 'AVG(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the number of rows (without a NULL value) of a column. + * + * If a '*' is used instead of a column the number of selected rows is returned. + * + * @param string|integer $column The column to use. + * + * @return string Generated SQL including a COUNT aggregate function. + */ + public function getCountExpression($column) + { + return 'COUNT(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the highest value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a MAX aggregate function. + */ + public function getMaxExpression($column) + { + return 'MAX(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the lowest value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a MIN aggregate function. + */ + public function getMinExpression($column) + { + return 'MIN(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the total sum of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including a SUM aggregate function. + */ + public function getSumExpression($column) + { + return 'SUM(' . $column . ')'; + } + + // scalar functions + + /** + * Returns the SQL snippet to get the md5 sum of a field. + * + * Note: Not SQL92, but common functionality. + * + * @param string $column + * + * @return string + */ + public function getMd5Expression($column) + { + return 'MD5(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the length of a text field. + * + * @param string $column + * + * @return string + */ + public function getLengthExpression($column) + { + return 'LENGTH(' . $column . ')'; + } + + /** + * Returns the SQL snippet to get the squared value of a column. + * + * @param string $column The column to use. + * + * @return string Generated SQL including an SQRT aggregate function. + */ + public function getSqrtExpression($column) + { + return 'SQRT(' . $column . ')'; + } + + /** + * Returns the SQL snippet to round a numeric field to the number of decimals specified. + * + * @param string $column + * @param integer $decimals + * + * @return string + */ + public function getRoundExpression($column, $decimals = 0) + { + return 'ROUND(' . $column . ', ' . $decimals . ')'; + } + + /** + * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2. + * + * @param string $expression1 + * @param string $expression2 + * + * @return string + */ + public function getModExpression($expression1, $expression2) + { + return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + } + + /** + * Returns the SQL snippet to trim a string. + * + * @param string $str The expression to apply the trim to. + * @param integer $pos The position of the trim (leading/trailing/both). + * @param string|boolean $char The char to trim, has to be quoted already. Defaults to space. + * + * @return string + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $expression = ''; + + switch ($pos) { + case self::TRIM_LEADING: + $expression = 'LEADING '; + break; + + case self::TRIM_TRAILING: + $expression = 'TRAILING '; + break; + + case self::TRIM_BOTH: + $expression = 'BOTH '; + break; + } + + if (false !== $char) { + $expression .= $char . ' '; + } + + if ($pos || false !== $char) { + $expression .= 'FROM '; + } + + return 'TRIM(' . $expression . $str . ')'; + } + + /** + * Returns the SQL snippet to trim trailing space characters from the expression. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getRtrimExpression($str) + { + return 'RTRIM(' . $str . ')'; + } + + /** + * Returns the SQL snippet to trim leading space characters from the expression. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getLtrimExpression($str) + { + return 'LTRIM(' . $str . ')'; + } + + /** + * Returns the SQL snippet to change all characters from the expression to uppercase, + * according to the current character set mapping. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getUpperExpression($str) + { + return 'UPPER(' . $str . ')'; + } + + /** + * Returns the SQL snippet to change all characters from the expression to lowercase, + * according to the current character set mapping. + * + * @param string $str Literal string or column name. + * + * @return string + */ + public function getLowerExpression($str) + { + return 'LOWER(' . $str . ')'; + } + + /** + * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str. + * + * @param string $str Literal string. + * @param string $substr Literal string to find. + * @param integer|boolean $startPos Position to start at, beginning of string by default. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to get the current system date. + * + * @return string + */ + public function getNowExpression() + { + return 'NOW()'; + } + + /** + * Returns a SQL snippet to get a substring inside an SQL statement. + * + * Note: Not SQL92, but common functionality. + * + * SQLite only supports the 2 parameter variant of this function. + * + * @param string $value An sql string literal or column name/alias. + * @param integer $from Where to start the substring portion. + * @param integer|null $length The substring portion length. + * + * @return string + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * Returns a SQL snippet to concatenate the given expressions. + * + * Accepts an arbitrary number of string parameters. Each parameter must contain an expression. + * + * @return string + */ + public function getConcatExpression() + { + return join(' || ', func_get_args()); + } + + /** + * Returns the SQL for a logical not. + * + * Example: + * + * $q = new Doctrine_Query(); + * $e = $q->expr; + * $q->select('*')->from('table') + * ->where($e->eq('id', $e->not('null')); + * + * + * @param string $expression + * + * @return string The logical expression. + */ + public function getNotExpression($expression) + { + return 'NOT(' . $expression . ')'; + } + + /** + * Returns the SQL that checks if an expression is null. + * + * @param string $expression The expression that should be compared to null. + * + * @return string The logical expression. + */ + public function getIsNullExpression($expression) + { + return $expression . ' IS NULL'; + } + + /** + * Returns the SQL that checks if an expression is not null. + * + * @param string $expression The expression that should be compared to null. + * + * @return string The logical expression. + */ + public function getIsNotNullExpression($expression) + { + return $expression . ' IS NOT NULL'; + } + + /** + * Returns the SQL that checks if an expression evaluates to a value between two values. + * + * The parameter $expression is checked if it is between $value1 and $value2. + * + * Note: There is a slight difference in the way BETWEEN works on some databases. + * http://www.w3schools.com/sql/sql_between.asp. If you want complete database + * independence you should avoid using between(). + * + * @param string $expression The value to compare to. + * @param string $value1 The lower value to compare with. + * @param string $value2 The higher value to compare with. + * + * @return string The logical expression. + */ + public function getBetweenExpression($expression, $value1, $value2) + { + return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; + } + + /** + * Returns the SQL to get the arccosine of a value. + * + * @param string $value + * + * @return string + */ + public function getAcosExpression($value) + { + return 'ACOS(' . $value . ')'; + } + + /** + * Returns the SQL to get the sine of a value. + * + * @param string $value + * + * @return string + */ + public function getSinExpression($value) + { + return 'SIN(' . $value . ')'; + } + + /** + * Returns the SQL to get the PI value. + * + * @return string + */ + public function getPiExpression() + { + return 'PI()'; + } + + /** + * Returns the SQL to get the cosine of a value. + * + * @param string $value + * + * @return string + */ + public function getCosExpression($value) + { + return 'COS(' . $value . ')'; + } + + /** + * Returns the SQL to calculate the difference in days between the two passed dates. + * + * Computes diff = date1 - date2. + * + * @param string $date1 + * @param string $date2 + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateDiffExpression($date1, $date2) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to add the number of given seconds to a date. + * + * @param string $date + * @param integer $seconds + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddSecondsExpression($date, $seconds) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, self::DATE_INTERVAL_UNIT_SECOND); + } + + /** + * Returns the SQL to subtract the number of given seconds from a date. + * + * @param string $date + * @param integer $seconds + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubSecondsExpression($date, $seconds) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, self::DATE_INTERVAL_UNIT_SECOND); + } + + /** + * Returns the SQL to add the number of given minutes to a date. + * + * @param string $date + * @param integer $minutes + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddMinutesExpression($date, $minutes) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, self::DATE_INTERVAL_UNIT_MINUTE); + } + + /** + * Returns the SQL to subtract the number of given minutes from a date. + * + * @param string $date + * @param integer $minutes + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubMinutesExpression($date, $minutes) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, self::DATE_INTERVAL_UNIT_MINUTE); + } + + /** + * Returns the SQL to add the number of given hours to a date. + * + * @param string $date + * @param integer $hours + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddHourExpression($date, $hours) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $hours, self::DATE_INTERVAL_UNIT_HOUR); + } + + /** + * Returns the SQL to subtract the number of given hours to a date. + * + * @param string $date + * @param integer $hours + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubHourExpression($date, $hours) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $hours, self::DATE_INTERVAL_UNIT_HOUR); + } + + /** + * Returns the SQL to add the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddDaysExpression($date, $days) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $days, self::DATE_INTERVAL_UNIT_DAY); + } + + /** + * Returns the SQL to subtract the number of given days to a date. + * + * @param string $date + * @param integer $days + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubDaysExpression($date, $days) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $days, self::DATE_INTERVAL_UNIT_DAY); + } + + /** + * Returns the SQL to add the number of given weeks to a date. + * + * @param string $date + * @param integer $weeks + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddWeeksExpression($date, $weeks) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, self::DATE_INTERVAL_UNIT_WEEK); + } + + /** + * Returns the SQL to subtract the number of given weeks from a date. + * + * @param string $date + * @param integer $weeks + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubWeeksExpression($date, $weeks) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, self::DATE_INTERVAL_UNIT_WEEK); + } + + /** + * Returns the SQL to add the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddMonthExpression($date, $months) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $months, self::DATE_INTERVAL_UNIT_MONTH); + } + + /** + * Returns the SQL to subtract the number of given months to a date. + * + * @param string $date + * @param integer $months + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubMonthExpression($date, $months) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $months, self::DATE_INTERVAL_UNIT_MONTH); + } + + /** + * Returns the SQL to add the number of given quarters to a date. + * + * @param string $date + * @param integer $quarters + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddQuartersExpression($date, $quarters) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, self::DATE_INTERVAL_UNIT_QUARTER); + } + + /** + * Returns the SQL to subtract the number of given quarters from a date. + * + * @param string $date + * @param integer $quarters + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubQuartersExpression($date, $quarters) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, self::DATE_INTERVAL_UNIT_QUARTER); + } + + /** + * Returns the SQL to add the number of given years to a date. + * + * @param string $date + * @param integer $years + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateAddYearsExpression($date, $years) + { + return $this->getDateArithmeticIntervalExpression($date, '+', $years, self::DATE_INTERVAL_UNIT_YEAR); + } + + /** + * Returns the SQL to subtract the number of given years from a date. + * + * @param string $date + * @param integer $years + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateSubYearsExpression($date, $years) + { + return $this->getDateArithmeticIntervalExpression($date, '-', $years, self::DATE_INTERVAL_UNIT_YEAR); + } + + /** + * Returns the SQL for a date arithmetic expression. + * + * @param string $date The column or literal representing a date to perform the arithmetic operation on. + * @param string $operator The arithmetic operator (+ or -). + * @param integer $interval The interval that shall be calculated into the date. + * @param string $unit The unit of the interval that shall be calculated into the date. + * One of the DATE_INTERVAL_UNIT_* constants. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL bit AND comparison expression. + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' & ' . $value2 . ')'; + } + + /** + * Returns the SQL bit OR comparison expression. + * + * @param string $value1 + * @param string $value2 + * + * @return string + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . ' | ' . $value2 . ')'; + } + + /** + * Returns the FOR UPDATE expression. + * + * @return string + */ + public function getForUpdateSQL() + { + return 'FOR UPDATE'; + } + + /** + * Honors that some SQL vendors such as MsSql use table hints for locking instead of the ANSI SQL FOR UPDATE specification. + * + * @param string $fromClause The FROM clause to append the hint for the given lock mode to. + * @param integer|null $lockMode One of the Doctrine\DBAL\LockMode::* constants. If null is given, nothing will + * be appended to the FROM clause. + * + * @return string + */ + public function appendLockHint($fromClause, $lockMode) + { + return $fromClause; + } + + /** + * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock. + * + * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database + * vendors allow to lighten this constraint up to be a real read lock. + * + * @return string + */ + public function getReadLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. + * + * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard. + * + * @return string + */ + public function getWriteLockSQL() + { + return $this->getForUpdateSQL(); + } + + /** + * Returns the SQL snippet to drop an existing database. + * + * @param string $database The name of the database that should be dropped. + * + * @return string + */ + public function getDropDatabaseSQL($database) + { + return 'DROP DATABASE ' . $database; + } + + /** + * Returns the SQL snippet to drop an existing table. + * + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDropTableSQL($table) + { + $tableArg = $table; + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { + $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return $eventArgs->getSql(); + } + } + + return 'DROP TABLE ' . $table; + } + + /** + * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. + * + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropTemporaryTableSQL($table) + { + return $this->getDropTableSQL($table); + } + + /** + * Returns the SQL to drop an index from a table. + * + * @param \Doctrine\DBAL\Schema\Index|string $index + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + return 'DROP INDEX ' . $index; + } + + /** + * Returns the SQL to drop a constraint. + * + * @param \Doctrine\DBAL\Schema\Constraint|string $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropConstraintSQL($constraint, $table) + { + if ($constraint instanceof Constraint) { + $constraint = $constraint->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; + } + + /** + * Returns the SQL to drop a foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; + } + + /** + * Returns the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. + * + * @param \Doctrine\DBAL\Schema\Table $table + * @param integer $createFlags + * + * @return array The sequence of SQL statements. + * + * @throws \Doctrine\DBAL\DBALException + * @throws \InvalidArgumentException + */ + public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) + { + if ( ! is_int($createFlags)) { + throw new \InvalidArgumentException("Second argument of AbstractPlatform::getCreateTableSQL() has to be integer."); + } + + if (count($table->getColumns()) === 0) { + throw DBALException::noColumnsSpecifiedForTable($table->getName()); + } + + $tableName = $table->getQuotedName($this); + $options = $table->getOptions(); + $options['uniqueConstraints'] = array(); + $options['indexes'] = array(); + $options['primary'] = array(); + + if (($createFlags&self::CREATE_INDEXES) > 0) { + foreach ($table->getIndexes() as $index) { + /* @var $index Index */ + if ($index->isPrimary()) { + $options['primary'] = $index->getQuotedColumns($this); + $options['primary_index'] = $index; + } else { + $options['indexes'][$index->getQuotedName($this)] = $index; + } + } + } + + $columnSql = array(); + $columns = array(); + + foreach ($table->getColumns() as $column) { + /* @var \Doctrine\DBAL\Schema\Column $column */ + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) { + $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + if ($eventArgs->isDefaultPrevented()) { + continue; + } + } + + $columnData = $column->toArray(); + $columnData['name'] = $column->getQuotedName($this); + $columnData['version'] = $column->hasPlatformOption("version") ? $column->getPlatformOption('version') : false; + $columnData['comment'] = $this->getColumnComment($column); + + if (strtolower($columnData['type']) == "string" && $columnData['length'] === null) { + $columnData['length'] = 255; + } + + if (in_array($column->getName(), $options['primary'])) { + $columnData['primary'] = true; + } + + $columns[$columnData['name']] = $columnData; + } + + if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = array(); + foreach ($table->getForeignKeys() as $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; + } + } + + if (null !== $this->_eventManager && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { + $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); + + if ($eventArgs->isDefaultPrevented()) { + return array_merge($eventArgs->getSql(), $columnSql); + } + } + + $sql = $this->_getCreateTableSQL($tableName, $columns, $options); + if ($this->supportsCommentOnStatement()) { + foreach ($table->getColumns() as $column) { + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment); + } + } + } + + return array_merge($sql, $columnSql); + } + + /** + * @param string $tableName + * @param string $columnName + * @param string $comment + * + * @return string + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) . + " IS " . $comment; + } + + /** + * Returns the SQL used to create a table. + * + * @param string $tableName + * @param array $columns + * @param array $options + * + * @return array + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if ( ! empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * @return string + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TEMPORARY TABLE"; + } + + /** + * Returns the SQL to create a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to change a sequence on this platform. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to create a constraint on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); + + $columnList = '('. implode(', ', $constraint->getQuotedColumns($this)) . ')'; + + $referencesClause = ''; + if ($constraint instanceof Index) { + if ($constraint->isPrimary()) { + $query .= ' PRIMARY KEY'; + } elseif ($constraint->isUnique()) { + $query .= ' UNIQUE'; + } else { + throw new \InvalidArgumentException( + 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().' + ); + } + } elseif ($constraint instanceof ForeignKeyConstraint) { + $query .= ' FOREIGN KEY'; + + $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) . + ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')'; + } + $query .= ' '.$columnList.$referencesClause; + + return $query; + } + + /** + * Returns the SQL to create an index on a table on this platform. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getCreateIndexSQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + $name = $index->getQuotedName($this); + $columns = $index->getQuotedColumns($this); + + if (count($columns) == 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } + + $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($columns) . ')' . $this->getPartialIndexSQL($index); + + return $query; + } + + /** + * Adds condition for partial index. + * + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + protected function getPartialIndexSQL(Index $index) + { + if ($this->supportsPartialIndexes() && $index->hasOption('where')) { + return ' WHERE ' . $index->getOption('where'); + } + + return ''; + } + + /** + * Adds additional flags for index generation. + * + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + protected function getCreateIndexSQLFlags(Index $index) + { + return $index->isUnique() ? 'UNIQUE ' : ''; + } + + /** + * Returns the SQL to create an unnamed primary key constraint. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return string + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')'; + } + + /** + * Returns the SQL to create a named schema. + * + * @param string $schemaName + * + * @return string + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateSchemaSQL($schemaName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Quotes a string so that it can be safely used as a table or column name, + * even if it is a reserved word of the platform. This also detects identifier + * chains separated by dot and quotes them independently. + * + * NOTE: Just because you CAN use quoted identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * @param string $str The identifier name to be quoted. + * + * @return string The quoted identifier string. + */ + public function quoteIdentifier($str) + { + if (strpos($str, ".") !== false) { + $parts = array_map(array($this, "quoteSingleIdentifier"), explode(".", $str)); + + return implode(".", $parts); + } + + return $this->quoteSingleIdentifier($str); + } + + /** + * Quotes a single identifier (no dot chain separation). + * + * @param string $str The identifier name to be quoted. + * + * @return string The quoted identifier string. + */ + public function quoteSingleIdentifier($str) + { + $c = $this->getIdentifierQuoteCharacter(); + + return $c . str_replace($c, $c.$c, $str) . $c; + } + + /** + * Returns the SQL to create a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key constraint. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return string + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); + + return $query; + } + + /** + * Gets the SQL statements for altering an existing table. + * + * This method returns an array of SQL statements, since some platforms need several statements. + * + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getAlterTableSQL(TableDiff $diff) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\ColumnDiff $columnDiff + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param string $oldColumnName + * @param \Doctrine\DBAL\Schema\Column $column + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $columnSql + * + * @return boolean + */ + protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { + return false; + } + + $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); + + $columnSql = array_merge($columnSql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * @param array $sql + * + * @return boolean + */ + protected function onSchemaAlterTable(TableDiff $diff, &$sql) + { + if (null === $this->_eventManager) { + return false; + } + + if ( ! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { + return false; + } + + $eventArgs = new SchemaAlterTableEventArgs($diff, $this); + $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); + + $sql = array_merge($sql, $eventArgs->getSql()); + + return $eventArgs->isDefaultPrevented(); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = $diff->getName($this)->getQuotedName($this); + + $sql = array(); + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->removedForeignKeys as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + foreach ($diff->changedForeignKeys as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->removedIndexes as $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + foreach ($diff->changedIndexes as $index) { + $sql[] = $this->getDropIndexSQL($index, $tableName); + } + + return $sql; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $tableName = (false !== $diff->newName) + ? $diff->getNewName()->getQuotedName($this) + : $diff->getName($this)->getQuotedName($this); + + $sql = array(); + + if ($this->supportsForeignKeyConstraints()) { + foreach ($diff->addedForeignKeys as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + + foreach ($diff->changedForeignKeys as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + foreach ($diff->addedIndexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + foreach ($diff->changedIndexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + + foreach ($diff->renamedIndexes as $oldIndexName => $index) { + $oldIndexName = new Identifier($oldIndexName); + $sql = array_merge( + $sql, + $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableName) + ); + } + + return $sql; + } + + /** + * Returns the SQL for renaming an index on a table. + * + * @param string $oldIndexName The name of the index to rename from. + * @param \Doctrine\DBAL\Schema\Index $index The definition of the index to rename to. + * @param string $tableName The table to rename the given index on. + * + * @return array The sequence of SQL statements for renaming the given index. + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + $this->getDropIndexSQL($oldIndexName, $tableName), + $this->getCreateIndexSQL($index, $tableName) + ); + } + + /** + * Common code for alter table statement generation that updates the changed Index and Foreign Key definitions. + * + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + protected function _getAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge($this->getPreAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + /** + * Gets declaration of a number of fields in bulk. + * + * @param array $fields A multidimensional associative array. + * The first dimension determines the field name, while the second + * dimension is keyed with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * + * @return string + */ + public function getColumnDeclarationListSQL(array $fields) + { + $queryFields = array(); + + foreach ($fields as $fieldName => $field) { + $queryFields[] = $this->getColumnDeclarationSQL($fieldName, $field); + } + + return implode(', ', $queryFields); + } + + /** + * Obtains DBMS specific SQL code portion needed to declare a generic type + * field to be used in statements like CREATE TABLE. + * + * @param string $name The name the field to be declared. + * @param array $field An associative array with the name of the properties + * of the field being declared as array indexes. Currently, the types + * of supported field properties are as follows: + * + * length + * Integer value that determines the maximum length of the text + * field. If this argument is missing the field should be + * declared to have the longest length allowed by the DBMS. + * + * default + * Text value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this field. + * collation + * Text value with the default COLLATION for this field. + * unique + * unique constraint + * check + * column check constraint + * columnDefinition + * a string that defines the complete column + * + * @return string DBMS specific SQL code portion that should be used to declare the column. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $charset = (isset($field['charset']) && $field['charset']) ? + ' ' . $this->getColumnCharsetDeclarationSQL($field['charset']) : ''; + + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSqlDeclaration($field, $this); + $columnDef = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; + } + + if ($this->supportsInlineColumnComments() && isset($field['comment']) && $field['comment'] !== '') { + $columnDef .= " COMMENT " . $this->quoteStringLiteral($field['comment']); + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns the SQL snippet that declares a floating point column of arbitrary precision. + * + * @param array $columnDef + * + * @return string + */ + public function getDecimalTypeDeclarationSQL(array $columnDef) + { + $columnDef['precision'] = ( ! isset($columnDef['precision']) || empty($columnDef['precision'])) + ? 10 : $columnDef['precision']; + $columnDef['scale'] = ( ! isset($columnDef['scale']) || empty($columnDef['scale'])) + ? 0 : $columnDef['scale']; + + return 'NUMERIC(' . $columnDef['precision'] . ', ' . $columnDef['scale'] . ')'; + } + + /** + * Obtains DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. + * + * @param array $field The field definition array. + * + * @return string DBMS specific SQL code portion needed to set a default value. + */ + public function getDefaultValueDeclarationSQL($field) + { + $default = empty($field['notnull']) ? ' DEFAULT NULL' : ''; + + if (isset($field['default'])) { + $default = " DEFAULT '".$field['default']."'"; + if (isset($field['type'])) { + if (in_array((string) $field['type'], array("Integer", "BigInt", "SmallInt"))) { + $default = " DEFAULT ".$field['default']; + } elseif (in_array((string) $field['type'], array('DateTime', 'DateTimeTz')) && $field['default'] == $this->getCurrentTimestampSQL()) { + $default = " DEFAULT ".$this->getCurrentTimestampSQL(); + } elseif ((string) $field['type'] == 'Time' && $field['default'] == $this->getCurrentTimeSQL()) { + $default = " DEFAULT ".$this->getCurrentTimeSQL(); + } elseif ((string) $field['type'] == 'Date' && $field['default'] == $this->getCurrentDateSQL()) { + $default = " DEFAULT ".$this->getCurrentDateSQL(); + } elseif ((string) $field['type'] == 'Boolean') { + $default = " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + } + } + + return $default; + } + + /** + * Obtains DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. + * + * @param array $definition The check definition. + * + * @return string DBMS specific SQL code portion needed to set a CHECK constraint. + */ + public function getCheckDeclarationSQL(array $definition) + { + $constraints = array(); + foreach ($definition as $field => $def) { + if (is_string($def)) { + $constraints[] = 'CHECK (' . $def . ')'; + } else { + if (isset($def['min'])) { + $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')'; + } + + if (isset($def['max'])) { + $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')'; + } + } + } + + return implode(', ', $constraints); + } + + /** + * Obtains DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. + * + * @param string $name The name of the unique constraint. + * @param \Doctrine\DBAL\Schema\Index $index The index definition. + * + * @return string DBMS specific SQL code portion needed to set a constraint. + * + * @throws \InvalidArgumentException + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $columns = $index->getQuotedColumns($this); + $name = new Identifier($name); + + if (count($columns) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE (' + . $this->getIndexFieldDeclarationListSQL($columns) + . ')' . $this->getPartialIndexSQL($index); + } + + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param string $name The name of the index. + * @param \Doctrine\DBAL\Schema\Index $index The index definition. + * + * @return string DBMS specific SQL code portion needed to set an index. + * + * @throws \InvalidArgumentException + */ + public function getIndexDeclarationSQL($name, Index $index) + { + $columns = $index->getQuotedColumns($this); + $name = new Identifier($name); + + if (count($columns) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' (' + . $this->getIndexFieldDeclarationListSQL($columns) + . ')' . $this->getPartialIndexSQL($index); + } + + /** + * Obtains SQL code portion needed to create a custom column, + * e.g. when a field has the "columnDefinition" keyword. + * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. + * + * @param array $columnDef + * + * @return string + */ + public function getCustomTypeDeclarationSQL(array $columnDef) + { + return $columnDef['columnDefinition']; + } + + /** + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. + * + * @param array $fields + * + * @return string + */ + public function getIndexFieldDeclarationListSQL(array $fields) + { + $ret = array(); + + foreach ($fields as $field => $definition) { + if (is_array($definition)) { + $ret[] = $field; + } else { + $ret[] = $definition; + } + } + + return implode(', ', $ret); + } + + /** + * Returns the required SQL string that fits between CREATE ... TABLE + * to create the table as a temporary table. + * + * Should be overridden in driver classes to return the correct string for the + * specific database type. + * + * The default is to return the string "TEMPORARY" - this will result in a + * SQL error for any database that does not support temporary tables, or that + * requires a different SQL command from "CREATE TEMPORARY TABLE". + * + * @return string The string required to be placed between "CREATE" and "TABLE" + * to generate a temporary table, if possible. + */ + public function getTemporaryTableSQL() + { + return 'TEMPORARY'; + } + + /** + * Some vendors require temporary table names to be qualified specially. + * + * @param string $tableName + * + * @return string + */ + public function getTemporaryTableName($tableName) + { + return $tableName; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration. + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); + $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $sql; + } + + /** + * Returns the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The foreign key definition. + * + * @return string + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); + } + if ($foreignKey->hasOption('onDelete')) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + + return $query; + } + + /** + * Returns the given referential action in uppercase if valid, otherwise throws an exception. + * + * @param string $action The foreign key referential action. + * + * @return string + * + * @throws \InvalidArgumentException if unknown referential action given + */ + public function getForeignKeyReferentialActionSQL($action) + { + $upper = strtoupper($action); + switch ($upper) { + case 'CASCADE': + case 'SET NULL': + case 'NO ACTION': + case 'RESTRICT': + case 'SET DEFAULT': + return $upper; + default: + throw new \InvalidArgumentException('Invalid foreign key action: ' . $upper); + } + } + + /** + * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + if (strlen($foreignKey->getName())) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + $sql .= 'FOREIGN KEY ('; + + if (count($foreignKey->getLocalColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + if (count($foreignKey->getForeignColumns()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + if (strlen($foreignKey->getForeignTableName()) === 0) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + $sql .= implode(', ', $foreignKey->getQuotedLocalColumns($this)) + . ') REFERENCES ' + . $foreignKey->getQuotedForeignTableName($this) . ' (' + . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')'; + + return $sql; + } + + /** + * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration to be used in statements like CREATE TABLE. + * + * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint + * of a field declaration. + */ + public function getUniqueFieldDeclarationSQL() + { + return 'UNIQUE'; + } + + /** + * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $charset The name of the charset. + * + * @return string DBMS specific SQL code portion needed to set the CHARACTER SET + * of a field declaration. + */ + public function getColumnCharsetDeclarationSQL($charset) + { + return ''; + } + + /** + * Obtains DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @param string $collation The name of the collation. + * + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getColumnCollationDeclarationSQL($collation) + { + return $this->supportsColumnCollation() ? 'COLLATE ' . $collation : ''; + } + + /** + * Whether the platform prefers sequences for ID generation. + * Subclasses should override this method to return TRUE if they prefer sequences. + * + * @return boolean + */ + public function prefersSequences() + { + return false; + } + + /** + * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. + * Subclasses should override this method to return TRUE if they prefer identity columns. + * + * @return boolean + */ + public function prefersIdentityColumns() + { + return false; + } + + /** + * Some platforms need the boolean values to be converted. + * + * The default conversion in this implementation converts to integers (false => 0, true => 1). + * + * Note: if the input is not a boolean the original input might be returned. + * + * There are two contexts when converting booleans: Literals and Prepared Statements. + * This method should handle the literal case + * + * @param mixed $item A boolean or an array of them. + * + * @return mixed A boolean database value or an array of them. + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $k => $value) { + if (is_bool($value)) { + $item[$k] = (int) $value; + } + } + } elseif (is_bool($item)) { + $item = (int) $item; + } + + return $item; + } + + /** + * Some platforms have boolean literals that needs to be correctly converted + * + * The default conversion tries to convert value into bool "(bool)$item" + * + * @param mixed $item + * + * @return bool|null + */ + public function convertFromBoolean($item) + { + return null === $item ? null: (bool) $item ; + } + + /** + * This method should handle the prepared statements case. When there is no + * distinction, it's OK to use the same method. + * + * Note: if the input is not a boolean the original input might be returned. + * + * @param mixed $item A boolean or an array of them. + * + * @return mixed A boolean database value or an array of them. + */ + public function convertBooleansToDatabaseValue($item) + { + return $this->convertBooleans($item); + } + + /** + * Returns the SQL specific for the platform to get the current date. + * + * @return string + */ + public function getCurrentDateSQL() + { + return 'CURRENT_DATE'; + } + + /** + * Returns the SQL specific for the platform to get the current time. + * + * @return string + */ + public function getCurrentTimeSQL() + { + return 'CURRENT_TIME'; + } + + /** + * Returns the SQL specific for the platform to get the current timestamp + * + * @return string + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT_TIMESTAMP'; + } + + /** + * Returns the SQL for a given transaction isolation level Connection constant. + * + * @param integer $level + * + * @return string + * + * @throws \InvalidArgumentException + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case Connection::TRANSACTION_REPEATABLE_READ: + return 'REPEATABLE READ'; + case Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListDatabasesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL statement for retrieving the namespaces defined in the database. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListNamespacesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListSequencesSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableConstraintsSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * @param string|null $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableColumnsSQL($table, $database = null) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTablesSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListUsersSQL() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to list all views of a database or user. + * + * @param string $database + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListViewsSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the list of indexes for the current database. + * + * The current database parameter is optional but will always be passed + * when using the SchemaManager API and is the database the given table is in. + * + * Attention: Some platforms only support currentDatabase when they + * are connected with that database. Cross-database information schema + * requests may be impossible. + * + * @param string $table + * @param string $currentDatabase + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $table + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getListTableForeignKeysSQL($table) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $name + * @param string $sql + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateViewSQL($name, $sql) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $name + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDropViewSQL($name) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL snippet to drop an existing sequence. + * + * @param Sequence|string $sequence + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDropSequenceSQL($sequence) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param string $sequenceName + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getSequenceNextValSQL($sequenceName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to create a new database. + * + * @param string $database The name of the database that should be created. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getCreateDatabaseSQL($database) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Returns the SQL to set the transaction isolation level. + * + * @param integer $level + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getSetTransactionIsolationSQL($level) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create datetime fields in + * statements like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create datetime with timezone offset fields. + * + * @param array $fieldDeclaration + * + * @return string + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return $this->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + + /** + * Obtains DBMS specific SQL to be used to create date fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Obtains DBMS specific SQL to be used to create time fields in statements + * like CREATE TABLE. + * + * @param array $fieldDeclaration + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * @param array $fieldDeclaration + * + * @return string + */ + public function getFloatDeclarationSQL(array $fieldDeclaration) + { + return 'DOUBLE PRECISION'; + } + + /** + * Gets the default transaction isolation level of the platform. + * + * @return integer The default isolation level. + * + * @see Doctrine\DBAL\Connection\TRANSACTION_* constants. + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_COMMITTED; + } + + /* supports*() methods */ + + /** + * Whether the platform supports sequences. + * + * @return boolean + */ + public function supportsSequences() + { + return false; + } + + /** + * Whether the platform supports identity columns. + * + * Identity columns are columns that receive an auto-generated value from the + * database on insert of a row. + * + * @return boolean + */ + public function supportsIdentityColumns() + { + return false; + } + + /** + * Whether the platform emulates identity columns through sequences. + * + * Some platforms that do not support identity columns natively + * but support sequences can emulate identity columns by using + * sequences. + * + * @return boolean + */ + public function usesSequenceEmulatedIdentityColumns() + { + return false; + } + + /** + * Returns the name of the sequence for a particular identity column in a particular table. + * + * @param string $tableName The name of the table to return the sequence name for. + * @param string $columnName The name of the identity column in the table to return the sequence name for. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + * + * @see usesSequenceEmulatedIdentityColumns + */ + public function getIdentitySequenceName($tableName, $columnName) + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Whether the platform supports indexes. + * + * @return boolean + */ + public function supportsIndexes() + { + return true; + } + + /** + * Whether the platform supports partial indexes. + * + * @return boolean + */ + public function supportsPartialIndexes() + { + return false; + } + + /** + * Whether the platform supports altering tables. + * + * @return boolean + */ + public function supportsAlterTable() + { + return true; + } + + /** + * Whether the platform supports transactions. + * + * @return boolean + */ + public function supportsTransactions() + { + return true; + } + + /** + * Whether the platform supports savepoints. + * + * @return boolean + */ + public function supportsSavepoints() + { + return true; + } + + /** + * Whether the platform supports releasing savepoints. + * + * @return boolean + */ + public function supportsReleaseSavepoints() + { + return $this->supportsSavepoints(); + } + + /** + * Whether the platform supports primary key constraints. + * + * @return boolean + */ + public function supportsPrimaryConstraints() + { + return true; + } + + /** + * Whether the platform supports foreign key constraints. + * + * @return boolean + */ + public function supportsForeignKeyConstraints() + { + return true; + } + + /** + * Whether this platform supports onUpdate in foreign key constraints. + * + * @return boolean + */ + public function supportsForeignKeyOnUpdate() + { + return ($this->supportsForeignKeyConstraints() && true); + } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return false; + } + + /** + * Whether this platform can emulate schemas. + * + * Platforms that either support or emulate schemas don't automatically + * filter a schema for the namespaced elements in {@link + * AbstractManager#createSchema}. + * + * @return boolean + */ + public function canEmulateSchemas() + { + return false; + } + + /** + * Returns the default schema name. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + public function getDefaultSchemaName() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Whether this platform supports create database. + * + * Some databases don't allow to create and drop databases at all or only with certain tools. + * + * @return boolean + */ + public function supportsCreateDropDatabase() + { + return true; + } + + /** + * Whether the platform supports getting the affected rows of a recent update/delete type query. + * + * @return boolean + */ + public function supportsGettingAffectedRows() + { + return true; + } + + /** + * Whether this platform support to add inline column comments as postfix. + * + * @return boolean + */ + public function supportsInlineColumnComments() + { + return false; + } + + /** + * Whether this platform support the proprietary syntax "COMMENT ON asset". + * + * @return boolean + */ + public function supportsCommentOnStatement() + { + return false; + } + + /** + * Does this platform have native guid type. + * + * @return boolean + */ + public function hasNativeGuidType() + { + return false; + } + + /** + * Does this platform have native JSON type. + * + * @return boolean + */ + public function hasNativeJsonType() + { + return false; + } + + /** + * @deprecated + * @todo Remove in 3.0 + */ + public function getIdentityColumnNullInsertSQL() + { + return ""; + } + + /** + * Whether this platform supports views. + * + * @return boolean + */ + public function supportsViews() + { + return true; + } + + /** + * Does this platform support column collation? + * + * @return boolean + */ + public function supportsColumnCollation() + { + return false; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime value of this platform. + * + * @return string The format string. + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored datetime with timezone value of this platform. + * + * @return string The format string. + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored date value of this platform. + * + * @return string The format string. + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * Gets the format string, as accepted by the date() function, that describes + * the format of a stored time value of this platform. + * + * @return string The format string. + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * Adds an driver-specific LIMIT clause to the query. + * + * @param string $query + * @param integer|null $limit + * @param integer|null $offset + * + * @return string + * + * @throws DBALException + */ + final public function modifyLimitQuery($query, $limit, $offset = null) + { + if ($limit !== null) { + $limit = (int) $limit; + } + + if ($offset !== null) { + $offset = (int) $offset; + + if ($offset < 0) { + throw new DBALException("LIMIT argument offset=$offset is not valid"); + } + if ($offset > 0 && ! $this->supportsLimitOffset()) { + throw new DBALException(sprintf("Platform %s does not support offset values in limit queries.", $this->getName())); + } + } + + return $this->doModifyLimitQuery($query, $limit, $offset); + } + + /** + * Adds an driver-specific LIMIT clause to the query. + * + * @param string $query + * @param integer|null $limit + * @param integer|null $offset + * + * @return string + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + } + + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + + return $query; + } + + /** + * Whether the database platform support offsets in modify limit clauses. + * + * @return boolean + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * Gets the character casing of a column in an SQL result set of this platform. + * + * @param string $column The column name for which to get the correct character casing. + * + * @return string The column name in the character casing used in SQL result sets. + */ + public function getSQLResultCasing($column) + { + return $column; + } + + /** + * Makes any fixes to a name of a schema element (table, sequence, ...) that are required + * by restrictions of the platform, like a maximum length. + * + * @param string $schemaElementName + * + * @return string + */ + public function fixSchemaElementName($schemaElementName) + { + return $schemaElementName; + } + + /** + * Maximum length of any given database identifier, like tables or column names. + * + * @return integer + */ + public function getMaxIdentifierLength() + { + return 63; + } + + /** + * Returns the insert SQL for an empty insert statement. + * + * @param string $tableName + * @param string $identifierColumnName + * + * @return string + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (null)'; + } + + /** + * Generates a Truncate Table SQL statement for a given table. + * + * Cascade is not supported on many platforms but would optionally cascade the truncate by + * following the foreign keys. + * + * @param string $tableName + * @param boolean $cascade + * + * @return string + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName; + } + + /** + * This is for test reasons, many vendors have special requirements for dummy statements. + * + * @return string + */ + public function getDummySelectSQL() + { + return 'SELECT 1'; + } + + /** + * Returns the SQL to create a new savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function createSavePoint($savepoint) + { + return 'SAVEPOINT ' . $savepoint; + } + + /** + * Returns the SQL to release a savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function releaseSavePoint($savepoint) + { + return 'RELEASE SAVEPOINT ' . $savepoint; + } + + /** + * Returns the SQL to rollback a savepoint. + * + * @param string $savepoint + * + * @return string + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TO SAVEPOINT ' . $savepoint; + } + + /** + * Returns the keyword list instance of this platform. + * + * @return \Doctrine\DBAL\Platforms\Keywords\KeywordList + * + * @throws \Doctrine\DBAL\DBALException If no keyword list is specified. + */ + final public function getReservedKeywordsList() + { + // Check for an existing instantiation of the keywords class. + if ($this->_keywords) { + return $this->_keywords; + } + + $class = $this->getReservedKeywordsClass(); + $keywords = new $class; + if ( ! $keywords instanceof \Doctrine\DBAL\Platforms\Keywords\KeywordList) { + throw DBALException::notSupported(__METHOD__); + } + + // Store the instance so it doesn't need to be generated on every request. + $this->_keywords = $keywords; + + return $keywords; + } + + /** + * Returns the class name of the reserved keywords list. + * + * @return string + * + * @throws \Doctrine\DBAL\DBALException If not supported on this platform. + */ + protected function getReservedKeywordsClass() + { + throw DBALException::notSupported(__METHOD__); + } + + /** + * Quotes a literal string. + * This method is NOT meant to fix SQL injections! + * It is only meant to escape this platform's string literal + * quote character inside the given literal string. + * + * @param string $str The literal string to be quoted. + * + * @return string The quoted literal string. + */ + public function quoteStringLiteral($str) + { + $c = $this->getStringLiteralQuoteCharacter(); + + return $c . str_replace($c, $c . $c, $str) . $c; + } + + /** + * Gets the character used for string literal quoting. + * + * @return string + */ + public function getStringLiteralQuoteCharacter() + { + return "'"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php new file mode 100644 index 0000000..9cc794d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -0,0 +1,842 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +class DB2Platform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 32704; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 1; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + // todo blob(n) with $field['length']; + return 'BLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'bigint' => 'bigint', + 'integer' => 'integer', + 'time' => 'time', + 'date' => 'date', + 'varchar' => 'string', + 'character' => 'string', + 'varbinary' => 'binary', + 'binary' => 'binary', + 'clob' => 'text', + 'blob' => 'blob', + 'decimal' => 'decimal', + 'double' => 'float', + 'real' => 'float', + 'timestamp' => 'datetime', + ); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + // todo clob(n) with $field['length']; + return 'CLOB(1M)'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'db2'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; + } + + return $autoinc; + } + + /** + * {@inheritdoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND(' . $value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return 'BITOR(' . $value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_WEEK: + $interval *= 7; + $unit = self::DATE_INTERVAL_UNIT_DAY; + break; + + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + break; + } + + return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return "TIMESTAMP(0) WITH DEFAULT"; + } + + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE ' . $tableName . ' IMMEDIATE'; + } + + /** + * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. + * + * @license New BSD License + * + * @param string $table + * @param string $database + * + * @return string + */ + public function getListTableColumnsSQL($table, $database = null) + { + // We do the funky subquery and join syscat.columns.default this crazy way because + // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB, + // it wants shorter stuff like a varchar. + return " + SELECT + cols.default, + subq.* + FROM ( + SELECT DISTINCT + c.tabschema, + c.tabname, + c.colname, + c.colno, + c.typename, + c.nulls, + c.length, + c.scale, + c.identity, + tc.type AS tabconsttype, + k.colseq, + CASE + WHEN c.generated = 'D' THEN 1 + ELSE 0 + END AS autoincrement + FROM syscat.columns c + LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc + ON (k.tabschema = tc.tabschema + AND k.tabname = tc.tabname + AND tc.type = 'P')) + ON (c.tabschema = k.tabschema + AND c.tabname = k.tabname + AND c.colname = k.colname) + WHERE UPPER(c.tabname) = UPPER('" . $table . "') + ORDER BY c.colno + ) subq + JOIN syscat.columns cols + ON subq.tabschema = cols.tabschema + AND subq.tabname = cols.tabname + AND subq.colno = cols.colno + ORDER BY subq.colno + "; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT idx.INDNAME AS key_name, + idxcol.COLNAME AS column_name, + CASE + WHEN idx.UNIQUERULE = 'P' THEN 1 + ELSE 0 + END AS primary, + CASE + WHEN idx.UNIQUERULE = 'D' THEN 1 + ELSE 0 + END AS non_unique + FROM SYSCAT.INDEXES AS idx + JOIN SYSCAT.INDEXCOLUSE AS idxcol + ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME + WHERE idx.TABNAME = UPPER('" . $table . "') + ORDER BY idxcol.COLSEQ ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table) + { + return "SELECT fkcol.COLNAME AS local_column, + fk.REFTABNAME AS foreign_table, + pkcol.COLNAME AS foreign_column, + fk.CONSTNAME AS index_name, + CASE + WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_update, + CASE + WHEN fk.DELETERULE = 'C' THEN 'CASCADE' + WHEN fk.DELETERULE = 'N' THEN 'SET NULL' + WHEN fk.DELETERULE = 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_delete + FROM SYSCAT.REFERENCES AS fk + JOIN SYSCAT.KEYCOLUSE AS fkcol + ON fk.CONSTNAME = fkcol.CONSTNAME + AND fk.TABSCHEMA = fkcol.TABSCHEMA + AND fk.TABNAME = fkcol.TABNAME + JOIN SYSCAT.KEYCOLUSE AS pkcol + ON fk.REFKEYNAME = pkcol.CONSTNAME + AND fk.REFTABSCHEMA = pkcol.TABSCHEMA + AND fk.REFTABNAME = pkcol.TABNAME + WHERE fk.TABNAME = UPPER('" . $table . "') + ORDER BY fkcol.COLSEQ ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW ".$name." AS ".$sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return "DROP VIEW ".$name; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($database) + { + return "CREATE DATABASE ".$database; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return "DROP DATABASE " . $database; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDateSQL() + { + return 'CURRENT DATE'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimeSQL() + { + return 'CURRENT TIME'; + } + + /** + * {@inheritDoc} + */ + public function getCurrentTimestampSQL() + { + return "CURRENT TIMESTAMP"; + } + + /** + * {@inheritDoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + // Index declaration in statements like CREATE TABLE is not supported. + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $indexes = array(); + if (isset($options['indexes'])) { + $indexes = $options['indexes']; + } + $options['indexes'] = array(); + + $sqls = parent::_getCreateTableSQL($tableName, $columns, $options); + + foreach ($indexes as $definition) { + $sqls[] = $this->getCreateIndexSQL($definition, $tableName); + } + return $sqls; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + $commentsSQL = array(); + + $queryParts = array(); + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + // Adding non-nullable columns to a table requires a default value to be specified. + if ( ! empty($columnDef['notnull']) && + ! isset($columnDef['default']) && + empty($columnDef['autoincrement']) + ) { + $queryPart .= ' WITH DEFAULT'; + } + + $queryParts[] = $queryPart; + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $columnDiff->column->getQuotedName($this), + $this->getColumnComment($columnDiff->column) + ); + + if (count($columnDiff->changedProperties) === 1) { + continue; + } + } + + $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . + ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts); + } + + // Some table alteration operations require a table reorganization. + if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) { + $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')"; + } + + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Gathers the table alteration SQL for a given column diff. + * + * @param Table $table The table to gather the SQL for. + * @param ColumnDiff $columnDiff The column diff to evaluate. + * @param array $sql The sequence of table alteration statements to fill. + * @param array $queryParts The sequence of column alteration clauses to fill. + */ + private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts) + { + $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff); + + if (empty($alterColumnClauses)) { + return; + } + + // If we have a single column alteration, we can append the clause to the main query. + if (count($alterColumnClauses) === 1) { + $queryParts[] = current($alterColumnClauses); + + return; + } + + // We have multiple alterations for the same column, + // so we need to trigger a complete ALTER TABLE statement + // for each ALTER COLUMN clause. + foreach ($alterColumnClauses as $alterColumnClause) { + $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause; + } + } + + /** + * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return array + */ + private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) + { + $column = $columnDiff->column->toArray(); + + $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this); + + if ($column['columnDefinition']) { + return array($alterClause . ' ' . $column['columnDefinition']); + } + + $clauses = array(); + + if ($columnDiff->hasChanged('type') || + $columnDiff->hasChanged('length') || + $columnDiff->hasChanged('precision') || + $columnDiff->hasChanged('scale') || + $columnDiff->hasChanged('fixed') + ) { + $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this); + } + + if ($columnDiff->hasChanged('notnull')) { + $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; + } + + if ($columnDiff->hasChanged('default')) { + if (isset($column['default'])) { + $defaultClause = $this->getDefaultValueDeclarationSQL($column); + + if ($defaultClause) { + $clauses[] = $alterClause . ' SET' . $defaultClause; + } + } else { + $clauses[] = $alterClause . ' DROP DEFAULT'; + } + } + + return $clauses; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + if ($remIndex->isPrimary()) { + $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } elseif ($remIndex->isUnique()) { + $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this); + } else { + $sql[] = $this->getDropIndexSQL($remIndex, $table); + } + + $sql[] = $this->getCreateIndexSQL($addIndex, $table); + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! empty($field['autoincrement'])) { + return ''; + } + + if (isset($field['version']) && $field['version']) { + if ((string) $field['type'] != "DateTime") { + $field['default'] = "1"; + } + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName) + { + return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "DECLARE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return "SESSION." . $tableName; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null && $offset === null) { + return $query; + } + + $limit = (int) $limit; + $offset = (int) (($offset)?:0); + + // Todo OVER() needs ORDER BY data! + $sql = 'SELECT db22.* FROM (SELECT ROW_NUMBER() OVER() AS DC_ROWNUM, db21.* '. + 'FROM (' . $query . ') db21) db22 WHERE db22.DC_ROWNUM BETWEEN ' . ($offset+1) .' AND ' . ($offset+$limit); + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTR(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * DB2 returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' WITH RR USE AND KEEP UPDATE LOCKS'; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM sysibm.sysdummy1'; + } + + /** + * {@inheritDoc} + * + * DB2 supports savepoints, but they work semantically different than on other vendor platforms. + * + * TODO: We have to investigate how to get DB2 up and running with savepoints. + */ + public function supportsSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php new file mode 100644 index 0000000..08f053b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php @@ -0,0 +1,624 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\BinaryType; + +/** + * Drizzle platform + * + * @author Kim Hemsø Rasmussen + */ +class DrizzlePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + + return $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'; + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'varchar' => 'string', + 'varbinary' => 'binary', + 'integer' => 'integer', + 'blob' => 'blob', + 'decimal' => 'decimal', + 'datetime' => 'datetime', + 'date' => 'date', + 'time' => 'time', + 'text' => 'text', + 'timestamp' => 'datetime', + 'double' => 'float', + 'bigint' => 'bigint', + ); + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $index => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'"; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\DrizzleKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT, CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT," . + " NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME" . + " FROM DATA_DICTIONARY.COLUMNS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS, UPDATE_RULE, DELETE_RULE" . + " FROM DATA_DICTIONARY.FOREIGN_KEYS" . + " WHERE CONSTRAINT_SCHEMA=" . $database . " AND CONSTRAINT_TABLE='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT INDEX_NAME AS 'key_name', COLUMN_NAME AS 'column_name', IS_USED_IN_PRIMARY AS 'primary', IS_UNIQUE=0 AS 'non_unique'" . + " FROM DATA_DICTIONARY.INDEX_PARTS" . + " WHERE TABLE_SCHEMA=" . $database . " AND TABLE_NAME='" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsViews() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // drizzle primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * {@inheritDoc} + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + + // Do not generate column alteration clause if type is binary and only fixed property has changed. + // Drizzle only supports binary type columns with variable length. + // Avoids unnecessary table alteration statements. + if ($columnArray['type'] instanceof BinaryType && + $columnDiff->hasChanged('fixed') && + count($columnDiff->changedProperties) === 1 + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 'true' : 'false'; + } + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 'true' : 'false'; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php new file mode 100644 index 0000000..ff8aac8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -0,0 +1,441 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * DB2 Keywords. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DB2Keywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'DB2'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ACTIVATE', + 'ADD', + 'AFTER', + 'ALIAS', + 'ALL', + 'ALLOCATE', + 'DOCUMENT', + 'DOUBLE', + 'DROP', + 'DSSIZE', + 'DYNAMIC', + 'EACH', + 'LOCK', + 'LOCKMAX', + 'LOCKSIZE', + 'LONG', + 'LOOP', + 'MAINTAINED', + 'ROUND_CEILING', + 'ROUND_DOWN', + 'ROUND_FLOOR', + 'ROUND_HALF_DOWN', + 'ROUND_HALF_EVEN', + 'ROUND_HALF_UP', + 'ALLOW', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASENSITIVE', + 'ASSOCIATE', + 'ASUTIME', + 'AT', + 'ATTRIBUTES', + 'AUDIT', + 'AUTHORIZATION', + 'AUX', + 'AUXILIARY', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BINARY', + 'BUFFERPOOL', + 'BY', + 'CACHE', + 'CALL', + 'CALLED', + 'CAPTURE', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CCSID', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'CLONE', + 'CLOSE', + 'CLUSTER', + 'COLLECTION', + 'COLLID', + 'COLUMN', + 'COMMENT', + 'COMMIT', + 'CONCAT', + 'CONDITION', + 'CONNECT', + 'CONNECTION', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'COUNT', + 'COUNT_BIG', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_LC_CTYPE', + 'CURRENT_PATH', + 'CURRENT_SCHEMA', + 'CURRENT_SERVER', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TIMEZONE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATA', + 'DATABASE', + 'DATAPARTITIONNAME', + 'DATAPARTITIONNUM', + 'EDITPROC', + 'ELSE', + 'ELSEIF', + 'ENABLE', + 'ENCODING', + 'ENCRYPTION', + 'END', + 'END-EXEC', + 'ENDING', + 'ERASE', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXCEPTION', + 'EXCLUDING', + 'EXCLUSIVE', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'EXTERNAL', + 'EXTRACT', + 'FENCED', + 'FETCH', + 'FIELDPROC', + 'FILE', + 'FINAL', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GENERAL', + 'GENERATED', + 'GET', + 'GLOBAL', + 'GO', + 'GOTO', + 'GRANT', + 'GRAPHIC', + 'GROUP', + 'HANDLER', + 'HASH', + 'HASHED_VALUE', + 'HAVING', + 'HINT', + 'HOLD', + 'HOUR', + 'HOURS', + 'IDENTITY', + 'IF', + 'IMMEDIATE', + 'IN', + 'INCLUDING', + 'INCLUSIVE', + 'INCREMENT', + 'INDEX', + 'INDICATOR', + 'INF', + 'INFINITY', + 'INHERIT', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INTEGRITY', + 'MATERIALIZED', + 'MAXVALUE', + 'MICROSECOND', + 'MICROSECONDS', + 'MINUTE', + 'MINUTES', + 'MINVALUE', + 'MODE', + 'MODIFIES', + 'MONTH', + 'MONTHS', + 'NAN', + 'NEW', + 'NEW_TABLE', + 'NEXTVAL', + 'NO', + 'NOCACHE', + 'NOCYCLE', + 'NODENAME', + 'NODENUMBER', + 'NOMAXVALUE', + 'NOMINVALUE', + 'NONE', + 'NOORDER', + 'NORMALIZED', + 'NOT', + 'NULL', + 'NULLS', + 'NUMPARTS', + 'OBID', + 'OF', + 'OLD', + 'OLD_TABLE', + 'ON', + 'OPEN', + 'OPTIMIZATION', + 'OPTIMIZE', + 'OPTION', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERRIDING', + 'PACKAGE', + 'PADDED', + 'PAGESIZE', + 'PARAMETER', + 'PART', + 'PARTITION', + 'PARTITIONED', + 'PARTITIONING', + 'PARTITIONS', + 'PASSWORD', + 'PATH', + 'PIECESIZE', + 'PLAN', + 'POSITION', + 'PRECISION', + 'PREPARE', + 'PREVVAL', + 'PRIMARY', + 'PRIQTY', + 'PRIVILEGES', + 'PROCEDURE', + 'PROGRAM', + 'PSID', + 'ROUND_UP', + 'ROUTINE', + 'ROW', + 'ROW_NUMBER', + 'ROWNUMBER', + 'ROWS', + 'ROWSET', + 'RRN', + 'RUN', + 'SAVEPOINT', + 'SCHEMA', + 'SCRATCHPAD', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SECONDS', + 'SECQTY', + 'SECURITY', + 'SELECT', + 'SENSITIVE', + 'SEQUENCE', + 'SESSION', + 'SESSION_USER', + 'SET', + 'SIGNAL', + 'SIMPLE', + 'SNAN', + 'SOME', + 'SOURCE', + 'SPECIFIC', + 'SQL', + 'SQLID', + 'STACKED', + 'STANDARD', + 'START', + 'STARTING', + 'STATEMENT', + 'STATIC', + 'STATMENT', + 'STAY', + 'STOGROUP', + 'STORES', + 'STYLE', + 'SUBSTRING', + 'SUMMARY', + 'SYNONYM', + 'SYSFUN', + 'SYSIBM', + 'SYSPROC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESPACE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'TRIM', + 'TRUNCATE', + 'TYPE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNTIL', + 'UPDATE', + 'DATE', + 'DAY', + 'DAYS', + 'DB2GENERAL', + 'DB2GENRL', + 'DB2SQL', + 'DBINFO', + 'DBPARTITIONNAME', + 'DBPARTITIONNUM', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DEFAULTS', + 'DEFINITION', + 'DELETE', + 'DENSE_RANK', + 'DENSERANK', + 'DESCRIBE', + 'DESCRIPTOR', + 'DETERMINISTIC', + 'DIAGNOSTICS', + 'DISABLE', + 'DISALLOW', + 'DISCONNECT', + 'DISTINCT', + 'DO', + 'INTERSECT', + 'PUBLIC', + 'USAGE', + 'INTO', + 'QUERY', + 'USER', + 'IS', + 'QUERYNO', + 'USING', + 'ISOBID', + 'RANGE', + 'VALIDPROC', + 'ISOLATION', + 'RANK', + 'VALUE', + 'ITERATE', + 'READ', + 'VALUES', + 'JAR', + 'READS', + 'VARIABLE', + 'JAVA', + 'RECOVERY', + 'VARIANT', + 'JOIN', + 'REFERENCES', + 'VCAT', + 'KEEP', + 'REFERENCING', + 'VERSION', + 'KEY', + 'REFRESH', + 'VIEW', + 'LABEL', + 'RELEASE', + 'VOLATILE', + 'LANGUAGE', + 'RENAME', + 'VOLUMES', + 'LATERAL', + 'REPEAT', + 'WHEN', + 'LC_CTYPE', + 'RESET', + 'WHENEVER', + 'LEAVE', + 'RESIGNAL', + 'WHERE', + 'LEFT', + 'RESTART', + 'WHILE', + 'LIKE', + 'RESTRICT', + 'WITH', + 'LINKTYPE', + 'RESULT', + 'WITHOUT', + 'LOCAL', + 'RESULT_SET_LOCATOR WLM', + 'LOCALDATE', + 'RETURN', + 'WRITE', + 'LOCALE', + 'RETURNS', + 'XMLELEMENT', + 'LOCALTIME', + 'REVOKE', + 'XMLEXISTS', + 'LOCALTIMESTAMP RIGHT', + 'XMLNAMESPACES', + 'LOCATOR', + 'ROLE', + 'YEAR', + 'LOCATORS', + 'ROLLBACK', + 'YEARS', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php new file mode 100644 index 0000000..21d59d8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php @@ -0,0 +1,345 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Drizzle Keywordlist. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'drizzle'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ABS', + 'ALL', + 'ALLOCATE', + 'ALTER', + 'AND', + 'ANY', + 'ARE', + 'ARRAY', + 'AS', + 'ASENSITIVE', + 'ASYMMETRIC', + 'AT', + 'ATOMIC', + 'AUTHORIZATION', + 'AVG', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOOLEAN', + 'BOTH', + 'BY', + 'CALL', + 'CALLED', + 'CARDINALITY', + 'CASCADED', + 'CASE', + 'CAST', + 'CEIL', + 'CEILING', + 'CHAR', + 'CHARACTER', + 'CHARACTER_LENGTH', + 'CHAR_LENGTH', + 'CHECK', + 'CLOB', + 'CLOSE', + 'COALESCE', + 'COLLATE', + 'COLLECT', + 'COLUMN', + 'COMMIT', + 'CONDITION', + 'CONNECT', + 'CONSTRAINT', + 'CONVERT', + 'CORR', + 'CORRESPONDING', + 'COUNT', + 'COVAR_POP', + 'COVAR_SAMP', + 'CREATE', + 'CROSS', + 'CUBE', + 'CUME_DIST', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_DEFAULT_TRANSFORM_GROUP', + 'CURRENT_PATH', + 'CURRENT_ROLE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TRANSFORM_GROUP_FOR_TYPE', + 'CURRENT_USER', + 'CURSOR', + 'CYCLE', + 'DATE', + 'DAY', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENSE_RANK', + 'DEREF', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISCONNECT', + 'DISTINCT', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'EACH', + 'ELEMENT', + 'ELSE', + 'END', + 'ESCAPE', + 'EVERY', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXP', + 'EXTERNAL', + 'EXTRACT', + 'FALSE', + 'FETCH', + 'FILTER', + 'FLOAT', + 'FLOOR', + 'FOR', + 'FOREIGN', + 'FREE', + 'FROM', + 'FULL', + 'FUNCTION', + 'FUSION', + 'GET', + 'GLOBAL', + 'GRANT', + 'GROUP', + 'GROUPING', + 'HAVING', + 'HOLD', + 'HOUR', + 'IDENTITY', + 'IN', + 'INDICATOR', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INTEGER', + 'INTERSECT', + 'INTERSECTION', + 'INTERVAL', + 'INTO', + 'IS', + 'JOIN', + 'LANGUAGE', + 'LARGE', + 'LATERAL', + 'LEADING', + 'LEFT', + 'LIKE', + 'LN', + 'LOCAL', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOWER', + 'MATCH', + 'MAX', + 'MEMBER', + 'MERGE', + 'METHOD', + 'MIN', + 'MINUTE', + 'MOD', + 'MODIFIES', + 'MODULE', + 'MONTH', + 'MULTISET', + 'NATIONAL', + 'NATURAL', + 'NCHAR', + 'NCLOB', + 'NEW', + 'NO', + 'NONE', + 'NORMALIZE', + 'NOT', + 'NULL_SYM', + 'NULLIF', + 'NUMERIC', + 'OCTET_LENGTH', + 'OF', + 'OLD', + 'ON', + 'ONLY', + 'OPEN', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'OVERLAY', + 'PARAMETER', + 'PARTITION', + 'PERCENTILE_CONT', + 'PERCENTILE_DISC', + 'PERCENT_RANK', + 'POSITION', + 'POWER', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PROCEDURE', + 'RANGE', + 'RANK', + 'READS', + 'REAL', + 'RECURSIVE', + 'REF', + 'REFERENCES', + 'REFERENCING', + 'REGR_AVGX', + 'REGR_AVGY', + 'REGR_COUNT', + 'REGR_INTERCEPT', + 'REGR_R2', + 'REGR_SLOPE', + 'REGR_SXX', + 'REGR_SXY', + 'REGR_SYY', + 'RELEASE', + 'RESULT', + 'RETURN', + 'RETURNS', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'ROW', + 'ROWS', + 'ROW_NUMBER', + 'SAVEPOINT', + 'SCOPE', + 'SCROLL', + 'SEARCH', + 'SECOND', + 'SELECT', + 'SENSITIVE', + 'SESSION_USER', + 'SET', + 'SIMILAR', + 'SMALLINT', + 'SOME', + 'SPECIFIC', + 'SPECIFICTYPE', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQRT', + 'START', + 'STATIC', + 'STDDEV_POP', + 'STDDEV_SAMP', + 'SUBMULTISET', + 'SUBSTRING', + 'SUM', + 'SYMMETRIC', + 'SYSTEM', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TIMEZONE_HOUR', + 'TIMEZONE_MINUTE', + 'TO', + 'TRAILING', + 'TRANSLATE', + 'TRANSLATION', + 'TREAT', + 'TRIGGER', + 'TRIM', + 'TRUE', + 'UESCAPE', + 'UNION', + 'UNIQUE', + 'UNKNOWN', + 'UNNEST', + 'UPDATE', + 'UPPER', + 'USER', + 'USING', + 'VALUE', + 'VALUES', + 'VARCHAR', + 'VARYING', + 'VAR_POP', + 'VAR_SAMP', + 'WHEN', + 'WHENEVER', + 'WHERE', + 'WIDTH_BUCKET', + 'WINDOW', + 'WITH', + 'WITHIN', + 'WITHOUT', + 'XML', + 'XMLAGG', + 'XMLATTRIBUTES', + 'XMLBINARY', + 'XMLCOMMENT', + 'XMLCONCAT', + 'XMLELEMENT', + 'XMLFOREST', + 'XMLNAMESPACES', + 'XMLPARSE', + 'XMLPI', + 'XMLROOT', + 'XMLSERIALIZE', + 'YEAR', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php new file mode 100644 index 0000000..5b8d74d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Abstract interface for a SQL reserved keyword dictionary. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class KeywordList +{ + /** + * @var array|null + */ + private $keywords = null; + + /** + * Checks if the given word is a keyword of this dialect/vendor platform. + * + * @param string $word + * + * @return boolean + */ + public function isKeyword($word) + { + if ($this->keywords === null) { + $this->initializeKeywords(); + } + + return isset($this->keywords[strtoupper($word)]); + } + + /** + * @return void + */ + protected function initializeKeywords() + { + $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); + } + + /** + * Returns the list of keywords. + * + * @return array + */ + abstract protected function getKeywords(); + + /** + * Returns the name of this keyword list. + * + * @return string + */ + abstract public function getName(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php new file mode 100644 index 0000000..9c8f614 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MsSQLKeywords.php @@ -0,0 +1,43 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MsSQL Keywordlist + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + * @deprecated Use SQLServerKeywords class instead. + */ +class MsSQLKeywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MsSQL'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php new file mode 100644 index 0000000..7d7b202 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQL57Keywords.php @@ -0,0 +1,281 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL 5.7 reserved keywords list. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class MySQL57Keywords extends MySQLKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MySQL57'; + } + + /** + * {@inheritdoc} + * + * @link http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-7.html + */ + protected function getKeywords() + { + return array( + 'ACCESSIBLE', + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GET', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IO_AFTER_GTIDS', + 'IO_BEFORE_GTIDS', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINEAR', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MASTER_BIND', + 'MASTER_SSL_VERIFY_SERVER_CERT', + 'MATCH', + 'MAXVALUE', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NO_WRITE_TO_BINLOG', + 'NONBLOCKING', + 'NOT', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PARTITION', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RANGE', + 'READ', + 'READ_WRITE', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESIGNAL', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SIGNAL', + 'SMALLINT', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SSL', + 'STARTING', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php new file mode 100644 index 0000000..ba25774 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php @@ -0,0 +1,273 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * MySQL Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class MySQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'MySQL'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONNECTION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LABEL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MATCH', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NOT', + 'NO_WRITE_TO_BINLOG', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RAID0', + 'RANGE', + 'READ', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SMALLINT', + 'SONAME', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SSL', + 'STARTING', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'X509', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php new file mode 100644 index 0000000..b37fdad --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/OracleKeywords.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Oracle Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + */ +class OracleKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'Oracle'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ACCESS', + 'ELSE', + 'MODIFY', + 'START', + 'ADD', + 'EXCLUSIVE', + 'NOAUDIT', + 'SELECT', + 'ALL', + 'EXISTS', + 'NOCOMPRESS', + 'SESSION', + 'ALTER', + 'FILE', + 'NOT', + 'SET', + 'AND', + 'FLOAT', + 'NOTFOUND ', + 'SHARE', + 'ANY', + 'FOR', + 'NOWAIT', + 'SIZE', + 'ARRAYLEN', + 'FROM', + 'NULL', + 'SMALLINT', + 'AS', + 'GRANT', + 'NUMBER', + 'SQLBUF', + 'ASC', + 'GROUP', + 'OF', + 'SUCCESSFUL', + 'AUDIT', + 'HAVING', + 'OFFLINE ', + 'SYNONYM', + 'BETWEEN', + 'IDENTIFIED', + 'ON', + 'SYSDATE', + 'BY', + 'IMMEDIATE', + 'ONLINE', + 'TABLE', + 'CHAR', + 'IN', + 'OPTION', + 'THEN', + 'CHECK', + 'INCREMENT', + 'OR', + 'TO', + 'CLUSTER', + 'INDEX', + 'ORDER', + 'TRIGGER', + 'COLUMN', + 'INITIAL', + 'PCTFREE', + 'UID', + 'COMMENT', + 'INSERT', + 'PRIOR', + 'UNION', + 'COMPRESS', + 'INTEGER', + 'PRIVILEGES', + 'UNIQUE', + 'CONNECT', + 'INTERSECT', + 'PUBLIC', + 'UPDATE', + 'CREATE', + 'INTO', + 'RAW', + 'USER', + 'CURRENT', + 'IS', + 'RENAME', + 'VALIDATE', + 'DATE', + 'LEVEL', + 'RESOURCE', + 'VALUES', + 'DECIMAL', + 'LIKE', + 'REVOKE', + 'VARCHAR', + 'DEFAULT', + 'LOCK', + 'ROW', + 'VARCHAR2', + 'DELETE', + 'LONG', + 'ROWID', + 'VIEW', + 'DESC', + 'MAXEXTENTS', + 'ROWLABEL', + 'WHENEVER', + 'DISTINCT', + 'MINUS', + 'ROWNUM', + 'WHERE', + 'DROP', + 'MODE', + 'ROWS', + 'WITH', + 'RANGE', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php new file mode 100644 index 0000000..4e5c632 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL91Keywords.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.1 reserved keywords list. + * + * @author Martin Hasoň + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL91Keywords extends PostgreSQLKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL91'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.1/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'ARRAY', + 'AS', + 'ASC', + 'ASYMMETRIC', + 'AUTHORIZATION', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONCURRENTLY', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_CATALOG', + 'CURRENT_DATE', + 'CURRENT_ROLE', + 'CURRENT_SCHEMA', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FETCH', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFFSET', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'RETURNING', + 'RIGHT', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'SYMMETRIC', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VARIADIC', + 'VERBOSE', + 'WHEN', + 'WHERE', + 'WINDOW', + 'WITH', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php new file mode 100644 index 0000000..7180dfd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQL92Keywords.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL 9.2 reserved keywords list. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL92Keywords extends PostgreSQL91Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL92'; + } + + /** + * {@inheritdoc} + * + * @link http://www.postgresql.org/docs/9.2/static/sql-keywords-appendix.html + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'COLLATION', + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php new file mode 100644 index 0000000..9e49d7f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/PostgreSQLKeywords.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * PostgreSQL Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Marcelo Santos Araujo + */ +class PostgreSQLKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'PostgreSQL'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BETWEEN', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONSTRAINT', + 'CREATE', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NEW', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFF', + 'OFFSET', + 'OLD', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VERBOSE', + 'WHEN', + 'WHERE' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php new file mode 100644 index 0000000..e1fdd49 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/ReservedKeywordsValidator.php @@ -0,0 +1,143 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +class ReservedKeywordsValidator implements Visitor +{ + /** + * @var KeywordList[] + */ + private $keywordLists = array(); + + /** + * @var array + */ + private $violations = array(); + + /** + * @param \Doctrine\DBAL\Platforms\Keywords\KeywordList[] $keywordLists + */ + public function __construct(array $keywordLists) + { + $this->keywordLists = $keywordLists; + } + + /** + * @return array + */ + public function getViolations() + { + return $this->violations; + } + + /** + * @param string $word + * + * @return array + */ + private function isReservedWord($word) + { + if ($word[0] == "`") { + $word = str_replace('`', '', $word); + } + + $keywordLists = array(); + foreach ($this->keywordLists as $keywordList) { + if ($keywordList->isKeyword($word)) { + $keywordLists[] = $keywordList->getName(); + } + } + + return $keywordLists; + } + + /** + * @param string $asset + * @param array $violatedPlatforms + * + * @return void + */ + private function addViolation($asset, $violatedPlatforms) + { + if ( ! $violatedPlatforms) { + return; + } + + $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + $this->addViolation( + 'Table ' . $table->getName() . ' column ' . $column->getName(), + $this->isReservedWord($column->getName()) + ); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->addViolation( + 'Table ' . $table->getName(), + $this->isReservedWord($table->getName()) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php new file mode 100644 index 0000000..12eacbf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere11Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 11 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere11Keywords extends SQLAnywhereKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere11'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/1100/en/dbreference_en11/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + array_diff( + parent::getKeywords(), + array('IQ') + ), + array( + 'MERGE', + 'OPENSTRING' + ) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php new file mode 100644 index 0000000..22e0489 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere12Keywords.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 12 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere12Keywords extends SQLAnywhere11Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere12'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/1200/en/dbreference/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + array_diff( + parent::getKeywords(), + array( + 'INDEX_LPAREN', + 'SYNTAX_ERROR', + 'WITH_CUBE', + 'WITH_LPAREN', + 'WITH_ROLLUP' + ) + ), + array( + 'DATETIMEOFFSET', + 'LIMIT', + 'OPENXML', + 'SPATIAL', + 'TREAT' + ) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php new file mode 100644 index 0000000..4d78b84 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhere16Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 16 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhere16Keywords extends SQLAnywhere12Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere16'; + } + + /** + * {@inheritdoc} + * + * @link http://dcx.sybase.com/index.html#sa160/en/dbreference/alhakeywords.html + */ + protected function getKeywords() + { + return array_merge( + parent::getKeywords(), + array( + 'ARRAY', + 'JSON', + 'ROW', + 'ROWTYPE', + 'UNNEST', + 'VARRAY' + ) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php new file mode 100644 index 0000000..cabf1e4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLAnywhereKeywords.php @@ -0,0 +1,281 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SAP Sybase SQL Anywhere 10 reserved keywords list. + * + * @author Steve Müller + */ +class SQLAnywhereKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLAnywhere'; + } + + /** + * {@inheritdoc} + * + * @link http://infocenter.sybase.com/help/topic/com.sybase.dbrfen10/pdf/dbrfen10.pdf?noframes=true + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'ATTACH', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BIT', + 'BOTTOM', + 'BREAK', + 'BY', + 'CALL', + 'CAPABILITY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHAR', + 'CHAR_CONVERT', + 'CHARACTER', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'COMMENT', + 'COMMIT', + 'COMPRESSED', + 'CONFLICT', + 'CONNECT', + 'CONSTRAINT', + 'CONTAINS', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CUBE', + 'CURRENT', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATE', + 'DBSPACE', + 'DEALLOCATE', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DELETING', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DO', + 'DOUBLE', + 'DROP', + 'DYNAMIC', + 'ELSE', + 'ELSEIF', + 'ENCRYPTED', + 'END', + 'ENDIF', + 'ESCAPE', + 'EXCEPT', + 'EXCEPTION', + 'EXEC', + 'EXECUTE', + 'EXISTING', + 'EXISTS', + 'EXTERNLOGIN', + 'FETCH', + 'FIRST', + 'FLOAT', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FORWARD', + 'FROM', + 'FULL', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTIFIED', + 'IF', + 'IN', + 'INDEX', + 'INDEX_LPAREN', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INSERTING', + 'INSTALL', + 'INSTEAD', + 'INT', + 'INTEGER', + 'INTEGRATED', + 'INTERSECT', + 'INTO', + 'IQ', + 'IS', + 'ISOLATION', + 'JOIN', + 'KERBEROS', + 'KEY', + 'LATERAL', + 'LEFT', + 'LIKE', + 'LOCK', + 'LOGIN', + 'LONG', + 'MATCH', + 'MEMBERSHIP', + 'MESSAGE', + 'MODE', + 'MODIFY', + 'NATURAL', + 'NCHAR', + 'NEW', + 'NO', + 'NOHOLDLOCK', + 'NOT', + 'NOTIFY', + 'NULL', + 'NUMERIC', + 'NVARCHAR', + 'OF', + 'OFF', + 'ON', + 'OPEN', + 'OPTION', + 'OPTIONS', + 'OR', + 'ORDER', + 'OTHERS', + 'OUT', + 'OUTER', + 'OVER', + 'PASSTHROUGH', + 'PRECISION', + 'PREPARE', + 'PRIMARY', + 'PRINT', + 'PRIVILEGES', + 'PROC', + 'PROCEDURE', + 'PUBLICATION', + 'RAISERROR', + 'READTEXT', + 'REAL', + 'REFERENCE', + 'REFERENCES', + 'REFRESH', + 'RELEASE', + 'REMOTE', + 'REMOVE', + 'RENAME', + 'REORGANIZE', + 'RESOURCE', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROLLUP', + 'SAVE', + 'SAVEPOINT', + 'SCROLL', + 'SELECT', + 'SENSITIVE', + 'SESSION', + 'SET', + 'SETUSER', + 'SHARE', + 'SMALLINT', + 'SOME', + 'SQLCODE', + 'SQLSTATE', + 'START', + 'STOP', + 'SUBTRANS', + 'SUBTRANSACTION', + 'SYNCHRONIZE', + 'SYNTAX_ERROR', + 'TABLE', + 'TEMPORARY', + 'THEN', + 'TIME', + 'TIMESTAMP', + 'TINYINT', + 'TO', + 'TOP', + 'TRAN', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNBOUNDED', + 'UNION', + 'UNIQUE', + 'UNIQUEIDENTIFIER', + 'UNKNOWN', + 'UNSIGNED', + 'UPDATE', + 'UPDATING', + 'USER', + 'USING', + 'VALIDATE', + 'VALUES', + 'VARBINARY', + 'VARBIT', + 'VARCHAR', + 'VARIABLE', + 'VARYING', + 'VIEW', + 'WAIT', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WINDOW', + 'WITH', + 'WITH_CUBE', + 'WITH_LPAREN', + 'WITH_ROLLUP', + 'WITHIN', + 'WORK', + 'WRITETEXT', + 'XML' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php new file mode 100644 index 0000000..3a74349 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2005Keywords.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2005 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2005Keywords extends SQLServerKeywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2005'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-US/library/ms189822%28v=sql.90%29.aspx + */ + protected function getKeywords() + { + return array_merge(array_diff(parent::getKeywords(), array('DUMMY')), array( + 'EXTERNAL', + 'PIVOT', + 'REVERT', + 'SECURITYAUDIT', + 'TABLESAMPLE', + 'UNPIVOT' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php new file mode 100644 index 0000000..38556b5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2008Keywords.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2008 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2008Keywords extends SQLServer2005Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2008'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822%28v=sql.100%29.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'MERGE' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php new file mode 100644 index 0000000..ada1c02 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServer2012Keywords.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2012 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.3 + * @author Steve Müller + */ +class SQLServer2012Keywords extends SQLServer2008Keywords +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer2012'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/ms189822.aspx + */ + protected function getKeywords() + { + return array_merge(parent::getKeywords(), array( + 'SEMANTICKEYPHRASETABLE', + 'SEMANTICSIMILARITYDETAILSTABLE', + 'SEMANTICSIMILARITYTABLE', + 'TRY_CONVERT', + 'WITHIN GROUP' + )); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php new file mode 100644 index 0000000..fb43d2d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLServerKeywords.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Microsoft SQL Server 2000 reserved keyword dictionary. + * + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + * @author David Coallier + * @author Steve Müller + */ +class SQLServerKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLServer'; + } + + /** + * {@inheritdoc} + * + * @link http://msdn.microsoft.com/en-us/library/aa238507%28v=sql.80%29.aspx + */ + protected function getKeywords() + { + return array( + 'ADD', + 'ALL', + 'ALTER', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BACKUP', + 'BEGIN', + 'BETWEEN', + 'BREAK', + 'BROWSE', + 'BULK', + 'BY', + 'CASCADE', + 'CASE', + 'CHECK', + 'CHECKPOINT', + 'CLOSE', + 'CLUSTERED', + 'COALESCE', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'COMPUTE', + 'CONSTRAINT', + 'CONTAINS', + 'CONTAINSTABLE', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DBCC', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DELETE', + 'DENY', + 'DESC', + 'DISK', + 'DISTINCT', + 'DISTRIBUTED', + 'DOUBLE', + 'DROP', + 'DUMP', + 'ELSE', + 'END', + 'ERRLVL', + 'ESCAPE', + 'EXCEPT', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXTERNAL', + 'FETCH', + 'FILE', + 'FILLFACTOR', + 'FOR', + 'FOREIGN', + 'FREETEXT', + 'FREETEXTTABLE', + 'FROM', + 'FULL', + 'FUNCTION', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HOLDLOCK', + 'IDENTITY', + 'IDENTITY_INSERT', + 'IDENTITYCOL', + 'IF', + 'IN', + 'INDEX', + 'INNER', + 'INSERT', + 'INTERSECT', + 'INTO', + 'IS', + 'JOIN', + 'KEY', + 'KILL', + 'LEFT', + 'LIKE', + 'LINENO', + 'LOAD', + 'NATIONAL', + 'NOCHECK ', + 'NONCLUSTERED', + 'NOT', + 'NULL', + 'NULLIF', + 'OF', + 'OFF', + 'OFFSETS', + 'ON', + 'OPEN', + 'OPENDATASOURCE', + 'OPENQUERY', + 'OPENROWSET', + 'OPENXML', + 'OPTION', + 'OR', + 'ORDER', + 'OUTER', + 'OVER', + 'PERCENT', + 'PIVOT', + 'PLAN', + 'PRECISION', + 'PRIMARY', + 'PRINT', + 'PROC', + 'PROCEDURE', + 'PUBLIC', + 'RAISERROR', + 'READ', + 'READTEXT', + 'RECONFIGURE', + 'REFERENCES', + 'REPLICATION', + 'RESTORE', + 'RESTRICT', + 'RETURN', + 'REVERT', + 'REVOKE', + 'RIGHT', + 'ROLLBACK', + 'ROWCOUNT', + 'ROWGUIDCOL', + 'RULE', + 'SAVE', + 'SCHEMA', + 'SECURITYAUDIT', + 'SELECT', + 'SESSION_USER', + 'SET', + 'SETUSER', + 'SHUTDOWN', + 'SOME', + 'STATISTICS', + 'SYSTEM_USER', + 'TABLE', + 'TABLESAMPLE', + 'TEXTSIZE', + 'THEN', + 'TO', + 'TOP', + 'TRAN', + 'TRANSACTION', + 'TRIGGER', + 'TRUNCATE', + 'TSEQUAL', + 'UNION', + 'UNIQUE', + 'UNPIVOT', + 'UPDATE', + 'UPDATETEXT', + 'USE', + 'USER', + 'VALUES', + 'VARYING', + 'VIEW', + 'WAITFOR', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITETEXT' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php new file mode 100644 index 0000000..a162d3d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/SQLiteKeywords.php @@ -0,0 +1,168 @@ +. + */ + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * SQLite Keywordlist. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLiteKeywords extends KeywordList +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'SQLite'; + } + + /** + * {@inheritdoc} + */ + protected function getKeywords() + { + return array( + 'ABORT', + 'ACTION', + 'ADD', + 'AFTER', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ATTACH', + 'AUTOINCREMENT', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BY', + 'CASCADE', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'COMMIT', + 'CONFLICT', + 'CONSTRAINT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'DATABASE', + 'DEFAULT', + 'DEFERRABLE', + 'DEFERRED', + 'DELETE', + 'DESC', + 'DETACH', + 'DISTINCT', + 'DROP', + 'EACH', + 'ELSE', + 'END', + 'ESCAPE', + 'EXCEPT', + 'EXCLUSIVE', + 'EXISTS', + 'EXPLAIN', + 'FAIL', + 'FOR', + 'FOREIGN', + 'FROM', + 'FULL', + 'GLOB', + 'GROUP', + 'HAVING', + 'IF', + 'IGNORE', + 'IMMEDIATE', + 'IN', + 'INDEX', + 'INDEXED', + 'INITIALLY', + 'INNER', + 'INSERT', + 'INSTEAD', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'KEY', + 'LEFT', + 'LIKE', + 'LIMIT', + 'MATCH', + 'NATURAL', + 'NO', + 'NOT', + 'NOTNULL', + 'NULL', + 'OF', + 'OFFSET', + 'ON', + 'OR', + 'ORDER', + 'OUTER', + 'PLAN', + 'PRAGMA', + 'PRIMARY', + 'QUERY', + 'RAISE', + 'REFERENCES', + 'REGEXP', + 'REINDEX', + 'RELEASE', + 'RENAME', + 'REPLACE', + 'RESTRICT', + 'RIGHT', + 'ROLLBACK', + 'ROW', + 'SAVEPOINT', + 'SELECT', + 'SET', + 'TABLE', + 'TEMP', + 'TEMPORARY', + 'THEN', + 'TO', + 'TRANSACTION', + 'TRIGGER', + 'UNION', + 'UNIQUE', + 'UPDATE', + 'USING', + 'VACUUM', + 'VALUES', + 'VIEW', + 'VIRTUAL', + 'WHEN', + 'WHERE' + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php new file mode 100644 index 0000000..36b525e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySQL57Platform.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * Provides the behavior, features and SQL dialect of the MySQL 5.7 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class MySQL57Platform extends MySqlPlatform +{ + /** + * {@inheritdoc} + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + 'ALTER TABLE ' . $tableName . ' RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this) + ); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php new file mode 100644 index 0000000..e22851b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -0,0 +1,1048 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\TextType; + +/** + * The MySqlPlatform provides the behavior, features and SQL dialect of the + * MySQL database platform. This platform represents a MySQL 5.0 or greater platform that + * uses the InnoDB storage engine. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: MySQLPlatform + */ +class MySqlPlatform extends AbstractPlatform +{ + const LENGTH_LIMIT_TINYTEXT = 255; + const LENGTH_LIMIT_TEXT = 65535; + const LENGTH_LIMIT_MEDIUMTEXT = 16777215; + + const LENGTH_LIMIT_TINYBLOB = 255; + const LENGTH_LIMIT_BLOB = 65535; + const LENGTH_LIMIT_MEDIUMBLOB = 16777215; + + /** + * Adds MySQL-specific LIMIT clause to the query + * 18446744073709551615 is 2^64-1 maximum of unsigned BIGINT the biggest limit possible + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if ($limit !== null) { + $query .= ' LIMIT ' . $limit; + if ($offset !== null) { + $query .= ' OFFSET ' . $offset; + } + } elseif ($offset !== null) { + $query .= ' LIMIT 18446744073709551615 OFFSET ' . $offset; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierQuoteCharacter() + { + return '`'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'RLIKE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $substr . ', ' . $str . ')'; + } + + return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return 'CONCAT(' . join(', ', (array) $args) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $function = '+' === $operator ? 'DATE_ADD' : 'DATE_SUB'; + + return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SHOW DATABASES'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + * + * Two approaches to listing the table indexes. The information_schema is + * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + if ($currentDatabase) { + return "SELECT TABLE_NAME AS `Table`, NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, ". + "SEQ_IN_INDEX AS Seq_in_index, COLUMN_NAME AS Column_Name, COLLATION AS Collation, ". + "CARDINALITY AS Cardinality, SUB_PART AS Sub_Part, PACKED AS Packed, " . + "NULLABLE AS `Null`, INDEX_TYPE AS Index_Type, COMMENT AS Comment " . + "FROM information_schema.STATISTICS WHERE TABLE_NAME = '" . $table . "' AND TABLE_SCHEMA = '" . $currentDatabase . "'"; + } + + return 'SHOW INDEX FROM ' . $table; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = '".$database."'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $sql = "SELECT DISTINCT k.`CONSTRAINT_NAME`, k.`COLUMN_NAME`, k.`REFERENCED_TABLE_NAME`, ". + "k.`REFERENCED_COLUMN_NAME` /*!50116 , c.update_rule, c.delete_rule */ ". + "FROM information_schema.key_column_usage k /*!50116 ". + "INNER JOIN information_schema.referential_constraints c ON ". + " c.constraint_name = k.constraint_name AND ". + " c.table_name = '$table' */ WHERE k.table_name = '$table'"; + + if ($database) { + $sql .= " AND k.table_schema = '$database' /*!50116 AND c.constraint_schema = '$database' */"; + } + + $sql .= " AND k.`REFERENCED_COLUMN_NAME` is not NULL"; + + return $sql; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * Gets the SQL snippet used to declare a CLOB column type. + * TINYTEXT : 2 ^ 8 - 1 = 255 + * TEXT : 2 ^ 16 - 1 = 65535 + * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 + * LONGTEXT : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getClobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYTEXT) { + return 'TINYTEXT'; + } + + if ($length <= static::LENGTH_LIMIT_TEXT) { + return 'TEXT'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { + return 'MEDIUMTEXT'; + } + } + + return 'LONGTEXT'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) { + return 'TIMESTAMP'; + } + + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'TINYINT(1)'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration to be used in statements like CREATE TABLE. + * + * @deprecated Deprecated since version 2.5, Use {@link self::getColumnCollationDeclarationSQL()} instead. + * + * @param string $collation name of the collation + * + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a field declaration. + */ + public function getCollationFieldDeclaration($collation) + { + return $this->getColumnCollationDeclarationSQL($collation); + } + + /** + * {@inheritDoc} + * + * MySql prefers "autoincrement" identity columns since sequences can only + * be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * MySql supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsInlineColumnComments() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + public function getListTablesSQL() + { + return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + if ($database) { + $database = "'" . $database . "'"; + } else { + $database = 'DATABASE()'; + } + + return "SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ". + "COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, " . + "CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ". + "FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " . $database . " AND TABLE_NAME = '" . $table . "'"; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $index => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + } + } + + // add all indexes + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); + } + } + + // attach all primary keys + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE '; + + if (!empty($options['temporary'])) { + $query .= 'TEMPORARY '; + } + + $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') '; + $query .= $this->buildTableOptions($options); + $query .= $this->buildPartitionOptions($options); + + $sql[] = $query; + $engine = 'INNODB'; + + if (isset($options['engine'])) { + $engine = strtoupper(trim($options['engine'])); + } + + // Propagate foreign key constraints only for InnoDB. + if (isset($options['foreignKeys']) && $engine === 'INNODB') { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + // Unset the default value if the given field definition does not allow default values. + if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) { + $field['default'] = null; + } + + return parent::getDefaultValueDeclarationSQL($field); + } + + /** + * Build SQL for table options + * + * @param array $options + * + * @return string + */ + private function buildTableOptions(array $options) + { + if (isset($options['table_options'])) { + return $options['table_options']; + } + + $tableOptions = array(); + + // Charset + if ( ! isset($options['charset'])) { + $options['charset'] = 'utf8'; + } + + $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); + + // Collate + if ( ! isset($options['collate'])) { + $options['collate'] = 'utf8_unicode_ci'; + } + + $tableOptions[] = sprintf('COLLATE %s', $options['collate']); + + // Engine + if ( ! isset($options['engine'])) { + $options['engine'] = 'InnoDB'; + } + + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); + + // Auto increment + if (isset($options['auto_increment'])) { + $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); + } + + // Comment + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableOptions[] = sprintf("COMMENT = %s ", $this->quoteStringLiteral($comment)); + } + + // Row format + if (isset($options['row_format'])) { + $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); + } + + return implode(' ', $tableOptions); + } + + /** + * Build SQL for partition options. + * + * @param array $options + * + * @return string + */ + private function buildPartitionOptions(array $options) + { + return (isset($options['partition_options'])) + ? ' ' . $options['partition_options'] + : ''; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $columnSql = array(); + $queryParts = array(); + if ($diff->newName !== false) { + $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP ' . $column->getQuotedName($this); + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + $columnArray = $column->toArray(); + + // Don't propagate default value changes for unsupported column types. + if ($columnDiff->hasChanged('default') && + count($columnDiff->changedProperties) === 1 && + ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) + ) { + continue; + } + + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + $columnArray = $column->toArray(); + $columnArray['comment'] = $this->getColumnComment($column); + $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' + . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); + } + + if (isset($diff->addedIndexes['primary'])) { + $keyColumns = array_unique(array_values($diff->addedIndexes['primary']->getColumns())); + $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; + unset($diff->addedIndexes['primary']); + } + + $sql = array(); + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(", ", $queryParts); + } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->removedIndexes as $remKey => $remIndex) { + // Dropping primary keys requires to unset autoincrement attribute on the particular column first. + if ($remIndex->isPrimary() && $diff->fromTable instanceof Table) { + foreach ($remIndex->getColumns() as $columnName) { + $column = $diff->fromTable->getColumn($columnName); + + if ($column->getAutoincrement() === true) { + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // original autoincrement information might be needed later on by other parts of the table alteration + $column->setAutoincrement(true); + } + } + } + + foreach ($diff->addedIndexes as $addKey => $addIndex) { + if ($remIndex->getColumns() == $addIndex->getColumns()) { + + $indexClause = 'INDEX ' . $addIndex->getName(); + + if ($addIndex->isPrimary()) { + $indexClause = 'PRIMARY KEY'; + } elseif ($addIndex->isUnique()) { + $indexClause = 'UNIQUE INDEX ' . $addIndex->getName(); + } + + $query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', '; + $query .= 'ADD ' . $indexClause; + $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex->getQuotedColumns($this)) . ')'; + + $sql[] = $query; + + unset($diff->removedIndexes[$remKey]); + unset($diff->addedIndexes[$addKey]); + + break; + } + } + } + + $engine = 'INNODB'; + + if ($diff->fromTable instanceof Table && $diff->fromTable->hasOption('engine')) { + $engine = strtoupper(trim($diff->fromTable->getOption('engine'))); + } + + // Suppress foreign key constraint propagation on non-supporting engines. + if ('INNODB' !== $engine) { + $diff->addedForeignKeys = array(); + $diff->changedForeignKeys = array(); + $diff->removedForeignKeys = array(); + } + + $sql = array_merge( + $sql, + $this->getPreAlterTableAlterIndexForeignKeySQL($diff), + parent::getPreAlterTableIndexForeignKeySQL($diff), + $this->getPreAlterTableRenameIndexForeignKeySQL($diff) + ); + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $table = $diff->getName($this)->getQuotedName($this); + + foreach ($diff->changedIndexes as $changedIndex) { + // Changed primary key + if ($changedIndex->isPrimary() && $diff->fromTable instanceof Table) { + foreach ($diff->fromTable->getPrimaryKeyColumns() as $columnName) { + $column = $diff->fromTable->getColumn($columnName); + + // Check if an autoincrement column was dropped from the primary key. + if ($column->getAutoincrement() && ! in_array($columnName, $changedIndex->getColumns())) { + // The autoincrement attribute needs to be removed from the dropped column + // before we can drop and recreate the primary key. + $column->setAutoincrement(false); + + $sql[] = 'ALTER TABLE ' . $table . ' MODIFY ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + // Restore the autoincrement attribute as it might be needed later on + // by other parts of the table alteration. + $column->setAutoincrement(true); + } + } + } + } + + return $sql; + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $tableName = $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (! in_array($foreignKey, $diff->changedForeignKeys, true)) { + $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName); + } + } + + return $sql; + } + + /** + * Returns the remaining foreign key constraints that require one of the renamed indexes. + * + * "Remaining" here refers to the diff between the foreign keys currently defined in the associated + * table and the foreign keys to be removed. + * + * @param TableDiff $diff The table diff to evaluate. + * + * @return array + */ + private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff) + { + if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) { + return array(); + } + + $foreignKeys = array(); + /** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */ + $remainingForeignKeys = array_diff_key( + $diff->fromTable->getForeignKeys(), + $diff->removedForeignKeys + ); + + foreach ($remainingForeignKeys as $foreignKey) { + foreach ($diff->renamedIndexes as $index) { + if ($foreignKey->intersectsIndexColumns($index)) { + $foreignKeys[] = $foreignKey; + + break; + } + } + } + + return $foreignKeys; + } + + /** + * {@inheritdoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + return array_merge( + parent::getPostAlterTableIndexForeignKeySQL($diff), + $this->getPostAlterTableRenameIndexForeignKeySQL($diff) + ); + } + + /** + * @param TableDiff $diff The table diff to gather the SQL for. + * + * @return array + */ + protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + { + $sql = array(); + $tableName = (false !== $diff->newName) + ? $diff->getNewName()->getQuotedName($this) + : $diff->getName($this)->getQuotedName($this); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (! in_array($foreignKey, $diff->changedForeignKeys, true)) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } elseif ($index->hasFlag('fulltext')) { + $type .= 'FULLTEXT '; + } elseif ($index->hasFlag('spatial')) { + $type .= 'SPATIAL '; + } + + return $type; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $autoinc = ''; + if ( ! empty($columnDef['autoincrement'])) { + $autoinc = ' AUTO_INCREMENT'; + } + $unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : ''; + + return $unsigned . $autoinc; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table=null) + { + if ($index instanceof Index) { + $indexName = $index->getQuotedName($this); + } elseif (is_string($index)) { + $indexName = $index; + } else { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('MysqlPlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + if ($index instanceof Index && $index->isPrimary()) { + // mysql primary keys are always named "PRIMARY", + // so we cannot use them in statements because of them being keyword. + return $this->getDropPrimaryKeySQL($table); + } + + return 'DROP INDEX ' . $indexName . ' ON ' . $table; + } + + /** + * @param string $table + * + * @return string + */ + protected function getDropPrimaryKeySQL($table) + { + return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mysql'; + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'LOCK IN SHARE MODE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'bigint' => 'bigint', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'string' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'longblob' => 'blob', + 'blob' => 'blob', + 'mediumblob' => 'blob', + 'tinyblob' => 'blob', + 'binary' => 'binary', + 'varbinary' => 'binary', + 'set' => 'simple_array', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 65535; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords'; + } + + /** + * {@inheritDoc} + * + * MySQL commits a transaction implicitly when DROP TABLE is executed, however not + * if DROP TEMPORARY TABLE is executed. + */ + public function getDropTemporaryTableSQL($table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } elseif (!is_string($table)) { + throw new \InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); + } + + return 'DROP TEMPORARY TABLE ' . $table; + } + + /** + * Gets the SQL Snippet used to declare a BLOB column type. + * TINYBLOB : 2 ^ 8 - 1 = 255 + * BLOB : 2 ^ 16 - 1 = 65535 + * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 + * LONGBLOB : 2 ^ 32 - 1 = 4294967295 + * + * @param array $field + * + * @return string + */ + public function getBlobTypeDeclarationSQL(array $field) + { + if ( ! empty($field['length']) && is_numeric($field['length'])) { + $length = $field['length']; + + if ($length <= static::LENGTH_LIMIT_TINYBLOB) { + return 'TINYBLOB'; + } + + if ($length <= static::LENGTH_LIMIT_BLOB) { + return 'BLOB'; + } + + if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { + return 'MEDIUMBLOB'; + } + } + + return 'LONGBLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php new file mode 100644 index 0000000..a58c747 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -0,0 +1,1123 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Types\BinaryType; + +/** + * OraclePlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + */ +class OraclePlatform extends AbstractPlatform +{ + /** + * Assertion for Oracle identifiers. + * + * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm + * + * @param string $identifier + * + * @throws DBALException + */ + static public function assertValidIdentifier($identifier) + { + if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) { + throw new DBALException("Invalid Oracle identifier"); + } + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return "SUBSTR($value, $position, $length)"; + } + + return "SUBSTR($value, $position)"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'date': + case 'time': + case 'timestamp': + default: + return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'INSTR('.$str.', '.$substr.')'; + } + + return 'INSTR('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'SYS_GUID()'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_MONTH: + case self::DATE_INTERVAL_UNIT_QUARTER: + case self::DATE_INTERVAL_UNIT_YEAR: + switch ($unit) { + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + break; + + case self::DATE_INTERVAL_UNIT_YEAR: + $interval *= 12; + break; + } + + return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')'; + + default: + $calculationClause = ''; + + switch ($unit) { + case self::DATE_INTERVAL_UNIT_SECOND: + $calculationClause = '/24/60/60'; + break; + + case self::DATE_INTERVAL_UNIT_MINUTE: + $calculationClause = '/24/60'; + break; + + case self::DATE_INTERVAL_UNIT_HOUR: + $calculationClause = '/24'; + break; + + case self::DATE_INTERVAL_UNIT_WEEK: + $calculationClause = '*7'; + break; + } + + return '(' . $date . $operator . $interval . $calculationClause . ')'; + } + } + + /** + * {@inheritDoc} + * + * Note: Since Oracle timestamp differences are calculated down to the microsecond we have to truncate + * them to the difference in days. This is obviously a restriction of the original functionality, but we + * need to make this a portable function. + */ + public function getDateDiffExpression($date1, $date2) + { + return "TRUNC(TO_NUMBER(SUBSTR((" . $date1 . "-" . $date2 . "), 1, INSTR(" . $date1 . "-" . $date2 .", ' '))))"; + } + + /** + * {@inheritDoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return 'BITAND('.$value1 . ', ' . $value2 . ')'; + } + + /** + * {@inheritDoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return '(' . $value1 . '-' . + $this->getBitAndComparisonExpression($value1, $value2) + . '+' . $value2 . ')'; + } + + /** + * {@inheritDoc} + * + * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. + * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection + * in {@see listSequences()} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() + . $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @param Sequence $sequence + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() === 0) { + return ' NOCACHE'; + } else if ($sequence->getCache() === 1) { + return ' NOCACHE'; + } else if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 'READ UNCOMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + return 'READ COMMITTED'; + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 'SERIALIZABLE'; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'NUMBER(1)'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'NUMBER(10)'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(20)'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'NUMBER(5)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)') + : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 2000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT username FROM all_users'; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + $database = $this->normalizeIdentifier($database); + + return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ". + "WHERE SEQUENCE_OWNER = '" . $database->getName() . "'"; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($table, array $columns, array $options = array()) + { + $indexes = isset($options['indexes']) ? $options['indexes'] : array(); + $options['indexes'] = array(); + $sql = parent::_getCreateTableSQL($table, $columns, $options); + + foreach ($columns as $name => $column) { + if (isset($column['sequence'])) { + $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1); + } + + if (isset($column['autoincrement']) && $column['autoincrement'] || + (isset($column['autoinc']) && $column['autoinc'])) { + $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table)); + } + } + + if (isset($indexes) && ! empty($indexes)) { + foreach ($indexes as $index) { + $sql[] = $this->getCreateIndexSQL($index, $table); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = $this->normalizeIdentifier($table); + + return "SELECT uind.index_name AS name, " . + " uind.index_type AS type, " . + " decode( uind.uniqueness, 'NONUNIQUE', 0, 'UNIQUE', 1 ) AS is_unique, " . + " uind_col.column_name AS column_name, " . + " uind_col.column_position AS column_pos, " . + " (SELECT ucon.constraint_type FROM user_constraints ucon WHERE ucon.constraint_name = uind.index_name) AS is_primary ". + "FROM user_indexes uind, user_ind_columns uind_col " . + "WHERE uind.index_name = uind_col.index_name AND uind_col.table_name = '" . $table->getName() . "' " . + "ORDER BY uind_col.column_position ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return 'SELECT * FROM sys.user_tables'; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT view_name, text FROM sys.user_views'; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * @param string $name + * @param string $table + * @param integer $start + * + * @return array + */ + public function getCreateAutoincrementSql($name, $table, $start = 1) + { + $tableIdentifier = $this->normalizeIdentifier($table); + $quotedTableName = $tableIdentifier->getQuotedName($this); + $unquotedTableName = $tableIdentifier->getName(); + + $nameIdentifier = $this->normalizeIdentifier($name); + $quotedName = $nameIdentifier->getQuotedName($this); + $unquotedName = $nameIdentifier->getName(); + + $sql = array(); + + $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier); + + $idx = new Index($autoincrementIdentifierName, array($quotedName), true, true); + + $sql[] = 'DECLARE + constraints_Count NUMBER; +BEGIN + SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \'' . $unquotedTableName . '\' AND CONSTRAINT_TYPE = \'P\'; + IF constraints_Count = 0 OR constraints_Count = \'\' THEN + EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\'; + END IF; +END;'; + + $sequenceName = $this->getIdentitySequenceName( + $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, + $nameIdentifier->isQuoted() ? $quotedName : $unquotedName + ); + $sequence = new Sequence($sequenceName, $start); + $sql[] = $this->getCreateSequenceSQL($sequence); + + $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . ' + BEFORE INSERT + ON ' . $quotedTableName . ' + FOR EACH ROW +DECLARE + last_Sequence NUMBER; + last_InsertID NUMBER; +BEGIN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; + IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; + ELSE + SELECT NVL(Last_Number, 0) INTO last_Sequence + FROM User_Sequences + WHERE Sequence_Name = \'' . $sequence->getName() . '\'; + SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL; + WHILE (last_InsertID > last_Sequence) LOOP + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; + END LOOP; + END IF; +END;'; + + return $sql; + } + + /** + * Returns the SQL statements to drop the autoincrement for the given table name. + * + * @param string $table The table name to drop the autoincrement for. + * + * @return array + */ + public function getDropAutoincrementSql($table) + { + $table = $this->normalizeIdentifier($table); + $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); + $identitySequenceName = $this->getIdentitySequenceName( + $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), + '' + ); + + return array( + 'DROP TRIGGER ' . $autoincrementIdentifierName, + $this->getDropSequenceSQL($identitySequenceName), + $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)), + ); + } + + /** + * Normalizes the given identifier. + * + * Uppercases the given identifier if it is not quoted by intention + * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers. + * + * @param string $name The identifier to normalize. + * + * @return Identifier The normalized identifier. + */ + private function normalizeIdentifier($name) + { + $identifier = new Identifier($name); + + return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name)); + } + + /** + * Returns the autoincrement primary key identifier name for the given table identifier. + * + * Quotes the autoincrement primary key identifier name + * if the given table name is quoted by intention. + * + * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for. + * + * @return string + */ + private function getAutoincrementIdentifierName(Identifier $table) + { + $identifierName = $table->getName() . '_AI_PK'; + + return $table->isQuoted() + ? $this->quoteSingleIdentifier($identifierName) + : $identifierName; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table) + { + $table = $table = $this->normalizeIdentifier($table); + + return "SELECT alc.constraint_name, + alc.DELETE_RULE, + alc.search_condition, + cols.column_name \"local_column\", + cols.position, + r_alc.table_name \"references_table\", + r_cols.column_name \"foreign_column\" + FROM user_cons_columns cols +LEFT JOIN user_constraints alc + ON alc.constraint_name = cols.constraint_name +LEFT JOIN user_constraints r_alc + ON alc.r_constraint_name = r_alc.constraint_name +LEFT JOIN user_cons_columns r_cols + ON r_alc.constraint_name = r_cols.constraint_name + AND cols.position = r_cols.position + WHERE alc.constraint_name = cols.constraint_name + AND alc.constraint_type = 'R' + AND alc.table_name = '" . $table->getName() . "' + ORDER BY alc.constraint_name ASC, cols.position ASC"; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = $this->normalizeIdentifier($table); + + return "SELECT * FROM user_constraints WHERE table_name = '" . $table->getName() . "'"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $table = $this->normalizeIdentifier($table); + + $tabColumnsTableName = "user_tab_columns"; + $colCommentsTableName = "user_col_comments"; + $ownerCondition = ''; + + if (null !== $database) { + $database = $this->normalizeIdentifier($database); + $tabColumnsTableName = "all_tab_columns"; + $colCommentsTableName = "all_col_comments"; + $ownerCondition = "AND c.owner = '" . $database->getName() . "'"; + } + + return "SELECT c.*, d.comments FROM $tabColumnsTableName c ". + "INNER JOIN " . $colCommentsTableName . " d ON d.TABLE_NAME = c.TABLE_NAME AND d.COLUMN_NAME = c.COLUMN_NAME ". + "WHERE c.table_name = '" . $table->getName() . "' ".$ownerCondition." ORDER BY c.column_name"; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritdoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $referentialAction = null; + + if ($foreignKey->hasOption('onDelete')) { + $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + + return $referentialAction ? ' ON DELETE ' . $referentialAction : ''; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + $action = strtoupper($action); + + switch ($action) { + case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION. + case 'NO ACTION': + // NO ACTION cannot be declared explicitly, + // therefore returning empty string to indicate to OMIT the referential clause. + return ''; + + case 'CASCADE': + case 'SET NULL': + return $action; + + default: + // SET DEFAULT is not supported, throw exception instead. + throw new \InvalidArgumentException('Invalid foreign key action: ' . $action); + } + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($database) + { + return 'DROP USER ' . $database . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + $fields = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + if ($comment = $this->getColumnComment($column)) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')'; + } + + $fields = array(); + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + $column = $columnDiff->column; + + // Do not generate column alteration clause if type is binary and only fixed property has changed. + // Oracle only supports binary type columns with variable length. + // Avoids unnecessary table alteration statements. + if ($column->getType() instanceof BinaryType && + $columnDiff->hasChanged('fixed') && + count($columnDiff->changedProperties) === 1 + ) { + continue; + } + + $columnHasChangedComment = $columnDiff->hasChanged('comment'); + + /** + * Do not add query part if only comment has changed + */ + if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { + $columnInfo = $column->toArray(); + + if ( ! $columnDiff->hasChanged('notnull')) { + unset($columnInfo['notnull']); + } + + $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo); + } + + if ($columnHasChangedComment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')'; + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this); + } + + $fields = array(); + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $fields[] = $column->getQuotedName($this); + } + + if (count($fields)) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')'; + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritdoc} + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $default = $this->getDefaultValueDeclarationSQL($field); + + $notnull = ''; + + if (isset($field['notnull'])) { + $notnull = $field['notnull'] ? ' NOT NULL' : ' NULL'; + } + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSqlDeclaration($field, $this); + $columnDef = $typeDecl . $default . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + $table = new Identifier($tableName); + + // No usage of column name to preserve BC compatibility with <2.5 + $identitySequenceName = $table->getName() . '_SEQ'; + + if ($table->isQuoted()) { + $identitySequenceName = '"' . $identitySequenceName . '"'; + } + + $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName); + + return $identitySequenceIdentifier->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'oracle'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + $limit = (int) $limit; + $offset = (int) $offset; + + if (preg_match('/^\s*SELECT/i', $query)) { + if (!preg_match('/\sFROM\s/i', $query)) { + $query .= " FROM dual"; + } + if ($limit > 0) { + $max = $offset + $limit; + $column = '*'; + if ($offset > 0) { + $min = $offset + 1; + $query = 'SELECT * FROM (SELECT a.' . $column . ', rownum AS doctrine_rownum FROM (' . + $query . + ') a WHERE rownum <= ' . $max . ') WHERE doctrine_rownum >= ' . $min; + } else { + $query = 'SELECT a.' . $column . ' FROM (' . $query . ') a WHERE ROWNUM <= ' . $max; + } + } + } + + return $query; + } + + /** + * {@inheritDoc} + * + * Oracle returns all column names in SQL result sets in uppercase. + */ + public function getSQLResultCasing($column) + { + return strtoupper($column); + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE GLOBAL TEMPORARY TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sP'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d 00:00:00'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return '1900-01-01 H:i:s'; + } + + /** + * {@inheritDoc} + */ + public function fixSchemaElementName($schemaElementName) + { + if (strlen($schemaElementName) > 30) { + // Trim it + return substr($schemaElementName, 0, 30); + } + + return $schemaElementName; + } + + /** + * {@inheritDoc} + */ + public function getMaxIdentifierLength() + { + return 30; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyOnUpdate() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * {@inheritDoc} + */ + public function getDummySelectSQL() + { + return 'SELECT 1 FROM DUAL'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'integer' => 'integer', + 'number' => 'integer', + 'pls_integer' => 'boolean', + 'binary_integer' => 'boolean', + 'varchar' => 'string', + 'varchar2' => 'string', + 'nvarchar2' => 'string', + 'char' => 'string', + 'nchar' => 'string', + 'date' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'float' => 'float', + 'binary_float' => 'float', + 'binary_double' => 'float', + 'long' => 'string', + 'clob' => 'text', + 'nclob' => 'text', + 'raw' => 'binary', + 'long raw' => 'blob', + 'rowid' => 'string', + 'urowid' => 'string', + 'blob' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php new file mode 100644 index 0000000..b14aa73 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.1 database platform. + * + * @author Martin Hasoň + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL91Platform extends PostgreSqlPlatform +{ + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords'; + } + + /** + * {@inheritDoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $this->quoteSingleIdentifier($collation); + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $sql = parent::getListTableColumnsSQL($table, $database); + $parts = explode('AS complete_type,', $sql, 2); + + return $parts[0].'AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,'.$parts[1]; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php new file mode 100644 index 0000000..19cf803 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.2 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class PostgreSQL92Platform extends PostgreSQL91Platform +{ + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return 'JSON'; + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SMALLSERIAL'; + } + + return parent::getSmallIntTypeDeclarationSQL($field); + } + + /** + * {@inheritdoc} + */ + public function hasNativeJsonType() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords'; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['json'] = 'json_array'; + } + + /** + * {@inheritdoc} + */ + public function getCloseActiveDatabaseConnectionsSQL($database) + { + return "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database'"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php new file mode 100644 index 0000000..681eb59 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -0,0 +1,1168 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\BinaryType; +use Doctrine\DBAL\Types\BlobType; + +/** + * PostgreSqlPlatform. + * + * @since 2.0 + * @author Roman Borschel + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @todo Rename: PostgreSQLPlatform + */ +class PostgreSqlPlatform extends AbstractPlatform +{ + /** + * @var bool + */ + private $useBooleanTrueFalseStrings = true; + + /** + * @var array PostgreSQL booleans literals + */ + private $booleanLiterals = array( + 'true' => array( + 't', + 'true', + 'y', + 'yes', + 'on', + '1' + ), + 'false' => array( + 'f', + 'false', + 'n', + 'no', + 'off', + '0' + ) + ); + + /** + * PostgreSQL has different behavior with some drivers + * with regard to how booleans have to be handled. + * + * Enables use of 'true'/'false' or otherwise 1 and 0 instead. + * + * @param bool $flag + */ + public function setUseBooleanTrueFalseStrings($flag) + { + $this->useBooleanTrueFalseStrings = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if ($length === null) { + return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')'; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression() + { + return 'LOCALTIMESTAMP(0)'; + } + + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'SIMILAR TO'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos !== false) { + $str = $this->getSubstringExpression($str, $startPos); + + return 'CASE WHEN (POSITION('.$substr.' IN '.$str.') = 0) THEN 0 ELSE (POSITION('.$substr.' IN '.$str.') + '.($startPos-1).') END'; + } + + return 'POSITION('.$substr.' IN '.$str.')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + if (self::DATE_INTERVAL_UNIT_QUARTER === $unit) { + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + } + + return "(" . $date ." " . $operator . " (" . $interval . " || ' " . $unit . "')::interval)"; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; + } + + /** + * {@inheritDoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'public'; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsPartialIndexes() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function usesSequenceEmulatedIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getIdentitySequenceName($tableName, $columnName) + { + return $tableName . '_' . $columnName . '_seq'; + } + + /** + * {@inheritDoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT datname FROM pg_database'; + } + + /** + * {@inheritDoc} + */ + public function getListNamespacesSQL() + { + return "SELECT schema_name AS nspname + FROM information_schema.schemata + WHERE schema_name NOT LIKE 'pg_%' + AND schema_name != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListSequencesSQL($database) + { + return "SELECT sequence_name AS relname, + sequence_schema AS schemaname + FROM information_schema.sequences + WHERE sequence_schema NOT LIKE 'pg_%' + AND sequence_schema != 'information_schema'"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT quote_ident(table_name) AS table_name, + table_schema AS schema_name + FROM information_schema.tables + WHERE table_schema NOT LIKE 'pg_%' + AND table_schema != 'information_schema' + AND table_name != 'geometry_columns' + AND table_name != 'spatial_ref_sys' + AND table_type != 'VIEW'"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return 'SELECT quote_ident(table_name) AS viewname, + table_schema AS schemaname, + view_definition AS definition + FROM information_schema.views + WHERE view_definition IS NOT NULL'; + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef + FROM pg_catalog.pg_constraint r + WHERE r.conrelid = + ( + SELECT c.oid + FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE " .$this->getTableWhereClause($table) ." AND n.oid = c.relnamespace + ) + AND r.contype = 'f'"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = new Identifier($table); + $table = $table->getName(); + + return "SELECT + quote_ident(relname) as relname + FROM + pg_class + WHERE oid IN ( + SELECT indexrelid + FROM pg_index, pg_class + WHERE pg_class.relname = '$table' + AND pg_class.oid = pg_index.indrelid + AND (indisunique = 't' OR indisprimary = 't') + )"; + } + + /** + * {@inheritDoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, + pg_index.indkey, pg_index.indrelid, + pg_get_expr(indpred, indrelid) AS where + FROM pg_class, pg_index + WHERE oid IN ( + SELECT indexrelid + FROM pg_index si, pg_class sc, pg_namespace sn + WHERE " . $this->getTableWhereClause($table, 'sc', 'sn')." AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid + ) AND pg_index.indexrelid = oid"; + } + + /** + * @param string $table + * @param string $classAlias + * @param string $namespaceAlias + * + * @return string + */ + private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n') + { + $whereClause = $namespaceAlias.".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = "'" . $schema . "'"; + } else { + $schema = "ANY(string_to_array((select replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))"; + } + + $table = new Identifier($table); + $whereClause .= "$classAlias.relname = '" . $table->getName() . "' AND $namespaceAlias.nspname = $schema"; + + return $whereClause; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT + a.attnum, + quote_ident(a.attname) AS field, + t.typname AS type, + format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, + (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM + pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, + a.attnotnull AS isnotnull, + (SELECT 't' + FROM pg_index + WHERE c.oid = pg_index.indrelid + AND pg_index.indkey[0] = a.attnum + AND pg_index.indisprimary = 't' + ) AS pri, + (SELECT pg_get_expr(adbin, adrelid) + FROM pg_attrdef + WHERE c.oid = pg_attrdef.adrelid + AND pg_attrdef.adnum=a.attnum + ) AS default, + (SELECT pg_description.description + FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid + ) AS comment + FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n + WHERE ".$this->getTableWhereClause($table, 'c', 'n') ." + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + ORDER BY a.attnum"; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * Returns the SQL statement for disallowing new connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to disallow new connections for. + * + * @return string + */ + public function getDisallowDatabaseConnectionsSQL($database) + { + return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = '$database'"; + } + + /** + * Returns the SQL statement for closing currently active connections on the given database. + * + * This is useful to force DROP DATABASE operations which could fail because of active connections. + * + * @param string $database The name of the database to close currently active connections for. + * + * @return string + */ + public function getCloseActiveDatabaseConnectionsSQL($database) + { + return "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = '$database'"; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query .= ' MATCH ' . $foreignKey->getOption('match'); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { + $query .= ' DEFERRABLE'; + } else { + $query .= ' NOT DEFERRABLE'; + } + + if (($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) + || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + ) { + $query .= ' INITIALLY DEFERRED'; + } else { + $query .= ' INITIALLY IMMEDIATE'; + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = 'DROP ' . $column->getQuotedName($this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if ($this->isUnchangedBinaryColumn($columnDiff)) { + continue; + } + + $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('precision') || $columnDiff->hasChanged('scale') || $columnDiff->hasChanged('fixed')) { + $type = $column->getType(); + + // here was a server version check before, but DBAL API does not support this anymore. + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('default') || $columnDiff->hasChanged('type')) { + $defaultClause = null === $column->getDefault() + ? ' DROP DEFAULT' + : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); + $query = 'ALTER ' . $oldColumnName . $defaultClause; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'SET' : 'DROP') . ' NOT NULL'; + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + if ($columnDiff->hasChanged('autoincrement')) { + if ($column->getAutoincrement()) { + // add autoincrement + $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); + + $sql[] = "CREATE SEQUENCE " . $seqName; + $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->getName($this)->getQuotedName($this) . "))"; + $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $query; + } else { + // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have + $query = "ALTER " . $oldColumnName . " " . "DROP DEFAULT"; + $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $query; + } + } + + if ($columnDiff->hasChanged('comment')) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + + if ($columnDiff->hasChanged('length')) { + $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $column->getType()->getSqlDeclaration($column->toArray(), $this); + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . + ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); + } + + $tableSql = array(); + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Checks whether a given column diff is a logically unchanged binary type column. + * + * Used to determine whether a column alteration for a binary type column can be skipped. + * Doctrine's {@link \Doctrine\DBAL\Types\BinaryType} and {@link \Doctrine\DBAL\Types\BlobType} + * are mapped to the same database column type on this platform as this platform + * does not have a native VARBINARY/BINARY column type. Therefore the {@link \Doctrine\DBAL\Schema\Comparator} + * might detect differences for binary type columns which do not have to be propagated + * to database as there actually is no difference at database level. + * + * @param ColumnDiff $columnDiff The column diff to check against. + * + * @return boolean True if the given column diff is an unchanged binary type column, false otherwise. + */ + private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) + { + $columnType = $columnDiff->column->getType(); + + if ( ! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { + return false; + } + + $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null; + + if ($fromColumn) { + $fromColumnType = $fromColumn->getType(); + + if ( ! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, array('type', 'length', 'fixed'))) === 0; + } + + if ($columnDiff->hasChanged('type')) { + return false; + } + + return count(array_diff($columnDiff->changedProperties, array('length', 'fixed'))) === 0; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + if (strpos($tableName, '.') !== false) { + list($schema) = explode('.', $tableName); + $oldIndexName = $schema . '.' . $oldIndexName; + } + + return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)); + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) . + " IS $comment"; + } + + /** + * {@inheritDoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue() . + ' START ' . $sequence->getInitialValue() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * {@inheritDoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + $this->getSequenceCacheSQL($sequence); + } + + /** + * Cache definition for sequences + * + * @param Sequence $sequence + * + * @return string + */ + private function getSequenceCacheSQL(Sequence $sequence) + { + if ($sequence->getCache() > 1) { + return ' CACHE ' . $sequence->getCache(); + } + + return ''; + } + + /** + * {@inheritDoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + return $this->getDropConstraintSQL($foreignKey, $table); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return $sql; + } + + /** + * Converts a single boolean value. + * + * First converts the value to its native PHP boolean type + * and passes it to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $value The value to convert. + * @param callable $callback The callback function to use for converting the real boolean value. + * + * @return mixed + * @throws \UnexpectedValueException + */ + private function convertSingleBooleanValue($value, $callback) + { + if (null === $value) { + return $callback(null); + } + + if (is_bool($value) || is_numeric($value)) { + return $callback($value ? true : false); + } + + if (!is_string($value)) { + return $callback(true); + } + + /** + * Better safe than sorry: http://php.net/in_array#106319 + */ + if (in_array(trim(strtolower($value)), $this->booleanLiterals['false'], true)) { + return $callback(false); + } + + if (in_array(trim(strtolower($value)), $this->booleanLiterals['true'], true)) { + return $callback(true); + } + + throw new \UnexpectedValueException("Unrecognized boolean literal '${value}'"); + } + + /** + * Converts one or multiple boolean values. + * + * First converts the value(s) to their native PHP boolean type + * and passes them to the given callback function to be reconverted + * into any custom representation. + * + * @param mixed $item The value(s) to convert. + * @param callable $callback The callback function to use for converting the real boolean value(s). + * + * @return mixed + */ + private function doConvertBooleans($item, $callback) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + $item[$key] = $this->convertSingleBooleanValue($value, $callback); + } + + return $item; + } + + return $this->convertSingleBooleanValue($item, $callback); + } + + /** + * {@inheritDoc} + * + * Postgres wants boolean values converted to the strings 'true'/'false'. + */ + public function convertBooleans($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleans($item); + } + + return $this->doConvertBooleans( + $item, + function ($boolean) { + if (null === $boolean) { + return 'NULL'; + } + + return true === $boolean ? 'true' : 'false'; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertBooleansToDatabaseValue($item) + { + if ( ! $this->useBooleanTrueFalseStrings) { + return parent::convertBooleansToDatabaseValue($item); + } + + return $this->doConvertBooleans( + $item, + function ($boolean) { + return null === $boolean ? null : (int) $boolean; + } + ); + } + + /** + * {@inheritDoc} + */ + public function convertFromBoolean($item) + { + if (in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + return false; + } + + return parent::convertFromBoolean($item); + } + + /** + * {@inheritDoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return "SELECT NEXTVAL('" . $sequenceName . "')"; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' + . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SERIAL'; + } + + return 'INT'; + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'BIGSERIAL'; + } + + return 'BIGINT'; + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT'; + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UUID'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP(0) WITH TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0) WITHOUT TIME ZONE'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'UUID_GENERATE_V4()'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BYTEA'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'postgresql'; + } + + /** + * {@inheritDoc} + * + * PostgreSQL returns all column names in SQL result sets in lowercase. + */ + public function getSQLResultCasing($column) + { + return strtolower($column); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:sO'; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE '.$tableName.' '.(($cascade)?'CASCADE':''); + } + + /** + * {@inheritDoc} + */ + public function getReadLockSQL() + { + return 'FOR SHARE'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'int2' => 'smallint', + 'serial' => 'integer', + 'serial4' => 'integer', + 'int' => 'integer', + 'int4' => 'integer', + 'integer' => 'integer', + 'bigserial' => 'bigint', + 'serial8' => 'bigint', + 'bigint' => 'bigint', + 'int8' => 'bigint', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'text' => 'text', + 'varchar' => 'string', + 'interval' => 'string', + '_varchar' => 'string', + 'char' => 'string', + 'bpchar' => 'string', + 'inet' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'time' => 'time', + 'timetz' => 'time', + 'float' => 'float', + 'float4' => 'float', + 'float8' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'money' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'uuid' => 'guid', + 'bytea' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + public function getVarcharMaxLength() + { + return 65535; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords'; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BYTEA'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php new file mode 100644 index 0000000..44ba707 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * The SQLAnywhere11Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 11 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere11Platform extends SQLAnywherePlatform +{ + /** + * {@inheritdoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php new file mode 100644 index 0000000..17b413e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere12Platform.php @@ -0,0 +1,135 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; + +/** + * The SQLAnywhere12Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 12 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere12Platform extends SQLAnywhere11Platform +{ + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.uP'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP WITH TIME ZONE'; + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.NEXTVAL'; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + if ( ! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) { + return ' WITH NULLS NOT DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); + } + + return parent::getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['timestamp with time zone'] = 'datetime'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php new file mode 100644 index 0000000..6808d73 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\DBAL\Schema\Index; + +/** + * The SQLAnywhere16Platform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 16 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhere16Platform extends SQLAnywhere12Platform +{ + /** + * {@inheritdoc} + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + if ($index->hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) { + throw new UnexpectedValueException( + 'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.' + ); + } + + if ( ! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) { + return ' WITH NULLS DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); + } + + return parent::getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php new file mode 100644 index 0000000..36debc3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -0,0 +1,1480 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Constraint; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; + +/** + * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the + * SAP Sybase SQL Anywhere 10 database platform. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywherePlatform extends AbstractPlatform +{ + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_SIMPLE = 1; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_FULL = 2; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129; + /** + * @var integer + */ + const FOREIGN_KEY_MATCH_FULL_UNIQUE = 130; + + /** + * {@inheritdoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch (true) { + case $lockMode === LockMode::NONE: + return $fromClause . ' WITH (NOLOCK)'; + + case $lockMode === LockMode::PESSIMISTIC_READ: + return $fromClause . ' WITH (UPDLOCK)'; + + case $lockMode === LockMode::PESSIMISTIC_WRITE: + return $fromClause . ' WITH (XLOCK)'; + + default: + return $fromClause; + } + } + + /** + * {@inheritdoc} + * + * SQL Anywhere supports a maximum length of 128 bytes for identifiers. + */ + public function fixSchemaElementName($schemaElementName) + { + $maxIdentifierLength = $this->getMaxIdentifierLength(); + + if (strlen($schemaElementName) > $maxIdentifierLength) { + return substr($schemaElementName, 0, $maxIdentifierLength); + } + + return $schemaElementName; + } + + /** + * {@inheritdoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = ''; + + if ($foreignKey->hasOption('match')) { + $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match')); + } + + $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if ($foreignKey->hasOption('check_on_commit') && (boolean) $foreignKey->getOption('check_on_commit')) { + $query .= ' CHECK ON COMMIT'; + } + + if ($foreignKey->hasOption('clustered') && (boolean) $foreignKey->getOption('clustered')) { + $query .= ' CLUSTERED'; + } + + if ($foreignKey->hasOption('for_olap_workload') && (boolean) $foreignKey->getOption('for_olap_workload')) { + $query .= ' FOR OLAP WORKLOAD'; + } + + return $query; + } + + /** + * {@inheritdoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $columnSql = array(); + $commentsSQL = array(); + $tableSql = array(); + $alterClauses = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $alterClauses[] = $this->getAlterTableAddColumnClause($column); + + $comment = $this->getColumnComment($column); + + if (null !== $comment && '' !== $comment) { + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $comment + ); + } + } + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $alterClauses[] = $this->getAlterTableRemoveColumnClause($column); + } + + /** @var \Doctrine\DBAL\Schema\ColumnDiff $columnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $alterClause = $this->getAlterTableChangeColumnClause($columnDiff); + + if (null !== $alterClause) { + $alterClauses[] = $alterClause; + } + + if ($columnDiff->hasChanged('comment')) { + $column = $columnDiff->column; + + $commentsSQL[] = $this->getCommentOnColumnSQL( + $diff->getName($this)->getQuotedName($this), + $column->getQuotedName($this), + $this->getColumnComment($column) + ); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . + $this->getAlterTableRenameColumnClause($oldColumnName, $column); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ( ! empty($alterClauses)) { + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(", ", $alterClauses); + } + + $sql = array_merge($sql, $commentsSQL); + + if ($diff->newName !== false) { + $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . + $this->getAlterTableRenameTableClause($diff->getNewName()); + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for creating a column in a table alteration. + * + * @param Column $column The column to add. + * + * @return string + */ + protected function getAlterTableAddColumnClause(Column $column) + { + return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + } + + /** + * Returns the SQL clause for altering a table. + * + * @param Identifier $tableName The quoted name of the table to alter. + * + * @return string + */ + protected function getAlterTableClause(Identifier $tableName) + { + return 'ALTER TABLE ' . $tableName->getQuotedName($this); + } + + /** + * Returns the SQL clause for dropping a column in a table alteration. + * + * @param Column $column The column to drop. + * + * @return string + */ + protected function getAlterTableRemoveColumnClause(Column $column) + { + return 'DROP ' . $column->getQuotedName($this); + } + + /** + * Returns the SQL clause for renaming a column in a table alteration. + * + * @param string $oldColumnName The quoted name of the column to rename. + * @param Column $column The column to rename to. + * + * @return string + */ + protected function getAlterTableRenameColumnClause($oldColumnName, Column $column) + { + $oldColumnName = new Identifier($oldColumnName); + + return 'RENAME ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this); + } + + /** + * Returns the SQL clause for renaming a table in a table alteration. + * + * @param Identifier $newTableName The quoted name of the table to rename to. + * + * @return string + */ + protected function getAlterTableRenameTableClause(Identifier $newTableName) + { + return 'RENAME ' . $newTableName->getQuotedName($this); + } + + /** + * Returns the SQL clause for altering a column in a table alteration. + * + * This method returns null in case that only the column comment has changed. + * Changes in column comments have to be handled differently. + * + * @param ColumnDiff $columnDiff The diff of the column to alter. + * + * @return string|null + */ + protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff) + { + $column = $columnDiff->column; + + // Do not return alter clause if only comment has changed. + if ( ! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) { + $columnAlterationClause = 'ALTER ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + + if ($columnDiff->hasChanged('default') && null === $column->getDefault()) { + $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT'; + } + + return $columnAlterationClause; + } + } + + /** + * {@inheritdoc} + */ + public function getBigIntTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'BIGINT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 32767; + } + + /** + * {@inheritdoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'LONG BINARY'; + } + + /** + * {@inheritdoc} + * + * BIT type columns require an explicit NULL declaration + * in SQL Anywhere if they shall be nullable. + * Otherwise by just omitting the NOT NULL clause, + * SQL Anywhere will declare them NOT NULL nonetheless. + */ + public function getBooleanTypeDeclarationSQL(array $columnDef) + { + $nullClause = isset($columnDef['notnull']) && (boolean) $columnDef['notnull'] === false ? ' NULL' : ''; + + return 'BIT' . $nullClause; + } + + /** + * {@inheritdoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'TEXT'; + } + + /** + * {@inheritdoc} + */ + public function getCommentOnColumnSQL($tableName, $columnName, $comment) + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); + $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); + + return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . '.' . $columnName->getQuotedName($this) . + " IS $comment"; + } + + /** + * {@inheritdoc} + */ + public function getConcatExpression() + { + return 'STRING(' . implode(', ', (array) func_get_args()) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + if ($constraint instanceof ForeignKeyConstraint) { + return $this->getCreateForeignKeySQL($constraint, $table); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . + ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this)); + } + + /** + * {@inheritdoc} + */ + public function getCreateDatabaseSQL($database) + { + $database = new Identifier($database); + + return "CREATE DATABASE '" . $database->getName() . "'"; + } + + /** + * {@inheritdoc} + * + * Appends SQL Anywhere specific flags if given. + */ + public function getCreateIndexSQL(Index $index, $table) + { + return parent::getCreateIndexSQL($index, $table). $this->getAdvancedIndexOptionsSQL($index); + } + + /** + * {@inheritdoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index); + } + + /** + * {@inheritdoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE'; + } + + /** + * {@inheritdoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritdoc} + */ + public function getCurrentDateSQL() + { + return 'CURRENT DATE'; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimeSQL() + { + return 'CURRENT TIME'; + } + + /** + * {@inheritdoc} + */ + public function getCurrentTimestampSQL() + { + return 'CURRENT TIMESTAMP'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ('-' === $operator) { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritdoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritdoc} + */ + public function getDefaultTransactionIsolationLevel() + { + return Connection::TRANSACTION_READ_UNCOMMITTED; + } + + /** + * {@inheritdoc} + */ + public function getDropDatabaseSQL($database) + { + $database = new Identifier($database); + + return "DROP DATABASE '" . $database->getName() . "'"; + } + + /** + * {@inheritdoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } + + if ( ! is_string($index)) { + throw new \InvalidArgumentException( + 'SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be string or ' . + '\Doctrine\DBAL\Schema\Index.' + ); + } + + if ( ! isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + if ( ! is_string($table)) { + throw new \InvalidArgumentException( + 'SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be string or ' . + '\Doctrine\DBAL\Schema\Table.' + ); + } + + return 'DROP INDEX ' . $table . '.' . $index; + } + + /** + * {@inheritdoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + $sql = ''; + $foreignKeyName = $foreignKey->getName(); + $localColumns = $foreignKey->getQuotedLocalColumns($this); + $foreignColumns = $foreignKey->getQuotedForeignColumns($this); + $foreignTableName = $foreignKey->getQuotedForeignTableName($this); + + if ( ! empty($foreignKeyName)) { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } + + if (empty($localColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'local' required."); + } + + if (empty($foreignColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'foreign' required."); + } + + if (empty($foreignTableName)) { + throw new \InvalidArgumentException("Incomplete definition. 'foreignTable' required."); + } + + if ($foreignKey->hasOption('notnull') && (boolean) $foreignKey->getOption('notnull')) { + $sql .= 'NOT NULL '; + } + + return $sql . + 'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' . + 'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) . + ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')'; + } + + /** + * Returns foreign key MATCH clause for given type. + * + * @param integer $type The foreign key match type + * + * @return string + * + * @throws \InvalidArgumentException if unknown match type given + */ + public function getForeignKeyMatchClauseSQL($type) + { + switch ((int) $type) { + case self::FOREIGN_KEY_MATCH_SIMPLE: + return 'SIMPLE'; + break; + case self::FOREIGN_KEY_MATCH_FULL: + return 'FULL'; + break; + case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE: + return 'UNIQUE SIMPLE'; + break; + case self::FOREIGN_KEY_MATCH_FULL_UNIQUE: + return 'UNIQUE FULL'; + default: + throw new \InvalidArgumentException('Invalid foreign key match type: ' . $type); + } + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // NO ACTION is not supported, therefore falling back to RESTRICT. + if (strtoupper($action) === 'NO ACTION') { + return 'RESTRICT'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + /** + * {@inheritdoc} + */ + public function getForUpdateSQL() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritdoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritdoc} + */ + public function getIndexDeclarationSQL($name, Index $index) + { + // Index declaration in statements like CREATE TABLE is not supported. + throw DBALException::notSupported(__METHOD__); + } + + /** + * {@inheritdoc} + */ + public function getIntegerTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'INT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * {@inheritdoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT db_name(number) AS name FROM sa_db_list()'; + } + + /** + * {@inheritdoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $user = 'USER_NAME()'; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT col.column_name, + COALESCE(def.user_type_name, def.domain_name) AS 'type', + def.declared_width AS 'length', + def.scale, + CHARINDEX('unsigned', def.domain_name) AS 'unsigned', + IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull', + col.\"default\", + def.is_autoincrement AS 'autoincrement', + rem.remarks AS 'comment' + FROM sa_describe_query('SELECT * FROM \"$table\"') AS def + JOIN SYS.SYSTABCOL AS col + ON col.table_id = def.base_table_id AND col.column_id = def.base_column_id + LEFT JOIN SYS.SYSREMARK AS rem + ON col.object_id = rem.object_id + WHERE def.base_owner_name = $user + ORDER BY def.base_column_id ASC"; + } + + /** + * {@inheritdoc} + * + * @todo Where is this used? Which information should be retrieved? + */ + public function getListTableConstraintsSQL($table) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT con.* + FROM SYS.SYSCONSTRAINT AS con + JOIN SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id + WHERE tab.table_name = '$table' + AND tab.creator = USER_ID($user)"; + } + + /** + * {@inheritdoc} + */ + public function getListTableForeignKeysSQL($table) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT fcol.column_name AS local_column, + ptbl.table_name AS foreign_table, + pcol.column_name AS foreign_column, + idx.index_name, + IF fk.nulls = 'N' + THEN 1 + ELSE NULL + ENDIF AS notnull, + CASE ut.referential_action + WHEN 'C' THEN 'CASCADE' + WHEN 'D' THEN 'SET DEFAULT' + WHEN 'N' THEN 'SET NULL' + WHEN 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_update, + CASE dt.referential_action + WHEN 'C' THEN 'CASCADE' + WHEN 'D' THEN 'SET DEFAULT' + WHEN 'N' THEN 'SET NULL' + WHEN 'R' THEN 'RESTRICT' + ELSE NULL + END AS on_delete, + IF fk.check_on_commit = 'Y' + THEN 1 + ELSE NULL + ENDIF AS check_on_commit, -- check_on_commit flag + IF ftbl.clustered_index_id = idx.index_id + THEN 1 + ELSE NULL + ENDIF AS 'clustered', -- clustered flag + IF fk.match_type = 0 + THEN NULL + ELSE fk.match_type + ENDIF AS 'match', -- match option + IF pidx.max_key_distance = 1 + THEN 1 + ELSE NULL + ENDIF AS for_olap_workload -- for_olap_workload flag + FROM SYS.SYSFKEY AS fk + JOIN SYS.SYSIDX AS idx + ON fk.foreign_table_id = idx.table_id + AND fk.foreign_index_id = idx.index_id + JOIN SYS.SYSPHYSIDX pidx + ON idx.table_id = pidx.table_id + AND idx.phys_index_id = pidx.phys_index_id + JOIN SYS.SYSTAB AS ptbl + ON fk.primary_table_id = ptbl.table_id + JOIN SYS.SYSTAB AS ftbl + ON fk.foreign_table_id = ftbl.table_id + JOIN SYS.SYSIDXCOL AS idxcol + ON idx.table_id = idxcol.table_id + AND idx.index_id = idxcol.index_id + JOIN SYS.SYSTABCOL AS pcol + ON ptbl.table_id = pcol.table_id + AND idxcol.primary_column_id = pcol.column_id + JOIN SYS.SYSTABCOL AS fcol + ON ftbl.table_id = fcol.table_id + AND idxcol.column_id = fcol.column_id + LEFT JOIN SYS.SYSTRIGGER ut + ON fk.foreign_table_id = ut.foreign_table_id + AND fk.foreign_index_id = ut.foreign_key_id + AND ut.event = 'C' + LEFT JOIN SYS.SYSTRIGGER dt + ON fk.foreign_table_id = dt.foreign_table_id + AND fk.foreign_index_id = dt.foreign_key_id + AND dt.event = 'D' + WHERE ftbl.table_name = '$table' + AND ftbl.creator = USER_ID($user) + ORDER BY fk.foreign_index_id ASC, idxcol.sequence ASC"; + } + + /** + * {@inheritdoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $user = ''; + + if (strpos($table, '.') !== false) { + list($user, $table) = explode('.', $table); + $user = "'" . $user . "'"; + } + + return "SELECT idx.index_name AS key_name, + IF idx.index_category = 1 + THEN 1 + ELSE 0 + ENDIF AS 'primary', + col.column_name, + IF idx.\"unique\" IN(1, 2, 5) + THEN 0 + ELSE 1 + ENDIF AS non_unique, + IF tbl.clustered_index_id = idx.index_id + THEN 1 + ELSE NULL + ENDIF AS 'clustered', -- clustered flag + IF idx.\"unique\" = 5 + THEN 1 + ELSE NULL + ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag + IF pidx.max_key_distance = 1 + THEN 1 + ELSE NULL + ENDIF AS for_olap_workload -- for_olap_workload flag + FROM SYS.SYSIDX AS idx + JOIN SYS.SYSPHYSIDX pidx + ON idx.table_id = pidx.table_id + AND idx.phys_index_id = pidx.phys_index_id + JOIN SYS.SYSIDXCOL AS idxcol + ON idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id + JOIN SYS.SYSTABCOL AS col + ON idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id + JOIN SYS.SYSTAB AS tbl + ON idx.table_id = tbl.table_id + WHERE tbl.table_name = '$table' + AND tbl.creator = USER_ID($user) + AND idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints + ORDER BY idx.index_id ASC, idxcol.sequence ASC"; + } + + /** + * {@inheritdoc} + */ + public function getListTablesSQL() + { + return "SELECT tbl.table_name + FROM SYS.SYSTAB AS tbl + JOIN SYS.SYSUSER AS usr ON tbl.creator = usr.user_id + JOIN dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id + WHERE tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP' + AND usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users + AND obj.type = 'U' -- user created tables only + ORDER BY tbl.table_name ASC"; + } + + /** + * {@inheritdoc} + * + * @todo Where is this used? Which information should be retrieved? + */ + public function getListUsersSQL() + { + return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC'; + } + + /** + * {@inheritdoc} + */ + public function getListViewsSQL($database) + { + return "SELECT tbl.table_name, v.view_def + FROM SYS.SYSVIEW v + JOIN SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id + JOIN SYS.SYSUSER usr ON tbl.creator = usr.user_id + JOIN dbo.SYSOBJECTS obj ON tbl.object_id = obj.id + WHERE usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users + ORDER BY tbl.table_name ASC"; + } + + /** + * {@inheritdoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE(' . $str . ', ' . $substr . ')'; + } + + return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')'; + } + + /** + * {@inheritdoc} + */ + public function getMaxIdentifierLength() + { + return 128; + } + + /** + * {@inheritdoc} + */ + public function getMd5Expression($column) + { + return "HASH(" . $column . ", 'MD5')"; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'sqlanywhere'; + } + + /** + * Obtain DBMS specific SQL code portion needed to set a primary key + * declaration to be used in statements like ALTER TABLE. + * + * @param Index $index Index definition + * @param string $name Name of the primary key + * + * @return string DBMS specific SQL code portion needed to set a primary key + * + * @throws \InvalidArgumentException if the given index is not a primary key. + */ + public function getPrimaryKeyDeclarationSQL(Index $index, $name = null) + { + if ( ! $index->isPrimary()) { + throw new \InvalidArgumentException( + 'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()' + ); + } + + return $this->getTableConstraintDeclarationSQL($index, $name); + } + + /** + * {@inheritdoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $columnDef) + { + $columnDef['integer_type'] = 'SMALLINT'; + + return $this->_getCommonIntegerTypeDeclarationSQL($columnDef); + } + + /** + * Returns the SQL statement for starting an existing database. + * + * In SQL Anywhere you can start and stop databases on a + * database server instance. + * This is a required statement after having created a new database + * as it has to be explicitly started to be usable. + * SQL Anywhere does not automatically start a database after creation! + * + * @param string $database Name of the database to start. + * + * @return string + */ + public function getStartDatabaseSQL($database) + { + $database = new Identifier($database); + + return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF"; + } + + /** + * Returns the SQL statement for stopping a running database. + * + * In SQL Anywhere you can start and stop databases on a + * database server instance. + * This is a required statement before dropping an existing database + * as it has to be explicitly stopped before it can be dropped. + * + * @param string $database Name of the database to stop. + * + * @return string + */ + public function getStopDatabaseSQL($database) + { + $database = new Identifier($database); + + return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY'; + } + + /** + * {@inheritdoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (null === $length) { + return 'SUBSTRING(' . $value . ', ' . $from . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + /** + * {@inheritdoc} + */ + public function getTemporaryTableSQL() + { + return 'GLOBAL TEMPORARY'; + } + + /** + * {@inheritdoc} + */ + public function getTimeFormatString() + { + return 'H:i:s.u'; + } + + /** + * {@inheritdoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritdoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + return $this->getLtrimExpression($str); + case self::TRIM_TRAILING: + return $this->getRtrimExpression($str); + default: + return 'TRIM(' . $str . ')'; + } + } + + $pattern = "'%[^' + $char + ']%'"; + + switch ($pos) { + case self::TRIM_LEADING: + return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))'; + case self::TRIM_TRAILING: + return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))'; + default: + return + 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' . + 'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))'; + } + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE ' . $tableName; + } + + /** + * {@inheritdoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + if ($index->isPrimary()) { + throw new \InvalidArgumentException( + 'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().' + ); + } + + if ( ! $index->isUnique()) { + throw new \InvalidArgumentException( + 'Can only create unique constraint declarations, no common index declarations with ' . + 'getUniqueConstraintDeclarationSQL().' + ); + } + + return $this->getTableConstraintDeclarationSQL($index, $name); + } + + /** + * {@inheritdoc} + */ + public function getVarcharDefaultLength() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function getVarcharMaxLength() + { + return 32767; + } + + /** + * {@inheritdoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsCommentOnStatement() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + $unsigned = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : ''; + $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : ''; + + return $unsigned . $columnDef['integer_type'] . $autoincrement; + } + + /** + * {@inheritdoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + $indexSql = array(); + + if ( ! empty($options['uniqueConstraints'])) { + foreach ((array) $options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if ( ! empty($options['indexes'])) { + /** @var \Doctrine\DBAL\Schema\Index $index */ + foreach ((array) $options['indexes'] as $index) { + $indexSql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if ( ! empty($options['primary'])) { + $flags = ''; + + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) { + $flags = ' CLUSTERED '; + } + + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')'; + } + + if ( ! empty($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition); + } + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + $check = $this->getCheckDeclarationSQL($columns); + + if ( ! empty($check)) { + $query .= ', ' . $check; + } + + $query .= ')'; + + return array_merge(array($query), $indexSql); + } + + /** + * {@inheritdoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case Connection::TRANSACTION_READ_COMMITTED: + return 1; + case Connection::TRANSACTION_REPEATABLE_READ: + return 2; + case Connection::TRANSACTION_SERIALIZABLE: + return 3; + default: + throw new \InvalidArgumentException('Invalid isolation level:' . $level); + } + } + + /** + * {@inheritdoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + $limitOffsetClause = ''; + + if ($limit > 0) { + $limitOffsetClause = 'TOP ' . $limit . ' '; + } + + if ($offset > 0) { + if ($limit == 0) { + $limitOffsetClause = 'TOP ALL '; + } + + $limitOffsetClause .= 'START AT ' . ($offset + 1) . ' '; + } + + if ($limitOffsetClause) { + return preg_replace('/^\s*(SELECT\s+(DISTINCT\s+)?)/i', '\1' . $limitOffsetClause, $query); + } + + return $query; + } + + /** + * Return the INDEX query section dealing with non-standard + * SQL Anywhere options. + * + * @param Index $index Index definition + * + * @return string + */ + protected function getAdvancedIndexOptionsSQL(Index $index) + { + $sql = ''; + + if ( ! $index->isPrimary() && $index->hasFlag('for_olap_workload')) { + $sql .= ' FOR OLAP WORKLOAD'; + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')' + : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'; + } + + /** + * Returns the SQL snippet for creating a table constraint. + * + * @param Constraint $constraint The table constraint to create the SQL snippet for. + * @param string|null $name The table constraint name to use if any. + * + * @return string + * + * @throws \InvalidArgumentException if the given table constraint type is not supported by this method. + */ + protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null) + { + if ($constraint instanceof ForeignKeyConstraint) { + return $this->getForeignKeyDeclarationSQL($constraint); + } + + if ( ! $constraint instanceof Index) { + throw new \InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint)); + } + + if ( ! $constraint->isPrimary() && ! $constraint->isUnique()) { + throw new \InvalidArgumentException( + 'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' . + 'with getTableConstraintDeclarationSQL().' + ); + } + + $constraintColumns = $constraint->getQuotedColumns($this); + + if (empty($constraintColumns)) { + throw new \InvalidArgumentException("Incomplete definition. 'columns' required."); + } + + $sql = ''; + $flags = ''; + + if ( ! empty($name)) { + $name = new Identifier($name); + $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' '; + } + + if ($constraint->hasFlag('clustered')) { + $flags = 'CLUSTERED '; + } + + if ($constraint->isPrimary()) { + return $sql . 'PRIMARY KEY ' . $flags . '('. $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; + } + + return $sql . 'UNIQUE ' . $flags . '('. $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')'; + } + + /** + * {@inheritdoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->hasFlag('virtual')) { + $type .= 'VIRTUAL '; + } + + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } + + return $type; + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + 'ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this) + ); + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords'; + } + + /** + * {@inheritdoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed + ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')'); + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'char' => 'string', + 'long nvarchar' => 'text', + 'long varchar' => 'text', + 'nchar' => 'string', + 'ntext' => 'text', + 'nvarchar' => 'string', + 'text' => 'text', + 'uniqueidentifierstr' => 'guid', + 'varchar' => 'string', + 'xml' => 'text', + 'bigint' => 'bigint', + 'unsigned bigint' => 'bigint', + 'bit' => 'boolean', + 'decimal' => 'decimal', + 'double' => 'float', + 'float' => 'float', + 'int' => 'integer', + 'integer' => 'integer', + 'unsigned int' => 'integer', + 'numeric' => 'decimal', + 'smallint' => 'smallint', + 'unsigned smallint', 'smallint', + 'tinyint' => 'smallint', + 'unsigned tinyint', 'smallint', + 'money' => 'decimal', + 'smallmoney' => 'decimal', + 'long varbit' => 'text', + 'varbit' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'smalldatetime' => 'datetime', + 'time' => 'time', + 'timestamp' => 'datetime', + 'binary' => 'binary', + 'image' => 'blob', + 'long binary' => 'blob', + 'uniqueidentifier' => 'guid', + 'varbinary' => 'binary', + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php new file mode 100644 index 0000000..71829cd --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Table; + +/** + * Platform to ensure compatibility of Doctrine with SQL Azure + * + * On top of SQL Server 2008 the following functionality is added: + * + * - Create tables with the FEDERATED ON syntax. + */ +class SQLAzurePlatform extends SQLServer2008Platform +{ + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags=self::CREATE_INDEXES) + { + $sql = parent::getCreateTableSQL($table, $createFlags); + + if ($table->hasOption('azure.federatedOnColumnName')) { + $distributionName = $table->getOption('azure.federatedOnDistributionName'); + $columnName = $table->getOption('azure.federatedOnColumnName'); + $stmt = ' FEDERATED ON (' . $distributionName . ' = ' . $columnName . ')'; + + $sql[0] = $sql[0] . $stmt; + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php new file mode 100644 index 0000000..6dc1188 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2005 version and + * higher. + * + * Differences to SQL Server 2008 are: + * + * - DATETIME2 datatype does not exist, only DATETIME which has a precision of + * 3. This is not supported by PHP DateTime, so we are emulating it by + * setting .000 manually. + * - Starting with SQLServer2005 VARCHAR(MAX), VARBINARY(MAX) and + * NVARCHAR(max) replace the old TEXT, NTEXT and IMAGE types. See + * {@link http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx} + * for more information. + */ +class SQLServer2005Platform extends SQLServerPlatform +{ + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2005 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php new file mode 100644 index 0000000..8b1184d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2008Platform.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2008 version. + * + * Differences to SQL Server 2005 and before are that a new DATETIME2 type was + * introduced that has a higher precision. + */ +class SQLServer2008Platform extends SQLServer2005Platform +{ + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME(0)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIMEOFFSET(6)'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.u'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return 'Y-m-d H:i:s.u P'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'H:i:s'; + } + + /** + * {@inheritDoc} + * + * Adding Datetime2 Type + */ + protected function initializeDoctrineTypeMappings() + { + parent::initializeDoctrineTypeMappings(); + $this->doctrineTypeMapping['datetime2'] = 'datetime'; + $this->doctrineTypeMapping['date'] = 'date'; + $this->doctrineTypeMapping['time'] = 'time'; + $this->doctrineTypeMapping['datetimeoffset'] = 'datetimetz'; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2008 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php new file mode 100644 index 0000000..fb78a80 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php @@ -0,0 +1,105 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Sequence; + +/** + * Platform to ensure compatibility of Doctrine with Microsoft SQL Server 2012 version. + * + * Differences to SQL Server 2008 and before are that sequences are introduced. + * + * @author Steve Müller + */ +class SQLServer2012Platform extends SQLServer2008Platform +{ + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(Sequence $sequence) + { + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' START WITH ' . $sequence->getInitialValue() . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' MINVALUE ' . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); + } + + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT seq.name, + CAST( + seq.increment AS VARCHAR(MAX) + ) AS increment, -- CAST avoids driver error for sql_variant type + CAST( + seq.start_value AS VARCHAR(MAX) + ) AS start_value -- CAST avoids driver error for sql_variant type + FROM sys.sequences AS seq'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT NEXT VALUE FOR ' . $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + * + * Returns Microsoft SQL Server 2012 specific keywords class + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php new file mode 100644 index 0000000..1a5f99c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -0,0 +1,1587 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Table; + +/** + * The SQLServerPlatform provides the behavior, features and SQL dialect of the + * Microsoft SQL Server database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @author Steve Müller + */ +class SQLServerPlatform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + $factorClause = ''; + + if ('-' === $operator) { + $factorClause = '-1 * '; + } + + return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server prefers "autoincrement" identity columns + * since sequences can only be emulated with a table. + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + * + * Microsoft SQL Server supports this through AUTO_INCREMENT columns. + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsReleaseSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSchemaName() + { + return 'dbo'; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function hasNativeGuidType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getCreateDatabaseSQL($name) + { + return 'CREATE DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getDropDatabaseSQL($name) + { + return 'DROP DATABASE ' . $name; + } + + /** + * {@inheritDoc} + */ + public function supportsCreateDropDatabase() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCreateSchemaSQL($schemaName) + { + return 'CREATE SCHEMA ' . $schemaName; + } + + /** + * {@inheritDoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + if ($foreignKey instanceof ForeignKeyConstraint) { + $foreignKey = $foreignKey->getQuotedName($this); + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + } + + /** + * {@inheritDoc} + */ + public function getDropIndexSQL($index, $table = null) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this); + } elseif (!is_string($index)) { + throw new \InvalidArgumentException('AbstractPlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); + } + + if (!isset($table)) { + return 'DROP INDEX ' . $index; + } + + if ($table instanceof Table) { + $table = $table->getQuotedName($this); + } + + return "IF EXISTS (SELECT * FROM sysobjects WHERE name = '$index') + ALTER TABLE " . $table . " DROP CONSTRAINT " . $index . " + ELSE + DROP INDEX " . $index . " ON " . $table; + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $defaultConstraintsSql = array(); + $commentsSql = array(); + + // @todo does other code breaks because of this? + // force primary keys to be not null + foreach ($columns as &$column) { + if (isset($column['primary']) && $column['primary']) { + $column['notnull'] = true; + } + + // Build default constraints SQL statements. + if (isset($column['default'])) { + $defaultConstraintsSql[] = 'ALTER TABLE ' . $tableName . + ' ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $column); + } + + if ( ! empty($column['comment']) || is_numeric($column['comment'])) { + $commentsSql[] = $this->getCreateColumnCommentSQL($tableName, $column['name'], $column['comment']); + } + } + + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && !empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && !empty($options['primary'])) { + $flags = ''; + if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } + + $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql; + + $check = $this->getCheckDeclarationSQL($columns); + if (!empty($check)) { + $query .= ', ' . $check; + } + $query .= ')'; + + $sql[] = $query; + + if (isset($options['indexes']) && !empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + if (isset($options['foreignKeys'])) { + foreach ((array) $options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); + } + } + + return array_merge($sql, $commentsSql, $defaultConstraintsSql); + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + $flags = ''; + if ($index->hasFlag('nonclustered')) { + $flags = ' NONCLUSTERED'; + } + + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY' . $flags . ' (' . $this->getIndexFieldDeclarationListSQL($index->getQuotedColumns($this)) . ')'; + } + + /** + * Returns the SQL statement for creating a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to create the comment for. + * @param string $comment The column's comment. + * + * @return string + */ + protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) + { + return $this->getAddExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL snippet for declaring a default constraint. + * + * @param string $table Name of the table to return the default constraint declaration for. + * @param array $column Column definition. + * + * @return string + * + * @throws \InvalidArgumentException + */ + public function getDefaultConstraintDeclarationSQL($table, array $column) + { + if ( ! isset($column['default'])) { + throw new \InvalidArgumentException("Incomplete column definition. 'default' required."); + } + + $columnName = new Identifier($column['name']); + + return + ' CONSTRAINT ' . + $this->generateDefaultConstraintName($table, $column['name']) . + $this->getDefaultValueDeclarationSQL($column) . + ' FOR ' . $columnName->getQuotedName($this); + } + + /** + * {@inheritDoc} + */ + public function getUniqueConstraintDeclarationSQL($name, Index $index) + { + $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); + + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + + return $constraint; + } + + /** + * {@inheritDoc} + */ + public function getCreateIndexSQL(Index $index, $table) + { + $constraint = parent::getCreateIndexSQL($index, $table); + + if ($index->isUnique() && !$index->isPrimary()) { + $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); + } + + return $constraint; + } + + /** + * {@inheritDoc} + */ + protected function getCreateIndexSQLFlags(Index $index) + { + $type = ''; + if ($index->isUnique()) { + $type .= 'UNIQUE '; + } + + if ($index->hasFlag('clustered')) { + $type .= 'CLUSTERED '; + } elseif ($index->hasFlag('nonclustered')) { + $type .= 'NONCLUSTERED '; + } + + return $type; + } + + /** + * Extend unique key constraint with required filters + * + * @param string $sql + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return string + */ + private function _appendUniqueConstraintDefinition($sql, Index $index) + { + $fields = array(); + + foreach ($index->getQuotedColumns($this) as $field) { + $fields[] = $field . ' IS NOT NULL'; + } + + return $sql . ' WHERE ' . implode(' AND ', $fields); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $queryParts = array(); + $sql = array(); + $columnSql = array(); + $commentsSql = array(); + + /** @var \Doctrine\DBAL\Schema\Column $column */ + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columnDef = $column->toArray(); + $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if (isset($columnDef['default'])) { + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + + $comment = $this->getColumnComment($column); + + if ( ! empty($comment) || is_numeric($comment)) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); + } + + /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $column = $columnDiff->column; + $comment = $this->getColumnComment($column); + $hasComment = ! empty ($comment) || is_numeric($comment); + + if ($columnDiff->fromColumn instanceof Column) { + $fromComment = $this->getColumnComment($columnDiff->fromColumn); + $hasFromComment = ! empty ($fromComment) || is_numeric($fromComment); + + if ($hasFromComment && $hasComment && $fromComment != $comment) { + $commentsSql[] = $this->getAlterColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } elseif ($hasFromComment && ! $hasComment) { + $commentsSql[] = $this->getDropColumnCommentSQL($diff->name, $column->getQuotedName($this)); + } elseif ($hasComment) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $diff->name, + $column->getQuotedName($this), + $comment + ); + } + } else { + // todo: Original comment cannot be determined. What to do? Add, update, drop or skip? + } + + // Do not add query part if only comment has changed. + if ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1) { + continue; + } + + $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); + + if ($requireDropDefaultConstraint) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $columnDiff->oldColumnName + ); + } + + $columnDef = $column->toArray(); + + $queryParts[] = 'ALTER COLUMN ' . + $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); + + if (isset($columnDef['default']) && ($requireDropDefaultConstraint || $columnDiff->hasChanged('default'))) { + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = new Identifier($oldColumnName); + + $sql[] = "sp_RENAME '" . + $diff->getName($this)->getQuotedName($this) . "." . $oldColumnName->getQuotedName($this) . + "', '" . $column->getQuotedName($this) . "', 'COLUMN'"; + + // Recreate default constraint with new column name if necessary (for future reference). + if ($column->getDefault() !== null) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( + $diff->name, + $oldColumnName->getQuotedName($this) + ); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($diff->name, $column); + } + } + + $tableSql = array(); + + if ($this->onSchemaAlterTable($diff, $tableSql)) { + return array_merge($tableSql, $columnSql); + } + + foreach ($queryParts as $query) { + $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; + } + + $sql = array_merge($sql, $commentsSql); + + if ($diff->newName !== false) { + $sql[] = "sp_RENAME '" . $diff->getName($this)->getQuotedName($this) . "', '" . $diff->getNewName()->getName() . "'"; + + /** + * Rename table's default constraints names + * to match the new table name. + * This is necessary to ensure that the default + * constraints can be referenced in future table + * alterations as the table name is encoded in + * default constraints' names. + */ + $sql[] = "DECLARE @sql NVARCHAR(MAX) = N''; " . + "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " . + "+ REPLACE(dc.name, '" . $this->generateIdentifierName($diff->name) . "', " . + "'" . $this->generateIdentifierName($diff->newName) . "') + ''', ''OBJECT'';' " . + "FROM sys.default_constraints dc " . + "JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " . + "WHERE tbl.name = '" . $diff->getNewName()->getName() . "';" . + "EXEC sp_executesql @sql"; + } + + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff) + ); + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param Column $column The column to generate the clause for. + * + * @return string + */ + private function getAlterTableAddDefaultConstraintClause($tableName, Column $column) + { + $columnDef = $column->toArray(); + $columnDef['name'] = $column->getQuotedName($this); + + return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); + } + + /** + * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. + * + * @param string $tableName The name of the table to generate the clause for. + * @param string $columnName The name of the column to generate the clause for. + * + * @return string + */ + private function getAlterTableDropDefaultConstraintClause($tableName, $columnName) + { + return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); + } + + /** + * Checks whether a column alteration requires dropping its default constraint first. + * + * Different to other database vendors SQL Server implements column default values + * as constraints and therefore changes in a column's default value as well as changes + * in a column's type require dropping the default constraint first before being to + * alter the particular column to the new definition. + * + * @param ColumnDiff $columnDiff The column diff to evaluate. + * + * @return boolean True if the column alteration requires dropping its default constraint first, false otherwise. + */ + private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff) + { + // We can only decide whether to drop an existing default constraint + // if we know the original default value. + if ( ! $columnDiff->fromColumn instanceof Column) { + return false; + } + + // We only need to drop an existing default constraint if we know the + // column was defined with a default value before. + if ($columnDiff->fromColumn->getDefault() === null) { + return false; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and it has changed. + if ($columnDiff->hasChanged('default')) { + return true; + } + + // We need to drop an existing default constraint if the column was + // defined with a default value before and the native column type has changed. + if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('fixed')) { + return true; + } + + return false; + } + + /** + * Returns the SQL statement for altering a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to alter the comment for. + * @param string $comment The column's comment. + * + * @return string + */ + protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) + { + return $this->getUpdateExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * Returns the SQL statement for dropping a column comment. + * + * SQL Server does not support native column comments, + * therefore the extended properties functionality is used + * as a workaround to store them. + * The property name used to store column comments is "MS_Description" + * which provides compatibility with SQL Server Management Studio, + * as column comments are stored in the same property there when + * specifying a column's "Description" attribute. + * + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to drop the comment for. + * + * @return string + */ + protected function getDropColumnCommentSQL($tableName, $columnName) + { + return $this->getDropExtendedPropertySQL( + 'MS_Description', + 'SCHEMA', + 'dbo', + 'TABLE', + $tableName, + 'COLUMN', + $columnName + ); + } + + /** + * {@inheritdoc} + */ + protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + { + return array( + sprintf( + "EXEC sp_RENAME N'%s.%s', N'%s', N'INDEX'", + $tableName, + $oldIndexName, + $index->getQuotedName($this) + ) + ); + } + + /** + * Returns the SQL statement for adding an extended property to a database object. + * + * @param string $name The name of the property to add. + * @param string|null $value The value of the property to add. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx + */ + public function getAddExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_addextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", N" . $this->quoteStringLiteral($value) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * Returns the SQL statement for dropping an extended property from a database object. + * + * @param string $name The name of the property to drop. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx + */ + public function getDropExtendedPropertySQL( + $name, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_dropextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * Returns the SQL statement for updating an extended property of a database object. + * + * @param string $name The name of the property to update. + * @param string|null $value The value of the property to update. + * @param string|null $level0Type The type of the object at level 0 the property belongs to. + * @param string|null $level0Name The name of the object at level 0 the property belongs to. + * @param string|null $level1Type The type of the object at level 1 the property belongs to. + * @param string|null $level1Name The name of the object at level 1 the property belongs to. + * @param string|null $level2Type The type of the object at level 2 the property belongs to. + * @param string|null $level2Name The name of the object at level 2 the property belongs to. + * + * @return string + * + * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx + */ + public function getUpdateExtendedPropertySQL( + $name, + $value = null, + $level0Type = null, + $level0Name = null, + $level1Type = null, + $level1Name = null, + $level2Type = null, + $level2Name = null + ) { + return "EXEC sp_updateextendedproperty " . + "N" . $this->quoteStringLiteral($name) . ", N" . $this->quoteStringLiteral($value) . ", " . + "N" . $this->quoteStringLiteral($level0Type) . ", " . $level0Name . ', ' . + "N" . $this->quoteStringLiteral($level1Type) . ", " . $level1Name . ', ' . + "N" . $this->quoteStringLiteral($level2Type) . ", " . $level2Name; + } + + /** + * {@inheritDoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams + // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication + return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + return "SELECT col.name, + type.name AS type, + col.max_length AS length, + ~col.is_nullable AS notnull, + def.definition AS [default], + col.scale, + col.precision, + col.is_identity AS autoincrement, + col.collation_name AS collation, + CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type + FROM sys.columns AS col + JOIN sys.types AS type + ON col.user_type_id = type.user_type_id + JOIN sys.objects AS obj + ON col.object_id = obj.object_id + JOIN sys.schemas AS scm + ON obj.schema_id = scm.schema_id + LEFT JOIN sys.default_constraints def + ON col.default_object_id = def.object_id + AND col.object_id = def.parent_object_id + LEFT JOIN sys.extended_properties AS prop + ON obj.object_id = prop.major_id + AND col.column_id = prop.minor_id + AND prop.name = 'MS_Description' + WHERE obj.type = 'U' + AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + return "SELECT f.name AS ForeignKey, + SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, + OBJECT_NAME (f.parent_object_id) AS TableName, + COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, + SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, + OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, + COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, + f.delete_referential_action_desc, + f.update_referential_action_desc + FROM sys.foreign_keys AS f + INNER JOIN sys.foreign_key_columns AS fc + INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id + ON f.OBJECT_ID = fc.constraint_object_id + WHERE " . + $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)'); + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + return "SELECT idx.name AS key_name, + col.name AS column_name, + ~idx.is_unique AS non_unique, + idx.is_primary_key AS [primary], + CASE idx.type + WHEN '1' THEN 'clustered' + WHEN '2' THEN 'nonclustered' + ELSE NULL + END AS flags + FROM sys.tables AS tbl + JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id + JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id + JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id + JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id + WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . " + ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name FROM sysobjects WHERE type = 'V' ORDER BY name"; + } + + /** + * Returns the where clause to filter schema and table name in a query. + * + * @param string $table The full qualified name of the table. + * @param string $schemaColumn The name of the column to compare the schema to in the where clause. + * @param string $tableColumn The name of the column to compare the table to in the where clause. + * + * @return string + */ + private function getTableWhereClause($table, $schemaColumn, $tableColumn) + { + if (strpos($table, ".") !== false) { + list($schema, $table) = explode(".", $table); + $schema = "'" . $schema . "'"; + } else { + $schema = "SCHEMA_NAME()"; + } + + return "({$tableColumn} = '{$table}' AND {$schemaColumn} = {$schema})"; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW ' . $name; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return 'NEWID()'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'CHARINDEX(' . $substr . ', ' . $str . ')'; + } + + return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + } + + /** + * {@inheritDoc} + */ + public function getModExpression($expression1, $expression2) + { + return $expression1 . ' % ' . $expression2; + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + if ( ! $char) { + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + return 'LTRIM(RTRIM(' . $str . '))'; + } + + return $trimFn . '(' . $str . ')'; + } + + /** Original query used to get those expressions + declare @c varchar(100) = 'xxxBarxxx', @trim_char char(1) = 'x'; + declare @pat varchar(10) = '%[^' + @trim_char + ']%'; + select @c as string + , @trim_char as trim_char + , stuff(@c, 1, patindex(@pat, @c) - 1, null) as trim_leading + , reverse(stuff(reverse(@c), 1, patindex(@pat, reverse(@c)) - 1, null)) as trim_trailing + , reverse(stuff(reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null)), 1, patindex(@pat, reverse(stuff(@c, 1, patindex(@pat, @c) - 1, null))) - 1, null)) as trim_both; + */ + $pattern = "'%[^' + $char + ']%'"; + + if ($pos == self::TRIM_LEADING) { + return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; + } + + if ($pos == self::TRIM_TRAILING) { + return 'reverse(stuff(reverse(' . $str . '), 1, patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; + } + + return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))'; + } + + /** + * {@inheritDoc} + */ + public function getConcatExpression() + { + $args = func_get_args(); + + return '(' . implode(' + ', $args) . ')'; + } + + /** + * {@inheritDoc} + */ + public function getListDatabasesSQL() + { + return 'SELECT * FROM sys.databases'; + } + + /** + * {@inheritDoc} + */ + public function getListNamespacesSQL() + { + return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; + } + + /** + * {@inheritDoc} + */ + public function getSubstringExpression($value, $from, $length = null) + { + if (!is_null($length)) { + return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')'; + } + + return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)'; + } + + /** + * {@inheritDoc} + */ + public function getLengthExpression($column) + { + return 'LEN(' . $column . ')'; + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getGuidTypeDeclarationSQL(array $field) + { + return 'UNIQUEIDENTIFIER'; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'NCHAR(' . $length . ')' : 'CHAR(255)') : ($length ? 'NVARCHAR(' . $length . ')' : 'NVARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 8000; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'VARCHAR(MAX)'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return (!empty($columnDef['autoincrement'])) ? ' IDENTITY' : ''; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BIT'; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset = null) + { + if ($limit === null) { + return $query; + } + + $start = $offset + 1; + $end = $offset + $limit; + + // We'll find a SELECT or SELECT distinct and prepend TOP n to it + // Even if the TOP n is very large, the use of a CTE will + // allow the SQL Server query planner to optimize it so it doesn't + // actually scan the entire range covered by the TOP clause. + $selectPattern = '/^(\s*SELECT\s+(?:DISTINCT\s+)?)(.*)$/i'; + $replacePattern = sprintf('$1%s $2', "TOP $end"); + $query = preg_replace($selectPattern, $replacePattern, $query); + + if (stristr($query, "ORDER BY")) { + // Inner order by is not valid in SQL Server for our purposes + // unless it's in a TOP N subquery. + $query = $this->scrubInnerOrderBy($query); + } + + // Build a new limited query around the original, using a CTE + return sprintf( + "WITH dctrn_cte AS (%s) " + . "SELECT * FROM (" + . "SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte" + . ") AS doctrine_tbl " + . "WHERE doctrine_rownum BETWEEN %d AND %d ORDER BY doctrine_rownum ASC", + $query, + $start, + $end + ); + } + + /** + * Remove ORDER BY clauses in subqueries - they're not supported by SQL Server. + * Caveat: will leave ORDER BY in TOP N subqueries. + * + * @param $query + * @return string + */ + private function scrubInnerOrderBy($query) + { + $count = substr_count(strtoupper($query), "ORDER BY"); + $offset = 0; + + while ($count-- > 0) { + $qLen = strlen($query); + $orderByPos = stripos($query, " ORDER BY", $offset); + $parenCount = 0; + $currentPosition = $orderByPos; + + while ($parenCount >= 0 && $currentPosition < $qLen) { + if ($query[$currentPosition] === '(') { + $parenCount++; + } elseif ($query[$currentPosition] === ')') { + $parenCount--; + } + + $currentPosition++; + } + + if ($this->isOrderByInTopNSubquery($query, $orderByPos)) { + // If the order by clause is in a TOP N subquery, do not remove + // it and continue iteration from the current position. + $offset = $currentPosition; + continue; + } + + if ($currentPosition < $qLen - 1) { + $query = substr($query, 0, $orderByPos) . substr($query, $currentPosition - 1); + $offset = $orderByPos; + } + } + return $query; + } + + /** + * Check an ORDER BY clause to see if it is in a TOP N query or subquery. + * + * @param string $query The query + * @param int $currentPosition Start position of ORDER BY clause + * @return bool true if ORDER BY is in a TOP N query, false otherwise + */ + private function isOrderByInTopNSubquery($query, $currentPosition) + { + // Grab query text on the same nesting level as the ORDER BY clause we're examining. + $subQueryBuffer = ''; + $parenCount = 0; + + // If $parenCount goes negative, we've exited the subquery we're examining. + // If $currentPosition goes negative, we've reached the beginning of the query. + while ($parenCount >= 0 && $currentPosition >= 0) { + if ($query[$currentPosition] === '(') { + $parenCount--; + } elseif ($query[$currentPosition] === ')') { + $parenCount++; + } + + // Only yank query text on the same nesting level as the ORDER BY clause. + $subQueryBuffer = ($parenCount === 0 ? $query[$currentPosition] : " ") . $subQueryBuffer; + + $currentPosition--; + } + + if (preg_match('/SELECT\s+(DISTINCT\s+)?TOP\s/i', $subQueryBuffer)) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function supportsLimitOffset() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function convertBooleans($item) + { + if (is_array($item)) { + foreach ($item as $key => $value) { + if (is_bool($value) || is_numeric($item)) { + $item[$key] = ($value) ? 1 : 0; + } + } + } elseif (is_bool($item) || is_numeric($item)) { + $item = ($item) ? 1 : 0; + } + + return $item; + } + + /** + * {@inheritDoc} + */ + public function getCreateTemporaryTableSnippetSQL() + { + return "CREATE TABLE"; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + return '#' . $tableName; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getTimeFormatString() + { + return 'Y-m-d H:i:s.000'; + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTzFormatString() + { + return $this->getDateTimeFormatString(); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'mssql'; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'bigint' => 'bigint', + 'numeric' => 'decimal', + 'bit' => 'boolean', + 'smallint' => 'smallint', + 'decimal' => 'decimal', + 'smallmoney' => 'integer', + 'int' => 'integer', + 'tinyint' => 'smallint', + 'money' => 'integer', + 'float' => 'float', + 'real' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'smalldatetime' => 'datetime', + 'datetime' => 'datetime', + 'char' => 'string', + 'varchar' => 'string', + 'text' => 'text', + 'nchar' => 'string', + 'nvarchar' => 'string', + 'ntext' => 'text', + 'binary' => 'binary', + 'varbinary' => 'binary', + 'image' => 'blob', + 'uniqueidentifier' => 'guid', + ); + } + + /** + * {@inheritDoc} + */ + public function createSavePoint($savepoint) + { + return 'SAVE TRANSACTION ' . $savepoint; + } + + /** + * {@inheritDoc} + */ + public function releaseSavePoint($savepoint) + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function rollbackSavePoint($savepoint) + { + return 'ROLLBACK TRANSACTION ' . $savepoint; + } + + /** + * {@inheritdoc} + */ + public function getForeignKeyReferentialActionSQL($action) + { + // RESTRICT is not supported, therefore falling back to NO ACTION. + if (strtoupper($action) === 'RESTRICT') { + return 'NO ACTION'; + } + + return parent::getForeignKeyReferentialActionSQL($action); + } + + /** + * {@inheritDoc} + */ + public function appendLockHint($fromClause, $lockMode) + { + switch (true) { + case LockMode::NONE === $lockMode: + return $fromClause . ' WITH (NOLOCK)'; + + case LockMode::PESSIMISTIC_READ === $lockMode: + return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; + + case LockMode::PESSIMISTIC_WRITE === $lockMode: + return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; + + default: + return $fromClause; + } + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSQL() + { + return ' '; + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords'; + } + + /** + * {@inheritDoc} + */ + public function quoteSingleIdentifier($str) + { + return "[" . str_replace("]", "][", $str) . "]"; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return 'TRUNCATE TABLE '.$tableName; + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'VARBINARY(MAX)'; + } + + /** + * {@inheritDoc} + */ + public function getDefaultValueDeclarationSQL($field) + { + if ( ! isset($field['default'])) { + return empty($field['notnull']) ? ' NULL' : ''; + } + + if ( ! isset($field['type'])) { + return " DEFAULT '" . $field['default'] . "'"; + } + + if (in_array((string) $field['type'], array('Integer', 'BigInt', 'SmallInt'))) { + return " DEFAULT " . $field['default']; + } + + if (in_array((string) $field['type'], array('DateTime', 'DateTimeTz')) && $field['default'] == $this->getCurrentTimestampSQL()) { + return " DEFAULT " . $this->getCurrentTimestampSQL(); + } + + if ((string) $field['type'] == 'Boolean') { + return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; + } + + return " DEFAULT '" . $field['default'] . "'"; + } + + /** + * {@inheritdoc} + * + * Modifies column declaration order as it differs in Microsoft SQL Server. + */ + public function getColumnDeclarationSQL($name, array $field) + { + if (isset($field['columnDefinition'])) { + $columnDef = $this->getCustomTypeDeclarationSQL($field); + } else { + $collation = (isset($field['collation']) && $field['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($field['collation']) : ''; + + $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : ''; + + $unique = (isset($field['unique']) && $field['unique']) ? + ' ' . $this->getUniqueFieldDeclarationSQL() : ''; + + $check = (isset($field['check']) && $field['check']) ? + ' ' . $field['check'] : ''; + + $typeDecl = $field['type']->getSqlDeclaration($field, $this); + $columnDef = $typeDecl . $collation . $notnull . $unique . $check; + } + + return $name . ' ' . $columnDef; + } + + /** + * Returns a unique default constraint name for a table and column. + * + * @param string $table Name of the table to generate the unique default constraint name for. + * @param string $column Name of the column in the table to generate the unique default constraint name for. + * + * @return string + */ + private function generateDefaultConstraintName($table, $column) + { + return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); + } + + /** + * Returns a hash value for a given identifier. + * + * @param string $identifier Identifier to generate a hash value for. + * + * @return string + */ + private function generateIdentifierName($identifier) + { + // Always generate name for unquoted identifiers to ensure consistency. + $identifier = new Identifier($identifier); + + return strtoupper(dechex(crc32($identifier->getName()))); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php new file mode 100644 index 0000000..3d8a09b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -0,0 +1,1113 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Identifier; +use Doctrine\DBAL\Schema\Constraint; + +/** + * The SqlitePlatform class describes the specifics and dialects of the SQLite + * database platform. + * + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Martin Hasoň + * @todo Rename: SQLitePlatform + */ +class SqlitePlatform extends AbstractPlatform +{ + /** + * {@inheritDoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + + /** + * {@inheritDoc} + */ + public function getGuidExpression() + { + return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || " + . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || " + . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))"; + } + + /** + * {@inheritDoc} + */ + public function getNowExpression($type = 'timestamp') + { + switch ($type) { + case 'time': + return 'time(\'now\')'; + case 'date': + return 'date(\'now\')'; + case 'timestamp': + default: + return 'datetime(\'now\')'; + } + } + + /** + * {@inheritDoc} + */ + public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false) + { + $trimChar = ($char != false) ? (', ' . $char) : ''; + + switch ($pos) { + case self::TRIM_LEADING: + $trimFn = 'LTRIM'; + break; + + case self::TRIM_TRAILING: + $trimFn = 'RTRIM'; + break; + + default: + $trimFn = 'TRIM'; + } + + return $trimFn . '(' . $str . $trimChar . ')'; + } + + /** + * {@inheritDoc} + * + * SQLite only supports the 2 parameter variant of this function + */ + public function getSubstringExpression($value, $position, $length = null) + { + if ($length !== null) { + return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')'; + } + + return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))'; + } + + /** + * {@inheritDoc} + */ + public function getLocateExpression($str, $substr, $startPos = false) + { + if ($startPos == false) { + return 'LOCATE('.$str.', '.$substr.')'; + } + + return 'LOCATE('.$str.', '.$substr.', '.$startPos.')'; + } + + /** + * {@inheritdoc} + */ + protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + { + switch ($unit) { + case self::DATE_INTERVAL_UNIT_SECOND: + case self::DATE_INTERVAL_UNIT_MINUTE: + case self::DATE_INTERVAL_UNIT_HOUR: + return "DATETIME(" . $date . ",'" . $operator . $interval . " " . $unit . "')"; + + default: + switch ($unit) { + case self::DATE_INTERVAL_UNIT_WEEK: + $interval *= 7; + $unit = self::DATE_INTERVAL_UNIT_DAY; + break; + + case self::DATE_INTERVAL_UNIT_QUARTER: + $interval *= 3; + $unit = self::DATE_INTERVAL_UNIT_MONTH; + break; + } + + return "DATE(" . $date . ",'" . $operator . $interval . " " . $unit . "')"; + } + } + + /** + * {@inheritDoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return 'ROUND(JULIANDAY('.$date1 . ')-JULIANDAY('.$date2.'))'; + } + + /** + * {@inheritDoc} + */ + protected function _getTransactionIsolationLevelSQL($level) + { + switch ($level) { + case \Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED: + return 0; + case \Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED: + case \Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ: + case \Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE: + return 1; + default: + return parent::_getTransactionIsolationLevelSQL($level); + } + } + + /** + * {@inheritDoc} + */ + public function getSetTransactionIsolationSQL($level) + { + return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function prefersIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getTinyIntTypeDeclarationSql(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getMediumIntTypeDeclarationSql(array $field) + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT fields. + if ( ! empty($field['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($field); + } + + return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + // sqlite autoincrement is implicit for integer PKs, but not when the field is unsigned + if ( ! empty($columnDef['autoincrement'])) { + return ''; + } + + return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; + } + + /** + * {@inheritDoc} + */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) + { + return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( + $foreignKey->getQuotedLocalColumns($this), + str_replace('.', '__', $foreignKey->getQuotedForeignTableName($this)), + $foreignKey->getQuotedForeignColumns($this), + $foreignKey->getName(), + $foreignKey->getOptions() + )); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL($name, array $columns, array $options = array()) + { + $name = str_replace('.', '__', $name); + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; + } + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $foreignKey) { + $queryFields.= ', '.$this->getForeignKeyDeclarationSQL($foreignKey); + } + } + + $query[] = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; + + if (isset($options['alter']) && true === $options['alter']) { + return $query; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + return $query; + } + + /** + * {@inheritDoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + } + + /** + * {@inheritdoc} + */ + protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + { + return 'BLOB'; + } + + /** + * {@inheritdoc} + */ + public function getBinaryMaxLength() + { + return 0; + } + + /** + * {@inheritdoc} + */ + public function getBinaryDefaultLength() + { + return 0; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return 'CLOB'; + } + + /** + * {@inheritDoc} + */ + public function getListTableConstraintsSQL($table) + { + $table = str_replace('.', '__', $table); + + return "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = '$table' AND sql NOT NULL ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListTableColumnsSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA table_info('$table')"; + } + + /** + * {@inheritDoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA index_list('$table')"; + } + + /** + * {@inheritDoc} + */ + public function getListTablesSQL() + { + return "SELECT name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type = 'table' ORDER BY name"; + } + + /** + * {@inheritDoc} + */ + public function getListViewsSQL($database) + { + return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + } + + /** + * {@inheritDoc} + */ + public function getCreateViewSQL($name, $sql) + { + return 'CREATE VIEW ' . $name . ' AS ' . $sql; + } + + /** + * {@inheritDoc} + */ + public function getDropViewSQL($name) + { + return 'DROP VIEW '. $name; + } + + /** + * {@inheritDoc} + */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + { + $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + $query .= (($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) ? ' ' : ' NOT ') . 'DEFERRABLE'; + $query .= ' INITIALLY ' . (($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ? 'DEFERRED' : 'IMMEDIATE'); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'sqlite'; + } + + /** + * {@inheritDoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + $tableName = str_replace('.', '__', $tableName); + + return 'DELETE FROM ' . $tableName; + } + + /** + * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction(). + * + * @param integer|float $value + * + * @return float + */ + static public function udfSqrt($value) + { + return sqrt($value); + } + + /** + * User-defined function for Sqlite that implements MOD(a, b). + * + * @param integer $a + * @param integer $b + * + * @return integer + */ + static public function udfMod($a, $b) + { + return ($a % $b); + } + + /** + * @param string $str + * @param string $substr + * @param integer $offset + * + * @return integer + */ + static public function udfLocate($str, $substr, $offset = 0) + { + // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions. + // So we have to make them compatible if an offset is given. + if ($offset > 0) { + $offset -= 1; + } + + $pos = strpos($str, $substr, $offset); + + if ($pos !== false) { + return $pos + 1; + } + + return 0; + } + + /** + * {@inheritDoc} + */ + public function getForUpdateSql() + { + return ''; + } + + /** + * {@inheritDoc} + */ + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'boolean' => 'boolean', + 'tinyint' => 'boolean', + 'smallint' => 'smallint', + 'mediumint' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'serial' => 'integer', + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'clob' => 'text', + 'tinytext' => 'text', + 'mediumtext' => 'text', + 'longtext' => 'text', + 'text' => 'text', + 'varchar' => 'string', + 'longvarchar' => 'string', + 'varchar2' => 'string', + 'nvarchar' => 'string', + 'image' => 'string', + 'ntext' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'double precision' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'blob' => 'blob', + ); + } + + /** + * {@inheritDoc} + */ + protected function getReservedKeywordsClass() + { + return 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords'; + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + foreach ($diff->fromTable->getIndexes() as $index) { + if ( ! $index->isPrimary()) { + $sql[] = $this->getDropIndexSQL($index, $diff->name); + } + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) + { + if ( ! $diff->fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $sql = array(); + $tableName = $diff->newName ? $diff->getNewName(): $diff->getName($this); + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + continue; + } + + $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this)); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function doModifyLimitQuery($query, $limit, $offset) + { + if (null === $limit && null !== $offset) { + return $query . ' LIMIT -1 OFFSET ' . $offset; + } + + return parent::doModifyLimitQuery($query, $limit, $offset); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getTemporaryTableName($tableName) + { + $tableName = str_replace('.', '__', $tableName); + + return $tableName; + } + + /** + * {@inheritDoc} + * + * Sqlite Platform emulates schema by underscoring each dot and generating tables + * into the default database. + * + * This hack is implemented to be able to use SQLite as testdriver when + * using schema supporting databases. + */ + public function canEmulateSchemas() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getCreatePrimaryKeySQL(Index $index, $table) + { + throw new DBALException('Sqlite platform does not support alter primary key.'); + } + + /** + * {@inheritdoc} + */ + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritdoc} + */ + public function getDropForeignKeySQL($foreignKey, $table) + { + throw new DBALException('Sqlite platform does not support alter foreign key.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateConstraintSQL(Constraint $constraint, $table) + { + throw new DBALException('Sqlite platform does not support alter constraint.'); + } + + /** + * {@inheritDoc} + */ + public function getCreateTableSQL(Table $table, $createFlags = null) + { + $createFlags = null === $createFlags ? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS : $createFlags; + + return parent::getCreateTableSQL($table, $createFlags); + } + + /** + * {@inheritDoc} + */ + public function getListTableForeignKeysSQL($table, $database = null) + { + $table = str_replace('.', '__', $table); + + return "PRAGMA foreign_key_list('$table')"; + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = $this->getSimpleAlterTableSQL($diff); + if (false !== $sql) { + return $sql; + } + + $fromTable = $diff->fromTable; + if ( ! $fromTable instanceof Table) { + throw new DBALException('Sqlite platform requires for alter table the table diff with reference to original table schema'); + } + + $table = clone $fromTable; + + $columns = array(); + $oldColumnNames = array(); + $newColumnNames = array(); + $columnSql = array(); + + foreach ($table->getColumns() as $columnName => $column) { + $columnName = strtolower($columnName); + $columns[$columnName] = $column; + $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); + } + + foreach ($diff->removedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + unset($oldColumnNames[$columnName]); + unset($newColumnNames[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + + $oldColumnName = strtolower($oldColumnName); + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($column->getName())] = $column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $column->getQuotedName($this); + } + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + if (isset($columns[$oldColumnName])) { + unset($columns[$oldColumnName]); + } + + $columns[strtolower($columnDiff->column->getName())] = $columnDiff->column; + + if (isset($newColumnNames[$oldColumnName])) { + $newColumnNames[$oldColumnName] = $columnDiff->column->getQuotedName($this); + } + } + + foreach ($diff->addedColumns as $columnName => $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $columns[strtolower($columnName)] = $column; + } + + $sql = array(); + $tableSql = array(); + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + $dataTable = new Table('__temp__'.$table->getName()); + + $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions()); + $newTable->addOption('alter', true); + + $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); + //$sql = array_merge($sql, $this->getCreateTableSQL($dataTable, 0)); + $sql[] = sprintf('CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($fromTable); + + $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); + $sql[] = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this)); + $sql[] = $this->getDropTableSQL($dataTable); + + if ($diff->newName && $diff->newName != $diff->name) { + $renamedTable = $diff->getNewName(); + $sql[] = 'ALTER TABLE '.$newTable->getQuotedName($this).' RENAME TO '.$renamedTable->getQuotedName($this); + } + + $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array|bool + */ + private function getSimpleAlterTableSQL(TableDiff $diff) + { + // Suppress changes on integer type autoincrement columns. + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + if ( ! $columnDiff->fromColumn instanceof Column || + ! $columnDiff->column instanceof Column || + ! $columnDiff->column->getAutoincrement() || + ! (string) $columnDiff->column->getType() === 'Integer' + ) { + continue; + } + + if ( ! $columnDiff->hasChanged('type') && $columnDiff->hasChanged('unsigned')) { + unset($diff->changedColumns[$oldColumnName]); + + continue; + } + + $fromColumnType = (string) $columnDiff->fromColumn->getType(); + + if ($fromColumnType === 'SmallInt' || $fromColumnType === 'BigInt') { + unset($diff->changedColumns[$oldColumnName]); + } + } + + if ( ! empty($diff->renamedColumns) || ! empty($diff->addedForeignKeys) || ! empty($diff->addedIndexes) + || ! empty($diff->changedColumns) || ! empty($diff->changedForeignKeys) || ! empty($diff->changedIndexes) + || ! empty($diff->removedColumns) || ! empty($diff->removedForeignKeys) || ! empty($diff->removedIndexes) + || ! empty($diff->renamedIndexes) + ) { + return false; + } + + $table = new Table($diff->name); + + $sql = array(); + $tableSql = array(); + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $field = array_merge(array('unique' => null, 'autoincrement' => null, 'default' => null), $column->toArray()); + $type = (string) $field['type']; + switch (true) { + case isset($field['columnDefinition']) || $field['autoincrement'] || $field['unique']: + case $type == 'DateTime' && $field['default'] == $this->getCurrentTimestampSQL(): + case $type == 'Date' && $field['default'] == $this->getCurrentDateSQL(): + case $type == 'Time' && $field['default'] == $this->getCurrentTimeSQL(): + return false; + } + + $field['name'] = $column->getQuotedName($this); + if (strtolower($field['type']) == 'string' && $field['length'] === null) { + $field['length'] = 255; + } + + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' ADD COLUMN '.$this->getColumnDeclarationSQL($field['name'], $field); + } + + if ( ! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + $newTable = new Identifier($diff->newName); + $sql[] = 'ALTER TABLE '.$table->getQuotedName($this).' RENAME TO '.$newTable->getQuotedName($this); + } + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getColumnNamesInAlteredTable(TableDiff $diff) + { + $columns = array(); + + foreach ($diff->fromTable->getColumns() as $columnName => $column) { + $columns[strtolower($columnName)] = $column->getName(); + } + + foreach ($diff->removedColumns as $columnName => $column) { + $columnName = strtolower($columnName); + if (isset($columns[$columnName])) { + unset($columns[$columnName]); + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + $columnName = $column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->changedColumns as $oldColumnName => $columnDiff) { + $columnName = $columnDiff->column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->addedColumns as $columnName => $column) { + $columns[strtolower($columnName)] = $columnName; + } + + return $columns; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return \Doctrine\DBAL\Schema\Index[] + */ + private function getIndexesInAlteredTable(TableDiff $diff) + { + $indexes = $diff->fromTable->getIndexes(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($indexes as $key => $index) { + foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) { + if (strtolower($key) === strtolower($oldIndexName)) { + unset($indexes[$key]); + } + } + + $changed = false; + $indexColumns = array(); + foreach ($index->getColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($indexes[$key]); + continue 2; + } else { + $indexColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $indexes[$key] = new Index($index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags()); + } + } + + foreach ($diff->removedIndexes as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName) && isset($indexes[$indexName])) { + unset($indexes[$indexName]); + } + } + + foreach (array_merge($diff->changedIndexes, $diff->addedIndexes, $diff->renamedIndexes) as $index) { + $indexName = strtolower($index->getName()); + if (strlen($indexName)) { + $indexes[$indexName] = $index; + } else { + $indexes[] = $index; + } + } + + return $indexes; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getForeignKeysInAlteredTable(TableDiff $diff) + { + $foreignKeys = $diff->fromTable->getForeignKeys(); + $columnNames = $this->getColumnNamesInAlteredTable($diff); + + foreach ($foreignKeys as $key => $constraint) { + $changed = false; + $localColumns = array(); + foreach ($constraint->getLocalColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if ( ! isset($columnNames[$normalizedColumnName])) { + unset($foreignKeys[$key]); + continue 2; + } else { + $localColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName !== $columnNames[$normalizedColumnName]) { + $changed = true; + } + } + } + + if ($changed) { + $foreignKeys[$key] = new ForeignKeyConstraint($localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions()); + } + } + + foreach ($diff->removedForeignKeys as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName) && isset($foreignKeys[$constraintName])) { + unset($foreignKeys[$constraintName]); + } + } + + foreach (array_merge($diff->changedForeignKeys, $diff->addedForeignKeys) as $constraint) { + $constraintName = strtolower($constraint->getName()); + if (strlen($constraintName)) { + $foreignKeys[$constraintName] = $constraint; + } else { + $foreignKeys[] = $constraint; + } + } + + return $foreignKeys; + } + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $diff + * + * @return array + */ + private function getPrimaryIndexInAlteredTable(TableDiff $diff) + { + $primaryIndex = array(); + + foreach ($this->getIndexesInAlteredTable($diff) as $index) { + if ($index->isPrimary()) { + $primaryIndex = array($index->getName() => $index); + } + } + + return $primaryIndex; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php new file mode 100644 index 0000000..df6b957 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Connection.php @@ -0,0 +1,150 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use Doctrine\DBAL\Cache\QueryCacheProfile; + +/** + * Portability wrapper for a Connection. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Connection extends \Doctrine\DBAL\Connection +{ + const PORTABILITY_ALL = 255; + const PORTABILITY_NONE = 0; + const PORTABILITY_RTRIM = 1; + const PORTABILITY_EMPTY_TO_NULL = 4; + const PORTABILITY_FIX_CASE = 8; + + const PORTABILITY_DB2 = 13; + const PORTABILITY_ORACLE = 9; + const PORTABILITY_POSTGRESQL = 13; + const PORTABILITY_SQLITE = 13; + const PORTABILITY_OTHERVENDORS = 12; + const PORTABILITY_DRIZZLE = 13; + const PORTABILITY_SQLANYWHERE = 13; + const PORTABILITY_SQLSRV = 13; + + /** + * @var integer + */ + private $portability = self::PORTABILITY_NONE; + + /** + * @var integer + */ + private $case; + + /** + * {@inheritdoc} + */ + public function connect() + { + $ret = parent::connect(); + if ($ret) { + $params = $this->getParams(); + if (isset($params['portability'])) { + if ($this->getDatabasePlatform()->getName() === "oracle") { + $params['portability'] = $params['portability'] & self::PORTABILITY_ORACLE; + } elseif ($this->getDatabasePlatform()->getName() === "postgresql") { + $params['portability'] = $params['portability'] & self::PORTABILITY_POSTGRESQL; + } elseif ($this->getDatabasePlatform()->getName() === "sqlite") { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE; + } elseif ($this->getDatabasePlatform()->getName() === "drizzle") { + $params['portability'] = self::PORTABILITY_DRIZZLE; + } elseif ($this->getDatabasePlatform()->getName() === 'sqlanywhere') { + $params['portability'] = self::PORTABILITY_SQLANYWHERE; + } elseif ($this->getDatabasePlatform()->getName() === 'db2') { + $params['portability'] = self::PORTABILITY_DB2; + } elseif ($this->getDatabasePlatform()->getName() === 'mssql') { + $params['portability'] = $params['portability'] & self::PORTABILITY_SQLSRV; + } else { + $params['portability'] = $params['portability'] & self::PORTABILITY_OTHERVENDORS; + } + $this->portability = $params['portability']; + } + if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { + if ($this->_conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // make use of c-level support for case handling + $this->_conn->setAttribute(\PDO::ATTR_CASE, $params['fetch_case']); + } else { + $this->case = ($params['fetch_case'] == \PDO::CASE_LOWER) ? CASE_LOWER : CASE_UPPER; + } + } + } + + return $ret; + } + + /** + * @return integer + */ + public function getPortability() + { + return $this->portability; + } + + /** + * @return integer + */ + public function getFetchCase() + { + return $this->case; + } + + /** + * {@inheritdoc} + */ + public function executeQuery($query, array $params = array(), $types = array(), QueryCacheProfile $qcp = null) + { + $stmt = new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function prepare($statement) + { + $stmt = new Statement(parent::prepare($statement), $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } + + /** + * {@inheritdoc} + */ + public function query() + { + $this->connect(); + + $stmt = call_user_func_array(array($this->_conn, 'query'), func_get_args()); + $stmt = new Statement($stmt, $this); + $stmt->setFetchMode($this->defaultFetchMode); + + return $stmt; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php new file mode 100644 index 0000000..352fda9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Portability/Statement.php @@ -0,0 +1,240 @@ +. + */ + +namespace Doctrine\DBAL\Portability; + +use PDO; + +/** + * Portability wrapper for a Statement. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Statement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement +{ + /** + * @var integer + */ + private $portability; + + /** + * @var \Doctrine\DBAL\Driver\Statement + */ + private $stmt; + + /** + * @var integer + */ + private $case; + + /** + * @var integer + */ + private $defaultFetchMode = PDO::FETCH_BOTH; + + /** + * Wraps Statement and applies portability measures. + * + * @param \Doctrine\DBAL\Driver\Statement $stmt + * @param \Doctrine\DBAL\Portability\Connection $conn + */ + public function __construct($stmt, Connection $conn) + { + $this->stmt = $stmt; + $this->portability = $conn->getPortability(); + $this->case = $conn->getFetchCase(); + } + + /** + * {@inheritdoc} + */ + public function bindParam($column, &$variable, $type = null, $length = null) + { + return $this->stmt->bindParam($column, $variable, $type, $length); + } + /** + * {@inheritdoc} + */ + + public function bindValue($param, $value, $type = null) + { + return $this->stmt->bindValue($param, $value, $type); + } + + /** + * {@inheritdoc} + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + /** + * {@inheritdoc} + */ + public function execute($params = null) + { + return $this->stmt->execute($params); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg1 = null, $arg2 = null) + { + $this->defaultFetchMode = $fetchMode; + + return $this->stmt->setFetchMode($fetchMode, $arg1, $arg2); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $data = $this->fetchAll(); + + return new \ArrayIterator($data); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + $row = $this->stmt->fetch($fetchMode); + + $row = $this->fixRow($row, + $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM), + !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE) + ); + + return $row; + } + + /** + * {@inheritdoc} + */ + public function fetchAll($fetchMode = null, $columnIndex = 0) + { + $fetchMode = $fetchMode ?: $this->defaultFetchMode; + + if ($columnIndex != 0) { + $rows = $this->stmt->fetchAll($fetchMode, $columnIndex); + } else { + $rows = $this->stmt->fetchAll($fetchMode); + } + + $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); + $fixCase = !is_null($this->case) && ($fetchMode == PDO::FETCH_ASSOC || $fetchMode == PDO::FETCH_BOTH) && ($this->portability & Connection::PORTABILITY_FIX_CASE); + if ( ! $iterateRow && !$fixCase) { + return $rows; + } + + foreach ($rows as $num => $row) { + $rows[$num] = $this->fixRow($row, $iterateRow, $fixCase); + } + + return $rows; + } + + /** + * @param mixed $row + * @param integer $iterateRow + * @param boolean $fixCase + * + * @return array + */ + protected function fixRow($row, $iterateRow, $fixCase) + { + if ( ! $row) { + return $row; + } + + if ($fixCase) { + $row = array_change_key_case($row, $this->case); + } + + if ($iterateRow) { + foreach ($row as $k => $v) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $v === '') { + $row[$k] = null; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($v)) { + $row[$k] = rtrim($v); + } + } + } + + return $row; + } + + /** + * {@inheritdoc} + */ + public function fetchColumn($columnIndex = 0) + { + $value = $this->stmt->fetchColumn($columnIndex); + + if ($this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM)) { + if (($this->portability & Connection::PORTABILITY_EMPTY_TO_NULL) && $value === '') { + $value = null; + } elseif (($this->portability & Connection::PORTABILITY_RTRIM) && is_string($value)) { + $value = rtrim($value); + } + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function rowCount() + { + return $this->stmt->rowCount(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php new file mode 100644 index 0000000..937726f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/CompositeExpression.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +/** + * Composite expression is responsible to build a group of similar expression. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class CompositeExpression implements \Countable +{ + /** + * Constant that represents an AND composite expression. + */ + const TYPE_AND = 'AND'; + + /** + * Constant that represents an OR composite expression. + */ + const TYPE_OR = 'OR'; + + /** + * The instance type of composite expression. + * + * @var string + */ + private $type; + + /** + * Each expression part of the composite expression. + * + * @var array + */ + private $parts = array(); + + /** + * Constructor. + * + * @param string $type Instance type of composite expression. + * @param array $parts Composition of expressions to be joined on composite expression. + */ + public function __construct($type, array $parts = array()) + { + $this->type = $type; + + $this->addMultiple($parts); + } + + /** + * Adds multiple parts to composite expression. + * + * @param array $parts + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function addMultiple(array $parts = array()) + { + foreach ((array) $parts as $part) { + $this->add($part); + } + + return $this; + } + + /** + * Adds an expression to composite expression. + * + * @param mixed $part + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function add($part) + { + if ( ! empty($part) || ($part instanceof self && $part->count() > 0)) { + $this->parts[] = $part; + } + + return $this; + } + + /** + * Retrieves the amount of expressions on composite expression. + * + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * Retrieves the string representation of this composite expression. + * + * @return string + */ + public function __toString() + { + if (count($this->parts) === 1) { + return (string) $this->parts[0]; + } + + return '(' . implode(') ' . $this->type . ' (', $this->parts) . ')'; + } + + /** + * Returns the type of this composite expression (AND/OR). + * + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php new file mode 100644 index 0000000..2736fcf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -0,0 +1,313 @@ +. + */ + +namespace Doctrine\DBAL\Query\Expression; + +use Doctrine\DBAL\Connection; + +/** + * ExpressionBuilder class is responsible to dynamically create SQL query parts. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class ExpressionBuilder +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * The DBAL Connection. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * Initializes a new ExpressionBuilder. + * + * @param \Doctrine\DBAL\Connection $connection The DBAL Connection. + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) AND (u.role = ?) + * $expr->andX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?) OR (u.role = ?) + * $qb->where($qb->expr()->orX('u.type = ?', 'u.role = ?')); + * + * @param mixed $x Optional clause. Defaults = null, but requires + * at least one defined when converting to string. + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * Creates a comparison expression. + * + * @param mixed $x The left expression. + * @param string $operator One of the ExpressionBuilder::* constants. + * @param mixed $y The right expression. + * + * @return string + */ + public function comparison($x, $operator, $y) + { + return $x . ' ' . $operator . ' ' . $y; + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ? + * $expr->eq('u.id', '?'); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function eq($x, $y) + { + return $this->comparison($x, self::EQ, $y); + } + + /** + * Creates a non equality comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> 1 + * $q->where($q->expr()->neq('u.id', '1')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function neq($x, $y) + { + return $this->comparison($x, self::NEQ, $y); + } + + /** + * Creates a lower-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ? + * $q->where($q->expr()->lt('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function lt($x, $y) + { + return $this->comparison($x, self::LT, $y); + } + + /** + * Creates a lower-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ? + * $q->where($q->expr()->lte('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function lte($x, $y) + { + return $this->comparison($x, self::LTE, $y); + } + + /** + * Creates a greater-than comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ? + * $q->where($q->expr()->gt('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function gt($x, $y) + { + return $this->comparison($x, self::GT, $y); + } + + /** + * Creates a greater-than-equal comparison expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ? + * $q->where($q->expr()->gte('u.id', '?')); + * + * @param mixed $x The left expression. + * @param mixed $y The right expression. + * + * @return string + */ + public function gte($x, $y) + { + return $this->comparison($x, self::GTE, $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x The field in string format to be restricted by IS NULL. + * + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x The field in string format to be restricted by IS NOT NULL. + * + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return string + */ + public function like($x, $y) + { + return $this->comparison($x, 'LIKE', $y); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by NOT LIKE() comparison. + * @param mixed $y Argument to be used in NOT LIKE() comparison. + * + * @return string + */ + public function notLike($x, $y) + { + return $this->comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by IN() comparison. + * @param string|array $y The placeholder or the array of values to be used by IN() comparison. + * + * @return string + */ + public function in($x, $y) + { + return $this->comparison($x, 'IN', '('.implode(', ', (array) $y).')'); + } + + /** + * Creates a NOT IN () comparison expression with the given arguments. + * + * @param string $x The field in string format to be inspected by NOT IN() comparison. + * @param string|array $y The placeholder or the array of values to be used by NOT IN() comparison. + * + * @return string + */ + public function notIn($x, $y) + { + return $this->comparison($x, 'NOT IN', '('.implode(', ', (array) $y).')'); + } + + /** + * Quotes a given input parameter. + * + * @param mixed $input The parameter to be quoted. + * @param string|null $type The type of the parameter. + * + * @return string + */ + public function literal($input, $type = null) + { + return $this->connection->quote($input, $type); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php new file mode 100644 index 0000000..0a6d59e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -0,0 +1,1354 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\Query\Expression\CompositeExpression; +use Doctrine\DBAL\Connection; + +/** + * QueryBuilder class is responsible to dynamically create SQL queries. + * + * Important: Verify that every feature you use will work with your database vendor. + * SQL Query Builder does not attempt to validate the generated SQL at all. + * + * The query builder does no validation whatsoever if certain features even work with the + * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements + * even if some vendors such as MySQL support it. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class QueryBuilder +{ + /* + * The query types. + */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + const INSERT = 3; + + /* + * The builder states. + */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * The DBAL Connection. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * @var array The array of SQL parts collected. + */ + private $sqlParts = array( + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array(), + 'values' => array(), + ); + + /** + * The complete SQL string for this query. + * + * @var string + */ + private $sql; + + /** + * The query parameters. + * + * @var array + */ + private $params = array(); + + /** + * The parameter type map of this query. + * + * @var array + */ + private $paramTypes = array(); + + /** + * The type of query this is. Can be select, update or delete. + * + * @var integer + */ + private $type = self::SELECT; + + /** + * The state of the query object. Can be dirty or clean. + * + * @var integer + */ + private $state = self::STATE_CLEAN; + + /** + * The index of the first result to retrieve. + * + * @var integer + */ + private $firstResult = null; + + /** + * The maximum number of results to retrieve. + * + * @var integer + */ + private $maxResults = null; + + /** + * The counter of bound parameters used with {@see bindValue). + * + * @var integer + */ + private $boundCounter = 0; + + /** + * Initializes a new QueryBuilder. + * + * @param \Doctrine\DBAL\Connection $connection The DBAL Connection. + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder + */ + public function expr() + { + return $this->connection->getExpressionBuilder(); + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->type; + } + + /** + * Gets the associated DBAL Connection for this query builder. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->state; + } + + /** + * Executes this query using the bound parameters and their types. + * + * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate} + * for insert, update and delete statements. + * + * @return \Doctrine\DBAL\Driver\Statement|int + */ + public function execute() + { + if ($this->type == self::SELECT) { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } else { + return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes); + } + } + + /** + * Gets the complete SQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * echo $qb->getSQL(); // SELECT u FROM User u + * + * + * @return string The SQL query string. + */ + public function getSQL() + { + if ($this->sql !== null && $this->state === self::STATE_CLEAN) { + return $this->sql; + } + + switch ($this->type) { + case self::INSERT: + $sql = $this->getSQLForInsert(); + break; + case self::DELETE: + $sql = $this->getSQLForDelete(); + break; + + case self::UPDATE: + $sql = $this->getSQLForUpdate(); + break; + + case self::SELECT: + default: + $sql = $this->getSQLForSelect(); + break; + } + + $this->state = self::STATE_CLEAN; + $this->sql = $sql; + + return $sql; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id') + * ->setParameter(':user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type One of the PDO::PARAM_* constants. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + if ($type !== null) { + $this->paramTypes[$key] = $type; + } + + $this->params[$key] = $value; + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(array( + * ':user_id1' => 1, + * ':user_id2' => 2 + * )); + * + * + * @param array $params The query parameters to set. + * @param array $types The query parameters types to set. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setParameters(array $params, array $types = array()) + { + $this->paramTypes = $types; + $this->params = $params; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameters indexed by parameter index or name. + */ + public function getParameters() + { + return $this->params; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return mixed The value of the bound parameter. + */ + public function getParameter($key) + { + return isset($this->params[$key]) ? $this->params[$key] : null; + } + + /** + * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. + * + * @return array The currently defined query parameter types indexed by parameter index or name. + */ + public function getParameterTypes() + { + return $this->paramTypes; + } + + /** + * Gets a (previously set) query parameter type of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter type. + * + * @return mixed The value of the bound parameter type. + */ + public function getParameterType($key) + { + return isset($this->paramTypes[$key]) ? $this->paramTypes[$key] : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->state = self::STATE_DIRTY; + $this->firstResult = $firstResult; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->state = self::STATE_DIRTY; + $this->maxResults = $maxResults; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer The maximum number of results. + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $sqlPartName + * @param string $sqlPart + * @param boolean $append + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function add($sqlPartName, $sqlPart, $append = false) + { + $isArray = is_array($sqlPart); + $isMultiple = is_array($this->sqlParts[$sqlPartName]); + + if ($isMultiple && !$isArray) { + $sqlPart = array($sqlPart); + } + + $this->state = self::STATE_DIRTY; + + if ($append) { + if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") { + foreach ($sqlPart as $part) { + $this->sqlParts[$sqlPartName][] = $part; + } + } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) { + $key = key($sqlPart); + $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; + } elseif ($isMultiple) { + $this->sqlParts[$sqlPartName][] = $sqlPart; + } else { + $this->sqlParts[$sqlPartName] = $sqlPart; + } + + return $this; + } + + $this->sqlParts[$sqlPartName] = $sqlPart; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id', 'p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expressions. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function select($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, false); + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->addSelect('p.id') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); + * + * + * @param mixed $select The selection expression. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', $selects, true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain table. + * + * + * $qb = $conn->createQueryBuilder() + * ->delete('users', 'u') + * ->where('u.id = :user_id'); + * ->setParameter(':user_id', 1); + * + * + * @param string $delete The table whose rows are subject to the deletion. + * @param string $alias The table alias used in the constructed query. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', array( + 'table' => $delete, + 'alias' => $alias + )); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The table whose rows are subject to the update. + * @param string $alias The table alias used in the constructed query. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', array( + 'table' => $update, + 'alias' => $alias + )); + } + + /** + * Turns the query being built into an insert query that inserts into + * a certain table + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param string $insert The table into which the rows should be inserted. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function insert($insert = null) + { + $this->type = self::INSERT; + + if ( ! $insert) { + return $this; + } + + return $this->add('from', array( + 'table' => $insert + )); + } + + /** + * Creates and adds a query root corresponding to the table identified by the + * given alias, forming a cartesian product with any existing query roots. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->from('users', 'u') + * + * + * @param string $from The table. + * @param string|null $alias The alias of the table. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias = null) + { + return $this->add('from', array( + 'table' => $from, + 'alias' => $alias + ), true); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function join($fromAlias, $join, $alias, $condition = null) + { + return $this->innerJoin($fromAlias, $join, $alias, $condition); + } + + /** + * Creates and adds a join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'inner', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Creates and adds a left join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'left', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Creates and adds a right join to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); + * + * + * @param string $fromAlias The alias that points to a from clause. + * @param string $join The table name to join. + * @param string $alias The alias of the join table. + * @param string $condition The condition for the join. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function rightJoin($fromAlias, $join, $alias, $condition = null) + { + return $this->add('join', array( + $fromAlias => array( + 'joinType' => 'right', + 'joinTable' => $join, + 'joinAlias' => $alias, + 'joinCondition' => $condition + ) + ), true); + } + + /** + * Sets a new value for a column in a bulk update query. + * + * + * $qb = $conn->createQueryBuilder() + * ->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The column to set. + * @param string $value The value, expression, placeholder, etc. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', $key .' = ' . $value, true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $conn->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('users', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression)) { + $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u') + * ->from('users', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function andWhere($where) + { + $args = func_get_args(); + $where = $this->getQueryPart('where'); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function orWhere($where) + { + $args = func_get_args(); + $where = $this->getQueryPart('where'); + + if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('where', $where, true); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.id'); + * + * + * @param mixed $groupBy The grouping expression. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, false); + } + + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.name') + * ->from('users', 'u') + * ->groupBy('u.lastLogin'); + * ->addGroupBy('u.createdAt') + * + * + * @param mixed $groupBy The grouping expression. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + if (empty($groupBy)) { + return $this; + } + + $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + + return $this->add('groupBy', $groupBy, true); + } + + /** + * Sets a value for a column in an insert query. + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?' + * ) + * ) + * ->setValue('password', '?'); + * + * + * @param string $column The column into which the value should be inserted. + * @param string $value The value that should be inserted into the column. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setValue($column, $value) + { + $this->sqlParts['values'][$column] = $value; + + return $this; + } + + /** + * Specifies values for an insert query indexed by column names. + * Replaces any previous values, if any. + * + * + * $qb = $conn->createQueryBuilder() + * ->insert('users') + * ->values( + * array( + * 'name' => '?', + * 'password' => '?' + * ) + * ); + * + * + * @param array $values The values to specify for the insert query indexed by column names. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function values(array $values) + { + return $this->add('values', $values); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) { + $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $args = func_get_args(); + $having = $this->getQueryPart('having'); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $args = func_get_args(); + $having = $this->getQueryPart('having'); + + if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false); + } + + /** + * Adds an ordering to the query results. + * + * @param string $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true); + } + + /** + * Gets a query part by its name. + * + * @param string $queryPartName + * + * @return mixed + */ + public function getQueryPart($queryPartName) + { + return $this->sqlParts[$queryPartName]; + } + + /** + * Gets all query parts. + * + * @return array + */ + public function getQueryParts() + { + return $this->sqlParts; + } + + /** + * Resets SQL parts. + * + * @param array|null $queryPartNames + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function resetQueryParts($queryPartNames = null) + { + if (is_null($queryPartNames)) { + $queryPartNames = array_keys($this->sqlParts); + } + + foreach ($queryPartNames as $queryPartName) { + $this->resetQueryPart($queryPartName); + } + + return $this; + } + + /** + * Resets a single SQL part. + * + * @param string $queryPartName + * + * @return \Doctrine\DBAL\Query\QueryBuilder This QueryBuilder instance. + */ + public function resetQueryPart($queryPartName) + { + $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) + ? array() : null; + + $this->state = self::STATE_DIRTY; + + return $this; + } + + /** + * @return string + * + * @throws \Doctrine\DBAL\Query\QueryException + */ + private function getSQLForSelect() + { + $query = 'SELECT ' . implode(', ', $this->sqlParts['select']) . ' FROM '; + + $query .= implode(', ', $this->getFromClauses()) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '') + . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '') + . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '') + . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : ''); + + if ($this->isLimitQuery()) { + return $this->connection->getDatabasePlatform()->modifyLimitQuery( + $query, + $this->maxResults, + $this->firstResult + ); + } + + return $query; + } + + /** + * @return string[] + */ + private function getFromClauses() + { + $fromClauses = array(); + $knownAliases = array(); + + // Loop through all FROM clauses + foreach ($this->sqlParts['from'] as $from) { + if ($from['alias'] === null) { + $tableSql = $from['table']; + $tableReference = $from['table']; + } else { + $tableSql = $from['table'] . ' ' . $from['alias']; + $tableReference = $from['alias']; + } + + $knownAliases[$tableReference] = true; + + $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases); + } + + $this->verifyAllAliasesAreKnown($knownAliases); + + return $fromClauses; + } + + /** + * @param array $knownAliases + * + * @throws QueryException + */ + private function verifyAllAliasesAreKnown(array $knownAliases) + { + foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + if ( ! isset($knownAliases[$fromAlias])) { + throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); + } + } + } + + /** + * @return bool + */ + private function isLimitQuery() + { + return $this->maxResults !== null || $this->firstResult !== null; + } + + /** + * Converts this instance into an INSERT string in SQL. + * + * @return string + */ + private function getSQLForInsert() + { + return 'INSERT INTO ' . $this->sqlParts['from']['table'] . + ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . + ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')'; + } + + /** + * Converts this instance into an UPDATE string in SQL. + * + * @return string + */ + private function getSQLForUpdate() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'UPDATE ' . $table + . ' SET ' . implode(", ", $this->sqlParts['set']) + . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Converts this instance into a DELETE string in SQL. + * + * @return string + */ + private function getSQLForDelete() + { + $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + + return $query; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final SQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getSQL(); + } + + /** + * Creates a new named parameter and bind the value $value to it. + * + * This method provides a shortcut for PDOStatement::bindValue + * when using prepared statements. + * + * The parameter $value specifies the value that you want to bind. If + * $placeholder is not provided bindValue() will automatically create a + * placeholder for you. An automatic placeholder will be of the name + * ':dcValue1', ':dcValue2' etc. + * + * For more information see {@link http://php.net/pdostatement-bindparam} + * + * Example: + * + * $value = 2; + * $q->eq( 'id', $q->bindValue( $value ) ); + * $stmt = $q->executeQuery(); // executed with 'id = 2' + * + * + * @license New BSD License + * @link http://www.zetacomponents.org + * + * @param mixed $value + * @param mixed $type + * @param string $placeHolder The name to bind with. The string must start with a colon ':'. + * + * @return string the placeholder name used. + */ + public function createNamedParameter($value, $type = \PDO::PARAM_STR, $placeHolder = null) + { + if ($placeHolder === null) { + $this->boundCounter++; + $placeHolder = ":dcValue" . $this->boundCounter; + } + $this->setParameter(substr($placeHolder, 1), $value, $type); + + return $placeHolder; + } + + /** + * Creates a new positional parameter and bind the given value to it. + * + * Attention: If you are using positional parameters with the query builder you have + * to be very careful to bind all parameters in the order they appear in the SQL + * statement , otherwise they get bound in the wrong order which can lead to serious + * bugs in your code. + * + * Example: + * + * $qb = $conn->createQueryBuilder(); + * $qb->select('u.*') + * ->from('users', 'u') + * ->where('u.username = ' . $qb->createPositionalParameter('Foo', PDO::PARAM_STR)) + * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', PDO::PARAM_STR)) + * + * + * @param mixed $value + * @param integer $type + * + * @return string + */ + public function createPositionalParameter($value, $type = \PDO::PARAM_STR) + { + $this->boundCounter++; + $this->setParameter($this->boundCounter, $value, $type); + + return "?"; + } + + /** + * @param string $fromAlias + * @param array $knownAliases + * + * @return string + */ + private function getSQLForJoins($fromAlias, array &$knownAliases) + { + $sql = ''; + + if (isset($this->sqlParts['join'][$fromAlias])) { + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + if (array_key_exists($join['joinAlias'], $knownAliases)) { + throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases)); + } + $sql .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string) $join['joinCondition']); + $knownAliases[$join['joinAlias']] = true; + } + + foreach ($this->sqlParts['join'][$fromAlias] as $join) { + $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); + } + } + + return $sql; + } + + /** + * Deep clone of all expression objects in the SQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->sqlParts as $part => $elements) { + if (is_array($this->sqlParts[$part])) { + foreach ($this->sqlParts[$part] as $idx => $element) { + if (is_object($element)) { + $this->sqlParts[$part][$idx] = clone $element; + } + } + } elseif (is_object($elements)) { + $this->sqlParts[$part] = clone $elements; + } + } + + foreach ($this->params as $name => $param) { + if (is_object($param)) { + $this->params[$name] = clone $param; + } + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php new file mode 100644 index 0000000..92c9336 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/QueryException.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Query; + +use Doctrine\DBAL\DBALException; + +/** + * @since 2.1.4 + */ +class QueryException extends DBALException +{ + /** + * @param string $alias + * @param array $registeredAliases + * + * @return \Doctrine\DBAL\Query\QueryException + */ + static public function unknownAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not part of " . + "any FROM or JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } + + /** + * @param string $alias + * @param array $registeredAliases + * + * @return \Doctrine\DBAL\Query\QueryException + */ + static public function nonUniqueAlias($alias, $registeredAliases) + { + return new self("The given alias '" . $alias . "' is not unique " . + "in FROM and JOIN clause table. The currently registered " . + "aliases are: " . implode(", ", $registeredAliases) . "."); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown b/vendor/doctrine/dbal/lib/Doctrine/DBAL/README.markdown new file mode 100644 index 0000000..e69de29 diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php new file mode 100644 index 0000000..a9037ff --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtils.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Utility class that parses sql statements with regard to types and parameters. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SQLParserUtils +{ + const POSITIONAL_TOKEN = '\?'; + const NAMED_TOKEN = '(? integer pair (indexed from zero) for a positional statement + * and a string => int[] pair for a named statement. + * + * @param string $statement + * @param boolean $isPositional + * + * @return array + */ + static public function getPlaceholderPositions($statement, $isPositional = true) + { + $match = ($isPositional) ? '?' : ':'; + if (strpos($statement, $match) === false) { + return array(); + } + + $token = ($isPositional) ? self::POSITIONAL_TOKEN : self::NAMED_TOKEN; + $paramMap = array(); + + foreach (self::getUnquotedStatementFragments($statement) as $fragment) { + preg_match_all("/$token/", $fragment[0], $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $placeholder) { + if ($isPositional) { + $paramMap[] = $placeholder[1] + $fragment[1]; + } else { + $pos = $placeholder[1] + $fragment[1]; + $paramMap[$pos] = substr($placeholder[0], 1, strlen($placeholder[0])); + } + } + } + + return $paramMap; + } + + /** + * For a positional query this method can rewrite the sql statement with regard to array parameters. + * + * @param string $query The SQL query to execute. + * @param array $params The parameters to bind to the query. + * @param array $types The types the previous parameters are in. + * + * @return array + * + * @throws SQLParserUtilsException + */ + static public function expandListParameters($query, $params, $types) + { + $isPositional = is_int(key($params)); + $arrayPositions = array(); + $bindIndex = -1; + + if ($isPositional) { + ksort($params); + ksort($types); + } + + foreach ($types as $name => $type) { + ++$bindIndex; + + if ($type !== Connection::PARAM_INT_ARRAY && $type !== Connection::PARAM_STR_ARRAY) { + continue; + } + + if ($isPositional) { + $name = $bindIndex; + } + + $arrayPositions[$name] = false; + } + + if (( ! $arrayPositions && $isPositional)) { + return array($query, $params, $types); + } + + $paramPos = self::getPlaceholderPositions($query, $isPositional); + + if ($isPositional) { + $paramOffset = 0; + $queryOffset = 0; + $params = array_values($params); + $types = array_values($types); + + foreach ($paramPos as $needle => $needlePos) { + if ( ! isset($arrayPositions[$needle])) { + continue; + } + + $needle += $paramOffset; + $needlePos += $queryOffset; + $count = count($params[$needle]); + + $params = array_merge( + array_slice($params, 0, $needle), + $params[$needle], + array_slice($params, $needle + 1) + ); + + $types = array_merge( + array_slice($types, 0, $needle), + $count ? + array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : // array needles are at PDO::PARAM_* + 100 + array(), + array_slice($types, $needle + 1) + ); + + $expandStr = $count ? implode(", ", array_fill(0, $count, "?")) : 'NULL'; + $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); + + $paramOffset += ($count - 1); // Grows larger by number of parameters minus the replaced needle. + $queryOffset += (strlen($expandStr) - 1); + } + + return array($query, $params, $types); + } + + $queryOffset = 0; + $typesOrd = array(); + $paramsOrd = array(); + + foreach ($paramPos as $pos => $paramName) { + $paramLen = strlen($paramName) + 1; + $value = static::extractParam($paramName, $params, true); + + if ( ! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) { + $pos += $queryOffset; + $queryOffset -= ($paramLen - 1); + $paramsOrd[] = $value; + $typesOrd[] = static::extractParam($paramName, $types, false, \PDO::PARAM_STR); + $query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen)); + + continue; + } + + $count = count($value); + $expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; + + foreach ($value as $val) { + $paramsOrd[] = $val; + $typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET; + } + + $pos += $queryOffset; + $queryOffset += (strlen($expandStr) - $paramLen); + $query = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen)); + } + + return array($query, $paramsOrd, $typesOrd); + } + + /** + * Slice the SQL statement around pairs of quotes and + * return string fragments of SQL outside of quoted literals. + * Each fragment is captured as a 2-element array: + * + * 0 => matched fragment string, + * 1 => offset of fragment in $statement + * + * @param string $statement + * @return array + */ + static private function getUnquotedStatementFragments($statement) + { + $literal = self::ESCAPED_SINGLE_QUOTED_TEXT . '|' . + self::ESCAPED_DOUBLE_QUOTED_TEXT . '|' . + self::ESCAPED_BACKTICK_QUOTED_TEXT . '|' . + self::ESCAPED_BRACKET_QUOTED_TEXT; + preg_match_all("/([^'\"`\[]+)(?:$literal)?/s", $statement, $fragments, PREG_OFFSET_CAPTURE); + + return $fragments[1]; + } + + /** + * @param string $paramName The name of the parameter (without a colon in front) + * @param array $paramsOrTypes A hash of parameters or types + * @param bool $isParam + * @param mixed $defaultValue An optional default value. If omitted, an exception is thrown + * + * @throws SQLParserUtilsException + * @return mixed + */ + static private function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null) + { + if (array_key_exists($paramName, $paramsOrTypes)) { + return $paramsOrTypes[$paramName]; + } + + // Hash keys can be prefixed with a colon for compatibility + if (array_key_exists(':' . $paramName, $paramsOrTypes)) { + return $paramsOrTypes[':' . $paramName]; + } + + if (null !== $defaultValue) { + return $defaultValue; + } + + if ($isParam) { + throw SQLParserUtilsException::missingParam($paramName); + } + + throw SQLParserUtilsException::missingType($paramName); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php new file mode 100644 index 0000000..25c7209 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/SQLParserUtilsException.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Doctrine\DBAL\ConnectionException + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.4 + * @author Lars Strojny + */ +class SQLParserUtilsException extends DBALException +{ + /** + * @param string $paramName + * + * @return \Doctrine\DBAL\SQLParserUtilsException + */ + public static function missingParam($paramName) + { + return new self(sprintf('Value for :%1$s not found in params array. Params array key should be "%1$s"', $paramName)); + } + + /** + * @param string $typeName + * + * @return \Doctrine\DBAL\SQLParserUtilsException + */ + public static function missingType($typeName) + { + return new self(sprintf('Value for :%1$s not found in types array. Types array key should be "%1$s"', $typeName)); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php new file mode 100644 index 0000000..051dce9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php @@ -0,0 +1,227 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * The abstract asset allows to reset the name of all assets without publishing this to the public userland. + * + * This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables + * array($tableName => Table($tableName)); if you want to rename the table, you have to make sure + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +abstract class AbstractAsset +{ + /** + * @var string + */ + protected $_name; + + /** + * Namespace of the asset. If none isset the default namespace is assumed. + * + * @var string|null + */ + protected $_namespace = null; + + /** + * @var boolean + */ + protected $_quoted = false; + + /** + * Sets the name of this asset. + * + * @param string $name + * + * @return void + */ + protected function _setName($name) + { + if ($this->isIdentifierQuoted($name)) { + $this->_quoted = true; + $name = $this->trimQuotes($name); + } + if (strpos($name, ".") !== false) { + $parts = explode(".", $name); + $this->_namespace = $parts[0]; + $name = $parts[1]; + } + $this->_name = $name; + } + + /** + * Is this asset in the default namespace? + * + * @param string $defaultNamespaceName + * + * @return boolean + */ + public function isInDefaultNamespace($defaultNamespaceName) + { + return $this->_namespace == $defaultNamespaceName || $this->_namespace === null; + } + + /** + * Gets the namespace name of this asset. + * + * If NULL is returned this means the default namespace is used. + * + * @return string|null + */ + public function getNamespaceName() + { + return $this->_namespace; + } + + /** + * The shortest name is stripped of the default namespace. All other + * namespaced elements are returned as full-qualified names. + * + * @param string $defaultNamespaceName + * + * @return string + */ + public function getShortestName($defaultNamespaceName) + { + $shortestName = $this->getName(); + if ($this->_namespace == $defaultNamespaceName) { + $shortestName = $this->_name; + } + + return strtolower($shortestName); + } + + /** + * The normalized name is full-qualified and lowerspaced. Lowerspacing is + * actually wrong, but we have to do it to keep our sanity. If you are + * using database objects that only differentiate in the casing (FOO vs + * Foo) then you will NOT be able to use Doctrine Schema abstraction. + * + * Every non-namespaced element is prefixed with the default namespace + * name which is passed as argument to this method. + * + * @param string $defaultNamespaceName + * + * @return string + */ + public function getFullQualifiedName($defaultNamespaceName) + { + $name = $this->getName(); + if ( ! $this->_namespace) { + $name = $defaultNamespaceName . "." . $name; + } + + return strtolower($name); + } + + /** + * Checks if this asset's name is quoted. + * + * @return boolean + */ + public function isQuoted() + { + return $this->_quoted; + } + + /** + * Checks if this identifier is quoted. + * + * @param string $identifier + * + * @return boolean + */ + protected function isIdentifierQuoted($identifier) + { + return (isset($identifier[0]) && ($identifier[0] == '`' || $identifier[0] == '"' || $identifier[0] == '[')); + } + + /** + * Trim quotes from the identifier. + * + * @param string $identifier + * + * @return string + */ + protected function trimQuotes($identifier) + { + return str_replace(array('`', '"', '[', ']'), '', $identifier); + } + + /** + * Returns the name of this schema asset. + * + * @return string + */ + public function getName() + { + if ($this->_namespace) { + return $this->_namespace . "." . $this->_name; + } + + return $this->_name; + } + + /** + * Gets the quoted representation of this asset but only if it was defined with one. Otherwise + * return the plain unquoted value as inserted. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedName(AbstractPlatform $platform) + { + $keywords = $platform->getReservedKeywordsList(); + $parts = explode(".", $this->getName()); + foreach ($parts as $k => $v) { + $parts[$k] = ($this->_quoted || $keywords->isKeyword($v)) ? $platform->quoteIdentifier($v) : $v; + } + + return implode(".", $parts); + } + + /** + * Generates an identifier from a list of column names obeying a certain string length. + * + * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, + * however building idents automatically for foreign keys, composite keys or such can easily create + * very long names. + * + * @param array $columnNames + * @param string $prefix + * @param integer $maxSize + * + * @return string + */ + protected function _generateIdentifierName($columnNames, $prefix='', $maxSize=30) + { + $hash = implode("", array_map(function ($column) { + return dechex(crc32($column)); + }, $columnNames)); + + return substr(strtoupper($prefix . "_" . $hash), 0, $maxSize); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php new file mode 100644 index 0000000..19dfa93 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -0,0 +1,1113 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; +use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Base class for schema managers. Schema managers are used to inspect and/or + * modify the database schema/structure. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Jonathan H. Wage + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractSchemaManager +{ + /** + * Holds instance of the Doctrine connection for this schema manager. + * + * @var \Doctrine\DBAL\Connection + */ + protected $_conn; + + /** + * Holds instance of the database platform used for this schema manager. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * Constructor. Accepts the Connection instance to manage the schema for. + * + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\DBAL\Platforms\AbstractPlatform|null $platform + */ + public function __construct(\Doctrine\DBAL\Connection $conn, AbstractPlatform $platform = null) + { + $this->_conn = $conn; + $this->_platform = $platform ?: $this->_conn->getDatabasePlatform(); + } + + /** + * Returns the associated platform. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + */ + public function getDatabasePlatform() + { + return $this->_platform; + } + + /** + * Tries any method on the schema manager. Normally a method throws an + * exception when your DBMS doesn't support it or if an error occurs. + * This method allows you to try and method on your SchemaManager + * instance and will return false if it does not work or is not supported. + * + * + * $result = $sm->tryMethod('dropView', 'view_name'); + * + * + * @return mixed + */ + public function tryMethod() + { + $args = func_get_args(); + $method = $args[0]; + unset($args[0]); + $args = array_values($args); + + try { + return call_user_func_array(array($this, $method), $args); + } catch (\Exception $e) { + return false; + } + } + + /** + * Lists the available databases for this connection. + * + * @return array + */ + public function listDatabases() + { + $sql = $this->_platform->getListDatabasesSQL(); + + $databases = $this->_conn->fetchAll($sql); + + return $this->_getPortableDatabasesList($databases); + } + + /** + * Returns a list of all namespaces in the current database. + * + * @return array + */ + public function listNamespaceNames() + { + $sql = $this->_platform->getListNamespacesSQL(); + + $namespaces = $this->_conn->fetchAll($sql); + + return $this->getPortableNamespacesList($namespaces); + } + + /** + * Lists the available sequences for this connection. + * + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\Sequence[] + */ + public function listSequences($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListSequencesSQL($database); + + $sequences = $this->_conn->fetchAll($sql); + + return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); + } + + /** + * Lists the columns for a given table. + * + * In contrast to other libraries and to the old version of Doctrine, + * this column definition does try to contain the 'primary' field for + * the reason that it is not portable accross different RDBMS. Use + * {@see listTableIndexes($tableName)} to retrieve the primary key + * of a table. We're a RDBMS specifies more details these are held + * in the platformDetails array. + * + * @param string $table The name of the table. + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\Column[] + */ + public function listTableColumns($table, $database = null) + { + if ( ! $database) { + $database = $this->_conn->getDatabase(); + } + + $sql = $this->_platform->getListTableColumnsSQL($table, $database); + + $tableColumns = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableColumnList($table, $database, $tableColumns); + } + + /** + * Lists the indexes for a given table returning an array of Index instances. + * + * Keys of the portable indexes list are all lower-cased. + * + * @param string $table The name of the table. + * + * @return \Doctrine\DBAL\Schema\Index[] + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + $tableIndexes = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * Returns true if all the given tables exist. + * + * @param array $tableNames + * + * @return boolean + */ + public function tablesExist($tableNames) + { + $tableNames = array_map('strtolower', (array) $tableNames); + + return count($tableNames) == count(\array_intersect($tableNames, array_map('strtolower', $this->listTableNames()))); + } + + /** + * Returns a list of all tables in the current database. + * + * @return array + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + + $tables = $this->_conn->fetchAll($sql); + $tableNames = $this->_getPortableTablesList($tables); + + return $this->filterAssetNames($tableNames); + } + + /** + * Filters asset names if they are configured to return only a subset of all + * the found elements. + * + * @param array $assetNames + * + * @return array + */ + protected function filterAssetNames($assetNames) + { + $filterExpr = $this->getFilterSchemaAssetsExpression(); + if ( ! $filterExpr) { + return $assetNames; + } + + return array_values( + array_filter($assetNames, function ($assetName) use ($filterExpr) { + $assetName = ($assetName instanceof AbstractAsset) ? $assetName->getName() : $assetName; + + return preg_match($filterExpr, $assetName); + }) + ); + } + + /** + * @return string|null + */ + protected function getFilterSchemaAssetsExpression() + { + return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression(); + } + + /** + * Lists the tables for this connection. + * + * @return \Doctrine\DBAL\Schema\Table[] + */ + public function listTables() + { + $tableNames = $this->listTableNames(); + + $tables = array(); + foreach ($tableNames as $tableName) { + $tables[] = $this->listTableDetails($tableName); + } + + return $tables; + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + */ + public function listTableDetails($tableName) + { + $columns = $this->listTableColumns($tableName); + $foreignKeys = array(); + if ($this->_platform->supportsForeignKeyConstraints()) { + $foreignKeys = $this->listTableForeignKeys($tableName); + } + $indexes = $this->listTableIndexes($tableName); + + return new Table($tableName, $columns, $indexes, $foreignKeys, false, array()); + } + + /** + * Lists the views this connection has. + * + * @return \Doctrine\DBAL\Schema\View[] + */ + public function listViews() + { + $database = $this->_conn->getDatabase(); + $sql = $this->_platform->getListViewsSQL($database); + $views = $this->_conn->fetchAll($sql); + + return $this->_getPortableViewsList($views); + } + + /** + * Lists the foreign keys for the given table. + * + * @param string $table The name of the table. + * @param string|null $database + * + * @return \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public function listTableForeignKeys($table, $database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /* drop*() Methods */ + + /** + * Drops a database. + * + * NOTE: You can not drop the database this SchemaManager is currently connected to. + * + * @param string $database The name of the database to drop. + * + * @return void + */ + public function dropDatabase($database) + { + $this->_execSql($this->_platform->getDropDatabaseSQL($database)); + } + + /** + * Drops the given table. + * + * @param string $tableName The name of the table to drop. + * + * @return void + */ + public function dropTable($tableName) + { + $this->_execSql($this->_platform->getDropTableSQL($tableName)); + } + + /** + * Drops the index from the given table. + * + * @param \Doctrine\DBAL\Schema\Index|string $index The name of the index. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table. + * + * @return void + */ + public function dropIndex($index, $table) + { + if ($index instanceof Index) { + $index = $index->getQuotedName($this->_platform); + } + + $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); + } + + /** + * Drops the constraint from the given table. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table. + * + * @return void + */ + public function dropConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); + } + + /** + * Drops a foreign key from a table. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint|string $foreignKey The name of the foreign key. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table with the foreign key. + * + * @return void + */ + public function dropForeignKey($foreignKey, $table) + { + $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table)); + } + + /** + * Drops a sequence with a given name. + * + * @param string $name The name of the sequence to drop. + * + * @return void + */ + public function dropSequence($name) + { + $this->_execSql($this->_platform->getDropSequenceSQL($name)); + } + + /** + * Drops a view. + * + * @param string $name The name of the view. + * + * @return void + */ + public function dropView($name) + { + $this->_execSql($this->_platform->getDropViewSQL($name)); + } + + /* create*() Methods */ + + /** + * Creates a new database. + * + * @param string $database The name of the database to create. + * + * @return void + */ + public function createDatabase($database) + { + $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); + } + + /** + * Creates a new table. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function createTable(Table $table) + { + $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS; + $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + } + + /** + * Creates a new sequence. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If something fails at database level. + */ + public function createSequence($sequence) + { + $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); + } + + /** + * Creates a constraint on a table. + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return void + */ + public function createConstraint(Constraint $constraint, $table) + { + $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); + } + + /** + * Creates a new index on a table. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return void + */ + public function createIndex(Index $index, $table) + { + $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); + } + + /** + * Creates a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey The ForeignKey instance. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return void + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); + } + + /** + * Creates a new view. + * + * @param \Doctrine\DBAL\Schema\View $view + * + * @return void + */ + public function createView(View $view) + { + $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); + } + + /* dropAndCreate*() Methods */ + + /** + * Drops and creates a constraint. + * + * @see dropConstraint() + * @see createConstraint() + * + * @param \Doctrine\DBAL\Schema\Constraint $constraint + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return void + */ + public function dropAndCreateConstraint(Constraint $constraint, $table) + { + $this->tryMethod('dropConstraint', $constraint, $table); + $this->createConstraint($constraint, $table); + } + + /** + * Drops and creates a new index on a table. + * + * @param \Doctrine\DBAL\Schema\Index $index + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the index is to be created. + * + * @return void + */ + public function dropAndCreateIndex(Index $index, $table) + { + $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); + $this->createIndex($index, $table); + } + + /** + * Drops and creates a new foreign key. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created. + * @param \Doctrine\DBAL\Schema\Table|string $table The name of the table on which the foreign key is to be created. + * + * @return void + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $this->tryMethod('dropForeignKey', $foreignKey, $table); + $this->createForeignKey($foreignKey, $table); + } + + /** + * Drops and create a new sequence. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\ConnectionException If something fails at database level. + */ + public function dropAndCreateSequence(Sequence $sequence) + { + $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); + $this->createSequence($sequence); + } + + /** + * Drops and creates a new table. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function dropAndCreateTable(Table $table) + { + $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); + $this->createTable($table); + } + + /** + * Drops and creates a new database. + * + * @param string $database The name of the database to create. + * + * @return void + */ + public function dropAndCreateDatabase($database) + { + $this->tryMethod('dropDatabase', $database); + $this->createDatabase($database); + } + + /** + * Drops and creates a new view. + * + * @param \Doctrine\DBAL\Schema\View $view + * + * @return void + */ + public function dropAndCreateView(View $view) + { + $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); + $this->createView($view); + } + + /* alterTable() Methods */ + + /** + * Alters an existing tables schema. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + * + * @return void + */ + public function alterTable(TableDiff $tableDiff) + { + $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (is_array($queries) && count($queries)) { + foreach ($queries as $ddlQuery) { + $this->_execSql($ddlQuery); + } + } + } + + /** + * Renames a given table to another name. + * + * @param string $name The current name of the table. + * @param string $newName The new name of the table. + * + * @return void + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * Methods for filtering return values of list*() methods to convert + * the native DBMS data definition to a portable Doctrine definition + */ + + /** + * @param array $databases + * + * @return array + */ + protected function _getPortableDatabasesList($databases) + { + $list = array(); + foreach ($databases as $value) { + if ($value = $this->_getPortableDatabaseDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. + * + * @param array $namespaces The list of namespace names in the native DBMS data definition. + * + * @return array + */ + protected function getPortableNamespacesList(array $namespaces) + { + $namespacesList = array(); + + foreach ($namespaces as $namespace) { + $namespacesList[] = $this->getPortableNamespaceDefinition($namespace); + } + + return $namespacesList; + } + + /** + * @param array $database + * + * @return mixed + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database; + } + + /** + * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. + * + * @param array $namespace The native DBMS namespace definition. + * + * @return mixed + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace; + } + + /** + * @param array $functions + * + * @return array + */ + protected function _getPortableFunctionsList($functions) + { + $list = array(); + foreach ($functions as $value) { + if ($value = $this->_getPortableFunctionDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $function + * + * @return mixed + */ + protected function _getPortableFunctionDefinition($function) + { + return $function; + } + + /** + * @param array $triggers + * + * @return array + */ + protected function _getPortableTriggersList($triggers) + { + $list = array(); + foreach ($triggers as $value) { + if ($value = $this->_getPortableTriggerDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $trigger + * + * @return mixed + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger; + } + + /** + * @param array $sequences + * + * @return array + */ + protected function _getPortableSequencesList($sequences) + { + $list = array(); + foreach ($sequences as $value) { + if ($value = $this->_getPortableSequenceDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $sequence + * + * @return \Doctrine\DBAL\Schema\Sequence + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function _getPortableSequenceDefinition($sequence) + { + throw DBALException::notSupported('Sequences'); + } + + /** + * Independent of the database the keys of the column list result are lowercased. + * + * The name of the created column instance however is kept in its case. + * + * @param string $table The name of the table. + * @param string $database + * @param array $tableColumns + * + * @return array + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $eventManager = $this->_platform->getEventManager(); + + $list = array(); + foreach ($tableColumns as $tableColumn) { + $column = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { + $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $column = $eventArgs->getColumn(); + } + + if ( ! $defaultPrevented) { + $column = $this->_getPortableTableColumnDefinition($tableColumn); + } + + if ($column) { + $name = strtolower($column->getQuotedName($this->_platform)); + $list[$name] = $column; + } + } + + return $list; + } + + /** + * Gets Table Column Definition. + * + * @param array $tableColumn + * + * @return \Doctrine\DBAL\Schema\Column + */ + abstract protected function _getPortableTableColumnDefinition($tableColumn); + + /** + * Aggregates and groups the index results according to the required data result. + * + * @param array $tableIndexRows + * @param string|null $tableName + * + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + $result = array(); + foreach ($tableIndexRows as $tableIndex) { + $indexName = $keyName = $tableIndex['key_name']; + if ($tableIndex['primary']) { + $keyName = 'primary'; + } + $keyName = strtolower($keyName); + + if (!isset($result[$keyName])) { + $result[$keyName] = array( + 'name' => $indexName, + 'columns' => array($tableIndex['column_name']), + 'unique' => $tableIndex['non_unique'] ? false : true, + 'primary' => $tableIndex['primary'], + 'flags' => isset($tableIndex['flags']) ? $tableIndex['flags'] : array(), + 'options' => isset($tableIndex['where']) ? array('where' => $tableIndex['where']) : array(), + ); + } else { + $result[$keyName]['columns'][] = $tableIndex['column_name']; + } + } + + $eventManager = $this->_platform->getEventManager(); + + $indexes = array(); + foreach ($result as $indexKey => $data) { + $index = null; + $defaultPrevented = false; + + if (null !== $eventManager && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { + $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); + $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); + + $defaultPrevented = $eventArgs->isDefaultPrevented(); + $index = $eventArgs->getIndex(); + } + + if ( ! $defaultPrevented) { + $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']); + } + + if ($index) { + $indexes[$indexKey] = $index; + } + } + + return $indexes; + } + + /** + * @param array $tables + * + * @return array + */ + protected function _getPortableTablesList($tables) + { + $list = array(); + foreach ($tables as $value) { + if ($value = $this->_getPortableTableDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $table + * + * @return array + */ + protected function _getPortableTableDefinition($table) + { + return $table; + } + + /** + * @param array $users + * + * @return array + */ + protected function _getPortableUsersList($users) + { + $list = array(); + foreach ($users as $value) { + if ($value = $this->_getPortableUserDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $user + * + * @return mixed + */ + protected function _getPortableUserDefinition($user) + { + return $user; + } + + /** + * @param array $views + * + * @return array + */ + protected function _getPortableViewsList($views) + { + $list = array(); + foreach ($views as $value) { + if ($view = $this->_getPortableViewDefinition($value)) { + $viewName = strtolower($view->getQuotedName($this->_platform)); + $list[$viewName] = $view; + } + } + + return $list; + } + + /** + * @param array $view + * + * @return mixed + */ + protected function _getPortableViewDefinition($view) + { + return false; + } + + /** + * @param array $tableForeignKeys + * + * @return array + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + if ($value = $this->_getPortableTableForeignKeyDefinition($value)) { + $list[] = $value; + } + } + + return $list; + } + + /** + * @param array $tableForeignKey + * + * @return mixed + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return $tableForeignKey; + } + + /** + * @param array|string $sql + * + * @return void + */ + protected function _execSql($sql) + { + foreach ((array) $sql as $query) { + $this->_conn->executeUpdate($query); + } + } + + /** + * Creates a schema instance for the current database. + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function createSchema() + { + $namespaces = array(); + + if ($this->_platform->supportsSchemas()) { + $namespaces = $this->listNamespaceNames(); + } + + $sequences = array(); + + if ($this->_platform->supportsSequences()) { + $sequences = $this->listSequences(); + } + + $tables = $this->listTables(); + + return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces); + } + + /** + * Creates the configuration for this schema. + * + * @return \Doctrine\DBAL\Schema\SchemaConfig + */ + public function createSchemaConfig() + { + $schemaConfig = new SchemaConfig(); + $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); + + $searchPaths = $this->getSchemaSearchPaths(); + if (isset($searchPaths[0])) { + $schemaConfig->setName($searchPaths[0]); + } + + $params = $this->_conn->getParams(); + if (isset($params['defaultTableOptions'])) { + $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); + } + + return $schemaConfig; + } + + /** + * The search path for namespaces in the currently connected database. + * + * The first entry is usually the default namespace in the Schema. All + * further namespaces contain tables/sequences which can also be addressed + * with a short, not full-qualified name. + * + * For databases that don't support subschema/namespaces this method + * returns the name of the currently connected database. + * + * @return array + */ + public function getSchemaSearchPaths() + { + return array($this->_conn->getDatabase()); + } + + /** + * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns + * the type given as default. + * + * @param string $comment + * @param string $currentType + * + * @return string + */ + public function extractDoctrineTypeFromComment($comment, $currentType) + { + if (preg_match("(\(DC2Type:([a-zA-Z0-9_]+)\))", $comment, $match)) { + $currentType = $match[1]; + } + + return $currentType; + } + + /** + * @param string $comment + * @param string $type + * + * @return string + */ + public function removeDoctrineTypeFromComment($comment, $type) + { + return str_replace('(DC2Type:'.$type.')', '', $comment); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php new file mode 100644 index 0000000..192ce1d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php @@ -0,0 +1,487 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Object representation of a database column. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Column extends AbstractAsset +{ + /** + * @var Type + */ + protected $_type; + + /** + * @var integer|null + */ + protected $_length = null; + + /** + * @var integer + */ + protected $_precision = 10; + + /** + * @var integer + */ + protected $_scale = 0; + + /** + * @var boolean + */ + protected $_unsigned = false; + + /** + * @var boolean + */ + protected $_fixed = false; + + /** + * @var boolean + */ + protected $_notnull = true; + + /** + * @var string|null + */ + protected $_default = null; + + /** + * @var boolean + */ + protected $_autoincrement = false; + + /** + * @var array + */ + protected $_platformOptions = array(); + + /** + * @var string|null + */ + protected $_columnDefinition = null; + + /** + * @var string|null + */ + protected $_comment = null; + + /** + * @var array + */ + protected $_customSchemaOptions = array(); + + /** + * Creates a new Column. + * + * @param string $columnName + * @param Type $type + * @param array $options + */ + public function __construct($columnName, Type $type, array $options=array()) + { + $this->_setName($columnName); + $this->setType($type); + $this->setOptions($options); + } + + /** + * @param array $options + * + * @return Column + */ + public function setOptions(array $options) + { + foreach ($options as $name => $value) { + $method = "set".$name; + if (method_exists($this, $method)) { + $this->$method($value); + } + } + + return $this; + } + + /** + * @param Type $type + * + * @return Column + */ + public function setType(Type $type) + { + $this->_type = $type; + + return $this; + } + + /** + * @param integer|null $length + * + * @return Column + */ + public function setLength($length) + { + if ($length !== null) { + $this->_length = (int) $length; + } else { + $this->_length = null; + } + + return $this; + } + + /** + * @param integer $precision + * + * @return Column + */ + public function setPrecision($precision) + { + if (!is_numeric($precision)) { + $precision = 10; // defaults to 10 when no valid precision is given. + } + + $this->_precision = (int) $precision; + + return $this; + } + + /** + * @param integer $scale + * + * @return Column + */ + public function setScale($scale) + { + if (!is_numeric($scale)) { + $scale = 0; + } + + $this->_scale = (int) $scale; + + return $this; + } + + /** + * @param boolean $unsigned + * + * @return Column + */ + public function setUnsigned($unsigned) + { + $this->_unsigned = (bool) $unsigned; + + return $this; + } + + /** + * @param boolean $fixed + * + * @return Column + */ + public function setFixed($fixed) + { + $this->_fixed = (bool) $fixed; + + return $this; + } + + /** + * @param boolean $notnull + * + * @return Column + */ + public function setNotnull($notnull) + { + $this->_notnull = (bool) $notnull; + + return $this; + } + + /** + * @param mixed $default + * + * @return Column + */ + public function setDefault($default) + { + $this->_default = $default; + + return $this; + } + + /** + * @param array $platformOptions + * + * @return Column + */ + public function setPlatformOptions(array $platformOptions) + { + $this->_platformOptions = $platformOptions; + + return $this; + } + + /** + * @param string $name + * @param mixed $value + * + * @return Column + */ + public function setPlatformOption($name, $value) + { + $this->_platformOptions[$name] = $value; + + return $this; + } + + /** + * @param string $value + * + * @return Column + */ + public function setColumnDefinition($value) + { + $this->_columnDefinition = $value; + + return $this; + } + + /** + * @return Type + */ + public function getType() + { + return $this->_type; + } + + /** + * @return integer|null + */ + public function getLength() + { + return $this->_length; + } + + /** + * @return integer + */ + public function getPrecision() + { + return $this->_precision; + } + + /** + * @return integer + */ + public function getScale() + { + return $this->_scale; + } + + /** + * @return boolean + */ + public function getUnsigned() + { + return $this->_unsigned; + } + + /** + * @return boolean + */ + public function getFixed() + { + return $this->_fixed; + } + + /** + * @return boolean + */ + public function getNotnull() + { + return $this->_notnull; + } + + /** + * @return string|null + */ + public function getDefault() + { + return $this->_default; + } + + /** + * @return array + */ + public function getPlatformOptions() + { + return $this->_platformOptions; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasPlatformOption($name) + { + return isset($this->_platformOptions[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getPlatformOption($name) + { + return $this->_platformOptions[$name]; + } + + /** + * @return string|null + */ + public function getColumnDefinition() + { + return $this->_columnDefinition; + } + + /** + * @return boolean + */ + public function getAutoincrement() + { + return $this->_autoincrement; + } + + /** + * @param boolean $flag + * + * @return Column + */ + public function setAutoincrement($flag) + { + $this->_autoincrement = $flag; + + return $this; + } + + /** + * @param string $comment + * + * @return Column + */ + public function setComment($comment) + { + $this->_comment = $comment; + + return $this; + } + + /** + * @return string|null + */ + public function getComment() + { + return $this->_comment; + } + + /** + * @param string $name + * @param mixed $value + * + * @return Column + */ + public function setCustomSchemaOption($name, $value) + { + $this->_customSchemaOptions[$name] = $value; + + return $this; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasCustomSchemaOption($name) + { + return isset($this->_customSchemaOptions[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getCustomSchemaOption($name) + { + return $this->_customSchemaOptions[$name]; + } + + /** + * @param array $customSchemaOptions + * + * @return Column + */ + public function setCustomSchemaOptions(array $customSchemaOptions) + { + $this->_customSchemaOptions = $customSchemaOptions; + + return $this; + } + + /** + * @return array + */ + public function getCustomSchemaOptions() + { + return $this->_customSchemaOptions; + } + + /** + * @return array + */ + public function toArray() + { + return array_merge(array( + 'name' => $this->_name, + 'type' => $this->_type, + 'default' => $this->_default, + 'notnull' => $this->_notnull, + 'length' => $this->_length, + 'precision' => $this->_precision, + 'scale' => $this->_scale, + 'fixed' => $this->_fixed, + 'unsigned' => $this->_unsigned, + 'autoincrement' => $this->_autoincrement, + 'columnDefinition' => $this->_columnDefinition, + 'comment' => $this->_comment, + ), $this->_platformOptions, $this->_customSchemaOptions); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php new file mode 100644 index 0000000..c993c9b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ColumnDiff.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Represents the change of a column. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class ColumnDiff +{ + /** + * @var string + */ + public $oldColumnName; + + /** + * @var Column + */ + public $column; + + /** + * @var array + */ + public $changedProperties = array(); + + /** + * @var Column + */ + public $fromColumn; + + /** + * @param string $oldColumnName + * @param Column $column + * @param string[] $changedProperties + * @param Column $fromColumn + */ + public function __construct($oldColumnName, Column $column, array $changedProperties = array(), Column $fromColumn = null) + { + $this->oldColumnName = $oldColumnName; + $this->column = $column; + $this->changedProperties = $changedProperties; + $this->fromColumn = $fromColumn; + } + + /** + * @param string $propertyName + * + * @return boolean + */ + public function hasChanged($propertyName) + { + return in_array($propertyName, $this->changedProperties); + } + + /** + * @return Identifier + */ + public function getOldColumnName() + { + return new Identifier($this->oldColumnName); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php new file mode 100644 index 0000000..4f11e6c --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php @@ -0,0 +1,515 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types; + +/** + * Compares two Schemas and return an instance of SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Comparator +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + static public function compareSchemas(Schema $fromSchema, Schema $toSchema) + { + $c = new self(); + + return $c->compare($fromSchema, $toSchema); + } + + /** + * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. + * + * The returned differences are returned in such a way that they contain the + * operations to change the schema stored in $fromSchema to the schema that is + * stored in $toSchema. + * + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + public function compare(Schema $fromSchema, Schema $toSchema) + { + $diff = new SchemaDiff(); + $diff->fromSchema = $fromSchema; + + $foreignKeysToTable = array(); + + foreach ($toSchema->getNamespaces() as $namespace) { + if ( ! $fromSchema->hasNamespace($namespace)) { + $diff->newNamespaces[$namespace] = $namespace; + } + } + + foreach ($fromSchema->getNamespaces() as $namespace) { + if ( ! $toSchema->hasNamespace($namespace)) { + $diff->removedNamespaces[$namespace] = $namespace; + } + } + + foreach ($toSchema->getTables() as $table) { + $tableName = $table->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasTable($tableName)) { + $diff->newTables[$tableName] = $toSchema->getTable($tableName); + } else { + $tableDifferences = $this->diffTable($fromSchema->getTable($tableName), $toSchema->getTable($tableName)); + if ($tableDifferences !== false) { + $diff->changedTables[$tableName] = $tableDifferences; + } + } + } + + /* Check if there are tables removed */ + foreach ($fromSchema->getTables() as $table) { + $tableName = $table->getShortestName($fromSchema->getName()); + + $table = $fromSchema->getTable($tableName); + if ( ! $toSchema->hasTable($tableName)) { + $diff->removedTables[$tableName] = $table; + } + + // also remember all foreign keys that point to a specific table + foreach ($table->getForeignKeys() as $foreignKey) { + $foreignTable = strtolower($foreignKey->getForeignTableName()); + if (!isset($foreignKeysToTable[$foreignTable])) { + $foreignKeysToTable[$foreignTable] = array(); + } + $foreignKeysToTable[$foreignTable][] = $foreignKey; + } + } + + foreach ($diff->removedTables as $tableName => $table) { + if (isset($foreignKeysToTable[$tableName])) { + $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]); + + // deleting duplicated foreign keys present on both on the orphanedForeignKey + // and the removedForeignKeys from changedTables + foreach ($foreignKeysToTable[$tableName] as $foreignKey) { + // strtolower the table name to make if compatible with getShortestName + $localTableName = strtolower($foreignKey->getLocalTableName()); + if (isset($diff->changedTables[$localTableName])) { + foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) { + unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]); + } + } + } + } + } + + foreach ($toSchema->getSequences() as $sequence) { + $sequenceName = $sequence->getShortestName($toSchema->getName()); + if ( ! $fromSchema->hasSequence($sequenceName)) { + if ( ! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { + $diff->newSequences[] = $sequence; + } + } else { + if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { + $diff->changedSequences[] = $toSchema->getSequence($sequenceName); + } + } + } + + foreach ($fromSchema->getSequences() as $sequence) { + if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { + continue; + } + + $sequenceName = $sequence->getShortestName($fromSchema->getName()); + + if ( ! $toSchema->hasSequence($sequenceName)) { + $diff->removedSequences[] = $sequence; + } + } + + return $diff; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return boolean + */ + private function isAutoIncrementSequenceInSchema($schema, $sequence) + { + foreach ($schema->getTables() as $table) { + if ($sequence->isAutoIncrementsFor($table)) { + return true; + } + } + + return false; + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence1 + * @param \Doctrine\DBAL\Schema\Sequence $sequence2 + * + * @return boolean + */ + public function diffSequence(Sequence $sequence1, Sequence $sequence2) + { + if ($sequence1->getAllocationSize() != $sequence2->getAllocationSize()) { + return true; + } + + if ($sequence1->getInitialValue() != $sequence2->getInitialValue()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the tables $table1 and $table2. + * + * If there are no differences this method returns the boolean false. + * + * @param \Doctrine\DBAL\Schema\Table $table1 + * @param \Doctrine\DBAL\Schema\Table $table2 + * + * @return boolean|\Doctrine\DBAL\Schema\TableDiff + */ + public function diffTable(Table $table1, Table $table2) + { + $changes = 0; + $tableDifferences = new TableDiff($table1->getName()); + $tableDifferences->fromTable = $table1; + + $table1Columns = $table1->getColumns(); + $table2Columns = $table2->getColumns(); + + /* See if all the fields in table 1 exist in table 2 */ + foreach ($table2Columns as $columnName => $column) { + if ( !$table1->hasColumn($columnName)) { + $tableDifferences->addedColumns[$columnName] = $column; + $changes++; + } + } + /* See if there are any removed fields in table 2 */ + foreach ($table1Columns as $columnName => $column) { + // See if column is removed in table 2. + if ( ! $table2->hasColumn($columnName)) { + $tableDifferences->removedColumns[$columnName] = $column; + $changes++; + continue; + } + + // See if column has changed properties in table 2. + $changedProperties = $this->diffColumn($column, $table2->getColumn($columnName)); + + if ( ! empty($changedProperties)) { + $columnDiff = new ColumnDiff($column->getName(), $table2->getColumn($columnName), $changedProperties); + $columnDiff->fromColumn = $column; + $tableDifferences->changedColumns[$column->getName()] = $columnDiff; + $changes++; + } + } + + $this->detectColumnRenamings($tableDifferences); + + $table1Indexes = $table1->getIndexes(); + $table2Indexes = $table2->getIndexes(); + + /* See if all the indexes in table 1 exist in table 2 */ + foreach ($table2Indexes as $indexName => $index) { + if (($index->isPrimary() && $table1->hasPrimaryKey()) || $table1->hasIndex($indexName)) { + continue; + } + + $tableDifferences->addedIndexes[$indexName] = $index; + $changes++; + } + /* See if there are any removed indexes in table 2 */ + foreach ($table1Indexes as $indexName => $index) { + // See if index is removed in table 2. + if (($index->isPrimary() && ! $table2->hasPrimaryKey()) || + ! $index->isPrimary() && ! $table2->hasIndex($indexName) + ) { + $tableDifferences->removedIndexes[$indexName] = $index; + $changes++; + continue; + } + + // See if index has changed in table 2. + $table2Index = $index->isPrimary() ? $table2->getPrimaryKey() : $table2->getIndex($indexName); + + if ($this->diffIndex($index, $table2Index)) { + $tableDifferences->changedIndexes[$indexName] = $table2Index; + $changes++; + } + } + + $this->detectIndexRenamings($tableDifferences); + + $fromFkeys = $table1->getForeignKeys(); + $toFkeys = $table2->getForeignKeys(); + + foreach ($fromFkeys as $key1 => $constraint1) { + foreach ($toFkeys as $key2 => $constraint2) { + if ($this->diffForeignKey($constraint1, $constraint2) === false) { + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } else { + if (strtolower($constraint1->getName()) == strtolower($constraint2->getName())) { + $tableDifferences->changedForeignKeys[] = $constraint2; + $changes++; + unset($fromFkeys[$key1]); + unset($toFkeys[$key2]); + } + } + } + } + + foreach ($fromFkeys as $constraint1) { + $tableDifferences->removedForeignKeys[] = $constraint1; + $changes++; + } + + foreach ($toFkeys as $constraint2) { + $tableDifferences->addedForeignKeys[] = $constraint2; + $changes++; + } + + return $changes ? $tableDifferences : false; + } + + /** + * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences + * + * @return void + */ + private function detectColumnRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + foreach ($tableDifferences->addedColumns as $addedColumnName => $addedColumn) { + foreach ($tableDifferences->removedColumns as $removedColumn) { + if (count($this->diffColumn($addedColumn, $removedColumn)) == 0) { + $renameCandidates[$addedColumn->getName()][] = array($removedColumn, $addedColumn, $addedColumnName); + } + } + } + + foreach ($renameCandidates as $candidateColumns) { + if (count($candidateColumns) == 1) { + list($removedColumn, $addedColumn) = $candidateColumns[0]; + $removedColumnName = strtolower($removedColumn->getName()); + $addedColumnName = strtolower($addedColumn->getName()); + + if ( ! isset($tableDifferences->renamedColumns[$removedColumnName])) { + $tableDifferences->renamedColumns[$removedColumnName] = $addedColumn; + unset($tableDifferences->addedColumns[$addedColumnName]); + unset($tableDifferences->removedColumns[$removedColumnName]); + } + } + } + } + + /** + * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param \Doctrine\DBAL\Schema\TableDiff $tableDifferences + * + * @return void + */ + private function detectIndexRenamings(TableDiff $tableDifferences) + { + $renameCandidates = array(); + + // Gather possible rename candidates by comparing each added and removed index based on semantics. + foreach ($tableDifferences->addedIndexes as $addedIndexName => $addedIndex) { + foreach ($tableDifferences->removedIndexes as $removedIndex) { + if (! $this->diffIndex($addedIndex, $removedIndex)) { + $renameCandidates[$addedIndex->getName()][] = array($removedIndex, $addedIndex, $addedIndexName); + } + } + } + + foreach ($renameCandidates as $candidateIndexes) { + // If the current rename candidate contains exactly one semantically equal index, + // we can safely rename it. + // Otherwise it is unclear if a rename action is really intended, + // therefore we let those ambiguous indexes be added/dropped. + if (count($candidateIndexes) === 1) { + list($removedIndex, $addedIndex) = $candidateIndexes[0]; + + $removedIndexName = strtolower($removedIndex->getName()); + $addedIndexName = strtolower($addedIndex->getName()); + + if (! isset($tableDifferences->renamedIndexes[$removedIndexName])) { + $tableDifferences->renamedIndexes[$removedIndexName] = $addedIndex; + unset($tableDifferences->addedIndexes[$addedIndexName]); + unset($tableDifferences->removedIndexes[$removedIndexName]); + } + } + } + } + + /** + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key1 + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $key2 + * + * @return boolean + */ + public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + { + if (array_map('strtolower', $key1->getUnquotedLocalColumns()) != array_map('strtolower', $key2->getUnquotedLocalColumns())) { + return true; + } + + if (array_map('strtolower', $key1->getUnquotedForeignColumns()) != array_map('strtolower', $key2->getUnquotedForeignColumns())) { + return true; + } + + if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { + return true; + } + + if ($key1->onUpdate() != $key2->onUpdate()) { + return true; + } + + if ($key1->onDelete() != $key2->onDelete()) { + return true; + } + + return false; + } + + /** + * Returns the difference between the fields $field1 and $field2. + * + * If there are differences this method returns $field2, otherwise the + * boolean false. + * + * @param \Doctrine\DBAL\Schema\Column $column1 + * @param \Doctrine\DBAL\Schema\Column $column2 + * + * @return array + */ + public function diffColumn(Column $column1, Column $column2) + { + $properties1 = $column1->toArray(); + $properties2 = $column2->toArray(); + + $changedProperties = array(); + + foreach (array('type', 'notnull', 'unsigned', 'autoincrement') as $property) { + if ($properties1[$property] != $properties2[$property]) { + $changedProperties[] = $property; + } + } + + if ($properties1['default'] != $properties2['default'] || + // Null values need to be checked additionally as they tell whether to create or drop a default value. + // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. + (null === $properties1['default'] && null !== $properties2['default']) || + (null === $properties2['default'] && null !== $properties1['default']) + ) { + $changedProperties[] = 'default'; + } + + if (($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) || + $properties1['type'] instanceof Types\BinaryType + ) { + // check if value of length is set at all, default value assumed otherwise. + $length1 = $properties1['length'] ?: 255; + $length2 = $properties2['length'] ?: 255; + if ($length1 != $length2) { + $changedProperties[] = 'length'; + } + + if ($properties1['fixed'] != $properties2['fixed']) { + $changedProperties[] = 'fixed'; + } + } elseif ($properties1['type'] instanceof Types\DecimalType) { + if (($properties1['precision'] ?: 10) != ($properties2['precision'] ?: 10)) { + $changedProperties[] = 'precision'; + } + if ($properties1['scale'] != $properties2['scale']) { + $changedProperties[] = 'scale'; + } + } + + // A null value and an empty string are actually equal for a comment so they should not trigger a change. + if ($properties1['comment'] !== $properties2['comment'] && + ! (null === $properties1['comment'] && '' === $properties2['comment']) && + ! (null === $properties2['comment'] && '' === $properties1['comment']) + ) { + $changedProperties[] = 'comment'; + } + + $customOptions1 = $column1->getCustomSchemaOptions(); + $customOptions2 = $column2->getCustomSchemaOptions(); + + foreach (array_merge(array_keys($customOptions1), array_keys($customOptions2)) as $key) { + if ( ! array_key_exists($key, $properties1) || ! array_key_exists($key, $properties2)) { + $changedProperties[] = $key; + } elseif ($properties1[$key] !== $properties2[$key]) { + $changedProperties[] = $key; + } + } + + $platformOptions1 = $column1->getPlatformOptions(); + $platformOptions2 = $column2->getPlatformOptions(); + + foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) { + if ($properties1[$key] !== $properties2[$key]) { + $changedProperties[] = $key; + } + } + + return array_unique($changedProperties); + } + + /** + * Finds the difference between the indexes $index1 and $index2. + * + * Compares $index1 with $index2 and returns $index2 if there are any + * differences or false in case there are no differences. + * + * @param \Doctrine\DBAL\Schema\Index $index1 + * @param \Doctrine\DBAL\Schema\Index $index2 + * + * @return boolean + */ + public function diffIndex(Index $index1, Index $index2) + { + if ($index1->isFullfilledBy($index2) && $index2->isFullfilledBy($index1)) { + return false; + } + + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php new file mode 100644 index 0000000..c373472 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Marker interface for contraints. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +interface Constraint +{ + /** + * @return string + */ + public function getName(); + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedName(AbstractPlatform $platform); + + /** + * Returns the names of the referencing table columns + * the constraint is associated with. + * + * @return array + */ + public function getColumns(); + + /** + * Returns the quoted representation of the column names + * the constraint is associated with. + * + * But only if they were defined with one or a column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedColumns(AbstractPlatform $platform); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php new file mode 100644 index 0000000..ff914c6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -0,0 +1,210 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * IBM Db2 Schema Manager. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class DB2SchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * Apparently creator is the schema not the user who created it: + * {@link http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.sqlref/db2z_sysibmsystablestable.htm} + */ + public function listTableNames() + { + $sql = $this->_platform->getListTablesSQL(); + $sql .= " AND CREATOR = UPPER('".$this->_conn->getUsername()."')"; + + $tables = $this->_conn->fetchAll($sql); + + return $this->_getPortableTablesList($tables); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, \CASE_LOWER); + + $length = null; + $fixed = null; + $unsigned = false; + $scale = false; + $precision = false; + + $default = null; + + if (null !== $tableColumn['default'] && 'NULL' != $tableColumn['default']) { + $default = trim($tableColumn['default'], "'"); + } + + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); + + switch (strtolower($tableColumn['typename'])) { + case 'varchar': + $length = $tableColumn['length']; + $fixed = false; + break; + case 'character': + $length = $tableColumn['length']; + $fixed = true; + break; + case 'clob': + $length = $tableColumn['length']; + break; + case 'decimal': + case 'double': + case 'real': + $scale = $tableColumn['scale']; + $precision = $tableColumn['length']; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => (bool) $fixed, + 'default' => $default, + 'autoincrement' => (boolean) $tableColumn['autoincrement'], + 'notnull' => (bool) ($tableColumn['nulls'] == 'N'), + 'scale' => null, + 'precision' => null, + 'platformOptions' => array(), + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + return new Column($tableColumn['colname'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTablesList($tables) + { + $tableNames = array(); + foreach ($tables as $tableRow) { + $tableRow = array_change_key_case($tableRow, \CASE_LOWER); + $tableNames[] = $tableRow['name']; + } + + return $tableNames; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null) + { + foreach ($tableIndexRows as &$tableIndexRow) { + $tableIndexRow = array_change_key_case($tableIndexRow, \CASE_LOWER); + $tableIndexRow['primary'] = (boolean) $tableIndexRow['primary']; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + $tableForeignKey = array_change_key_case($tableForeignKey, \CASE_LOWER); + + if (!isset($foreignKeys[$tableForeignKey['index_name']])) { + $foreignKeys[$tableForeignKey['index_name']] = array( + 'local_columns' => array($tableForeignKey['local_column']), + 'foreign_table' => $tableForeignKey['foreign_table'], + 'foreign_columns' => array($tableForeignKey['foreign_column']), + 'name' => $tableForeignKey['index_name'], + 'options' => array( + 'onUpdate' => $tableForeignKey['on_update'], + 'onDelete' => $tableForeignKey['on_delete'], + ) + ); + } else { + $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; + $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableForeignKeyRuleDef($def) + { + if ($def == "C") { + return "CASCADE"; + } elseif ($def == "N") { + return "SET NULL"; + } + + return null; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + $view = array_change_key_case($view, \CASE_LOWER); + // sadly this still segfaults on PDO_IBM, see http://pecl.php.net/bugs/bug.php?id=17199 + //$view['text'] = (is_resource($view['text']) ? stream_get_contents($view['text']) : $view['text']); + if (!is_resource($view['text'])) { + $pos = strpos($view['text'], ' AS '); + $sql = substr($view['text'], $pos+4); + } else { + $sql = ''; + } + + return new View($view['name'], $sql); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php new file mode 100644 index 0000000..e9503c3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Schema manager for the Drizzle RDBMS. + * + * @author Kim Hemsø Rasmussen + */ +class DrizzleSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtolower($tableColumn['DATA_TYPE']); + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + $tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); + + $options = array( + 'notnull' => !(bool) $tableColumn['IS_NULLABLE'], + 'length' => (int) $tableColumn['CHARACTER_MAXIMUM_LENGTH'], + 'default' => isset($tableColumn['COLUMN_DEFAULT']) ? $tableColumn['COLUMN_DEFAULT'] : null, + 'autoincrement' => (bool) $tableColumn['IS_AUTO_INCREMENT'], + 'scale' => (int) $tableColumn['NUMERIC_SCALE'], + 'precision' => (int) $tableColumn['NUMERIC_PRECISION'], + 'comment' => isset($tableColumn['COLUMN_COMMENT']) && '' !== $tableColumn['COLUMN_COMMENT'] + ? $tableColumn['COLUMN_COMMENT'] + : null, + ); + + $column = new Column($tableColumn['COLUMN_NAME'], Type::getType($type), $options); + + if ( ! empty($tableColumn['COLLATION_NAME'])) { + $column->setPlatformOption('collation', $tableColumn['COLLATION_NAME']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['SCHEMA_NAME']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['TABLE_NAME']; + } + + /** + * {@inheritdoc} + */ + public function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $columns = array(); + foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { + $columns[] = trim($value, ' `'); + } + + $refColumns = array(); + foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { + $refColumns[] = trim($value, ' `'); + } + + return new ForeignKeyConstraint( + $columns, + $tableForeignKey['REFERENCED_TABLE_NAME'], + $refColumns, + $tableForeignKey['CONSTRAINT_NAME'], + array( + 'onUpdate' => $tableForeignKey['UPDATE_RULE'], + 'onDelete' => $tableForeignKey['DELETE_RULE'], + ) + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + { + $indexes = array(); + foreach ($tableIndexes as $k) { + $k['primary'] = (boolean) $k['primary']; + $indexes[] = $k; + } + + return parent::_getPortableTableIndexesList($indexes, $tableName); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php new file mode 100644 index 0000000..6070eb2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php @@ -0,0 +1,389 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * An abstraction class for a foreign key constraint. + * + * @author Benjamin Eberlei + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.0 + */ +class ForeignKeyConstraint extends AbstractAsset implements Constraint +{ + /** + * Instance of the referencing table the foreign key constraint is associated with. + * + * @var \Doctrine\DBAL\Schema\Table + */ + protected $_localTable; + + /** + * Asset identifier instances of the referencing table column names the foreign key constraint is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_localColumnNames; + + /** + * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. + * + * @var Table|Identifier + */ + protected $_foreignTableName; + + /** + * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_foreignColumnNames; + + /** + * @var array Options associated with the foreign key constraint. + */ + protected $_options; + + /** + * Initializes the foreign key constraint. + * + * @param array $localColumnNames Names of the referencing table columns. + * @param Table|string $foreignTableName Referenced table. + * @param array $foreignColumnNames Names of the referenced table columns. + * @param string|null $name Name of the foreign key constraint. + * @param array $options Options associated with the foreign key constraint. + */ + public function __construct(array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = array()) + { + $this->_setName($name); + $identifierConstructorCallback = function ($column) { + return new Identifier($column); + }; + $this->_localColumnNames = $localColumnNames + ? array_combine($localColumnNames, array_map($identifierConstructorCallback, $localColumnNames)) + : array(); + + if ($foreignTableName instanceof Table) { + $this->_foreignTableName = $foreignTableName; + } else { + $this->_foreignTableName = new Identifier($foreignTableName); + } + + $this->_foreignColumnNames = $foreignColumnNames + ? array_combine($foreignColumnNames, array_map($identifierConstructorCallback, $foreignColumnNames)) + : array(); + $this->_options = $options; + } + + /** + * Returns the name of the referencing table + * the foreign key constraint is associated with. + * + * @return string + */ + public function getLocalTableName() + { + return $this->_localTable->getName(); + } + + /** + * Sets the Table instance of the referencing table + * the foreign key constraint is associated with. + * + * @param \Doctrine\DBAL\Schema\Table $table Instance of the referencing table. + * + * @return void + */ + public function setLocalTable(Table $table) + { + $this->_localTable = $table; + } + + /** + * @return Table + */ + public function getLocalTable() + { + return $this->_localTable; + } + + /** + * Returns the names of the referencing table columns + * the foreign key constraint is associated with. + * + * @return array + */ + public function getLocalColumns() + { + return array_keys($this->_localColumnNames); + } + + /** + * Returns the quoted representation of the referencing table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referencing table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedLocalColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_localColumnNames as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * Returns unquoted representation of local table column names for comparison with other FK + * + * @return array + */ + public function getUnquotedLocalColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getLocalColumns()); + } + + /** + * Returns unquoted representation of foreign table column names for comparison with other FK + * + * @return array + */ + public function getUnquotedForeignColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getForeignColumns()); + } + + /** + * {@inheritdoc} + * + * @see getLocalColumns + */ + public function getColumns() + { + return $this->getLocalColumns(); + } + + /** + * Returns the quoted representation of the referencing table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referencing table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @see getQuotedLocalColumns + * + * @return array + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + return $this->getQuotedLocalColumns($platform); + } + + /** + * Returns the name of the referenced table + * the foreign key constraint is associated with. + * + * @return string + */ + public function getForeignTableName() + { + return $this->_foreignTableName->getName(); + } + + /** + * Returns the non-schema qualified foreign table name. + * + * @return string + */ + public function getUnqualifiedForeignTableName() + { + $parts = explode(".", $this->_foreignTableName->getName()); + + return strtolower(end($parts)); + } + + /** + * Returns the quoted representation of the referenced table name + * the foreign key constraint is associated with. + * + * But only if it was defined with one or the referenced table name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return string + */ + public function getQuotedForeignTableName(AbstractPlatform $platform) + { + return $this->_foreignTableName->getQuotedName($platform); + } + + /** + * Returns the names of the referenced table columns + * the foreign key constraint is associated with. + * + * @return array + */ + public function getForeignColumns() + { + return array_keys($this->_foreignColumnNames); + } + + /** + * Returns the quoted representation of the referenced table column names + * the foreign key constraint is associated with. + * + * But only if they were defined with one or the referenced table column name + * is a keyword reserved by the platform. + * Otherwise the plain unquoted value as inserted is returned. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The platform to use for quotation. + * + * @return array + */ + public function getQuotedForeignColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_foreignColumnNames as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * Returns whether or not a given option + * is associated with the foreign key constraint. + * + * @param string $name Name of the option to check. + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * Returns an option associated with the foreign key constraint. + * + * @param string $name Name of the option the foreign key constraint is associated with. + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * Returns the options associated with the foreign key constraint. + * + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * Returns the referential action for UPDATE operations + * on the referenced table the foreign key constraint is associated with. + * + * @return string|null + */ + public function onUpdate() + { + return $this->onEvent('onUpdate'); + } + + /** + * Returns the referential action for DELETE operations + * on the referenced table the foreign key constraint is associated with. + * + * @return string|null + */ + public function onDelete() + { + return $this->onEvent('onDelete'); + } + + /** + * Returns the referential action for a given database operation + * on the referenced table the foreign key constraint is associated with. + * + * @param string $event Name of the database operation/event to return the referential action for. + * + * @return string|null + */ + private function onEvent($event) + { + if (isset($this->_options[$event])) { + $onEvent = strtoupper($this->_options[$event]); + + if ( ! in_array($onEvent, array('NO ACTION', 'RESTRICT'))) { + return $onEvent; + } + } + + return false; + } + + /** + * Checks whether this foreign key constraint intersects the given index columns. + * + * Returns `true` if at least one of this foreign key's local columns + * matches one of the given index's columns, `false` otherwise. + * + * @param Index $index The index to be checked against. + * + * @return boolean + */ + public function intersectsIndexColumns(Index $index) + { + foreach ($index->getColumns() as $indexColumn) { + foreach ($this->_localColumnNames as $localColumn) { + if (strtolower($indexColumn) === strtolower($localColumn->getName())) { + return true; + } + } + } + + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php new file mode 100644 index 0000000..1e48614 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * An abstraction class for an asset identifier. + * + * Wraps identifier names like column names in indexes / foreign keys + * in an abstract class for proper quotation capabilities. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.4 + */ +class Identifier extends AbstractAsset +{ + /** + * Constructor. + * + * @param string $identifier Identifier name to wrap. + */ + public function __construct($identifier) + { + $this->_setName($identifier); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php new file mode 100644 index 0000000..bc5cd93 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php @@ -0,0 +1,358 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class Index extends AbstractAsset implements Constraint +{ + /** + * Asset identifier instances of the column names the index is associated with. + * array($columnName => Identifier) + * + * @var Identifier[] + */ + protected $_columns = array(); + + /** + * @var boolean + */ + protected $_isUnique = false; + + /** + * @var boolean + */ + protected $_isPrimary = false; + + /** + * Platform specific flags for indexes. + * array($flagName => true) + * + * @var array + */ + protected $_flags = array(); + + /** + * Platform specific options + * + * @todo $_flags should eventually be refactored into options + * + * @var array + */ + private $options = array(); + + /** + * @param string $indexName + * @param string[] $columns + * @param boolean $isUnique + * @param boolean $isPrimary + * @param string[] $flags + * @param array $options + */ + public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array(), array $options = array()) + { + $isUnique = $isUnique || $isPrimary; + + $this->_setName($indexName); + $this->_isUnique = $isUnique; + $this->_isPrimary = $isPrimary; + $this->options = $options; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * @param string $column + * + * @return void + * + * @throws \InvalidArgumentException + */ + protected function _addColumn($column) + { + if (is_string($column)) { + $this->_columns[$column] = new Identifier($column); + } else { + throw new \InvalidArgumentException("Expecting a string as Index Column"); + } + } + + /** + * {@inheritdoc} + */ + public function getColumns() + { + return array_keys($this->_columns); + } + + /** + * {@inheritdoc} + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + $columns = array(); + + foreach ($this->_columns as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * @return string[] + */ + public function getUnquotedColumns() + { + return array_map(array($this, 'trimQuotes'), $this->getColumns()); + } + + /** + * Is the index neither unique nor primary key? + * + * @return boolean + */ + public function isSimpleIndex() + { + return !$this->_isPrimary && !$this->_isUnique; + } + + /** + * @return boolean + */ + public function isUnique() + { + return $this->_isUnique; + } + + /** + * @return boolean + */ + public function isPrimary() + { + return $this->_isPrimary; + } + + /** + * @param string $columnName + * @param integer $pos + * + * @return boolean + */ + public function hasColumnAtPosition($columnName, $pos = 0) + { + $columnName = $this->trimQuotes(strtolower($columnName)); + $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); + + return array_search($columnName, $indexColumns) === $pos; + } + + /** + * Checks if this index exactly spans the given column names in the correct order. + * + * @param array $columnNames + * + * @return boolean + */ + public function spansColumns(array $columnNames) + { + $columns = $this->getColumns(); + $numberOfColumns = count($columns); + $sameColumns = true; + + for ($i = 0; $i < $numberOfColumns; $i++) { + if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) { + $sameColumns = false; + } + } + + return $sameColumns; + } + + /** + * Checks if the other index already fulfills all the indexing and constraint needs of the current one. + * + * @param Index $other + * + * @return boolean + */ + public function isFullfilledBy(Index $other) + { + // allow the other index to be equally large only. It being larger is an option + // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) + if (count($other->getColumns()) != count($this->getColumns())) { + return false; + } + + // Check if columns are the same, and even in the same order + $sameColumns = $this->spansColumns($other->getColumns()); + + if ($sameColumns) { + if ( ! $this->samePartialIndex($other)) { + return false; + } + + if ( ! $this->isUnique() && ! $this->isPrimary()) { + // this is a special case: If the current key is neither primary or unique, any uniqe or + // primary key will always have the same effect for the index and there cannot be any constraint + // overlaps. This means a primary or unique index can always fulfill the requirements of just an + // index that has no constraints. + return true; + } + + if ($other->isPrimary() != $this->isPrimary()) { + return false; + } + + if ($other->isUnique() != $this->isUnique()) { + return false; + } + + return true; + } + + return false; + } + + /** + * Detects if the other index is a non-unique, non primary index that can be overwritten by this one. + * + * @param Index $other + * + * @return boolean + */ + public function overrules(Index $other) + { + if ($other->isPrimary()) { + return false; + } elseif ($this->isSimpleIndex() && $other->isUnique()) { + return false; + } + + if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other)) { + return true; + } + + return false; + } + + /** + * Returns platform specific flags for indexes. + * + * @return string[] + */ + public function getFlags() + { + return array_keys($this->_flags); + } + + /** + * Adds Flag for an index that translates to platform specific handling. + * + * @example $index->addFlag('CLUSTERED') + * + * @param string $flag + * + * @return Index + */ + public function addFlag($flag) + { + $this->_flags[strtolower($flag)] = true; + + return $this; + } + + /** + * Does this index have a specific flag? + * + * @param string $flag + * + * @return boolean + */ + public function hasFlag($flag) + { + return isset($this->_flags[strtolower($flag)]); + } + + /** + * Removes a flag. + * + * @param string $flag + * + * @return void + */ + public function removeFlag($flag) + { + unset($this->_flags[strtolower($flag)]); + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->options[strtolower($name)]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->options[strtolower($name)]; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Return whether the two indexes have the same partial index + * @param \Doctrine\DBAL\Schema\Index $other + * + * @return boolean + */ + private function samePartialIndex(Index $other) + { + if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) { + return true; + } + + if ( ! $this->hasOption('where') && ! $other->hasOption('where')) { + return true; + } + + return false; + } + +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php new file mode 100644 index 0000000..0b36762 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -0,0 +1,252 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Types\Type; + +/** + * Schema manager for the MySql RDBMS. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class MySqlSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return array_shift($table); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['User'], + 'password' => $user['Password'], + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + foreach ($tableIndexes as $k => $v) { + $v = array_change_key_case($v, CASE_LOWER); + if ($v['key_name'] == 'PRIMARY') { + $v['primary'] = true; + } else { + $v['primary'] = false; + } + if (strpos($v['index_type'], 'FULLTEXT') !== false) { + $v['flags'] = array('FULLTEXT'); + } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { + $v['flags'] = array('SPATIAL'); + } + $tableIndexes[$k] = $v; + } + + return parent::_getPortableTableIndexesList($tableIndexes, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return end($sequence); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['Database']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['type']); + $dbType = strtok($dbType, '(), '); + if (isset($tableColumn['length'])) { + $length = $tableColumn['length']; + } else { + $length = strtok('(), '); + } + + $fixed = null; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $scale = null; + $precision = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + // In cases where not connected to a database DESCRIBE $table does not return 'Comment' + if (isset($tableColumn['comment'])) { + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + } + + switch ($dbType) { + case 'char': + case 'binary': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'numeric': + case 'decimal': + if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'tinytext': + $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT; + break; + case 'text': + $length = MySqlPlatform::LENGTH_LIMIT_TEXT; + break; + case 'mediumtext': + $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT; + break; + case 'tinyblob': + $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB; + break; + case 'blob': + $length = MySqlPlatform::LENGTH_LIMIT_BLOB; + break; + case 'mediumblob': + $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB; + break; + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + case 'year': + $length = null; + break; + } + + $length = ((int) $length == 0) ? null : (int) $length; + + $options = array( + 'length' => $length, + 'unsigned' => (bool) (strpos($tableColumn['type'], 'unsigned') !== false), + 'fixed' => (bool) $fixed, + 'default' => isset($tableColumn['default']) ? $tableColumn['default'] : null, + 'notnull' => (bool) ($tableColumn['null'] != 'YES'), + 'scale' => null, + 'precision' => null, + 'autoincrement' => (bool) (strpos($tableColumn['extra'], 'auto_increment') !== false), + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ); + + if ($scale !== null && $precision !== null) { + $options['scale'] = $scale; + $options['precision'] = $precision; + } + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if (!isset($value['delete_rule']) || $value['delete_rule'] == "RESTRICT") { + $value['delete_rule'] = null; + } + if (!isset($value['update_rule']) || $value['update_rule'] == "RESTRICT") { + $value['update_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $value['constraint_name'], + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['referenced_table_name'], + 'onDelete' => $value['delete_rule'], + 'onUpdate' => $value['update_rule'], + ); + } + $list[$value['constraint_name']]['local'][] = $value['column_name']; + $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + ) + ); + } + + return $result; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php new file mode 100644 index 0000000..a02933d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -0,0 +1,357 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * Oracle Schema Manager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class OracleSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + $view = \array_change_key_case($view, CASE_LOWER); + + return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + $user = \array_change_key_case($user, CASE_LOWER); + + return array( + 'user' => $user['username'], + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + $table = \array_change_key_case($table, CASE_LOWER); + + return $this->getQuotedIdentifierName($table['table_name']); + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + foreach ($tableIndexes as $tableIndex) { + $tableIndex = \array_change_key_case($tableIndex, CASE_LOWER); + + $keyName = strtolower($tableIndex['name']); + + if (strtolower($tableIndex['is_primary']) == "p") { + $keyName = 'primary'; + $buffer['primary'] = true; + $buffer['non_unique'] = false; + } else { + $buffer['primary'] = false; + $buffer['non_unique'] = ($tableIndex['is_unique'] == 0) ? true : false; + } + $buffer['key_name'] = $keyName; + $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']); + $indexBuffer[] = $buffer; + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = \array_change_key_case($tableColumn, CASE_LOWER); + + $dbType = strtolower($tableColumn['data_type']); + if (strpos($dbType, "timestamp(") === 0) { + if (strpos($dbType, "WITH TIME ZONE")) { + $dbType = "timestamptz"; + } else { + $dbType = "timestamp"; + } + } + + $unsigned = $fixed = null; + + if ( ! isset($tableColumn['column_name'])) { + $tableColumn['column_name'] = ''; + } + + // Default values returned from database sometimes have trailing spaces. + $tableColumn['data_default'] = trim($tableColumn['data_default']); + + if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { + $tableColumn['data_default'] = null; + } + + if (null !== $tableColumn['data_default']) { + // Default values returned from database are enclosed in single quotes. + $tableColumn['data_default'] = trim($tableColumn['data_default'], "'"); + } + + $precision = null; + $scale = null; + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); + $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); + + switch ($dbType) { + case 'number': + if ($tableColumn['data_precision'] == 20 && $tableColumn['data_scale'] == 0) { + $precision = 20; + $scale = 0; + $type = 'bigint'; + } elseif ($tableColumn['data_precision'] == 5 && $tableColumn['data_scale'] == 0) { + $type = 'smallint'; + $precision = 5; + $scale = 0; + } elseif ($tableColumn['data_precision'] == 1 && $tableColumn['data_scale'] == 0) { + $precision = 1; + $scale = 0; + $type = 'boolean'; + } elseif ($tableColumn['data_scale'] > 0) { + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $type = 'decimal'; + } + $length = null; + break; + case 'pls_integer': + case 'binary_integer': + $length = null; + break; + case 'varchar': + case 'varchar2': + case 'nvarchar2': + $length = $tableColumn['char_length']; + $fixed = false; + break; + case 'char': + case 'nchar': + $length = $tableColumn['char_length']; + $fixed = true; + break; + case 'date': + case 'timestamp': + $length = null; + break; + case 'float': + case 'binary_float': + case 'binary_double': + $precision = $tableColumn['data_precision']; + $scale = $tableColumn['data_scale']; + $length = null; + break; + case 'clob': + case 'nclob': + $length = null; + break; + case 'blob': + case 'raw': + case 'long raw': + case 'bfile': + $length = null; + break; + case 'rowid': + case 'urowid': + default: + $length = null; + } + + $options = array( + 'notnull' => (bool) ($tableColumn['nullable'] === 'N'), + 'fixed' => (bool) $fixed, + 'unsigned' => (bool) $unsigned, + 'default' => $tableColumn['data_default'], + 'length' => $length, + 'precision' => $precision, + 'scale' => $scale, + 'comment' => isset($tableColumn['comments']) && '' !== $tableColumn['comments'] + ? $tableColumn['comments'] + : null, + 'platformDetails' => array(), + ); + + return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = \array_change_key_case($value, CASE_LOWER); + if (!isset($list[$value['constraint_name']])) { + if ($value['delete_rule'] == "NO ACTION") { + $value['delete_rule'] = null; + } + + $list[$value['constraint_name']] = array( + 'name' => $this->getQuotedIdentifierName($value['constraint_name']), + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['references_table'], + 'onDelete' => $value['delete_rule'], + ); + } + + $localColumn = $this->getQuotedIdentifierName($value['local_column']); + $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); + + $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; + $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $this->getQuotedIdentifierName($constraint['foreignTable']), + array_values($constraint['foreign']), $this->getQuotedIdentifierName($constraint['name']), + array('onDelete' => $constraint['onDelete']) + ); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + $sequence = \array_change_key_case($sequence, CASE_LOWER); + + return new Sequence( + $this->getQuotedIdentifierName($sequence['sequence_name']), + $sequence['increment_by'], + $sequence['min_value'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableFunctionDefinition($function) + { + $function = \array_change_key_case($function, CASE_LOWER); + + return $function['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + $database = \array_change_key_case($database, CASE_LOWER); + + return $database['username']; + } + + /** + * {@inheritdoc} + */ + public function createDatabase($database = null) + { + if (is_null($database)) { + $database = $this->_conn->getDatabase(); + } + + $params = $this->_conn->getParams(); + $username = $database; + $password = $params['password']; + + $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password; + $this->_conn->executeUpdate($query); + + $query = 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' . $username; + $this->_conn->executeUpdate($query); + + return true; + } + + /** + * @param string $table + * + * @return boolean + */ + public function dropAutoincrement($table) + { + $sql = $this->_platform->getDropAutoincrementSql($table); + foreach ($sql as $query) { + $this->_conn->executeUpdate($query); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function dropTable($name) + { + $this->tryMethod('dropAutoincrement', $name); + + parent::dropTable($name); + } + + /** + * Returns the quoted representation of the given identifier name. + * + * Quotes non-uppercase identifiers explicitly to preserve case + * and thus make references to the particular identifier work. + * + * @param string $identifier The identifier to quote. + * + * @return string The quoted identifier. + */ + private function getQuotedIdentifierName($identifier) + { + if (preg_match('/[a-z]/', $identifier)) { + return $this->_platform->quoteIdentifier($identifier); + } + + return $identifier; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php new file mode 100644 index 0000000..5b7b82e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -0,0 +1,438 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Types\Type; + +/** + * PostgreSQL Schema Manager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Benjamin Eberlei + * @since 2.0 + */ +class PostgreSqlSchemaManager extends AbstractSchemaManager +{ + /** + * @var array + */ + private $existingSchemaPaths; + + /** + * Gets all the existing schema names. + * + * @return array + */ + public function getSchemaNames() + { + $rows = $this->_conn->fetchAll("SELECT nspname as schema_name FROM pg_namespace WHERE nspname !~ '^pg_.*' and nspname != 'information_schema'"); + + return array_map(function ($v) { return $v['schema_name']; }, $rows); + } + + /** + * Returns an array of schema search paths. + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getSchemaSearchPaths() + { + $params = $this->_conn->getParams(); + $schema = explode(",", $this->_conn->fetchColumn('SHOW search_path')); + + if (isset($params['user'])) { + $schema = str_replace('"$user"', $params['user'], $schema); + } + + return array_map('trim', $schema); + } + + /** + * Gets names of all existing schemas in the current users search path. + * + * This is a PostgreSQL only function. + * + * @return array + */ + public function getExistingSchemaSearchPaths() + { + if ($this->existingSchemaPaths === null) { + $this->determineExistingSchemaSearchPaths(); + } + + return $this->existingSchemaPaths; + } + + /** + * Sets or resets the order of the existing schemas in the current search path of the user. + * + * This is a PostgreSQL only function. + * + * @return void + */ + public function determineExistingSchemaSearchPaths() + { + $names = $this->getSchemaNames(); + $paths = $this->getSchemaSearchPaths(); + + $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) { + return in_array($v, $names); + }); + } + + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + try { + parent::dropDatabase($database); + } catch (DriverException $exception) { + // If we have a SQLSTATE 55006, the drop database operation failed + // because of active connections on the database. + // To force dropping the database, we first have to close all active connections + // on that database and issue the drop database operation again. + if ($exception->getSQLState() !== '55006') { + throw $exception; + } + + $this->_execSql( + array( + $this->_platform->getDisallowDatabaseConnectionsSQL($database), + $this->_platform->getCloseActiveDatabaseConnectionsSQL($database), + ) + ); + + parent::dropDatabase($database); + } + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + $onUpdate = null; + $onDelete = null; + + if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onUpdate = $match[1]; + } + if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) { + $onDelete = $match[1]; + } + + if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) { + // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get + // the idea to trim them here. + $localColumns = array_map('trim', explode(",", $values[1])); + $foreignColumns = array_map('trim', explode(",", $values[3])); + $foreignTable = $values[2]; + } + + return new ForeignKeyConstraint( + $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], + array('onUpdate' => $onUpdate, 'onDelete' => $onDelete) + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTriggerDefinition($trigger) + { + return $trigger['trigger_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['schemaname'].'.'.$view['viewname'], $view['definition']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + $schemas = $this->getExistingSchemaSearchPaths(); + $firstSchema = array_shift($schemas); + + if ($table['schema_name'] == $firstSchema) { + return $table['table_name']; + } else { + return $table['schema_name'] . "." . $table['table_name']; + } + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $buffer = array(); + foreach ($tableIndexes as $row) { + $colNumbers = explode(' ', $row['indkey']); + $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )'; + $columnNameSql = "SELECT attnum, attname FROM pg_attribute + WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;"; + + $stmt = $this->_conn->executeQuery($columnNameSql); + $indexColumns = $stmt->fetchAll(); + + // required for getting the order of the columns right. + foreach ($colNumbers as $colNum) { + foreach ($indexColumns as $colRow) { + if ($colNum == $colRow['attnum']) { + $buffer[] = array( + 'key_name' => $row['relname'], + 'column_name' => trim($colRow['attname']), + 'non_unique' => !$row['indisunique'], + 'primary' => $row['indisprimary'], + 'where' => $row['where'], + ); + } + } + } + } + + return parent::_getPortableTableIndexesList($buffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['datname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequencesList($sequences) + { + $sequenceDefinitions = array(); + + foreach ($sequences as $sequence) { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $sequenceDefinitions[$sequenceName] = $sequence; + } + + $list = array(); + + foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { + $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace['nspname']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + if ($sequence['schemaname'] != 'public') { + $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; + } else { + $sequenceName = $sequence['relname']; + } + + $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $this->_platform->quoteIdentifier($sequenceName)); + + return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { + // get length from varchar definition + $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); + $tableColumn['length'] = $length; + } + + $matches = array(); + + $autoincrement = false; + if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) { + $tableColumn['sequence'] = $matches[1]; + $tableColumn['default'] = null; + $autoincrement = true; + } + + if (preg_match("/^'(.*)'::.*$/", $tableColumn['default'], $matches)) { + $tableColumn['default'] = $matches[1]; + } + + if (stripos($tableColumn['default'], 'NULL') === 0) { + $tableColumn['default'] = null; + } + + $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null; + if ($length == '-1' && isset($tableColumn['atttypmod'])) { + $length = $tableColumn['atttypmod'] - 4; + } + if ((int) $length <= 0) { + $length = null; + } + $fixed = null; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + $dbType = strtolower($tableColumn['type']); + if (strlen($tableColumn['domain_type']) && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) { + $dbType = strtolower($tableColumn['domain_type']); + $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + switch ($dbType) { + case 'smallint': + case 'int2': + $length = null; + break; + case 'int': + case 'int4': + case 'integer': + $length = null; + break; + case 'bigint': + case 'int8': + $length = null; + break; + case 'bool': + case 'boolean': + if ($tableColumn['default'] === 'true') { + $tableColumn['default'] = true; + } + + if ($tableColumn['default'] === 'false') { + $tableColumn['default'] = false; + } + + $length = null; + break; + case 'text': + $fixed = false; + break; + case 'varchar': + case 'interval': + case '_varchar': + $fixed = false; + break; + case 'char': + case 'bpchar': + $fixed = true; + break; + case 'float': + case 'float4': + case 'float8': + case 'double': + case 'double precision': + case 'real': + case 'decimal': + case 'money': + case 'numeric': + if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) { + $precision = $match[1]; + $scale = $match[2]; + $length = null; + } + break; + case 'year': + $length = null; + break; + } + + if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) { + $tableColumn['default'] = $match[1]; + } + + $options = array( + 'length' => $length, + 'notnull' => (bool) $tableColumn['isnotnull'], + 'default' => $tableColumn['default'], + 'primary' => (bool) ($tableColumn['pri'] == 't'), + 'precision' => $precision, + 'scale' => $scale, + 'fixed' => $fixed, + 'unsigned' => false, + 'autoincrement' => $autoincrement, + 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' + ? $tableColumn['comment'] + : null, + ); + + $column = new Column($tableColumn['field'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && !empty($tableColumn['collation'])) { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php new file mode 100644 index 0000000..793a5fe --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLAnywhereSchemaManager.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; + +/** + * SAP Sybase SQL Anywhere schema manager. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +class SQLAnywhereSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + * + * Starts a database after creation + * as SQL Anywhere needs a database to be started + * before it can be used. + * + * @see startDatabase + */ + public function createDatabase($database) + { + parent::createDatabase($database); + $this->startDatabase($database); + } + + /** + * {@inheritdoc} + * + * Tries stopping a database before dropping + * as SQL Anywhere needs a database to be stopped + * before it can be dropped. + * + * @see stopDatabase + */ + public function dropDatabase($database) + { + $this->tryMethod('stopDatabase', $database); + parent::dropDatabase($database); + } + + /** + * Starts a database. + * + * @param string $database The name of the database to start. + */ + public function startDatabase($database) + { + $this->_execSql($this->_platform->getStartDatabaseSQL($database)); + } + + /** + * Stops a database. + * + * @param string $database The name of the database to stop. + */ + public function stopDatabase($database) + { + $this->_execSql($this->_platform->getStopDatabaseSQL($database)); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['start_with']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $type = $this->_platform->getDoctrineTypeMapping($tableColumn['type']); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + $precision = null; + $scale = null; + $fixed = false; + $default = null; + + if (null !== $tableColumn['default']) { + // Strip quotes from default value. + $default = preg_replace(array("/^'(.*)'$/", "/''/"), array("$1", "'"), $tableColumn['default']); + + if ('autoincrement' == $default) { + $default = null; + } + } + + switch ($tableColumn['type']) { + case 'binary': + case 'char': + case 'nchar': + $fixed = true; + } + + switch ($type) { + case 'decimal': + case 'float': + $precision = $tableColumn['length']; + $scale = $tableColumn['scale']; + } + + return new Column( + $tableColumn['column_name'], + Type::getType($type), + array( + 'length' => $type == 'string' ? $tableColumn['length'] : null, + 'precision' => $precision, + 'scale' => $scale, + 'unsigned' => (bool) $tableColumn['unsigned'], + 'fixed' => $fixed, + 'notnull' => (bool) $tableColumn['notnull'], + 'default' => $default, + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'comment' => isset($tableColumn['comment']) && '' !== $tableColumn['comment'] + ? $tableColumn['comment'] + : null, + )); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['table_name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if (!isset($foreignKeys[$tableForeignKey['index_name']])) { + $foreignKeys[$tableForeignKey['index_name']] = array( + 'local_columns' => array($tableForeignKey['local_column']), + 'foreign_table' => $tableForeignKey['foreign_table'], + 'foreign_columns' => array($tableForeignKey['foreign_column']), + 'name' => $tableForeignKey['index_name'], + 'options' => array( + 'notnull' => $tableForeignKey['notnull'], + 'match' => $tableForeignKey['match'], + 'onUpdate' => $tableForeignKey['on_update'], + 'onDelete' => $tableForeignKey['on_delete'], + 'check_on_commit' => $tableForeignKey['check_on_commit'], + 'clustered' => $tableForeignKey['clustered'], + 'for_olap_workload' => $tableForeignKey['for_olap_workload'] + ) + ); + } else { + $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; + $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = array(); + + if ($tableIndex['clustered']) { + $tableIndex['flags'][] = 'clustered'; + } + + if ($tableIndex['with_nulls_not_distinct']) { + $tableIndex['flags'][] = 'with_nulls_not_distinct'; + } + + if ($tableIndex['for_olap_workload']) { + $tableIndex['flags'][] = 'for_olap_workload'; + } + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View( + $view['table_name'], + preg_replace('/^.*\s+as\s+SELECT(.*)/i', "SELECT$1", $view['view_def']) + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php new file mode 100644 index 0000000..b56f8b2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -0,0 +1,261 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Driver\SQLSrv\SQLSrvException; +use Doctrine\DBAL\Types\Type; + +/** + * SQL Server Schema Manager. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Juozas Kaziukenas + * @author Steve Müller + * @since 2.0 + */ +class SQLServerSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['name'], $sequence['increment'], $sequence['start_value']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $dbType = strtok($tableColumn['type'], '(), '); + $fixed = null; + $length = (int) $tableColumn['length']; + $default = $tableColumn['default']; + + if (!isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + while ($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) { + $default = trim($default2, "'"); + + if ($default == 'getdate()') { + $default = $this->_platform->getCurrentTimestampSQL(); + } + } + + switch ($dbType) { + case 'nchar': + case 'nvarchar': + case 'ntext': + // Unicode data requires 2 bytes per character + $length = $length / 2; + break; + case 'varchar': + // TEXT type is returned as VARCHAR(MAX) with a length of -1 + if ($length == -1) { + $dbType = 'text'; + } + break; + } + + if ('char' === $dbType || 'nchar' === $dbType || 'binary' === $dbType) { + $fixed = true; + } + + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); + $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + + $options = array( + 'length' => ($length == 0 || !in_array($type, array('text', 'string'))) ? null : $length, + 'unsigned' => false, + 'fixed' => (bool) $fixed, + 'default' => $default !== 'NULL' ? $default : null, + 'notnull' => (bool) $tableColumn['notnull'], + 'scale' => $tableColumn['scale'], + 'precision' => $tableColumn['precision'], + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, + ); + + $column = new Column($tableColumn['name'], Type::getType($type), $options); + + if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { + $column->setPlatformOption('collation', $tableColumn['collation']); + } + + return $column; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $foreignKeys = array(); + + foreach ($tableForeignKeys as $tableForeignKey) { + if ( ! isset($foreignKeys[$tableForeignKey['ForeignKey']])) { + $foreignKeys[$tableForeignKey['ForeignKey']] = array( + 'local_columns' => array($tableForeignKey['ColumnName']), + 'foreign_table' => $tableForeignKey['ReferenceTableName'], + 'foreign_columns' => array($tableForeignKey['ReferenceColumnName']), + 'name' => $tableForeignKey['ForeignKey'], + 'options' => array( + 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), + 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']) + ) + ); + } else { + $foreignKeys[$tableForeignKey['ForeignKey']]['local_columns'][] = $tableForeignKey['ColumnName']; + $foreignKeys[$tableForeignKey['ForeignKey']]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName']; + } + } + + return parent::_getPortableTableForeignKeysList($foreignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null) + { + foreach ($tableIndexRows as &$tableIndex) { + $tableIndex['non_unique'] = (boolean) $tableIndex['non_unique']; + $tableIndex['primary'] = (boolean) $tableIndex['primary']; + $tableIndex['flags'] = $tableIndex['flags'] ? array($tableIndex['flags']) : null; + } + + return parent::_getPortableTableIndexesList($tableIndexRows, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + { + return new ForeignKeyConstraint( + $tableForeignKey['local_columns'], + $tableForeignKey['foreign_table'], + $tableForeignKey['foreign_columns'], + $tableForeignKey['name'], + $tableForeignKey['options'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableDatabaseDefinition($database) + { + return $database['name']; + } + + /** + * {@inheritdoc} + */ + protected function getPortableNamespaceDefinition(array $namespace) + { + return $namespace['name']; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + // @todo + return new View($view['name'], null); + } + + /** + * {@inheritdoc} + */ + public function listTableIndexes($table) + { + $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); + + try { + $tableIndexes = $this->_conn->fetchAll($sql); + } catch (\PDOException $e) { + if ($e->getCode() == "IMSSP") { + return array(); + } else { + throw $e; + } + } catch (SQLSrvException $e) { + if (strpos($e->getMessage(), 'SQLSTATE [01000, 15472]') === 0) { + return array(); + } else { + throw $e; + } + } + + return $this->_getPortableTableIndexesList($tableIndexes, $table); + } + + /** + * {@inheritdoc} + */ + public function alterTable(TableDiff $tableDiff) + { + if (count($tableDiff->removedColumns) > 0) { + foreach ($tableDiff->removedColumns as $col) { + $columnConstraintSql = $this->getColumnConstraintSQL($tableDiff->name, $col->getName()); + foreach ($this->_conn->fetchAll($columnConstraintSql) as $constraint) { + $this->_conn->exec("ALTER TABLE $tableDiff->name DROP CONSTRAINT " . $constraint['Name']); + } + } + } + + parent::alterTable($tableDiff); + } + + /** + * Returns the SQL to retrieve the constraints for a given column. + * + * @param string $table + * @param string $column + * + * @return string + */ + private function getColumnConstraintSQL($table, $column) + { + return "SELECT SysObjects.[Name] + FROM SysObjects INNER JOIN (SELECT [Name],[ID] FROM SysObjects WHERE XType = 'U') AS Tab + ON Tab.[ID] = Sysobjects.[Parent_Obj] + INNER JOIN sys.default_constraints DefCons ON DefCons.[object_id] = Sysobjects.[ID] + INNER JOIN SysColumns Col ON Col.[ColID] = DefCons.[parent_column_id] AND Col.[ID] = Tab.[ID] + WHERE Col.[Name] = " . $this->_conn->quote($column) ." AND Tab.[Name] = " . $this->_conn->quote($table) . " + ORDER BY Col.[Name]"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php new file mode 100644 index 0000000..b7e24fe --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Schema.php @@ -0,0 +1,510 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Object representation of a database schema. + * + * Different vendors have very inconsistent naming with regard to the concept + * of a "schema". Doctrine understands a schema as the entity that conceptually + * wraps a set of database objects such as tables, sequences, indexes and + * foreign keys that belong to each other into a namespace. A Doctrine Schema + * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more + * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL. + * + * Every asset in the doctrine schema has a name. A name consists of either a + * namespace.local name pair or just a local unqualified name. + * + * The abstraction layer that covers a PostgreSQL schema is the namespace of an + * database object (asset). A schema can have a name, which will be used as + * default namespace for the unqualified database objects that are created in + * the schema. + * + * In the case of MySQL where cross-database queries are allowed this leads to + * databases being "misinterpreted" as namespaces. This is intentional, however + * the CREATE/DROP SQL visitors will just filter this queries and do not + * execute them. Only the queries for the currently connected database are + * executed. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Schema extends AbstractAsset +{ + /** + * The namespaces in this schema. + * + * @var array + */ + private $namespaces = array(); + + /** + * @var \Doctrine\DBAL\Schema\Table[] + */ + protected $_tables = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + protected $_sequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\SchemaConfig + */ + protected $_schemaConfig = false; + + /** + * @param \Doctrine\DBAL\Schema\Table[] $tables + * @param \Doctrine\DBAL\Schema\Sequence[] $sequences + * @param \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig + * @param array $namespaces + */ + public function __construct( + array $tables = array(), + array $sequences = array(), + SchemaConfig $schemaConfig = null, + array $namespaces = array() + ) { + if ($schemaConfig == null) { + $schemaConfig = new SchemaConfig(); + } + $this->_schemaConfig = $schemaConfig; + $this->_setName($schemaConfig->getName() ?: 'public'); + + foreach ($namespaces as $namespace) { + $this->createNamespace($namespace); + } + + foreach ($tables as $table) { + $this->_addTable($table); + } + + foreach ($sequences as $sequence) { + $this->_addSequence($sequence); + } + } + + /** + * @return boolean + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function _addTable(Table $table) + { + $namespaceName = $table->getNamespaceName(); + $tableName = $table->getFullQualifiedName($this->getName()); + + if (isset($this->_tables[$tableName])) { + throw SchemaException::tableAlreadyExists($tableName); + } + + if ( ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) { + $this->createNamespace($namespaceName); + } + + $this->_tables[$tableName] = $table; + $table->setSchemaConfig($this->_schemaConfig); + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + protected function _addSequence(Sequence $sequence) + { + $namespaceName = $sequence->getNamespaceName(); + $seqName = $sequence->getFullQualifiedName($this->getName()); + + if (isset($this->_sequences[$seqName])) { + throw SchemaException::sequenceAlreadyExists($seqName); + } + + if ( ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) { + $this->createNamespace($namespaceName); + } + + $this->_sequences[$seqName] = $sequence; + } + + /** + * Returns the namespaces of this schema. + * + * @return array A list of namespace names. + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * Gets all tables of this schema. + * + * @return \Doctrine\DBAL\Schema\Table[] + */ + public function getTables() + { + return $this->_tables; + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + public function getTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + if (!isset($this->_tables[$tableName])) { + throw SchemaException::tableDoesNotExist($tableName); + } + + return $this->_tables[$tableName]; + } + + /** + * @param string $name + * + * @return string + */ + private function getFullQualifiedAssetName($name) + { + $name = $this->getUnquotedAssetName($name); + + if (strpos($name, ".") === false) { + $name = $this->getName() . "." . $name; + } + + return strtolower($name); + } + + /** + * Returns the unquoted representation of a given asset name. + * + * @param string $assetName Quoted or unquoted representation of an asset name. + * + * @return string + */ + private function getUnquotedAssetName($assetName) + { + if ($this->isIdentifierQuoted($assetName)) { + return $this->trimQuotes($assetName); + } + + return $assetName; + } + + /** + * Does this schema have a namespace with the given name? + * + * @param string $namespaceName + * + * @return boolean + */ + public function hasNamespace($namespaceName) + { + $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName)); + + return isset($this->namespaces[$namespaceName]); + } + + /** + * Does this schema have a table with the given name? + * + * @param string $tableName + * + * @return boolean + */ + public function hasTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + + return isset($this->_tables[$tableName]); + } + + /** + * Gets all table names, prefixed with a schema name, even the default one if present. + * + * @return array + */ + public function getTableNames() + { + return array_keys($this->_tables); + } + + /** + * @param string $sequenceName + * + * @return boolean + */ + public function hasSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + + return isset($this->_sequences[$sequenceName]); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\Sequence + * + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + public function getSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + if (!$this->hasSequence($sequenceName)) { + throw SchemaException::sequenceDoesNotExist($sequenceName); + } + + return $this->_sequences[$sequenceName]; + } + + /** + * @return \Doctrine\DBAL\Schema\Sequence[] + */ + public function getSequences() + { + return $this->_sequences; + } + + /** + * Creates a new namespace. + * + * @param string $namespaceName The name of the namespace to create. + * + * @return \Doctrine\DBAL\Schema\Schema This schema instance. + */ + public function createNamespace($namespaceName) + { + $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName)); + + if (isset($this->namespaces[$unquotedNamespaceName])) { + throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName); + } + + $this->namespaces[$unquotedNamespaceName] = $namespaceName; + + return $this; + } + + /** + * Creates a new table. + * + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Table + */ + public function createTable($tableName) + { + $table = new Table($tableName); + $this->_addTable($table); + + foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) { + $table->addOption($name, $value); + } + + return $table; + } + + /** + * Renames a table. + * + * @param string $oldTableName + * @param string $newTableName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function renameTable($oldTableName, $newTableName) + { + $table = $this->getTable($oldTableName); + $table->_setName($newTableName); + + $this->dropTable($oldTableName); + $this->_addTable($table); + + return $this; + } + + /** + * Drops a table from the schema. + * + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function dropTable($tableName) + { + $tableName = $this->getFullQualifiedAssetName($tableName); + $this->getTable($tableName); + unset($this->_tables[$tableName]); + + return $this; + } + + /** + * Creates a new sequence. + * + * @param string $sequenceName + * @param integer $allocationSize + * @param integer $initialValue + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) + { + $seq = new Sequence($sequenceName, $allocationSize, $initialValue); + $this->_addSequence($seq); + + return $seq; + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\Schema + */ + public function dropSequence($sequenceName) + { + $sequenceName = $this->getFullQualifiedAssetName($sequenceName); + unset($this->_sequences[$sequenceName]); + + return $this; + } + + /** + * Returns an array of necessary SQL queries to create the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + $sqlCollector = new CreateSchemaSqlCollector($platform); + $this->visit($sqlCollector); + + return $sqlCollector->getQueries(); + } + + /** + * Return an array of necessary SQL queries to drop the schema on the given platform. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toDropSql(AbstractPlatform $platform) + { + $dropSqlCollector = new DropSchemaSqlCollector($platform); + $this->visit($dropSqlCollector); + + return $dropSqlCollector->getQueries(); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($this, $toSchema); + + return $schemaDiff->toSql($platform); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $fromSchema + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) + { + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $this); + + return $schemaDiff->toSql($platform); + } + + /** + * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSchema($this); + + if ($visitor instanceof NamespaceVisitor) { + foreach ($this->namespaces as $namespace) { + $visitor->acceptNamespace($namespace); + } + } + + foreach ($this->_tables as $table) { + $table->visit($visitor); + } + + foreach ($this->_sequences as $sequence) { + $sequence->visit($visitor); + } + } + + /** + * Cloning a Schema triggers a deep clone of all related assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_tables as $k => $table) { + $this->_tables[$k] = clone $table; + } + foreach ($this->_sequences as $k => $sequence) { + $this->_sequences[$k] = clone $sequence; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php new file mode 100644 index 0000000..cc23237 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaConfig.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Configuration for a Schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaConfig +{ + /** + * @var boolean + */ + protected $hasExplicitForeignKeyIndexes = false; + + /** + * @var integer + */ + protected $maxIdentifierLength = 63; + + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $defaultTableOptions = array(); + + /** + * @return boolean + */ + public function hasExplicitForeignKeyIndexes() + { + return $this->hasExplicitForeignKeyIndexes; + } + + /** + * @param boolean $flag + * + * @return void + */ + public function setExplicitForeignKeyIndexes($flag) + { + $this->hasExplicitForeignKeyIndexes = (bool) $flag; + } + + /** + * @param integer $length + * + * @return void + */ + public function setMaxIdentifierLength($length) + { + $this->maxIdentifierLength = (int) $length; + } + + /** + * @return integer + */ + public function getMaxIdentifierLength() + { + return $this->maxIdentifierLength; + } + + /** + * Gets the default namespace of schema objects. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the default namespace name of schema objects. + * + * @param string $name The value to set. + * + * @return void + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the default options that are passed to Table instances created with + * Schema#createTable(). + * + * @return array + */ + public function getDefaultTableOptions() + { + return $this->defaultTableOptions; + } + + /** + * @param array $defaultTableOptions + * + * @return void + */ + public function setDefaultTableOptions(array $defaultTableOptions) + { + $this->defaultTableOptions = $defaultTableOptions; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php new file mode 100644 index 0000000..35afe1f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaDiff.php @@ -0,0 +1,204 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use \Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Schema Diff. + * + * @link www.doctrine-project.org + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @since 2.0 + * @author Benjamin Eberlei + */ +class SchemaDiff +{ + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + public $fromSchema; + + /** + * All added namespaces. + * + * @var string[] + */ + public $newNamespaces = array(); + + /** + * All removed namespaces. + * + * @var string[] + */ + public $removedNamespaces = array(); + + /** + * All added tables. + * + * @var \Doctrine\DBAL\Schema\Table[] + */ + public $newTables = array(); + + /** + * All changed tables. + * + * @var \Doctrine\DBAL\Schema\TableDiff[] + */ + public $changedTables = array(); + + /** + * All removed tables. + * + * @var \Doctrine\DBAL\Schema\Table[] + */ + public $removedTables = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $newSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $changedSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\Sequence[] + */ + public $removedSequences = array(); + + /** + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $orphanedForeignKeys = array(); + + /** + * Constructs an SchemaDiff object. + * + * @param \Doctrine\DBAL\Schema\Table[] $newTables + * @param \Doctrine\DBAL\Schema\TableDiff[] $changedTables + * @param \Doctrine\DBAL\Schema\Table[] $removedTables + * @param \Doctrine\DBAL\Schema\Schema|null $fromSchema + */ + public function __construct($newTables = array(), $changedTables = array(), $removedTables = array(), Schema $fromSchema = null) + { + $this->newTables = $newTables; + $this->changedTables = $changedTables; + $this->removedTables = $removedTables; + $this->fromSchema = $fromSchema; + } + + /** + * The to save sql mode ensures that the following things don't happen: + * + * 1. Tables are deleted + * 2. Sequences are deleted + * 3. Foreign Keys which reference tables that would otherwise be deleted. + * + * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSaveSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, true); + } + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function toSql(AbstractPlatform $platform) + { + return $this->_toSql($platform, false); + } + + /** + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @param boolean $saveMode + * + * @return array + */ + protected function _toSql(AbstractPlatform $platform, $saveMode = false) + { + $sql = array(); + + if ($platform->supportsSchemas()) { + foreach ($this->newNamespaces as $newNamespace) { + $sql[] = $platform->getCreateSchemaSQL($newNamespace); + } + } + + if ($platform->supportsForeignKeyConstraints() && $saveMode == false) { + foreach ($this->orphanedForeignKeys as $orphanedForeignKey) { + $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTableName()); + } + } + + if ($platform->supportsSequences() == true) { + foreach ($this->changedSequences as $sequence) { + $sql[] = $platform->getAlterSequenceSQL($sequence); + } + + if ($saveMode === false) { + foreach ($this->removedSequences as $sequence) { + $sql[] = $platform->getDropSequenceSQL($sequence); + } + } + + foreach ($this->newSequences as $sequence) { + $sql[] = $platform->getCreateSequenceSQL($sequence); + } + } + + $foreignKeySql = array(); + foreach ($this->newTables as $table) { + $sql = array_merge( + $sql, + $platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES) + ); + + if ($platform->supportsForeignKeyConstraints()) { + foreach ($table->getForeignKeys() as $foreignKey) { + $foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table); + } + } + } + $sql = array_merge($sql, $foreignKeySql); + + if ($saveMode === false) { + foreach ($this->removedTables as $table) { + $sql[] = $platform->getDropTableSQL($table); + } + } + + foreach ($this->changedTables as $tableDiff) { + $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php new file mode 100644 index 0000000..152f374 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -0,0 +1,181 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +class SchemaException extends \Doctrine\DBAL\DBALException +{ + const TABLE_DOESNT_EXIST = 10; + const TABLE_ALREADY_EXISTS = 20; + const COLUMN_DOESNT_EXIST = 30; + const COLUMN_ALREADY_EXISTS = 40; + const INDEX_DOESNT_EXIST = 50; + const INDEX_ALREADY_EXISTS = 60; + const SEQUENCE_DOENST_EXIST = 70; + const SEQUENCE_ALREADY_EXISTS = 80; + const INDEX_INVALID_NAME = 90; + const FOREIGNKEY_DOESNT_EXIST = 100; + const NAMESPACE_ALREADY_EXISTS = 110; + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function tableDoesNotExist($tableName) + { + return new self("There is no table with name '".$tableName."' in the schema.", self::TABLE_DOESNT_EXIST); + } + + /** + * @param string $indexName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexNameInvalid($indexName) + { + return new self("Invalid index-name $indexName given, has to be [a-zA-Z0-9_]", self::INDEX_INVALID_NAME); + } + + /** + * @param string $indexName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexDoesNotExist($indexName, $table) + { + return new self("Index '$indexName' does not exist on table '$table'.", self::INDEX_DOESNT_EXIST); + } + + /** + * @param string $indexName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function indexAlreadyExists($indexName, $table) + { + return new self("An index with name '$indexName' was already defined on table '$table'.", self::INDEX_ALREADY_EXISTS); + } + + /** + * @param string $columnName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function columnDoesNotExist($columnName, $table) + { + return new self("There is no column with name '$columnName' on table '$table'.", self::COLUMN_DOESNT_EXIST); + } + + /** + * @param string $namespaceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function namespaceAlreadyExists($namespaceName) + { + return new self( + sprintf("The namespace with name '%s' already exists.", $namespaceName), + self::NAMESPACE_ALREADY_EXISTS + ); + } + + /** + * @param string $tableName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function tableAlreadyExists($tableName) + { + return new self("The table with name '".$tableName."' already exists.", self::TABLE_ALREADY_EXISTS); + } + + /** + * @param string $tableName + * @param string $columnName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function columnAlreadyExists($tableName, $columnName) + { + return new self( + "The column '".$columnName."' on table '".$tableName."' already exists.", self::COLUMN_ALREADY_EXISTS + ); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function sequenceAlreadyExists($sequenceName) + { + return new self("The sequence '".$sequenceName."' already exists.", self::SEQUENCE_ALREADY_EXISTS); + } + + /** + * @param string $sequenceName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function sequenceDoesNotExist($sequenceName) + { + return new self("There exists no sequence with the name '".$sequenceName."'.", self::SEQUENCE_DOENST_EXIST); + } + + /** + * @param string $fkName + * @param string $table + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function foreignKeyDoesNotExist($fkName, $table) + { + return new self("There exists no foreign key with the name '$fkName' on table '$table'.", self::FOREIGNKEY_DOESNT_EXIST); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function namedForeignKeyRequired(Table $localTable, ForeignKeyConstraint $foreignKey) + { + return new self( + "The performed schema operation on ".$localTable->getName()." requires a named foreign key, ". + "but the given foreign key from (".implode(", ", $foreignKey->getColumns()).") onto foreign table ". + "'".$foreignKey->getForeignTableName()."' (".implode(", ", $foreignKey->getForeignColumns()).") is currently ". + "unnamed." + ); + } + + /** + * @param string $changeName + * + * @return \Doctrine\DBAL\Schema\SchemaException + */ + static public function alterTableChangeNotSupported($changeName) + { + return new self("Alter table change not supported, given '$changeName'"); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php new file mode 100644 index 0000000..cc5b501 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Sequence.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; + +/** + * Sequence structure. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Sequence extends AbstractAsset +{ + /** + * @var integer + */ + protected $allocationSize = 1; + + /** + * @var integer + */ + protected $initialValue = 1; + + /** + * @var integer|null + */ + protected $cache = null; + + /** + * @param string $name + * @param integer $allocationSize + * @param integer $initialValue + * @param integer|null $cache + */ + public function __construct($name, $allocationSize = 1, $initialValue = 1, $cache = null) + { + $this->_setName($name); + $this->allocationSize = is_numeric($allocationSize) ? $allocationSize : 1; + $this->initialValue = is_numeric($initialValue) ? $initialValue : 1; + $this->cache = $cache; + } + + /** + * @return integer + */ + public function getAllocationSize() + { + return $this->allocationSize; + } + + /** + * @return integer + */ + public function getInitialValue() + { + return $this->initialValue; + } + + /** + * @return integer|null + */ + public function getCache() + { + return $this->cache; + } + + /** + * @param integer $allocationSize + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setAllocationSize($allocationSize) + { + $this->allocationSize = is_numeric($allocationSize) ? $allocationSize : 1; + + return $this; + } + + /** + * @param integer $initialValue + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setInitialValue($initialValue) + { + $this->initialValue = is_numeric($initialValue) ? $initialValue : 1; + + return $this; + } + + /** + * @param integer $cache + * + * @return \Doctrine\DBAL\Schema\Sequence + */ + public function setCache($cache) + { + $this->cache = $cache; + + return $this; + } + + /** + * Checks if this sequence is an autoincrement sequence for a given table. + * + * This is used inside the comparator to not report sequences as missing, + * when the "from" schema implicitly creates the sequences. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return boolean + */ + public function isAutoIncrementsFor(Table $table) + { + if ( ! $table->hasPrimaryKey()) { + return false; + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + + if (count($pkColumns) != 1) { + return false; + } + + $column = $table->getColumn($pkColumns[0]); + + if ( ! $column->getAutoincrement()) { + return false; + } + + $sequenceName = $this->getShortestName($table->getNamespaceName()); + $tableName = $table->getShortestName($table->getNamespaceName()); + $tableSequenceName = sprintf('%s_%s_seq', $tableName, $pkColumns[0]); + + return $tableSequenceName === $sequenceName; + } + + /** + * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptSequence($this); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php new file mode 100644 index 0000000..08c90cf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -0,0 +1,433 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Types\StringType; +use Doctrine\DBAL\Types\TextType; + +/** + * Sqlite SchemaManager. + * + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @author Jonathan H. Wage + * @author Martin Hasoň + * @since 2.0 + */ +class SqliteSchemaManager extends AbstractSchemaManager +{ + /** + * {@inheritdoc} + */ + public function dropDatabase($database) + { + if (file_exists($database)) { + unlink($database); + } + } + + /** + * {@inheritdoc} + */ + public function createDatabase($database) + { + $params = $this->_conn->getParams(); + $driver = $params['driver']; + $options = array( + 'driver' => $driver, + 'path' => $database + ); + $conn = \Doctrine\DBAL\DriverManager::getConnection($options); + $conn->connect(); + $conn->close(); + } + + /** + * {@inheritdoc} + */ + public function renameTable($name, $newName) + { + $tableDiff = new TableDiff($name); + $tableDiff->fromTable = $this->listTableDetails($name); + $tableDiff->newName = $newName; + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->addedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->changedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function dropForeignKey($foreignKey, $table) + { + $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table); + $tableDiff->removedForeignKeys[] = $foreignKey; + + $this->alterTable($tableDiff); + } + + /** + * {@inheritdoc} + */ + public function listTableForeignKeys($table, $database = null) + { + if (null === $database) { + $database = $this->_conn->getDatabase(); + } + $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); + $tableForeignKeys = $this->_conn->fetchAll($sql); + + if ( ! empty($tableForeignKeys)) { + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + if (preg_match_all('# + (?:CONSTRAINT\s+([^\s]+)\s+)? + (?:FOREIGN\s+KEY[^\)]+\)\s*)? + REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))? + (?: + [^,]*? + (NOT\s+DEFERRABLE|DEFERRABLE) + (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? + )?#isx', + $createSql, $match)) { + + $names = array_reverse($match[1]); + $deferrable = array_reverse($match[2]); + $deferred = array_reverse($match[3]); + } else { + $names = $deferrable = $deferred = array(); + } + + foreach ($tableForeignKeys as $key => $value) { + $id = $value['id']; + $tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && '' != $names[$id] ? $names[$id] : $id; + $tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && 'deferrable' == strtolower($deferrable[$id]) ? true : false; + $tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && 'deferred' == strtolower($deferred[$id]) ? true : false; + } + } + + return $this->_getPortableTableForeignKeysList($tableForeignKeys); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableDefinition($table) + { + return $table['name']; + } + + /** + * {@inheritdoc} + * + * @license New BSD License + * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + + // fetch primary + $stmt = $this->_conn->executeQuery("PRAGMA TABLE_INFO ('$tableName')"); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + usort($indexArray, function($a, $b) { + if ($a['pk'] == $b['pk']) { + return $a['cid'] - $b['cid']; + } + + return $a['pk'] - $b['pk']; + }); + foreach ($indexArray as $indexColumnRow) { + if ($indexColumnRow['pk'] != "0") { + $indexBuffer[] = array( + 'key_name' => 'primary', + 'primary' => true, + 'non_unique' => false, + 'column_name' => $indexColumnRow['name'] + ); + } + } + + // fetch regular indexes + foreach ($tableIndexes as $tableIndex) { + // Ignore indexes with reserved names, e.g. autoindexes + if (strpos($tableIndex['name'], 'sqlite_') !== 0) { + $keyName = $tableIndex['name']; + $idx = array(); + $idx['key_name'] = $keyName; + $idx['primary'] = false; + $idx['non_unique'] = $tableIndex['unique']?false:true; + + $stmt = $this->_conn->executeQuery("PRAGMA INDEX_INFO ('{$keyName}')"); + $indexArray = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + foreach ($indexArray as $indexColumnRow) { + $idx['column_name'] = $indexColumnRow['name']; + $indexBuffer[] = $idx; + } + } + } + + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableIndexDefinition($tableIndex) + { + return array( + 'name' => $tableIndex['name'], + 'unique' => (bool) $tableIndex['unique'] + ); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnList($table, $database, $tableColumns) + { + $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); + + // find column with autoincrement + $autoincrementColumn = null; + $autoincrementCount = 0; + + foreach ($tableColumns as $tableColumn) { + if ('0' != $tableColumn['pk']) { + $autoincrementCount++; + if (null === $autoincrementColumn && 'integer' == strtolower($tableColumn['type'])) { + $autoincrementColumn = $tableColumn['name']; + } + } + } + + if (1 == $autoincrementCount && null !== $autoincrementColumn) { + foreach ($list as $column) { + if ($autoincrementColumn == $column->getName()) { + $column->setAutoincrement(true); + } + } + } + + // inspect column collation + $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'"); + $createSql = isset($createSql[0]['sql']) ? $createSql[0]['sql'] : ''; + + foreach ($list as $columnName => $column) { + $type = $column->getType(); + + if ($type instanceof StringType || $type instanceof TextType) { + $column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY'); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableColumnDefinition($tableColumn) + { + $parts = explode('(', $tableColumn['type']); + $tableColumn['type'] = $parts[0]; + if (isset($parts[1])) { + $length = trim($parts[1], ')'); + $tableColumn['length'] = $length; + } + + $dbType = strtolower($tableColumn['type']); + $length = isset($tableColumn['length']) ? $tableColumn['length'] : null; + $unsigned = false; + + if (strpos($dbType, ' unsigned') !== false) { + $dbType = str_replace(' unsigned', '', $dbType); + $unsigned = true; + } + + $fixed = false; + $type = $this->_platform->getDoctrineTypeMapping($dbType); + $default = $tableColumn['dflt_value']; + if ($default == 'NULL') { + $default = null; + } + if ($default !== null) { + // SQLite returns strings wrapped in single quotes, so we need to strip them + $default = preg_replace("/^'(.*)'$/", '\1', $default); + } + $notnull = (bool) $tableColumn['notnull']; + + if ( ! isset($tableColumn['name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + switch ($dbType) { + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'real': + case 'decimal': + case 'numeric': + if (isset($tableColumn['length'])) { + if (strpos($tableColumn['length'], ',') === false) { + $tableColumn['length'] .= ",0"; + } + list($precision, $scale) = array_map('trim', explode(',', $tableColumn['length'])); + } + $length = null; + break; + } + + $options = array( + 'length' => $length, + 'unsigned' => (bool) $unsigned, + 'fixed' => $fixed, + 'notnull' => $notnull, + 'default' => $default, + 'precision' => $precision, + 'scale' => $scale, + 'autoincrement' => false, + ); + + return new Column($tableColumn['name'], \Doctrine\DBAL\Types\Type::getType($type), $options); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableViewDefinition($view) + { + return new View($view['name'], $view['sql']); + } + + /** + * {@inheritdoc} + */ + protected function _getPortableTableForeignKeysList($tableForeignKeys) + { + $list = array(); + foreach ($tableForeignKeys as $value) { + $value = array_change_key_case($value, CASE_LOWER); + $name = $value['constraint_name']; + if ( ! isset($list[$name])) { + if ( ! isset($value['on_delete']) || $value['on_delete'] == "RESTRICT") { + $value['on_delete'] = null; + } + if ( ! isset($value['on_update']) || $value['on_update'] == "RESTRICT") { + $value['on_update'] = null; + } + + $list[$name] = array( + 'name' => $name, + 'local' => array(), + 'foreign' => array(), + 'foreignTable' => $value['table'], + 'onDelete' => $value['on_delete'], + 'onUpdate' => $value['on_update'], + 'deferrable' => $value['deferrable'], + 'deferred'=> $value['deferred'], + ); + } + $list[$name]['local'][] = $value['from']; + $list[$name]['foreign'][] = $value['to']; + } + + $result = array(); + foreach ($list as $constraint) { + $result[] = new ForeignKeyConstraint( + array_values($constraint['local']), $constraint['foreignTable'], + array_values($constraint['foreign']), $constraint['name'], + array( + 'onDelete' => $constraint['onDelete'], + 'onUpdate' => $constraint['onUpdate'], + 'deferrable' => $constraint['deferrable'], + 'deferred'=> $constraint['deferred'], + ) + ); + } + + return $result; + } + + /** + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + * @param \Doctrine\DBAL\Schema\Table|string $table + * + * @return \Doctrine\DBAL\Schema\TableDiff + * + * @throws \Doctrine\DBAL\DBALException + */ + private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table) + { + if ( ! $table instanceof Table) { + $tableDetails = $this->tryMethod('listTableDetails', $table); + if (false === $table) { + throw new DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table)); + } + + $table = $tableDetails; + } + + $tableDiff = new TableDiff($table->getName()); + $tableDiff->fromTable = $table; + + return $tableDiff; + } + + private function parseColumnCollationFromSQL($column, $sql) + { + if (preg_match( + '{(?:'.preg_quote($column).'|'.preg_quote($this->_platform->quoteSingleIdentifier($column)).') + [^,(]+(?:\([^()]+\)[^,]*)? + (?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)* + COLLATE\s+["\']?([^\s,"\')]+)}isx', $sql, $match)) { + return $match[1]; + } + + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php new file mode 100644 index 0000000..3158df4 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/AbstractSchemaSynchronizer.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; + +/** + * Abstract schema synchronizer with methods for executing batches of SQL. + */ +abstract class AbstractSchemaSynchronizer implements SchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + } + + /** + * @param array $sql + */ + protected function processSqlSafely(array $sql) + { + foreach ($sql as $s) { + try { + $this->conn->exec($s); + } catch (\Exception $e) { + + } + } + } + + /** + * @param array $sql + */ + protected function processSql(array $sql) + { + foreach ($sql as $s) { + $this->conn->exec($s); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php new file mode 100644 index 0000000..1a5552e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SchemaSynchronizer.php @@ -0,0 +1,101 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Schema\Schema; + +/** + * The synchronizer knows how to synchronize a schema with the configured + * database. + * + * @author Benjamin Eberlei + */ +interface SchemaSynchronizer +{ + /** + * Gets the SQL statements that can be executed to create the schema. + * + * @param \Doctrine\DBAL\Schema\Schema $createSchema + * + * @return array + */ + function getCreateSchema(Schema $createSchema); + + /** + * Gets the SQL Statements to update given schema with the underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param boolean $noDrops + * + * @return array + */ + function getUpdateSchema(Schema $toSchema, $noDrops = false); + + /** + * Gets the SQL Statements to drop the given schema from underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $dropSchema + * + * @return array + */ + function getDropSchema(Schema $dropSchema); + + /** + * Gets the SQL statements to drop all schema assets from underlying db. + * + * @return array + */ + function getDropAllSchema(); + + /** + * Creates the Schema. + * + * @param \Doctrine\DBAL\Schema\Schema $createSchema + * + * @return void + */ + function createSchema(Schema $createSchema); + + /** + * Updates the Schema to new schema version. + * + * @param \Doctrine\DBAL\Schema\Schema $toSchema + * @param boolean $noDrops + * + * @return void + */ + function updateSchema(Schema $toSchema, $noDrops = false); + + /** + * Drops the given database schema from the underlying db. + * + * @param \Doctrine\DBAL\Schema\Schema $dropSchema + * + * @return void + */ + function dropSchema(Schema $dropSchema); + + /** + * Drops all assets from the underlying db. + * + * @return void + */ + function dropAllSchema(); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php new file mode 100644 index 0000000..7cab7a5 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Synchronizer/SingleDatabaseSynchronizer.php @@ -0,0 +1,176 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Synchronizer; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; + +/** + * Schema Synchronizer for Default DBAL Connection. + * + * @author Benjamin Eberlei + */ +class SingleDatabaseSynchronizer extends AbstractSchemaSynchronizer +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @param \Doctrine\DBAL\Connection $conn + */ + public function __construct(Connection $conn) + { + parent::__construct($conn); + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * {@inheritdoc} + */ + public function getCreateSchema(Schema $createSchema) + { + return $createSchema->toSql($this->platform); + } + + + /** + * {@inheritdoc} + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + $comparator = new Comparator(); + $sm = $this->conn->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($noDrops) { + return $schemaDiff->toSaveSql($this->platform); + } + + return $schemaDiff->toSql($this->platform); + } + + /** + * {@inheritdoc} + */ + public function getDropSchema(Schema $dropSchema) + { + $visitor = new DropSchemaSqlCollector($this->platform); + $sm = $this->conn->getSchemaManager(); + + $fullSchema = $sm->createSchema(); + + foreach ($fullSchema->getTables() as $table) { + if ($dropSchema->hasTable($table->getName())) { + $visitor->acceptTable($table); + } + + foreach ($table->getForeignKeys() as $foreignKey) { + if ( ! $dropSchema->hasTable($table->getName())) { + continue; + } + + if ( ! $dropSchema->hasTable($foreignKey->getForeignTableName())) { + continue; + } + + $visitor->acceptForeignKey($table, $foreignKey); + } + } + + if ( ! $this->platform->supportsSequences()) { + return $visitor->getQueries(); + } + + foreach ($dropSchema->getSequences() as $sequence) { + $visitor->acceptSequence($sequence); + } + + foreach ($dropSchema->getTables() as $table) { + if ( ! $table->hasPrimaryKey()) { + continue; + } + + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) > 1) { + continue; + } + + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + + return $visitor->getQueries(); + } + + /** + * {@inheritdoc} + */ + public function getDropAllSchema() + { + $sm = $this->conn->getSchemaManager(); + $visitor = new DropSchemaSqlCollector($this->platform); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $sm->createSchema(); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * {@inheritdoc} + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * {@inheritdoc} + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * {@inheritdoc} + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * {@inheritdoc} + */ + public function dropAllSchema() + { + $this->processSql($this->getDropAllSchema()); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php new file mode 100644 index 0000000..857caeb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php @@ -0,0 +1,850 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\DBALException; + +/** + * Object Representation of a table. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class Table extends AbstractAsset +{ + /** + * @var string + */ + protected $_name = null; + + /** + * @var Column[] + */ + protected $_columns = array(); + + /** + * @var Index[] + */ + private $implicitIndexes = array(); + + /** + * @var Index[] + */ + protected $_indexes = array(); + + /** + * @var string + */ + protected $_primaryKeyName = false; + + /** + * @var ForeignKeyConstraint[] + */ + protected $_fkConstraints = array(); + + /** + * @var array + */ + protected $_options = array(); + + /** + * @var SchemaConfig + */ + protected $_schemaConfig = null; + + /** + * @param string $tableName + * @param Column[] $columns + * @param Index[] $indexes + * @param ForeignKeyConstraint[] $fkConstraints + * @param integer $idGeneratorType + * @param array $options + * + * @throws DBALException + */ + public function __construct($tableName, array $columns=array(), array $indexes=array(), array $fkConstraints=array(), $idGeneratorType = 0, array $options=array()) + { + if (strlen($tableName) == 0) { + throw DBALException::invalidTableName($tableName); + } + + $this->_setName($tableName); + + foreach ($columns as $column) { + $this->_addColumn($column); + } + + foreach ($indexes as $idx) { + $this->_addIndex($idx); + } + + foreach ($fkConstraints as $constraint) { + $this->_addForeignKeyConstraint($constraint); + } + + $this->_options = $options; + } + + /** + * @param SchemaConfig $schemaConfig + * + * @return void + */ + public function setSchemaConfig(SchemaConfig $schemaConfig) + { + $this->_schemaConfig = $schemaConfig; + } + + /** + * @return integer + */ + protected function _getMaxIdentifierLength() + { + if ($this->_schemaConfig instanceof SchemaConfig) { + return $this->_schemaConfig->getMaxIdentifierLength(); + } else { + return 63; + } + } + + /** + * Sets the Primary Key. + * + * @param array $columns + * @param string|boolean $indexName + * + * @return self + */ + public function setPrimaryKey(array $columns, $indexName = false) + { + $this->_addIndex($this->_createIndex($columns, $indexName ?: "primary", true, true)); + + foreach ($columns as $columnName) { + $column = $this->getColumn($columnName); + $column->setNotnull(true); + } + + return $this; + } + + /** + * @param array $columnNames + * @param string|null $indexName + * @param array $flags + * @param array $options + * + * @return self + */ + public function addIndex(array $columnNames, $indexName = null, array $flags = array(), array $options = array()) + { + if ($indexName == null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "idx", $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); + } + + /** + * Drops the primary key from this table. + * + * @return void + */ + public function dropPrimaryKey() + { + $this->dropIndex($this->_primaryKeyName); + $this->_primaryKeyName = false; + } + + /** + * Drops an index from this table. + * + * @param string $indexName The index name. + * + * @return void + * + * @throws SchemaException If the index does not exist. + */ + public function dropIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + unset($this->_indexes[$indexName]); + } + + /** + * @param array $columnNames + * @param string|null $indexName + * @param array $options + * + * @return self + */ + public function addUniqueIndex(array $columnNames, $indexName = null, array $options = array()) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $columnNames), "uniq", $this->_getMaxIdentifierLength() + ); + } + + return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, array(), $options)); + } + + /** + * Renames an index. + * + * @param string $oldIndexName The name of the index to rename from. + * @param string|null $newIndexName The name of the index to rename to. + * If null is given, the index name will be auto-generated. + * + * @return self This table instance. + * + * @throws SchemaException if no index exists for the given current name + * or if an index with the given new name already exists on this table. + */ + public function renameIndex($oldIndexName, $newIndexName = null) + { + $oldIndexName = $this->normalizeIdentifier($oldIndexName); + $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName); + + if ($oldIndexName === $normalizedNewIndexName) { + return $this; + } + + if ( ! $this->hasIndex($oldIndexName)) { + throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name); + } + + if ($this->hasIndex($normalizedNewIndexName)) { + throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name); + } + + $oldIndex = $this->_indexes[$oldIndexName]; + + if ($oldIndex->isPrimary()) { + $this->dropPrimaryKey(); + + return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName); + } + + unset($this->_indexes[$oldIndexName]); + + if ($oldIndex->isUnique()) { + return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName); + } + + return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags()); + } + + /** + * Checks if an index begins in the order of the given columns. + * + * @param array $columnsNames + * + * @return boolean + */ + public function columnsAreIndexed(array $columnsNames) + { + foreach ($this->getIndexes() as $index) { + /* @var $index Index */ + if ($index->spansColumns($columnsNames)) { + return true; + } + } + + return false; + } + + /** + * @param array $columnNames + * @param string $indexName + * @param boolean $isUnique + * @param boolean $isPrimary + * @param array $flags + * @param array $options + * + * @return Index + * + * @throws SchemaException + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = array(), array $options = array()) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName => $indexColOptions) { + if (is_numeric($columnName) && is_string($indexColOptions)) { + $columnName = $indexColOptions; + } + + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); + } + + /** + * @param string $columnName + * @param string $typeName + * @param array $options + * + * @return Column + */ + public function addColumn($columnName, $typeName, array $options=array()) + { + $column = new Column($columnName, Type::getType($typeName), $options); + + $this->_addColumn($column); + + return $column; + } + + /** + * Renames a Column. + * + * @param string $oldColumnName + * @param string $newColumnName + * + * @return self + * + * @throws DBALException + */ + public function renameColumn($oldColumnName, $newColumnName) + { + throw new DBALException("Table#renameColumn() was removed, because it drops and recreates " . + "the column instead. There is no fix available, because a schema diff cannot reliably detect if a " . + "column was renamed or one column was created and another one dropped."); + } + + /** + * Change Column Details. + * + * @param string $columnName + * @param array $options + * + * @return self + */ + public function changeColumn($columnName, array $options) + { + $column = $this->getColumn($columnName); + $column->setOptions($options); + + return $this; + } + + /** + * Drops a Column from the Table. + * + * @param string $columnName + * + * @return self + */ + public function dropColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + unset($this->_columns[$columnName]); + + return $this; + } + + /** + * Adds a foreign key constraint. + * + * Name is inferred from the local columns. + * + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * @param string|null $constraintName + * + * @return self + */ + public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array(), $constraintName = null) + { + $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), "fk", $this->_getMaxIdentifierLength()); + + return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Adds a foreign key constraint. + * + * Name is to be generated by the database itself. + * + * @deprecated Use {@link addForeignKeyConstraint} + * + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * + * @return self + */ + public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options); + } + + /** + * Adds a foreign key constraint with a given name. + * + * @deprecated Use {@link addForeignKeyConstraint} + * + * @param string $name + * @param Table|string $foreignTable Table schema instance or table name + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options + * + * @return self + * + * @throws SchemaException + */ + public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options=array()) + { + if ($foreignTable instanceof Table) { + foreach ($foreignColumnNames as $columnName) { + if ( ! $foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); + } + } + } + + foreach ($localColumnNames as $columnName) { + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + $constraint = new ForeignKeyConstraint( + $localColumnNames, $foreignTable, $foreignColumnNames, $name, $options + ); + $this->_addForeignKeyConstraint($constraint); + + return $this; + } + + /** + * @param string $name + * @param string $value + * + * @return self + */ + public function addOption($name, $value) + { + $this->_options[$name] = $value; + + return $this; + } + + /** + * @param Column $column + * + * @return void + * + * @throws SchemaException + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = $this->normalizeIdentifier($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Adds an index to the table. + * + * @param Index $indexCandidate + * + * @return self + * + * @throws SchemaException + */ + protected function _addIndex(Index $indexCandidate) + { + $indexName = $indexCandidate->getName(); + $indexName = $this->normalizeIdentifier($indexName); + $replacedImplicitIndexes = array(); + + foreach ($this->implicitIndexes as $name => $implicitIndex) { + if ($implicitIndex->isFullfilledBy($indexCandidate) && isset($this->_indexes[$name])) { + $replacedImplicitIndexes[] = $name; + } + } + + if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || + ($this->_primaryKeyName != false && $indexCandidate->isPrimary()) + ) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + foreach ($replacedImplicitIndexes as $name) { + unset($this->_indexes[$name], $this->implicitIndexes[$name]); + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + + return $this; + } + + /** + * @param ForeignKeyConstraint $constraint + * + * @return void + */ + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + { + $constraint->setLocalTable($this); + + if (strlen($constraint->getName())) { + $name = $constraint->getName(); + } else { + $name = $this->_generateIdentifierName( + array_merge((array) $this->getName(), $constraint->getLocalColumns()), "fk", $this->_getMaxIdentifierLength() + ); + } + $name = $this->normalizeIdentifier($name); + + $this->_fkConstraints[$name] = $constraint; + + // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request. + // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes + // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns). + $indexName = $this->_generateIdentifierName( + array_merge(array($this->getName()), $constraint->getColumns()), + "idx", + $this->_getMaxIdentifierLength() + ); + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return; + } + } + + $this->_addIndex($indexCandidate); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + } + + /** + * Returns whether this table has a foreign key constraint with the given name. + * + * @param string $constraintName + * + * @return boolean + */ + public function hasForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + + return isset($this->_fkConstraints[$constraintName]); + } + + /** + * Returns the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * + * @return ForeignKeyConstraint + * + * @throws SchemaException If the foreign key does not exist. + */ + public function getForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + if (!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + return $this->_fkConstraints[$constraintName]; + } + + /** + * Removes the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * + * @return void + * + * @throws SchemaException + */ + public function removeForeignKey($constraintName) + { + $constraintName = $this->normalizeIdentifier($constraintName); + if (!$this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + } + + unset($this->_fkConstraints[$constraintName]); + } + + /** + * @return Column[] + */ + public function getColumns() + { + $columns = $this->_columns; + + $pkCols = array(); + $fkCols = array(); + + if ($this->hasPrimaryKey()) { + $pkCols = $this->getPrimaryKey()->getColumns(); + } + foreach ($this->getForeignKeys() as $fk) { + /* @var $fk ForeignKeyConstraint */ + $fkCols = array_merge($fkCols, $fk->getColumns()); + } + $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns))); + + uksort($columns, function ($a, $b) use ($colNames) { + return (array_search($a, $colNames) >= array_search($b, $colNames)); + }); + + return $columns; + } + + /** + * Returns whether this table has a Column with the given name. + * + * @param string $columnName The column name. + * + * @return boolean + */ + public function hasColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + + return isset($this->_columns[$columnName]); + } + + /** + * Returns the Column with the given name. + * + * @param string $columnName The column name. + * + * @return Column + * + * @throws SchemaException If the column does not exist. + */ + public function getColumn($columnName) + { + $columnName = $this->normalizeIdentifier($columnName); + if ( ! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + + return $this->_columns[$columnName]; + } + + /** + * Returns the primary key. + * + * @return Index|null The primary key, or null if this Table has no primary key. + */ + public function getPrimaryKey() + { + if ( ! $this->hasPrimaryKey()) { + return null; + } + + return $this->getIndex($this->_primaryKeyName); + } + + /** + * Returns the primary key columns. + * + * @return array + * + * @throws DBALException + */ + public function getPrimaryKeyColumns() + { + if ( ! $this->hasPrimaryKey()) { + throw new DBALException("Table " . $this->getName() . " has no primary key."); + } + + return $this->getPrimaryKey()->getColumns(); + } + + /** + * Returns whether this table has a primary key. + * + * @return boolean + */ + public function hasPrimaryKey() + { + return ($this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName)); + } + + /** + * Returns whether this table has an Index with the given name. + * + * @param string $indexName The index name. + * + * @return boolean + */ + public function hasIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + + return (isset($this->_indexes[$indexName])); + } + + /** + * Returns the Index with the given name. + * + * @param string $indexName The index name. + * + * @return Index + * + * @throws SchemaException If the index does not exist. + */ + public function getIndex($indexName) + { + $indexName = $this->normalizeIdentifier($indexName); + if ( ! $this->hasIndex($indexName)) { + throw SchemaException::indexDoesNotExist($indexName, $this->_name); + } + + return $this->_indexes[$indexName]; + } + + /** + * @return Index[] + */ + public function getIndexes() + { + return $this->_indexes; + } + + /** + * Returns the foreign key constraints. + * + * @return ForeignKeyConstraint[] + */ + public function getForeignKeys() + { + return $this->_fkConstraints; + } + + /** + * @param string $name + * + * @return boolean + */ + public function hasOption($name) + { + return isset($this->_options[$name]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->_options[$name]; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } + + /** + * @param Visitor $visitor + * + * @return void + */ + public function visit(Visitor $visitor) + { + $visitor->acceptTable($this); + + foreach ($this->getColumns() as $column) { + $visitor->acceptColumn($this, $column); + } + + foreach ($this->getIndexes() as $index) { + $visitor->acceptIndex($this, $index); + } + + foreach ($this->getForeignKeys() as $constraint) { + $visitor->acceptForeignKey($this, $constraint); + } + } + + /** + * Clone of a Table triggers a deep clone of all affected assets. + * + * @return void + */ + public function __clone() + { + foreach ($this->_columns as $k => $column) { + $this->_columns[$k] = clone $column; + } + foreach ($this->_indexes as $k => $index) { + $this->_indexes[$k] = clone $index; + } + foreach ($this->_fkConstraints as $k => $fk) { + $this->_fkConstraints[$k] = clone $fk; + $this->_fkConstraints[$k]->setLocalTable($this); + } + } + + /** + * Normalizes a given identifier. + * + * Trims quotes and lowercases the given identifier. + * + * @param string $identifier The identifier to normalize. + * + * @return string The normalized identifier. + */ + private function normalizeIdentifier($identifier) + { + return $this->trimQuotes(strtolower($identifier)); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php new file mode 100644 index 0000000..a837fab --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php @@ -0,0 +1,170 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Table Diff. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class TableDiff +{ + /** + * @var string + */ + public $name = null; + + /** + * @var string|boolean + */ + public $newName = false; + + /** + * All added fields. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $addedColumns; + + /** + * All changed fields. + * + * @var \Doctrine\DBAL\Schema\ColumnDiff[] + */ + public $changedColumns = array(); + + /** + * All removed fields. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $removedColumns = array(); + + /** + * Columns that are only renamed from key to column instance name. + * + * @var \Doctrine\DBAL\Schema\Column[] + */ + public $renamedColumns = array(); + + /** + * All added indexes. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $addedIndexes = array(); + + /** + * All changed indexes. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $changedIndexes = array(); + + /** + * All removed indexes + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $removedIndexes = array(); + + /** + * Indexes that are only renamed but are identical otherwise. + * + * @var \Doctrine\DBAL\Schema\Index[] + */ + public $renamedIndexes = array(); + + /** + * All added foreign key definitions + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $addedForeignKeys = array(); + + /** + * All changed foreign keys + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $changedForeignKeys = array(); + + /** + * All removed foreign keys + * + * @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] + */ + public $removedForeignKeys = array(); + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + public $fromTable; + + /** + * Constructs an TableDiff object. + * + * @param string $tableName + * @param \Doctrine\DBAL\Schema\Column[] $addedColumns + * @param \Doctrine\DBAL\Schema\ColumnDiff[] $changedColumns + * @param \Doctrine\DBAL\Schema\Column[] $removedColumns + * @param \Doctrine\DBAL\Schema\Index[] $addedIndexes + * @param \Doctrine\DBAL\Schema\Index[] $changedIndexes + * @param \Doctrine\DBAL\Schema\Index[] $removedIndexes + * @param \Doctrine\DBAL\Schema\Table|null $fromTable + */ + public function __construct($tableName, $addedColumns = array(), + $changedColumns = array(), $removedColumns = array(), $addedIndexes = array(), + $changedIndexes = array(), $removedIndexes = array(), Table $fromTable = null) + { + $this->name = $tableName; + $this->addedColumns = $addedColumns; + $this->changedColumns = $changedColumns; + $this->removedColumns = $removedColumns; + $this->addedIndexes = $addedIndexes; + $this->changedIndexes = $changedIndexes; + $this->removedIndexes = $removedIndexes; + $this->fromTable = $fromTable; + } + + /** + * @param AbstractPlatform $platform The platform to use for retrieving this table diff's name. + * + * @return \Doctrine\DBAL\Schema\Identifier + */ + public function getName(AbstractPlatform $platform) + { + return new Identifier( + $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name + ); + } + + /** + * @return \Doctrine\DBAL\Schema\Identifier|boolean + */ + public function getNewName() + { + return $this->newName ? new Identifier($this->newName) : $this->newName; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php new file mode 100644 index 0000000..0ef7d30 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/View.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +/** + * Representation of a Database View. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + */ +class View extends AbstractAsset +{ + /** + * @var string + */ + private $_sql; + + /** + * @param string $name + * @param string $sql + */ + public function __construct($name, $sql) + { + $this->_setName($name); + $this->_sql = $sql; + } + + /** + * @return string + */ + public function getSql() + { + return $this->_sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php new file mode 100644 index 0000000..7bf5b2f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/AbstractVisitor.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Abstract Visitor with empty methods for easy extension. + */ +class AbstractVisitor implements Visitor, NamespaceVisitor +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptNamespace($namespaceName) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + public function acceptTable(Table $table) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Column $column + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $fkConstraint + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Index $index + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php new file mode 100644 index 0000000..5e0c6ed --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -0,0 +1,148 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +class CreateSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var array + */ + private $createNamespaceQueries = array(); + + /** + * @var array + */ + private $createTableQueries = array(); + + /** + * @var array + */ + private $createSequenceQueries = array(); + + /** + * @var array + */ + private $createFkConstraintQueries = array(); + + /** + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform = null; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + } + + /** + * {@inheritdoc} + */ + public function acceptNamespace($namespaceName) + { + if ($this->platform->supportsSchemas()) { + $this->createNamespaceQueries = array_merge( + $this->createNamespaceQueries, + (array) $this->platform->getCreateSchemaSQL($namespaceName) + ); + } + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->createTableQueries = array_merge($this->createTableQueries, (array) $this->platform->getCreateTableSQL($table)); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if ($this->platform->supportsForeignKeyConstraints()) { + $this->createFkConstraintQueries = array_merge( + $this->createFkConstraintQueries, + (array) $this->platform->getCreateForeignKeySQL( + $fkConstraint, $localTable + ) + ); + } + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + $this->createSequenceQueries = array_merge( + $this->createSequenceQueries, + (array) $this->platform->getCreateSequenceSQL($sequence) + ); + } + + /** + * @return void + */ + public function resetQueries() + { + $this->createNamespaceQueries = array(); + $this->createTableQueries = array(); + $this->createSequenceQueries = array(); + $this->createFkConstraintQueries = array(); + } + + /** + * Gets all queries collected so far. + * + * @return array + */ + public function getQueries() + { + $sql = array(); + + foreach ($this->createNamespaceQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + foreach ($this->createTableQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + foreach ($this->createSequenceQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + foreach ($this->createFkConstraintQueries as $schemaSql) { + $sql = array_merge($sql, (array) $schemaSql); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php new file mode 100644 index 0000000..ab9752d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/DropSchemaSqlCollector.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\SchemaException; + +/** + * Gathers SQL statements that allow to completely drop the current schema. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DropSchemaSqlCollector extends AbstractVisitor +{ + /** + * @var \SplObjectStorage + */ + private $constraints; + + /** + * @var \SplObjectStorage + */ + private $sequences; + + /** + * @var \SplObjectStorage + */ + private $tables; + + /** + * @var AbstractPlatform + */ + private $platform; + + /** + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + $this->clearQueries(); + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->tables->attach($table); + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + if (strlen($fkConstraint->getName()) == 0) { + throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); + } + + $this->constraints->attach($fkConstraint, $localTable); + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + $this->sequences->attach($sequence); + } + + /** + * @return void + */ + public function clearQueries() + { + $this->constraints = new \SplObjectStorage(); + $this->sequences = new \SplObjectStorage(); + $this->tables = new \SplObjectStorage(); + } + + /** + * @return array + */ + public function getQueries() + { + $sql = array(); + + foreach ($this->constraints as $fkConstraint) { + $localTable = $this->constraints[$fkConstraint]; + $sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable); + } + + foreach ($this->sequences as $sequence) { + $sql[] = $this->platform->getDropSequenceSQL($sequence); + } + + foreach ($this->tables as $table) { + $sql[] = $this->platform->getDropTableSQL($table); + } + + return $sql; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php new file mode 100644 index 0000000..1573ddf --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Graphviz.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; + +/** + * Create a Graphviz output of a Schema. + */ +class Graphviz extends AbstractVisitor +{ + /** + * @var string + */ + private $output = ''; + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + $this->output .= $this->createNodeRelation( + $fkConstraint->getLocalTableName() . ":col" . current($fkConstraint->getLocalColumns()).":se", + $fkConstraint->getForeignTableName() . ":col" . current($fkConstraint->getForeignColumns()).":se", + array( + 'dir' => 'back', + 'arrowtail' => 'dot', + 'arrowhead' => 'normal', + ) + ); + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $this->output = 'digraph "' . sha1(mt_rand()) . '" {' . "\n"; + $this->output .= 'splines = true;' . "\n"; + $this->output .= 'overlap = false;' . "\n"; + $this->output .= 'outputorder=edgesfirst;'."\n"; + $this->output .= 'mindist = 0.6;' . "\n"; + $this->output .= 'sep = .2;' . "\n"; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + $this->output .= $this->createNode( + $table->getName(), + array( + 'label' => $this->createTableLabel($table), + 'shape' => 'plaintext', + ) + ); + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return string + */ + private function createTableLabel(Table $table) + { + // Start the table + $label = '<'; + + // The title + $label .= ''; + + // The attributes block + foreach ($table->getColumns() as $column) { + $columnLabel = $column->getName(); + + $label .= ''; + $label .= ''; + $label .= ''; + } + + // End the table + $label .= '
' . $table->getName() . '
'; + $label .= '' . $columnLabel . ''; + $label .= '' . strtolower($column->getType()) . ''; + if ($table->hasPrimaryKey() && in_array($column->getName(), $table->getPrimaryKey()->getColumns())) { + $label .= "\xe2\x9c\xb7"; + } + $label .= '
>'; + + return $label; + } + + /** + * @param string $name + * @param array $options + * + * @return string + */ + private function createNode($name, $options) + { + $node = $name . " ["; + foreach ($options as $key => $value) { + $node .= $key . '=' . $value . ' '; + } + $node .= "]\n"; + + return $node; + } + + /** + * @param string $node1 + * @param string $node2 + * @param array $options + * + * @return string + */ + private function createNodeRelation($node1, $node2, $options) + { + $relation = $node1 . ' -> ' . $node2 . ' ['; + foreach ($options as $key => $value) { + $relation .= $key . '=' . $value . ' '; + } + $relation .= "]\n"; + + return $relation; + } + + /** + * Get Graphviz Output + * + * @return string + */ + public function getOutput() + { + return $this->output . "}"; + } + + /** + * Writes dot language output to a file. This should usually be a *.dot file. + * + * You have to convert the output into a viewable format. For example use "neato" on linux systems + * and execute: + * + * neato -Tpng -o er.png er.dot + * + * @param string $filename + * + * @return void + */ + public function write($filename) + { + file_put_contents($filename, $this->getOutput()); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php new file mode 100644 index 0000000..da7b1ed --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/NamespaceVisitor.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +/** + * Visitor that can visit schema namespaces. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface NamespaceVisitor +{ + /** + * Accepts a schema namespace name. + * + * @param string $namespaceName The schema namespace name to accept. + */ + public function acceptNamespace($namespaceName); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php new file mode 100644 index 0000000..e328d07 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/RemoveNamespacedAssets.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +/** + * Removes assets from a schema that are not in the default namespace. + * + * Some databases such as MySQL support cross databases joins, but don't + * allow to call DDLs to a database from another connected database. + * Before a schema is serialized into SQL this visitor can cleanup schemas with + * non default namespaces. + * + * This visitor filters all these non-default namespaced tables and sequences + * and removes them from the SChema instance. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class RemoveNamespacedAssets extends AbstractVisitor +{ + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + $this->schema = $schema; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + if ( ! $table->isInDefaultNamespace($this->schema->getName())) { + $this->schema->dropTable($table->getName()); + } + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + if ( ! $sequence->isInDefaultNamespace($this->schema->getName())) { + $this->schema->dropSequence($sequence->getName()); + } + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + // The table may already be deleted in a previous + // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that + // point to nowhere. + if ( ! $this->schema->hasTable($fkConstraint->getForeignTableName())) { + $localTable->removeForeignKey($fkConstraint->getName()); + return; + } + + $foreignTable = $this->schema->getTable($fkConstraint->getForeignTableName()); + if ( ! $foreignTable->isInDefaultNamespace($this->schema->getName())) { + $localTable->removeForeignKey($fkConstraint->getName()); + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php new file mode 100644 index 0000000..bde4453 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/SchemaDiffVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; + +/** + * Visit a SchemaDiff. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Benjamin Eberlei + */ +interface SchemaDiffVisitor +{ + /** + * Visit an orphaned foreign key whose table was deleted. + * + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + */ + function visitOrphanedForeignKey(ForeignKeyConstraint $foreignKey); + + /** + * Visit a sequence that has changed. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitChangedSequence(Sequence $sequence); + + /** + * Visit a sequence that has been removed. + * + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitRemovedSequence(Sequence $sequence); + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + */ + function visitNewSequence(Sequence $sequence); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + function visitNewTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey + */ + function visitNewTableForeignKey(Table $table, ForeignKeyConstraint $foreignKey); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + */ + function visitRemovedTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\TableDiff $tableDiff + */ + function visitChangedTable(TableDiff $tableDiff); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php new file mode 100644 index 0000000..656f0e2 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Visitor/Visitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\DBAL\Schema\Visitor; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Schema Visitor used for Validation or Generation purposes. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +interface Visitor +{ + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * + * @return void + */ + public function acceptSchema(Schema $schema); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return void + */ + public function acceptTable(Table $table); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return void + */ + public function acceptColumn(Table $table, Column $column); + + /** + * @param \Doctrine\DBAL\Schema\Table $localTable + * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $fkConstraint + * + * @return void + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint); + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param \Doctrine\DBAL\Schema\Index $index + * + * @return void + */ + public function acceptIndex(Table $table, Index $index); + + /** + * @param \Doctrine\DBAL\Schema\Sequence $sequence + * + * @return void + */ + public function acceptSequence(Sequence $sequence); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php new file mode 100644 index 0000000..c5fb776 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardConnection.php @@ -0,0 +1,272 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver; +use Doctrine\DBAL\Event\ConnectionEventArgs; +use Doctrine\DBAL\Events; +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Sharding implementation that pools many different connections + * internally and serves data from the currently active connection. + * + * The internals of this class are: + * + * - All sharding clients are specified and given a shard-id during + * configuration. + * - By default, the global shard is selected. If no global shard is configured + * an exception is thrown on access. + * - Selecting a shard by distribution value delegates the mapping + * "distributionValue" => "client" to the ShardChooser interface. + * - An exception is thrown if trying to switch shards during an open + * transaction. + * + * Instantiation through the DriverManager looks like: + * + * @example + * + * $conn = DriverManager::getConnection(array( + * 'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection', + * 'driver' => 'pdo_mysql', + * 'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), + * 'shards' => array( + * array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''), + * array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''), + * ), + * 'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser', + * )); + * $shardManager = $conn->getShardManager(); + * $shardManager->selectGlobal(); + * $shardManager->selectShard($value); + * + * @author Benjamin Eberlei + */ +class PoolingShardConnection extends Connection +{ + /** + * @var array + */ + private $activeConnections; + + /** + * @var integer + */ + private $activeShardId; + + /** + * @var array + */ + private $connections; + + /** + * @param array $params + * @param \Doctrine\DBAL\Driver $driver + * @param \Doctrine\DBAL\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + * + * @throws \InvalidArgumentException + */ + public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null) + { + if ( !isset($params['global']) || !isset($params['shards'])) { + throw new \InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations."); + } + + if ( !isset($params['shardChoser'])) { + throw new \InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'"); + } + + if (is_string($params['shardChoser'])) { + $params['shardChoser'] = new $params['shardChoser']; + } + + if ( ! ($params['shardChoser'] instanceof ShardChoser)) { + throw new \InvalidArgumentException("The 'shardChoser' configuration is not a valid instance of Doctrine\DBAL\Sharding\ShardChoser\ShardChoser"); + } + + $this->connections[0] = array_merge($params, $params['global']); + + foreach ($params['shards'] as $shard) { + if ( ! isset($shard['id'])) { + throw new \InvalidArgumentException("Missing 'id' for one configured shard. Please specify a unique shard-id."); + } + + if ( !is_numeric($shard['id']) || $shard['id'] < 1) { + throw new \InvalidArgumentException("Shard Id has to be a non-negative number."); + } + + if (isset($this->connections[$shard['id']])) { + throw new \InvalidArgumentException("Shard " . $shard['id'] . " is duplicated in the configuration."); + } + + $this->connections[$shard['id']] = array_merge($params, $shard); + } + + parent::__construct($params, $driver, $config, $eventManager); + } + + /** + * Get active shard id. + * + * @return integer + */ + public function getActiveShardId() + { + return $this->activeShardId; + } + + /** + * {@inheritdoc} + */ + public function getParams() + { + return $this->activeShardId ? $this->connections[$this->activeShardId] : $this->connections[0]; + } + + /** + * {@inheritdoc} + */ + public function getHost() + { + $params = $this->getParams(); + + return isset($params['host']) ? $params['host'] : parent::getHost(); + } + + /** + * {@inheritdoc} + */ + public function getPort() + { + $params = $this->getParams(); + + return isset($params['port']) ? $params['port'] : parent::getPort(); + } + + /** + * {@inheritdoc} + */ + public function getUsername() + { + $params = $this->getParams(); + + return isset($params['user']) ? $params['user'] : parent::getUsername(); + } + + /** + * {@inheritdoc} + */ + public function getPassword() + { + $params = $this->getParams(); + + return isset($params['password']) ? $params['password'] : parent::getPassword(); + } + + /** + * Connects to a given shard. + * + * @param mixed $shardId + * + * @return boolean + * + * @throws \Doctrine\DBAL\Sharding\ShardingException + */ + public function connect($shardId = null) + { + if ($shardId === null && $this->_conn) { + return false; + } + + if ($shardId !== null && $shardId === $this->activeShardId) { + return false; + } + + if ($this->getTransactionNestingLevel() > 0) { + throw new ShardingException("Cannot switch shard when transaction is active."); + } + + $this->activeShardId = (int)$shardId; + + if (isset($this->activeConnections[$this->activeShardId])) { + $this->_conn = $this->activeConnections[$this->activeShardId]; + + return false; + } + + $this->_conn = $this->activeConnections[$this->activeShardId] = $this->connectTo($this->activeShardId); + + if ($this->_eventManager->hasListeners(Events::postConnect)) { + $eventArgs = new ConnectionEventArgs($this); + $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); + } + + return true; + } + + /** + * Connects to a specific connection. + * + * @param string $shardId + * + * @return \Doctrine\DBAL\Driver\Connection + */ + protected function connectTo($shardId) + { + $params = $this->getParams(); + + $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : array(); + + $connectionParams = $this->connections[$shardId]; + + $user = isset($connectionParams['user']) ? $connectionParams['user'] : null; + $password = isset($connectionParams['password']) ? $connectionParams['password'] : null; + + return $this->_driver->connect($connectionParams, $user, $password, $driverOptions); + } + + /** + * @param string|null $shardId + * + * @return boolean + */ + public function isConnected($shardId = null) + { + if ($shardId === null) { + return $this->_conn !== null; + } + + return isset($this->activeConnections[$shardId]); + } + + /** + * @return void + */ + public function close() + { + $this->_conn = null; + $this->activeConnections = null; + $this->activeShardId = null; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php new file mode 100644 index 0000000..64307ca --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/PoolingShardManager.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser; + +/** + * Shard Manager for the Connection Pooling Shard Strategy + * + * @author Benjamin Eberlei + */ +class PoolingShardManager implements ShardManager +{ + /** + * @var PoolingShardConnection + */ + private $conn; + + /** + * @var ShardChoser + */ + private $choser; + + /** + * @var string|null + */ + private $currentDistributionValue; + + /** + * @param PoolingShardConnection $conn + */ + public function __construct(PoolingShardConnection $conn) + { + $params = $conn->getParams(); + $this->conn = $conn; + $this->choser = $params['shardChoser']; + } + + /** + * @return void + */ + public function selectGlobal() + { + $this->conn->connect(0); + $this->currentDistributionValue = null; + } + + /** + * @param string $distributionValue + * + * @return void + */ + public function selectShard($distributionValue) + { + $shardId = $this->choser->pickShard($distributionValue, $this->conn); + $this->conn->connect($shardId); + $this->currentDistributionValue = $distributionValue; + } + + /** + * @return string|null + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * @return array + */ + public function getShards() + { + $params = $this->conn->getParams(); + $shards = array(); + + foreach ($params['shards'] as $shard) { + $shards[] = array('id' => $shard['id']); + } + + return $shards; + } + + /** + * @param string $sql + * @param array $params + * @param array $types + * + * @return array + * + * @throws \RuntimeException + */ + public function queryAll($sql, array $params, array $types) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found."); + } + + $result = array(); + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->conn->connect($shard['id']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php new file mode 100644 index 0000000..edda835 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureFederationsSynchronizer.php @@ -0,0 +1,298 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\DBAL\Schema\Synchronizer\AbstractSchemaSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer; +use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer; + +/** + * SQL Azure Schema Synchronizer. + * + * Will iterate over all shards when performing schema operations. This is done + * by partitioning the passed schema into subschemas for the federation and the + * global database and then applying the operations step by step using the + * {@see \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer}. + * + * @author Benjamin Eberlei + */ +class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer +{ + const FEDERATION_TABLE_FEDERATED = 'azure.federated'; + const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName'; + + /** + * @var \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager + */ + private $shardManager; + + /** + * @var \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer + */ + private $synchronizer; + + /** + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager $shardManager + * @param \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer|null $sync + */ + public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null) + { + parent::__construct($conn); + $this->shardManager = $shardManager; + $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn); + } + + /** + * {@inheritdoc} + */ + public function getCreateSchema(Schema $createSchema) + { + $sql = array(); + + list($global, $federation) = $this->partitionSchema($createSchema); + + $globalSql = $this->synchronizer->getCreateSchema($global); + if ($globalSql) { + $sql[] = "-- Create Root Federation\n" . + "USE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $federationSql = $this->synchronizer->getCreateSchema($federation); + + if ($federationSql) { + $defaultValue = $this->getFederationTypeDefaultValue(); + + $sql[] = $this->getCreateFederationStatement(); + $sql[] = "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $defaultValue . ") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getUpdateSchema(Schema $toSchema, $noDrops = false) + { + return $this->work($toSchema, function ($synchronizer, $schema) use ($noDrops) { + return $synchronizer->getUpdateSchema($schema, $noDrops); + }); + } + + /** + * {@inheritdoc} + */ + public function getDropSchema(Schema $dropSchema) + { + return $this->work($dropSchema, function ($synchronizer, $schema) { + return $synchronizer->getDropSchema($schema); + }); + } + + /** + * {@inheritdoc} + */ + public function createSchema(Schema $createSchema) + { + $this->processSql($this->getCreateSchema($createSchema)); + } + + /** + * {@inheritdoc} + */ + public function updateSchema(Schema $toSchema, $noDrops = false) + { + $this->processSql($this->getUpdateSchema($toSchema, $noDrops)); + } + + /** + * {@inheritdoc} + */ + public function dropSchema(Schema $dropSchema) + { + $this->processSqlSafely($this->getDropSchema($dropSchema)); + } + + /** + * {@inheritdoc} + */ + public function getDropAllSchema() + { + $this->shardManager->selectGlobal(); + $globalSql = $this->synchronizer->getDropAllSchema(); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $this->synchronizer->getDropAllSchema(); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + $sql[] = "USE FEDERATION ROOT WITH RESET;"; + $sql[] = "DROP FEDERATION " . $this->shardManager->getFederationName(); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function dropAllSchema() + { + $this->processSqlSafely($this->getDropAllSchema()); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * + * @return array + */ + private function partitionSchema(Schema $schema) + { + return array( + $this->extractSchemaFederation($schema, false), + $this->extractSchemaFederation($schema, true), + ); + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param boolean $isFederation + * + * @return \Doctrine\DBAL\Schema\Schema + * + * @throws \RuntimeException + */ + private function extractSchemaFederation(Schema $schema, $isFederation) + { + $partitionedSchema = clone $schema; + + foreach ($partitionedSchema->getTables() as $table) { + if ($isFederation) { + $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey()); + } + + if ($table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + $partitionedSchema->dropTable($table->getName()); + } else { + foreach ($table->getForeignKeys() as $fk) { + $foreignTable = $schema->getTable($fk->getForeignTableName()); + if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) { + throw new \RuntimeException("Cannot have foreign key between global/federation."); + } + } + } + } + + return $partitionedSchema; + } + + /** + * Work on the Global/Federation based on currently existing shards and + * perform the given operation on the underlying schema synchronizer given + * the different partitioned schema instances. + * + * @param \Doctrine\DBAL\Schema\Schema $schema + * @param \Closure $operation + * + * @return array + */ + private function work(Schema $schema, \Closure $operation) + { + list($global, $federation) = $this->partitionSchema($schema); + $sql = array(); + + $this->shardManager->selectGlobal(); + $globalSql = $operation($this->synchronizer, $global); + + if ($globalSql) { + $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;"; + $sql = array_merge($sql, $globalSql); + } + + $shards = $this->shardManager->getShards(); + + foreach ($shards as $shard) { + $this->shardManager->selectShard($shard['rangeLow']); + + $federationSql = $operation($this->synchronizer, $federation); + if ($federationSql) { + $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" . + "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;"; + $sql = array_merge($sql, $federationSql); + } + } + + return $sql; + } + + /** + * @return string + */ + private function getFederationTypeDefaultValue() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + + switch ($federationType->getName()) { + case Type::GUID: + $defaultValue = '00000000-0000-0000-0000-000000000000'; + break; + case Type::INTEGER: + case Type::SMALLINT: + case Type::BIGINT: + $defaultValue = '0'; + break; + default: + $defaultValue = ''; + break; + } + + return $defaultValue; + } + + /** + * @return string + */ + private function getCreateFederationStatement() + { + $federationType = Type::getType($this->shardManager->getDistributionType()); + $federationTypeSql = $federationType->getSqlDeclaration(array(), $this->conn->getDatabasePlatform()); + + return "--Create Federation\n" . + "CREATE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " " . $federationTypeSql ." RANGE)"; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php new file mode 100644 index 0000000..b9047a9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -0,0 +1,243 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure; + +use Doctrine\DBAL\Sharding\ShardManager; +use Doctrine\DBAL\Sharding\ShardingException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Sharding using the SQL Azure Federations support. + * + * @author Benjamin Eberlei + */ +class SQLAzureShardManager implements ShardManager +{ + /** + * @var string + */ + private $federationName; + + /** + * @var boolean + */ + private $filteringEnabled; + + /** + * @var string + */ + private $distributionKey; + + /** + * @var string + */ + private $distributionType; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var string + */ + private $currentDistributionValue; + + /** + * @param \Doctrine\DBAL\Connection $conn + * + * @throws \Doctrine\DBAL\Sharding\ShardingException + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + $params = $conn->getParams(); + + if ( ! isset($params['sharding']['federationName'])) { + throw ShardingException::missingDefaultFederationName(); + } + + if ( ! isset($params['sharding']['distributionKey'])) { + throw ShardingException::missingDefaultDistributionKey(); + } + + if ( ! isset($params['sharding']['distributionType'])) { + throw ShardingException::missingDistributionType(); + } + + $this->federationName = $params['sharding']['federationName']; + $this->distributionKey = $params['sharding']['distributionKey']; + $this->distributionType = $params['sharding']['distributionType']; + $this->filteringEnabled = (isset($params['sharding']['filteringEnabled'])) ? (bool) $params['sharding']['filteringEnabled'] : false; + } + + /** + * Gets the name of the federation. + * + * @return string + */ + public function getFederationName() + { + return $this->federationName; + } + + /** + * Gets the distribution key. + * + * @return string + */ + public function getDistributionKey() + { + return $this->distributionKey; + } + + /** + * Gets the Doctrine Type name used for the distribution. + * + * @return string + */ + public function getDistributionType() + { + return $this->distributionType; + } + + /** + * Sets Enabled/Disable filtering on the fly. + * + * @param boolean $flag + * + * @return void + */ + public function setFilteringEnabled($flag) + { + $this->filteringEnabled = (bool) $flag; + } + + /** + * {@inheritDoc} + */ + public function selectGlobal() + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + $sql = "USE FEDERATION ROOT WITH RESET"; + $this->conn->exec($sql); + $this->currentDistributionValue = null; + } + + /** + * {@inheritDoc} + */ + public function selectShard($distributionValue) + { + if ($this->conn->isTransactionActive()) { + throw ShardingException::activeTransaction(); + } + + if ($distributionValue === null || is_bool($distributionValue) || !is_scalar($distributionValue)) { + throw ShardingException::noShardDistributionValue(); + } + + $platform = $this->conn->getDatabasePlatform(); + $sql = sprintf( + "USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;", + $platform->quoteIdentifier($this->federationName), + $platform->quoteIdentifier($this->distributionKey), + $this->conn->quote($distributionValue), + ($this->filteringEnabled ? 'ON' : 'OFF') + ); + + $this->conn->exec($sql); + $this->currentDistributionValue = $distributionValue; + } + + /** + * {@inheritDoc} + */ + public function getCurrentDistributionValue() + { + return $this->currentDistributionValue; + } + + /** + * {@inheritDoc} + */ + public function getShards() + { + $sql = "SELECT member_id as id, + distribution_name as distribution_key, + CAST(range_low AS CHAR) AS rangeLow, + CAST(range_high AS CHAR) AS rangeHigh + FROM sys.federation_member_distributions d + INNER JOIN sys.federations f ON f.federation_id = d.federation_id + WHERE f.name = " . $this->conn->quote($this->federationName); + + return $this->conn->fetchAll($sql); + } + + /** + * {@inheritDoc} + */ + public function queryAll($sql, array $params = array(), array $types = array()) + { + $shards = $this->getShards(); + if (!$shards) { + throw new \RuntimeException("No shards found for " . $this->federationName); + } + + $result = array(); + $oldDistribution = $this->getCurrentDistributionValue(); + + foreach ($shards as $shard) { + $this->selectShard($shard['rangeLow']); + foreach ($this->conn->fetchAll($sql, $params, $types) as $row) { + $result[] = $row; + } + } + + if ($oldDistribution === null) { + $this->selectGlobal(); + } else { + $this->selectShard($oldDistribution); + } + + return $result; + } + + /** + * Splits Federation at a given distribution value. + * + * @param mixed $splitDistributionValue + * + * @return void + */ + public function splitFederation($splitDistributionValue) + { + $type = Type::getType($this->distributionType); + + $sql = "ALTER FEDERATION " . $this->getFederationName() . " " . + "SPLIT AT (" . $this->getDistributionKey() . " = " . + $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ")"; + $this->conn->exec($sql); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php new file mode 100644 index 0000000..c1a524f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/SQLAzure/Schema/MultiTenantVisitor.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\SQLAzure\Schema; + +use Doctrine\DBAL\Schema\Visitor\Visitor; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Index; + +/** + * Converts a single tenant schema into a multi-tenant schema for SQL Azure + * Federations under the following assumptions: + * + * - Every table is part of the multi-tenant application, only explicitly + * excluded tables are non-federated. The behavior of the tables being in + * global or federated database is undefined. It depends on you selecting a + * federation before DDL statements or not. + * - Every Primary key of a federated table is extended by another column + * 'tenant_id' with a default value of the SQLAzure function + * `federation_filtering_value('tenant_id')`. + * - You always have to work with `filtering=On` when using federations with this + * multi-tenant approach. + * - Primary keys are either using globally unique ids (GUID, Table Generator) + * or you explicitly add the tenent_id in every UPDATE or DELETE statement + * (otherwise they will affect the same-id rows from other tenents as well). + * SQLAzure throws errors when you try to create IDENTIY columns on federated + * tables. + * + * @author Benjamin Eberlei + */ +class MultiTenantVisitor implements Visitor +{ + /** + * @var array + */ + private $excludedTables = array(); + + /** + * @var string + */ + private $tenantColumnName; + + /** + * @var string + */ + private $tenantColumnType = 'integer'; + + /** + * Name of the federation distribution, defaulting to the tenantColumnName + * if not specified. + * + * @var string + */ + private $distributionName; + + /** + * @param array $excludedTables + * @param string $tenantColumnName + * @param string|null $distributionName + */ + public function __construct(array $excludedTables = array(), $tenantColumnName = 'tenant_id', $distributionName = null) + { + $this->excludedTables = $excludedTables; + $this->tenantColumnName = $tenantColumnName; + $this->distributionName = $distributionName ?: $tenantColumnName; + } + + /** + * {@inheritdoc} + */ + public function acceptTable(Table $table) + { + if (in_array($table->getName(), $this->excludedTables)) { + return; + } + + $table->addColumn($this->tenantColumnName, $this->tenantColumnType, array( + 'default' => "federation_filtering_value('". $this->distributionName ."')", + )); + + $clusteredIndex = $this->getClusteredIndex($table); + + $indexColumns = $clusteredIndex->getColumns(); + $indexColumns[] = $this->tenantColumnName; + + if ($clusteredIndex->isPrimary()) { + $table->dropPrimaryKey(); + $table->setPrimaryKey($indexColumns); + } else { + $table->dropIndex($clusteredIndex->getName()); + $table->addIndex($indexColumns, $clusteredIndex->getName()); + $table->getIndex($clusteredIndex->getName())->addFlag('clustered'); + } + } + + /** + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return \Doctrine\DBAL\Schema\Index + * + * @throws \RuntimeException + */ + private function getClusteredIndex($table) + { + foreach ($table->getIndexes() as $index) { + if ($index->isPrimary() && ! $index->hasFlag('nonclustered')) { + return $index; + } elseif ($index->hasFlag('clustered')) { + return $index; + } + } + throw new \RuntimeException("No clustered index found on table " . $table->getName()); + } + + /** + * {@inheritdoc} + */ + public function acceptSchema(Schema $schema) + { + } + + /** + * {@inheritdoc} + */ + public function acceptColumn(Table $table, Column $column) + { + } + + /** + * {@inheritdoc} + */ + public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) + { + } + + /** + * {@inheritdoc} + */ + public function acceptIndex(Table $table, Index $index) + { + } + + /** + * {@inheritdoc} + */ + public function acceptSequence(Sequence $sequence) + { + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php new file mode 100644 index 0000000..f1d51b8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/MultiTenantShardChoser.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * The MultiTenant Shard choser assumes that the distribution value directly + * maps to the shard id. + * + * @author Benjamin Eberlei + */ +class MultiTenantShardChoser implements ShardChoser +{ + /** + * {@inheritdoc} + */ + public function pickShard($distributionValue, PoolingShardConnection $conn) + { + return $distributionValue; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php new file mode 100644 index 0000000..a7b85a6 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardChoser/ShardChoser.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\DBAL\Sharding\ShardChoser; + +use Doctrine\DBAL\Sharding\PoolingShardConnection; + +/** + * Given a distribution value this shard-choser strategy will pick the shard to + * connect to for retrieving rows with the distribution value. + * + * @author Benjamin Eberlei + */ +interface ShardChoser +{ + /** + * Picks a shard for the given distribution value. + * + * @param string $distributionValue + * @param \Doctrine\DBAL\Sharding\PoolingShardConnection $conn + * + * @return integer + */ + function pickShard($distributionValue, PoolingShardConnection $conn); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php new file mode 100644 index 0000000..b742793 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardManager.php @@ -0,0 +1,93 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +/** + * Sharding Manager gives access to APIs to implementing sharding on top of + * Doctrine\DBAL\Connection instances. + * + * For simplicity and developer ease-of-use (and understanding) the sharding + * API only covers single shard queries, no fan-out support. It is primarily + * suited for multi-tenant applications. + * + * The assumption about sharding here + * is that a distribution value can be found that gives access to all the + * necessary data for all use-cases. Switching between shards should be done with + * caution, especially if lazy loading is implemented. Any query is always + * executed against the last shard that was selected. If a query is created for + * a shard Y but then a shard X is selected when its actually executed you + * will hit the wrong shard. + * + * @author Benjamin Eberlei + */ +interface ShardManager +{ + /** + * Selects global database with global data. + * + * This is the default database that is connected when no shard is + * selected. + * + * @return void + */ + function selectGlobal(); + + /** + * Selects the shard against which the queries after this statement will be issued. + * + * @param string $distributionValue + * + * @return void + * + * @throws \Doctrine\DBAL\Sharding\ShardingException If no value is passed as shard identifier. + */ + function selectShard($distributionValue); + + /** + * Gets the distribution value currently used for sharding. + * + * @return string + */ + function getCurrentDistributionValue(); + + /** + * Gets information about the amount of shards and other details. + * + * Format is implementation specific, each shard is one element and has an + * 'id' attribute at least. + * + * @return array + */ + function getShards(); + + /** + * Queries all shards in undefined order and return the results appended to + * each other. Restore the previous distribution value after execution. + * + * Using {@link \Doctrine\DBAL\Connection::fetchAll} to retrieve rows internally. + * + * @param string $sql + * @param array $params + * @param array $types + * + * @return array + */ + function queryAll($sql, array $params, array $types); +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php new file mode 100644 index 0000000..407bf84 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Sharding/ShardingException.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Sharding; + +use Doctrine\DBAL\DBALException; + +/** + * Sharding related Exceptions + * + * @since 2.3 + */ +class ShardingException extends DBALException +{ + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function notImplemented() + { + return new self("This functionality is not implemented with this sharding provider.", 1331557937); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDefaultFederationName() + { + return new self("SQLAzure requires a federation name to be set during sharding configuration.", 1332141280); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDefaultDistributionKey() + { + return new self("SQLAzure requires a distribution key to be set during sharding configuration.", 1332141329); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function activeTransaction() + { + return new self("Cannot switch shard during an active transaction.", 1332141766); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function noShardDistributionValue() + { + return new self("You have to specify a string or integer as shard distribution value.", 1332142103); + } + + /** + * @return \Doctrine\DBAL\Sharding\ShardingException + */ + static public function missingDistributionType() + { + return new self("You have to specify a sharding distribution type such as 'integer', 'string', 'guid'."); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php new file mode 100644 index 0000000..25ec652 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php @@ -0,0 +1,312 @@ +. + */ + +namespace Doctrine\DBAL; + +use PDO; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Driver\Statement as DriverStatement; + +/** + * A thin wrapper around a Doctrine\DBAL\Driver\Statement that adds support + * for logging, DBAL mapping types, etc. + * + * @author Roman Borschel + * @since 2.0 + */ +class Statement implements \IteratorAggregate, DriverStatement +{ + /** + * The SQL statement. + * + * @var string + */ + protected $sql; + + /** + * The bound parameters. + * + * @var array + */ + protected $params = array(); + + /** + * The parameter types. + * + * @var array + */ + protected $types = array(); + + /** + * The underlying driver statement. + * + * @var \Doctrine\DBAL\Driver\Statement + */ + protected $stmt; + + /** + * The underlying database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The connection this statement is bound to and executed on. + * + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * Creates a new Statement for the given SQL and Connection. + * + * @param string $sql The SQL of the statement. + * @param \Doctrine\DBAL\Connection $conn The connection on which the statement should be executed. + */ + public function __construct($sql, Connection $conn) + { + $this->sql = $sql; + $this->stmt = $conn->getWrappedConnection()->prepare($sql); + $this->conn = $conn; + $this->platform = $conn->getDatabasePlatform(); + } + + /** + * Binds a parameter value to the statement. + * + * The value can optionally be bound with a PDO binding type or a DBAL mapping type. + * If bound with a DBAL mapping type, the binding type is derived from the mapping + * type and the value undergoes the conversion routines of the mapping type before + * being bound. + * + * @param string $name The name or position of the parameter. + * @param mixed $value The value of the parameter. + * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindValue($name, $value, $type = null) + { + $this->params[$name] = $value; + $this->types[$name] = $type; + if ($type !== null) { + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + $bindingType = $type->getBindingType(); + } else { + $bindingType = $type; // PDO::PARAM_* constants + } + + return $this->stmt->bindValue($name, $value, $bindingType); + } else { + return $this->stmt->bindValue($name, $value); + } + } + + /** + * Binds a parameter to a value by reference. + * + * Binding a parameter by reference does not support DBAL mapping types. + * + * @param string $name The name or position of the parameter. + * @param mixed $var The reference to the variable to bind. + * @param integer $type The PDO binding type. + * @param integer|null $length Must be specified when using an OUT bind + * so that PHP allocates enough memory to hold the returned value. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function bindParam($name, &$var, $type = PDO::PARAM_STR, $length = null) + { + return $this->stmt->bindParam($name, $var, $type, $length); + } + + /** + * Executes the statement with the currently bound parameters. + * + * @param array|null $params + * + * @return boolean TRUE on success, FALSE on failure. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function execute($params = null) + { + if (is_array($params)) { + $this->params = $params; + } + + $logger = $this->conn->getConfiguration()->getSQLLogger(); + if ($logger) { + $logger->startQuery($this->sql, $this->params, $this->types); + } + + try { + $stmt = $this->stmt->execute($params); + } catch (\Exception $ex) { + if ($logger) { + $logger->stopQuery(); + } + throw DBALException::driverExceptionDuringQuery( + $this->conn->getDriver(), + $ex, + $this->sql, + $this->conn->resolveParams($this->params, $this->types) + ); + } + + if ($logger) { + $logger->stopQuery(); + } + $this->params = array(); + $this->types = array(); + + return $stmt; + } + + /** + * Closes the cursor, freeing the database resources used by this statement. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function closeCursor() + { + return $this->stmt->closeCursor(); + } + + /** + * Returns the number of columns in the result set. + * + * @return integer + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * Fetches the SQLSTATE associated with the last operation on the statement. + * + * @return string + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * Fetches extended error information associated with the last operation on the statement. + * + * @return array + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + + /** + * {@inheritdoc} + */ + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + { + if ($arg2 === null) { + return $this->stmt->setFetchMode($fetchMode); + } elseif ($arg3 === null) { + return $this->stmt->setFetchMode($fetchMode, $arg2); + } + + return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritdoc} + */ + public function getIterator() + { + return $this->stmt; + } + + /** + * Fetches the next row from a result set. + * + * @param integer|null $fetchMode + * + * @return mixed The return value of this function on success depends on the fetch type. + * In all cases, FALSE is returned on failure. + */ + public function fetch($fetchMode = null) + { + return $this->stmt->fetch($fetchMode); + } + + /** + * Returns an array containing all of the result set rows. + * + * @param integer|null $fetchMode + * @param mixed $fetchArgument + * + * @return array An array containing all of the remaining rows in the result set. + */ + public function fetchAll($fetchMode = null, $fetchArgument = 0) + { + if ($fetchArgument !== 0) { + return $this->stmt->fetchAll($fetchMode, $fetchArgument); + } + + return $this->stmt->fetchAll($fetchMode); + } + + /** + * Returns a single column from the next row of a result set. + * + * @param integer $columnIndex + * + * @return mixed A single column from the next row of a result set or FALSE if there are no more rows. + */ + public function fetchColumn($columnIndex = 0) + { + return $this->stmt->fetchColumn($columnIndex); + } + + /** + * Returns the number of rows affected by the last execution of this statement. + * + * @return integer The number of affected rows. + */ + public function rowCount() + { + return $this->stmt->rowCount(); + } + + /** + * Gets the wrapped driver statement. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + public function getWrappedStatement() + { + return $this->stmt; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php new file mode 100644 index 0000000..4d76ca9 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ImportCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('dbal:import') + ->setDescription('Import SQL file(s) directly to Database.') + ->setDefinition(array( + new InputArgument( + 'file', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'File path(s) of SQL to be executed.' + ) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($fileNames = $input->getArgument('file')) !== null) { + foreach ((array) $fileNames as $fileName) { + $filePath = realpath($fileName); + + // Phar compatibility. + if (false === $filePath) { + $filePath = $fileName; + } + + if ( ! file_exists($filePath)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not exist.", $filePath) + ); + } elseif ( ! is_readable($filePath)) { + throw new \InvalidArgumentException( + sprintf("SQL file '%s' does not have read permissions.", $filePath) + ); + } + + $output->write(sprintf("Processing file '%s'... ", $filePath)); + $sql = file_get_contents($filePath); + + if ($conn instanceof \Doctrine\DBAL\Driver\PDOConnection) { + // PDO Drivers + try { + $lines = 0; + + $stmt = $conn->prepare($sql); + $stmt->execute(); + + do { + // Required due to "MySQL has gone away!" issue + $stmt->fetch(); + $stmt->closeCursor(); + + $lines++; + } while ($stmt->nextRowset()); + + $output->write(sprintf('%d statements executed!', $lines) . PHP_EOL); + } catch (\PDOException $e) { + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } else { + // Non-PDO Drivers (ie. OCI8 driver) + $stmt = $conn->prepare($sql); + $rs = $stmt->execute(); + + if ($rs) { + $output->writeln('OK!' . PHP_EOL); + } else { + $error = $stmt->errorInfo(); + + $output->write('error!' . PHP_EOL); + + throw new \RuntimeException($error[2], $error[0]); + } + + $stmt->closeCursor(); + } + } + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php new file mode 100644 index 0000000..f7acd0a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -0,0 +1,172 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; + +class ReservedWordsCommand extends Command +{ + /** + * @var array + */ + private $keywordListClasses = array( + 'mysql' => 'Doctrine\DBAL\Platforms\Keywords\MySQLKeywords', + 'mysql57' => 'Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords', + 'sqlserver' => 'Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords', + 'sqlserver2005' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords', + 'sqlserver2008' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords', + 'sqlserver2012' => 'Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords', + 'sqlite' => 'Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords', + 'pgsql' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords', + 'pgsql91' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords', + 'pgsql92' => 'Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords', + 'oracle' => 'Doctrine\DBAL\Platforms\Keywords\OracleKeywords', + 'db2' => 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords', + 'sqlanywhere' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords', + 'sqlanywhere11' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords', + 'sqlanywhere12' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords', + 'sqlanywhere16' => 'Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords', + ); + + /** + * If you want to add or replace a keywords list use this command. + * + * @param string $name + * @param string $class + * + * @return void + */ + public function setKeywordListClass($name, $class) + { + $this->keywordListClasses[$name] = $class; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('dbal:reserved-words') + ->setDescription('Checks if the current database contains identifiers that are reserved.') + ->setDefinition(array( + new InputOption( + 'list', 'l', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Keyword-List name.' + ) + )) + ->setHelp(<<%command.full_name% + +If you want to check against specific dialects you can +pass them to the command: + + %command.full_name% mysql pgsql + +The following keyword lists are currently shipped with Doctrine: + + * mysql + * mysql57 + * pgsql + * pgsql92 + * sqlite + * oracle + * sqlserver + * sqlserver2005 + * sqlserver2008 + * sqlserver2012 + * sqlanywhere + * sqlanywhere11 + * sqlanywhere12 + * sqlanywhere16 + * db2 (Not checked by default) +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $conn \Doctrine\DBAL\Connection */ + $conn = $this->getHelper('db')->getConnection(); + + $keywordLists = (array) $input->getOption('list'); + if ( ! $keywordLists) { + $keywordLists = array( + 'mysql', + 'mysql57', + 'pgsql', + 'pgsql92', + 'sqlite', + 'oracle', + 'sqlserver', + 'sqlserver2005', + 'sqlserver2008', + 'sqlserver2012', + 'sqlanywhere', + 'sqlanywhere11', + 'sqlanywhere12', + 'sqlanywhere16', + ); + } + + $keywords = array(); + foreach ($keywordLists as $keywordList) { + if (!isset($this->keywordListClasses[$keywordList])) { + throw new \InvalidArgumentException( + "There exists no keyword list with name '" . $keywordList . "'. ". + "Known lists: " . implode(", ", array_keys($this->keywordListClasses)) + ); + } + $class = $this->keywordListClasses[$keywordList]; + $keywords[] = new $class; + } + + $output->write('Checking keyword violations for ' . implode(", ", $keywordLists) . "...", true); + + /* @var $schema \Doctrine\DBAL\Schema\Schema */ + $schema = $conn->getSchemaManager()->createSchema(); + $visitor = new ReservedKeywordsValidator($keywords); + $schema->visit($visitor); + + $violations = $visitor->getViolations(); + if (count($violations) == 0) { + $output->write("No reserved keywords violations have been found!", true); + } else { + $output->write('There are ' . count($violations) . ' reserved keyword violations in your database schema:', true); + foreach ($violations as $violation) { + $output->write(' - ' . $violation, true); + } + + return 1; + } + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php new file mode 100644 index 0000000..b2b2d97 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/Command/RunSqlCommand.php @@ -0,0 +1,88 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Task for executing arbitrary SQL that can come from a file or directly from + * the command line. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunSqlCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('dbal:run-sql') + ->setDescription('Executes arbitrary SQL directly from the command line.') + ->setDefinition(array( + new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'), + new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set.', 7) + )) + ->setHelp(<<getHelper('db')->getConnection(); + + if (($sql = $input->getArgument('sql')) === null) { + throw new \RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + if (stripos($sql, 'select') === 0) { + $resultSet = $conn->fetchAll($sql); + } else { + $resultSet = $conn->executeUpdate($sql); + } + + ob_start(); + \Doctrine\Common\Util\Debug::dump($resultSet, (int) $depth); + $message = ob_get_clean(); + + $output->write($message); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php new file mode 100644 index 0000000..3fb9c05 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\DBAL\Tools\Console; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Tools\Console\Command\ImportCommand; +use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand; +use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Symfony\Component\Console\Application; +use Doctrine\DBAL\Version; + +/** + * Handles running the Console Tools inside Symfony Console context. + */ +class ConsoleRunner +{ + /** + * Create a Symfony Console HelperSet + * + * @param Connection $connection + * + * @return HelperSet + */ + static public function createHelperSet(Connection $connection) + { + return new HelperSet(array( + 'db' => new ConnectionHelper($connection) + )); + } + + /** + * Runs console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param \Symfony\Component\Console\Command\Command[] $commands + * + * @return void + */ + static public function run(HelperSet $helperSet, $commands = array()) + { + $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + + self::addCommands($cli); + + $cli->addCommands($commands); + $cli->run(); + } + + /** + * @param Application $cli + * + * @return void + */ + static public function addCommands(Application $cli) + { + $cli->addCommands(array( + new RunSqlCommand(), + new ImportCommand(), + new ReservedWordsCommand(), + )); + } + + /** + * Prints the instructions to create a configuration file + */ + static public function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine-DBAL Console working. You can use the +following sample as a template: + +. + */ + +namespace Doctrine\DBAL\Tools\Console\Helper; + +use Symfony\Component\Console\Helper\Helper; +use Doctrine\DBAL\Connection; + +/** + * Doctrine CLI Connection Helper. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConnectionHelper extends Helper +{ + /** + * The Doctrine database Connection. + * + * @var \Doctrine\DBAL\Connection + */ + protected $_connection; + + /** + * Constructor. + * + * @param \Doctrine\DBAL\Connection $connection The Doctrine database Connection. + */ + public function __construct(Connection $connection) + { + $this->_connection = $connection; + } + + /** + * Retrieves the Doctrine database Connection. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'connection'; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php new file mode 100644 index 0000000..f8c62cb --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP array to a clob SQL type. + * + * @since 2.0 + */ +class ArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + // @todo 3.0 - $value === null check to save real NULL in database + return serialize($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value != 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php new file mode 100644 index 0000000..1a9a6c3 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database BIGINT to a PHP string. + * + * @author robo + * @since 2.0 + */ +class BigIntType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BIGINT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (string) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php new file mode 100644 index 0000000..a22a440 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps ab SQL BINARY/VARBINARY to a PHP resource stream. + * + * @author Steve Müller + * @since 2.5 + */ +class BinaryType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBinaryTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $fp = fopen('php://temp', 'rb+'); + fwrite($fp, $value); + fseek($fp, 0); + $value = $fp; + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BINARY); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BINARY; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php new file mode 100644 index 0000000..bd89301 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL BLOB to a PHP resource stream. + * + * @since 2.2 + */ +class BlobType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBlobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + if (is_string($value)) { + $fp = fopen('php://temp', 'rb+'); + fwrite($fp, $value); + fseek($fp, 0); + $value = $fp; + } + + if ( ! is_resource($value)) { + throw ConversionException::conversionFailed($value, self::BLOB); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BLOB; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_LOB; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php new file mode 100644 index 0000000..9fea80f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL boolean to a PHP boolean. + * + * @since 2.0 + */ +class BooleanType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getBooleanTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $platform->convertBooleansToDatabaseValue($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $platform->convertFromBoolean($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::BOOLEAN; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_BOOL; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php new file mode 100644 index 0000000..4675101 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php @@ -0,0 +1,68 @@ +. + */ + +/** + * Conversion Exception is thrown when the database to PHP conversion fails. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +namespace Doctrine\DBAL\Types; + +class ConversionException extends \Doctrine\DBAL\DBALException +{ + /** + * Thrown when a Database to Doctrine Type Conversion fails. + * + * @param string $value + * @param string $toType + * + * @return \Doctrine\DBAL\Types\ConversionException + */ + static public function conversionFailed($value, $toType) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . "..." : $value; + + return new self('Could not convert database value "' . $value . '" to Doctrine Type ' . $toType); + } + + /** + * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement + * about the expected format. + * + * @param string $value + * @param string $toType + * @param string $expectedFormat + * + * @return \Doctrine\DBAL\Types\ConversionException + */ + static public function conversionFailedFormat($value, $toType, $expectedFormat) + { + $value = (strlen($value) > 32) ? substr($value, 0, 20) . "..." : $value; + + return new self( + 'Could not convert database value "' . $value . '" to Doctrine Type ' . + $toType . '. Expected format: ' . $expectedFormat + ); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php new file mode 100644 index 0000000..c5715a7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATETIME/TIMESTAMP to a PHP DateTime object. + * + * @since 2.0 + */ +class DateTimeType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); + + if ( ! $val) { + $val = date_create($value); + } + + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeFormatString()); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php new file mode 100644 index 0000000..2a9c6fa --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * DateTime type saving additional timezone information. + * + * Caution: Databases are not necessarily experts at storing timezone related + * data of dates. First, of all the supported vendors only PostgreSQL and Oracle + * support storing Timezone data. But those two don't save the actual timezone + * attached to a DateTime instance (for example "Europe/Berlin" or "America/Montreal") + * but the current offset of them related to UTC. That means depending on daylight saving times + * or not you may get different offsets. + * + * This datatype makes only sense to use, if your application works with an offset, not + * with an actual timezone that uses transitions. Otherwise your DateTime instance + * attached with a timezone such as Europe/Berlin gets saved into the database with + * the offset and re-created from persistence with only the offset, not the original timezone + * attached. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DateTimeTzType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATETIMETZ; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTimeTzTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateTimeTzFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateTimeTzFormatString()); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php new file mode 100644 index 0000000..9d33755 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DATE to a PHP Date object. + * + * @since 2.0 + */ +class DateType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DATE; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDateTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getDateFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat('!'.$platform->getDateFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getDateFormatString()); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php new file mode 100644 index 0000000..df251d7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL DECIMAL to a PHP string. + * + * @since 2.0 + */ +class DecimalType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::DECIMAL; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php new file mode 100644 index 0000000..87f9c32 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +class FloatType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::FLOAT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getFloatDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (double) $value; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php new file mode 100644 index 0000000..761d58a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Represents a GUID/UUID datatype (both are actually synonyms) in the database. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class GuidType extends StringType +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getGuidTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::GUID; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return !$platform->hasNativeGuidType(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php new file mode 100644 index 0000000..6fe6375 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL INT to a PHP integer. + * + * @author Roman Borschel + * @since 2.0 + */ +class IntegerType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::INTEGER; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getIntegerTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php new file mode 100644 index 0000000..79c3259 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used to generate json arrays. + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class JsonArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (null === $value) { + return null; + } + + return json_encode($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value === '') { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return json_decode($value, true); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::JSON_ARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return ! $platform->hasNativeJsonType(); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php new file mode 100644 index 0000000..0b1f87a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a PHP object to a clob SQL type. + * + * @since 2.0 + */ +class ObjectType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return serialize($value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + $val = unserialize($value); + if ($val === false && $value !== 'b:0;') { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::OBJECT; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php new file mode 100644 index 0000000..47c821b --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Array Type which can be used for simple values. + * + * Only use this type if you are sure that your values cannot contain a ",". + * + * @since 2.3 + * @author Johannes M. Schmitt + */ +class SimpleArrayType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!$value) { + return null; + } + + return implode(',', $value); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return array(); + } + + $value = (is_resource($value)) ? stream_get_contents($value) : $value; + + return explode(',', $value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::SIMPLE_ARRAY; + } + + /** + * {@inheritdoc} + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php new file mode 100644 index 0000000..7ebae6e --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps a database SMALLINT to a PHP integer. + * + * @author robo + */ +class SmallIntType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::SMALLINT; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (null === $value) ? null : (int) $value; + } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return \PDO::PARAM_INT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php new file mode 100644 index 0000000..39bd32d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL VARCHAR to a PHP string. + * + * @since 2.0 + */ +class StringType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return $platform->getVarcharDefaultLength(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::STRING; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php new file mode 100644 index 0000000..4055ac8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL CLOB to a PHP string. + * + * @since 2.0 + */ +class TextType extends Type +{ + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getClobTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return (is_resource($value)) ? stream_get_contents($value) : $value; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TEXT; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php new file mode 100644 index 0000000..554d56d --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Type that maps an SQL TIME to a PHP DateTime object. + * + * @since 2.0 + */ +class TimeType extends Type +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return Type::TIME; + } + + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getTimeTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritdoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return ($value !== null) + ? $value->format($platform->getTimeFormatString()) : null; + } + + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = \DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); + if ( ! $val) { + throw ConversionException::conversionFailedFormat($value, $this->getName(), $platform->getTimeFormatString()); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php new file mode 100644 index 0000000..9fa464f --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php @@ -0,0 +1,341 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\DBALException; + +/** + * The base class for so-called Doctrine mapping types. + * + * A Type object is obtained by calling the static {@link getType()} method. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class Type +{ + const TARRAY = 'array'; + const SIMPLE_ARRAY = 'simple_array'; + const JSON_ARRAY = 'json_array'; + const BIGINT = 'bigint'; + const BOOLEAN = 'boolean'; + const DATETIME = 'datetime'; + const DATETIMETZ = 'datetimetz'; + const DATE = 'date'; + const TIME = 'time'; + const DECIMAL = 'decimal'; + const INTEGER = 'integer'; + const OBJECT = 'object'; + const SMALLINT = 'smallint'; + const STRING = 'string'; + const TEXT = 'text'; + const BINARY = 'binary'; + const BLOB = 'blob'; + const FLOAT = 'float'; + const GUID = 'guid'; + + /** + * Map of already instantiated type objects. One instance per type (flyweight). + * + * @var array + */ + private static $_typeObjects = array(); + + /** + * The map of supported doctrine mapping types. + * + * @var array + */ + private static $_typesMap = array( + self::TARRAY => 'Doctrine\DBAL\Types\ArrayType', + self::SIMPLE_ARRAY => 'Doctrine\DBAL\Types\SimpleArrayType', + self::JSON_ARRAY => 'Doctrine\DBAL\Types\JsonArrayType', + self::OBJECT => 'Doctrine\DBAL\Types\ObjectType', + self::BOOLEAN => 'Doctrine\DBAL\Types\BooleanType', + self::INTEGER => 'Doctrine\DBAL\Types\IntegerType', + self::SMALLINT => 'Doctrine\DBAL\Types\SmallIntType', + self::BIGINT => 'Doctrine\DBAL\Types\BigIntType', + self::STRING => 'Doctrine\DBAL\Types\StringType', + self::TEXT => 'Doctrine\DBAL\Types\TextType', + self::DATETIME => 'Doctrine\DBAL\Types\DateTimeType', + self::DATETIMETZ => 'Doctrine\DBAL\Types\DateTimeTzType', + self::DATE => 'Doctrine\DBAL\Types\DateType', + self::TIME => 'Doctrine\DBAL\Types\TimeType', + self::DECIMAL => 'Doctrine\DBAL\Types\DecimalType', + self::FLOAT => 'Doctrine\DBAL\Types\FloatType', + self::BINARY => 'Doctrine\DBAL\Types\BinaryType', + self::BLOB => 'Doctrine\DBAL\Types\BlobType', + self::GUID => 'Doctrine\DBAL\Types\GuidType', + ); + + /** + * Prevents instantiation and forces use of the factory method. + */ + final private function __construct() + { + } + + /** + * Converts a value from its PHP representation to its database representation + * of this type. + * + * @param mixed $value The value to convert. + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return mixed The database representation of the value. + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Converts a value from its database representation to its PHP representation + * of this type. + * + * @param mixed $value The value to convert. + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return mixed The PHP representation of the value. + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + /** + * Gets the default length of this type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return integer|null + * + * @todo Needed? + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return null; + } + + /** + * Gets the SQL declaration snippet for a field of this type. + * + * @param array $fieldDeclaration The field declaration. + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform. + * + * @return string + */ + abstract public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform); + + /** + * Gets the name of this type. + * + * @return string + * + * @todo Needed? + */ + abstract public function getName(); + + /** + * Factory method to create type instances. + * Type instances are implemented as flyweights. + * + * @param string $name The name of the type (as returned by getName()). + * + * @return \Doctrine\DBAL\Types\Type + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function getType($name) + { + if ( ! isset(self::$_typeObjects[$name])) { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::unknownColumnType($name); + } + self::$_typeObjects[$name] = new self::$_typesMap[$name](); + } + + return self::$_typeObjects[$name]; + } + + /** + * Adds a custom type to the type map. + * + * @param string $name The name of the type. This should correspond to what getName() returns. + * @param string $className The class name of the custom type. + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function addType($name, $className) + { + if (isset(self::$_typesMap[$name])) { + throw DBALException::typeExists($name); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Checks if exists support for a type. + * + * @param string $name The name of the type. + * + * @return boolean TRUE if type is supported; FALSE otherwise. + */ + public static function hasType($name) + { + return isset(self::$_typesMap[$name]); + } + + /** + * Overrides an already defined type to use a different implementation. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws \Doctrine\DBAL\DBALException + */ + public static function overrideType($name, $className) + { + if ( ! isset(self::$_typesMap[$name])) { + throw DBALException::typeNotFound($name); + } + + if (isset(self::$_typeObjects[$name])) { + unset(self::$_typeObjects[$name]); + } + + self::$_typesMap[$name] = $className; + } + + /** + * Gets the (preferred) binding type for values of this type that + * can be used when binding parameters to prepared statements. + * + * This method should return one of the PDO::PARAM_* constants, that is, one of: + * + * PDO::PARAM_BOOL + * PDO::PARAM_NULL + * PDO::PARAM_INT + * PDO::PARAM_STR + * PDO::PARAM_LOB + * + * @return integer + */ + public function getBindingType() + { + return \PDO::PARAM_STR; + } + + /** + * Gets the types array map which holds all registered types and the corresponding + * type class + * + * @return array + */ + public static function getTypesMap() + { + return self::$_typesMap; + } + + /** + * @return string + */ + public function __toString() + { + $e = explode('\\', get_class($this)); + + return str_replace('Type', '', end($e)); + } + + /** + * Does working with this column require SQL conversion functions? + * + * This is a metadata function that is required for example in the ORM. + * Usage of {@link convertToDatabaseValueSQL} and + * {@link convertToPHPValueSQL} works for any type and mostly + * does nothing. This method can additionally be used for optimization purposes. + * + * @return boolean + */ + public function canRequireSQLConversion() + { + return false; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a database value. + * + * @param string $sqlExpr + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return $sqlExpr; + } + + /** + * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. + * + * @param string $sqlExpr + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function convertToPHPValueSQL($sqlExpr, $platform) + { + return $sqlExpr; + } + + /** + * Gets an array of database types that map to this Doctrine type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getMappedDatabaseTypes(AbstractPlatform $platform) + { + return array(); + } + + /** + * If this Doctrine Type maps to an already mapped database type, + * reverse schema engineering can't take them apart. You need to mark + * one of those types as commented, which will have Doctrine use an SQL + * comment to typehint the actual Doctrine Type. + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return boolean + */ + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return false; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php new file mode 100644 index 0000000..5cf35f7 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\DBAL\Types; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * Variable DateTime Type using date_create() instead of DateTime::createFromFormat(). + * + * This type has performance implications as it runs twice as long as the regular + * {@see DateTimeType}, however in certain PostgreSQL configurations with + * TIMESTAMP(n) columns where n > 0 it is necessary to use this type. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class VarDateTimeType extends DateTimeType +{ + /** + * {@inheritdoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null || $value instanceof \DateTime) { + return $value; + } + + $val = date_create($value); + if ( ! $val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php new file mode 100644 index 0000000..25739e8 --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/Version.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Class to store and retrieve the version of Doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.5.3'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version The Doctrine version to compare to. + * + * @return integer -1 if older, 0 if it is the same, 1 if version passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php b/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php new file mode 100644 index 0000000..53e2d3a --- /dev/null +++ b/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\DBAL; + +/** + * Contract for a driver that is able to create platform instances by version. + * + * Doctrine uses different platform classes for different vendor versions to + * support the correct features and SQL syntax of each version. + * This interface should be implemented by drivers that are capable to do this + * distinction. + * + * @author Steve Müller + * @link www.doctrine-project.org + * @since 2.5 + */ +interface VersionAwarePlatformDriver +{ + /** + * Factory method for creating the appropriate platform instance for the given version. + * + * @param string $version The platform/server version string to evaluate. This should be given in the notation + * the underlying database vendor uses. + * + * @return \Doctrine\DBAL\Platforms\AbstractPlatform + * + * @throws DBALException if the given version string could not be evaluated. + */ + public function createDatabasePlatformForVersion($version); +} diff --git a/vendor/doctrine/doctrine-bundle/.travis.yml b/vendor/doctrine/doctrine-bundle/.travis.yml new file mode 100644 index 0000000..165c61d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/.travis.yml @@ -0,0 +1,37 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: + include: + - php: 5.3 + env: deps=low + +env: + global: + - deps=no + +before_install: + - composer self-update + +install: + - if [ "$deps" = "no" ]; then composer install; fi; + - if [ "$deps" = "low" ]; then composer --prefer-lowest --prefer-stable update; fi; + +script: + - ./vendor/bin/phpunit -v --coverage-clover ./build/logs/clover.xml + +after_script: + - php ./vendor/bin/coveralls -v diff --git a/vendor/doctrine/doctrine-bundle/Changelog.md b/vendor/doctrine/doctrine-bundle/Changelog.md new file mode 100644 index 0000000..2dc85f2 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Changelog.md @@ -0,0 +1,141 @@ +## 1.5.2 (2015-08-31) + +Security: + +* Fix Security Misconfiguration Vulnerability, allowing potential local arbitrary code execution. + +## 1.5.1 (2015-08-12) + +Bugfix: + +* Fixed the JS expanding all queries in the profiler in case of multiple connections +* Fixed the retrieval of the namespace in DisconnectedMetadataFactory +* Changed the composer constraint to allow Symfony 3.0 for people wanting to do early testing + +## 1.5.0 (2015-05-28) + +Features: + +* Added the possibility to configure the quote strategy in the configuration +* Improved the rendering of the query explanation for Postgres +* Added support for tagging entity listeners, without the need to map the listener + +Bugfix: + +* Fixed the serverVersion configuration for master slave connections +* Fixed the enabling of the profiler to avoid automatically enabling the logger +* Fixed the detection of existing databases when quoted names are used +* Fixed the profiler template when having a manager without any loaded metadata + +## 1.4.0 (2015-02-28) + +Features: + +* Added the ``--if-not-exists`` option in the ``doctrine:database:create`` command +* Added the ``--if-exists`` option in the ``doctrine:database:drop`` command +* Added the support for the ``url`` DBAL setting +* Added profiling ability for SQL Server + +Bugfix: + +* Fixed the cache configuration when using ``cache_provider`` +* Removed usage of deprecated DI features when using Symfony 2.6+ +* Close connections and clear managers on shutdown. This avoids leaking connections in tests. +* Added an exception when the ORM layer is configured but not DBAL + +## 1.3.0 (2014-11-28) + +Features: + +* add support for bundle namespace alias in the mapping compiler pass +* Added support for server_version connection parameter +* Added a way to enable auto_mapping option using multiple entity managers + +Bugfix: + +* Inlined the profiler picto images instead of getting from FrameworkBundle (where they are gone) +* Remove duplicates in the list of mapped entities in the profile +* Fixed the compatibility with PHP 5.3 (broken in 1.3.0-beta1) + +## 1.3.0-beta2 (2014-07-09) + +Feature: + +* add auto-commit DBAL configuration option +* Use DoctrineCacheBundle to create cache drivers, supporting more configuration +* Added sorting by time in DB panel + +Bugfix: + +* Fixed the compatibility of the DataCollector with Doctrine 2.4 (bug introduced in 1.3.0-beta1) +* Fixed the exit code of commands on failure +* Fixed the replacement of query parameters in the profiler + +## 1.3.0-beta1 (2014-01-26) + +Features: + +* Added option to configure entity listener resolver service +* add compiler pass for bundles to register mappings +* Added a button to expand/collapse all queries in the profiler +* Added configuration for sharding +* Added support for the new ways to generate proxies in Doctrine Common +* Added configuration for the second-level cache + +Bugfix: + +* Removed deprecated call +* fix drop and create command for connections with master slave configuration +* Remove usage of deprecated Twig features + +## 1.2.0 (2013-03-25) + + * Bumped the requirement to Symfony 2.2 + * Updated the profiler templates for Symfony 2.2 + +## 1.1.0 (2013-01-12) + + * Added syntax highlighting for queries in teh profiler + * Added the validation of the mapping in the profiler panel + * Added return codes for doctrine:database:[create|drop] commands + +## 1.0.0 (2012-09-07) + + * Removed the mysql charset hack for 5.3.6+ as PDO has been fixed + * Implement "keep_slave"/"keepSlave" configuration + * Added missing Redis cache class mapping. + * Fixed the XSD schema for the configuration. + * Added support for schema assets filter configuration + * integrate naming_strategy into config + +## 1.0.0-RC1 (2012-07-04) + + * Add support for targetEntity resolving through the ORM 2.2 listener. + * Fixed quote database name in doctrine:database:create and doctrine:database:drop commands + * added a way to use cache services + * Added a way to configure the default entity repository class + * Added the support for SQL filters + * Removed the InfoCommand and proxy the ORM command instead + * Made the ORM fully optional by avoiding breaking the console + * Added support for master_slave connections + * Fixed xml config for proxy parameters + * Fixes doctrine:generate:entities when called with the --path argument + * Added missing Memcached cache driver + * Fix memory leak in Doctrine Autoload Proxy Magic + * adds lazy-loading event manager, improved listener registration + * Added a configuration setting for commented types + * Fixed bug with MetadataFactory having problem when the StaticReflection is used. + * Added the possibility to explain queries in the profiler + * Splitted the configuration for the logging and the profiling of the connection + +## 1.0.0-beta1 (2011-12-15) + + * [BC break] Changed the namespace from Symfony\Bundle to Doctrine\Bundle + * Enhance error reporting during mapping validation when nested exceptions occur. + * Add DoctrineValidationPass to load validation files conditionally + * Moved the entity provider service to DoctrineBundle + * Added Stopwatch support in debug mode to integrate in the profiler timeline + * Removed the IndexedReader + * Added the implementation of the ManagerRegistry to replace the symfony 2.0 registry + * Added access to Doctrine's ValidateSchema command from the console. See symfony/symfony#2200. + * Extracted the bundle from Symfony 2.0 diff --git a/vendor/doctrine/doctrine-bundle/Command/CreateDatabaseDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/CreateDatabaseDoctrineCommand.php new file mode 100644 index 0000000..76a5dd5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/CreateDatabaseDoctrineCommand.php @@ -0,0 +1,104 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\DriverManager; + +/** + * Database tool allows you to easily drop and create your configured databases. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class CreateDatabaseDoctrineCommand extends DoctrineCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('doctrine:database:create') + ->setDescription('Creates the configured database') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->addOption('if-not-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database already exists') + ->setHelp(<<%command.name% command creates the default connections database: + + php %command.full_name% + +You can also optionally specify the name of a connection to create the database for: + + php %command.full_name% --connection=default +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $connectionName = $input->getOption('connection'); + if (empty($connectionName) === true) { + $connectionName = $this->getContainer()->get('doctrine')->getDefaultConnectionName(); + } + $connection = $this->getDoctrineConnection($connectionName); + + $ifNotExists = $input->getOption('if-not-exists'); + + $params = $connection->getParams(); + if (isset($params['master'])) { + $params = $params['master']; + } + + $hasPath = isset($params['path']); + $name = $hasPath ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false); + if (!$name) { + throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped."); + } + // Need to get rid of _every_ occurrence of dbname from connection configuration and we have already extracted all relevant info from url + unset($params['dbname'], $params['path'], $params['url']); + + $tmpConnection = DriverManager::getConnection($params); + $shouldNotCreateDatabase = $ifNotExists && in_array($name, $tmpConnection->getSchemaManager()->listDatabases()); + + // Only quote if we don't have a path + if (!$hasPath) { + $name = $tmpConnection->getDatabasePlatform()->quoteSingleIdentifier($name); + } + + $error = false; + try { + if ($shouldNotCreateDatabase) { + $output->writeln(sprintf('Database %s for connection named %s already exists. Skipped.', $name, $connectionName)); + } else { + $tmpConnection->getSchemaManager()->createDatabase($name); + $output->writeln(sprintf('Created database %s for connection named %s', $name, $connectionName)); + } + } catch (\Exception $e) { + $output->writeln(sprintf('Could not create database %s for connection named %s', $name, $connectionName)); + $output->writeln(sprintf('%s', $e->getMessage())); + $error = true; + } + + $tmpConnection->close(); + + return $error ? 1 : 0; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/DoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/DoctrineCommand.php new file mode 100644 index 0000000..fbfac0a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/DoctrineCommand.php @@ -0,0 +1,68 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Doctrine\ORM\Tools\EntityGenerator; + +/** + * Base class for Doctrine console commands to extend from. + * + * @author Fabien Potencier + */ +abstract class DoctrineCommand extends ContainerAwareCommand +{ + /** + * get a doctrine entity generator + * + * @return EntityGenerator + */ + protected function getEntityGenerator() + { + $entityGenerator = new EntityGenerator(); + $entityGenerator->setGenerateAnnotations(false); + $entityGenerator->setGenerateStubMethods(true); + $entityGenerator->setRegenerateEntityIfExists(false); + $entityGenerator->setUpdateEntityIfExists(true); + $entityGenerator->setNumSpaces(4); + $entityGenerator->setAnnotationPrefix('ORM\\'); + + return $entityGenerator; + } + + /** + * Get a doctrine entity manager by symfony name. + * + * @param string $name + * + * @return \Doctrine\ORM\EntityManager + */ + protected function getEntityManager($name) + { + return $this->getContainer()->get('doctrine')->getManager($name); + } + + /** + * Get a doctrine dbal connection by symfony name. + * + * @param string $name + * + * @return \Doctrine\DBAL\Connection + */ + protected function getDoctrineConnection($name) + { + return $this->getContainer()->get('doctrine')->getConnection($name); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/DropDatabaseDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/DropDatabaseDoctrineCommand.php new file mode 100644 index 0000000..35629f9 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/DropDatabaseDoctrineCommand.php @@ -0,0 +1,115 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Doctrine\DBAL\DriverManager; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Database tool allows you to easily drop and create your configured databases. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DropDatabaseDoctrineCommand extends DoctrineCommand +{ + const RETURN_CODE_NOT_DROP = 1; + + const RETURN_CODE_NO_FORCE = 2; + + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('doctrine:database:drop') + ->setDescription('Drops the configured database') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->addOption('if-exists', null, InputOption::VALUE_NONE, 'Don\'t trigger an error, when the database doesn\'t exist') + ->addOption('force', null, InputOption::VALUE_NONE, 'Set this parameter to execute this action') + ->setHelp(<<%command.name% command drops the default connections database: + + php %command.full_name% + +The --force parameter has to be used to actually drop the database. + +You can also optionally specify the name of a connection to drop the database for: + + php %command.full_name% --connection=default + +Be careful: All data in a given database will be lost when executing this command. +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $connection = $this->getDoctrineConnection($input->getOption('connection')); + $ifExists = $input->getOption('if-exists'); + + $params = $connection->getParams(); + if (isset($params['master'])) { + $params = $params['master']; + } + + $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false); + if (!$name) { + throw new \InvalidArgumentException("Connection does not contain a 'path' or 'dbname' parameter and cannot be dropped."); + } + unset($params['dbname']); + + if ($input->getOption('force')) { + // Reopen connection without database name set + // as some vendors do not allow dropping the database connected to. + $connection->close(); + $connection = DriverManager::getConnection($params); + $shouldDropDatabase = !$ifExists || in_array($name, $connection->getSchemaManager()->listDatabases()); + + // Only quote if we don't have a path + if (!isset($params['path'])) { + $name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name); + } + + try { + if ($shouldDropDatabase) { + $connection->getSchemaManager()->dropDatabase($name); + $output->writeln(sprintf('Dropped database for connection named %s', $name)); + } else { + $output->writeln(sprintf('Database for connection named %s doesn\'t exist. Skipped.', $name)); + } + } catch (\Exception $e) { + $output->writeln(sprintf('Could not drop database for connection named %s', $name)); + $output->writeln(sprintf('%s', $e->getMessage())); + + return self::RETURN_CODE_NOT_DROP; + } + } else { + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'); + $output->writeln(''); + $output->writeln(sprintf('Would drop the database named %s.', $name)); + $output->writeln('Please run the operation with --force to execute'); + $output->writeln('All data will be lost!'); + + return self::RETURN_CODE_NO_FORCE; + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/GenerateEntitiesDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/GenerateEntitiesDoctrineCommand.php new file mode 100644 index 0000000..8e53728 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/GenerateEntitiesDoctrineCommand.php @@ -0,0 +1,140 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory; + +/** + * Generate entity classes from mapping information + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class GenerateEntitiesDoctrineCommand extends DoctrineCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('doctrine:generate:entities') + ->setAliases(array('generate:doctrine:entities')) + ->setDescription('Generates entity classes and method stubs from your mapping information') + ->addArgument('name', InputArgument::REQUIRED, 'A bundle name, a namespace, or a class name') + ->addOption('path', null, InputOption::VALUE_REQUIRED, 'The path where to generate entities when it cannot be guessed') + ->addOption('no-backup', null, InputOption::VALUE_NONE, 'Do not backup existing entities files.') + ->setHelp(<<%command.name% command generates entity classes +and method stubs from your mapping information: + +You have to limit generation of entities: + +* To a bundle: + + php %command.full_name% MyCustomBundle + +* To a single entity: + + php %command.full_name% MyCustomBundle:User + php %command.full_name% MyCustomBundle/Entity/User + +* To a namespace + + php %command.full_name% MyCustomBundle/Entity + +If the entities are not stored in a bundle, and if the classes do not exist, +the command has no way to guess where they should be generated. In this case, +you must provide the --path option: + + php %command.full_name% Blog/Entity --path=src/ + +By default, the unmodified version of each entity is backed up and saved +(e.g. Product.php~). To prevent this task from creating the backup file, +pass the --no-backup option: + + php %command.full_name% Blog/Entity --no-backup + +Important: Even if you specified Inheritance options in your +XML or YAML Mapping files the generator cannot generate the base and +child classes for you correctly, because it doesn't know which +class is supposed to extend which. You have to adjust the entity +code manually for inheritance to work! + +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $manager = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine')); + + try { + $bundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('name')); + + $output->writeln(sprintf('Generating entities for bundle "%s"', $bundle->getName())); + $metadata = $manager->getBundleMetadata($bundle); + } catch (\InvalidArgumentException $e) { + $name = strtr($input->getArgument('name'), '/', '\\'); + + if (false !== $pos = strpos($name, ':')) { + $name = $this->getContainer()->get('doctrine')->getAliasNamespace(substr($name, 0, $pos)).'\\'.substr($name, $pos + 1); + } + + if (class_exists($name)) { + $output->writeln(sprintf('Generating entity "%s"', $name)); + $metadata = $manager->getClassMetadata($name, $input->getOption('path')); + } else { + $output->writeln(sprintf('Generating entities for namespace "%s"', $name)); + $metadata = $manager->getNamespaceMetadata($name, $input->getOption('path')); + } + } + + $generator = $this->getEntityGenerator(); + + $backupExisting = !$input->getOption('no-backup'); + $generator->setBackupExisting($backupExisting); + + $repoGenerator = new EntityRepositoryGenerator(); + foreach ($metadata->getMetadata() as $m) { + if ($backupExisting) { + $basename = substr($m->name, strrpos($m->name, '\\') + 1); + $output->writeln(sprintf(' > backing up %s.php to %s.php~', $basename, $basename)); + } + // Getting the metadata for the entity class once more to get the correct path if the namespace has multiple occurrences + try { + $entityMetadata = $manager->getClassMetadata($m->getName(), $input->getOption('path')); + } catch (\RuntimeException $e) { + // fall back to the bundle metadata when no entity class could be found + $entityMetadata = $metadata; + } + + $output->writeln(sprintf(' > generating %s', $m->name)); + $generator->generate(array($m), $entityMetadata->getPath()); + + if ($m->customRepositoryClassName && false !== strpos($m->customRepositoryClassName, $metadata->getNamespace())) { + $repoGenerator->writeEntityRepositoryClass($m->customRepositoryClassName, $metadata->getPath()); + } + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/ImportMappingDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/ImportMappingDoctrineCommand.php new file mode 100644 index 0000000..825e3f7 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/ImportMappingDoctrineCommand.php @@ -0,0 +1,137 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Mapping\Driver\DatabaseDriver; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\Console\MetadataFilter; + +/** + * Import Doctrine ORM metadata mapping information from an existing database. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ImportMappingDoctrineCommand extends DoctrineCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('doctrine:mapping:import') + ->addArgument('bundle', InputArgument::REQUIRED, 'The bundle to import the mapping information to') + ->addArgument('mapping-type', InputArgument::OPTIONAL, 'The mapping type to export the imported mapping information to') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A string pattern used to match entities that should be mapped.') + ->addOption('force', null, InputOption::VALUE_NONE, 'Force to overwrite existing mapping files.') + ->setDescription('Imports mapping information from an existing database') + ->setHelp(<<%command.name% command imports mapping information +from an existing database: + +php %command.full_name% "MyCustomBundle" xml + +You can also optionally specify which entity manager to import from with the +--em option: + +php %command.full_name% "MyCustomBundle" xml --em=default + +If you don't want to map every entity that can be found in the database, use the +--filter option. It will try to match the targeted mapped entity with the +provided pattern string. + +php %command.full_name% "MyCustomBundle" xml --filter=MyMatchedEntity + +Use the --force option, if you want to override existing mapping files: + +php %command.full_name% "MyCustomBundle" xml --force +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $bundle = $this->getApplication()->getKernel()->getBundle($input->getArgument('bundle')); + + $destPath = $bundle->getPath(); + $type = $input->getArgument('mapping-type') ? $input->getArgument('mapping-type') : 'xml'; + if ('annotation' === $type) { + $destPath .= '/Entity'; + } else { + $destPath .= '/Resources/config/doctrine'; + } + if ('yaml' === $type) { + $type = 'yml'; + } + + $cme = new ClassMetadataExporter(); + $exporter = $cme->getExporter($type); + $exporter->setOverwriteExistingFiles($input->getOption('force')); + + if ('annotation' === $type) { + $entityGenerator = $this->getEntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + } + + $em = $this->getEntityManager($input->getOption('em')); + + $databaseDriver = new DatabaseDriver($em->getConnection()->getSchemaManager()); + $em->getConfiguration()->setMetadataDriverImpl($databaseDriver); + + $emName = $input->getOption('em'); + $emName = $emName ? $emName : 'default'; + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); + if ($metadata) { + $output->writeln(sprintf('Importing mapping information from "%s" entity manager', $emName)); + foreach ($metadata as $class) { + $className = $class->name; + $class->name = $bundle->getNamespace().'\\Entity\\'.$className; + if ('annotation' === $type) { + $path = $destPath.'/'.str_replace('\\', '.', $className).'.php'; + } else { + $path = $destPath.'/'.str_replace('\\', '.', $className).'.orm.'.$type; + } + $output->writeln(sprintf(' > writing %s', $path)); + $code = $exporter->exportClassMetadata($class); + if (!is_dir($dir = dirname($path))) { + mkdir($dir, 0775, true); + } + file_put_contents($path, $code); + chmod($path, 0664); + } + + return 0; + } else { + $output->writeln('Database does not have any mapping information.', 'ERROR'); + $output->writeln('', 'ERROR'); + + return 1; + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php new file mode 100644 index 0000000..dc8797c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearMetadataCacheDoctrineCommand.php @@ -0,0 +1,52 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ClearMetadataCacheDoctrineCommand extends MetadataCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:cache:clear-metadata') + ->setDescription('Clears all metadata cache for an entity manager') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php new file mode 100644 index 0000000..119d3cd --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearQueryCacheDoctrineCommand.php @@ -0,0 +1,52 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand; + +/** + * Command to clear the query cache of the various cache drivers. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ClearQueryCacheDoctrineCommand extends QueryCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:cache:clear-query') + ->setDescription('Clears all query cache for an entity manager') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearResultCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearResultCacheDoctrineCommand.php new file mode 100644 index 0000000..95107cf --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/ClearResultCacheDoctrineCommand.php @@ -0,0 +1,52 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand; + +/** + * Command to clear the result cache of the various cache drivers. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ClearResultCacheDoctrineCommand extends ResultCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:cache:clear-result') + ->setDescription('Clears result cache for an entity manager') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/CollectionRegionDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/CollectionRegionDoctrineCommand.php new file mode 100644 index 0000000..e9432b5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/CollectionRegionDoctrineCommand.php @@ -0,0 +1,51 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Doctrine\ORM\Tools\Console\Command\ClearCache\CollectionRegionCommand; + +/** + * Command to clear a collection cache region. + * + * @author Fabio B. Silva + */ +class CollectionRegionDoctrineCommand extends DelegateCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this->setName('doctrine:cache:clear-collection-region'); + } + + /** + * {@inheritDoc} + */ + protected function createCommand() + { + return new CollectionRegionCommand(); + } + + /** + * {@inheritDoc} + */ + protected function getMinimalVersion() + { + return '2.5.0-DEV'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/ConvertMappingDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/ConvertMappingDoctrineCommand.php new file mode 100644 index 0000000..6e09f3b --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/ConvertMappingDoctrineCommand.php @@ -0,0 +1,72 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand; +use Doctrine\ORM\Tools\Export\Driver\XmlExporter; +use Doctrine\ORM\Tools\Export\Driver\YamlExporter; + +/** + * Convert Doctrine ORM metadata mapping information between the various supported + * formats. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class ConvertMappingDoctrineCommand extends ConvertMappingCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + $this + ->setName('doctrine:mapping:convert') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } + + /** + * @param string $toType + * @param string $destPath + * + * @return \Doctrine\ORM\Tools\Export\Driver\AbstractExporter + */ + protected function getExporter($toType, $destPath) + { + /** @var $exporter \Doctrine\ORM\Tools\Export\Driver\AbstractExporter */ + $exporter = parent::getExporter($toType, $destPath); + if ($exporter instanceof XmlExporter) { + $exporter->setExtension('.orm.xml'); + } elseif ($exporter instanceof YamlExporter) { + $exporter->setExtension('.orm.yml'); + } + + return $exporter; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/CreateSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/CreateSchemaDoctrineCommand.php new file mode 100644 index 0000000..61dd67d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/CreateSchemaDoctrineCommand.php @@ -0,0 +1,53 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand; + +/** + * Command to execute the SQL needed to generate the database schema for + * a given entity manager. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class CreateSchemaDoctrineCommand extends CreateCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:create') + ->setDescription('Executes (or dumps) the SQL needed to generate the database schema') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/DelegateCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/DelegateCommand.php new file mode 100644 index 0000000..bd71147 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/DelegateCommand.php @@ -0,0 +1,119 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command Delegate. + * + * @author Fabio B. Silva + */ +abstract class DelegateCommand extends Command +{ + /** + * @var \Symfony\Component\Console\Command\Command + */ + protected $command; + + /** + * @return \Symfony\Component\Console\Command\Command + */ + abstract protected function createCommand(); + + /** + * @return string + */ + protected function getMinimalVersion() + { + return '2.3.0-DEV'; + } + + /** + * @return boolean + */ + private function isVersionCompatible() + { + return (version_compare(\Doctrine\ORM\Version::VERSION, $this->getMinimalVersion()) >= 0); + } + + /** + * {@inheritDoc} + */ + public function isEnabled() + { + return $this->isVersionCompatible(); + } + + /** + * @param string $entityManagerName + * + * @return Command + */ + protected function wrapCommand($entityManagerName) + { + if (!$this->isVersionCompatible()) { + throw new \RuntimeException(sprintf('"%s" requires doctrine-orm "%s" or newer', $this->getName(), $this->getMinimalVersion())); + } + + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $entityManagerName); + $this->command->setApplication($this->getApplication()); + + return $this->command; + } + + /** + * {@inheritDoc} + */ + protected function configure() + { + if ($this->isVersionCompatible()) { + $this->command = $this->createCommand(); + + $this->setHelp($this->command->getHelp()); + $this->setDefinition($this->command->getDefinition()); + $this->setDescription($this->command->getDescription()); + } + + $this->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + return $this->wrapCommand($input->getOption('em'))->execute($input, $output); + } + + /** + * {@inheritDoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + $this->wrapCommand($input->getOption('em'))->interact($input, $output); + } + + /** + * {@inheritDoc} + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->wrapCommand($input->getOption('em'))->initialize($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/DoctrineCommandHelper.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/DoctrineCommandHelper.php new file mode 100644 index 0000000..f65b63f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/DoctrineCommandHelper.php @@ -0,0 +1,55 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; + +/** + * Provides some helper and convenience methods to configure doctrine commands in the context of bundles + * and multiple connections/entity managers. + * + * @author Fabien Potencier + */ +abstract class DoctrineCommandHelper +{ + /** + * Convenience method to push the helper sets of a given entity manager into the application. + * + * @param Application $application + * @param string $emName + */ + public static function setApplicationEntityManager(Application $application, $emName) + { + /** @var $em \Doctrine\ORM\EntityManager */ + $em = $application->getKernel()->getContainer()->get('doctrine')->getManager($emName); + $helperSet = $application->getHelperSet(); + $helperSet->set(new ConnectionHelper($em->getConnection()), 'db'); + $helperSet->set(new EntityManagerHelper($em), 'em'); + } + + /** + * Convenience method to push the helper sets of a given connection into the application. + * + * @param Application $application + * @param string $connName + */ + public static function setApplicationConnection(Application $application, $connName) + { + $connection = $application->getKernel()->getContainer()->get('doctrine')->getConnection($connName); + $helperSet = $application->getHelperSet(); + $helperSet->set(new ConnectionHelper($connection), 'db'); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/DropSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/DropSchemaDoctrineCommand.php new file mode 100644 index 0000000..b69d9f7 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/DropSchemaDoctrineCommand.php @@ -0,0 +1,52 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand; + +/** + * Command to drop the database schema for a set of classes based on their mappings. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DropSchemaDoctrineCommand extends DropCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:drop') + ->setDescription('Executes (or dumps) the SQL needed to drop the current database schema') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php new file mode 100644 index 0000000..5601164 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php @@ -0,0 +1,51 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand; + +/** + * Ensure the Doctrine ORM is configured properly for a production environment. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class EnsureProductionSettingsDoctrineCommand extends EnsureProductionSettingsCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:ensure-production-settings') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/EntityRegionCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/EntityRegionCacheDoctrineCommand.php new file mode 100644 index 0000000..7674df5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/EntityRegionCacheDoctrineCommand.php @@ -0,0 +1,51 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand; + +/** + * Command to clear a entity cache region. + * + * @author Fabio B. Silva + */ +class EntityRegionCacheDoctrineCommand extends DelegateCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this->setName('doctrine:cache:clear-entity-region'); + } + + /** + * {@inheritDoc} + */ + protected function createCommand() + { + return new EntityRegionCommand(); + } + + /** + * {@inheritDoc} + */ + protected function getMinimalVersion() + { + return '2.5.0-DEV'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/InfoDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/InfoDoctrineCommand.php new file mode 100644 index 0000000..2228a35 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/InfoDoctrineCommand.php @@ -0,0 +1,48 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Doctrine\ORM\Tools\Console\Command\InfoCommand; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Show information about mapped entities + * + * @author Benjamin Eberlei + */ +class InfoDoctrineCommand extends InfoCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('doctrine:mapping:info') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/QueryRegionCacheDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/QueryRegionCacheDoctrineCommand.php new file mode 100644 index 0000000..bfec367 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/QueryRegionCacheDoctrineCommand.php @@ -0,0 +1,51 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryRegionCommand; + +/** + * Command to clear a query cache region. + * + * @author Fabio B. Silva + */ +class QueryRegionCacheDoctrineCommand extends DelegateCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this->setName('doctrine:cache:clear-query-region'); + } + + /** + * {@inheritDoc} + */ + protected function createCommand() + { + return new QueryRegionCommand(); + } + + /** + * {@inheritDoc} + */ + protected function getMinimalVersion() + { + return '2.5.0-DEV'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/RunDqlDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/RunDqlDoctrineCommand.php new file mode 100644 index 0000000..ad65a26 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/RunDqlDoctrineCommand.php @@ -0,0 +1,68 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\RunDqlCommand; + +/** + * Execute a Doctrine DQL query and output the results. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class RunDqlDoctrineCommand extends RunDqlCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:query:dql') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command') + ->setHelp(<<%command.name% command executes the given DQL query and +outputs the results: + +php %command.full_name% "SELECT u FROM UserBundle:User u" + +You can also optional specify some additional options like what type of +hydration to use when executing the query: + +php %command.full_name% "SELECT u FROM UserBundle:User u" --hydrate=array + +Additionally you can specify the first result and maximum amount of results to +show: + +php %command.full_name% "SELECT u FROM UserBundle:User u" --first-result=0 --max-result=30 +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/RunSqlDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/RunSqlDoctrineCommand.php new file mode 100644 index 0000000..b4e94d5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/RunSqlDoctrineCommand.php @@ -0,0 +1,58 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; + +/** + * Execute a SQL query and output the results. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class RunSqlDoctrineCommand extends RunSqlCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:query:sql') + ->addOption('connection', null, InputOption::VALUE_OPTIONAL, 'The connection to use for this command') + ->setHelp(<<%command.name% command executes the given SQL query and +outputs the results: + +php %command.full_name% "SELECT * from user" +EOT + ); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationConnection($this->getApplication(), $input->getOption('connection')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/UpdateSchemaDoctrineCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/UpdateSchemaDoctrineCommand.php new file mode 100644 index 0000000..918fc2c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/UpdateSchemaDoctrineCommand.php @@ -0,0 +1,52 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand; + +/** + * Command to generate the SQL needed to update the database schema to match + * the current mapping information. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class UpdateSchemaDoctrineCommand extends UpdateCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:update') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Command/Proxy/ValidateSchemaCommand.php b/vendor/doctrine/doctrine-bundle/Command/Proxy/ValidateSchemaCommand.php new file mode 100644 index 0000000..d981b42 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Command/Proxy/ValidateSchemaCommand.php @@ -0,0 +1,52 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Command\Proxy; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand as DoctrineValidateSchemaCommand; + +/** + * Command to run Doctrine ValidateSchema() on the current mappings. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + * @author Neil Katin + */ +class ValidateSchemaCommand extends DoctrineValidateSchemaCommand +{ + /** + * {@inheritDoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setName('doctrine:schema:validate') + ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command'); + } + + /** + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em')); + + return parent::execute($input, $output); + } +} diff --git a/vendor/doctrine/doctrine-bundle/ConnectionFactory.php b/vendor/doctrine/doctrine-bundle/ConnectionFactory.php new file mode 100644 index 0000000..5377002 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/ConnectionFactory.php @@ -0,0 +1,89 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Types\Type; + +/** + * Connection + */ +class ConnectionFactory +{ + private $typesConfig = array(); + private $commentedTypes = array(); + private $initialized = false; + + /** + * Construct. + * + * @param array $typesConfig + */ + public function __construct(array $typesConfig) + { + $this->typesConfig = $typesConfig; + } + + /** + * Create a connection by name. + * + * @param array $params + * @param Configuration $config + * @param EventManager $eventManager + * @param array $mappingTypes + * + * @return \Doctrine\DBAL\Connection + */ + public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array()) + { + if (!$this->initialized) { + $this->initializeTypes(); + $this->initialized = true; + } + + $connection = DriverManager::getConnection($params, $config, $eventManager); + + if (!empty($mappingTypes)) { + $platform = $connection->getDatabasePlatform(); + foreach ($mappingTypes as $dbType => $doctrineType) { + $platform->registerDoctrineTypeMapping($dbType, $doctrineType); + } + foreach ($this->commentedTypes as $type) { + $platform->markDoctrineTypeCommented(Type::getType($type)); + } + } + + return $connection; + } + + /** + * initialize the types + */ + private function initializeTypes() + { + foreach ($this->typesConfig as $type => $typeConfig) { + if (Type::hasType($type)) { + Type::overrideType($type, $typeConfig['class']); + } else { + Type::addType($type, $typeConfig['class']); + } + if ($typeConfig['commented']) { + $this->commentedTypes[] = $type; + } + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Controller/ProfilerController.php b/vendor/doctrine/doctrine-bundle/Controller/ProfilerController.php new file mode 100644 index 0000000..1b9b86a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Controller/ProfilerController.php @@ -0,0 +1,91 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Controller; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Platforms\SQLServerPlatform; +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\Response; + +/** + * ProfilerController. + * + * @author Christophe Coevoet + */ +class ProfilerController extends ContainerAware +{ + /** + * Renders the profiler panel for the given token. + * + * @param string $token The profiler token + * @param string $connectionName + * @param integer $query + * + * @return Response A Response instance + */ + public function explainAction($token, $connectionName, $query) + { + /** @var $profiler \Symfony\Component\HttpKernel\Profiler\Profiler */ + $profiler = $this->container->get('profiler'); + $profiler->disable(); + + $profile = $profiler->loadProfile($token); + $queries = $profile->getCollector('db')->getQueries(); + + if (!isset($queries[$connectionName][$query])) { + return new Response('This query does not exist.'); + } + + $query = $queries[$connectionName][$query]; + if (!$query['explainable']) { + return new Response('This query cannot be explained.'); + } + + /** @var $connection \Doctrine\DBAL\Connection */ + $connection = $this->container->get('doctrine')->getConnection($connectionName); + try { + if ($connection->getDatabasePlatform() instanceof SQLServerPlatform) { + $results = $this->explainSQLServerPlatform($connection, $query); + } else { + $results = $this->explainOtherPlatform($connection, $query); + } + } catch (\Exception $e) { + return new Response('This query cannot be explained.'); + } + + return $this->container->get('templating')->renderResponse('@Doctrine/Collector/explain.html.twig', array( + 'data' => $results, + 'query' => $query, + )); + } + + private function explainSQLServerPlatform(Connection $connection, $query) + { + if (stripos($query['sql'], 'SELECT') === 0) { + $sql = 'SET STATISTICS PROFILE ON; ' . $query['sql'] . '; SET STATISTICS PROFILE OFF;'; + } else { + $sql = 'SET SHOWPLAN_TEXT ON; GO; SET NOEXEC ON; ' . $query['sql'] .'; SET NOEXEC OFF; GO; SET SHOWPLAN_TEXT OFF;'; + } + $stmt = $connection->executeQuery($sql, $query['params'], $query['types']); + $stmt->nextRowset(); + return $stmt->fetchAll(\PDO::FETCH_ASSOC); + } + + private function explainOtherPlatform(Connection $connection, $query) + { + return $connection->executeQuery('EXPLAIN '.$query['sql'], $query['params'], $query['types']) + ->fetchAll(\PDO::FETCH_ASSOC); + } +} diff --git a/vendor/doctrine/doctrine-bundle/DataCollector/DoctrineDataCollector.php b/vendor/doctrine/doctrine-bundle/DataCollector/DoctrineDataCollector.php new file mode 100644 index 0000000..6ced587 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/DataCollector/DoctrineDataCollector.php @@ -0,0 +1,192 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DataCollector; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\Tools\SchemaValidator; +use Doctrine\ORM\Version; +use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector as BaseCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * DoctrineDataCollector. + * + * @author Christophe Coevoet + */ +class DoctrineDataCollector extends BaseCollector +{ + private $registry; + private $invalidEntityCount; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + + parent::__construct($registry); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + parent::collect($request, $response, $exception); + + $errors = array(); + $entities = array(); + $caches = array( + 'enabled' => false, + 'log_enabled' => false, + 'counts' => array( + 'puts' => 0, + 'hits' => 0, + 'misses' => 0, + ), + 'regions' => array( + 'puts' => array(), + 'hits' => array(), + 'misses' => array(), + ), + ); + + foreach ($this->registry->getManagers() as $name => $em) { + $entities[$name] = array(); + /** @var $factory \Doctrine\ORM\Mapping\ClassMetadataFactory */ + $factory = $em->getMetadataFactory(); + $validator = new SchemaValidator($em); + + /** @var $class \Doctrine\ORM\Mapping\ClassMetadataInfo */ + foreach ($factory->getLoadedMetadata() as $class) { + if (!isset($entities[$name][$class->getName()])) { + $classErrors = $validator->validateClass($class); + $entities[$name][$class->getName()] = $class->getName(); + + if (!empty($classErrors)) { + $errors[$name][$class->getName()] = $classErrors; + } + } + } + + if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) { + continue; + } + + /** @var $emConfig \Doctrine\ORM\Configuration */ + $emConfig = $em->getConfiguration(); + $slcEnabled = $emConfig->isSecondLevelCacheEnabled(); + + if (!$slcEnabled) { + continue; + } + + $caches['enabled'] = true; + + /** @var $cacheConfiguration \Doctrine\ORM\Cache\CacheConfiguration */ + /** @var $cacheLoggerChain \Doctrine\ORM\Cache\Logging\CacheLoggerChain */ + $cacheConfiguration = $emConfig->getSecondLevelCacheConfiguration(); + $cacheLoggerChain = $cacheConfiguration->getCacheLogger(); + + if (!$cacheLoggerChain || !$cacheLoggerChain->getLogger('statistics')) { + continue; + } + + /** @var $cacheLoggerStats \Doctrine\ORM\Cache\Logging\StatisticsCacheLogger */ + $cacheLoggerStats = $cacheLoggerChain->getLogger('statistics'); + $caches['log_enabled'] = true; + + $caches['counts']['puts'] += $cacheLoggerStats->getPutCount(); + $caches['counts']['hits'] += $cacheLoggerStats->getHitCount(); + $caches['counts']['misses'] += $cacheLoggerStats->getMissCount(); + + foreach ($cacheLoggerStats->getRegionsPut() as $key => $value) { + if (!isset($caches['regions']['puts'][$key])) { + $caches['regions']['puts'][$key] = 0; + } + + $caches['regions']['puts'][$key] += $value; + } + + foreach ($cacheLoggerStats->getRegionsHit() as $key => $value) { + if (!isset($caches['regions']['hits'][$key])) { + $caches['regions']['hits'][$key] = 0; + } + + $caches['regions']['hits'][$key] += $value; + } + + foreach ($cacheLoggerStats->getRegionsMiss() as $key => $value) { + if (!isset($caches['regions']['misses'][$key])) { + $caches['regions']['misses'][$key] = 0; + } + + $caches['regions']['misses'][$key] += $value; + } + } + + $this->data['entities'] = $entities; + $this->data['errors'] = $errors; + $this->data['caches'] = $caches; + } + + public function getEntities() + { + return $this->data['entities']; + } + + public function getMappingErrors() + { + return $this->data['errors']; + } + + public function getCacheHitsCount() + { + return $this->data['caches']['counts']['hits']; + } + + public function getCachePutsCount() + { + return $this->data['caches']['counts']['puts']; + } + + public function getCacheMissesCount() + { + return $this->data['caches']['counts']['misses']; + } + + public function getCacheEnabled() + { + return $this->data['caches']['enabled']; + } + + public function getCacheRegions() + { + return $this->data['caches']['regions']; + } + + public function getCacheCounts() + { + return $this->data['caches']['counts']; + } + + public function getInvalidEntityCount() + { + if (null === $this->invalidEntityCount) { + $this->invalidEntityCount = array_sum(array_map('count', $this->data['errors'])); + } + + return $this->invalidEntityCount; + } +} diff --git a/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/DoctrineOrmMappingsPass.php b/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/DoctrineOrmMappingsPass.php new file mode 100644 index 0000000..3c76f3d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/DoctrineOrmMappingsPass.php @@ -0,0 +1,167 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler; + +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterMappingsPass; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Class for Symfony bundles to configure mappings for model classes not in the + * auto-mapped folder. + * + * NOTE: alias is only supported by Symfony 2.6+ and will be ignored with older versions. + * + * @author David Buchmann + */ +class DoctrineOrmMappingsPass extends RegisterMappingsPass +{ + /** + * You should not directly instantiate this class but use one of the + * factory methods. + * + * @param Definition|Reference $driver Driver DI definition or reference. + * @param array $namespaces List of namespaces handled by $driver. + * @param string[] $managerParameters Ordered list of container parameters that + * could hold the manager name. + * doctrine.default_entity_manager is appended + * automatically. + * @param string|false $enabledParameter If specified, the compiler pass only executes + * if this parameter is defined in the service + * container. + * @param array $aliasMap Map of alias to namespace. + */ + public function __construct($driver, array $namespaces, array $managerParameters, $enabledParameter = false, array $aliasMap = array()) + { + $managerParameters[] = 'doctrine.default_entity_manager'; + parent::__construct( + $driver, + $namespaces, + $managerParameters, + 'doctrine.orm.%s_metadata_driver', + $enabledParameter, + 'doctrine.orm.%s_configuration', + 'addEntityNamespace', + $aliasMap + ); + } + + /** + * @param array $namespaces Hashmap of directory path to namespace. + * @param string[] $managerParameters List of parameters that could which object manager name + * your bundle uses. This compiler pass will automatically + * append the parameter name for the default entity manager + * to this list. + * @param string|false $enabledParameter Service container parameter that must be present to + * enable the mapping. Set to false to not do any check, + * optional. + * @param string[] $aliasMap Map of alias to namespace. + * + * @return self + */ + public static function createXmlMappingDriver(array $namespaces, array $managerParameters = array(), $enabledParameter = false, array $aliasMap = array()) + { + $arguments = array($namespaces, '.orm.xml'); + $locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments); + $driver = new Definition('Doctrine\ORM\Mapping\Driver\XmlDriver', array($locator)); + + return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap); + } + + /** + * @param array $namespaces Hashmap of directory path to namespace + * @param string[] $managerParameters List of parameters that could which object manager name + * your bundle uses. This compiler pass will automatically + * append the parameter name for the default entity manager + * to this list. + * @param string|false $enabledParameter Service container parameter that must be present to + * enable the mapping. Set to false to not do any check, + * optional. + * @param string[] $aliasMap Map of alias to namespace. + * + * @return self + */ + public static function createYamlMappingDriver(array $namespaces, array $managerParameters = array(), $enabledParameter = false, array $aliasMap = array()) + { + $arguments = array($namespaces, '.orm.yml'); + $locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments); + $driver = new Definition('Doctrine\ORM\Mapping\Driver\YamlDriver', array($locator)); + + return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap); + } + + /** + * @param array $namespaces Hashmap of directory path to namespace + * @param string[] $managerParameters List of parameters that could which object manager name + * your bundle uses. This compiler pass will automatically + * append the parameter name for the default entity manager + * to this list. + * @param string $enabledParameter Service container parameter that must be present to + * enable the mapping. Set to false to not do any check, + * optional. + * @param string[] $aliasMap Map of alias to namespace. + * + * @return self + */ + public static function createPhpMappingDriver(array $namespaces, array $managerParameters = array(), $enabledParameter = false, array $aliasMap = array()) + { + $arguments = array($namespaces, '.php'); + $locator = new Definition('Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator', $arguments); + $driver = new Definition('Doctrine\Common\Persistence\Mapping\Driver\PHPDriver', array($locator)); + + return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap); + } + + /** + * @param array $namespaces List of namespaces that are handled with annotation mapping + * @param array $directories List of directories to look for annotated classes + * @param string[] $managerParameters List of parameters that could which object manager name + * your bundle uses. This compiler pass will automatically + * append the parameter name for the default entity manager + * to this list. + * @param string|false $enabledParameter Service container parameter that must be present to + * enable the mapping. Set to false to not do any check, + * optional. + * @param string[] $aliasMap Map of alias to namespace. + * + * @return self + */ + public static function createAnnotationMappingDriver(array $namespaces, array $directories, array $managerParameters = array(), $enabledParameter = false, array $aliasMap = array()) + { + $reader = new Reference('annotation_reader'); + $driver = new Definition('Doctrine\ORM\Mapping\Driver\AnnotationDriver', array($reader, $directories)); + + return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap); + } + + /** + * @param array $namespaces List of namespaces that are handled with static php mapping + * @param array $directories List of directories to look for static php mapping files + * @param string[] $managerParameters List of parameters that could which object manager name + * your bundle uses. This compiler pass will automatically + * append the parameter name for the default entity manager + * to this list. + * @param string|false $enabledParameter Service container parameter that must be present to + * enable the mapping. Set to false to not do any check, + * optional. + * @param string[] $aliasMap Map of alias to namespace. + * + * @return self + */ + public static function createStaticPhpMappingDriver(array $namespaces, array $directories, array $managerParameters = array(), $enabledParameter = false, array $aliasMap = array()) + { + $driver = new Definition('Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver', array($directories)); + + return new DoctrineOrmMappingsPass($driver, $namespaces, $managerParameters, $enabledParameter, $aliasMap); + } +} diff --git a/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/EntityListenerPass.php b/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/EntityListenerPass.php new file mode 100644 index 0000000..bbceb8c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/DependencyInjection/Compiler/EntityListenerPass.php @@ -0,0 +1,82 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Class for Symfony bundles to register entity listeners + * + * @author Sander Marechal + */ +class EntityListenerPass implements CompilerPassInterface +{ + /** + * {@inheritDoc} + */ + public function process(ContainerBuilder $container) + { + $resolvers = $container->findTaggedServiceIds('doctrine.orm.entity_listener'); + + foreach ($resolvers as $id => $tagAttributes) { + foreach ($tagAttributes as $attributes) { + $name = isset($attributes['entity_manager']) ? $attributes['entity_manager'] : $container->getParameter('doctrine.default_entity_manager'); + $entityManager = sprintf('doctrine.orm.%s_entity_manager', $name); + + if (!$container->hasDefinition($entityManager)) { + continue; + } + + $resolver = sprintf('doctrine.orm.%s_entity_listener_resolver', $name); + if ($container->hasAlias($resolver)) { + $resolver = (string) $container->getAlias($resolver); + } + + if (!$container->hasDefinition($resolver)) { + continue; + } + + if (isset($attributes['entity']) && isset($attributes['event'])) { + $this->attachToListener($container, $name, $id, $attributes); + } + + $container->getDefinition($resolver)->addMethodCall('register', array(new Reference($id))); + } + } + } + + private function attachToListener(ContainerBuilder $container, $name, $id, array $attributes) + { + $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name); + + if (!$container->has($listenerId)) { + return; + } + + $serviceDef = $container->getDefinition($id); + + $args = array( + $attributes['entity'], + $serviceDef->getClass(), + $attributes['event'], + ); + + if (isset($attributes['method'])) { + $args[] = $attributes['method']; + } + + $container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args); + } +} diff --git a/vendor/doctrine/doctrine-bundle/DependencyInjection/Configuration.php b/vendor/doctrine/doctrine-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..495d380 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,657 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This class contains the configuration information for the bundle + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * @author Christophe Coevoet + */ +class Configuration implements ConfigurationInterface +{ + private $debug; + + /** + * Constructor + * + * @param Boolean $debug Whether to use the debug mode + */ + public function __construct($debug) + { + $this->debug = (Boolean) $debug; + } + + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('doctrine'); + + $this->addDbalSection($rootNode); + $this->addOrmSection($rootNode); + + return $treeBuilder; + } + + /** + * Add DBAL section to configuration tree + * + * @param ArrayNodeDefinition $node + */ + private function addDbalSection(ArrayNodeDefinition $node) + { + $node + ->children() + ->arrayNode('dbal') + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && !array_key_exists('connections', $v) && !array_key_exists('connection', $v); }) + ->then(function ($v) { + // Key that should not be rewritten to the connection config + $excludedKeys = array('default_connection' => true, 'types' => true, 'type' => true); + $connection = array(); + foreach ($v as $key => $value) { + if (isset($excludedKeys[$key])) { + continue; + } + $connection[$key] = $v[$key]; + unset($v[$key]); + } + $v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default'; + $v['connections'] = array($v['default_connection'] => $connection); + + return $v; + }) + ->end() + ->children() + ->scalarNode('default_connection')->end() + ->end() + ->fixXmlConfig('type') + ->children() + ->arrayNode('types') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('class' => $v); }) + ->end() + ->children() + ->scalarNode('class')->isRequired()->end() + ->booleanNode('commented')->defaultTrue()->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('connection') + ->append($this->getDbalConnectionsNode()) + ->end() + ; + } + + /** + * Return the dbal connections node + * + * @return ArrayNodeDefinition + */ + private function getDbalConnectionsNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('connections'); + + /** @var $connectionNode ArrayNodeDefinition */ + $connectionNode = $node + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ; + + $this->configureDbalDriverNode($connectionNode); + + $connectionNode + ->fixXmlConfig('option') + ->fixXmlConfig('mapping_type') + ->fixXmlConfig('slave') + ->fixXmlConfig('shard') + ->fixXmlConfig('default_table_option') + ->children() + ->scalarNode('driver')->defaultValue('pdo_mysql')->end() + ->scalarNode('platform_service')->end() + ->booleanNode('auto_commit')->end() + ->scalarNode('schema_filter')->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->booleanNode('profiling')->defaultValue($this->debug)->end() + ->scalarNode('server_version')->end() + ->scalarNode('driver_class')->end() + ->scalarNode('wrapper_class')->end() + ->scalarNode('shard_choser')->end() + ->scalarNode('shard_choser_service')->end() + ->booleanNode('keep_slave')->end() + ->arrayNode('options') + ->useAttributeAsKey('key') + ->prototype('scalar')->end() + ->end() + ->arrayNode('mapping_types') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('default_table_options') + ->info("This option is used by the schema-tool and affects generated SQL. Possible keys include 'charset','collate', and 'engine'.") + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ; + + $slaveNode = $connectionNode + ->children() + ->arrayNode('slaves') + ->useAttributeAsKey('name') + ->prototype('array') + ; + $this->configureDbalDriverNode($slaveNode); + + $shardNode = $connectionNode + ->children() + ->arrayNode('shards') + ->prototype('array') + ->children() + ->integerNode('id') + ->min(1) + ->isRequired() + ->end() + ->end() + ; + $this->configureDbalDriverNode($shardNode); + + return $node; + } + + /** + * Adds config keys related to params processed by the DBAL drivers + * + * These keys are available for slave configurations too. + * + * @param ArrayNodeDefinition $node + */ + private function configureDbalDriverNode(ArrayNodeDefinition $node) + { + $node + ->children() + ->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end() + ->scalarNode('dbname')->end() + ->scalarNode('host')->defaultValue('localhost')->end() + ->scalarNode('port')->defaultNull()->end() + ->scalarNode('user')->defaultValue('root')->end() + ->scalarNode('password')->defaultNull()->end() + ->scalarNode('charset')->end() + ->scalarNode('path')->end() + ->booleanNode('memory')->end() + ->scalarNode('unix_socket')->info('The unix socket to use for MySQL')->end() + ->booleanNode('persistent')->info('True to use as persistent connection for the ibm_db2 driver')->end() + ->scalarNode('protocol')->info('The protocol to use for the ibm_db2 driver (default to TCPIP if ommited)')->end() + ->booleanNode('service') + ->info('True to use SERVICE_NAME as connection parameter instead of SID for Oracle') + ->end() + ->scalarNode('servicename') + ->info( + 'Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter '. + 'for Oracle depending on the service parameter.' + ) + ->end() + ->scalarNode('sessionMode') + ->info('The session mode to use for the oci8 driver') + ->end() + ->scalarNode('server') + ->info('The name of a running database server to connect to for SQL Anywhere.') + ->end() + ->scalarNode('sslmode') + ->info( + 'Determines whether or with what priority a SSL TCP/IP connection will be negotiated with '. + 'the server for PostgreSQL.' + ) + ->end() + ->booleanNode('pooled')->info('True to use a pooled server with the oci8/pdo_oracle driver')->end() + ->booleanNode('MultipleActiveResultSets')->info('Configuring MultipleActiveResultSets for the pdo_sqlsrv driver')->end() + ->booleanNode('use_savepoints')->info('Use savepoints for nested transactions')->end() + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) {return !isset($v['sessionMode']) && isset($v['session_mode']);}) + ->then(function ($v) { + $v['sessionMode'] = $v['session_mode']; + unset($v['session_mode']); + + return $v; + }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) {return !isset($v['MultipleActiveResultSets']) && isset($v['multiple_active_result_sets']);}) + ->then(function ($v) { + $v['MultipleActiveResultSets'] = $v['multiple_active_result_sets']; + unset($v['multiple_active_result_sets']); + + return $v; + }) + ->end() + ; + } + + /** + * Add the ORM section to configuration tree + * + * @param ArrayNodeDefinition $node + */ + private function addOrmSection(ArrayNodeDefinition $node) + { + $generationModes = $this->getAutoGenerateModes(); + + $node + ->children() + ->arrayNode('orm') + ->beforeNormalization() + ->ifTrue(function ($v) { return null === $v || (is_array($v) && !array_key_exists('entity_managers', $v) && !array_key_exists('entity_manager', $v)); }) + ->then(function ($v) { + $v = (array) $v; + // Key that should not be rewritten to the connection config + $excludedKeys = array( + 'default_entity_manager' => true, 'auto_generate_proxy_classes' => true, + 'proxy_dir' => true, 'proxy_namespace' => true, 'resolve_target_entities' => true, + 'resolve_target_entity' => true, + ); + $entityManager = array(); + foreach ($v as $key => $value) { + if (isset($excludedKeys[$key])) { + continue; + } + $entityManager[$key] = $v[$key]; + unset($v[$key]); + } + $v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default'; + $v['entity_managers'] = array($v['default_entity_manager'] => $entityManager); + + return $v; + }) + ->end() + ->children() + ->scalarNode('default_entity_manager')->end() + ->scalarNode('auto_generate_proxy_classes')->defaultValue(false) + ->info('Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL"') + ->validate() + ->ifTrue(function ($v) use ($generationModes) { + if (is_int($v) && in_array($v, $generationModes['values']/*array(0, 1, 2, 3)*/)) { + return false; + } + if (is_bool($v)) { + return false; + } + if (is_string($v)) { + if (in_array(strtoupper($v), $generationModes['names']/*array('NEVER', 'ALWAYS', 'FILE_NOT_EXISTS', 'EVAL')*/)) { + return false; + } + } + + return true; + }) + ->thenInvalid('Invalid auto generate mode value %s') + ->end() + ->validate() + ->ifString() + ->then(function ($v) { + return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_'.strtoupper($v)); + }) + ->end() + ->end() + ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end() + ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end() + ->end() + ->fixXmlConfig('entity_manager') + ->append($this->getOrmEntityManagersNode()) + ->fixXmlConfig('resolve_target_entity', 'resolve_target_entities') + ->append($this->getOrmTargetEntityResolverNode()) + ->end() + ->end() + ; + } + + /** + * Return ORM target entity resolver node + * + * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition + */ + private function getOrmTargetEntityResolverNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('resolve_target_entities'); + + $node + ->useAttributeAsKey('interface') + ->prototype('scalar') + ->cannotBeEmpty() + ->end() + ; + + return $node; + } + + /** + * Return ORM entity listener node + * + * @return \Symfony\Component\Config\Definition\Builder\NodeDefinition + */ + private function getOrmEntityListenersNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('entity_listeners'); + $normalizer = function ($mappings) { + $entities = array(); + + foreach ($mappings as $entityClass => $mapping) { + $listeners = array(); + + foreach ($mapping as $listenerClass => $listenerEvent) { + $events = array(); + + foreach ($listenerEvent as $eventType => $eventMapping) { + if ($eventMapping === null) { + $eventMapping = array(null); + } + + foreach ($eventMapping as $method) { + $events[] = array( + 'type' => $eventType, + 'method' => $method, + ); + } + } + + $listeners[] = array( + 'class' => $listenerClass, + 'event' => $events, + ); + } + + $entities[] = array( + 'class' => $entityClass, + 'listener' => $listeners, + ); + } + + return array('entities' => $entities); + }; + + $node + ->beforeNormalization() + // Yaml normalization + ->ifTrue(function ($v) { return is_array(reset($v)) && is_string(key(reset($v))); }) + ->then($normalizer) + ->end() + ->fixXmlConfig('entity', 'entities') + ->children() + ->arrayNode('entities') + ->useAttributeAsKey('class') + ->prototype('array') + ->fixXmlConfig('listener') + ->children() + ->arrayNode('listeners') + ->useAttributeAsKey('class') + ->prototype('array') + ->fixXmlConfig('event') + ->children() + ->arrayNode('events') + ->prototype('array') + ->children() + ->scalarNode('type')->end() + ->scalarNode('method')->defaultNull()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $node; + } + + /** + * Return ORM entity manager node + * + * @return ArrayNodeDefinition + */ + private function getOrmEntityManagersNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('entity_managers'); + + $node + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ->addDefaultsIfNotSet() + ->append($this->getOrmCacheDriverNode('query_cache_driver')) + ->append($this->getOrmCacheDriverNode('metadata_cache_driver')) + ->append($this->getOrmCacheDriverNode('result_cache_driver')) + ->append($this->getOrmEntityListenersNode()) + ->children() + ->scalarNode('connection')->end() + ->scalarNode('class_metadata_factory_name')->defaultValue('Doctrine\ORM\Mapping\ClassMetadataFactory')->end() + ->scalarNode('default_repository_class')->defaultValue('Doctrine\ORM\EntityRepository')->end() + ->scalarNode('auto_mapping')->defaultFalse()->end() + ->scalarNode('naming_strategy')->defaultValue('doctrine.orm.naming_strategy.default')->end() + ->scalarNode('quote_strategy')->defaultValue('doctrine.orm.quote_strategy.default')->end() + ->scalarNode('entity_listener_resolver')->defaultNull()->end() + ->scalarNode('repository_factory')->defaultNull()->end() + ->end() + ->children() + ->arrayNode('second_level_cache') + ->children() + ->append($this->getOrmCacheDriverNode('region_cache_driver')) + ->scalarNode('region_lock_lifetime')->defaultValue(60)->end() + ->booleanNode('log_enabled')->defaultValue($this->debug)->end() + ->scalarNode('region_lifetime')->defaultValue(0)->end() + ->booleanNode('enabled')->defaultValue(true)->end() + ->scalarNode('factory')->end() + ->end() + ->fixXmlConfig('region') + ->children() + ->arrayNode('regions') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->append($this->getOrmCacheDriverNode('cache_driver')) + ->scalarNode('lock_path')->defaultValue('%kernel.cache_dir%/doctrine/orm/slc/filelock')->end() + ->scalarNode('lock_lifetime')->defaultValue(60)->end() + ->scalarNode('type')->defaultValue('default')->end() + ->scalarNode('lifetime')->defaultValue(0)->end() + ->scalarNode('service')->end() + ->scalarNode('name')->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('logger') + ->children() + ->arrayNode('loggers') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('name')->end() + ->scalarNode('service')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('hydrator') + ->children() + ->arrayNode('hydrators') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('mapping') + ->children() + ->arrayNode('mappings') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('type' => $v); }) + ->end() + ->treatNullLike(array()) + ->treatFalseLike(array('mapping' => false)) + ->performNoDeepMerging() + ->children() + ->scalarNode('mapping')->defaultValue(true)->end() + ->scalarNode('type')->end() + ->scalarNode('dir')->end() + ->scalarNode('alias')->end() + ->scalarNode('prefix')->end() + ->booleanNode('is_bundle')->end() + ->end() + ->end() + ->end() + ->arrayNode('dql') + ->fixXmlConfig('string_function') + ->fixXmlConfig('numeric_function') + ->fixXmlConfig('datetime_function') + ->children() + ->arrayNode('string_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('numeric_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('datetime_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('filter') + ->children() + ->arrayNode('filters') + ->info('Register SQL Filters in the entity manager') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('class' => $v); }) + ->end() + ->beforeNormalization() + // The content of the XML node is returned as the "value" key so we need to rename it + ->ifTrue(function ($v) { + return is_array($v) && isset($v['value']); + }) + ->then(function ($v) { + $v['class'] = $v['value']; + unset($v['value']); + + return $v; + }) + ->end() + ->fixXmlConfig('parameter') + ->children() + ->scalarNode('class')->isRequired()->end() + ->booleanNode('enabled')->defaultFalse()->end() + ->arrayNode('parameters') + ->useAttributeAsKey('name') + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $node; + } + + /** + * Return a ORM cache driver node for an given entity manager + * + * @param string $name + * + * @return ArrayNodeDefinition + */ + private function getOrmCacheDriverNode($name) + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root($name); + + $node + ->addDefaultsIfNotSet() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('type' => $v); }) + ->end() + ->children() + ->scalarNode('type')->defaultValue('array')->end() + ->scalarNode('host')->end() + ->scalarNode('port')->end() + ->scalarNode('instance_class')->end() + ->scalarNode('class')->end() + ->scalarNode('id')->end() + ->scalarNode('namespace')->defaultNull()->end() + ->scalarNode('cache_provider')->defaultNull()->end() + ->end() + ; + + return $node; + } + + /** + * Find proxy auto generate modes for their names and int values + * + * @return array + */ + private function getAutoGenerateModes() + { + $constPrefix = 'AUTOGENERATE_'; + $prefixLen = strlen($constPrefix); + $refClass = new \ReflectionClass('Doctrine\Common\Proxy\AbstractProxyFactory'); + $constsArray = $refClass->getConstants(); + $namesArray = array(); + $valuesArray = array(); + + foreach ($constsArray as $key => $value) { + if (strpos($key, $constPrefix) === 0) { + $namesArray[] = substr($key, $prefixLen); + $valuesArray[] = (int) $value; + } + } + + return array( + 'names' => $namesArray, + 'values' => $valuesArray, + ); + } +} diff --git a/vendor/doctrine/doctrine-bundle/DependencyInjection/DoctrineExtension.php b/vendor/doctrine/doctrine-bundle/DependencyInjection/DoctrineExtension.php new file mode 100644 index 0000000..a03b944 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/DependencyInjection/DoctrineExtension.php @@ -0,0 +1,794 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection; + +use Doctrine\ORM\Version; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension; +use Symfony\Component\Config\FileLocator; +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter; +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader; + +/** + * DoctrineExtension is an extension for the Doctrine DBAL and ORM library. + * + * @author Jonathan H. Wage + * @author Fabien Potencier + * @author Benjamin Eberlei + * @author Fabio B. Silva + * @author Kinn Coelho Julião + */ +class DoctrineExtension extends AbstractDoctrineExtension +{ + /** + * @var string + */ + private $defaultConnection; + + /** + * @var array + */ + private $entityManagers; + + /** + * @var SymfonyBridgeAdapter + */ + private $adapter; + + /** + * @param SymfonyBridgeAdapter $adapter + */ + public function __construct(SymfonyBridgeAdapter $adapter = null) + { + $this->adapter = $adapter ?: new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm'); + } + + /** + * {@inheritDoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $this->adapter->loadServicesConfiguration($container); + + if (!empty($config['dbal'])) { + $this->dbalLoad($config['dbal'], $container); + } + + if (!empty($config['orm'])) { + if (empty($config['dbal'])) { + throw new \LogicException('Configuring the ORM layer requires to configure the DBAL layer as well.'); + } + + $this->ormLoad($config['orm'], $container); + } + + $this->addClassesToCompile(array( + 'Doctrine\\Common\\Annotations\\DocLexer', + 'Doctrine\\Common\\Annotations\\FileCacheReader', + 'Doctrine\\Common\\Annotations\\PhpParser', + 'Doctrine\\Common\\Annotations\\Reader', + 'Doctrine\\Common\\Lexer', + 'Doctrine\\Common\\Persistence\\ConnectionRegistry', + 'Doctrine\\Common\\Persistence\\Proxy', + 'Doctrine\\Common\\Util\\ClassUtils', + 'Doctrine\\Bundle\\DoctrineBundle\\Registry', + )); + } + + /** + * Loads the DBAL configuration. + * + * Usage example: + * + * + * + * @param array $config An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function dbalLoad(array $config, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('dbal.xml'); + + if (empty($config['default_connection'])) { + $keys = array_keys($config['connections']); + $config['default_connection'] = reset($keys); + } + + $this->defaultConnection = $config['default_connection']; + + $container->setAlias('database_connection', sprintf('doctrine.dbal.%s_connection', $this->defaultConnection)); + $container->setAlias('doctrine.dbal.event_manager', new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $this->defaultConnection), false)); + + $container->setParameter('doctrine.dbal.connection_factory.types', $config['types']); + + $connections = array(); + + foreach (array_keys($config['connections']) as $name) { + $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name); + } + + $container->setParameter('doctrine.connections', $connections); + $container->setParameter('doctrine.default_connection', $this->defaultConnection); + + $def = $container->getDefinition('doctrine.dbal.connection'); + if (method_exists($def, 'setFactory')) { + // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6 + $def->setFactory(array(new Reference('doctrine.dbal.connection_factory'), 'createConnection')); + } else { + // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6 + $def->setFactoryService('doctrine.dbal.connection_factory'); + $def->setFactoryMethod('createConnection'); + } + + foreach ($config['connections'] as $name => $connection) { + $this->loadDbalConnection($name, $connection, $container); + } + } + + /** + * Loads a configured DBAL connection. + * + * @param string $name The name of the connection + * @param array $connection A dbal connection configuration. + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadDbalConnection($name, array $connection, ContainerBuilder $container) + { + // configuration + $configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new DefinitionDecorator('doctrine.dbal.connection.configuration')); + $logger = null; + if ($connection['logging']) { + $logger = new Reference('doctrine.dbal.logger'); + } + unset ($connection['logging']); + if ($connection['profiling']) { + $profilingLoggerId = 'doctrine.dbal.logger.profiling.'.$name; + $container->setDefinition($profilingLoggerId, new DefinitionDecorator('doctrine.dbal.logger.profiling')); + $profilingLogger = new Reference($profilingLoggerId); + $container->getDefinition('data_collector.doctrine')->addMethodCall('addLogger', array($name, $profilingLogger)); + + if (null !== $logger) { + $chainLogger = new DefinitionDecorator('doctrine.dbal.logger.chain'); + $chainLogger->addMethodCall('addLogger', array($profilingLogger)); + + $loggerId = 'doctrine.dbal.logger.chain.'.$name; + $container->setDefinition($loggerId, $chainLogger); + $logger = new Reference($loggerId); + } else { + $logger = $profilingLogger; + } + } + unset($connection['profiling']); + + if (isset($connection['auto_commit'])) { + $configuration->addMethodCall('setAutoCommit', array($connection['auto_commit'])); + } + + unset($connection['auto_commit']); + + if (isset($connection['schema_filter']) && $connection['schema_filter']) { + $configuration->addMethodCall('setFilterSchemaAssetsExpression', array($connection['schema_filter'])); + } + + unset($connection['schema_filter']); + + if ($logger) { + $configuration->addMethodCall('setSQLLogger', array($logger)); + } + + // event manager + $container->setDefinition(sprintf('doctrine.dbal.%s_connection.event_manager', $name), new DefinitionDecorator('doctrine.dbal.connection.event_manager')); + + // connection + // PDO ignores the charset property before 5.3.6 so the init listener has to be used instead. + if (isset($connection['charset']) && version_compare(PHP_VERSION, '5.3.6', '<')) { + if ((isset($connection['driver']) && stripos($connection['driver'], 'mysql') !== false) || + (isset($connection['driver_class']) && stripos($connection['driver_class'], 'mysql') !== false)) { + $mysqlSessionInit = new Definition('%doctrine.dbal.events.mysql_session_init.class%'); + $mysqlSessionInit->setArguments(array($connection['charset'])); + $mysqlSessionInit->setPublic(false); + $mysqlSessionInit->addTag('doctrine.event_subscriber', array('connection' => $name)); + + $container->setDefinition( + sprintf('doctrine.dbal.%s_connection.events.mysqlsessioninit', $name), + $mysqlSessionInit + ); + unset($connection['charset']); + } + } + + $options = $this->getConnectionOptions($connection); + + $def = $container + ->setDefinition(sprintf('doctrine.dbal.%s_connection', $name), new DefinitionDecorator('doctrine.dbal.connection')) + ->setArguments(array( + $options, + new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)), + new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)), + $connection['mapping_types'], + )) + ; + + if (!empty($connection['use_savepoints'])) { + $def->addMethodCall('setNestTransactionsWithSavepoints', array($connection['use_savepoints'])); + } + } + + protected function getConnectionOptions($connection) + { + $options = $connection; + + if (isset($options['platform_service'])) { + $options['platform'] = new Reference($options['platform_service']); + unset($options['platform_service']); + } + unset($options['mapping_types']); + + if (isset($options['shard_choser_service'])) { + $options['shard_choser'] = new Reference($options['shard_choser_service']); + unset($options['shard_choser_service']); + } + + foreach (array( + 'options' => 'driverOptions', + 'driver_class' => 'driverClass', + 'wrapper_class' => 'wrapperClass', + 'keep_slave' => 'keepSlave', + 'shard_choser' => 'shardChoser', + 'server_version' => 'serverVersion', + 'default_table_options' => 'defaultTableOptions', + ) as $old => $new) { + if (isset($options[$old])) { + $options[$new] = $options[$old]; + unset($options[$old]); + } + } + + if (!empty($options['slaves']) && !empty($options['shards'])) { + throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together'); + } + + if (!empty($options['slaves'])) { + $nonRewrittenKeys = array( + 'driver' => true, 'driverOptions' => true, 'driverClass' => true, + 'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true, + 'platform' => true, 'slaves' => true, 'master' => true, 'shards' => true, + 'serverVersion' => true, + // included by safety but should have been unset already + 'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true, + ); + foreach ($options as $key => $value) { + if (isset($nonRewrittenKeys[$key])) { + continue; + } + $options['master'][$key] = $value; + unset($options[$key]); + } + if (empty($options['wrapperClass'])) { + // Change the wrapper class only if the user does not already forced using a custom one. + $options['wrapperClass'] = 'Doctrine\\DBAL\\Connections\\MasterSlaveConnection'; + } + } else { + unset($options['slaves']); + } + + if (!empty($options['shards'])) { + $nonRewrittenKeys = array( + 'driver' => true, 'driverOptions' => true, 'driverClass' => true, + 'wrapperClass' => true, 'keepSlave' => true, 'shardChoser' => true, + 'platform' => true, 'slaves' => true, 'global' => true, 'shards' => true, + // included by safety but should have been unset already + 'logging' => true, 'profiling' => true, 'mapping_types' => true, 'platform_service' => true, + ); + foreach ($options as $key => $value) { + if (isset($nonRewrittenKeys[$key])) { + continue; + } + $options['global'][$key] = $value; + unset($options[$key]); + } + if (empty($options['wrapperClass'])) { + // Change the wrapper class only if the user does not already forced using a custom one. + $options['wrapperClass'] = 'Doctrine\\DBAL\\Sharding\\PoolingShardConnection'; + } + } else { + unset($options['shards']); + } + + return $options; + } + + /** + * Loads the Doctrine ORM configuration. + * + * Usage example: + * + * + * + * @param array $config An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function ormLoad(array $config, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('orm.xml'); + + $this->entityManagers = array(); + foreach (array_keys($config['entity_managers']) as $name) { + $this->entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name); + } + $container->setParameter('doctrine.entity_managers', $this->entityManagers); + + if (empty($config['default_entity_manager'])) { + $tmp = array_keys($this->entityManagers); + $config['default_entity_manager'] = reset($tmp); + } + $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']); + + $options = array('auto_generate_proxy_classes', 'proxy_dir', 'proxy_namespace'); + foreach ($options as $key) { + $container->setParameter('doctrine.orm.'.$key, $config[$key]); + } + + $container->setAlias('doctrine.orm.entity_manager', sprintf('doctrine.orm.%s_entity_manager', $config['default_entity_manager'])); + + // BC logic to handle DoctrineBridge < 2.6 + if (!method_exists($this, 'fixManagersAutoMappings')) { + foreach ($config['entity_managers'] as $entityManager) { + if ($entityManager['auto_mapping'] && count($config['entity_managers']) > 1) { + throw new \LogicException('You cannot enable "auto_mapping" when several entity managers are defined.'); + } + } + } else { + $config['entity_managers'] = $this->fixManagersAutoMappings($config['entity_managers'], $container->getParameter('kernel.bundles')); + } + + $def = $container->getDefinition('doctrine.orm.entity_manager.abstract'); + if (method_exists($def, 'setFactory')) { + // to be inlined in dbal.xml when dependency on Symfony DependencyInjection is bumped to 2.6 + $def->setFactory(array('%doctrine.orm.entity_manager.class%', 'create')); + } else { + // to be removed when dependency on Symfony DependencyInjection is bumped to 2.6 + $def->setFactoryClass('%doctrine.orm.entity_manager.class%'); + $def->setFactoryMethod('create'); + } + + foreach ($config['entity_managers'] as $name => $entityManager) { + $entityManager['name'] = $name; + $this->loadOrmEntityManager($entityManager, $container); + } + + if ($config['resolve_target_entities']) { + $def = $container->findDefinition('doctrine.orm.listeners.resolve_target_entity'); + foreach ($config['resolve_target_entities'] as $name => $implementation) { + $def->addMethodCall('addResolveTargetEntity', array( + $name, $implementation, array(), + )); + } + + // BC: ResolveTargetEntityListener implements the subscriber interface since + // v2.5.0-beta1 (Commit 437f812) + if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) { + $def->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata')); + } else { + $def->addTag('doctrine.event_subscriber'); + } + } + } + + /** + * Loads a configured ORM entity manager. + * + * @param array $entityManager A configured ORM entity manager. + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmEntityManager(array $entityManager, ContainerBuilder $container) + { + $ormConfigDef = $container->setDefinition(sprintf('doctrine.orm.%s_configuration', $entityManager['name']), new DefinitionDecorator('doctrine.orm.configuration')); + + $this->loadOrmEntityManagerMappingInformation($entityManager, $ormConfigDef, $container); + $this->loadOrmCacheDrivers($entityManager, $container); + + if (isset($entityManager['entity_listener_resolver']) && $entityManager['entity_listener_resolver']) { + $container->setAlias(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), $entityManager['entity_listener_resolver']); + } else { + $container->setDefinition(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name']), new Definition('%doctrine.orm.entity_listener_resolver.class%')); + } + + $methods = array( + 'setMetadataCacheImpl' => new Reference(sprintf('doctrine.orm.%s_metadata_cache', $entityManager['name'])), + 'setQueryCacheImpl' => new Reference(sprintf('doctrine.orm.%s_query_cache', $entityManager['name'])), + 'setResultCacheImpl' => new Reference(sprintf('doctrine.orm.%s_result_cache', $entityManager['name'])), + 'setMetadataDriverImpl' => new Reference('doctrine.orm.'.$entityManager['name'].'_metadata_driver'), + 'setProxyDir' => '%doctrine.orm.proxy_dir%', + 'setProxyNamespace' => '%doctrine.orm.proxy_namespace%', + 'setAutoGenerateProxyClasses' => '%doctrine.orm.auto_generate_proxy_classes%', + 'setClassMetadataFactoryName' => $entityManager['class_metadata_factory_name'], + 'setDefaultRepositoryClassName' => $entityManager['default_repository_class'], + ); + // check for version to keep BC + if (version_compare(Version::VERSION, "2.3.0-DEV") >= 0) { + $methods = array_merge($methods, array( + 'setNamingStrategy' => new Reference($entityManager['naming_strategy']), + 'setQuoteStrategy' => new Reference($entityManager['quote_strategy']), + )); + } + + if (version_compare(Version::VERSION, "2.4.0-DEV") >= 0) { + $methods = array_merge($methods, array( + 'setEntityListenerResolver' => new Reference(sprintf('doctrine.orm.%s_entity_listener_resolver', $entityManager['name'])), + )); + } + + if (version_compare(Version::VERSION, "2.5.0-DEV") >= 0) { + $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $entityManager['name']); + $listenerDef = $container->setDefinition($listenerId, new Definition('%doctrine.orm.listeners.attach_entity_listeners.class%')); + $listenerDef->addTag('doctrine.event_listener', array('event' => 'loadClassMetadata')); + } + + if (isset($entityManager['second_level_cache'])) { + $this->loadOrmSecondLevelCache($entityManager, $ormConfigDef, $container); + } + + if ($entityManager['repository_factory']) { + $methods['setRepositoryFactory'] = new Reference($entityManager['repository_factory']); + } + + foreach ($methods as $method => $arg) { + $ormConfigDef->addMethodCall($method, array($arg)); + } + + foreach ($entityManager['hydrators'] as $name => $class) { + $ormConfigDef->addMethodCall('addCustomHydrationMode', array($name, $class)); + } + + if (!empty($entityManager['dql'])) { + foreach ($entityManager['dql']['string_functions'] as $name => $function) { + $ormConfigDef->addMethodCall('addCustomStringFunction', array($name, $function)); + } + foreach ($entityManager['dql']['numeric_functions'] as $name => $function) { + $ormConfigDef->addMethodCall('addCustomNumericFunction', array($name, $function)); + } + foreach ($entityManager['dql']['datetime_functions'] as $name => $function) { + $ormConfigDef->addMethodCall('addCustomDatetimeFunction', array($name, $function)); + } + } + + $enabledFilters = array(); + $filtersParameters = array(); + foreach ($entityManager['filters'] as $name => $filter) { + $ormConfigDef->addMethodCall('addFilter', array($name, $filter['class'])); + if ($filter['enabled']) { + $enabledFilters[] = $name; + } + if ($filter['parameters']) { + $filtersParameters[$name] = $filter['parameters']; + } + } + + $managerConfiguratorName = sprintf('doctrine.orm.%s_manager_configurator', $entityManager['name']); + $container + ->setDefinition($managerConfiguratorName, new DefinitionDecorator('doctrine.orm.manager_configurator.abstract')) + ->replaceArgument(0, $enabledFilters) + ->replaceArgument(1, $filtersParameters) + ; + + if (!isset($entityManager['connection'])) { + $entityManager['connection'] = $this->defaultConnection; + } + + $container + ->setDefinition(sprintf('doctrine.orm.%s_entity_manager', $entityManager['name']), new DefinitionDecorator('doctrine.orm.entity_manager.abstract')) + ->setArguments(array( + new Reference(sprintf('doctrine.dbal.%s_connection', $entityManager['connection'])), + new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager['name'])), + )) + ->setConfigurator(array(new Reference($managerConfiguratorName), 'configure')) + ; + + $container->setAlias( + sprintf('doctrine.orm.%s_entity_manager.event_manager', $entityManager['name']), + new Alias(sprintf('doctrine.dbal.%s_connection.event_manager', $entityManager['connection']), false) + ); + + if (isset($entityManager['entity_listeners'])) { + if (!isset($listenerDef)) { + throw new InvalidArgumentException('Entity listeners configuration requires doctrine-orm 2.5.0 or newer'); + } + + $entities = $entityManager['entity_listeners']['entities']; + + foreach ($entities as $entityListenerClass => $entity) { + foreach ($entity['listeners'] as $listenerClass => $listener) { + foreach ($listener['events'] as $listenerEvent) { + $listenerEventName = $listenerEvent['type']; + $listenerMethod = $listenerEvent['method']; + + $listenerDef->addMethodCall('addEntityListener', array( + $entityListenerClass, $listenerClass, $listenerEventName, $listenerMethod, + )); + } + } + } + + } + } + + /** + * Loads an ORM entity managers bundle mapping information. + * + * There are two distinct configuration possibilities for mapping information: + * + * 1. Specify a bundle and optionally details where the entity and mapping information reside. + * 2. Specify an arbitrary mapping location. + * + * @example + * + * doctrine.orm: + * mappings: + * MyBundle1: ~ + * MyBundle2: yml + * MyBundle3: { type: annotation, dir: Entities/ } + * MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping } + * MyBundle5: + * type: yml + * dir: bundle-mappings/ + * alias: BundleAlias + * arbitrary_key: + * type: xml + * dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entities + * prefix: DoctrineExtensions\Entities\ + * alias: DExt + * + * In the case of bundles everything is really optional (which leads to autodetection for this bundle) but + * in the mappings key everything except alias is a required argument. + * + * @param array $entityManager A configured ORM entity manager + * @param Definition $ormConfigDef A Definition instance + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmEntityManagerMappingInformation(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container) + { + // reset state of drivers and alias map. They are only used by this methods and children. + $this->drivers = array(); + $this->aliasMap = array(); + + $this->loadMappingInformation($entityManager, $container); + $this->registerMappingDrivers($entityManager, $container); + + $ormConfigDef->addMethodCall('setEntityNamespaces', array($this->aliasMap)); + } + + /** + * Loads an ORM second level cache bundle mapping information. + * + * @example + * entity_managers: + * default: + * second_level_cache: + * region_cache_driver: apc + * log_enabled: true + * regions: + * my_service_region: + * type: service + * service : "my_service_region" + * + * my_query_region: + * lifetime: 300 + * cache_driver: array + * type: filelock + * + * my_entity_region: + * lifetime: 600 + * cache_driver: + * type: apc + * + * @param array $entityManager A configured ORM entity manager + * @param Definition $ormConfigDef A Definition instance + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmSecondLevelCache(array $entityManager, Definition $ormConfigDef, ContainerBuilder $container) + { + if (version_compare(Version::VERSION, '2.5.0-DEV') < 0) { + throw new \InvalidArgumentException('Second-level cache requires doctrine-orm 2.5.0 or newer'); + } + + $driverId = null; + $enabled = $entityManager['second_level_cache']['enabled']; + + if (isset($entityManager['second_level_cache']['region_cache_driver'])) { + $driverName = 'second_level_cache.region_cache_driver'; + $driverMap = $entityManager['second_level_cache']['region_cache_driver']; + $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container); + } + + $configId = sprintf('doctrine.orm.%s_second_level_cache.cache_configuration', $entityManager['name']); + $regionsId = sprintf('doctrine.orm.%s_second_level_cache.regions_configuration', $entityManager['name']); + $driverId = $driverId ?: sprintf('doctrine.orm.%s_second_level_cache.region_cache_driver', $entityManager['name']); + $configDef = $container->setDefinition($configId, new Definition('%doctrine.orm.second_level_cache.cache_configuration.class%')); + $regionsDef = $container->setDefinition($regionsId, new Definition('%doctrine.orm.second_level_cache.regions_configuration.class%')); + + $slcFactoryId = sprintf('doctrine.orm.%s_second_level_cache.default_cache_factory', $entityManager['name']); + $slcFactoryDef = $container + ->setDefinition($slcFactoryId, new Definition('%doctrine.orm.second_level_cache.default_cache_factory.class%')) + ->setArguments(array(new Reference($regionsId), new Reference($driverId))); + + if (isset($entityManager['second_level_cache']['regions'])) { + foreach ($entityManager['second_level_cache']['regions'] as $name => $region) { + $regionRef = null; + $regionType = $region['type']; + + if ($regionType === 'service') { + $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name); + $regionRef = new Reference($region['service']); + + $container->setAlias($regionId, new Alias($region['service'], false)); + } + + if ($regionType === 'default' || $regionType === 'filelock') { + $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s', $entityManager['name'], $name); + $driverName = sprintf('second_level_cache.region.%s_driver', $name); + $driverMap = $region['cache_driver']; + $driverId = $this->loadCacheDriver($driverName, $entityManager['name'], $driverMap, $container); + $regionRef = new Reference($regionId); + + $container + ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.default_region.class%')) + ->setArguments(array($name, new Reference($driverId), $region['lifetime'])); + } + + if ($regionType === 'filelock') { + $regionId = sprintf('doctrine.orm.%s_second_level_cache.region.%s_filelock', $entityManager['name'], $name); + + $container + ->setDefinition($regionId, new Definition('%doctrine.orm.second_level_cache.filelock_region.class%')) + ->setArguments(array($regionRef, $region['lock_path'], $region['lock_lifetime'])); + + $regionRef = new Reference($regionId); + $regionsDef->addMethodCall('getLockLifetime', array($name, $region['lock_lifetime'])); + } + + $regionsDef->addMethodCall('setLifetime', array($name, $region['lifetime'])); + $slcFactoryDef->addMethodCall('setRegion', array($regionRef)); + } + } + + if ($entityManager['second_level_cache']['log_enabled']) { + $loggerChainId = sprintf('doctrine.orm.%s_second_level_cache.logger_chain', $entityManager['name']); + $loggerStatsId = sprintf('doctrine.orm.%s_second_level_cache.logger_statistics', $entityManager['name']); + $loggerChaingDef = $container->setDefinition($loggerChainId, new Definition('%doctrine.orm.second_level_cache.logger_chain.class%')); + $loggerStatsDef = $container->setDefinition($loggerStatsId, new Definition('%doctrine.orm.second_level_cache.logger_statistics.class%')); + + $loggerChaingDef->addMethodCall('setLogger', array('statistics', $loggerStatsDef)); + $configDef->addMethodCall('setCacheLogger', array($loggerChaingDef)); + + foreach ($entityManager['second_level_cache']['loggers'] as $name => $logger) { + $loggerId = sprintf('doctrine.orm.%s_second_level_cache.logger.%s', $entityManager['name'], $name); + $loggerRef = new Reference($logger['service']); + + $container->setAlias($loggerId, new Alias($logger['service'], false)); + $loggerChaingDef->addMethodCall('setLogger', array($name, $loggerRef)); + } + } + + $configDef->addMethodCall('setCacheFactory', array($slcFactoryDef)); + $configDef->addMethodCall('setRegionsConfiguration', array($regionsDef)); + $ormConfigDef->addMethodCall('setSecondLevelCacheEnabled', array($enabled)); + $ormConfigDef->addMethodCall('setSecondLevelCacheConfiguration', array($configDef)); + } + + /** + * {@inheritDoc} + */ + protected function getObjectManagerElementName($name) + { + return 'doctrine.orm.'.$name; + } + + protected function getMappingObjectDefaultName() + { + return 'Entity'; + } + + /** + * {@inheritDoc} + */ + protected function getMappingResourceConfigDirectory() + { + return 'Resources/config/doctrine'; + } + + /** + * {@inheritDoc} + */ + protected function getMappingResourceExtension() + { + return 'orm'; + } + + /** + * {@inheritDoc} + */ + protected function loadCacheDriver($driverName, $entityManagerName, array $driverMap, ContainerBuilder $container) + { + if (!empty($driverMap['cache_provider'])) { + $aliasId = $this->getObjectManagerElementName(sprintf('%s_%s', $entityManagerName, $driverName)); + $serviceId = sprintf('doctrine_cache.providers.%s', $driverMap['cache_provider']); + + $container->setAlias($aliasId, new Alias($serviceId, false)); + + return $aliasId; + } + + return $this->adapter->loadCacheDriver($driverName, $entityManagerName, $driverMap, $container); + } + + /** + * Loads a configured entity managers cache drivers. + * + * @param array $entityManager A configured ORM entity manager. + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function loadOrmCacheDrivers(array $entityManager, ContainerBuilder $container) + { + $this->loadCacheDriver('metadata_cache', $entityManager['name'], $entityManager['metadata_cache_driver'], $container); + $this->loadCacheDriver('result_cache', $entityManager['name'], $entityManager['result_cache_driver'], $container); + $this->loadCacheDriver('query_cache', $entityManager['name'], $entityManager['query_cache_driver'], $container); + } + + /** + * @param array $objectManager + * @param ContainerBuilder $container + * @param string $cacheName + */ + public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName) + { + $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName.'_driver'], $container); + } + + /** + * {@inheritDoc} + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + /** + * {@inheritDoc} + */ + public function getNamespace() + { + return 'http://symfony.com/schema/dic/doctrine'; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($container->getParameter('kernel.debug')); + } +} diff --git a/vendor/doctrine/doctrine-bundle/DoctrineBundle.php b/vendor/doctrine/doctrine-bundle/DoctrineBundle.php new file mode 100644 index 0000000..4a455d1 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/DoctrineBundle.php @@ -0,0 +1,155 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand; +use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand; +use Doctrine\Bundle\DoctrineBundle\Command\Proxy\RunSqlDoctrineCommand; +use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass; +use Doctrine\ORM\Proxy\Autoloader; +use Symfony\Component\Console\Application; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\IntrospectableContainerInterface; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\DoctrineValidationPass; +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; +use Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider\EntityFactory; + +/** + * Bundle. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DoctrineBundle extends Bundle +{ + private $autoloader; + + /** + * {@inheritDoc} + */ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new RegisterEventListenersAndSubscribersPass('doctrine.connections', 'doctrine.dbal.%s_connection.event_manager', 'doctrine'), PassConfig::TYPE_BEFORE_OPTIMIZATION); + + if ($container->hasExtension('security')) { + $container->getExtension('security')->addUserProviderFactory(new EntityFactory('entity', 'doctrine.orm.security.user.provider')); + } + + $container->addCompilerPass(new DoctrineValidationPass('orm')); + $container->addCompilerPass(new EntityListenerPass()); + } + + /** + * {@inheritDoc} + */ + public function boot() + { + // Register an autoloader for proxies to avoid issues when unserializing them + // when the ORM is used. + if ($this->container->hasParameter('doctrine.orm.proxy_namespace')) { + $namespace = $this->container->getParameter('doctrine.orm.proxy_namespace'); + $dir = $this->container->getParameter('doctrine.orm.proxy_dir'); + $proxyGenerator = null; + + if ($this->container->getParameter('doctrine.orm.auto_generate_proxy_classes')) { + // See https://github.com/symfony/symfony/pull/3419 for usage of references + $container = &$this->container; + + $proxyGenerator = function ($proxyDir, $proxyNamespace, $class) use (&$container) { + $originalClassName = ClassUtils::getRealClass($class); + /** @var $registry Registry */ + $registry = $container->get('doctrine'); + + // Tries to auto-generate the proxy file + /** @var $em \Doctrine\ORM\EntityManager */ + foreach ($registry->getManagers() as $em) { + if (!$em->getConfiguration()->getAutoGenerateProxyClasses()) { + continue; + } + + $metadataFactory = $em->getMetadataFactory(); + + if ($metadataFactory->isTransient($originalClassName)) { + continue; + } + + $classMetadata = $metadataFactory->getMetadataFor($originalClassName); + + $em->getProxyFactory()->generateProxyClasses(array($classMetadata)); + + clearstatcache(true, Autoloader::resolveFile($proxyDir, $proxyNamespace, $class)); + + break; + } + }; + } + + $this->autoloader = Autoloader::register($dir, $namespace, $proxyGenerator); + } + } + + /** + * {@inheritDoc} + */ + public function shutdown() + { + if (null !== $this->autoloader) { + spl_autoload_unregister($this->autoloader); + $this->autoloader = null; + } + + // Clear all entity managers to clear references to entities for GC + if ($this->container->hasParameter('doctrine.entity_managers')) { + foreach ($this->container->getParameter('doctrine.entity_managers') as $id) { + if (!$this->container instanceof IntrospectableContainerInterface || $this->container->initialized($id)) { + $this->container->get($id)->clear(); + } + } + } + + // Close all connections to avoid reaching too many connections in the process when booting again later (tests) + if ($this->container->hasParameter('doctrine.connections')) { + foreach ($this->container->getParameter('doctrine.connections') as $id) { + if (!$this->container instanceof IntrospectableContainerInterface || $this->container->initialized($id)) { + $this->container->get($id)->close(); + } + } + } + } + + /** + * {@inheritDoc} + */ + public function registerCommands(Application $application) + { + // Use the default logic when the ORM is available. + // This avoids listing all ORM commands by hand. + if (class_exists('Doctrine\\ORM\\Version')) { + parent::registerCommands($application); + + return; + } + + // Register only the DBAL commands if the ORM is not available. + $application->add(new CreateDatabaseDoctrineCommand()); + $application->add(new DropDatabaseDoctrineCommand()); + $application->add(new RunSqlDoctrineCommand()); + } +} diff --git a/vendor/doctrine/doctrine-bundle/LICENSE b/vendor/doctrine/doctrine-bundle/LICENSE new file mode 100644 index 0000000..655a5ce --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2011 Fabien Potencier, Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/doctrine/doctrine-bundle/ManagerConfigurator.php b/vendor/doctrine/doctrine-bundle/ManagerConfigurator.php new file mode 100644 index 0000000..86fd1ea --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/ManagerConfigurator.php @@ -0,0 +1,81 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Query\Filter\SQLFilter; + +/** + * Configurator for an EntityManager + * + * @author Christophe Coevoet + */ +class ManagerConfigurator +{ + private $enabledFilters = array(); + private $filtersParameters = array(); + + public function __construct(array $enabledFilters, array $filtersParameters) + { + $this->enabledFilters = $enabledFilters; + $this->filtersParameters = $filtersParameters; + } + + /** + * Create a connection by name. + * + * @param EntityManager $entityManager + */ + public function configure(EntityManager $entityManager) + { + $this->enableFilters($entityManager); + } + + /** + * Enables filters for a given entity manager + * + * @param EntityManager $entityManager + */ + private function enableFilters(EntityManager $entityManager) + { + if (empty($this->enabledFilters)) { + return; + } + + $filterCollection = $entityManager->getFilters(); + foreach ($this->enabledFilters as $filter) { + $filterObject = $filterCollection->enable($filter); + if (null !== $filterObject) { + $this->setFilterParameters($filter, $filterObject); + } + } + } + + /** + * Sets default parameters for a given filter + * + * @param string $name Filter name + * @param SQLFilter $filter Filter object + */ + private function setFilterParameters($name, SQLFilter $filter) + { + if (!empty($this->filtersParameters[$name])) { + $parameters = $this->filtersParameters[$name]; + foreach ($parameters as $paramName => $paramValue) { + $filter->setParameter($paramName, $paramValue); + } + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/Mapping/ClassMetadataCollection.php b/vendor/doctrine/doctrine-bundle/Mapping/ClassMetadataCollection.php new file mode 100644 index 0000000..1f5d6c5 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Mapping/ClassMetadataCollection.php @@ -0,0 +1,75 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Mapping; + +/** + * @author Fabien Potencier + */ +class ClassMetadataCollection +{ + private $path; + private $namespace; + private $metadata; + + /** + * Constructor + * + * @param array $metadata + */ + public function __construct(array $metadata) + { + $this->metadata = $metadata; + } + + /** + * @return array + */ + public function getMetadata() + { + return $this->metadata; + } + + /** + * @param string $path + */ + public function setPath($path) + { + $this->path = $path; + } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * @param string $namespace + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } +} diff --git a/vendor/doctrine/doctrine-bundle/Mapping/DisconnectedMetadataFactory.php b/vendor/doctrine/doctrine-bundle/Mapping/DisconnectedMetadataFactory.php new file mode 100644 index 0000000..a4df6e4 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Mapping/DisconnectedMetadataFactory.php @@ -0,0 +1,216 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Mapping; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * This class provides methods to access Doctrine entity class metadata for a + * given bundle, namespace or entity class, for generation purposes + * + * @author Fabien Potencier + */ +class DisconnectedMetadataFactory +{ + private $registry; + + /** + * Constructor. + * + * @param ManagerRegistry $registry A ManagerRegistry instance + */ + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + /** + * Gets the metadata of all classes of a bundle. + * + * @param BundleInterface $bundle A BundleInterface instance + * + * @return ClassMetadataCollection A ClassMetadataCollection instance + * + * @throws \RuntimeException When bundle does not contain mapped entities + */ + public function getBundleMetadata(BundleInterface $bundle) + { + $namespace = $bundle->getNamespace(); + $metadata = $this->getMetadataForNamespace($namespace); + if (!$metadata->getMetadata()) { + throw new \RuntimeException(sprintf('Bundle "%s" does not contain any mapped entities.', $bundle->getName())); + } + + $path = $this->getBasePathForClass($bundle->getName(), $bundle->getNamespace(), $bundle->getPath()); + + $metadata->setPath($path); + $metadata->setNamespace($bundle->getNamespace()); + + return $metadata; + } + + /** + * Gets the metadata of a class. + * + * @param string $class A class name + * @param string $path The path where the class is stored (if known) + * + * @return ClassMetadataCollection A ClassMetadataCollection instance + * + * @throws MappingException When class is not valid entity or mapped superclass + */ + public function getClassMetadata($class, $path = null) + { + $metadata = $this->getMetadataForClass($class); + if (!$metadata->getMetadata()) { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($class); + } + + $this->findNamespaceAndPathForMetadata($metadata, $path); + + return $metadata; + } + + /** + * Gets the metadata of all classes of a namespace. + * + * @param string $namespace A namespace name + * @param string $path The path where the class is stored (if known) + * + * @return ClassMetadataCollection A ClassMetadataCollection instance + * + * @throws \RuntimeException When namespace not contain mapped entities + */ + public function getNamespaceMetadata($namespace, $path = null) + { + $metadata = $this->getMetadataForNamespace($namespace); + if (!$metadata->getMetadata()) { + throw new \RuntimeException(sprintf('Namespace "%s" does not contain any mapped entities.', $namespace)); + } + + $this->findNamespaceAndPathForMetadata($metadata, $path); + + return $metadata; + } + + /** + * Find and configure path and namespace for the metadata collection. + * + * @param ClassMetadataCollection $metadata + * @param string|null $path + * + * @throws \RuntimeException When unable to determine the path + */ + public function findNamespaceAndPathForMetadata(ClassMetadataCollection $metadata, $path = null) + { + $all = $metadata->getMetadata(); + if (class_exists($all[0]->name)) { + $r = new \ReflectionClass($all[0]->name); + $path = $this->getBasePathForClass($r->getName(), $r->getNamespaceName(), dirname($r->getFilename())); + $ns = $r->getNamespaceName(); + + } elseif ($path) { + // Get namespace by removing the last component of the FQCN + $nsParts = explode('\\', $all[0]->name); + array_pop($nsParts); + $ns = implode('\\', $nsParts); + + } else { + throw new \RuntimeException(sprintf('Unable to determine where to save the "%s" class (use the --path option).', $all[0]->name)); + } + + $metadata->setPath($path); + $metadata->setNamespace($ns); + } + + /** + * Get a base path for a class + * + * @param string $name class name + * @param string $namespace class namespace + * @param string $path class path + * + * @return string + * @throws \RuntimeException When base path not found + */ + private function getBasePathForClass($name, $namespace, $path) + { + $namespace = str_replace('\\', '/', $namespace); + $search = str_replace('\\', '/', $path); + $destination = str_replace('/'.$namespace, '', $search, $c); + + if ($c != 1) { + throw new \RuntimeException(sprintf('Can\'t find base path for "%s" (path: "%s", destination: "%s").', $name, $path, $destination)); + } + + return $destination; + } + + /** + * @param string $namespace + * + * @return ClassMetadataCollection + */ + private function getMetadataForNamespace($namespace) + { + $metadata = array(); + foreach ($this->getAllMetadata() as $m) { + if (strpos($m->name, $namespace) === 0) { + $metadata[] = $m; + } + } + + return new ClassMetadataCollection($metadata); + } + + /** + * @param string $entity + * + * @return ClassMetadataCollection + */ + private function getMetadataForClass($entity) + { + foreach ($this->registry->getManagers() as $em) { + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + + if (!$cmf->isTransient($entity)) { + return new ClassMetadataCollection(array($cmf->getMetadataFor($entity))); + } + } + + return new ClassMetadataCollection(array()); + } + + /** + * @return array + */ + private function getAllMetadata() + { + $metadata = array(); + foreach ($this->registry->getManagers() as $em) { + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + foreach ($cmf->getAllMetadata() as $m) { + $metadata[] = $m; + } + } + + return $metadata; + } +} diff --git a/vendor/doctrine/doctrine-bundle/README.md b/vendor/doctrine/doctrine-bundle/README.md new file mode 100644 index 0000000..a46276c --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/README.md @@ -0,0 +1,24 @@ +# Doctrine Bundle + +Doctrine DBAL & ORM Bundle for the Symfony Framework. + +Build Status: [![Build Status](https://secure.travis-ci.org/doctrine/DoctrineBundle.png?branch=master)](http://travis-ci.org/doctrine/DoctrineBundle) + +## What is Doctrine? + +The Doctrine Project is the home of a selected set of PHP libraries primarily focused on providing persistence +services and related functionality. Its prize projects are a Object Relational Mapper and the Database Abstraction +Layer it is built on top of. You can read more about the projects below or view a list of all projects. + +Object relational mapper (ORM) for PHP that sits on top of a powerful database abstraction layer (DBAL). +One of its key features is the option to write database queries in a proprietary object oriented SQL dialect +called Doctrine Query Language (DQL), inspired by Hibernates HQL. This provides developers with a powerful +alternative to SQL that maintains flexibility without requiring unnecessary code duplication. + +DBAL is a powerful database abstraction layer with many features for database schema introspection, +schema management and PDO abstraction. + +## Documentation + +The documentation is rendered on [the symfony.com website](http://symfony.com/doc/master/bundles/DoctrineBundle/index.html). +The source of the documentation is available in the Resources/docs folder. diff --git a/vendor/doctrine/doctrine-bundle/Registry.php b/vendor/doctrine/doctrine-bundle/Registry.php new file mode 100644 index 0000000..63ec59e --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Registry.php @@ -0,0 +1,186 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Bridge\Doctrine\RegistryInterface; +use Symfony\Bridge\Doctrine\ManagerRegistry; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\EntityManager; + +/** + * References all Doctrine connections and entity managers in a given Container. + * + * @author Fabien Potencier + */ +class Registry extends ManagerRegistry implements RegistryInterface +{ + /** + * Construct. + * + * @param ContainerInterface $container + * @param array $connections + * @param array $entityManagers + * @param string $defaultConnection + * @param string $defaultEntityManager + */ + public function __construct(ContainerInterface $container, array $connections, array $entityManagers, $defaultConnection, $defaultEntityManager) + { + $this->setContainer($container); + + parent::__construct('ORM', $connections, $entityManagers, $defaultConnection, $defaultEntityManager, 'Doctrine\ORM\Proxy\Proxy'); + } + + /** + * Gets the default entity manager name. + * + * @return string The default entity manager name + * + * @deprecated + */ + public function getDefaultEntityManagerName() + { + trigger_error('getDefaultEntityManagerName is deprecated since Symfony 2.1. Use getDefaultManagerName instead', E_USER_DEPRECATED); + + return $this->getDefaultManagerName(); + } + + /** + * Gets a named entity manager. + * + * @param string $name The entity manager name (null for the default one) + * + * @return EntityManager + * + * @deprecated + */ + public function getEntityManager($name = null) + { + trigger_error('getEntityManager is deprecated since Symfony 2.1. Use getManager instead', E_USER_DEPRECATED); + + return $this->getManager($name); + } + + /** + * Gets an array of all registered entity managers + * + * @return EntityManager[] an array of all EntityManager instances + * + * @deprecated + */ + public function getEntityManagers() + { + trigger_error('getEntityManagers is deprecated since Symfony 2.1. Use getManagers instead', E_USER_DEPRECATED); + + return $this->getManagers(); + } + + /** + * Resets a named entity manager. + * + * This method is useful when an entity manager has been closed + * because of a rollbacked transaction AND when you think that + * it makes sense to get a new one to replace the closed one. + * + * Be warned that you will get a brand new entity manager as + * the existing one is not useable anymore. This means that any + * other object with a dependency on this entity manager will + * hold an obsolete reference. You can inject the registry instead + * to avoid this problem. + * + * @param string $name The entity manager name (null for the default one) + * + * @return EntityManager + * + * @deprecated + */ + public function resetEntityManager($name = null) + { + trigger_error('resetEntityManager is deprecated since Symfony 2.1. Use resetManager instead', E_USER_DEPRECATED); + + $this->resetManager($name); + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered entity managers. + * + * @param string $alias The alias + * + * @return string The full namespace + * + * @deprecated + */ + public function getEntityNamespace($alias) + { + trigger_error('getEntityNamespace is deprecated since Symfony 2.1. Use getAliasNamespace instead', E_USER_DEPRECATED); + + return $this->getAliasNamespace($alias); + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered entity managers. + * + * @param string $alias The alias + * + * @return string The full namespace + * + * @see Configuration::getEntityNamespace + */ + public function getAliasNamespace($alias) + { + foreach (array_keys($this->getManagers()) as $name) { + try { + return $this->getManager($name)->getConfiguration()->getEntityNamespace($alias); + } catch (ORMException $e) { + } + } + + throw ORMException::unknownEntityNamespace($alias); + } + + /** + * Gets all connection names. + * + * @return array An array of connection names + * + * @deprecated + */ + public function getEntityManagerNames() + { + trigger_error('getEntityManagerNames is deprecated since Symfony 2.1. Use getManagerNames instead', E_USER_DEPRECATED); + + return $this->getManagerNames(); + } + + /** + * Gets the entity manager associated with a given class. + * + * @param string $class A Doctrine Entity class name + * + * @return EntityManager|null + * + * @deprecated + */ + public function getEntityManagerForClass($class) + { + trigger_error('getEntityManagerForClass is deprecated since Symfony 2.1. Use getManagerForClass instead', E_USER_DEPRECATED); + + return $this->getManagerForClass($class); + } +} diff --git a/vendor/doctrine/doctrine-bundle/Resources/config/dbal.xml b/vendor/doctrine/doctrine-bundle/Resources/config/dbal.xml new file mode 100644 index 0000000..66c6f3d --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Resources/config/dbal.xml @@ -0,0 +1,67 @@ + + + + + + Doctrine\DBAL\Logging\LoggerChain + Doctrine\DBAL\Logging\DebugStack + Symfony\Bridge\Doctrine\Logger\DbalLogger + Doctrine\DBAL\Configuration + Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector + Symfony\Bridge\Doctrine\ContainerAwareEventManager + Doctrine\Bundle\DoctrineBundle\ConnectionFactory + Doctrine\DBAL\Event\Listeners\MysqlSessionInit + Doctrine\DBAL\Event\Listeners\OracleSessionInit + Doctrine\Bundle\DoctrineBundle\Registry + + + + + + + + + + + + + + + + + + + + + + + + + + %doctrine.dbal.connection_factory.types% + + + + + + + + + + + + + %doctrine.connections% + %doctrine.entity_managers% + %doctrine.default_connection% + %doctrine.default_entity_manager% + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Resources/config/orm.xml b/vendor/doctrine/doctrine-bundle/Resources/config/orm.xml new file mode 100644 index 0000000..92adb8a --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Resources/config/orm.xml @@ -0,0 +1,133 @@ + + + + + + Doctrine\ORM\Configuration + Doctrine\ORM\EntityManager + Doctrine\Bundle\DoctrineBundle\ManagerConfigurator + + + Doctrine\Common\Cache\ArrayCache + Doctrine\Common\Cache\ApcCache + Doctrine\Common\Cache\MemcacheCache + localhost + 11211 + Memcache + Doctrine\Common\Cache\MemcachedCache + localhost + 11211 + Memcached + Doctrine\Common\Cache\RedisCache + localhost + 6379 + Redis + Doctrine\Common\Cache\XcacheCache + Doctrine\Common\Cache\WinCacheCache + Doctrine\Common\Cache\ZendDataCache + + + Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain + Doctrine\ORM\Mapping\Driver\AnnotationDriver + Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver + Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver + Doctrine\ORM\Mapping\Driver\PHPDriver + Doctrine\ORM\Mapping\Driver\StaticPHPDriver + + + Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer + + + Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser + + + Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator + Symfony\Bridge\Doctrine\Validator\DoctrineInitializer + + + Symfony\Bridge\Doctrine\Security\User\EntityUserProvider + + + Doctrine\ORM\Tools\ResolveTargetEntityListener + Doctrine\ORM\Tools\AttachEntityListenersListener + + + Doctrine\ORM\Mapping\DefaultNamingStrategy + Doctrine\ORM\Mapping\UnderscoreNamingStrategy + + + Doctrine\ORM\Mapping\DefaultQuoteStrategy + Doctrine\ORM\Mapping\AnsiQuoteStrategy + + + Doctrine\ORM\Mapping\DefaultEntityListenerResolver + + + Doctrine\ORM\Cache\DefaultCacheFactory + Doctrine\ORM\Cache\Region\DefaultRegion + Doctrine\ORM\Cache\Region\FileLockRegion + Doctrine\ORM\Cache\Logging\CacheLoggerChain + Doctrine\ORM\Cache\Logging\StatisticsCacheLogger + Doctrine\ORM\Cache\CacheConfiguration + Doctrine\ORM\Cache\RegionsConfiguration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Resources/config/schema/doctrine-1.0.xsd b/vendor/doctrine/doctrine-bundle/Resources/config/schema/doctrine-1.0.xsd new file mode 100644 index 0000000..6d4a053 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Resources/config/schema/doctrine-1.0.xsd @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-bundle/Resources/views/Collector/db.html.twig b/vendor/doctrine/doctrine-bundle/Resources/views/Collector/db.html.twig new file mode 100644 index 0000000..d0c4b33 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Resources/views/Collector/db.html.twig @@ -0,0 +1,392 @@ +{% extends app.request.isXmlHttpRequest ? '@WebProfiler/Profiler/ajax_layout.html.twig' : '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set profiler_markup_version = profiler_markup_version|default(1) %} + + {% set icon %} + {% if profiler_markup_version == 1 %} + + Database + {{ collector.querycount }} + {% if collector.querycount > 0 %} + in {{ '%0.2f'|format(collector.time * 1000) }} ms + {% endif %} + {% if collector.invalidEntityCount > 0 %} + {{ collector.invalidEntityCount }} + {% endif %} + + {% else %} + + {% if collector.querycount > 0 or collector.invalidEntityCount > 0 %} + {% set status = collector.invalidEntityCount > 0 ? 'red' : collector.querycount > 50 ? 'yellow' %} + + {{ include('@Doctrine/Collector/icon.svg') }} + + {% if collector.querycount == 0 and collector.invalidEntityCount > 0 %} + {{ collector.invalidEntityCount }} + errors + {% else %} + {{ collector.querycount }} + + in + {{ '%0.2f'|format(collector.time * 1000) }} + ms + + {% endif %} + {% endif %} + + {% endif %} + {% endset %} + + {% set text %} +
+ Database Queries + {{ collector.querycount }} +
+
+ Query time + {{ '%0.2f'|format(collector.time * 1000) }} ms +
+
+ Invalid entities + {{ collector.invalidEntityCount }} +
+ {% if collector.cacheEnabled %} +
+ Cache hits + {{ collector.cacheHitsCount }} +
+
+ Cache misses + {{ collector.cacheMissesCount }} +
+
+ Cache puts + {{ collector.cachePutsCount }} +
+ {% else %} +
+ Second Level Cache + disabled +
+ {% endif %} + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status|default('') }) }} +{% endblock %} + +{% block menu %} + {% set profiler_markup_version = profiler_markup_version|default(1) %} + + {% if profiler_markup_version == 1 %} + + + + Doctrine + + {{ collector.querycount }} + {{ '%0.0f'|format(collector.time * 1000) }} ms + + + + {% else %} + + + {{ include('@Doctrine/Collector/icon.svg') }} + Doctrine + {% if collector.invalidEntityCount %} + + {{ collector.invalidEntityCount }} + + {% endif %} + + + {% endif %} +{% endblock %} + +{% block panel %} + {% set profiler_markup_version = profiler_markup_version|default(1) %} + + {% if 'explain' == page %} + {{ render(controller('DoctrineBundle:Profiler:explain', { + token: token, + panel: 'db', + connectionName: app.request.query.get('connection'), + query: app.request.query.get('query') + })) }} + {% else %} + {{ block('queries') }} + {% endif %} +{% endblock %} + +{% block queries %} + {% if profiler_markup_version == 1 %} + + {% endif %} + +

Queries

+ + {% for connection, queries in collector.queries %} + {% if collector.connections|length > 1 %} +

{{ connection }} connection

+ {% endif %} + + {% if queries is empty %} +
+

No database queries were performed.

+
+ {% else %} + + + + + + + + + + {% for i, query in queries %} + + + + + + {% endfor %} + +
#TimeInfo
{{ loop.index }}{{ '%0.2f'|format(query.executionMS * 1000) }} ms + {{ query.sql|doctrine_pretty_query(highlight_only = true) }} + +
+ Parameters: {{ query.params|yaml_encode }} +
+ +
+ View formatted query + +    + + View runnable query + + {% if query.explainable %} +    + Explain query + {% endif %} +
+ + + + + + {% if query.explainable %} +
+ {% endif %} +
+ {% endif %} + {% endfor %} + +

Database Connections

+ + {% if not collector.connections %} +
+

There are no configured database connections.

+
+ {% else %} + {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.connections, labels: ['Name', 'Service'] }, with_context = false ) }} + {% endif %} + +

Entity Managers

+ + {% if not collector.managers %} +
+

There are no configured entity managers.

+
+ {% else %} + {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.managers, labels: ['Name', 'Service'] }, with_context = false ) }} + {% endif %} + +

Second Level Cache

+ + {% if not collector.cacheEnabled %} +
+

Second Level Cache is not enabled.

+
+ {% else %} + {% if not collector.cacheCounts %} +
+

Second level cache information is not available.

+
+ {% else %} + {% if profiler_markup_version == 1 %} + {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheCounts }, with_context = false) }} + {% else %} +
+
+ {{ collector.cacheCounts.hits }} + Hits +
+ +
+ {{ collector.cacheCounts.misses }} + Misses +
+ +
+ {{ collector.cacheCounts.puts }} + Puts +
+
+ {% endif %} + + {% if collector.cacheRegions.hits %} +

Number of cache hits

+ {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheRegions.hits }, with_context = false) }} + {% endif %} + + {% if collector.cacheRegions.misses %} +

Number of cache misses

+ {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheRegions.misses }, with_context = false) }} + {% endif %} + + {% if collector.cacheRegions.puts %} +

Number of cache puts

+ {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.cacheRegions.puts }, with_context = false) }} + {% endif %} + {% endif %} + {% endif %} + +

Entities Mapping

+ + {% for manager, classes in collector.entities %} + {% if collector.managers|length > 1 %} +

{{ manager }} entity manager

+ {% endif %} + + {% if classes is empty %} +
+

No loaded entities.

+
+ {% else %} + + + + + + + + + {% for class in classes %} + {% set contains_errors = collector.mappingErrors[manager] is defined and collector.mappingErrors[manager][class] is defined %} + + + + + {% endfor %} + +
ClassMapping errors
{{ class }} + {% if contains_errors %} +
    + {% for error in collector.mappingErrors[manager][class] %} +
  • {{ error }}
  • + {% endfor %} +
+ {% else %} + No errors. + {% endif %} +
+ {% endif %} + {% endfor %} + + +{% endblock %} diff --git a/vendor/doctrine/doctrine-bundle/Resources/views/Collector/explain.html.twig b/vendor/doctrine/doctrine-bundle/Resources/views/Collector/explain.html.twig new file mode 100644 index 0000000..3b2dd7f --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Resources/views/Collector/explain.html.twig @@ -0,0 +1,28 @@ +{% if data[0]|length > 1 %} + {# The platform returns a table for the explanation (e.g. MySQL), display all columns #} + + + + {% for label in data[0]|keys %} + + {% endfor %} + + + + {% for row in data %} + + {% for key, item in row %} + + {% endfor %} + + {% endfor %} + +
{{ label }}
{{ item|replace({',': ', '}) }}
+{% else %} + {# The Platform returns a single column for a textual explanation (e.g. PostgreSQL), display all lines #} +
+        {%- for row in data -%}
+            {{ row|first }}{{ "\n" }}
+        {%- endfor -%}
+    
+{% endif %} diff --git a/vendor/doctrine/doctrine-bundle/Resources/views/Collector/icon.svg b/vendor/doctrine/doctrine-bundle/Resources/views/Collector/icon.svg new file mode 100644 index 0000000..299fbac --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Resources/views/Collector/icon.svg @@ -0,0 +1,4 @@ + + + diff --git a/vendor/doctrine/doctrine-bundle/Twig/DoctrineExtension.php b/vendor/doctrine/doctrine-bundle/Twig/DoctrineExtension.php new file mode 100644 index 0000000..4add5be --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/Twig/DoctrineExtension.php @@ -0,0 +1,351 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineBundle\Twig; + +/** + * This class contains the needed functions in order to do the query highlighting + * + * @author Florin Patan + * @author Christophe Coevoet + */ +class DoctrineExtension extends \Twig_Extension +{ + /** + * Number of maximum characters that one single line can hold in the interface + * + * @var int + */ + private $maxCharWidth = 100; + + /** + * Define our functions + * + * @return array + */ + public function getFilters() + { + return array( + new \Twig_SimpleFilter('doctrine_minify_query', array($this, 'minifyQuery'), array('deprecated' => true)), + new \Twig_SimpleFilter('doctrine_pretty_query', array($this, 'formatQuery'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('doctrine_replace_query_parameters', array($this, 'replaceQueryParameters')), + ); + } + + /** + * Get the possible combinations of elements from the given array + * + * @param array $elements + * @param integer $combinationsLevel + * + * @return array + */ + private function getPossibleCombinations(array $elements, $combinationsLevel) + { + $baseCount = count($elements); + $result = array(); + + if (1 === $combinationsLevel) { + foreach ($elements as $element) { + $result[] = array($element); + } + + return $result; + } + + $nextLevelElements = $this->getPossibleCombinations($elements, $combinationsLevel - 1); + + foreach ($nextLevelElements as $nextLevelElement) { + $lastElement = $nextLevelElement[$combinationsLevel - 2]; + $found = false; + + foreach ($elements as $key => $element) { + if ($element == $lastElement) { + $found = true; + continue; + } + + if ($found == true && $key < $baseCount) { + $tmp = $nextLevelElement; + $newCombination = array_slice($tmp, 0); + $newCombination[] = $element; + $result[] = array_slice($newCombination, 0); + } + } + } + + return $result; + } + + /** + * Shrink the values of parameters from a combination + * + * @param array $parameters + * @param array $combination + * + * @return string + */ + private function shrinkParameters(array $parameters, array $combination) + { + array_shift($parameters); + $result = ''; + + $maxLength = $this->maxCharWidth; + $maxLength -= count($parameters) * 5; + $maxLength = $maxLength / count($parameters); + + foreach ($parameters as $key => $value) { + $isLarger = false; + + if (strlen($value) > $maxLength) { + $value = wordwrap($value, $maxLength, "\n", true); + $value = explode("\n", $value); + $value = $value[0]; + + $isLarger = true; + } + $value = self::escapeFunction($value); + + if (!is_numeric($value)) { + $value = substr($value, 1, -1); + } + + if ($isLarger) { + $value .= ' [...]'; + } + + $result .= ' '.$combination[$key].' '.$value; + } + + return trim($result); + } + + /** + * Attempt to compose the best scenario minified query so that a user could find it without expanding it + * + * @param string $query + * @param array $keywords + * @param integer $required + * + * @return string + */ + private function composeMiniQuery($query, array $keywords, $required) + { + // Extract the mandatory keywords and consider the rest as optional keywords + $mandatoryKeywords = array_splice($keywords, 0, $required); + + $combinations = array(); + $combinationsCount = count($keywords); + + // Compute all the possible combinations of keywords to match the query for + while ($combinationsCount > 0) { + $combinations = array_merge($combinations, $this->getPossibleCombinations($keywords, $combinationsCount)); + $combinationsCount--; + } + + // Try and match the best case query pattern + foreach ($combinations as $combination) { + $combination = array_merge($mandatoryKeywords, $combination); + + $regexp = implode('(.*) ', $combination).' (.*)'; + $regexp = '/^'.$regexp.'/is'; + + if (preg_match($regexp, $query, $matches)) { + $result = $this->shrinkParameters($matches, $combination); + + return $result; + } + } + + // Try and match the simplest query form that contains only the mandatory keywords + $regexp = implode(' (.*)', $mandatoryKeywords).' (.*)'; + $regexp = '/^'.$regexp.'/is'; + + if (preg_match($regexp, $query, $matches)) { + $result = $this->shrinkParameters($matches, $mandatoryKeywords); + + return $result; + } + + // Fallback in case we didn't managed to find any good match (can we actually have that happen?!) + $result = substr($query, 0, $this->maxCharWidth); + + return $result; + } + + /** + * Minify the query + * + * @param string $query + * + * @return string + */ + public function minifyQuery($query) + { + $result = ''; + $keywords = array(); + $required = 1; + + // Check if we can match the query against any of the major types + switch (true) { + case stripos($query, 'SELECT') !== false: + $keywords = array('SELECT', 'FROM', 'WHERE', 'HAVING', 'ORDER BY', 'LIMIT'); + $required = 2; + break; + + case stripos($query, 'DELETE') !== false: + $keywords = array('DELETE', 'FROM', 'WHERE', 'ORDER BY', 'LIMIT'); + $required = 2; + break; + + case stripos($query, 'UPDATE') !== false: + $keywords = array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'); + $required = 2; + break; + + case stripos($query, 'INSERT') !== false: + $keywords = array('INSERT', 'INTO', 'VALUE', 'VALUES'); + $required = 2; + break; + + // If there's no match so far just truncate it to the maximum allowed by the interface + default: + $result = substr($query, 0, $this->maxCharWidth); + } + + // If we had a match then we should minify it + if ($result == '') { + $result = $this->composeMiniQuery($query, $keywords, $required); + } + + return $result; + } + + /** + * Escape parameters of a SQL query + * DON'T USE THIS FUNCTION OUTSIDE ITS INTENDED SCOPE + * + * @internal + * + * @param mixed $parameter + * + * @return string + */ + public static function escapeFunction($parameter) + { + $result = $parameter; + + switch (true) { + case is_string($result): + $result = "'".addslashes($result)."'"; + break; + + case is_array($result): + foreach ($result as &$value) { + $value = static::escapeFunction($value); + } + + $result = implode(', ', $result); + break; + + case is_object($result): + $result = addslashes((string) $result); + break; + + case null === $result: + $result = 'NULL'; + break; + + case is_bool($result): + $result = $result ? '1' : '0'; + break; + } + + return $result; + } + + /** + * Return a query with the parameters replaced + * + * @param string $query + * @param array $parameters + * + * @return string + */ + public function replaceQueryParameters($query, array $parameters) + { + $i = 0; + + $result = preg_replace_callback( + '/\?|((?([^"]*+)<\/pre>/Us', '\1', $html); + } else { + $html = \SqlFormatter::format($sql); + $html = preg_replace('/
([^"]*+)<\/pre>/Us', '
\2
', $html); + } + + return $html; + } + + /** + * Get the name of the extension + * + * @return string + */ + public function getName() + { + return 'doctrine_extension'; + } +} diff --git a/vendor/doctrine/doctrine-bundle/composer.json b/vendor/doctrine/doctrine-bundle/composer.json new file mode 100644 index 0000000..54c1963 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/composer.json @@ -0,0 +1,55 @@ +{ + "name": "doctrine/doctrine-bundle", + "type": "symfony-bundle", + "description": "Symfony DoctrineBundle", + "keywords": ["DBAL", "ORM", "Database", "Persistence"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + } + ], + "require": { + "php": ">=5.3.2", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/console": "~2.3|~3.0", + "doctrine/dbal": "~2.3", + "jdorn/sql-formatter": "~1.1", + "symfony/doctrine-bridge": "~2.2|~3.0", + "doctrine/doctrine-cache-bundle": "~1.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "symfony/yaml": "~2.2|~3.0", + "symfony/validator": "~2.2|~3.0", + "twig/twig": "~1.10", + "satooshi/php-coveralls": "~0.6.1", + "phpunit/phpunit": "~4" + }, + "suggest": { + "symfony/web-profiler-bundle": "to use the data collector", + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle." + }, + "autoload": { + "psr-4": { "Doctrine\\Bundle\\DoctrineBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + } +} diff --git a/vendor/doctrine/doctrine-bundle/phpunit.xml.dist b/vendor/doctrine/doctrine-bundle/phpunit.xml.dist new file mode 100644 index 0000000..9638715 --- /dev/null +++ b/vendor/doctrine/doctrine-bundle/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./Tests + + + + + + . + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/.coveralls.yml b/vendor/doctrine/doctrine-cache-bundle/.coveralls.yml new file mode 100644 index 0000000..67bd5dd --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +src_dir: ./ +coverage_clover: build/logs/clover.xml diff --git a/vendor/doctrine/doctrine-cache-bundle/.travis.yml b/vendor/doctrine/doctrine-cache-bundle/.travis.yml new file mode 100644 index 0000000..260db2f --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/.travis.yml @@ -0,0 +1,35 @@ +language: php +sudo: false + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +services: + - riak + - mongodb + - memcached + - redis-server + +before_script: + - ./Tests/travis/install-deps.sh + - composer self-update + - if [ "$DEPS" = "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi; + - composer update --prefer-source + +script: + - ./vendor/bin/phpunit -v --coverage-clover ./build/logs/clover.xml + - ./vendor/bin/phpcs -np --extensions=php --ignore=vendor/*,Tests/* --standard=ruleset.xml . + +after_script: + - php ./vendor/bin/coveralls -v + +matrix: + allow_failures: + - php: hhvm + include: + - php: 5.6 + env: DEPS="dev" diff --git a/vendor/doctrine/doctrine-cache-bundle/Acl/Model/AclCache.php b/vendor/doctrine/doctrine-cache-bundle/Acl/Model/AclCache.php new file mode 100644 index 0000000..d681af0 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Acl/Model/AclCache.php @@ -0,0 +1,239 @@ + + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class AclCache implements AclCacheInterface +{ + /** + * @var \Doctrine\Common\Cache\CacheProvider + */ + private $cache; + + /** + * @var \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface + */ + private $permissionGrantingStrategy; + + /** + * Constructor + * + * @param \Doctrine\Common\Cache\CacheProvider $cache + * @param \Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface $permissionGrantingStrategy + */ + public function __construct(CacheProvider $cache, PermissionGrantingStrategyInterface $permissionGrantingStrategy) + { + $this->cache = $cache; + $this->permissionGrantingStrategy = $permissionGrantingStrategy; + } + + /** + * {@inheritdoc} + */ + public function evictFromCacheById($primaryKey) + { + if ( ! $this->cache->contains($primaryKey)) { + return; + } + + $key = $this->cache->fetch($primaryKey); + + $this->cache->delete($primaryKey); + $this->evictFromCacheByKey($key); + } + + /** + * {@inheritdoc} + */ + public function evictFromCacheByIdentity(ObjectIdentityInterface $oid) + { + $key = $this->createKeyFromIdentity($oid); + + $this->evictFromCacheByKey($key); + } + + /** + * {@inheritdoc} + */ + public function getFromCacheById($primaryKey) + { + if ( ! $this->cache->contains($primaryKey)) { + return null; + } + + $key = $this->cache->fetch($primaryKey); + $acl = $this->getFromCacheByKey($key); + + if ( ! $acl) { + $this->cache->delete($primaryKey); + + return null; + } + + return $acl; + } + + /** + * {@inheritdoc} + */ + public function getFromCacheByIdentity(ObjectIdentityInterface $oid) + { + $key = $this->createKeyFromIdentity($oid); + + return $this->getFromCacheByKey($key); + } + + /** + * {@inheritdoc} + */ + public function putInCache(AclInterface $acl) + { + if (null === $acl->getId()) { + throw new \InvalidArgumentException('Transient ACLs cannot be cached.'); + } + + $parentAcl = $acl->getParentAcl(); + + if (null !== $parentAcl) { + $this->putInCache($parentAcl); + } + + $key = $this->createKeyFromIdentity($acl->getObjectIdentity()); + + $this->cache->save($key, serialize($acl)); + $this->cache->save($acl->getId(), $key); + } + + /** + * {@inheritdoc} + */ + public function clearCache() + { + return $this->cache->deleteAll(); + } + + /** + * Unserialize a given ACL. + * + * @param string $serialized + * + * @return \Symfony\Component\Security\Acl\Model\AclInterface + */ + private function unserializeAcl($serialized) + { + $acl = unserialize($serialized); + $parentId = $acl->getParentAcl(); + + if (null !== $parentId) { + $parentAcl = $this->getFromCacheById($parentId); + + if (null === $parentAcl) { + return null; + } + + $acl->setParentAcl($parentAcl); + } + + $reflectionProperty = new \ReflectionProperty($acl, 'permissionGrantingStrategy'); + + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($acl, $this->permissionGrantingStrategy); + $reflectionProperty->setAccessible(false); + + $aceAclProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Entry', 'acl'); + + $aceAclProperty->setAccessible(true); + + foreach ($acl->getObjectAces() as $ace) { + $aceAclProperty->setValue($ace, $acl); + } + + foreach ($acl->getClassAces() as $ace) { + $aceAclProperty->setValue($ace, $acl); + } + + $aceClassFieldProperty = new \ReflectionProperty($acl, 'classFieldAces'); + + $aceClassFieldProperty->setAccessible(true); + + foreach ($aceClassFieldProperty->getValue($acl) as $aces) { + foreach ($aces as $ace) { + $aceAclProperty->setValue($ace, $acl); + } + } + + $aceClassFieldProperty->setAccessible(false); + + $aceObjectFieldProperty = new \ReflectionProperty($acl, 'objectFieldAces'); + + $aceObjectFieldProperty->setAccessible(true); + + foreach ($aceObjectFieldProperty->getValue($acl) as $aces) { + foreach ($aces as $ace) { + $aceAclProperty->setValue($ace, $acl); + } + } + + $aceObjectFieldProperty->setAccessible(false); + + $aceAclProperty->setAccessible(false); + + return $acl; + } + + /** + * Returns the key for the object identity + * + * @param \Symfony\Component\Security\Acl\Model\ObjectIdentityInterface $oid + * + * @return string + */ + private function createKeyFromIdentity(ObjectIdentityInterface $oid) + { + return $oid->getType() . '_' . $oid->getIdentifier(); + } + + /** + * Removes an ACL from the cache + * + * @param string $key + */ + private function evictFromCacheByKey($key) + { + if ( ! $this->cache->contains($key)) { + return; + } + + $this->cache->delete($key); + } + + /** + * Retrieves an ACL for the given key from the cache + * + * @param string $key + * + * @return null|\Symfony\Component\Security\Acl\Model\AclInterface + */ + private function getFromCacheByKey($key) + { + if ( ! $this->cache->contains($key)) { + return null; + } + + $serialized = $this->cache->fetch($key); + + return $this->unserializeAcl($serialized); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Command/CacheCommand.php b/vendor/doctrine/doctrine-cache-bundle/Command/CacheCommand.php new file mode 100644 index 0000000..3400e42 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Command/CacheCommand.php @@ -0,0 +1,65 @@ + + */ +abstract class CacheCommand extends Command implements ContainerAwareInterface +{ + /** + * @var ContainerInterface + */ + private $container; + + /** + * Get the requested cache provider service. + * + * @param string $cacheName + * + * @return \Doctrine\Common\Cache\Cache + * + * @throws \InvalidArgumentException + */ + protected function getCacheProvider($cacheName) + { + $container = $this->getContainer(); + + // Try to use user input as cache service alias. + $cacheProvider = $container->get($cacheName, ContainerInterface::NULL_ON_INVALID_REFERENCE); + + // If cache provider was not found try the service provider name. + if ( ! $cacheProvider instanceof Cache) { + $cacheProvider = $container->get('doctrine_cache.providers.' . $cacheName, ContainerInterface::NULL_ON_INVALID_REFERENCE); + } + // Cache provider was not found. + if ( ! $cacheProvider instanceof Cache) { + throw new \InvalidArgumentException('Cache provider not found.'); + } + + return $cacheProvider; + } + + /** + * @return \Symfony\Component\DependencyInjection\ContainerInterface + */ + protected function getContainer() + { + return $this->container; + } + + /** + * {@inheritdoc} + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Command/ContainsCommand.php b/vendor/doctrine/doctrine-cache-bundle/Command/ContainsCommand.php new file mode 100644 index 0000000..6faca65 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Command/ContainsCommand.php @@ -0,0 +1,40 @@ + + */ +class ContainsCommand extends CacheCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('doctrine:cache:contains') + ->setDescription('Check if a cache entry exists') + ->addArgument('cache-name', InputArgument::REQUIRED, 'Which cache provider to use?') + ->addArgument('cache-id', InputArgument::REQUIRED, 'Which cache ID to check?'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $cacheName = $input->getArgument('cache-name'); + $cacheProvider = $this->getCacheProvider($cacheName); + $cacheId = $input->getArgument('cache-id'); + + $message = $cacheProvider->contains($cacheId) ? 'TRUE' : 'FALSE'; + $output->writeln($message); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Command/DeleteCommand.php b/vendor/doctrine/doctrine-cache-bundle/Command/DeleteCommand.php new file mode 100644 index 0000000..ca1b7bd --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Command/DeleteCommand.php @@ -0,0 +1,64 @@ + + */ +class DeleteCommand extends CacheCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('doctrine:cache:delete') + ->setDescription('Delete a cache entry') + ->addArgument('cache-name', InputArgument::REQUIRED, 'Which cache provider to use?') + ->addArgument('cache-id', InputArgument::OPTIONAL, 'Which cache ID to delete?') + ->addOption('all', 'a', InputOption::VALUE_NONE, 'Delete all cache entries in provider'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $cacheName = $input->getArgument('cache-name'); + $cacheProvider = $this->getCacheProvider($cacheName); + $cacheId = $input->getArgument('cache-id'); + $all = $input->getOption('all'); + + if ($all && ! method_exists($cacheProvider, 'deleteAll')) { + throw new \RuntimeException('Cache provider does not implement a deleteAll method.'); + } + + if ( ! $all && ! $cacheId) { + throw new \InvalidArgumentException('Missing cache ID'); + } + + $success = $all ? $cacheProvider->deleteAll() : $cacheProvider->delete($cacheId); + $color = $success ? 'info' : 'error'; + $success = $success ? 'succeeded' : 'failed'; + $message = null; + + if ( ! $all) { + $message = "Deletion of <$color>%s in <$color>%s has <$color>%s"; + $message = sprintf($message, $cacheId, $cacheName, $success, true); + } + + if ($all) { + $message = "Deletion of <$color>all entries in <$color>%s has <$color>%s"; + $message = sprintf($message, $cacheName, $success, true); + } + + $output->writeln($message); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Command/FlushCommand.php b/vendor/doctrine/doctrine-cache-bundle/Command/FlushCommand.php new file mode 100644 index 0000000..3996152 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Command/FlushCommand.php @@ -0,0 +1,43 @@ + + */ +class FlushCommand extends CacheCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('doctrine:cache:flush') + ->setAliases(array('doctrine:cache:clear')) + ->setDescription('Flush a given cache') + ->addArgument('cache-name', InputArgument::REQUIRED, 'Which cache provider to flush?'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $cacheName = $input->getArgument('cache-name'); + $cacheProvider = $this->getCacheProvider($cacheName); + + if ( ! method_exists($cacheProvider, 'flushAll')) { + throw new \RuntimeException('Cache provider does not implement a flushAll method.'); + } + + $cacheProviderName = get_class($cacheProvider); + $output->writeln(sprintf('Clearing the cache for the %s provider of type %s', $cacheName, $cacheProviderName, true)); + $cacheProvider->flushAll(); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Command/StatsCommand.php b/vendor/doctrine/doctrine-cache-bundle/Command/StatsCommand.php new file mode 100644 index 0000000..233c69a --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Command/StatsCommand.php @@ -0,0 +1,53 @@ + + */ +class StatsCommand extends CacheCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('doctrine:cache:stats') + ->setDescription('Get stats on a given cache provider') + ->addArgument('cache-name', InputArgument::REQUIRED, 'Which cache provider to flush?'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $cacheName = $input->getArgument('cache-name'); + $cacheProvider = $this->getCacheProvider($cacheName); + $cacheProviderName = get_class($cacheProvider); + + $stats = $cacheProvider->getStats(); + + if ($stats === null) { + $output->writeln(sprintf('Stats were not provided for the %s provider of type %s', $cacheName, $cacheProviderName, true)); + + return; + } + + $formatter = $this->getHelperSet()->get('formatter'); + + $lines = array(); + foreach ($stats as $key => $stat) { + $lines[] = $formatter->formatSection($key, $stat); + } + + $output->writeln(sprintf('Stats for the %s provider of type %s:', $cacheName, $cacheProviderName, true)); + $output->writeln($lines); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/CacheProviderLoader.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/CacheProviderLoader.php new file mode 100644 index 0000000..bbf8fa9 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/CacheProviderLoader.php @@ -0,0 +1,153 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection; + +use Doctrine\Common\Inflector\Inflector; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Cache provider loader + * + * @author Fabio B. Silva + */ +class CacheProviderLoader +{ + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + public function loadCacheProvider($name, array $config, ContainerBuilder $container) + { + $serviceId = 'doctrine_cache.providers.' . $name; + $decorator = $this->getProviderDecorator($container, $config); + $service = $container->setDefinition($serviceId, $decorator); + $type = ($config['type'] === 'custom_provider') + ? $config['custom_provider']['type'] + : $config['type']; + + if ($config['namespace']) { + $service->addMethodCall('setNamespace', array($config['namespace'])); + } + + foreach ($config['aliases'] as $alias) { + $container->setAlias($alias, $serviceId); + } + + if ($this->definitionClassExists($type, $container)) { + $this->getCacheDefinition($type, $container)->configure($name, $config, $service, $container); + } + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param array $config + * + * @return \Symfony\Component\DependencyInjection\DefinitionDecorator + */ + protected function getProviderDecorator(ContainerBuilder $container, array $config) + { + $type = $config['type']; + $id = 'doctrine_cache.abstract.' . $type; + + if ($type === 'custom_provider') { + $type = $config['custom_provider']['type']; + $param = $this->getCustomProviderParameter($type); + + if ($container->hasParameter($param)) { + return new DefinitionDecorator($container->getParameter($param)); + } + } + + if ($container->hasDefinition($id)) { + return new DefinitionDecorator($id); + } + + throw new \InvalidArgumentException(sprintf('"%s" is an unrecognized Doctrine cache driver.', $type)); + } + + /** + * @param string $type + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition\CacheDefinition + */ + private function getCacheDefinition($type, ContainerBuilder $container) + { + $class = $this->getDefinitionClass($type, $container); + $object = new $class($type); + + return $object; + } + + /** + * @param string $type + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return boolean + */ + private function definitionClassExists($type, ContainerBuilder $container) + { + if ($container->hasParameter($this->getCustomDefinitionClassParameter($type))) { + return true; + } + + return class_exists($this->getDefinitionClass($type, $container)); + } + + /** + * @param string $type + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return string + */ + protected function getDefinitionClass($type, ContainerBuilder $container) + { + if ($container->hasParameter($this->getCustomDefinitionClassParameter($type))) { + return $container->getParameter($this->getCustomDefinitionClassParameter($type)); + } + + $name = Inflector::classify($type) . 'Definition'; + $class = sprintf('%s\Definition\%s', __NAMESPACE__, $name); + + return $class; + } + + /** + * @param string $type + * + * @return string + */ + public function getCustomProviderParameter($type) + { + return 'doctrine_cache.custom_provider.' . $type; + } + + /** + * @param string $type + * + * @return string + */ + public function getCustomDefinitionClassParameter($type) + { + return 'doctrine_cache.custom_definition_class.' . $type; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Configuration.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..f46f18c --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,528 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * Cache Bundle Configuration + * + * @author Guilherme Blanco + * @author Fabio B. Silva + */ +class Configuration implements ConfigurationInterface +{ + /** + * @param array $parameters + * + * @return string + */ + public function getProviderParameters(array $parameters) + { + if (isset($parameters['type'])) { + unset($parameters['type']); + } + + if (isset($parameters['aliases'])) { + unset($parameters['aliases']); + } + + if (isset($parameters['namespace'])) { + unset($parameters['namespace']); + } + + return $parameters; + } + + /** + * @param array $parameters + * + * @return string + */ + public function resolveNodeType(array $parameters) + { + $values = $this->getProviderParameters($parameters); + $type = key($values); + + return $type; + } + + /** + * @param \Symfony\Component\Config\Definition\NodeInterface $tree + * + * @return array + */ + public function getProviderNames(NodeInterface $tree) + { + foreach ($tree->getChildren() as $providers) { + + if ($providers->getName() !== 'providers') { + continue; + } + + $children = $providers->getPrototype()->getChildren(); + $providers = array_diff(array_keys($children), array('type', 'aliases', 'namespace')); + + return $providers; + } + + return array(); + } + + /** + * @param string $type + * @param \Symfony\Component\Config\Definition\NodeInterface $tree + * + * @return boolean + */ + public function isCustomProvider($type, NodeInterface $tree) + { + return ( ! in_array($type, $this->getProviderNames($tree))); + } + + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder() + { + $self = $this; + $builder = new TreeBuilder(); + $node = $builder->root('doctrine_cache', 'array'); + $normalization = function ($conf) use ($self, $builder) { + $conf['type'] = isset($conf['type']) + ? $conf['type'] + : $self->resolveNodeType($conf); + + if ($self->isCustomProvider($conf['type'], $builder->buildTree())) { + $params = $self->getProviderParameters($conf); + $options = reset($params); + $conf = array( + 'type' => 'custom_provider', + 'custom_provider' => array( + 'type' => $conf['type'], + 'options' => $options ?: null, + ) + ); + } + + return $conf; + }; + + $node + ->children() + ->arrayNode('acl_cache') + ->beforeNormalization() + ->ifString() + ->then(function ($id) { + return array('id' => $id); + }) + ->end() + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('id')->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('custom_provider') + ->children() + ->arrayNode('custom_providers') + ->useAttributeAsKey('type') + ->prototype('array') + ->children() + ->scalarNode('prototype')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('definition_class')->defaultNull()->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('alias', 'aliases') + ->children() + ->arrayNode('aliases') + ->useAttributeAsKey('key') + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('provider') + ->children() + ->arrayNode('providers') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) use ($self, $builder) { + return ( ! isset($v['type']) || ! $self->isCustomProvider($v['type'], $builder->buildTree())); + }) + ->then($normalization) + ->end() + ->children() + ->scalarNode('namespace')->defaultNull()->end() + ->scalarNode('type')->defaultNull()->end() + ->append($this->addBasicProviderNode('apc')) + ->append($this->addBasicProviderNode('array')) + ->append($this->addBasicProviderNode('void')) + ->append($this->addBasicProviderNode('wincache')) + ->append($this->addBasicProviderNode('xcache')) + ->append($this->addBasicProviderNode('zenddata')) + ->append($this->addCustomProviderNode()) + ->append($this->addCouchbaseNode()) + ->append($this->addChainNode()) + ->append($this->addMemcachedNode()) + ->append($this->addMemcacheNode()) + ->append($this->addFileSystemNode()) + ->append($this->addPhpFileNode()) + ->append($this->addMongoNode()) + ->append($this->addRedisNode()) + ->append($this->addRiakNode()) + ->append($this->addSqlite3Node()) + ->end() + ->fixXmlConfig('alias', 'aliases') + ->children() + ->arrayNode('aliases') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $builder; + } + + /** + * @param string $name + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addBasicProviderNode($name) + { + $builder = new TreeBuilder(); + $node = $builder->root($name); + + return $node; + } + + /** + * Build custom node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addCustomProviderNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('custom_provider'); + + $node + ->children() + ->scalarNode('type')->isRequired()->cannotBeEmpty()->end() + ->arrayNode('options') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->end() + ; + + return $node; + } + + /** + * Build chain node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addChainNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('chain'); + + $node + ->fixXmlConfig('provider') + ->children() + ->arrayNode('providers') + ->prototype('scalar')->end() + ->end() + ->end() + ; + + return $node; + } + + /** + * Build memcache node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addMemcacheNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('memcache'); + $host = '%doctrine_cache.memcache.host%'; + $port = '%doctrine_cache.memcache.port%'; + + $node + ->addDefaultsIfNotSet() + ->fixXmlConfig('server') + ->children() + ->scalarNode('connection_id')->defaultNull()->end() + ->arrayNode('servers') + ->useAttributeAsKey('host') + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) { + return is_scalar($v); + }) + ->then(function ($val) { + return array('port' => $val); + }) + ->end() + ->children() + ->scalarNode('host')->defaultValue($host)->end() + ->scalarNode('port')->defaultValue($port)->end() + ->end() + ->end() + ->defaultValue(array($host => array( + 'host' => $host, + 'port' => $port + ))) + ->end() + ->end() + ; + + return $node; + } + + /** + * Build memcached node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addMemcachedNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('memcached'); + $host = '%doctrine_cache.memcached.host%'; + $port = '%doctrine_cache.memcached.port%'; + + $node + ->addDefaultsIfNotSet() + ->fixXmlConfig('server') + ->children() + ->scalarNode('connection_id')->defaultNull()->end() + ->arrayNode('servers') + ->useAttributeAsKey('host') + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) { + return is_scalar($v); + }) + ->then(function ($val) { + return array('port' => $val); + }) + ->end() + ->children() + ->scalarNode('host')->defaultValue($host)->end() + ->scalarNode('port')->defaultValue($port)->end() + ->end() + ->end() + ->defaultValue(array($host => array( + 'host' => $host, + 'port' => $port + ))) + ->end() + ->end() + ; + + return $node; + } + + /** + * Build redis node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addRedisNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('redis'); + + $node + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('connection_id')->defaultNull()->end() + ->scalarNode('host')->defaultValue('%doctrine_cache.redis.host%')->end() + ->scalarNode('port')->defaultValue('%doctrine_cache.redis.port%')->end() + ->scalarNode('password')->defaultNull()->end() + ->scalarNode('timeout')->defaultNull()->end() + ->scalarNode('database')->defaultNull()->end() + ->end() + ; + + return $node; + } + + /** + * Build riak node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addRiakNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('riak'); + + $node + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('host')->defaultValue('%doctrine_cache.riak.host%')->end() + ->scalarNode('port')->defaultValue('%doctrine_cache.riak.port%')->end() + ->scalarNode('bucket_name')->defaultValue('doctrine_cache')->end() + ->scalarNode('connection_id')->defaultNull()->end() + ->scalarNode('bucket_id')->defaultNull()->end() + ->arrayNode('bucket_property_list') + ->children() + ->scalarNode('allow_multiple')->defaultNull()->end() + ->scalarNode('n_value')->defaultNull()->end() + ->end() + ->end() + ->end() + ; + + return $node; + } + + /** + * Build couchbase node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addCouchbaseNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('couchbase'); + + $node + ->addDefaultsIfNotSet() + ->fixXmlConfig('hostname') + ->children() + ->scalarNode('connection_id')->defaultNull()->end() + ->arrayNode('hostnames') + ->prototype('scalar')->end() + ->defaultValue(array('%doctrine_cache.couchbase.hostnames%')) + ->end() + ->scalarNode('username')->defaultNull()->end() + ->scalarNode('password')->defaultNull()->end() + ->scalarNode('bucket_name')->defaultValue('doctrine_cache')->end() + ->end() + ; + + return $node; + } + + /** + * Build mongodb node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addMongoNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('mongodb'); + + $node + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('connection_id')->defaultNull()->end() + ->scalarNode('collection_id')->defaultNull()->end() + ->scalarNode('database_name')->defaultValue('doctrine_cache')->end() + ->scalarNode('collection_name')->defaultValue('doctrine_cache')->end() + ->scalarNode('server')->defaultValue('%doctrine_cache.mongodb.server%')->end() + ->end() + ; + + return $node; + } + + /** + * Build php_file node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addPhpFileNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('php_file'); + + $node + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/doctrine/cache/phpfile')->end() + ->scalarNode('extension')->defaultNull()->end() + ->integerNode('umask')->defaultValue(0002)->end() + ->end() + ; + + return $node; + } + + /** + * Build file_system node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addFileSystemNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('file_system'); + + $node + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/doctrine/cache/file_system')->end() + ->scalarNode('extension')->defaultNull()->end() + ->integerNode('umask')->defaultValue(0002)->end() + ->end() + ; + + return $node; + } + + /** + * Build sqlite3 node configuration definition + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder + */ + private function addSqlite3Node() + { + $builder = new TreeBuilder(); + $node = $builder->root('sqlite3'); + + $node + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('connection_id')->defaultNull()->end() + ->scalarNode('file_name')->defaultNull()->end() + ->scalarNode('table_name')->defaultNull()->end() + ->end() + ; + + return $node; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/CacheDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/CacheDefinition.php new file mode 100644 index 0000000..56552f8 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/CacheDefinition.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Cache Definition. + * + * @author Fabio B. Silva + */ +abstract class CacheDefinition +{ + /** + * @var string + */ + private $type; + + /** + * @param string $type + */ + public function __construct($type) + { + $this->type = $type; + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\Definition $service + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + abstract public function configure($name, array $config, Definition $service, ContainerBuilder $container); +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/ChainDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/ChainDefinition.php new file mode 100644 index 0000000..3845ae7 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/ChainDefinition.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Chain definition. + * + * @author Guilherme Blanco + */ +class ChainDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $providersConf = $config['chain']; + $providers = $this->getProviders($name, $providersConf, $container); + + $service->setArguments(array($providers)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return array + */ + private function getProviders($name, array $config, ContainerBuilder $container) + { + $providers = array(); + + foreach ($config['providers'] as $provider) { + if (strpos($provider, 'doctrine_cache.providers.') === false) { + $provider = sprintf('doctrine_cache.providers.%s', $provider); + } + + $providers[] = new Reference($provider); + } + + return $providers; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/CouchbaseDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/CouchbaseDefinition.php new file mode 100644 index 0000000..1916e6e --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/CouchbaseDefinition.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Couchbase definition. + * + * @author Fabio B. Silva + */ +class CouchbaseDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $couchbaseConf = $config['couchbase']; + $connRef = $this->getConnectionReference($name, $couchbaseConf, $container); + + $service->addMethodCall('setCouchbase', array($connRef)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $host = $config['hostnames']; + $user = $config['username']; + $pass = $config['password']; + $bucket = $config['bucket_name']; + $connClass = '%doctrine_cache.couchbase.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s_couchbase.connection', $name); + $connDef = new Definition($connClass, array($host, $user, $pass, $bucket)); + + $connDef->setPublic(false); + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/FileSystemDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/FileSystemDefinition.php new file mode 100644 index 0000000..e7837b2 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/FileSystemDefinition.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +/** + * FileSystem definition. + * + * @author Fabio B. Silva + */ +class FileSystemDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $service->setArguments(array( + $config['file_system']['directory'], + $config['file_system']['extension'], + $config['file_system']['umask'] + )); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MemcacheDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MemcacheDefinition.php new file mode 100644 index 0000000..12a537d --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MemcacheDefinition.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Memcache definition. + * + * @author Fabio B. Silva + */ +class MemcacheDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $memcacheConf = $config['memcache']; + $connRef = $this->getConnectionReference($name, $memcacheConf, $container); + + $service->addMethodCall('setMemcache', array($connRef)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $connClass = '%doctrine_cache.memcache.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s.connection', $name); + $connDef = new Definition($connClass); + + foreach ($config['servers'] as $host => $server) { + $connDef->addMethodCall('addServer', array($host, $server['port'])); + } + + $connDef->setPublic(false); + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MemcachedDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MemcachedDefinition.php new file mode 100644 index 0000000..926df98 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MemcachedDefinition.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Memcached definition. + * + * @author Fabio B. Silva + */ +class MemcachedDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $memcachedConf = $config['memcached']; + $connRef = $this->getConnectionReference($name, $memcachedConf, $container); + + $service->addMethodCall('setMemcached', array($connRef)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $connClass = '%doctrine_cache.memcached.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s.connection', $name); + $connDef = new Definition($connClass); + + foreach ($config['servers'] as $host => $server) { + $connDef->addMethodCall('addServer', array($host, $server['port'])); + } + + $connDef->setPublic(false); + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MongodbDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MongodbDefinition.php new file mode 100644 index 0000000..792a5b5 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/MongodbDefinition.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * MongoDB definition. + * + * @author Fabio B. Silva + */ +class MongodbDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $memcacheConf = $config['mongodb']; + $collRef = $this->getCollectionReference($name, $memcacheConf, $container); + + $service->setArguments(array($collRef)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getCollectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['collection_id'])) { + return new Reference($config['collection_id']); + } + + $databaseName = $config['database_name']; + $collectionName = $config['collection_name']; + $collClass = '%doctrine_cache.mongodb.collection.class%'; + $collId = sprintf('doctrine_cache.services.%s.collection', $name); + $collDef = new Definition($collClass, array($databaseName, $collectionName)); + $connRef = $this->getConnectionReference($name, $config, $container); + + $definition = $container->setDefinition($collId, $collDef)->setPublic(false); + + if (method_exists($definition, 'setFactory')) { + $definition->setFactory(array($connRef, 'selectCollection')); + + return new Reference($collId); + } + + $definition + ->setFactoryService($connRef) + ->setFactoryMethod('selectCollection') + ; + + return new Reference($collId); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $server = $config['server']; + $connClass = '%doctrine_cache.mongodb.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s.connection', $name); + $connDef = new Definition($connClass, array($server)); + + $connDef->setPublic(false); + $connDef->addMethodCall('connect'); + + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/PhpFileDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/PhpFileDefinition.php new file mode 100644 index 0000000..f5da3b6 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/PhpFileDefinition.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +/** + * PhpFile definition. + * + * @author Fabio B. Silva + */ +class PhpFileDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $service->setArguments(array( + $config['php_file']['directory'], + $config['php_file']['extension'], + $config['php_file']['umask'] + )); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/RedisDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/RedisDefinition.php new file mode 100644 index 0000000..0197279 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/RedisDefinition.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Redis definition. + * + * @author Fabio B. Silva + */ +class RedisDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $redisConf = $config['redis']; + $connRef = $this->getConnectionReference($name, $redisConf, $container); + + $service->addMethodCall('setRedis', array($connRef)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $host = $config['host']; + $port = $config['port']; + $connClass = '%doctrine_cache.redis.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s_redis.connection', $name); + $connDef = new Definition($connClass); + $connParams = array($host, $port); + + if (isset($config['timeout'])) { + $connParams[] = $config['timeout']; + } + + $connDef->setPublic(false); + $connDef->addMethodCall('connect', $connParams); + + if (isset($config['password'])) { + $password = $config['password']; + $connDef->addMethodCall('auth', array($password)); + } + + if (isset($config['database'])) { + $database = (int) $config['database']; + $connDef->addMethodCall('select', array($database)); + } + + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/RiakDefinition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/RiakDefinition.php new file mode 100644 index 0000000..463d001 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/RiakDefinition.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Riak definition. + * + * @author Fabio B. Silva + */ +class RiakDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $riakConf = $config['riak']; + $bucketRef = $this->getBucketReference($name, $riakConf, $container); + + $service->setArguments(array($bucketRef)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getBucketReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['bucket_id'])) { + return new Reference($config['bucket_id']); + } + + $bucketName = $config['bucket_name']; + $bucketClass = '%doctrine_cache.riak.bucket.class%'; + $bucketId = sprintf('doctrine_cache.services.%s.bucket', $name); + $connDef = $this->getConnectionReference($name, $config, $container); + $bucketDef = new Definition($bucketClass, array($connDef, $bucketName)); + + $bucketDef->setPublic(false); + $container->setDefinition($bucketId, $bucketDef); + + if ( ! empty($config['bucket_property_list'])) { + $this->configureBucketPropertyList($name, $config['bucket_property_list'], $bucketDef, $container); + } + + return new Reference($bucketId); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $host = $config['host']; + $port = $config['port']; + $connClass = '%doctrine_cache.riak.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s.connection', $name); + $connDef = new Definition($connClass, array($host, $port)); + + $connDef->setPublic(false); + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\Definition $bucketDefinition + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + private function configureBucketPropertyList($name, array $config, Definition $bucketDefinition, ContainerBuilder $container) + { + $propertyListClass = '%doctrine_cache.riak.bucket_property_list.class%'; + $propertyListServiceId = sprintf('doctrine_cache.services.%s.bucket_property_list', $name); + $propertyListReference = new Reference($propertyListServiceId); + $propertyListDefinition = new Definition($propertyListClass, array( + $config['n_value'], + $config['allow_multiple'] + )); + + $container->setDefinition($propertyListServiceId, $propertyListDefinition); + $bucketDefinition->addMethodCall('setPropertyList', array($propertyListReference)); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/Sqlite3Definition.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/Sqlite3Definition.php new file mode 100644 index 0000000..a0dcc36 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/Definition/Sqlite3Definition.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Sqlite3 definition. + * + * @author Guilherme Blanco + */ +class Sqlite3Definition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + $sqlite3Conf = $config['sqlite3']; + $tableName = $sqlite3Conf['table_name']; + $connectionRef = $this->getConnectionReference($name, $sqlite3Conf, $container); + + $service->setArguments(array($connectionRef, $tableName)); + } + + /** + * @param string $name + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\Reference + */ + private function getConnectionReference($name, array $config, ContainerBuilder $container) + { + if (isset($config['connection_id'])) { + return new Reference($config['connection_id']); + } + + $fileName = $config['file_name']; + $connClass = '%doctrine_cache.sqlite3.connection.class%'; + $connId = sprintf('doctrine_cache.services.%s.connection', $name); + $connDef = new Definition($connClass, array($fileName, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE)); + + $connDef->setPublic(false); + $container->setDefinition($connId, $connDef); + + return new Reference($connId); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/DoctrineCacheExtension.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/DoctrineCacheExtension.php new file mode 100644 index 0000000..2469333 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/DoctrineCacheExtension.php @@ -0,0 +1,161 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +/** + * Cache Bundle Extension + * + * @author Guilherme Blanco + * @author Fabio B. Silva + * @author Danilo Cabello + */ +class DoctrineCacheExtension extends Extension +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader + */ + private $loader; + + /** + * @param \Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader $loader + */ + public function __construct(CacheProviderLoader $loader = null) + { + $this->loader = $loader ?: new CacheProviderLoader; + } + + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $rootConfig = $this->processConfiguration($configuration, $configs); + + $locator = new FileLocator(__DIR__ . '/../Resources/config/'); + $loader = new XmlFileLoader($container, $locator); + + $loader->load('services.xml'); + + $this->loadAcl($rootConfig, $container); + $this->loadCustomProviders($rootConfig, $container); + $this->loadCacheProviders($rootConfig, $container); + $this->loadCacheAliases($rootConfig, $container); + } + + /** + * @param array $rootConfig + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + protected function loadAcl(array $rootConfig, ContainerBuilder $container) + { + if ( ! isset($rootConfig['acl_cache']['id'])) { + return; + } + + if ( ! interface_exists('Symfony\Component\Security\Acl\Model\AclInterface')) { + throw new \LogicException('You must install symfony/security-acl in order to use the acl_cache functionality.'); + } + + $aclCacheDefinition = new Definition( + $container->getParameter('doctrine_cache.security.acl.cache.class'), + array( + new Reference($rootConfig['acl_cache']['id']), + new Reference('security.acl.permission_granting_strategy'), + ) + ); + + $aclCacheDefinition->setPublic(false); + + $container->setDefinition('doctrine_cache.security.acl.cache', $aclCacheDefinition); + $container->setAlias('security.acl.cache', 'doctrine_cache.security.acl.cache'); + } + + /** + * @param array $rootConfig + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + protected function loadCacheProviders(array $rootConfig, ContainerBuilder $container) + { + foreach ($rootConfig['providers'] as $name => $config) { + $this->loader->loadCacheProvider($name, $config, $container); + } + } + + /** + * @param array $rootConfig + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + protected function loadCacheAliases(array $rootConfig, ContainerBuilder $container) + { + foreach ($rootConfig['aliases'] as $alias => $name) { + $container->setAlias($alias, 'doctrine_cache.providers.' . $name); + } + } + + /** + * @param array $rootConfig + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + protected function loadCustomProviders(array $rootConfig, ContainerBuilder $container) + { + foreach ($rootConfig['custom_providers'] as $type => $rootConfig) { + $providerParameterName = $this->loader->getCustomProviderParameter($type); + $definitionParameterName = $this->loader->getCustomDefinitionClassParameter($type); + + $container->setParameter($providerParameterName, $rootConfig['prototype']); + + if ($rootConfig['definition_class']) { + $container->setParameter($definitionParameterName, $rootConfig['definition_class']); + } + } + } + + /** + * {@inheritDoc} + */ + public function getAlias() + { + return 'doctrine_cache'; + } + + /** + * {@inheritDoc} + */ + public function getXsdValidationBasePath() + { + return __DIR__ . '/../Resources/config/schema'; + } + + /** + * {@inheritDoc} + **/ + public function getNamespace() + { + return 'http://doctrine-project.org/schemas/symfony-dic/cache'; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/SymfonyBridgeAdapter.php b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/SymfonyBridgeAdapter.php new file mode 100644 index 0000000..5950cfe --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DependencyInjection/SymfonyBridgeAdapter.php @@ -0,0 +1,155 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\Config\FileLocator; + +/** + * Symfony bridge adpter + * + * @author Kinn Coelho Julião + * @author Fabio B. Silva + */ +class SymfonyBridgeAdapter +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader + */ + private $cacheProviderLoader; + + /** + * @var string + */ + protected $objectManagerName; + + /** + * @var string + */ + protected $mappingResourceName; + + /** + * @param \Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader $cacheProviderLoader + * @param string $objectManagerName + * @param string $mappingResourceName + */ + public function __construct(CacheProviderLoader $cacheProviderLoader, $objectManagerName, $mappingResourceName) + { + $this->cacheProviderLoader = $cacheProviderLoader; + $this->objectManagerName = $objectManagerName; + $this->mappingResourceName = $mappingResourceName; + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + public function loadServicesConfiguration(ContainerBuilder $container) + { + $locator = new FileLocator(__DIR__ . '/../Resources/config/'); + $loader = new XmlFileLoader($container, $locator); + + $loader->load('services.xml'); + } + + /** + * @param string $cacheName + * @param string $objectManagerName + * @param array $cacheDriver + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return string + */ + public function loadCacheDriver($cacheName, $objectManagerName, array $cacheDriver, ContainerBuilder $container) + { + $id = $this->getObjectManagerElementName($objectManagerName . '_' . $cacheName); + $host = isset($cacheDriver['host']) ? $cacheDriver['host'] : null; + $port = isset($cacheDriver['port']) ? $cacheDriver['port'] : null; + $password = isset($cacheDriver['password']) ? $cacheDriver['password'] : null; + $database = isset($cacheDriver['database']) ? $cacheDriver['database'] : null; + $type = $cacheDriver['type']; + + if ($type == 'service') { + $container->setAlias($id, new Alias($cacheDriver['id'], false)); + + return $id; + } + + $config = array( + 'aliases' => array($id), + $type => array(), + 'type' => $type, + 'namespace' => null, + ); + + if ( ! isset($cacheDriver['namespace'])) { + // generate a unique namespace for the given application + $environment = $container->getParameter('kernel.root_dir').$container->getParameter('kernel.environment'); + $hash = hash('sha256', $environment); + $namespace = 'sf2' . $this->mappingResourceName .'_' . $objectManagerName . '_' . $hash; + + $cacheDriver['namespace'] = $namespace; + } + + $config['namespace'] = $cacheDriver['namespace']; + + if (in_array($type, array('memcache', 'memcached'))) { + $host = !empty($host) ? $host : 'localhost'; + $config[$type]['servers'][$host] = array( + 'host' => $host, + 'port' => !empty($port) ? $port : 11211, + ); + } + + if ($type === 'redis') { + $config[$type] = array( + 'host' => !empty($host) ? $host : 'localhost', + 'port' => !empty($port) ? $port : 6379, + 'password' => !empty($password) ? $password : null, + 'database' => !empty($database) ? $database : 0 + ); + } + + $this->cacheProviderLoader->loadCacheProvider($id, $config, $container); + + return $id; + } + + /** + * @param array $objectManager + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param string $cacheName + */ + public function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName) + { + $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName.'_driver'], $container); + } + + /** + * @param string $name + * + * @return string + */ + protected function getObjectManagerElementName($name) + { + return $this->objectManagerName . '.' . $name; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/DoctrineCacheBundle.php b/vendor/doctrine/doctrine-cache-bundle/DoctrineCacheBundle.php new file mode 100644 index 0000000..98a04f6 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/DoctrineCacheBundle.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Symfony Bundle for Doctrine Cache + * + * @author Guilherme Blanco + * @author Fabio B. Silva + */ +class DoctrineCacheBundle extends Bundle +{ +} diff --git a/vendor/doctrine/doctrine-cache-bundle/LICENSE b/vendor/doctrine/doctrine-cache-bundle/LICENSE new file mode 100644 index 0000000..d228a4e --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/README.md b/vendor/doctrine/doctrine-cache-bundle/README.md new file mode 100644 index 0000000..cbf4e7c --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/README.md @@ -0,0 +1,426 @@ +DoctrineCacheBundle +=================== + +Symfony Bundle for Doctrine Cache + +Master: [![Build Status](https://secure.travis-ci.org/doctrine/DoctrineCacheBundle.png?branch=master)](http://travis-ci.org/doctrine/DoctrineCacheBundle) + +Master: [![Coverage Status](https://coveralls.io/repos/doctrine/DoctrineCacheBundle/badge.png?branch=master)](https://coveralls.io/r/doctrine/DoctrineCacheBundle?branch=master) + +## Installation + +Installing this bundle can be done through these simple steps: + +1. Add this bundle to your project as a composer dependency: + + ```bash + composer require doctrine/doctrine-cache-bundle + ``` + +2. Add this bundle in your application kernel: + + ```php + // app/AppKernel.php + public function registerBundles() + { + // ... + $bundles[] = new \Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(); + + return $bundles; + } + ``` + +3. Check if the bundle is configured correctly: + + ```xml + + + + + + + apc + metadata_cache_ns + + + + + + + ``` + + ```yml + # app/config/config.yml + + doctrine_cache: + providers: + my_apc_metadata_cache: + type: apc + namespace: metadata_cache_ns + my_apc_query_cache: + namespace: query_cache_ns + apc: ~ + ``` + +## Usage +Simply use `doctrine_cache.providers.{provider_name}` to inject it into the desired service. + +Check the following sample: + +```php +$apcCache = $this->container->get('doctrine_cache.providers.my_apc_cache'); +$arrayCache = $this->container->get('doctrine_cache.providers.my_array_cache'); + +``` + +## Provider configuration +```xml + + + + + + + + + + + memcached01.ss + 11211 + + + + + + + my_bucket + + false + 1 + + + + + +``` + +```yml +# app/config/doctrine_cache.yml + +doctrine_cache: + providers: + my_memcached_cache: + memcached: + servers: + memcached01.ss: 11211 + memcached02.ss: + port: 11211 + my_riak_cache: + riak: + host: localhost + port: 8087 + bucket_name: my_bucket + bucket_property_list: + allow_multiple: false + n_value: 1 +``` + +###### See [Cache providers](#cache-providers) for all supported cache provider and its specific configurations + + +## Service aliases +```xml + + + + + + + my_apc_cache + + + apc + my_apc_cache_ns + apc_cache + + + +``` + +```yml +# app/config/doctrine_cache.yml + +doctrine_cache: + aliases: + cache_apc: my_apc_cache + + providers: + my_apc_cache: + type: apc + namespace: my_apc_cache_ns + aliases: + - apc_cache +``` + +You can access the cache providers by using created aliases: + +```php +$apcCache = $this->container->get('apc_cache'); +$cacheApc = $this->container->get('cache_apc'); +``` + + +## Custom providers +Is possible to register a custom cache driver +```xml + + + + + + + + + + + + + + + my_custom_provider_service + MyCustomTypeDefinition + + + + + foo + bar + + + + +``` + +```yml +# app/config/doctrine_cache.yml + +services: + my_custom_provider_service: + class: "MyCustomType" + # ... + +doctrine_cache: + custom_providers: + my_custom_type: + prototype: "my_custom_provider_service" + definition_class: "MyCustomTypeDefinition" # optional configuration + + providers: + my_custom_type_provider: + my_custom_type: + config_foo: "foo" + config_bar: "bar" +``` +###### Definition class is a optional configuration that will parse option arguments given to your custom cache driver See [CacheDefinition](https://github.com/doctrine/DoctrineCacheBundle/blob/master/DependencyInjection/Definition/CacheDefinition.php) + + +## Service parameter +Is possible to configure a cache provider using a specific connection/bucket/collection +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + my_bucket_name + + + + + + + + +``` + +```yml +# app/config/doctrine_cache.yml + +services: + my_riak_connection_service: + class: "Riak\Connection" + # ... + + my_riak_bucket_service: + class: "Riak\Bucket" + # ... + + my_memcached_connection_service: + class: "Memcached" + # ... + +doctrine_cache: + providers: + service_bucket_riak_provider: + riak: + bucket_id : "my_riak_bucket_service" + + service_connection_riak_provider: + riak: + connection_id: "my_riak_connection_service" + bucket_name: "my_bucket_name" + + service_connection_memcached_provider: + memcached: + connection_id: "my_memcached_connection_service" + +``` + +###### See [Cache providers](#cache-providers) for all specific configurations + + + +## Symfony acl cache +```xml + + + + + + + + + + + +``` + +```yml +# app/config/doctrine_cache.yml + +doctrine_cache: + acl_cache: + id: 'doctrine_cache.providers.acl_apc_provider' + providers: + acl_apc_provider: + type: 'apc' + +``` + +Check the following sample: +```php +/** @var $aclCache Symfony\Component\Security\Acl\Model\AclCacheInterface */ +$aclCache = $this->container->get('security.acl.cache'); + +``` + + +## Cache providers + +#### apc +#### array +#### chain + - providers - List of service ids (`doctrine_cache.providers.` can be skipped) of doctrine cache providers. Put fastest providers first (e.g. array cache) +#### couchbase + - connection_id - Couchbase connection service id + - hostnames - couchbase hostname list + - bucket_name - couchbase bucket name + - username - couchbase username + - password - couchbase password +#### file_system + - extension - file extension + - directory - cache directory + - umask - umask to revoke permissions +#### mongodb + - connection_id - MongoClient service id + - collection_id - MongoCollection service id + - server - mongodb server uri + - database_name - mongodb database name + - collection_name - mongodb collection name +#### memcache + - connection_id - Memcache connection service id + - servers - Server list + - server + - host - memcache host + - port - memcache port +#### memcached + - connection_id - Memcache connection service id + - servers - Server list + - server + - host - memcached host + - port - memcached port +#### php_file + - extension - file extension + - directory - cache directory + - umask - umask to revoke permissions +#### redis + - connection_id - Redis connection service id + - host - redis host + - port - redis port + - password - redis password + - timeout - redis connection timeout + - database - redis database selection (integer) +#### riak + - connection_id - Riak\Connection service id + - bucket_id - Riak\Bucket service id + - host - riak host + - port - riak port + - bucket_name - riak bucket name + - bucket_property_list - riak bucket configuration (property list) + - allow_multiple: false - riak bucket allow multiple configuration + - n_value: 1 - riak bucket n-value configuration +#### sqlite3 + - connection_id - SQLite3 connection service id + - file_name - SQLite3 database file name + - table_name - Cache table name +#### void +#### xcache +#### wincache +#### zenddata + +Check the [doctrine-cache documentation Page](http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html) for a better understanding. diff --git a/vendor/doctrine/doctrine-cache-bundle/Resources/config/schema/doctrine_cache-1.0.xsd b/vendor/doctrine/doctrine-cache-bundle/Resources/config/schema/doctrine_cache-1.0.xsd new file mode 100644 index 0000000..d1b4747 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Resources/config/schema/doctrine_cache-1.0.xsd @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Resources/config/services.xml b/vendor/doctrine/doctrine-cache-bundle/Resources/config/services.xml new file mode 100644 index 0000000..6745a4a --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Resources/config/services.xml @@ -0,0 +1,66 @@ + + + + + + Doctrine\Common\Cache\ApcCache + Doctrine\Common\Cache\ArrayCache + Doctrine\Common\Cache\ChainCache + Doctrine\Common\Cache\CouchbaseCache + Couchbase + localhost:8091 + Doctrine\Common\Cache\FilesystemCache + Doctrine\Common\Cache\PhpFileCache + Doctrine\Common\Cache\MemcacheCache + Memcache + localhost + 11211 + Doctrine\Common\Cache\MemcachedCache + Memcached + localhost + 11211 + Doctrine\Common\Cache\MongoDBCache + MongoCollection + MongoClient + localhost:27017 + Doctrine\Common\Cache\RedisCache + Redis + localhost + 6379 + Doctrine\Common\Cache\RiakCache + Riak\Bucket + Riak\Connection + Riak\BucketPropertyList + localhost + 8087 + Doctrine\Common\Cache\SQLite3Cache + SQLite3 + Doctrine\Common\Cache\VoidCache + Doctrine\Common\Cache\WinCacheCache + Doctrine\Common\Cache\XcacheCache + Doctrine\Common\Cache\ZendDataCache + Doctrine\Bundle\DoctrineCacheBundle\Acl\Model\AclCache + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Resources/doc/index.rst b/vendor/doctrine/doctrine-cache-bundle/Resources/doc/index.rst new file mode 100644 index 0000000..846032d --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Resources/doc/index.rst @@ -0,0 +1,88 @@ +DoctrineCacheBundle +=================== + +Since the version 1.3 of DoctrineBundle the DoctrineCacheBundle is included by default. + +The DoctrineCacheBundle is used to allow different systems of cache in your application using the `Doctrine Cache`_ library. + +Setup and Configuration +----------------------- + +Doctrine cache for Symfony is maintained in the `DoctrineCacheBundle`_. +The bundle uses external `Doctrine Cache`_ library. + +Follow these steps to install the bundle and the library in the Symfony +Standard edition. Add the following to your ``composer.json`` file: + +.. code-block:: json + + { + "require": { + "doctrine/doctrine-cache-bundle": "1.0.*" + } + } + +Update the vendor libraries: + +.. code-block:: bash + + $ php composer.phar update doctrine/doctrine-cache-bundle + +If everything worked, the ``DoctrineCacheBundle`` can now be found +at ``vendor/doctrine/doctrine-cache-bundle``. + +.. note:: + + ``DoctrineCacheBundle`` installs + `Doctrine Cache`_ library. The library can be found + at ``vendor/doctrine/cache``. + +Finally, register the Bundle ``DoctrineCacheBundle`` in ``app/AppKernel.php``. + +.. code-block:: php + + // ... + public function registerBundles() + { + $bundles = array( + // ... + + new Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(), + ); + + // ... + } + +ORM caching configuration +------------------------- + +Some configurations of the ORM can use cache to prevent an excessive number of requests, read the `Caching Drivers`_. + +.. code-block:: yaml + + # app/config/config.yml + doctrine: + orm: + metadata_cache_driver: + # will load doctrine_cache.providers.metadata_cache_driver + cache_provider: metadata_cache_driver + query_cache_driver: + cache_provider: query_cache_driver + result_cache_driver: + # if you're using a version < 1.3 of the DoctrineBundle + # you can use the "service" type + type: service + id: doctrine_cache.providers.result_cache_driver + + doctrine_cache: + providers: + metadata_cache_driver: + type: apc + query_cache_driver: + type: apc + result_cache_driver: + type: apc + + +.. _`Caching Drivers`: http://symfony.com/doc/current/reference/configuration/doctrine.html#caching-drivers +.. _`Doctrine Cache`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Acl/Model/AclCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Acl/Model/AclCacheTest.php new file mode 100644 index 0000000..a6e26c0 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Acl/Model/AclCacheTest.php @@ -0,0 +1,194 @@ +cacheProvider = new ArrayCache(); + $this->permissionGrantingStrategy = new PermissionGrantingStrategy(); + $this->aclCache = new AclCache($this->cacheProvider, $this->permissionGrantingStrategy); + } + + public function tearDown() + { + $this->cacheProvider = null; + $this->permissionGrantingStrategy = null; + $this->aclCache = null; + } + + /** + * @dataProvider provideDataForEvictFromCacheById + */ + public function testEvictFromCacheById($expected, $primaryKey) + { + $this->cacheProvider->save('bar', 'foo_1'); + $this->cacheProvider->save('foo_1', 's:4:test;'); + + $this->aclCache->evictFromCacheById($primaryKey); + + $this->assertEquals($expected, $this->cacheProvider->contains('bar')); + $this->assertEquals($expected, $this->cacheProvider->contains('foo_1')); + } + + public function provideDataForEvictFromCacheById() + { + return array( + array(false, 'bar'), + array(true, 'test'), + ); + } + + /** + * @dataProvider provideDataForEvictFromCacheByIdentity + */ + public function testEvictFromCacheByIdentity($expected, $identity) + { + $this->cacheProvider->save('foo_1', 's:4:test;'); + + $this->aclCache->evictFromCacheByIdentity($identity); + + $this->assertEquals($expected, $this->cacheProvider->contains('foo_1')); + } + + public function provideDataForEvictFromCacheByIdentity() + { + return array( + array(false, new ObjectIdentity(1, 'foo')), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testPutInCacheWithoutId() + { + $acl = new Acl(null, new ObjectIdentity(1, 'foo'), $this->permissionGrantingStrategy, array(), false); + + $this->aclCache->putInCache($acl); + } + + public function testPutInCacheWithoutParent() + { + $acl = $this->getAcl(0); + + $this->aclCache->putInCache($acl); + + $this->assertTrue($this->cacheProvider->contains('foo1_class')); + $this->assertTrue($this->cacheProvider->contains('oid1')); + } + + public function testPutInCacheWithParent() + { + $acl = $this->getAcl(2); + + $this->aclCache->putInCache($acl); + + // current + $this->assertTrue($this->cacheProvider->contains('foo2_class')); + $this->assertTrue($this->cacheProvider->contains('oid2')); + + // parent + $this->assertTrue($this->cacheProvider->contains('foo3_class')); + $this->assertTrue($this->cacheProvider->contains('oid3')); + + // grand-parent + $this->assertTrue($this->cacheProvider->contains('foo4_class')); + $this->assertTrue($this->cacheProvider->contains('oid4')); + } + + public function testClearCache() + { + $acl = $this->getAcl(0); + + $this->aclCache->putInCache($acl); + $this->aclCache->clearCache(); + + $this->assertFalse($this->cacheProvider->contains('foo5_class')); + $this->assertFalse($this->cacheProvider->contains('oid5')); + } + + public function testGetFromCacheById() + { + $acl = $this->getAcl(1); + + $this->aclCache->putInCache($acl); + + $cachedAcl = $this->aclCache->getFromCacheById($acl->getId()); + + $this->assertEquals($acl->getId(), $cachedAcl->getId()); + $this->assertNotNull($cachedParentAcl = $cachedAcl->getParentAcl()); + $this->assertEquals($acl->getParentAcl()->getId(), $cachedParentAcl->getId()); + + $this->assertEquals($acl->getClassFieldAces('foo'), $cachedAcl->getClassFieldAces('foo')); + $this->assertEquals($acl->getObjectFieldAces('foo'), $cachedAcl->getObjectFieldAces('foo')); + } + + public function testGetFromCacheByIdentity() + { + $acl = $this->getAcl(1); + + $this->aclCache->putInCache($acl); + + $cachedAcl = $this->aclCache->getFromCacheByIdentity($acl->getObjectIdentity()); + + $this->assertEquals($acl->getId(), $cachedAcl->getId()); + $this->assertNotNull($cachedParentAcl = $cachedAcl->getParentAcl()); + $this->assertEquals($acl->getParentAcl()->getId(), $cachedParentAcl->getId()); + + $this->assertEquals($acl->getClassFieldAces('foo'), $cachedAcl->getClassFieldAces('foo')); + $this->assertEquals($acl->getObjectFieldAces('foo'), $cachedAcl->getObjectFieldAces('foo')); + } + + protected function getAcl($depth = 0) + { + static $id = 1; + + $acl = new Acl( + 'oid' . $id, + new ObjectIdentity('class', 'foo' . $id), + $this->permissionGrantingStrategy, + array(), + $depth > 0 + ); + + // insert some ACEs + $sid = new UserSecurityIdentity('johannes', 'Foo'); + + $acl->insertClassAce($sid, 1); + $acl->insertClassFieldAce('foo', $sid, 1); + $acl->insertObjectAce($sid, 1); + $acl->insertObjectFieldAce('foo', $sid, 1); + + $id++; + + if ($depth > 0) { + $acl->setParentAcl($this->getAcl($depth - 1)); + } + + return $acl; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/AbstractDoctrineCacheExtensionTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/AbstractDoctrineCacheExtensionTest.php new file mode 100644 index 0000000..ef0384c --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/AbstractDoctrineCacheExtensionTest.php @@ -0,0 +1,382 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection; + +use Doctrine\Bundle\DoctrineCacheBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\DoctrineCacheExtension; + +/** + * @group Extension + * @group DependencyInjection + */ +abstract class AbstractDoctrineCacheExtensionTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + } + + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testParameters() + { + $container = $this->createContainer(); + $cacheExtension = new DoctrineCacheExtension(); + + $cacheExtension->load(array(), $container); + + $this->assertTrue($container->hasParameter('doctrine_cache.apc.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.array.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.couchbase.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.file_system.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.memcached.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.memcache.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.mongodb.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.php_file.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.redis.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.riak.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.sqlite3.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.void.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.xcache.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.wincache.class')); + $this->assertTrue($container->hasParameter('doctrine_cache.zenddata.class')); + } + + public function testBasicCache() + { + $container = $this->compileContainer('basic'); + $drivers = array( + 'basic_apc_provider' => '%doctrine_cache.apc.class%', + 'basic_array_provider' => '%doctrine_cache.array.class%', + 'basic_void_provider' => '%doctrine_cache.void.class%', + 'basic_xcache_provider' => '%doctrine_cache.xcache.class%', + 'basic_wincache_provider' => '%doctrine_cache.wincache.class%', + 'basic_zenddata_provider' => '%doctrine_cache.zenddata.class%', + 'basic_ns_zenddata_provider' => '%doctrine_cache.zenddata.class%', + + 'basic_apc_provider2' => '%doctrine_cache.apc.class%', + 'basic_array_provider2' => '%doctrine_cache.array.class%', + 'basic_void_provider2' => '%doctrine_cache.void.class%', + 'basic_xcache_provider2' => '%doctrine_cache.xcache.class%', + 'basic_wincache_provider2' => '%doctrine_cache.wincache.class%', + 'basic_zenddata_provider2' => '%doctrine_cache.zenddata.class%', + ); + + foreach ($drivers as $key => $value) { + $this->assertCacheProvider($container, $key, $value); + } + } + + public function testBasicConfigurableCache() + { + $container = $this->compileContainer('configurable'); + $drivers = array( + 'configurable_chain_provider' => array( + '%doctrine_cache.chain.class%' + ), + 'configurable_couchbase_provider' => array( + '%doctrine_cache.couchbase.class%' + ), + 'configurable_filesystem_provider' => array( + '%doctrine_cache.file_system.class%' + ), + 'configurable_memcached_provider' => array( + '%doctrine_cache.memcached.class%', array('setMemcached' => array()) + ), + 'configurable_memcache_provider' => array( + '%doctrine_cache.memcache.class%', array('setMemcache' => array()) + ), + 'configurable_mongodb_provider' => array( + '%doctrine_cache.mongodb.class%' + ), + 'configurable_phpfile_provider' => array( + '%doctrine_cache.php_file.class%' + ), + 'configurable_redis_provider' => array( + '%doctrine_cache.redis.class%', array('setRedis' => array()) + ), + 'configurable_riak_provider' => array( + '%doctrine_cache.riak.class%' + ), + 'configurable_sqlite3_provider' => array( + '%doctrine_cache.sqlite3.class%' + ), + ); + + foreach ($drivers as $id => $value) { + $this->assertCacheProvider($container, $id, $value[0]); + } + } + + public function testBasicConfigurableDefaultCache() + { + $container = $this->compileContainer('configurable_defaults'); + $drivers = array( + 'configurable_memcached_provider' => array( + '%doctrine_cache.memcached.class%', array('setMemcached' => array()) + ), + 'configurable_memcache_provider' => array( + '%doctrine_cache.memcache.class%', array('setMemcache' => array()) + ), + 'configurable_redis_provider' => array( + '%doctrine_cache.redis.class%', array('setRedis' => array()) + ), + 'configurable_mongodb_provider' => array( + '%doctrine_cache.mongodb.class%' + ), + 'configurable_riak_provider' => array( + '%doctrine_cache.riak.class%' + ), + 'configurable_filesystem_provider' => array( + '%doctrine_cache.file_system.class%' + ), + 'configurable_phpfile_provider' => array( + '%doctrine_cache.php_file.class%' + ), + 'configurable_couchbase_provider' => array( + '%doctrine_cache.couchbase.class%' + ), + 'configurable_memcached_provider_type' => array( + '%doctrine_cache.memcached.class%', array('setMemcached' => array()) + ), + 'configurable_memcache_provider_type' => array( + '%doctrine_cache.memcache.class%', array('setMemcache' => array()) + ), + 'configurable_redis_provider_type' => array( + '%doctrine_cache.redis.class%', array('setRedis' => array()) + ), + 'configurable_mongodb_provider_type' => array( + '%doctrine_cache.mongodb.class%' + ), + 'configurable_riak_provider_type' => array( + '%doctrine_cache.riak.class%' + ), + 'configurable_filesystem_provider_type' => array( + '%doctrine_cache.file_system.class%' + ), + 'configurable_phpfile_provider_type' => array( + '%doctrine_cache.php_file.class%' + ), + 'configurable_couchbase_provider_type' => array( + '%doctrine_cache.couchbase.class%' + ), + ); + + foreach ($drivers as $id => $value) { + $this->assertCacheProvider($container, $id, $value[0]); + } + } + + public function testBasicNamespaceCache() + { + $container = $this->compileContainer('namespaced'); + $drivers = array( + 'doctrine_cache.providers.foo_namespace_provider' => 'foo_namespace', + 'doctrine_cache.providers.barNamespaceProvider' => 'barNamespace', + ); + + foreach ($drivers as $key => $value) { + $this->assertTrue($container->hasDefinition($key)); + + $def = $container->getDefinition($key); + $calls = $def->getMethodCalls(); + + $this->assertEquals('setNamespace', $calls[0][0]); + $this->assertEquals($value, $calls[0][1][0]); + } + } + + public function testAliasesCache() + { + $container = $this->compileContainer('aliased'); + $providers = array( + 'doctrine_cache.providers.foo_namespace_provider' => array('fooNamespaceProvider', 'foo'), + 'doctrine_cache.providers.barNamespaceProvider' => array('bar_namespace_provider', 'bar'), + ); + + foreach ($providers as $key => $aliases) { + $this->assertTrue($container->hasDefinition($key)); + + foreach ($aliases as $alias) { + $this->assertEquals(strtolower($key), (string) $container->getAlias($alias)); + } + } + } + + public function testServiceParameters() + { + $container = $this->compileContainer('service_parameter'); + $providers = array( + 'service_bucket_riak_provider' => array( + '%doctrine_cache.riak.class%' + ), + 'service_connection_riak_provider' => array( + '%doctrine_cache.riak.class%' + ), + 'service_connection_memcached_provider' => array( + '%doctrine_cache.memcached.class%' + ), + 'service_connection_memcache_provider' => array( + '%doctrine_cache.memcache.class%' + ), + 'service_connection_redis_provider' => array( + '%doctrine_cache.redis.class%' + ), + 'service_connection_mongodb_provider' => array( + '%doctrine_cache.mongodb.class%' + ), + 'service_collection_mongodb_provider' => array( + '%doctrine_cache.mongodb.class%' + ), + 'service_connection_sqlite3_provider' => array( + '%doctrine_cache.sqlite3.class%' + ), + ); + + foreach ($providers as $id => $value) { + $this->assertCacheProvider($container, $id, $value[0]); + } + } + + public function testCustomCacheProviders() + { + $container = $this->compileContainer('custom_providers'); + $providers = array( + 'my_custom_type_provider' => array( + 'Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Cache\MyCustomType', + array('addConfig' => array( + array('config_foo', 'foo'), + array('config_bar', 'bar'), + )) + ), + 'my_custom_type_provider2' => array( + 'Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Cache\MyCustomType', + array() + ), + ); + + foreach ($providers as $id => $value) { + $this->assertCacheProvider($container, $id, $value[0], $value[1]); + } + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage "unrecognized_type" is an unrecognized Doctrine cache driver. + */ + public function testUnrecognizedCacheDriverException() + { + $this->compileContainer('unrecognized'); + } + + public function testAcl() + { + $container = $this->compileContainer('acl'); + + $this->assertTrue($container->hasDefinition('doctrine_cache.security.acl.cache')); + + $definition = $container->getDefinition('doctrine_cache.security.acl.cache'); + + $this->assertEquals('Doctrine\Bundle\DoctrineCacheBundle\Acl\Model\AclCache', $definition->getClass()); + $this->assertCount(2, $definition->getArguments()); + $this->assertEquals('doctrine_cache.providers.acl_apc_provider', (string) $definition->getArgument(0)); + $this->assertEquals('security.acl.permission_granting_strategy', (string) $definition->getArgument(1)); + $this->assertFalse($definition->isPublic()); + } + + public function assertCacheProvider(ContainerBuilder $container, $name, $class, array $expectedCalls = array()) + { + $service = "doctrine_cache.providers." . $name; + + $this->assertTrue($container->hasDefinition($service)); + + $definition = $container->getDefinition($service); + + $this->assertTrue($definition->isPublic()); + $this->assertEquals($class, $definition->getClass()); + + foreach (array_unique($expectedCalls) as $methodName => $params) { + $this->assertMethodCall($definition, $methodName, $params); + } + } + + public function assertCacheResource(ContainerBuilder $container, $name, $class, array $expectedCalls = array()) + { + $service = "doctrine_cache.services.$name"; + + $this->assertTrue($container->hasDefinition($service)); + + $definition = $container->getDefinition($service); + + $this->assertTrue($definition->isPublic()); + $this->assertEquals($class, $definition->getClass()); + + foreach ($expectedCalls as $methodName => $params) { + $this->assertMethodCall($definition, $methodName, $params); + } + } + + private function assertMethodCall(Definition $definition, $methodName, array $parameters = array()) + { + $methodCalls = $definition->getMethodCalls(); + $actualCalls = array(); + + foreach ($methodCalls as $call) { + $actualCalls[$call[0]][] = $call[1]; + } + + $this->assertArrayHasKey($methodName, $actualCalls); + $this->assertCount(count($parameters), $actualCalls[$methodName]); + + foreach ($parameters as $index => $param) { + $this->assertArrayHasKey($index, $actualCalls[$methodName]); + $this->assertEquals($param, $actualCalls[$methodName][$index]); + } + } + + /** + * @param string $file + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\ContainerBuilder + */ + protected function compileContainer($file, ContainerBuilder $container = null) + { + $container = $container ?: $this->createContainer(); + $cacheExtension = new DoctrineCacheExtension(); + + $container->registerExtension($cacheExtension); + + $compilerPassConfig = $container->getCompilerPassConfig(); + + $compilerPassConfig->setOptimizationPasses(array(new ResolveDefinitionTemplatesPass())); + $compilerPassConfig->setRemovingPasses(array()); + + $this->loadFromFile($container, $file); + + $container->compile(); + + return $container; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php new file mode 100644 index 0000000..eb35d09 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Bundles/XmlBundle/XmlBundle.php @@ -0,0 +1,22 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\YamlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class XmlBundle extends Bundle +{ + +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php new file mode 100644 index 0000000..b76fee0 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Bundles/YamlBundle/YamlBundle.php @@ -0,0 +1,22 @@ + + * (c) Doctrine Project, Benjamin Eberlei + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\YamlBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class YamlBundle extends Bundle +{ + +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Cache/MyCustomType.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Cache/MyCustomType.php new file mode 100644 index 0000000..2dbbdc0 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Cache/MyCustomType.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Cache; + +use Doctrine\Common\Cache\ArrayCache; + +class MyCustomType extends ArrayCache +{ + public $configs; + + public function addConfig($name, $value) + { + $this->configs[$name] = $value; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Definition/MyCustomTypeDefinition.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Definition/MyCustomTypeDefinition.php new file mode 100644 index 0000000..7a91e23 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/Definition/MyCustomTypeDefinition.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Definition; + +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\Definition\CacheDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +class MyCustomTypeDefinition extends CacheDefinition +{ + /** + * {@inheritDoc} + */ + public function configure($name, array $config, Definition $service, ContainerBuilder $container) + { + foreach ($config['custom_provider']['options'] as $name => $value) { + $service->addMethodCall('addConfig', array($name, $value)); + } + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/acl.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/acl.xml new file mode 100644 index 0000000..4dc3fab --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/acl.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/aliased.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/aliased.xml new file mode 100644 index 0000000..50975df --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/aliased.xml @@ -0,0 +1,20 @@ + + + + + foo_namespace_provider + + + fooNamespaceProvider + + + + bar_namespace_provider + bar + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/basic.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/basic.xml new file mode 100644 index 0000000..a148363 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/basic.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/configurable.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/configurable.xml new file mode 100644 index 0000000..8023bb3 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/configurable.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + configurable_array_provider + configurable_apc_provider + + + + + + + + + + + + + + + + + + + + + + + + my_database + my_collection + + + + + + my_bucket + + false + 1 + + + + + + + my_table + + + + + + %kernel.cache_dir%/configurable-filesystem-provider + + + + + + %kernel.cache_dir%/configurable-phpfile-provider + + + + + + my_bucket + 127.0.0.1:809 + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/configurable_defaults.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/configurable_defaults.xml new file mode 100644 index 0000000..59acc00 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/configurable_defaults.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + memcached_provider_type + + + memcache_provider_type + + + redis_provider_type + + + mongodb_provider_type + + + memcached_provider_type + + + filesystem_provider_type + + + phpfile_provider_type + + + couchbase_provider_type + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/custom_providers.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/custom_providers.xml new file mode 100644 index 0000000..d1f347e --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/custom_providers.xml @@ -0,0 +1,36 @@ + + + + + + + + + + my_custom_provider_service + Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Definition\MyCustomTypeDefinition + + + + my_custom_provider_service + + + + + foo + bar + + + + + + foobar + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/namespaced.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/namespaced.xml new file mode 100644 index 0000000..c968926 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/namespaced.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/service_parameter.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/service_parameter.xml new file mode 100644 index 0000000..f8b75a5 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/service_parameter.xml @@ -0,0 +1,95 @@ + + + + + + localhost + 8087 + + + + + my_service_bucket_name + + + + cache.db + + + + + localhost + 11211 + + + + + + localhost + 11211 + + + + + + localhost + 6379 + + + + + localhost:27017 + + + + my_database + my_cache_collection + + + + + + + + + + + my_bucket_name + + + + + + + + + + my_memcache_connection_service + + + + + + + + + + + + + + + + + + my_memcache_connection_service + my_database + my_cache_collection + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/unrecognized.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/unrecognized.xml new file mode 100644 index 0000000..d2af580 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/xml/unrecognized.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/acl.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/acl.yml new file mode 100644 index 0000000..5f5023b --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/acl.yml @@ -0,0 +1,6 @@ +doctrine_cache: + acl_cache: + id: 'doctrine_cache.providers.acl_apc_provider' + providers: + acl_apc_provider: + type: 'apc' \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/aliased.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/aliased.yml new file mode 100644 index 0000000..6b682f1 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/aliased.yml @@ -0,0 +1,13 @@ +doctrine_cache: + aliases: + foo: foo_namespace_provider + providers: + foo_namespace_provider: + type: array + aliases: + - fooNamespaceProvider + barNamespaceProvider: + type: array + aliases: + - bar_namespace_provider + - bar \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/basic.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/basic.yml new file mode 100644 index 0000000..23fea42 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/basic.yml @@ -0,0 +1,30 @@ +doctrine_cache: + providers: + basic_apc_provider: + type: apc + basic_array_provider: + type: array + basic_void_provider: + type: void + basic_xcache_provider: + type: xcache + basic_wincache_provider: + type: wincache + basic_zenddata_provider: + type: zenddata + basic_ns_zenddata_provider: + type: zenddata + namespace: zenddata_ns + + basic_apc_provider2: + apc: ~ + basic_array_provider2: + array: ~ + basic_void_provider2: + void: ~ + basic_xcache_provider2: + xcache: ~ + basic_wincache_provider2: + wincache: ~ + basic_zenddata_provider2: + zenddata: ~ diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/configurable.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/configurable.yml new file mode 100644 index 0000000..758f7fc --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/configurable.yml @@ -0,0 +1,67 @@ +doctrine_cache: + providers: + configurable_apc_provider: + apc: ~ + + configurable_array_provider: + array: ~ + + configurable_chain_provider: + chain: + providers: + - configurable_array_provider + - configurable_apc_provider + + configurable_memcached_provider: + memcached: + servers: + memcached01.ss: ~ + memcached02.ss: ~ + + configurable_memcache_provider: + memcache: + servers: + memcache01.ss: ~ + memcache02.ss: ~ + + configurable_redis_provider: + redis: + host: localhost + port: 11211 + + configurable_couchbase_provider: + couchbase: + hostnames: [ 127.0.0.1:809 ] + bucket_name: my_bucket + username: Administrator + password: password + + configurable_phpfile_provider: + php_file: + extension: phpc + directory: "%kernel.cache_dir%/configurable-phpfile-provider" + + configurable_filesystem_provider: + file_system: + extension: fsc + directory: "%kernel.cache_dir%/configurable-filesystem-provider" + + configurable_mongodb_provider: + mongodb: + server: localhost:11211 + database_name: my_database + collection_name: my_collection + + configurable_riak_provider: + riak: + host: localhost + port: 8087 + bucket_name: my_bucket + bucket_property_list: + allow_multiple: false + n_value: 1 + + configurable_sqlite3_provider: + sqlite3: + file_name: cache.db + table_name: my_table \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/configurable_defaults.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/configurable_defaults.yml new file mode 100644 index 0000000..0e4835e --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/configurable_defaults.yml @@ -0,0 +1,65 @@ +doctrine_cache: + providers: + configurable_memcached_provider: + memcached: ~ + + configurable_memcache_provider: + memcache: ~ + + configurable_redis_provider: + redis: ~ + + configurable_couchbase_provider: + couchbase: ~ + + configurable_phpfile_provider: + php_file: ~ + + configurable_filesystem_provider: + file_system: ~ + + configurable_mongodb_provider: + mongodb: ~ + + configurable_riak_provider: + riak: ~ + + configurable_memcached_provider_type: + type : memcached + aliases : + - memcached_provider_type + + configurable_memcache_provider_type: + type : memcache + aliases : + - memcache_provider_type + + configurable_redis_provider_type: + type : redis + aliases : + - redis_provider_type + + configurable_couchbase_provider_type: + type : couchbase + aliases : + - couchbase_provider_type + + configurable_phpfile_provider_type: + type : php_file + aliases : + - php_file_provider_type + + configurable_filesystem_provider_type: + type : file_system + aliases : + - file_system_provider_type + + configurable_mongodb_provider_type: + type : mongodb + aliases : + - mongodb_provider_type + + configurable_riak_provider_type: + type : riak + aliases : + - riak_provider_type \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/custom_providers.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/custom_providers.yml new file mode 100644 index 0000000..cd5605d --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/custom_providers.yml @@ -0,0 +1,22 @@ +services: + my_custom_provider_service: + class: Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Cache\MyCustomType + +doctrine_cache: + custom_providers: + my_custom_type: + prototype: "my_custom_provider_service" + definition_class: Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection\Fixtures\Definition\MyCustomTypeDefinition + + my_custom_type2: + prototype: "my_custom_provider_service" + + providers: + my_custom_type_provider: + my_custom_type: + config_foo: "foo" + config_bar: "bar" + + my_custom_type_provider2: + my_custom_type2: + config_foobar: "foobar" diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/namespaced.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/namespaced.yml new file mode 100644 index 0000000..a455774 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/namespaced.yml @@ -0,0 +1,8 @@ +doctrine_cache: + providers: + foo_namespace_provider: + type: array + namespace: foo_namespace + barNamespaceProvider: + type: array + namespace: barNamespace \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/service_parameter.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/service_parameter.yml new file mode 100644 index 0000000..80789ae --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/service_parameter.yml @@ -0,0 +1,77 @@ +services: + my_memcached_connection_service: + class: "%doctrine_cache.memcached.connection.class%" + calls: + - [addServer, ["localhost", 11211]] + + my_memcache_connection_service: + class: "%doctrine_cache.memcache.connection.class%" + calls: + - [connect, ["localhost", 11211]] + + my_mongodb_connection_service: + class: "%doctrine_cache.mongodb.connection.class%" + arguments: ["localhost:27017"] + + my_mongodb_collection_service: + class: "%doctrine_cache.mongodb.collection.class%" + arguments: ["my_database", "my_cache_collection"] + + my_redis_connection_service: + class: "%doctrine_cache.redis.connection.class%" + calls: + - [connect, ["localhost", 6379]] + + my_riak_connection_service: + class: "%doctrine_cache.riak.connection.class%" + arguments: ["localhost", 8087] + + my_riak_bucket_service: + class: "%doctrine_cache.riak.bucket.class%" + arguments: ["@my_riak_connection_service", "my_service_bucket_name"] + + my_sqlite3_connection_service: + class: "%doctrine_cache.sqlite3.connection.class%" + arguments: ["cache.db"] + +doctrine_cache: + providers: + service_connection_couchbase_provider: + couchbase: + connection_id: "my_couchbase_connection_service" + + service_connection_memcached_provider: + memcached: + connection_id: "my_memcached_connection_service" + + service_connection_memcache_provider: + memcache: + connection_id: "my_memcache_connection_service" + + service_collection_mongodb_provider: + mongodb: + collection_id: "my_mongodb_collection_service" + + service_connection_mongodb_provider: + mongodb: + connection_id: "my_mongodb_connection_service" + collection_name: "my_cache_collection" + database_name: "my_database" + + service_connection_redis_provider: + redis: + connection_id: "my_redis_connection_service" + + service_bucket_riak_provider: + riak: + bucket_id : "my_riak_bucket_service" + + service_connection_riak_provider: + riak: + connection_id: "my_riak_connection_service" + bucket_name: "my_bucket_name" + + service_connection_sqlite3_provider: + sqlite3: + connection_id: "my_sqlite3_connection_service" + table_name: 'my_table' diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/unrecognized.yml b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/unrecognized.yml new file mode 100644 index 0000000..d946f93 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/Fixtures/config/yml/unrecognized.yml @@ -0,0 +1,4 @@ +doctrine_cache: + providers: + unrecognized_cache_provider: + type: unrecognized_type \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/SymfonyBridgeAdapterTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/SymfonyBridgeAdapterTest.php new file mode 100644 index 0000000..6c1b72e --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/SymfonyBridgeAdapterTest.php @@ -0,0 +1,143 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection; + +use Doctrine\Bundle\DoctrineCacheBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Definition; +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter; +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\CacheProviderLoader; + +/** + * @group Extension + * @group SymfonyBridge + * + * @author Kinn Coelho Julião + * @author Fabio B. Silva + */ +class SymfonyBridgeAdpterTest extends TestCase +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\SymfonyBridgeAdapter + */ + private $adapter; + + protected function setUp() + { + parent::setUp(); + + $this->adapter = new SymfonyBridgeAdapter(new CacheProviderLoader(), 'doctrine.orm', 'orm'); + } + + public function providerBasicDrivers() + { + return array( + array('%doctrine_cache.apc.class%', array('type' => 'apc')), + array('%doctrine_cache.array.class%', array('type' => 'array')), + array('%doctrine_cache.xcache.class%', array('type' => 'xcache')), + array('%doctrine_cache.wincache.class%', array('type' => 'wincache')), + array('%doctrine_cache.zenddata.class%', array('type' => 'zenddata')), + array('%doctrine_cache.redis.class%', array('type' => 'redis'), array('setRedis')), + array('%doctrine_cache.memcache.class%', array('type' => 'memcache'), array('setMemcache')), + array('%doctrine_cache.memcached.class%', array('type' => 'memcached'), array('setMemcached')), + ); + } + + /** + * @param string $class + * @param array $config + * + * @dataProvider providerBasicDrivers + */ + public function testLoadBasicCacheDriver($class, array $config, array $expectedCalls = array()) + { + $container = $this->createServiceContainer(); + $cacheName = 'metadata_cache'; + $objectManager = array( + 'name' => 'default', + 'metadata_cache_driver' => $config + ); + + $this->adapter->loadObjectManagerCacheDriver($objectManager, $container, $cacheName); + $this->assertTrue($container->hasAlias('doctrine.orm.default_metadata_cache')); + + $alias = $container->getAlias('doctrine.orm.default_metadata_cache'); + $decorator = $container->getDefinition($alias); + $definition = $container->getDefinition($decorator->getParent()); + $defCalls = $decorator->getMethodCalls(); + $expectedCalls[] = 'setNamespace'; + $actualCalls = array_map(function ($call) { + return $call[0]; + }, $defCalls); + + $this->assertEquals($class, $definition->getClass()); + + foreach (array_unique($expectedCalls) as $call) { + $this->assertContains($call, $actualCalls); + } + } + + public function testServiceCacheDriver() + { + $cacheName = 'metadata_cache'; + $container = $this->createServiceContainer(); + $definition = new Definition('%doctrine.orm.cache.apc.class%'); + $objectManager = array( + 'name' => 'default', + 'metadata_cache_driver' => array( + 'type' => 'service', + 'id' => 'service_driver' + ) + ); + + $container->setDefinition('service_driver', $definition); + + $this->adapter->loadObjectManagerCacheDriver($objectManager, $container, $cacheName); + + $this->assertTrue($container->hasAlias('doctrine.orm.default_metadata_cache')); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage "unrecognized_type" is an unrecognized Doctrine cache driver. + */ + public function testUnrecognizedCacheDriverException() + { + $cacheName = 'metadata_cache'; + $container = $this->createServiceContainer(); + $objectManager = array( + 'name' => 'default', + 'metadata_cache_driver' => array( + 'type' => 'unrecognized_type' + ) + ); + + $this->adapter->loadObjectManagerCacheDriver($objectManager, $container, $cacheName); + } + + public function testLoadServicesConfiguration() + { + $container = $this->createContainer(); + + $this->assertFalse($container->hasParameter('doctrine_cache.array.class')); + $this->adapter->loadServicesConfiguration($container); + $this->assertTrue($container->hasParameter('doctrine_cache.array.class')); + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/XmlDoctrineCacheExtensionTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/XmlDoctrineCacheExtensionTest.php new file mode 100644 index 0000000..d63bbbe --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/XmlDoctrineCacheExtensionTest.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +/** + * @group Extension + * @group DependencyInjection + */ +class XmlDoctrineCacheExtensionTest extends AbstractDoctrineCacheExtensionTest +{ + /** + * {@inheritdoc} + */ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/config/xml')); + + $loader->load($file . '.xml'); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/YmlDoctrineCacheExtensionTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/YmlDoctrineCacheExtensionTest.php new file mode 100644 index 0000000..85ecfe5 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/DependencyInjection/YmlDoctrineCacheExtensionTest.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +/** + * @group Extension + * @group DependencyInjection + */ +class YmlDoctrineCacheExtensionTest extends AbstractDoctrineCacheExtensionTest +{ + /** + * {@inheritdoc} + */ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/config/yml')); + + $loader->load($file . '.yml'); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/ArrayCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/ArrayCacheTest.php new file mode 100644 index 0000000..e0bb784 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/ArrayCacheTest.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group Array + */ +class ArrayCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('array'); + $cache = $container->get('doctrine_cache.providers.my_array_cache'); + + return $cache; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/BaseCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/BaseCacheTest.php new file mode 100644 index 0000000..0e29728 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/BaseCacheTest.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +use Doctrine\Bundle\DoctrineCacheBundle\Tests\FunctionalTestCase; + +/** + * @group Functional + */ +abstract class BaseCacheTest extends FunctionalTestCase +{ + /** + * @return \Doctrine\Common\Cache\Cache + */ + abstract protected function createCacheDriver(); + + public function testCacheDriver() + { + $cache = $this->createCacheDriver(); + + $this->assertNotNull($cache); + $this->assertInstanceOf('Doctrine\Common\Cache\Cache', $cache); + + $this->assertTrue($cache->save('key', 'value')); + $this->assertTrue($cache->contains('key')); + $this->assertEquals('value', $cache->fetch('key')); + $this->assertTrue($cache->delete('key')); + $this->assertFalse($cache->contains('key')); + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/ChainCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/ChainCacheTest.php new file mode 100644 index 0000000..211ecc3 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/ChainCacheTest.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group Chain + */ +class ChainCacheTest extends BaseCacheTest +{ + public function setUp() + { + parent::setUp(); + + if (!class_exists('Doctrine\Common\Cache\ChainCache')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of ChainCache available in doctrine/cache since 1.4'); + } + } + + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('chain'); + $cache = $container->get('doctrine_cache.providers.my_chain_cache'); + + return $cache; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/CommandTestCase.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/CommandTestCase.php new file mode 100644 index 0000000..fa79108 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/CommandTestCase.php @@ -0,0 +1,88 @@ +container = $this->compileContainer('array'); + $this->provider = $this->container->get('doctrine_cache.providers.' . $this->cacheName); + $this->kernel = $this->getMockKernel(); + $this->app = new Application($this->kernel); + } + + /** + * @param \Doctrine\Bundle\DoctrineCacheBundle\Command\CacheCommand $command + * + * @return \Symfony\Component\Console\Tester\CommandTester + */ + protected function getTester(CacheCommand $command) + { + $command->setContainer($this->container); + $command->setApplication($this->app); + + return new CommandTester($command); + } + + /** + * Gets Kernel mock instance + * + * @return \Symfony\Component\HttpKernel\Kernel + */ + private function getMockKernel() + { + return $this->getMock('\Symfony\Component\HttpKernel\Kernel', array(), array(), '', false, false); + } + + /** + * Gets Filesystem mock instance + * + * @return \Symfony\Bundle\FrameworkBundle\Util\Filesystem + */ + private function getMockFilesystem() + { + return $this->getMock('\Symfony\Bundle\FrameworkBundle\Util\Filesystem', array(), array(), '', false, false); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/ContainsCommandTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/ContainsCommandTest.php new file mode 100644 index 0000000..923d68f --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/ContainsCommandTest.php @@ -0,0 +1,60 @@ + + */ +class ContainsCommandTest extends CommandTestCase +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\Command\ContainsCommand + */ + protected $command; + + /** + * @var \Symfony\Component\Console\Tester\CommandTester + */ + protected $tester; + + /** + * {@inheritdoc} + */ + public function setUp() + { + parent::setUp(); + + $this->command = new ContainsCommand(); + $this->tester = $this->getTester($this->command); + } + + /** + * Tests if a cache does not contain an entry. + */ + public function testContainsFalse() + { + $this->tester->execute(array( + 'cache-name' => $this->cacheName, + 'cache-id' => $this->cacheId, + )); + $this->assertEquals("FALSE\n", $this->tester->getDisplay()); + } + + /** + * Tests if a cache contains an entry. + */ + public function testContainsTrue() + { + $this->provider->save($this->cacheId, 'hello world'); + $this->tester->execute(array( + 'cache-name' => $this->cacheName, + 'cache-id' => $this->cacheId, + )); + $this->assertEquals("TRUE\n", $this->tester->getDisplay()); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/DeleteCommandTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/DeleteCommandTest.php new file mode 100644 index 0000000..bc0f7f7 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/DeleteCommandTest.php @@ -0,0 +1,61 @@ + + */ +class DeleteCommandTest extends CommandTestCase +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\Command\DeleteCommand + */ + protected $command; + + /** + * @var \Symfony\Component\Console\Tester\CommandTester + */ + protected $tester; + + /** + * {@inheritdoc} + */ + public function setUp() + { + parent::setUp(); + + $this->command = new DeleteCommand(); + $this->tester = $this->getTester($this->command); + } + + /** + * Tests a cache delete success. + */ + public function testDeleteSuccess() + { + $this->provider->save($this->cacheId, 'hello world'); + $this->tester->execute(array( + 'cache-name' => $this->cacheName, + 'cache-id' => $this->cacheId, + )); + $this->assertEquals("Deletion of {$this->cacheId} in {$this->cacheName} has succeeded\n", $this->tester->getDisplay()); + } + + /** + * Tests a cache delete all. + */ + public function testDeleteAll() + { + $this->tester->execute(array( + 'cache-name' => $this->cacheName, + 'cache-id' => $this->cacheId, + '--all' => true + )); + $this->assertEquals("Deletion of all entries in {$this->cacheName} has succeeded\n", $this->tester->getDisplay()); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/FlushCommandTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/FlushCommandTest.php new file mode 100644 index 0000000..195eca2 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/FlushCommandTest.php @@ -0,0 +1,46 @@ + + */ +class FlushCommandTest extends CommandTestCase +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\Command\FlushCommand + */ + protected $command; + + /** + * @var \Symfony\Component\Console\Tester\CommandTester + */ + protected $tester; + + /** + * {@inheritdoc} + */ + public function setUp() + { + parent::setUp(); + + $this->command = new FlushCommand(); + $this->tester = $this->getTester($this->command); + } + + /** + * Tests flushing a cache. + */ + public function testFlush() + { + $this->tester->execute(array( + 'cache-name' => $this->cacheName, + )); + $this->assertEquals("Clearing the cache for the {$this->cacheName} provider of type {$this->cacheProviderClass}\n", $this->tester->getDisplay()); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/StatsCommandTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/StatsCommandTest.php new file mode 100644 index 0000000..48f684a --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Command/StatsCommandTest.php @@ -0,0 +1,46 @@ + + */ +class StatsCommandTest extends CommandTestCase +{ + /** + * @var \Doctrine\Bundle\DoctrineCacheBundle\Command\StatsCommand + */ + protected $command; + + /** + * @var \Symfony\Component\Console\Tester\CommandTester + */ + protected $tester; + + /** + * {@inheritdoc} + */ + public function setUp() + { + parent::setUp(); + + $this->command = new StatsCommand(); + $this->tester = $this->getTester($this->command); + } + + /** + * Tests getting cache provider stats. + */ + public function testStats() + { + $this->tester->execute(array( + 'cache-name' => $this->cacheName, + )); + $this->assertEquals("Stats were not provided for the {$this->cacheName} provider of type Doctrine\\Common\\Cache\\ArrayCache\n", $this->tester->getDisplay()); + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/FileSystemCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/FileSystemCacheTest.php new file mode 100644 index 0000000..e2af9e0 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/FileSystemCacheTest.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group FileSystem + */ +class FileSystemCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('file_system'); + $cache = $container->get('doctrine_cache.providers.my_filesystem_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/array.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/array.xml new file mode 100644 index 0000000..e3a2936 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/array.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/chain.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/chain.xml new file mode 100644 index 0000000..e870e81 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/chain.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + my_array_cache + my_second_array_cache + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/file_system.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/file_system.xml new file mode 100644 index 0000000..f4baecd --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/file_system.xml @@ -0,0 +1,15 @@ + + + + + + + %kernel.cache_dir%/configurable-filesystem-provider + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/memcache.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/memcache.xml new file mode 100644 index 0000000..d1b094f --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/memcache.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/memcached.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/memcached.xml new file mode 100644 index 0000000..6c96062 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/memcached.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/mongodb.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/mongodb.xml new file mode 100644 index 0000000..57aab64 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/mongodb.xml @@ -0,0 +1,16 @@ + + + + + + + my_database + my_collection + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/php_file.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/php_file.xml new file mode 100644 index 0000000..2203e52 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/php_file.xml @@ -0,0 +1,15 @@ + + + + + + + %kernel.cache_dir%/configurable-phpfile-provider + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/riak.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/riak.xml new file mode 100644 index 0000000..44beb5c --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/riak.xml @@ -0,0 +1,19 @@ + + + + + + + my_bucket + + false + 1 + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/sqlite3.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/sqlite3.xml new file mode 100644 index 0000000..641d863 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/sqlite3.xml @@ -0,0 +1,15 @@ + + + + + + + my_table + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/void.xml b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/void.xml new file mode 100644 index 0000000..217256a --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Fixtures/config/void.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MemcacheCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MemcacheCacheTest.php new file mode 100644 index 0000000..ca5a135 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MemcacheCacheTest.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group Memcache + */ +class MemcacheCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + + if ( ! extension_loaded('memcache')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); + } + + if (@fsockopen('localhost', 11211) === false) { + $this->markTestSkipped('The ' . __CLASS__ .' cannot connect to memcache'); + } + } + + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('memcache'); + $cache = $container->get('doctrine_cache.providers.my_memcache_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MemcachedCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MemcachedCacheTest.php new file mode 100644 index 0000000..3670b78 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MemcachedCacheTest.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group Memcached + */ +class MemcachedCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + + if ( ! extension_loaded('memcached')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcached'); + } + + if (@fsockopen('localhost', 11211) === false) { + $this->markTestSkipped('The ' . __CLASS__ .' cannot connect to memcached'); + } + } + + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('memcached'); + $cache = $container->get('doctrine_cache.providers.my_memcached_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MongoDBCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MongoDBCacheTest.php new file mode 100644 index 0000000..6bf1943 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/MongoDBCacheTest.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group MongoDB + */ +class MongoDBCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + + if ( ! extension_loaded('mongo')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of mongo >= 1.3.0'); + } + + if (@fsockopen('localhost', 27017) === false) { + $this->markTestSkipped('The ' . __CLASS__ .' cannot connect to mongo'); + } + } + + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('mongodb'); + $cache = $container->get('doctrine_cache.providers.my_mongodb_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/PhpFileCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/PhpFileCacheTest.php new file mode 100644 index 0000000..e7c2e2a --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/PhpFileCacheTest.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group PhpFile + */ +class PhpFileCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('php_file'); + $cache = $container->get('doctrine_cache.providers.my_phpfile_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/RiakCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/RiakCacheTest.php new file mode 100644 index 0000000..b4889cc --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/RiakCacheTest.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group Riak + */ +class RiakCacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + + if ( ! extension_loaded('riak')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of riak'); + } + + if (@fsockopen('localhost', 8087) === false) { + $this->markTestSkipped('The ' . __CLASS__ .' cannot connect to riak'); + } + } + + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('riak'); + $cache = $container->get('doctrine_cache.providers.my_riak_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Sqlite3CacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Sqlite3CacheTest.php new file mode 100644 index 0000000..2036f3c --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/Sqlite3CacheTest.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +/** + * @group Functional + * @group Sqlite3 + */ +class Sqlite3CacheTest extends BaseCacheTest +{ + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + + if ( ! extension_loaded('sqlite3')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of sqlite3'); + } + + if (!class_exists('Doctrine\Common\Cache\SQLite3Cache')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of SQLite3Cache available in doctrine/cache since 1.4'); + } + } + + /** + * {@inheritDoc} + */ + protected function createCacheDriver() + { + $container = $this->compileContainer('sqlite3'); + $cache = $container->get('doctrine_cache.providers.my_sqlite3_cache'); + + return $cache; + } +} \ No newline at end of file diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/VoidCacheTest.php b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/VoidCacheTest.php new file mode 100644 index 0000000..57d00d6 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/Functional/VoidCacheTest.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests\Functional; + +use Doctrine\Bundle\DoctrineCacheBundle\Tests\FunctionalTestCase; + +/** + * @group Functional + * @group Void + */ +class VoidCacheTest extends BaseCacheTest +{ + public function setUp() + { + parent::setUp(); + + if (!class_exists('Doctrine\Common\Cache\VoidCache')) { + $this->markTestSkipped('The ' . __CLASS__ .' requires the use of VoidCache available in doctrine/cache since 1.5'); + } + } + + public function testCacheDriver() + { + $cache = $this->createCacheDriver(); + + $this->assertNotNull($cache); + $this->assertInstanceOf('Doctrine\Common\Cache\Cache', $cache); + + $this->assertTrue($cache->save('key', 'value')); + $this->assertFalse($cache->contains('key')); + $this->assertFalse($cache->fetch('key')); + $this->assertTrue($cache->delete('key')); + $this->assertFalse($cache->contains('key')); + } + + protected function createCacheDriver() + { + $container = $this->compileContainer('void'); + $cache = $container->get('doctrine_cache.providers.my_void_cache'); + + return $cache; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/FunctionalTestCase.php b/vendor/doctrine/doctrine-cache-bundle/Tests/FunctionalTestCase.php new file mode 100644 index 0000000..5d08992 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/FunctionalTestCase.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Doctrine\Bundle\DoctrineCacheBundle\DependencyInjection\DoctrineCacheExtension; + +class FunctionalTestCase extends TestCase +{ + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @param type $file + */ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/Functional/Fixtures/config')); + + $loader->load($file . '.xml'); + } + + /** + * @param string $file + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Symfony\Component\DependencyInjection\ContainerBuilder + */ + protected function compileContainer($file, ContainerBuilder $container = null) + { + $container = $container ?: $this->createContainer(); + $loader = new DoctrineCacheExtension(); + + $container->registerExtension($loader); + $this->loadFromFile($container, $file); + $container->compile(); + + return $container; + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/TestCase.php b/vendor/doctrine/doctrine-cache-bundle/Tests/TestCase.php new file mode 100644 index 0000000..ebedd94 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/TestCase.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\Bundle\DoctrineCacheBundle\Tests; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class TestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @param array $bundles + * @param string $vendor + * @return \Symfony\Component\DependencyInjection\ContainerBuilder + */ + protected function createContainer(array $bundles = array('YamlBundle'), $vendor = null) + { + $mappings = array(); + + foreach ($bundles as $bundle) { + require_once __DIR__.'/DependencyInjection/Fixtures/Bundles/'.($vendor ? $vendor.'/' : '').$bundle.'/'.$bundle.'.php'; + $mappings[$bundle] = 'DependencyInjection\\Fixtures\\Bundles\\'.($vendor ? $vendor.'\\' : '').$bundle.'\\'.$bundle; + } + + return new ContainerBuilder(new ParameterBag(array( + 'kernel.debug' => false, + 'kernel.bundles' => $mappings, + 'kernel.environment' => 'test', + 'kernel.root_dir' => __DIR__.'/../', + 'kernel.cache_dir' => sys_get_temp_dir(), + ))); + } + + /** + * @param array $bundles + * @param string $vendor + * @return \Symfony\Component\DependencyInjection\ContainerBuilder + */ + protected function createServiceContainer(array $bundles = array('YamlBundle'), $vendor = null) + { + $container = $this->createContainer($bundles, $vendor); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('services.xml'); + + return $container; + } + +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/bootstrap.php b/vendor/doctrine/doctrine-cache-bundle/Tests/bootstrap.php new file mode 100644 index 0000000..71ca3ff --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/bootstrap.php @@ -0,0 +1,25 @@ +. + */ + +if ( ! @include __DIR__ . '/../vendor/autoload.php') { + die("You must set up the project dependencies, run the following commands: +wget http://getcomposer.org/composer.phar +php composer.phar install --dev +"); +} diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/travis/install-deps.sh b/vendor/doctrine/doctrine-cache-bundle/Tests/travis/install-deps.sh new file mode 100755 index 0000000..5947b54 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/travis/install-deps.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh + +BASEDIR=$(dirname $0); + +if [ "$TRAVIS_PHP_VERSION" = "hhvm" ]; then + exit 0; +fi + +pecl install riak +phpenv config-add $BASEDIR/php.ini diff --git a/vendor/doctrine/doctrine-cache-bundle/Tests/travis/php.ini b/vendor/doctrine/doctrine-cache-bundle/Tests/travis/php.ini new file mode 100644 index 0000000..ef5d9a1 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/Tests/travis/php.ini @@ -0,0 +1,6 @@ +extension="mongo.so" +extension="memcache.so" +extension="memcached.so" + +apc.enabled=1 +apc.enable_cli=1 diff --git a/vendor/doctrine/doctrine-cache-bundle/composer.json b/vendor/doctrine/doctrine-cache-bundle/composer.json new file mode 100644 index 0000000..23ab607 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/composer.json @@ -0,0 +1,66 @@ +{ + "name": "doctrine/doctrine-cache-bundle", + "homepage": "http://www.doctrine-project.org", + "description": "Symfony Bundle for Doctrine Cache", + "keywords": ["cache", "caching"], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Fabio B. Silva", + "email": "fabio.bat.silva@gmail.com" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + } + ], + "require": { + "php": ">=5.3.2", + "symfony/doctrine-bridge": "~2.2|~3.0", + "doctrine/inflector": "~1.0", + "doctrine/cache": "^1.4.2" + }, + "require-dev": { + "phpunit/phpunit": "~4", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/yaml": "~2.2|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/console": "~2.2|~3.0", + "symfony/finder": "~2.2|~3.0", + "symfony/framework-bundle": "~2.2|~3.0", + "symfony/security-acl": "~2.3|~3.0", + "instaclick/coding-standard": "~1.1", + "satooshi/php-coveralls": "~0.6.1", + "squizlabs/php_codesniffer": "~1.5", + "instaclick/object-calisthenics-sniffs": "dev-master", + "instaclick/symfony2-coding-standard": "dev-remaster" + }, + "suggest": { + "symfony/security-acl": "For using this bundle to cache ACLs" + }, + "autoload": { + "psr-4": { "Doctrine\\Bundle\\DoctrineCacheBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} diff --git a/vendor/doctrine/doctrine-cache-bundle/phpunit.xml.dist b/vendor/doctrine/doctrine-cache-bundle/phpunit.xml.dist new file mode 100644 index 0000000..2157842 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/phpunit.xml.dist @@ -0,0 +1,35 @@ + + + + + + ./Tests + + + + + + benchmark + + + + + + . + + ./Resources + ./vendor + ./Tests + + + + diff --git a/vendor/doctrine/doctrine-cache-bundle/ruleset.xml b/vendor/doctrine/doctrine-cache-bundle/ruleset.xml new file mode 100644 index 0000000..75ec877 --- /dev/null +++ b/vendor/doctrine/doctrine-cache-bundle/ruleset.xml @@ -0,0 +1,11 @@ + + + Instaclick Coding Standard + + */Resources/* + + + + + + diff --git a/vendor/doctrine/inflector/.travis.yml b/vendor/doctrine/inflector/.travis.yml new file mode 100644 index 0000000..9ec68f7 --- /dev/null +++ b/vendor/doctrine/inflector/.travis.yml @@ -0,0 +1,21 @@ +language: php + +sudo: false + +cache: + directory: + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +install: + - composer install -n + +script: + - phpunit diff --git a/vendor/doctrine/inflector/LICENSE b/vendor/doctrine/inflector/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/inflector/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/inflector/README.md b/vendor/doctrine/inflector/README.md new file mode 100644 index 0000000..acb55a0 --- /dev/null +++ b/vendor/doctrine/inflector/README.md @@ -0,0 +1,6 @@ +# Doctrine Inflector + +Doctrine Inflector is a small library that can perform string manipulations +with regard to upper-/lowercase and singular/plural forms of words. + +[![Build Status](https://travis-ci.org/doctrine/inflector.svg?branch=master)](https://travis-ci.org/doctrine/inflector) diff --git a/vendor/doctrine/inflector/composer.json b/vendor/doctrine/inflector/composer.json new file mode 100644 index 0000000..7e5b2ef --- /dev/null +++ b/vendor/doctrine/inflector/composer.json @@ -0,0 +1,29 @@ +{ + "name": "doctrine/inflector", + "type": "library", + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "keywords": ["string", "inflection", "singularize", "pluralize"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Inflector\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php b/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php new file mode 100644 index 0000000..a53828a --- /dev/null +++ b/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php @@ -0,0 +1,482 @@ +. + */ + +namespace Doctrine\Common\Inflector; + +/** + * Doctrine inflector has static methods for inflecting text. + * + * The methods in these classes are from several different sources collected + * across several different php projects and several different authors. The + * original author names and emails are not known. + * + * Pluralize & Singularize implementation are borrowed from CakePHP with some modifications. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Konsta Vesterinen + * @author Jonathan H. Wage + */ +class Inflector +{ + /** + * Plural inflector rules. + * + * @var array + */ + private static $plural = array( + 'rules' => array( + '/(s)tatus$/i' => '\1\2tatuses', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(f)oot$/i' => '\1eet', + '/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(analys|ax|cris|test|thes)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ), + 'uninflected' => array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie' + ), + 'irregular' => array( + 'atlas' => 'atlases', + 'axe' => 'axes', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'chateau' => 'chateaux', + 'child' => 'children', + 'cookie' => 'cookies', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'criterion' => 'criteria', + 'curriculum' => 'curricula', + 'demo' => 'demos', + 'domino' => 'dominoes', + 'echo' => 'echoes', + 'foot' => 'feet', + 'fungus' => 'fungi', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hippopotamus' => 'hippopotami', + 'hoof' => 'hoofs', + 'human' => 'humans', + 'iris' => 'irises', + 'leaf' => 'leaves', + 'loaf' => 'loaves', + 'man' => 'men', + 'medium' => 'media', + 'memorandum' => 'memoranda', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'motto' => 'mottoes', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'nucleus' => 'nuclei', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'plateau' => 'plateaux', + 'runner-up' => 'runners-up', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'son-in-law' => 'sons-in-law', + 'syllabus' => 'syllabi', + 'testis' => 'testes', + 'thief' => 'thieves', + 'tooth' => 'teeth', + 'tornado' => 'tornadoes', + 'trilby' => 'trilbys', + 'turf' => 'turfs', + 'volcano' => 'volcanoes', + ) + ); + + /** + * Singular inflector rules. + * + * @var array + */ + private static $singular = array( + 'rules' => array( + '/(s)tatuses$/i' => '\1\2tatus', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(analys|ax|cris|test|thes)es$/i' => '\1is', + '/(shoe|slave)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/([^a])uses$/' => '\1us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(f)eet$/i' => '\1oot', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '', + ), + 'uninflected' => array( + '.*[nrlm]ese', + '.*deer', + '.*fish', + '.*measles', + '.*ois', + '.*pox', + '.*sheep', + '.*ss', + ), + 'irregular' => array( + 'criteria' => 'criterion', + 'curves' => 'curve', + 'emphases' => 'emphasis', + 'foes' => 'foe', + 'hoaxes' => 'hoax', + 'media' => 'medium', + 'neuroses' => 'neurosis', + 'waves' => 'wave', + 'oases' => 'oasis', + ) + ); + + /** + * Words that should not be inflected. + * + * @var array + */ + private static $uninflected = array( + 'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', + 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', + 'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder', + 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', + 'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', '.*?media', + 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', + 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', + 'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', + 'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine', + 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting', + 'wildebeest', 'Yengeese' + ); + + /** + * Method cache array. + * + * @var array + */ + private static $cache = array(); + + /** + * The initial state of Inflector so reset() works. + * + * @var array + */ + private static $initialState = array(); + + /** + * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. + * + * @param string $word The word to tableize. + * + * @return string The tableized word. + */ + public static function tableize($word) + { + return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); + } + + /** + * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. + * + * @param string $word The word to classify. + * + * @return string The classified word. + */ + public static function classify($word) + { + return str_replace(" ", "", ucwords(strtr($word, "_-", " "))); + } + + /** + * Camelizes a word. This uses the classify() method and turns the first character to lowercase. + * + * @param string $word The word to camelize. + * + * @return string The camelized word. + */ + public static function camelize($word) + { + return lcfirst(self::classify($word)); + } + + /** + * Uppercases words with configurable delimeters between words. + * + * Takes a string and capitalizes all of the words, like PHP's built-in + * ucwords function. This extends that behavior, however, by allowing the + * word delimeters to be configured, rather than only separating on + * whitespace. + * + * Here is an example: + * + * + * + * + * @param string $string The string to operate on. + * @param string $delimiters A list of word separators. + * + * @return string The string with all delimeter-separated words capitalized. + */ + public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-") + { + return preg_replace_callback( + '/[^' . preg_quote($delimiters, '/') . ']+/', + function($matches) { + return ucfirst($matches[0]); + }, + $string + ); + } + + /** + * Clears Inflectors inflected value caches, and resets the inflection + * rules to the initial values. + * + * @return void + */ + public static function reset() + { + if (empty(self::$initialState)) { + self::$initialState = get_class_vars('Inflector'); + + return; + } + + foreach (self::$initialState as $key => $val) { + if ($key != 'initialState') { + self::${$key} = $val; + } + } + } + + /** + * Adds custom inflection $rules, of either 'plural' or 'singular' $type. + * + * ### Usage: + * + * {{{ + * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); + * Inflector::rules('plural', array( + * 'rules' => array('/^(inflect)ors$/i' => '\1ables'), + * 'uninflected' => array('dontinflectme'), + * 'irregular' => array('red' => 'redlings') + * )); + * }}} + * + * @param string $type The type of inflection, either 'plural' or 'singular' + * @param array $rules An array of rules to be added. + * @param boolean $reset If true, will unset default inflections for all + * new rules that are being defined in $rules. + * + * @return void + */ + public static function rules($type, $rules, $reset = false) + { + foreach ($rules as $rule => $pattern) { + if ( ! is_array($pattern)) { + continue; + } + + if ($reset) { + self::${$type}[$rule] = $pattern; + } else { + self::${$type}[$rule] = ($rule === 'uninflected') + ? array_merge($pattern, self::${$type}[$rule]) + : $pattern + self::${$type}[$rule]; + } + + unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]); + + if (isset(self::${$type}['merged'][$rule])) { + unset(self::${$type}['merged'][$rule]); + } + + if ($type === 'plural') { + self::$cache['pluralize'] = self::$cache['tableize'] = array(); + } elseif ($type === 'singular') { + self::$cache['singularize'] = array(); + } + } + + self::${$type}['rules'] = $rules + self::${$type}['rules']; + } + + /** + * Returns a word in plural form. + * + * @param string $word The word in singular form. + * + * @return string The word in plural form. + */ + public static function pluralize($word) + { + if (isset(self::$cache['pluralize'][$word])) { + return self::$cache['pluralize'][$word]; + } + + if (!isset(self::$plural['merged']['irregular'])) { + self::$plural['merged']['irregular'] = self::$plural['irregular']; + } + + if (!isset(self::$plural['merged']['uninflected'])) { + self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected); + } + + if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) { + self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')'; + self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1); + + return self::$cache['pluralize'][$word]; + } + + if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$cache['pluralize'][$word] = $word; + + return $word; + } + + foreach (self::$plural['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word); + + return self::$cache['pluralize'][$word]; + } + } + } + + /** + * Returns a word in singular form. + * + * @param string $word The word in plural form. + * + * @return string The word in singular form. + */ + public static function singularize($word) + { + if (isset(self::$cache['singularize'][$word])) { + return self::$cache['singularize'][$word]; + } + + if (!isset(self::$singular['merged']['uninflected'])) { + self::$singular['merged']['uninflected'] = array_merge( + self::$singular['uninflected'], + self::$uninflected + ); + } + + if (!isset(self::$singular['merged']['irregular'])) { + self::$singular['merged']['irregular'] = array_merge( + self::$singular['irregular'], + array_flip(self::$plural['irregular']) + ); + } + + if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) { + self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')'; + self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1); + + return self::$cache['singularize'][$word]; + } + + if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $word; + + return $word; + } + + foreach (self::$singular['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word); + + return self::$cache['singularize'][$word]; + } + } + + self::$cache['singularize'][$word] = $word; + + return $word; + } +} diff --git a/vendor/doctrine/inflector/phpunit.xml.dist b/vendor/doctrine/inflector/phpunit.xml.dist new file mode 100644 index 0000000..ef07faa --- /dev/null +++ b/vendor/doctrine/inflector/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php b/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php new file mode 100644 index 0000000..4198d22 --- /dev/null +++ b/vendor/doctrine/inflector/tests/Doctrine/Tests/Common/Inflector/InflectorTest.php @@ -0,0 +1,309 @@ +assertEquals( + $singular, + Inflector::singularize($plural), + "'$plural' should be singularized to '$singular'" + ); + } + + /** + * testInflectingPlurals method + * + * @dataProvider dataSampleWords + * @return void + */ + public function testInflectingPlurals($singular, $plural) + { + $this->assertEquals( + $plural, + Inflector::pluralize($singular), + "'$singular' should be pluralized to '$plural'" + ); + } + + /** + * testCustomPluralRule method + * + * @return void + */ + public function testCustomPluralRule() + { + Inflector::reset(); + Inflector::rules('plural', array('/^(custom)$/i' => '\1izables')); + + $this->assertEquals(Inflector::pluralize('custom'), 'customizables'); + + Inflector::rules('plural', array('uninflected' => array('uninflectable'))); + + $this->assertEquals(Inflector::pluralize('uninflectable'), 'uninflectable'); + + Inflector::rules('plural', array( + 'rules' => array('/^(alert)$/i' => '\1ables'), + 'uninflected' => array('noflect', 'abtuse'), + 'irregular' => array('amaze' => 'amazable', 'phone' => 'phonezes') + )); + + $this->assertEquals(Inflector::pluralize('noflect'), 'noflect'); + $this->assertEquals(Inflector::pluralize('abtuse'), 'abtuse'); + $this->assertEquals(Inflector::pluralize('alert'), 'alertables'); + $this->assertEquals(Inflector::pluralize('amaze'), 'amazable'); + $this->assertEquals(Inflector::pluralize('phone'), 'phonezes'); + } + + /** + * testCustomSingularRule method + * + * @return void + */ + public function testCustomSingularRule() + { + Inflector::reset(); + Inflector::rules('singular', array('/(eple)r$/i' => '\1', '/(jente)r$/i' => '\1')); + + $this->assertEquals(Inflector::singularize('epler'), 'eple'); + $this->assertEquals(Inflector::singularize('jenter'), 'jente'); + + Inflector::rules('singular', array( + 'rules' => array('/^(bil)er$/i' => '\1', '/^(inflec|contribu)tors$/i' => '\1ta'), + 'uninflected' => array('singulars'), + 'irregular' => array('spins' => 'spinor') + )); + + $this->assertEquals(Inflector::singularize('inflectors'), 'inflecta'); + $this->assertEquals(Inflector::singularize('contributors'), 'contributa'); + $this->assertEquals(Inflector::singularize('spins'), 'spinor'); + $this->assertEquals(Inflector::singularize('singulars'), 'singulars'); + } + + /** + * test that setting new rules clears the inflector caches. + * + * @return void + */ + public function testRulesClearsCaches() + { + Inflector::reset(); + + $this->assertEquals(Inflector::singularize('Bananas'), 'Banana'); + $this->assertEquals(Inflector::pluralize('Banana'), 'Bananas'); + + Inflector::rules('singular', array( + 'rules' => array('/(.*)nas$/i' => '\1zzz') + )); + + $this->assertEquals('Banazzz', Inflector::singularize('Bananas'), 'Was inflected with old rules.'); + + Inflector::rules('plural', array( + 'rules' => array('/(.*)na$/i' => '\1zzz'), + 'irregular' => array('corpus' => 'corpora') + )); + + $this->assertEquals(Inflector::pluralize('Banana'), 'Banazzz', 'Was inflected with old rules.'); + $this->assertEquals(Inflector::pluralize('corpus'), 'corpora', 'Was inflected with old irregular form.'); + } + + /** + * Test resetting inflection rules. + * + * @return void + */ + public function testCustomRuleWithReset() + { + Inflector::reset(); + + $uninflected = array('atlas', 'lapis', 'onibus', 'pires', 'virus', '.*x'); + $pluralIrregular = array('as' => 'ases'); + + Inflector::rules('singular', array( + 'rules' => array('/^(.*)(a|e|o|u)is$/i' => '\1\2l'), + 'uninflected' => $uninflected, + ), true); + + Inflector::rules('plural', array( + 'rules' => array( + '/^(.*)(a|e|o|u)l$/i' => '\1\2is', + ), + 'uninflected' => $uninflected, + 'irregular' => $pluralIrregular + ), true); + + $this->assertEquals(Inflector::pluralize('Alcool'), 'Alcoois'); + $this->assertEquals(Inflector::pluralize('Atlas'), 'Atlas'); + $this->assertEquals(Inflector::singularize('Alcoois'), 'Alcool'); + $this->assertEquals(Inflector::singularize('Atlas'), 'Atlas'); + } + + /** + * Test basic ucwords functionality. + * + * @return void + */ + public function testUcwords() + { + $this->assertSame('Top-O-The-Morning To All_of_you!', Inflector::ucwords( 'top-o-the-morning to all_of_you!')); + } + + /** + * Test ucwords functionality with custom delimeters. + * + * @return void + */ + public function testUcwordsWithCustomDelimeters() + { + $this->assertSame('Top-O-The-Morning To All_Of_You!', Inflector::ucwords( 'top-o-the-morning to all_of_you!', '-_ ')); + } +} + diff --git a/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php b/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php new file mode 100644 index 0000000..e8323d2 --- /dev/null +++ b/vendor/doctrine/inflector/tests/Doctrine/Tests/DoctrineTestCase.php @@ -0,0 +1,10 @@ + composer-installer.php + hhvm composer-installer.php + hhvm -v ResourceLimit.SocketDefaultTimeout=30 -v Http.SlowQueryThreshold=30000 composer.phar update --prefer-source +elif [ "$TRAVIS_PHP_VERSION" = '5.3.3' ] ; then + composer self-update + composer update --prefer-source --no-dev + composer dump-autoload +else + composer self-update + composer update --prefer-source +fi diff --git a/vendor/doctrine/instantiator/.travis.yml b/vendor/doctrine/instantiator/.travis.yml new file mode 100644 index 0000000..7f1ec5f --- /dev/null +++ b/vendor/doctrine/instantiator/.travis.yml @@ -0,0 +1,22 @@ +language: php + +php: + - 5.3.3 + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - ./.travis.install.sh + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then PHPUNIT_FLAGS="--coverage-clover coverage.clover"; else PHPUNIT_FLAGS=""; fi + +script: + - if [ $TRAVIS_PHP_VERSION = '5.3.3' ]; then phpunit; fi + - if [ $TRAVIS_PHP_VERSION != '5.3.3' ]; then ./vendor/bin/phpunit $PHPUNIT_FLAGS; fi + - if [ $TRAVIS_PHP_VERSION != '5.3.3' ]; then ./vendor/bin/phpcs --standard=PSR2 ./src/ ./tests/; fi + - if [[ $TRAVIS_PHP_VERSION != '5.3.3' && $TRAVIS_PHP_VERSION != '5.4.29' && $TRAVIS_PHP_VERSION != '5.5.13' ]]; then php -n ./vendor/bin/athletic -p ./tests/DoctrineTest/InstantiatorPerformance/ -f GroupedFormatter; fi + +after_script: + - if [ $TRAVIS_PHP_VERSION = '5.6' ]; then wget https://scrutinizer-ci.com/ocular.phar; php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/vendor/doctrine/instantiator/CONTRIBUTING.md b/vendor/doctrine/instantiator/CONTRIBUTING.md new file mode 100644 index 0000000..75b84b2 --- /dev/null +++ b/vendor/doctrine/instantiator/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing + + * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) + * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) + * Any contribution must provide tests for additional introduced conditions + * Any un-confirmed issue needs a failing test case before being accepted + * Pull requests must be sent from a new hotfix/feature branch, not from `master`. + +## Installation + +To install the project and run the tests, you need to clone it first: + +```sh +$ git clone git://github.com/doctrine/instantiator.git +``` + +You will then need to run a composer installation: + +```sh +$ cd Instantiator +$ curl -s https://getcomposer.org/installer | php +$ php composer.phar update +``` + +## Testing + +The PHPUnit version to be used is the one installed as a dev- dependency via composer: + +```sh +$ ./vendor/bin/phpunit +``` + +Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement +won't be merged. + diff --git a/vendor/doctrine/instantiator/LICENSE b/vendor/doctrine/instantiator/LICENSE new file mode 100644 index 0000000..4d983d1 --- /dev/null +++ b/vendor/doctrine/instantiator/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/instantiator/README.md b/vendor/doctrine/instantiator/README.md new file mode 100644 index 0000000..393ec7c --- /dev/null +++ b/vendor/doctrine/instantiator/README.md @@ -0,0 +1,40 @@ +# Instantiator + +This library provides a way of avoiding usage of constructors when instantiating PHP classes. + +[![Build Status](https://travis-ci.org/doctrine/instantiator.svg?branch=master)](https://travis-ci.org/doctrine/instantiator) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/instantiator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/instantiator/?branch=master) +[![Dependency Status](https://www.versioneye.com/package/php--doctrine--instantiator/badge.svg)](https://www.versioneye.com/package/php--doctrine--instantiator) +[![HHVM Status](http://hhvm.h4cc.de/badge/doctrine/instantiator.png)](http://hhvm.h4cc.de/package/doctrine/instantiator) + +[![Latest Stable Version](https://poser.pugx.org/doctrine/instantiator/v/stable.png)](https://packagist.org/packages/doctrine/instantiator) +[![Latest Unstable Version](https://poser.pugx.org/doctrine/instantiator/v/unstable.png)](https://packagist.org/packages/doctrine/instantiator) + +## Installation + +The suggested installation method is via [composer](https://getcomposer.org/): + +```sh +php composer.phar require "doctrine/instantiator:~1.0.3" +``` + +## Usage + +The instantiator is able to create new instances of any class without using the constructor or any API of the class +itself: + +```php +$instantiator = new \Doctrine\Instantiator\Instantiator(); + +$instance = $instantiator->instantiate('My\\ClassName\\Here'); +``` + +## Contributing + +Please read the [CONTRIBUTING.md](CONTRIBUTING.md) contents if you wish to help out! + +## Credits + +This library was migrated from [ocramius/instantiator](https://github.com/Ocramius/Instantiator), which +has been donated to the doctrine organization, and which is now deprecated in favour of this package. diff --git a/vendor/doctrine/instantiator/composer.json b/vendor/doctrine/instantiator/composer.json new file mode 100644 index 0000000..4823890 --- /dev/null +++ b/vendor/doctrine/instantiator/composer.json @@ -0,0 +1,45 @@ +{ + "name": "doctrine/instantiator", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "type": "library", + "license": "MIT", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "instantiate", + "constructor" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "ext-phar": "*", + "ext-pdo": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0", + "athletic/athletic": "~0.1.8" + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "autoload-dev": { + "psr-0": { + "DoctrineTest\\InstantiatorPerformance\\": "tests", + "DoctrineTest\\InstantiatorTest\\": "tests", + "DoctrineTest\\InstantiatorTestAsset\\": "tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/instantiator/phpmd.xml.dist b/vendor/doctrine/instantiator/phpmd.xml.dist new file mode 100644 index 0000000..8254105 --- /dev/null +++ b/vendor/doctrine/instantiator/phpmd.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/instantiator/phpunit.xml.dist b/vendor/doctrine/instantiator/phpunit.xml.dist new file mode 100644 index 0000000..0a8d570 --- /dev/null +++ b/vendor/doctrine/instantiator/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + ./tests/DoctrineTest/InstantiatorTest + + + + ./src + + + diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php new file mode 100644 index 0000000..3065375 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +/** + * Base exception marker interface for the instantiator component + * + * @author Marco Pivetta + */ +interface ExceptionInterface +{ +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..ea8d28c --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +use InvalidArgumentException as BaseInvalidArgumentException; +use ReflectionClass; + +/** + * Exception for invalid arguments provided to the instantiator + * + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface +{ + /** + * @param string $className + * + * @return self + */ + public static function fromNonExistingClass($className) + { + if (interface_exists($className)) { + return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); + } + + if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { + return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); + } + + return new self(sprintf('The provided class "%s" does not exist', $className)); + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return self + */ + public static function fromAbstractClass(ReflectionClass $reflectionClass) + { + return new self(sprintf( + 'The provided class "%s" is abstract, and can not be instantiated', + $reflectionClass->getName() + )); + } +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php new file mode 100644 index 0000000..1681e56 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Instantiator\Exception; + +use Exception; +use ReflectionClass; +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Exception for given parameters causing invalid/unexpected state on instantiation + * + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface +{ + /** + * @param ReflectionClass $reflectionClass + * @param Exception $exception + * + * @return self + */ + public static function fromSerializationTriggeredException(ReflectionClass $reflectionClass, Exception $exception) + { + return new self( + sprintf( + 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', + $reflectionClass->getName() + ), + 0, + $exception + ); + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $errorString + * @param int $errorCode + * @param string $errorFile + * @param int $errorLine + * + * @return UnexpectedValueException + */ + public static function fromUncleanUnSerialization( + ReflectionClass $reflectionClass, + $errorString, + $errorCode, + $errorFile, + $errorLine + ) { + return new self( + sprintf( + 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' + . 'in file "%s" at line "%d"', + $reflectionClass->getName(), + $errorFile, + $errorLine + ), + 0, + new Exception($errorString, $errorCode) + ); + } +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php new file mode 100644 index 0000000..6d5b3b6 --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php @@ -0,0 +1,273 @@ +. + */ + +namespace Doctrine\Instantiator; + +use Closure; +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use ReflectionClass; + +/** + * {@inheritDoc} + * + * @author Marco Pivetta + */ +final class Instantiator implements InstantiatorInterface +{ + /** + * Markers used internally by PHP to define whether {@see \unserialize} should invoke + * the method {@see \Serializable::unserialize()} when dealing with classes implementing + * the {@see \Serializable} interface. + */ + const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; + const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; + + /** + * @var \Closure[] of {@see \Closure} instances used to instantiate specific classes + */ + private static $cachedInstantiators = array(); + + /** + * @var object[] of objects that can directly be cloned + */ + private static $cachedCloneables = array(); + + /** + * {@inheritDoc} + */ + public function instantiate($className) + { + if (isset(self::$cachedCloneables[$className])) { + return clone self::$cachedCloneables[$className]; + } + + if (isset(self::$cachedInstantiators[$className])) { + $factory = self::$cachedInstantiators[$className]; + + return $factory(); + } + + return $this->buildAndCacheFromFactory($className); + } + + /** + * Builds the requested object and caches it in static properties for performance + * + * @param string $className + * + * @return object + */ + private function buildAndCacheFromFactory($className) + { + $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); + $instance = $factory(); + + if ($this->isSafeToClone(new ReflectionClass($instance))) { + self::$cachedCloneables[$className] = clone $instance; + } + + return $instance; + } + + /** + * Builds a {@see \Closure} capable of instantiating the given $className without + * invoking its constructor. + * + * @param string $className + * + * @return Closure + */ + private function buildFactory($className) + { + $reflectionClass = $this->getReflectionClass($className); + + if ($this->isInstantiableViaReflection($reflectionClass)) { + return function () use ($reflectionClass) { + return $reflectionClass->newInstanceWithoutConstructor(); + }; + } + + $serializedString = sprintf( + '%s:%d:"%s":0:{}', + $this->getSerializationFormat($reflectionClass), + strlen($className), + $className + ); + + $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); + + return function () use ($serializedString) { + return unserialize($serializedString); + }; + } + + /** + * @param string $className + * + * @return ReflectionClass + * + * @throws InvalidArgumentException + */ + private function getReflectionClass($className) + { + if (! class_exists($className)) { + throw InvalidArgumentException::fromNonExistingClass($className); + } + + $reflection = new ReflectionClass($className); + + if ($reflection->isAbstract()) { + throw InvalidArgumentException::fromAbstractClass($reflection); + } + + return $reflection; + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) + { + set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) { + $error = UnexpectedValueException::fromUncleanUnSerialization( + $reflectionClass, + $message, + $code, + $file, + $line + ); + }); + + $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); + + restore_error_handler(); + + if ($error) { + throw $error; + } + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) + { + try { + unserialize($serializedString); + } catch (Exception $exception) { + restore_error_handler(); + + throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); + } + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function isInstantiableViaReflection(ReflectionClass $reflectionClass) + { + if (\PHP_VERSION_ID >= 50600) { + return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); + } + + return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass); + } + + /** + * Verifies whether the given class is to be considered internal + * + * @param ReflectionClass $reflectionClass + * + * @return bool + */ + private function hasInternalAncestors(ReflectionClass $reflectionClass) + { + do { + if ($reflectionClass->isInternal()) { + return true; + } + } while ($reflectionClass = $reflectionClass->getParentClass()); + + return false; + } + + /** + * Verifies if the given PHP version implements the `Serializable` interface serialization + * with an incompatible serialization format. If that's the case, use serialization marker + * "C" instead of "O". + * + * @link http://news.php.net/php.internals/74654 + * + * @param ReflectionClass $reflectionClass + * + * @return string the serialization format marker, either self::SERIALIZATION_FORMAT_USE_UNSERIALIZER + * or self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER + */ + private function getSerializationFormat(ReflectionClass $reflectionClass) + { + if ($this->isPhpVersionWithBrokenSerializationFormat() + && $reflectionClass->implementsInterface('Serializable') + ) { + return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER; + } + + return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER; + } + + /** + * Checks whether the current PHP runtime uses an incompatible serialization format + * + * @return bool + */ + private function isPhpVersionWithBrokenSerializationFormat() + { + return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513; + } + + /** + * Checks if a class is cloneable + * + * @param ReflectionClass $reflection + * + * @return bool + */ + private function isSafeToClone(ReflectionClass $reflection) + { + if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) { + return false; + } + + // not cloneable if it implements `__clone`, as we want to avoid calling it + return ! $reflection->hasMethod('__clone'); + } +} diff --git a/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php new file mode 100644 index 0000000..b665bea --- /dev/null +++ b/vendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Instantiator; + +/** + * Instantiator provides utility methods to build objects without invoking their constructors + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * @param string $className + * + * @return object + * + * @throws \Doctrine\Instantiator\Exception\ExceptionInterface + */ + public function instantiate($className); +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php new file mode 100644 index 0000000..3e8fc6f --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorPerformance/InstantiatorPerformanceEvent.php @@ -0,0 +1,96 @@ +. + */ + +namespace DoctrineTest\InstantiatorPerformance; + +use Athletic\AthleticEvent; +use Doctrine\Instantiator\Instantiator; + +/** + * Performance tests for {@see \Doctrine\Instantiator\Instantiator} + * + * @author Marco Pivetta + */ +class InstantiatorPerformanceEvent extends AthleticEvent +{ + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->instantiator = new Instantiator(); + + $this->instantiator->instantiate(__CLASS__); + $this->instantiator->instantiate('ArrayObject'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'); + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'); + } + + /** + * @iterations 20000 + * @baseline + * @group instantiation + */ + public function testInstantiateSelf() + { + $this->instantiator->instantiate(__CLASS__); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateInternalClass() + { + $this->instantiator->instantiate('ArrayObject'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateSimpleSerializableAssetClass() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateSerializableArrayObjectAsset() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'); + } + + /** + * @iterations 20000 + * @group instantiation + */ + public function testInstantiateUnCloneableAsset() + { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php new file mode 100644 index 0000000..39d9b94 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,83 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest\Exception; + +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Exception\InvalidArgumentException} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Exception\InvalidArgumentException + */ +class InvalidArgumentExceptionTest extends PHPUnit_Framework_TestCase +{ + public function testFromNonExistingTypeWithNonExistingClass() + { + $className = __CLASS__ . uniqid(); + $exception = InvalidArgumentException::fromNonExistingClass($className); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\InvalidArgumentException', $exception); + $this->assertSame('The provided class "' . $className . '" does not exist', $exception->getMessage()); + } + + public function testFromNonExistingTypeWithTrait() + { + if (PHP_VERSION_ID < 50400) { + $this->markTestSkipped('Need at least PHP 5.4.0, as this test requires traits support to run'); + } + + $exception = InvalidArgumentException::fromNonExistingClass( + 'DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset' + ); + + $this->assertSame( + 'The provided type "DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset" is a trait, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } + + public function testFromNonExistingTypeWithInterface() + { + $exception = InvalidArgumentException::fromNonExistingClass('Doctrine\\Instantiator\\InstantiatorInterface'); + + $this->assertSame( + 'The provided type "Doctrine\\Instantiator\\InstantiatorInterface" is an interface, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } + + public function testFromAbstractClass() + { + $reflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'); + $exception = InvalidArgumentException::fromAbstractClass($reflection); + + $this->assertSame( + 'The provided class "DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset" is abstract, ' + . 'and can not be instantiated', + $exception->getMessage() + ); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php new file mode 100644 index 0000000..84154e7 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/Exception/UnexpectedValueExceptionTest.php @@ -0,0 +1,69 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest\Exception; + +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Exception\UnexpectedValueException} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Exception\UnexpectedValueException + */ +class UnexpectedValueExceptionTest extends PHPUnit_Framework_TestCase +{ + public function testFromSerializationTriggeredException() + { + $reflectionClass = new ReflectionClass($this); + $previous = new Exception(); + $exception = UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $previous); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\UnexpectedValueException', $exception); + $this->assertSame($previous, $exception->getPrevious()); + $this->assertSame( + 'An exception was raised while trying to instantiate an instance of "' + . __CLASS__ . '" via un-serialization', + $exception->getMessage() + ); + } + + public function testFromUncleanUnSerialization() + { + $reflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'); + $exception = UnexpectedValueException::fromUncleanUnSerialization($reflection, 'foo', 123, 'bar', 456); + + $this->assertInstanceOf('Doctrine\\Instantiator\\Exception\\UnexpectedValueException', $exception); + $this->assertSame( + 'Could not produce an instance of "DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset" ' + . 'via un-serialization, since an error was triggered in file "bar" at line "456"', + $exception->getMessage() + ); + + $previous = $exception->getPrevious(); + + $this->assertInstanceOf('Exception', $previous); + $this->assertSame('foo', $previous->getMessage()); + $this->assertSame(123, $previous->getCode()); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php new file mode 100644 index 0000000..0a2cb93 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTest/InstantiatorTest.php @@ -0,0 +1,219 @@ +. + */ + +namespace DoctrineTest\InstantiatorTest; + +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Doctrine\Instantiator\Instantiator; +use PHPUnit_Framework_TestCase; +use ReflectionClass; + +/** + * Tests for {@see \Doctrine\Instantiator\Instantiator} + * + * @author Marco Pivetta + * + * @covers \Doctrine\Instantiator\Instantiator + */ +class InstantiatorTest extends PHPUnit_Framework_TestCase +{ + /** + * @var Instantiator + */ + private $instantiator; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->instantiator = new Instantiator(); + } + + /** + * @param string $className + * + * @dataProvider getInstantiableClasses + */ + public function testCanInstantiate($className) + { + $this->assertInstanceOf($className, $this->instantiator->instantiate($className)); + } + + /** + * @param string $className + * + * @dataProvider getInstantiableClasses + */ + public function testInstantiatesSeparateInstances($className) + { + $instance1 = $this->instantiator->instantiate($className); + $instance2 = $this->instantiator->instantiate($className); + + $this->assertEquals($instance1, $instance2); + $this->assertNotSame($instance1, $instance2); + } + + public function testExceptionOnUnSerializationException() + { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped( + 'As of facebook/hhvm#3432, HHVM has no PDORow, and therefore ' + . ' no internal final classes that cannot be instantiated' + ); + } + + $className = 'DoctrineTest\\InstantiatorTestAsset\\UnserializeExceptionArrayObjectAsset'; + + if (\PHP_VERSION_ID >= 50600) { + $className = 'PDORow'; + } + + if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { + $className = 'DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'; + } + + $this->setExpectedException('Doctrine\\Instantiator\\Exception\\UnexpectedValueException'); + + $this->instantiator->instantiate($className); + } + + public function testNoticeOnUnSerializationException() + { + if (\PHP_VERSION_ID >= 50600) { + $this->markTestSkipped( + 'PHP 5.6 supports `ReflectionClass#newInstanceWithoutConstructor()` for some internal classes' + ); + } + + try { + $this->instantiator->instantiate('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + + $this->fail('No exception was raised'); + } catch (UnexpectedValueException $exception) { + $wakeUpNoticesReflection = new ReflectionClass('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + $previous = $exception->getPrevious(); + + $this->assertInstanceOf('Exception', $previous); + + // in PHP 5.4.29 and PHP 5.5.13, this case is not a notice, but an exception being thrown + if (! (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513)) { + $this->assertSame( + 'Could not produce an instance of "DoctrineTest\\InstantiatorTestAsset\WakeUpNoticesAsset" ' + . 'via un-serialization, since an error was triggered in file "' + . $wakeUpNoticesReflection->getFileName() . '" at line "36"', + $exception->getMessage() + ); + + $this->assertSame('Something went bananas while un-serializing this instance', $previous->getMessage()); + $this->assertSame(\E_USER_NOTICE, $previous->getCode()); + } + } + } + + /** + * @param string $invalidClassName + * + * @dataProvider getInvalidClassNames + */ + public function testInstantiationFromNonExistingClass($invalidClassName) + { + $this->setExpectedException('Doctrine\\Instantiator\\Exception\\InvalidArgumentException'); + + $this->instantiator->instantiate($invalidClassName); + } + + public function testInstancesAreNotCloned() + { + $className = 'TemporaryClass' . uniqid(); + + eval('namespace ' . __NAMESPACE__ . '; class ' . $className . '{}'); + + $instance = $this->instantiator->instantiate(__NAMESPACE__ . '\\' . $className); + + $instance->foo = 'bar'; + + $instance2 = $this->instantiator->instantiate(__NAMESPACE__ . '\\' . $className); + + $this->assertObjectNotHasAttribute('foo', $instance2); + } + + /** + * Provides a list of instantiable classes (existing) + * + * @return string[][] + */ + public function getInstantiableClasses() + { + $classes = array( + array('stdClass'), + array(__CLASS__), + array('Doctrine\\Instantiator\\Instantiator'), + array('Exception'), + array('PharException'), + array('DoctrineTest\\InstantiatorTestAsset\\SimpleSerializableAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\ExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\FinalExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\PharExceptionAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\UnCloneableAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\XMLReaderAsset'), + ); + + if (\PHP_VERSION_ID === 50429 || \PHP_VERSION_ID === 50513) { + return $classes; + } + + $classes = array_merge( + $classes, + array( + array('PharException'), + array('ArrayObject'), + array('DoctrineTest\\InstantiatorTestAsset\\ArrayObjectAsset'), + array('DoctrineTest\\InstantiatorTestAsset\\SerializableArrayObjectAsset'), + ) + ); + + if (\PHP_VERSION_ID >= 50600) { + $classes[] = array('DoctrineTest\\InstantiatorTestAsset\\WakeUpNoticesAsset'); + $classes[] = array('DoctrineTest\\InstantiatorTestAsset\\UnserializeExceptionArrayObjectAsset'); + } + + return $classes; + } + + /** + * Provides a list of instantiable classes (existing) + * + * @return string[][] + */ + public function getInvalidClassNames() + { + $classNames = array( + array(__CLASS__ . uniqid()), + array('Doctrine\\Instantiator\\InstantiatorInterface'), + array('DoctrineTest\\InstantiatorTestAsset\\AbstractClassAsset'), + ); + + if (\PHP_VERSION_ID >= 50400) { + $classNames[] = array('DoctrineTest\\InstantiatorTestAsset\\SimpleTraitAsset'); + } + + return $classNames; + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php new file mode 100644 index 0000000..fbe28dd --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/AbstractClassAsset.php @@ -0,0 +1,29 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +abstract class AbstractClassAsset +{ +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php new file mode 100644 index 0000000..56146d7 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ArrayObjectAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; + +/** + * Test asset that extends an internal PHP class + * + * @author Marco Pivetta + */ +class ArrayObjectAsset extends ArrayObject +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php new file mode 100644 index 0000000..43bbe46 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/ExceptionAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Exception; + +/** + * Test asset that extends an internal PHP base exception + * + * @author Marco Pivetta + */ +class ExceptionAsset extends Exception +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php new file mode 100644 index 0000000..7d268f5 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/FinalExceptionAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Exception; + +/** + * Test asset that extends an internal PHP base exception + * + * @author Marco Pivetta + */ +final class FinalExceptionAsset extends Exception +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php new file mode 100644 index 0000000..553fd56 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Phar; + +/** + * Test asset that extends an internal PHP class + * + * @author Marco Pivetta + */ +class PharAsset extends Phar +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php new file mode 100644 index 0000000..42bf73e --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/PharExceptionAsset.php @@ -0,0 +1,44 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use PharException; + +/** + * Test asset that extends an internal PHP class + * This class should be serializable without problems + * and without getting the "Erroneous data format for unserializing" + * error + * + * @author Marco Pivetta + */ +class PharExceptionAsset extends PharException +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php new file mode 100644 index 0000000..ba19aaf --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SerializableArrayObjectAsset.php @@ -0,0 +1,62 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; +use Serializable; + +/** + * Serializable test asset that also extends an internal class + * + * @author Marco Pivetta + */ +class SerializableArrayObjectAsset extends ArrayObject implements Serializable +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritDoc} + * + * Should not be called + * + * @throws BadMethodCallException + */ + public function unserialize($serialized) + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php new file mode 100644 index 0000000..39f84a6 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleSerializableAsset.php @@ -0,0 +1,61 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use Serializable; + +/** + * Base serializable test asset + * + * @author Marco Pivetta + */ +class SimpleSerializableAsset implements Serializable +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * {@inheritDoc} + */ + public function serialize() + { + return ''; + } + + /** + * {@inheritDoc} + * + * Should not be called + * + * @throws BadMethodCallException + */ + public function unserialize($serialized) + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php new file mode 100644 index 0000000..04e7806 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/SimpleTraitAsset.php @@ -0,0 +1,29 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +/** + * A simple trait with no attached logic + * + * @author Marco Pivetta + */ +trait SimpleTraitAsset +{ +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php new file mode 100644 index 0000000..7d03bda --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnCloneableAsset.php @@ -0,0 +1,50 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; + +/** + * Base un-cloneable asset + * + * @author Marco Pivetta + */ +class UnCloneableAsset +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } + + /** + * Magic `__clone` - should not be invoked + * + * @throws BadMethodCallException + */ + public function __clone() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php new file mode 100644 index 0000000..b348a40 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/UnserializeExceptionArrayObjectAsset.php @@ -0,0 +1,39 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; +use BadMethodCallException; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +class UnserializeExceptionArrayObjectAsset extends ArrayObject +{ + /** + * {@inheritDoc} + */ + public function __wakeup() + { + throw new BadMethodCallException(); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php new file mode 100644 index 0000000..18dc671 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/WakeUpNoticesAsset.php @@ -0,0 +1,38 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use ArrayObject; + +/** + * A simple asset for an abstract class + * + * @author Marco Pivetta + */ +class WakeUpNoticesAsset extends ArrayObject +{ + /** + * Wakeup method called after un-serialization + */ + public function __wakeup() + { + trigger_error('Something went bananas while un-serializing this instance'); + } +} diff --git a/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php new file mode 100644 index 0000000..39ee699 --- /dev/null +++ b/vendor/doctrine/instantiator/tests/DoctrineTest/InstantiatorTestAsset/XMLReaderAsset.php @@ -0,0 +1,41 @@ +. + */ + +namespace DoctrineTest\InstantiatorTestAsset; + +use BadMethodCallException; +use XMLReader; + +/** + * Test asset that extends an internal PHP class + * + * @author Dave Marshall + */ +class XMLReaderAsset extends XMLReader +{ + /** + * Constructor - should not be called + * + * @throws BadMethodCallException + */ + public function __construct() + { + throw new BadMethodCallException('Not supposed to be called!'); + } +} diff --git a/vendor/doctrine/lexer/LICENSE b/vendor/doctrine/lexer/LICENSE new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/vendor/doctrine/lexer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/lexer/README.md b/vendor/doctrine/lexer/README.md new file mode 100644 index 0000000..66f4430 --- /dev/null +++ b/vendor/doctrine/lexer/README.md @@ -0,0 +1,5 @@ +# Doctrine Lexer + +Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers. + +This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL). diff --git a/vendor/doctrine/lexer/composer.json b/vendor/doctrine/lexer/composer.json new file mode 100644 index 0000000..8cd694c --- /dev/null +++ b/vendor/doctrine/lexer/composer.json @@ -0,0 +1,24 @@ +{ + "name": "doctrine/lexer", + "type": "library", + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "keywords": ["lexer", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php new file mode 100644 index 0000000..399a552 --- /dev/null +++ b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php @@ -0,0 +1,327 @@ +. + */ + +namespace Doctrine\Common\Lexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractLexer +{ + /** + * Lexer original input string. + * + * @var string + */ + private $input; + + /** + * Array of scanned tokens. + * + * Each token is an associative array containing three items: + * - 'value' : the string value of the token in the input string + * - 'type' : the type of the token (identifier, numeric, string, input + * parameter, none) + * - 'position' : the position of the token in the input string + * + * @var array + */ + private $tokens = array(); + + /** + * Current lexer position in input string. + * + * @var integer + */ + private $position = 0; + + /** + * Current peek of current lexer position. + * + * @var integer + */ + private $peek = 0; + + /** + * The next token in the input. + * + * @var array + */ + public $lookahead; + + /** + * The last matched/seen token. + * + * @var array + */ + public $token; + + /** + * Sets the input data to be tokenized. + * + * The Lexer is immediately reset and the new input tokenized. + * Any unprocessed tokens from any previous input are lost. + * + * @param string $input The input to be tokenized. + * + * @return void + */ + public function setInput($input) + { + $this->input = $input; + $this->tokens = array(); + + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + * + * @return void + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + * + * @return void + */ + public function resetPeek() + { + $this->peek = 0; + } + + /** + * Resets the lexer position on the input to the given position. + * + * @param integer $position Position to place the lexical scanner. + * + * @return void + */ + public function resetPosition($position = 0) + { + $this->position = $position; + } + + /** + * Retrieve the original lexer's input until a given position. + * + * @param integer $position + * + * @return string + */ + public function getInputUntilPosition($position) + { + return substr($this->input, 0, $position); + } + + /** + * Checks whether a given token matches the current lookahead. + * + * @param integer|string $token + * + * @return boolean + */ + public function isNextToken($token) + { + return null !== $this->lookahead && $this->lookahead['type'] === $token; + } + + /** + * Checks whether any of the given tokens matches the current lookahead. + * + * @param array $tokens + * + * @return boolean + */ + public function isNextTokenAny(array $tokens) + { + return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true); + } + + /** + * Moves to the next token in the input string. + * + * @return boolean + */ + public function moveNext() + { + $this->peek = 0; + $this->token = $this->lookahead; + $this->lookahead = (isset($this->tokens[$this->position])) + ? $this->tokens[$this->position++] : null; + + return $this->lookahead !== null; + } + + /** + * Tells the lexer to skip input tokens until it sees a token with the given value. + * + * @param string $type The token type to skip until. + * + * @return void + */ + public function skipUntil($type) + { + while ($this->lookahead !== null && $this->lookahead['type'] !== $type) { + $this->moveNext(); + } + } + + /** + * Checks if given value is identical to the given token. + * + * @param mixed $value + * @param integer $token + * + * @return boolean + */ + public function isA($value, $token) + { + return $this->getType($value) === $token; + } + + /** + * Moves the lookahead token forward. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function peek() + { + if (isset($this->tokens[$this->position + $this->peek])) { + return $this->tokens[$this->position + $this->peek++]; + } else { + return null; + } + } + + /** + * Peeks at the next token, returns it and immediately resets the peek. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function glimpse() + { + $peek = $this->peek(); + $this->peek = 0; + return $peek; + } + + /** + * Scans the input string for tokens. + * + * @param string $input A query string. + * + * @return void + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = sprintf( + '/(%s)|%s/%s', + implode(')|(', $this->getCatchablePatterns()), + implode('|', $this->getNonCatchablePatterns()), + $this->getModifiers() + ); + } + + $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; + $matches = preg_split($regex, $input, -1, $flags); + + foreach ($matches as $match) { + // Must remain before 'value' assignment since it can change content + $type = $this->getType($match[0]); + + $this->tokens[] = array( + 'value' => $match[0], + 'type' => $type, + 'position' => $match[1], + ); + } + } + + /** + * Gets the literal for a given token. + * + * @param integer $token + * + * @return string + */ + public function getLiteral($token) + { + $className = get_class($this); + $reflClass = new \ReflectionClass($className); + $constants = $reflClass->getConstants(); + + foreach ($constants as $name => $value) { + if ($value === $token) { + return $className . '::' . $name; + } + } + + return $token; + } + + /** + * Regex modifiers + * + * @return string + */ + protected function getModifiers() + { + return 'i'; + } + + /** + * Lexical catchable patterns. + * + * @return array + */ + abstract protected function getCatchablePatterns(); + + /** + * Lexical non-catchable patterns. + * + * @return array + */ + abstract protected function getNonCatchablePatterns(); + + /** + * Retrieve token type. Also processes the token value if necessary. + * + * @param string $value + * + * @return integer + */ + abstract protected function getType(&$value); +} diff --git a/vendor/doctrine/orm/.coveralls.yml b/vendor/doctrine/orm/.coveralls.yml new file mode 100644 index 0000000..b086fff --- /dev/null +++ b/vendor/doctrine/orm/.coveralls.yml @@ -0,0 +1,4 @@ +# for php-coveralls +service_name: travis-ci +src_dir: lib +coverage_clover: build/logs/clover*.xml diff --git a/vendor/doctrine/orm/LICENSE b/vendor/doctrine/orm/LICENSE new file mode 100644 index 0000000..4a91f0b --- /dev/null +++ b/vendor/doctrine/orm/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2012 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/orm/README.markdown b/vendor/doctrine/orm/README.markdown new file mode 100644 index 0000000..6cbccaf --- /dev/null +++ b/vendor/doctrine/orm/README.markdown @@ -0,0 +1,30 @@ +| [Master][Master] | [2.4][2.4] | [2.3][2.3] | [2.2][2.2] | [2.1][2.1] | +|:----------------:|:----------:|:----------:|:----------:|:----------:| +| [![Build status][Master image]][Master] | [![Build status][2.4 image]][2.4] | [![Build status][2.3 image]][2.3] | [![Build status][2.2 image]][2.2] | [![Build status][2.1 image]][2.1] | +| [![Coverage Status][Master coverage image]][Master coverage] | + +Doctrine 2 is an object-relational mapper (ORM) for PHP 5.4+ that provides transparent persistence +for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features +is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), +inspired by Hibernate's HQL. This provides developers with a powerful alternative to SQL that maintains flexibility +without requiring unnecessary code duplication. + +## More resources: + +* [Website](http://www.doctrine-project.org) +* [Documentation](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html) +* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC) +* [Downloads](http://github.com/doctrine/doctrine2/downloads) + + [Master image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=master + [Master]: https://travis-ci.org/doctrine/doctrine2 + [Master coverage image]: https://coveralls.io/repos/doctrine/doctrine2/badge.png?branch=master + [Master coverage]: https://coveralls.io/r/doctrine/doctrine2?branch=master + [2.4 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.4 + [2.4]: https://github.com/doctrine/doctrine2/tree/2.4 + [2.3 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.3 + [2.3]: https://github.com/doctrine/doctrine2/tree/2.3 + [2.2 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.2 + [2.2]: https://github.com/doctrine/doctrine2/tree/2.2 + [2.1 image]: https://travis-ci.org/doctrine/doctrine2.svg?branch=2.1.x + [2.1]: https://github.com/doctrine/doctrine2/tree/2.1.x diff --git a/vendor/doctrine/orm/SECURITY.md b/vendor/doctrine/orm/SECURITY.md new file mode 100644 index 0000000..3139002 --- /dev/null +++ b/vendor/doctrine/orm/SECURITY.md @@ -0,0 +1,18 @@ +Security +======== + +The Doctrine library is operating very close to your database and as such needs +to handle and make assumptions about SQL injection vulnerabilities. + +It is vital that you understand how Doctrine approaches security, because +we cannot protect you from SQL injection. + +Please read the documentation chapter on Security in Doctrine DBAL and ORM to +understand the assumptions we make. + +- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst) +- [ORM Security Page](https://github.com/doctrine/doctrine2/blob/master/docs/en/reference/security.rst) + +If you find a Security bug in Doctrine, please report it on Jira and change the +Security Level to "Security Issues". It will be visible to Doctrine Core +developers and you only. diff --git a/vendor/doctrine/orm/bin/doctrine b/vendor/doctrine/orm/bin/doctrine new file mode 100755 index 0000000..c359ca7 --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine @@ -0,0 +1,4 @@ +#!/usr/bin/env php +. + */ + +require_once 'Doctrine/Common/ClassLoader.php'; + +$classLoader = new \Doctrine\Common\ClassLoader('Doctrine'); +$classLoader->register(); + +$classLoader = new \Doctrine\Common\ClassLoader('Symfony'); +$classLoader->register(); + +$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; + +$helperSet = null; +if (file_exists($configFile)) { + if ( ! is_readable($configFile)) { + trigger_error( + 'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR + ); + } + + require $configFile; + + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); + +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet); diff --git a/vendor/doctrine/orm/bin/doctrine.bat b/vendor/doctrine/orm/bin/doctrine.bat new file mode 100644 index 0000000..a9e8cee --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine.bat @@ -0,0 +1,9 @@ +@echo off + +if "%PHPBIN%" == "" set PHPBIN=@php_bin@ +if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH +GOTO RUN +:USE_PEAR_PATH +set PHPBIN=%PHP_PEAR_PHP_BIN% +:RUN +"%PHPBIN%" "@bin_dir@\doctrine" %* diff --git a/vendor/doctrine/orm/bin/doctrine.php b/vendor/doctrine/orm/bin/doctrine.php new file mode 100755 index 0000000..842c549 --- /dev/null +++ b/vendor/doctrine/orm/bin/doctrine.php @@ -0,0 +1,66 @@ +. + */ + +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\ORM\Tools\Console\ConsoleRunner; + +$autoloadFiles = array(__DIR__ . '/../vendor/autoload.php', + __DIR__ . '/../../../autoload.php'); + +foreach ($autoloadFiles as $autoloadFile) { + if (file_exists($autoloadFile)) { + require_once $autoloadFile; + } +} + +$directories = array(getcwd(), getcwd() . DIRECTORY_SEPARATOR . 'config'); + +$configFile = null; +foreach ($directories as $directory) { + $configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php'; + + if (file_exists($configFile)) { + break; + } +} + +if ( ! file_exists($configFile)) { + ConsoleRunner::printCliConfigTemplate(); + exit(1); +} + +if ( ! is_readable($configFile)) { + echo 'Configuration file [' . $configFile . '] does not have read permission.' . "\n"; + exit(1); +} + +$commands = array(); + +$helperSet = require $configFile; + +if ( ! ($helperSet instanceof HelperSet)) { + foreach ($GLOBALS as $helperSetCandidate) { + if ($helperSetCandidate instanceof HelperSet) { + $helperSet = $helperSetCandidate; + break; + } + } +} + +\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet, $commands); diff --git a/vendor/doctrine/orm/composer.json b/vendor/doctrine/orm/composer.json new file mode 100644 index 0000000..d16ccf0 --- /dev/null +++ b/vendor/doctrine/orm/composer.json @@ -0,0 +1,48 @@ +{ + "name": "doctrine/orm", + "type": "library", + "description": "Object-Relational-Mapper for PHP", + "keywords": ["orm", "database"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.4", + "ext-pdo": "*", + "doctrine/collections": "~1.2", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/cache": "~1.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "symfony/yaml": "~2.3|~3.0", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "autoload": { + "psr-0": { "Doctrine\\ORM\\": "lib/" } + }, + "autoload-dev": { + "psr-0": { "Doctrine\\Tests\\": "tests/" } + }, + "bin": ["bin/doctrine", "bin/doctrine.php"], + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "archive": { + "exclude": ["!vendor", "tests", "*phpunit.xml", ".travis.yml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp", "*coveralls.yml"] + } +} diff --git a/vendor/doctrine/orm/docs/LICENSE.md b/vendor/doctrine/orm/docs/LICENSE.md new file mode 100644 index 0000000..1bf8659 --- /dev/null +++ b/vendor/doctrine/orm/docs/LICENSE.md @@ -0,0 +1,363 @@ +The Doctrine2 documentation is licensed under [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US) + +Creative Commons Legal Code + +Attribution-NonCommercial-ShareAlike 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR + DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(g) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "License Elements" means the following high-level license attributes + as selected by Licensor and indicated in the title of this License: + Attribution, Noncommercial, ShareAlike. + e. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + f. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + g. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + h. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + i. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + j. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved, including but not limited to the +rights described in Section 4(e). + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(d), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(d), as requested. + b. You may Distribute or Publicly Perform an Adaptation only under: (i) + the terms of this License; (ii) a later version of this License with + the same License Elements as this License; (iii) a Creative Commons + jurisdiction license (either this or a later license version) that + contains the same License Elements as this License (e.g., + Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). + You must include a copy of, or the URI, for Applicable License with + every copy of each Adaptation You Distribute or Publicly Perform. You + may not offer or impose any terms on the Adaptation that restrict the + terms of the Applicable License or the ability of the recipient of the + Adaptation to exercise the rights granted to that recipient under the + terms of the Applicable License. You must keep intact all notices that + refer to the Applicable License and to the disclaimer of warranties + with every copy of the Work as included in the Adaptation You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Adaptation, You may not impose any effective technological + measures on the Adaptation that restrict the ability of a recipient of + the Adaptation from You to exercise the rights granted to that + recipient under the terms of the Applicable License. This Section 4(b) + applies to the Adaptation as incorporated in a Collection, but this + does not require the Collection apart from the Adaptation itself to be + made subject to the terms of the Applicable License. + c. You may not exercise any of the rights granted to You in Section 3 + above in any manner that is primarily intended for or directed toward + commercial advantage or private monetary compensation. The exchange of + the Work for other copyrighted works by means of digital file-sharing + or otherwise shall not be considered to be intended for or directed + toward commercial advantage or private monetary compensation, provided + there is no payment of any monetary compensation in con-nection with + the exchange of copyrighted works. + d. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and, (iv) consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4(d) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor reserves + the exclusive right to collect such royalties for any exercise by + You of the rights granted under this License if Your exercise of + such rights is for a purpose or use which is otherwise than + noncommercial as permitted under Section 4(c) and otherwise waives + the right to collect royalties through any statutory or compulsory + licensing scheme; and, + iii. Voluntary License Schemes. The Licensor reserves the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License that is for a + purpose or use which is otherwise than noncommercial as permitted + under Section 4(c). + f. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE +FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS +AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE +WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT +LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, +ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT +DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED +WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at http://creativecommons.org/. + diff --git a/vendor/doctrine/orm/docs/README.md b/vendor/doctrine/orm/docs/README.md new file mode 100644 index 0000000..4315116 --- /dev/null +++ b/vendor/doctrine/orm/docs/README.md @@ -0,0 +1,8 @@ +# Doctrine ORM Documentation + +## How to Generate + +1. Run ./bin/install-dependencies.sh +2. Run ./bin/generate-docs.sh + +It will generate the documentation into the build directory of the checkout. \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/bin/generate-docs.sh b/vendor/doctrine/orm/docs/bin/generate-docs.sh new file mode 100755 index 0000000..7d06d2a --- /dev/null +++ b/vendor/doctrine/orm/docs/bin/generate-docs.sh @@ -0,0 +1,10 @@ +#!/bin/bash +EXECPATH=`dirname $0` +cd $EXECPATH +cd .. + +rm build -Rf +sphinx-build en build + +sphinx-build -b latex en build/pdf +rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/bin/install-dependencies.sh b/vendor/doctrine/orm/docs/bin/install-dependencies.sh new file mode 100644 index 0000000..86b3bdf --- /dev/null +++ b/vendor/doctrine/orm/docs/bin/install-dependencies.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo apt-get install python25 python25-dev texlive-full rubber +sudo easy_install pygments +sudo easy_install sphinx \ No newline at end of file diff --git a/vendor/doctrine/orm/docs/en/Makefile b/vendor/doctrine/orm/docs/en/Makefile new file mode 100644 index 0000000..a6f6fce --- /dev/null +++ b/vendor/doctrine/orm/docs/en/Makefile @@ -0,0 +1,89 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Doctrine2ORM.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Doctrine2ORM.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/vendor/doctrine/orm/docs/en/_exts/configurationblock.py b/vendor/doctrine/orm/docs/en/_exts/configurationblock.py new file mode 100644 index 0000000..36ca61f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/_exts/configurationblock.py @@ -0,0 +1,93 @@ +#Copyright (c) 2010 Fabien Potencier +# +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is furnished +#to do so, subject to the following conditions: +# +#The above copyright notice and this permission notice shall be included in all +#copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +from docutils.parsers.rst import Directive, directives +from docutils import nodes +from string import upper + +class configurationblock(nodes.General, nodes.Element): + pass + +class ConfigurationBlock(Directive): + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + formats = { + 'html': 'HTML', + 'xml': 'XML', + 'php': 'PHP', + 'yaml': 'YAML', + 'jinja': 'Twig', + 'html+jinja': 'Twig', + 'jinja+html': 'Twig', + 'php+html': 'PHP', + 'html+php': 'PHP', + 'ini': 'INI', + 'php-annotations': 'Annotations', + } + + def run(self): + env = self.state.document.settings.env + + node = nodes.Element() + node.document = self.state.document + self.state.nested_parse(self.content, self.content_offset, node) + + entries = [] + for i, child in enumerate(node): + if isinstance(child, nodes.literal_block): + # add a title (the language name) before each block + #targetid = "configuration-block-%d" % env.new_serialno('configuration-block') + #targetnode = nodes.target('', '', ids=[targetid]) + #targetnode.append(child) + + innernode = nodes.emphasis(self.formats[child['language']], self.formats[child['language']]) + + para = nodes.paragraph() + para += [innernode, child] + + entry = nodes.list_item('') + entry.append(para) + entries.append(entry) + + resultnode = configurationblock() + resultnode.append(nodes.bullet_list('', *entries)) + + return [resultnode] + +def visit_configurationblock_html(self, node): + self.body.append(self.starttag(node, 'div', CLASS='configuration-block')) + +def depart_configurationblock_html(self, node): + self.body.append('\n') + +def visit_configurationblock_latex(self, node): + pass + +def depart_configurationblock_latex(self, node): + pass + +def setup(app): + app.add_node(configurationblock, + html=(visit_configurationblock_html, depart_configurationblock_html), + latex=(visit_configurationblock_latex, depart_configurationblock_latex)) + app.add_directive('configuration-block', ConfigurationBlock) diff --git a/vendor/doctrine/orm/docs/en/changelog/migration_2_5.rst b/vendor/doctrine/orm/docs/en/changelog/migration_2_5.rst new file mode 100644 index 0000000..43a40e6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/changelog/migration_2_5.rst @@ -0,0 +1,710 @@ +What is new in Doctrine ORM 2.5? +================================ + +This document describes changes between Doctrine ORM 2.4 and 2.5 (currently in +Beta). It contains a description of all the new features and sections +about behavioral changes and potential backwards compatibility breaks. +Please review this document carefully when updating to Doctrine 2.5. + +First note, that with the ORM 2.5 release we are dropping support +for PHP 5.3. We are enforcing this with Composer, servers without +at least PHP 5.4 will not allow installing Doctrine 2.5. + +New Features and Improvements +----------------------------- + +Events: PostLoad now triggered after associations are loaded +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before Doctrine 2.5 if you had an entity with a ``@PostLoad`` event +defined then Doctrine would trigger listeners after the fields were +loaded, but before assocations are available. + +- `DDC-54 `_ +- `Commit `_ + +Events: Add API to programatically add event listeners to Entity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When developing third party libraries or decoupled applications +it can be interesting to develop an entity listener without knowing +the entities that require this listener. + +You can now attach entity listeners to entities using the +``AttachEntityListenersListener`` class, which is listening to the +``loadMetadata`` event that is fired once for every entity during +metadata generation: + +.. code-block:: php + + addEntityListener( + 'MyProject\Entity\User', 'MyProject\Listener\TimestampableListener', + Events::prePersist, 'onPrePersist' + ); + + $evm->addEventListener(Events::loadClassMetadata, $listener); + + class TimestampableListener + { + public function onPrePersist($event) + { + $entity = $event->getEntity(); + $entity->setCreated(new \DateTime('now')); + } + } + +Embeddedable Objects +~~~~~~~~~~~~~~~~~~~~ + +Doctrine now supports creating multiple PHP objects from one database table +implementing a feature called "Embeddedable Objects". Next to an ``@Entity`` +class you can now define a class that is embeddable into a database table of an +entity using the ``@Embeddable`` annotation. Embeddable objects can never be +saved, updated or deleted on their own, only as part of an entity (called +"root-entity" or "aggregate"). Consequently embeddables don't have a primary +key, they are identified only by their values. + +Example of defining and using embeddables classes: + +.. code-block:: php + + `_. + +This feature was developed by external contributor `Johannes Schmitt +`_ + +- `DDC-93 `_ +- `Pull Request `_ + +Second-Level-Cache +~~~~~~~~~~~~~~~~~~ + +Since version 2.0 of Doctrine, fetching the same object twice by primary key +would result in just one query. This was achieved by the identity map pattern +(first-level-cache) that kept entities in memory. + +The newly introduced second-level-cache works a bit differently. Instead +of saving objects in memory, it saves them in a fast in-memory cache such +as Memcache, Redis, Riak or MongoDB. Additionally it allows saving the result +of more complex queries than by primary key. Summarized this feature works +like the existing Query result cache, but it is much more powerful. + +As an example lets cache an entity Country that is a relation to the User +entity. We always want to display the country, but avoid the additional +query to this table. + +.. code-block:: php + + setSecondLevelCacheEnabled(); + + $cacheConfig = $config->getSecondLevelCacheConfiguration(); + $regionConfig = $cacheConfig->getRegionsConfiguration(); + $regionConfig->setLifetime('country_region', 3600); + +Now Doctrine will first check for the data of any country in the cache +instead of the database. + +- `Documentation + `_ +- `Pull Request `_ + +Criteria API: Support for ManyToMany assocations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We introduced support for querying collections using the `Criteria API +`_ +in 2.4. This only worked efficently for One-To-Many assocations, not for +Many-To-Many. With the start of 2.5 also Many-To-Many associations get queried +instead of loading them into memory. + +Criteria API: Add new contains() expression +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now possible to use the Criteria API to check for string contains needle +using ``contains()``. This translates to using a ``column LIKE '%needle%'`` SQL +condition. + +.. code-block:: php + + where(Criteria::expr()->contains('name', 'Benjamin')); + + $users = $repository->matching($criteria); + +Criteria API: Support for EXTRA_LAZY +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A collection that is marked as ``fetch="EXTRA_LAZY"`` will now return another +lazy collection when using ``Collection::matching($criteria)``: + +.. code-block:: php + + where(Criteria->expr()->eq("published", 1)); + + $publishedComments = $post->getComments()->matching($criteria); + + echo count($publishedComments); + +The lazy criteria currently supports the ``count()`` and ``contains()`` +functionality lazily. All other operations of the ``Collection`` interface +trigger a full load of the collection. + +This feature was contributed by `Michaël Gallego `_. + +- `Pull Request #1 `_ +- `Pull Request #2 `_ + +Mapping: Allow configuring Index flags +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now possible to control the index flags in the DBAL +schema abstraction from the ORM using metadata. This was possible +only with a schema event listener before. + +.. code-block:: php + + `_. + +- `Pull Request `_ + +SQLFilter API: Check if a parameter is set +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can now check in your SQLFilter if a parameter was set. This allows +to more easily control which features of a filter to enable or disable. + +Extending on the locale example of the documentation: + +.. code-block:: php + + reflClass->implementsInterface('LocaleAware')) { + return ""; + } + + if (!$this->hasParameter('locale')) { + return ""; + } + + return $targetTableAlias.'.locale = ' . $this->getParameter('locale'); + } + } + +This feature was contributed by `Miroslav Demovic `_ + +- `Pull Request `_ + + +EXTRA_LAZY Improvements +~~~~~~~~~~~~~~~~~~~~~~~ + +1. Efficient query when using EXTRA_LAZY and containsKey + + When calling ``Collection::containsKey($key)`` on one-to-many and many-to-many + collections using ``indexBy`` and ``EXTRA_LAZY`` a query is now executed to check + for the existance for the item. Prevoiusly this operation was performed in memory + by loading all entities of the collection. + + .. code-block:: php + + getGroups()->containsKey($groupId)) { + echo "User is in group $groupId\n"; + } + + This feature was contributed by `Asmir Mustafic `_ + + - `Pull Request `_ + +2. Add EXTRA_LAZY Support for get() for owning and inverse many-to-many + + This was contributed by `Sander Marechal `_. + +Improve efficiency of One-To-Many EAGER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When marking a one-to-many association with ``fetch="EAGER"`` it will now +execute one query less than before and work correctly in combination with +``indexBy``. + +Better support for EntityManagerInterface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Many of the locations where previously only the ``Doctrine\ORM\EntityManager`` +was allowed are now changed to accept the ``EntityManagerInterface`` that was +introduced in 2.4. This allows you to more easily use the decorator pattern +to extend the EntityManager if you need. It's still not replaced everywhere, +so you still have to be careful. + +DQL Improvements +~~~~~~~~~~~~~~~~ + +1. It is now possible to add functions to the ``ORDER BY`` clause in DQL statements: + +.. code-block:: php + + createQuery($dql); + $query->setParameter('groups', array(1, 2, 3)); + + $users = $query->getResult(); + +6. Expressions inside ``COUNT()`` now allowed + +.. code-block:: php + + `_ to pass a callback instead that resolves +the function: + +.. code-block:: php + + addCustomNumericFunction( + 'IS_PUBLISHED', function($funcName) use ($currentSiteId) { + return new IsPublishedFunction($currentSiteId); + } + ); + +Query API: WHERE IN Query using a Collection as parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When performing a ``WHERE IN`` query for a collection of entities you can +now pass the array collection of entities as a parameter value to the query +object: + +.. code-block:: php + + getChildren(); + + $queryBuilder + ->select('p') + ->from('Product', 'p') + ->where('p.category IN (:categories)') + ->setParameter('categories', $categories) + ; + +This feature was contributed by `Michael Perrin +`_. + +- `Pull Request `_ +- `DDC-2319 `_ + +Query API: Add suport for default Query Hints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To configure multiple different features such as custom AST Walker, fetch modes, +locking and other features affecting DQL generation we have had a feature +called "query hints" since version 2.0. + +It is now possible to add query hints that are always enabled for every Query: + +.. code-block:: php + + setDefaultQueryHints( + 'doctrine.customOutputWalker' => 'MyProject\CustomOutputWalker' + ); + +This feature was contributed by `Artur Eshenbrener +`_. + +- `Pull Request `_ + +ResultSetMappingBuilder: Add support for Single-Table Inheritance +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before 2.5 the ResultSetMappingBuilder did not work with entities +that are using Single-Table-Inheritance. This restriction was lifted +by adding the missing support. + +YAML Mapping: Many-To-Many doesnt require join column definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In Annotations and XML it was not necessary using conventions for naming +the many-to-many join column names, in YAML it was not possible however. + +A many-to-many definition in YAML is now possible using this minimal +definition: + +.. code-block:: yaml + + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + +Schema Validator Command: Allow to skip sub-checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Schema Validator command executes two independent checks +for validity of the mappings and if the schema is synchronized +correctly. It is now possible to skip any of the two steps +when executing the command: + +:: + + $ php vendor/bin/doctrine orm:validate-schema --skip-mapping + $ php vendor/bin/doctrine orm:validate-schema --skip-sync + +This allows you to write more specialized continuous integration and automation +checks. When no changes are found the command returns the exit code 0 +and 1, 2 or 3 when failing because of mapping, sync or both. + +EntityGenerator Command: Avoid backups +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When calling the EntityGenerator for an existing entity, Doctrine would +create a backup file every time to avoid loosing changes to the code. +You can now skip generating the backup file by passing the ``--no-backup`` +flag: + +:: + + $ php vendor/bin/doctrine orm:generate-entities src/ --no-backup + +Support for Objects as Identifiers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is now possible to use Objects as identifiers for Entities +as long as they implement the magic method ``__toString()``. + +.. code-block:: php + + value = $value; + } + + public function __toString() + { + return (string)$this->value; + } + } + + class User + { + /** @Id @Column(type="userid") */ + private $id; + + public function __construct(UserId $id) + { + $this->id = $id; + } + } + + class UserIdType extends \Doctrine\DBAL\Types\Type + { + // ... + } + + Doctrine\DBAL\Types\Type::addType('userid', 'MyProject\UserIdType'); + +Behavioral Changes (BC Breaks) +------------------------------ + +NamingStrategy interface changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Doctrine\ORM\Mapping\NamingStrategyInterface`` changed slightly +to pass the Class Name of the entity into the join column name generation: + +:: + + - function joinColumnName($propertyName); + + function joinColumnName($propertyName, $className = null); + +It also received a new method for supporting embeddables: + +:: + + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName); + +Minor BC BREAK: EntityManagerInterface instead of EntityManager in type-hints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of 2.5, classes requiring the ``EntityManager`` in any method signature will now require +an ``EntityManagerInterface`` instead. +If you are extending any of the following classes, then you need to check following +signatures: + +- ``Doctrine\ORM\Tools\DebugUnitOfWorkListener#dumpIdentityMap(EntityManagerInterface $em)`` +- ``Doctrine\ORM\Mapping\ClassMetadataFactory#setEntityManager(EntityManagerInterface $em)`` + +Minor BC BREAK: Custom Hydrators API change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of 2.5, ``AbstractHydrator`` does not enforce the usage of cache as part of +API, and now provides you a clean API for column information through the method +``hydrateColumnInfo($column)``. +Cache variable being passed around by reference is no longer needed since +Hydrators are per query instantiated since Doctrine 2.4. + +- `DDC-3060 `_ + +Minor BC BREAK: All non-transient classes in an inheritance must be part of the inheritance map +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of 2.5, classes, if you define an inheritance map for an inheritance tree, you are required +to map all non-transient classes in that inheritance, including the root of the inheritance. + +So far, the root of the inheritance was allowed to be skipped in the inheritance map: this is +not possible anymore, and if you don't plan to persist instances of that class, then you should +either: + +- make that class as ``abstract`` +- add that class to your inheritance map + +If you fail to do so, then a ``Doctrine\ORM\Mapping\MappingException`` will be thrown. + + +- `DDC-3300 `_ +- `DDC-3503 `_ + +Minor BC BREAK: Entity based EntityManager#clear() calls follow cascade detach +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whenever ``EntityManager#clear()`` method gets called with a given entity class +name, until 2.4, it was only detaching the specific requested entity. +As of 2.5, ``EntityManager`` will follow configured cascades, providing a better +memory management since associations will be garbage collected, optimizing +resources consumption on long running jobs. + +Updates on entities scheduled for deletion are no longer processed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In Doctrine 2.4, if you modified properties of an entity scheduled for deletion, UnitOfWork would +produce an ``UPDATE`` statement to be executed right before the ``DELETE`` statement. The entity in question +was therefore present in ``UnitOfWork#entityUpdates``, which means that ``preUpdate`` and ``postUpdate`` +listeners were (quite pointlessly) called. In ``preFlush`` listeners, it used to be possible to undo +the scheduled deletion for updated entities (by calling ``persist()`` if the entity was found in both +``entityUpdates`` and ``entityDeletions``). This does not work any longer, because the entire changeset +calculation logic is optimized away. + +Minor BC BREAK: Default lock mode changed from LockMode::NONE to null in method signatures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A misconception concerning default lock mode values in method signatures lead to unexpected behaviour +in SQL statements on SQL Server. With a default lock mode of ``LockMode::NONE`` throughout the +method signatures in ORM, the table lock hint ``WITH (NOLOCK)`` was appended to all locking related +queries by default. This could result in unpredictable results because an explicit ``WITH (NOLOCK)`` +table hint tells SQL Server to run a specific query in transaction isolation level READ UNCOMMITTED +instead of the default READ COMMITTED transaction isolation level. +Therefore there now is a distinction between ``LockMode::NONE`` and ``null`` to be able to tell +Doctrine whether to add table lock hints to queries by intention or not. To achieve this, the following +method signatures have been changed to declare ``$lockMode = null`` instead of ``$lockMode = LockMode::NONE``: + +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#load()`` +- ``Doctrine\ORM\Cache\Persister\AbstractEntityPersister#refresh()`` +- ``Doctrine\ORM\Decorator\EntityManagerDecorator#find()`` +- ``Doctrine\ORM\EntityManager#find()`` +- ``Doctrine\ORM\EntityRepository#find()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#load()`` +- ``Doctrine\ORM\Persisters\BasicEntityPersister#refresh()`` +- ``Doctrine\ORM\Persisters\EntityPersister#getSelectSQL()`` +- ``Doctrine\ORM\Persisters\EntityPersister#load()`` +- ``Doctrine\ORM\Persisters\EntityPersister#refresh()`` +- ``Doctrine\ORM\Persisters\JoinedSubclassPersister#getSelectSQL()`` + +You should update signatures for these methods if you have subclassed one of the above classes. +Please also check the calling code of these methods in your application and update if necessary. + +.. note:: + + This in fact is really a minor BC BREAK and should not have any affect on database vendors + other than SQL Server because it is the only one that supports and therefore cares about + ``LockMode::NONE``. It's really just a FIX for SQL Server environments using ORM. + +Minor BC BREAK: __clone method not called anymore when entities are instantiated via metadata API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As of PHP 5.6, instantiation of new entities is deferred to the +`doctrine/instantiator `_ library, which will avoid calling ``__clone`` +or any public API on instantiated objects. + +BC BREAK: DefaultRepositoryFactory is now final +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please implement the ``Doctrine\ORM\Repository\RepositoryFactory`` interface instead of extending +the ``Doctrine\ORM\Repository\DefaultRepositoryFactory``. + +BC BREAK: New object expression DQL queries now respects user provided aliasing and not return consumed fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When executing DQL queries with new object expressions, instead of returning +DTOs numerically indexes, it will now respect user provided aliases. Consider +the following query: + +:: + + SELECT new UserDTO(u.id,u.name) as user,new AddressDTO(a.street,a.postalCode) as address, a.id as addressId + FROM User u INNER JOIN u.addresses a WITH a.isPrimary = true + +Previously, your result would be similar to this: + +:: + + array( + 0=>array( + 0=>{UserDTO object}, + 1=>{AddressDTO object}, + 2=>{u.id scalar}, + 3=>{u.name scalar}, + 4=>{a.street scalar}, + 5=>{a.postalCode scalar}, + 'addressId'=>{a.id scalar}, + ), + ... + ) + +From now on, the resultset will look like this: + +:: + + array( + 0=>array( + 'user'=>{UserDTO object}, + 'address'=>{AddressDTO object}, + 'addressId'=>{a.id scalar} + ), + ... + ) diff --git a/vendor/doctrine/orm/docs/en/conf.py b/vendor/doctrine/orm/docs/en/conf.py new file mode 100644 index 0000000..5155ac9 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/conf.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# +# Doctrine 2 ORM documentation build configuration file, created by +# sphinx-quickstart on Fri Dec 3 18:10:24 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath('_exts')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['configurationblock'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Doctrine 2 ORM' +copyright = u'2010-12, Doctrine Project Team' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '2' +# The full version, including alpha/beta/rc tags. +release = '2' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'doctrine' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['_theme'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Doctrine2ORMdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Doctrine2ORM.tex', u'Doctrine 2 ORM Documentation', + u'Doctrine Project Team', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True + +primary_domain = "dcorm" + +def linkcode_resolve(domain, info): + if domain == 'dcorm': + return 'http://' + return None diff --git a/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst b/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst new file mode 100644 index 0000000..5bbd2a3 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/advanced-field-value-conversion-using-custom-mapping-types.rst @@ -0,0 +1,256 @@ +Advanced field value conversion using custom mapping types +========================================================== + +.. sectionauthor:: Jan Sorgalla + +When creating entities, you sometimes have the need to transform field values +before they are saved to the database. In Doctrine you can use Custom Mapping +Types to solve this (see: :ref:`reference-basic-mapping-custom-mapping-types`). + +There are several ways to achieve this: converting the value inside the Type +class, converting the value on the database-level or a combination of both. + +This article describes the third way by implementing the MySQL specific column +type `Point `_. + +The ``Point`` type is part of the `Spatial extension `_ +of MySQL and enables you to store a single location in a coordinate space by +using x and y coordinates. You can use the Point type to store a +longitude/latitude pair to represent a geographic location. + +The entity +---------- + +We create a simple entity with a field ``$point`` which holds a value object +``Point`` representing the latitude and longitude of the position. + +The entity class: + +.. code-block:: php + + point = $point; + } + + /** + * @return \Geo\ValueObject\Point + */ + public function getPoint() + { + return $this->point; + } + + /** + * @param string $address + */ + public function setAddress($address) + { + $this->address = $address; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + } + +We use the custom type ``point`` in the ``@Column`` docblock annotation of the +``$point`` field. We will create this custom mapping type in the next chapter. + +The point class: + +.. code-block:: php + + latitude = $latitude; + $this->longitude = $longitude; + } + + /** + * @return float + */ + public function getLatitude() + { + return $this->latitude; + } + + /** + * @return float + */ + public function getLongitude() + { + return $this->longitude; + } + } + +The mapping type +---------------- + +Now we're going to create the ``point`` type and implement all required methods. + +.. code-block:: php + + getLongitude(), $value->getLatitude()); + } + + return $value; + } + + public function canRequireSQLConversion() + { + return true; + } + + public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('AsText(%s)', $sqlExpr); + } + + public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + { + return sprintf('PointFromText(%s)', $sqlExpr); + } + } + +We do a 2-step conversion here. In the first step, we convert the ``Point`` +object into a string representation before saving to the database (in the +``convertToDatabaseValue`` method) and back into an object after fetching the +value from the database (in the ``convertToPHPValue`` method). + +The format of the string representation format is called `Well-known text (WKT) +`_. The advantage of this format +is, that it is both human readable and parsable by MySQL. + +Internally, MySQL stores geometry values in a binary format that is not +identical to the WKT format. So, we need to let MySQL transform the WKT +representation into its internal format. + +This is where the ``convertToPHPValueSQL`` and ``convertToDatabaseValueSQL`` +methods come into play. + +This methods wrap a sql expression (the WKT representation of the Point) into +MySQL functions `PointFromText `_ +and `AsText `_ +which convert WKT strings to and from the internal format of MySQL. + +.. note:: + + When using DQL queries, the ``convertToPHPValueSQL`` and + ``convertToDatabaseValueSQL`` methods only apply to identification variables + and path expressions in SELECT clauses. Expressions in WHERE clauses are + **not** wrapped! + + If you want to use Point values in WHERE clauses, you have to implement a + :doc:`user defined function ` for + ``PointFromText``. + +Example usage +------------- + +.. code-block:: php + + getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping('point', 'point'); + + // Store a Location object + use Geo\Entity\Location; + use Geo\ValueObject\Point; + + $location = new Location(); + + $location->setAddress('1600 Amphitheatre Parkway, Mountain View, CA'); + $location->setPoint(new Point(37.4220761, -122.0845187)); + + $em->persist($location); + $em->flush(); + $em->clear(); + + // Fetch the Location object + $query = $em->createQuery("SELECT l FROM Geo\Entity\Location WHERE l.address = '1600 Amphitheatre Parkway, Mountain View, CA'"); + $location = $query->getSingleResult(); + + /* @var Geo\ValueObject\Point */ + $point = $location->getPoint(); diff --git a/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst b/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst new file mode 100644 index 0000000..fdf8303 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/aggregate-fields.rst @@ -0,0 +1,376 @@ +Aggregate Fields +================ + +.. sectionauthor:: Benjamin Eberlei + +You will often come across the requirement to display aggregate +values of data that can be computed by using the MIN, MAX, COUNT or +SUM SQL functions. For any ORM this is a tricky issue +traditionally. Doctrine 2 offers several ways to get access to +these values and this article will describe all of them from +different perspectives. + +You will see that aggregate fields can become very explicit +features in your domain model and how this potentially complex +business rules can be easily tested. + +An example model +---------------- + +Say you want to model a bank account and all their entries. Entries +into the account can either be of positive or negative money +values. Each account has a credit limit and the account is never +allowed to have a balance below that value. + +For simplicity we live in a world were money is composed of +integers only. Also we omit the receiver/sender name, stated reason +for transfer and the execution date. These all would have to be +added on the ``Entry`` object. + +Our entities look like: + +.. code-block:: php + + no = $no; + $this->maxCredit = $maxCredit; + $this->entries = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** + * @Entity + */ + class Entry + { + /** @Id @GeneratedValue @Column(type="integer") */ + private $id; + + /** + * @ManyToOne(targetEntity="Account", inversedBy="entries") + */ + private $account; + + /** + * @Column(type="integer") + */ + private $amount; + + public function __construct($account, $amount) + { + $this->account = $account; + $this->amount = $amount; + // more stuff here, from/to whom, stated reason, execution date and such + } + + public function getAmount() + { + return $this->amount; + } + } + +Using DQL +--------- + +The Doctrine Query Language allows you to select for aggregate +values computed from fields of your Domain Model. You can select +the current balance of your account by calling: + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $myAccountId) + ->getSingleScalarResult(); + +The ``$em`` variable in this (and forthcoming) example holds the +Doctrine ``EntityManager``. We create a query for the SUM of all +amounts (negative amounts are withdraws) and retrieve them as a +single scalar result, essentially return only the first column of +the first row. + +This approach is simple and powerful, however it has a serious +drawback. We have to execute a specific query for the balance +whenever we need it. + +To implement a powerful domain model we would rather have access to +the balance from our ``Account`` entity during all times (even if +the Account was not persisted in the database before!). + +Also an additional requirement is the max credit per ``Account`` +rule. + +We cannot reliably enforce this rule in our ``Account`` entity with +the DQL retrieval of the balance. There are many different ways to +retrieve accounts. We cannot guarantee that we can execute the +aggregation query for all these use-cases, let alone that a +userland programmer checks this balance against newly added +entries. + +Using your Domain Model +----------------------- + +``Account`` and all the ``Entry`` instances are connected through a +collection, which means we can compute this value at runtime: + +.. code-block:: php + + entries as $entry) { + $balance += $entry->getAmount(); + } + return $balance; + } + } + +Now we can always call ``Account::getBalance()`` to access the +current account balance. + +To enforce the max credit rule we have to implement the "Aggregate +Root" pattern as described in Eric Evans book on Domain Driven +Design. Described with one sentence, an aggregate root controls the +instance creation, access and manipulation of its children. + +In our case we want to enforce that new entries can only added to +the ``Account`` by using a designated method. The ``Account`` is +the aggregate root of this relation. We can also enforce the +correctness of the bi-directional ``Account`` <-> ``Entry`` +relation with this method: + +.. code-block:: php + + assertAcceptEntryAllowed($amount); + + $e = new Entry($this, $amount); + $this->entries[] = $e; + return $e; + } + } + +Now look at the following test-code for our entities: + +.. code-block:: php + + assertEquals(0, $account->getBalance()); + + $account->addEntry(500); + $this->assertEquals(500, $account->getBalance()); + + $account->addEntry(-700); + $this->assertEquals(-200, $account->getBalance()); + } + + public function testExceedMaxLimit() + { + $account = new Account("123456", $maxCredit = 200); + + $this->setExpectedException("Exception"); + $account->addEntry(-1000); + } + } + +To enforce our rule we can now implement the assertion in +``Account::addEntry``: + +.. code-block:: php + + getBalance() + $amount; + $allowedMinimalBalance = ($this->maxCredit * -1); + if ($futureBalance < $allowedMinimalBalance) { + throw new Exception("Credit Limit exceeded, entry is not allowed!"); + } + } + } + +We haven't talked to the entity manager for persistence of our +account example before. You can call +``EntityManager::persist($account)`` and then +``EntityManager::flush()`` at any point to save the account to the +database. All the nested ``Entry`` objects are automatically +flushed to the database also. + +.. code-block:: php + + addEntry(500); + $account->addEntry(-200); + $em->persist($account); + $em->flush(); + +The current implementation has a considerable drawback. To get the +balance, we have to initialize the complete ``Account::$entries`` +collection, possibly a very large one. This can considerably hurt +the performance of your application. + +Using an Aggregate Field +------------------------ + +To overcome the previously mentioned issue (initializing the whole +entries collection) we want to add an aggregate field called +"balance" on the Account and adjust the code in +``Account::getBalance()`` and ``Account:addEntry()``: + +.. code-block:: php + + balance; + } + + public function addEntry($amount) + { + $this->assertAcceptEntryAllowed($amount); + + $e = new Entry($this, $amount); + $this->entries[] = $e; + $this->balance += $amount; + return $e; + } + } + +This is a very simple change, but all the tests still pass. Our +account entities return the correct balance. Now calling the +``Account::getBalance()`` method will not occur the overhead of +loading all entries anymore. Adding a new Entry to the +``Account::$entities`` will also not initialize the collection +internally. + +Adding a new entry is therefore very performant and explicitly +hooked into the domain model. It will only update the account with +the current balance and insert the new entry into the database. + +Tackling Race Conditions with Aggregate Fields +---------------------------------------------- + +Whenever you denormalize your database schema race-conditions can +potentially lead to inconsistent state. See this example: + +.. code-block:: php + + find('Bank\Entities\Account', $accId); + + // request 2 account + $account2 = $em->find('Bank\Entities\Account', $accId); + + $account1->addEntry(-200); + $account2->addEntry(-200); + + // now request 1 and 2 both flush the changes. + +The aggregate field ``Account::$balance`` is now -200, however the +SUM over all entries amounts yields -400. A violation of our max +credit rule. + +You can use both optimistic or pessimistic locking to save-guard +your aggregate fields against this kind of race-conditions. Reading +Eric Evans DDD carefully he mentions that the "Aggregate Root" +(Account in our example) needs a locking mechanism. + +Optimistic locking is as easy as adding a version column: + +.. code-block:: php + + find('Bank\Entities\Account', $accId, LockMode::PESSIMISTIC_READ); + +Keeping Updates and Deletes in Sync +----------------------------------- + +The example shown in this article does not allow changes to the +value in ``Entry``, which considerably simplifies the effort to +keep ``Account::$balance`` in sync. If your use-case allows fields +to be updated or related entities to be removed you have to +encapsulate this logic in your "Aggregate Root" entity and adjust +the aggregate field accordingly. + +Conclusion +---------- + +This article described how to obtain aggregate values using DQL or +your domain model. It showed how you can easily add an aggregate +field that offers serious performance benefits over iterating all +the related objects that make up an aggregate value. Finally I +showed how you can ensure that your aggregate fields do not get out +of sync due to race-conditions and concurrent access. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/custom-mapping-types.rst b/vendor/doctrine/orm/docs/en/cookbook/custom-mapping-types.rst new file mode 100644 index 0000000..1bd4f12 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/custom-mapping-types.rst @@ -0,0 +1,103 @@ +Custom Mapping Types +==================== + +Doctrine allows you to create new mapping types. This can come in +handy when you're missing a specific mapping type or when you want +to replace the existing implementation of a mapping type. + +In order to create a new mapping type you need to subclass +``Doctrine\DBAL\Types\Type`` and implement/override the methods as +you wish. Here is an example skeleton of such a custom type class: + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype'); + +When registering the custom types in the configuration you specify a unique +name for the mapping type and map that to the corresponding fully qualified +class name. Now the new type can be used when mapping columns: + +.. code-block:: php + + + +This recipe will show you a simple example of how you can use +Doctrine 2 to persist an implementation of the +`Decorator Pattern `_ + +Component +--------- + +The ``Component`` class needs to be persisted, so it's going to +be an ``Entity``. As the top of the inheritance hierarchy, it's going +to have to define the persistent inheritance. For this example, we +will use Single Table Inheritance, but Class Table Inheritance +would work as well. In the discriminator map, we will define two +concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``. + +.. code-block:: php + + id; + } + + /** + * Set name + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get name + * @return string $name + */ + public function getName() + { + return $this->name; + } + + } + +ConcreteComponent +----------------- + +The ``ConcreteComponent`` class is pretty simple and doesn't do much +more than extend the abstract ``Component`` class (only for the +purpose of keeping this example simple). + +.. code-block:: php + + setDecorates($c); + } + + /** + * (non-PHPdoc) + * @see Test.Component::getName() + */ + public function getName() + { + return 'Decorated ' . $this->getDecorates()->getName(); + } + + /** + * the component being decorated + * @return Component + */ + protected function getDecorates() + { + return $this->decorates; + } + + /** + * sets the component being decorated + * @param Component $c + */ + protected function setDecorates(Component $c) + { + $this->decorates = $c; + } + + } + +All operations on the ``Decorator`` (i.e. persist, remove, etc) will +cascade from the ``Decorator`` to the ``Component``. This means that +when we persist a ``Decorator``, Doctrine will take care of +persisting the chain of decorated objects for us. A ``Decorator`` can +be treated exactly as a ``Component`` when it comes time to +persisting it. + +The ``Decorator's`` constructor accepts an instance of a +``Component``, as defined by the ``Decorator`` pattern. The +setDecorates/getDecorates methods have been defined as protected to +hide the fact that a ``Decorator`` is decorating a ``Component`` and +keeps the ``Component`` interface and the ``Decorator`` interface +identical. + +To illustrate the intended result of the ``Decorator`` pattern, the +getName() method has been overridden to append a string to the +``Component's`` getName() method. + +ConcreteDecorator +----------------- + +The final class required to complete a simple implementation of the +Decorator pattern is the ``ConcreteDecorator``. In order to further +illustrate how the ``Decorator`` can alter data as it moves through +the chain of decoration, a new field, "special", has been added to +this class. The getName() has been overridden and appends the value +of the getSpecial() method to its return value. + +.. code-block:: php + + special = $special; + } + + /** + * Get special + * @return string $special + */ + public function getSpecial() + { + return $this->special; + } + + /** + * (non-PHPdoc) + * @see Test.Component::getName() + */ + public function getName() + { + return '[' . $this->getSpecial() + . '] ' . parent::getName(); + } + + } + +Examples +-------- + +Here is an example of how to persist and retrieve your decorated +objects + +.. code-block:: php + + setName('Test Component 1'); + $em->persist($c); // assigned unique ID = 1 + + // create a new concrete decorator + $c = new ConcreteComponent(); + $c->setName('Test Component 2'); + + $d = new ConcreteDecorator($c); + $d->setSpecial('Really'); + $em->persist($d); + // assigns c as unique ID = 2, and d as unique ID = 3 + + $em->flush(); + + $c = $em->find('Test\Component', 1); + $d = $em->find('Test\Component', 3); + + echo get_class($c); + // prints: Test\Component\ConcreteComponent + + echo $c->getName(); + // prints: Test Component 1 + + echo get_class($d) + // prints: Test\Component\ConcreteDecorator + + echo $d->getName(); + // prints: [Really] Decorated Test Component 2 + diff --git a/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst b/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst new file mode 100644 index 0000000..e840126 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/dql-custom-walkers.rst @@ -0,0 +1,217 @@ +Extending DQL in Doctrine 2: Custom AST Walkers +=============================================== + +.. sectionauthor:: Benjamin Eberlei + +The Doctrine Query Language (DQL) is a proprietary sql-dialect that +substitutes tables and columns for Entity names and their fields. +Using DQL you write a query against the database using your +entities. With the help of the metadata you can write very concise, +compact and powerful queries that are then translated into SQL by +the Doctrine ORM. + +In Doctrine 1 the DQL language was not implemented using a real +parser. This made modifications of the DQL by the user impossible. +Doctrine 2 in contrast has a real parser for the DQL language, +which transforms the DQL statement into an +`Abstract Syntax Tree `_ +and generates the appropriate SQL statement for it. Since this +process is deterministic Doctrine heavily caches the SQL that is +generated from any given DQL query, which reduces the performance +overhead of the parsing process to zero. + +You can modify the Abstract syntax tree by hooking into DQL parsing +process by adding a Custom Tree Walker. A walker is an interface +that walks each node of the Abstract syntax tree, thereby +generating the SQL statement. + +There are two types of custom tree walkers that you can hook into +the DQL parser: + + +- An output walker. This one actually generates the SQL, and there + is only ever one of them. We implemented the default SqlWalker + implementation for it. +- A tree walker. There can be many tree walkers, they cannot + generate the sql, however they can modify the AST before its + rendered to sql. + +Now this is all awfully technical, so let me come to some use-cases +fast to keep you motivated. Using walker implementation you can for +example: + + +- Modify the AST to generate a Count Query to be used with a + paginator for any given DQL query. +- Modify the Output Walker to generate vendor-specific SQL + (instead of ANSI). +- Modify the AST to add additional where clauses for specific + entities (example ACL, country-specific content...) +- Modify the Output walker to pretty print the SQL for debugging + purposes. + +In this cookbook-entry I will show examples on the first two +points. There are probably much more use-cases. + +Generic count query for pagination +---------------------------------- + +Say you have a blog and posts all with one category and one author. +A query for the front-page or any archive page might look something +like: + +.. code-block:: sql + + SELECT p, c, a FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ... + +Now in this query the blog post is the root entity, meaning its the +one that is hydrated directly from the query and returned as an +array of blog posts. In contrast the comment and author are loaded +for deeper use in the object tree. + +A pagination for this query would want to approximate the number of +posts that match the WHERE clause of this query to be able to +predict the number of pages to show to the user. A draft of the DQL +query for pagination would look like: + +.. code-block:: sql + + SELECT count(DISTINCT p.id) FROM BlogPost p JOIN p.category c JOIN p.author a WHERE ... + +Now you could go and write each of these queries by hand, or you +can use a tree walker to modify the AST for you. Lets see how the +API would look for this use-case: + +.. code-block:: php + + createQuery($dql); + $query->setFirstResult( ($pageNum-1) * 20)->setMaxResults(20); + + $totalResults = Paginate::count($query); + $results = $query->getResult(); + +The ``Paginate::count(Query $query)`` looks like: + +.. code-block:: php + + setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('DoctrineExtensions\Paginate\CountSqlWalker')); + $countQuery->setFirstResult(null)->setMaxResults(null); + + return $countQuery->getSingleScalarResult(); + } + } + +It clones the query, resets the limit clause first and max results +and registers the ``CountSqlWalker`` custom tree walker which +will modify the AST to execute a count query. The walkers +implementation is: + +.. code-block:: php + + _getQueryComponents() as $dqlAlias => $qComp) { + if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { + $parent = $qComp; + $parentName = $dqlAlias; + break; + } + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + $parent['metadata']->getSingleIdentifierFieldName() + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + $AST->selectClause->selectExpressions = array( + new SelectExpression( + new AggregateExpression('count', $pathExpression, true), null + ) + ); + } + } + +This will delete any given select expressions and replace them with +a distinct count query for the root entities primary key. This will +only work if your entity has only one identifier field (composite +keys won't work). + +Modify the Output Walker to generate Vendor specific SQL +-------------------------------------------------------- + +Most RMDBS have vendor-specific features for optimizing select +query execution plans. You can write your own output walker to +introduce certain keywords using the Query Hint API. A query hint +can be set via ``Query::setHint($name, $value)`` as shown in the +previous example with the ``HINT_CUSTOM_TREE_WALKERS`` query hint. + +We will implement a custom Output Walker that allows to specify the +SQL\_NO\_CACHE query hint. + +.. code-block:: php + + createQuery($dql); + $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'DoctrineExtensions\Query\MysqlWalker'); + $query->setHint("mysqlWalker.sqlNoCache", true); + $results = $query->getResult(); + +Our ``MysqlWalker`` will extend the default ``SqlWalker``. We will +modify the generation of the SELECT clause, adding the +SQL\_NO\_CACHE on those queries that need it: + +.. code-block:: php + + getQuery()->getHint('mysqlWalker.sqlNoCache') === true) { + if ($selectClause->isDistinct) { + $sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql); + } else { + $sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql); + } + } + + return $sql; + } + } + +Writing extensions to the Output Walker requires a very deep +understanding of the DQL Parser and Walkers, but may offer your +huge benefits with using vendor specific features. This would still +allow you write DQL queries instead of NativeQueries to make use of +vendor specific features. + diff --git a/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst b/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst new file mode 100644 index 0000000..a3a7606 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/dql-user-defined-functions.rst @@ -0,0 +1,251 @@ +DQL User Defined Functions +========================== + +.. sectionauthor:: Benjamin Eberlei + +By default DQL supports a limited subset of all the vendor-specific +SQL functions common between all the vendors. However in many cases +once you have decided on a specific database vendor, you will never +change it during the life of your project. This decision for a +specific vendor potentially allows you to make use of powerful SQL +features that are unique to the vendor. + +It is worth to mention that Doctrine 2 also allows you to handwrite +your SQL instead of extending the DQL parser. Extending DQL is sort of an +advanced extension point. You can map arbitrary SQL to your objects +and gain access to vendor specific functionalities using the +``EntityManager#createNativeQuery()`` API as described in +the :doc:`Native Query <../reference/native-sql>` chapter. + + +The DQL Parser has hooks to register functions that can then be +used in your DQL queries and transformed into SQL, allowing to +extend Doctrines Query capabilities to the vendors strength. This +post explains the Used-Defined Functions API (UDF) of the Dql +Parser and shows some examples to give you some hints how you would +extend DQL. + +There are three types of functions in DQL, those that return a +numerical value, those that return a string and those that return a +Date. Your custom method has to be registered as either one of +those. The return type information is used by the DQL parser to +check possible syntax errors during the parsing process, for +example using a string function return value in a math expression. + +Registering your own DQL functions +---------------------------------- + +You can register your functions adding them to the ORM +configuration: + +.. code-block:: php + + addCustomStringFunction($name, $class); + $config->addCustomNumericFunction($name, $class); + $config->addCustomDatetimeFunction($name, $class); + + $em = EntityManager::create($dbParams, $config); + +The ``$name`` is the name the function will be referred to in the +DQL query. ``$class`` is a string of a class-name which has to +extend ``Doctrine\ORM\Query\Node\FunctionNode``. This is a class +that offers all the necessary API and methods to implement a UDF. + +Instead of providing the function class name, you can also provide +a callable that returns the function object: + +.. code-block:: php + + addCustomStringFunction($name, function () { + return new MyCustomFunction(); + }); + +In this post we will implement some MySql specific Date calculation +methods, which are quite handy in my opinion: + +Date Diff +--------- + +`Mysql's DateDiff function `_ +takes two dates as argument and calculates the difference in days +with ``date1-date2``. + +The DQL parser is a top-down recursive descent parser to generate +the Abstract-Syntax Tree (AST) and uses a TreeWalker approach to +generate the appropriate SQL from the AST. This makes reading the +Parser/TreeWalker code manageable in a finite amount of time. + +The ``FunctionNode`` class I referred to earlier requires you to +implement two methods, one for the parsing process (obviously) +called ``parse`` and one for the TreeWalker process called +``getSql()``. I show you the code for the DateDiff method and +discuss it step by step: + +.. code-block:: php + + match(Lexer::T_IDENTIFIER); // (2) + $parser->match(Lexer::T_OPEN_PARENTHESIS); // (3) + $this->firstDateExpression = $parser->ArithmeticPrimary(); // (4) + $parser->match(Lexer::T_COMMA); // (5) + $this->secondDateExpression = $parser->ArithmeticPrimary(); // (6) + $parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3) + } + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'DATEDIFF(' . + $this->firstDateExpression->dispatch($sqlWalker) . ', ' . + $this->secondDateExpression->dispatch($sqlWalker) . + ')'; // (7) + } + } + +The Parsing process of the DATEDIFF function is going to find two +expressions the date1 and the date2 values, whose AST Node +representations will be saved in the variables of the DateDiff +FunctionNode instance at (1). + +The parse() method has to cut the function call "DATEDIFF" and its +argument into pieces. Since the parser detects the function using a +lookahead the T\_IDENTIFIER of the function name has to be taken +from the stack (2), followed by a detection of the arguments in +(4)-(6). The opening and closing parenthesis have to be detected +also. This happens during the Parsing process and leads to the +generation of a DateDiff FunctionNode somewhere in the AST of the +dql statement. + +The ``ArithmeticPrimary`` method call is the most common +denominator of valid EBNF tokens taken from the +`DQL EBNF grammar `_ +that matches our requirements for valid input into the DateDiff Dql +function. Picking the right tokens for your methods is a tricky +business, but the EBNF grammar is pretty helpful finding it, as is +looking at the Parser source code. + +Now in the TreeWalker process we have to pick up this node and +generate SQL from it, which apparently is quite easy looking at the +code in (7). Since we don't know which type of AST Node the first +and second Date expression are we are just dispatching them back to +the SQL Walker to generate SQL from and then wrap our DATEDIFF +function call around this output. + +Now registering this DateDiff FunctionNode with the ORM using: + +.. code-block:: php + + addCustomStringFunction('DATEDIFF', 'DoctrineExtensions\Query\MySql\DateDiff'); + +We can do fancy stuff like: + +.. code-block:: sql + + SELECT p FROM DoctrineExtensions\Query\BlogPost p WHERE DATEDIFF(CURRENT_TIME(), p.created) < 7 + +Date Add +-------- + +Often useful it the ability to do some simple date calculations in +your DQL query using +`MySql's DATE\_ADD function `_. + +I'll skip the blah and show the code for this function: + +.. code-block:: php + + match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstDateExpression = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_COMMA); + $parser->match(Lexer::T_IDENTIFIER); + + $this->intervalExpression = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_IDENTIFIER); + + /* @var $lexer Lexer */ + $lexer = $parser->getLexer(); + $this->unit = $lexer->token['value']; + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'DATE_ADD(' . + $this->firstDateExpression->dispatch($sqlWalker) . ', INTERVAL ' . + $this->intervalExpression->dispatch($sqlWalker) . ' ' . $this->unit . + ')'; + } + } + +The only difference compared to the DATEDIFF here is, we +additionally need the ``Lexer`` to access the value of the +``T_IDENTIFIER`` token for the Date Interval unit, for example the +MONTH in: + +.. code-block:: sql + + SELECT p FROM DoctrineExtensions\Query\BlogPost p WHERE DATE_ADD(CURRENT_TIME(), INTERVAL 4 MONTH) > p.created + +The above method now only supports the specification using +``INTERVAL``, to also allow a real date in DATE\_ADD we need to add +some decision logic to the parsing process (makes up for a nice +exercise). + +Now as you see, the Parsing process doesn't catch all the possible +SQL errors, here we don't match for all the valid inputs for the +interval unit. However where necessary we rely on the database +vendors SQL parser to show us further errors in the parsing +process, for example if the Unit would not be one of the supported +values by MySql. + +Conclusion +---------- + +Now that you all know how you can implement vendor specific SQL +functionalities in DQL, we would be excited to see user extensions +that add vendor specific function packages, for example more math +functions, XML + GIS Support, Hashing functions and so on. + +For 2.0 we will come with the current set of functions, however for +a future version we will re-evaluate if we can abstract even more +vendor sql functions and extend the DQL languages scope. + +Code for this Extension to DQL and other Doctrine Extensions can be +found +`in my Github DoctrineExtensions repository `_. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst b/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst new file mode 100644 index 0000000..664cff5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/entities-in-session.rst @@ -0,0 +1,68 @@ +Entities in the Session +======================= + +There are several use-cases to save entities in the session, for example: + +1. User object +2. Multi-step forms + +To achieve this with Doctrine you have to pay attention to some details to get +this working. + +Merging entity into an EntityManager +------------------------------------ + +In Doctrine an entity objects has to be "managed" by an EntityManager to be +updateable. Entities saved into the session are not managed in the next request +anymore. This means that you have to register these entities with an +EntityManager again if you want to change them or use them as part of +references between other entities. You can achieve this by calling +``EntityManager#merge()``. + +For a representative User object the code to get turn an instance from +the session into a managed Doctrine object looks like this: + +.. code-block:: php + + merge($user); + } + +.. note:: + + A frequent mistake is not to get the merged user object from the return + value of ``EntityManager#merge()``. The entity object passed to merge is + not necessarily the same object that is returned from the method. + +Serializing entity into the session +----------------------------------- + +Entities that are serialized into the session normally contain references to +other entities as well. Think of the user entity has a reference to his +articles, groups, photos or many other different entities. If you serialize +this object into the session then you don't want to serialize the related +entities as well. This is why you should call ``EntityManager#detach()`` on this +object or implement the __sleep() magic method on your entity. + +.. code-block:: php + + find("User", 1); + $em->detach($user); + $_SESSION['user'] = $user; + +.. note:: + + When you called detach on your objects they get "unmanaged" with that + entity manager. This means you cannot use them as part of write operations + during ``EntityManager#flush()`` anymore in this request. + diff --git a/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst b/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst new file mode 100644 index 0000000..4eb2b71 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/implementing-arrayaccess-for-domain-objects.rst @@ -0,0 +1,112 @@ +Implementing ArrayAccess for Domain Objects +=========================================== + +.. sectionauthor:: Roman Borschel (roman@code-factory.org) + +This recipe will show you how to implement ArrayAccess for your +domain objects in order to allow more uniform access, for example +in templates. In these examples we will implement ArrayAccess on a +`Layer Supertype `_ +for all our domain objects. + +Option 1 +-------- + +In this implementation we will make use of PHPs highly dynamic +nature to dynamically access properties of a subtype in a supertype +at runtime. Note that this implementation has 2 main caveats: + + +- It will not work with private fields +- It will not go through any getters/setters + +.. code-block:: php + + $offset); + } + + public function offsetSet($offset, $value) { + $this->$offset = $value; + } + + public function offsetGet($offset) { + return $this->$offset; + } + + public function offsetUnset($offset) { + $this->$offset = null; + } + } + +Option 2 +-------- + +In this implementation we will dynamically invoke getters/setters. +Again we use PHPs dynamic nature to invoke methods on a subtype +from a supertype at runtime. This implementation has the following +caveats: + + +- It relies on a naming convention +- The semantics of offsetExists can differ +- offsetUnset will not work with typehinted setters + +.. code-block:: php + + {"get$offset"}(); + return $value !== null; + } + + public function offsetSet($offset, $value) { + $this->{"set$offset"}($value); + } + + public function offsetGet($offset) { + return $this->{"get$offset"}(); + } + + public function offsetUnset($offset) { + $this->{"set$offset"}(null); + } + } + +Read-only +--------- + +You can slightly tweak option 1 or option 2 in order to make array +access read-only. This will also circumvent some of the caveats of +each option. Simply make offsetSet and offsetUnset throw an +exception (i.e. BadMethodCallException). + +.. code-block:: php + + `_ +for all our domain objects. + +Implementing NotifyPropertyChanged +---------------------------------- + +The NOTIFY policy is based on the assumption that the entities +notify interested listeners of changes to their properties. For +that purpose, a class that wants to use this policy needs to +implement the ``NotifyPropertyChanged`` interface from the +``Doctrine\Common`` namespace. + +.. code-block:: php + + listeners[] = $listener; + } + + /** Notifies listeners of a change. */ + protected function onPropertyChanged($propName, $oldValue, $newValue) { + if ($this->listeners) { + foreach ($this->listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } + } + +Then, in each property setter of concrete, derived domain classes, +you need to invoke onPropertyChanged as follows to notify +listeners: + +.. code-block:: php + + data) { // check: is it actually modified? + $this->onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + } + +The check whether the new value is different from the old one is +not mandatory but recommended. That way you can avoid unnecessary +updates and also have full control over when you consider a +property changed. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst b/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst new file mode 100644 index 0000000..6a8ef9c --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/implementing-wakeup-or-clone.rst @@ -0,0 +1,78 @@ +Implementing Wakeup or Clone +============================ + +.. sectionauthor:: Roman Borschel (roman@code-factory.org) + +As explained in the +`restrictions for entity classes in the manual `_, +it is usually not allowed for an entity to implement ``__wakeup`` +or ``__clone``, because Doctrine makes special use of them. +However, it is quite easy to make use of these methods in a safe +way by guarding the custom wakeup or clone code with an entity +identity check, as demonstrated in the following sections. + +Safely implementing \_\_wakeup +------------------------------ + +To safely implement ``__wakeup``, simply enclose your +implementation code in an identity check as follows: + +.. code-block:: php + + id) { + // ... Your code here as normal ... + } + // otherwise do nothing, do NOT throw an exception! + } + + //... + } + +Safely implementing \_\_clone +----------------------------- + +Safely implementing ``__clone`` is pretty much the same: + +.. code-block:: php + + id) { + // ... Your code here as normal ... + } + // otherwise do nothing, do NOT throw an exception! + } + + //... + } + +Summary +------- + +As you have seen, it is quite easy to safely make use of +``__wakeup`` and ``__clone`` in your entities without adding any +really Doctrine-specific or Doctrine-dependant code. + +These implementations are possible and safe because when Doctrine +invokes these methods, the entities never have an identity (yet). +Furthermore, it is possibly a good idea to check for the identity +in your code anyway, since it's rarely the case that you want to +unserialize or clone an entity with no identity. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst b/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst new file mode 100644 index 0000000..1c06a34 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/integrating-with-codeigniter.rst @@ -0,0 +1,140 @@ +Integrating with CodeIgniter +============================ + +This is recipe for using Doctrine 2 in your +`CodeIgniter `_ framework. + +.. note:: + + This might not work for all CodeIgniter versions and may require + slight adjustments. + + +Here is how to set it up: + +Make a CodeIgniter library that is both a wrapper and a bootstrap +for Doctrine 2. + +Setting up the file structure +----------------------------- + +Here are the steps: + + +- Add a php file to your system/application/libraries folder + called Doctrine.php. This is going to be your wrapper/bootstrap for + the D2 entity manager. +- Put the Doctrine folder (the one that contains Common, DBAL, and + ORM) inside that same libraries folder. +- Your system/application/libraries folder now looks like this: + + system/applications/libraries -Doctrine -Doctrine.php -index.html + +- If you want, open your config/autoload.php file and autoload + your Doctrine library. + + register(); + $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" )); + $entitiesClassLoader->register(); + $proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies'); + $proxiesClassLoader->register(); + + // Set up caches + $config = new Configuration; + $cache = new ArrayCache; + $config->setMetadataCacheImpl($cache); + $driverImpl = $config->newDefaultAnnotationDriver(array(APPPATH.'models/Entities')); + $config->setMetadataDriverImpl($driverImpl); + $config->setQueryCacheImpl($cache); + + $config->setQueryCacheImpl($cache); + + // Proxy configuration + $config->setProxyDir(APPPATH.'/models/proxies'); + $config->setProxyNamespace('Proxies'); + + // Set up logger + $logger = new EchoSQLLogger; + $config->setSQLLogger($logger); + + $config->setAutoGenerateProxyClasses( TRUE ); + + // Database connection information + $connectionOptions = array( + 'driver' => 'pdo_mysql', + 'user' => $db['default']['username'], + 'password' => $db['default']['password'], + 'host' => $db['default']['hostname'], + 'dbname' => $db['default']['database'] + ); + + // Create EntityManager + $this->em = EntityManager::create($connectionOptions, $config); + } + } + +Please note that this is a development configuration; for a +production system you'll want to use a real caching system like +APC, get rid of EchoSqlLogger, and turn off +autoGenerateProxyClasses. + +For more details, consult the +`Doctrine 2 Configuration documentation `_. + +Now to use it +------------- + +Whenever you need a reference to the entity manager inside one of +your controllers, views, or models you can do this: + +.. code-block:: php + + doctrine->em; + +That's all there is to it. Once you get the reference to your +EntityManager do your Doctrine 2.0 voodoo as normal. + +Note: If you do not choose to autoload the Doctrine library, you +will need to put this line before you get a reference to it: + +.. code-block:: php + + load->library('doctrine'); + +Good luck! + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst b/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst new file mode 100644 index 0000000..fcc0451 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/mysql-enums.rst @@ -0,0 +1,189 @@ +Mysql Enums +=========== + +The type system of Doctrine 2 consists of flyweights, which means there is only +one instance of any given type. Additionally types do not contain state. Both +assumptions make it rather complicated to work with the Enum Type of MySQL that +is used quite a lot by developers. + +When using Enums with a non-tweaked Doctrine 2 application you will get +errors from the Schema-Tool commands due to the unknown database type "enum". +By default Doctrine does not map the MySQL enum type to a Doctrine type. +This is because Enums contain state (their allowed values) and Doctrine +types don't. + +This cookbook entry shows two possible solutions to work with MySQL enums. +But first a word of warning. The MySQL Enum type has considerable downsides: + +- Adding new values requires to rebuild the whole table, which can take hours + depending on the size. +- Enums are ordered in the way the values are specified, not in their "natural" order. +- Enums validation mechanism for allowed values is not necessarily good, + specifying invalid values leads to an empty enum for the default MySQL error + settings. You can easily replicate the "allow only some values" requirement + in your Doctrine entities. + +Solution 1: Mapping to Varchars +------------------------------- + +You can map ENUMs to varchars. You can register MySQL ENUMs to map to Doctrine +varchars. This way Doctrine always resolves ENUMs to Doctrine varchars. It +will even detect this match correctly when using SchemaTool update commands. + +.. code-block:: php + + getConnection(); + $conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string'); + +In this case you have to ensure that each varchar field that is an enum in the +database only gets passed the allowed values. You can easily enforce this in your +entities: + +.. code-block:: php + + status = $status; + } + } + +If you want to actively create enums through the Doctrine Schema-Tool by using +the **columnDefinition** attribute. + +.. code-block:: php + + values); + + return "ENUM(".implode(", ", $values).") COMMENT '(DC2Type:".$this->name.")'"; + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return $value; + } + + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if (!in_array($value, $this->values)) { + throw new \InvalidArgumentException("Invalid '".$this->name."' value."); + } + return $value; + } + + public function getName() + { + return $this->name; + } + } + +With this base class you can define an enum as easily as: + +.. code-block:: php + + addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array()); + + // Add the ResolveTargetEntityListener + $evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + +Final Thoughts +-------------- + +With the ``ResolveTargetEntityListener``, we are able to decouple our +bundles, keeping them usable by themselves, but still being able to +define relationships between different objects. By using this method, +I've found my bundles end up being easier to maintain independently. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst b/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst new file mode 100644 index 0000000..fdfd403 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/sql-table-prefixes.rst @@ -0,0 +1,84 @@ +SQL-Table Prefixes +================== + +This recipe is intended as an example of implementing a +loadClassMetadata listener to provide a Table Prefix option for +your application. The method used below is not a hack, but fully +integrates into the Doctrine system, all SQL generated will include +the appropriate table prefix. + +In most circumstances it is desirable to separate different +applications into individual databases, but in certain cases, it +may be beneficial to have a table prefix for your Entities to +separate them from other vendor products in the same database. + +Implementing the listener +------------------------- + +The listener in this example has been set up with the +DoctrineExtensions namespace. You create this file in your +library/DoctrineExtensions directory, but will need to set up +appropriate autoloaders. + +.. code-block:: php + + prefix = (string) $prefix; + } + + public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + + if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) { + $classMetadata->setTableName($this->prefix . $classMetadata->getTableName()); + } + + foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { + if ($mapping['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) { + $mappedTableName = $mapping['joinTable']['name']; + $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; + } + } + } + + } + +Telling the EntityManager about our listener +-------------------------------------------- + +A listener of this type must be set up before the EntityManager has +been initialised, otherwise an Entity might be created or cached +before the prefix has been set. + +.. note:: + + If you set this listener up, be aware that you will need + to clear your caches and drop then recreate your database schema. + + +.. code-block:: php + + addEventListener(\Doctrine\ORM\Events::loadClassMetadata, $tablePrefix); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst b/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst new file mode 100644 index 0000000..d9934f5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/strategy-cookbook-introduction.rst @@ -0,0 +1,254 @@ +Strategy-Pattern +================ + +This recipe will give you a short introduction on how to design +similar entities without using expensive (i.e. slow) inheritance +but with not more than \* the well-known strategy pattern \* event +listeners + +Scenario / Problem +------------------ + +Given a Content-Management-System, we probably want to add / edit +some so-called "blocks" and "panels". What are they for? + + +- A block might be a registration form, some text content, a table + with information. A good example might also be a small calendar. +- A panel is by definition a block that can itself contain blocks. + A good example for a panel might be a sidebar box: You could easily + add a small calendar into it. + +So, in this scenario, when building your CMS, you will surely add +lots of blocks and panels to your pages and you will find yourself +highly uncomfortable because of the following: + + +- Every existing page needs to know about the panels it contains - + therefore, you'll have an association to your panels. But if you've + got several types of panels - what do you do? Add an association to + every panel-type? This wouldn't be flexible. You might be tempted + to add an AbstractPanelEntity and an AbstractBlockEntity that use + class inheritance. Your page could then only confer to the + AbstractPanelType and Doctrine 2 would do the rest for you, i.e. + load the right entities. But - you'll for sure have lots of panels + and blocks, and even worse, you'd have to edit the discriminator + map *manually* every time you or another developer implements a new + block / entity. This would tear down any effort of modular + programming. + +Therefore, we need something that's far more flexible. + +Solution +-------- + +The solution itself is pretty easy. We will have one base class +that will be loaded via the page and that has specific behaviour - +a Block class might render the front-end and even the backend, for +example. Now, every block that you'll write might look different or +need different data - therefore, we'll offer an API to these +methods but internally, we use a strategy that exactly knows what +to do. + +First of all, we need to make sure that we have an interface that +contains every needed action. Such actions would be rendering the +front-end or the backend, solving dependencies (blocks that are +supposed to be placed in the sidebar could refuse to be placed in +the middle of your page, for example). + +Such an interface could look like this: + + +.. code-block:: php + + blockStrategy. Will not be persisted by Doctrine 2. + * + * @var BlockStrategyInterface + */ + protected $strategyInstance; + + /** + * Returns the strategy that is used for this blockitem. + * + * The strategy itself defines how this block can be rendered etc. + * + * @return string + */ + public function getStrategyClassName() { + return $this->strategyClassName; + } + + /** + * Returns the instantiated strategy + * + * @return BlockStrategyInterface + */ + public function getStrategyInstance() { + return $this->strategyInstance; + } + + /** + * Sets the strategy this block / panel should work as. Make sure that you've used + * this method before persisting the block! + * + * @param BlockStrategyInterface $strategy + */ + public function setStrategy(BlockStrategyInterface $strategy) { + $this->strategyInstance = $strategy; + $this->strategyClassName = get_class($strategy); + $strategy->setBlockEntity($this); + } + +Now, the important point is that $strategyClassName is a Doctrine 2 +field, i.e. Doctrine will persist this value. This is only the +class name of your strategy and not an instance! + +Finishing your strategy pattern, we hook into the Doctrine postLoad +event and check whether a block has been loaded. If so, you will +initialize it - i.e. get the strategies classname, create an +instance of it and set it via setStrategyBlock(). + +This might look like this: + +.. code-block:: php + + view = $view; + } + + public function getSubscribedEvents() { + return array(ORM\Events::postLoad); + } + + public function postLoad(ORM\Event\LifecycleEventArgs $args) { + $blockItem = $args->getEntity(); + + // Both blocks and panels are instances of Block\AbstractBlock + if ($blockItem instanceof Block\AbstractBlock) { + $strategy = $blockItem->getStrategyClassName(); + $strategyInstance = new $strategy(); + if (null !== $blockItem->getConfig()) { + $strategyInstance->setConfig($blockItem->getConfig()); + } + $strategyInstance->setView($this->view); + $blockItem->setStrategy($strategyInstance); + } + } + } + +In this example, even some variables are set - like a view object +or a specific configuration object. + + diff --git a/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst b/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst new file mode 100644 index 0000000..fb02544 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/validation-of-entities.rst @@ -0,0 +1,137 @@ +Validation of Entities +====================== + +.. sectionauthor:: Benjamin Eberlei + +Doctrine 2 does not ship with any internal validators, the reason +being that we think all the frameworks out there already ship with +quite decent ones that can be integrated into your Domain easily. +What we offer are hooks to execute any kind of validation. + +.. note:: + + You don't need to validate your entities in the lifecycle + events. Its only one of many options. Of course you can also + perform validations in value setters or any other method of your + entities that are used in your code. + + +Entities can register lifecycle event methods with Doctrine that +are called on different occasions. For validation we would need to +hook into the events called before persisting and updating. Even +though we don't support validation out of the box, the +implementation is even simpler than in Doctrine 1 and you will get +the additional benefit of being able to re-use your validation in +any other part of your domain. + +Say we have an ``Order`` with several ``OrderLine`` instances. We +never want to allow any customer to order for a larger sum than he +is allowed to: + +.. code-block:: php + + customer->getOrderLimit(); + + $amount = 0; + foreach ($this->orderLines as $line) { + $amount += $line->getAmount(); + } + + if ($amount > $orderLimit) { + throw new CustomerOrderLimitExceededException(); + } + } + } + +Now this is some pretty important piece of business logic in your +code, enforcing it at any time is important so that customers with +a unknown reputation don't owe your business too much money. + +We can enforce this constraint in any of the metadata drivers. +First Annotations: + +.. code-block:: php + + + + + + + + + + +YAML needs some little change yet, to allow multiple lifecycle +events for one method, this will happen before Beta 1 though. + +Now validation is performed whenever you call +``EntityManager#persist($order)`` or when you call +``EntityManager#flush()`` and an order is about to be updated. Any +Exception that happens in the lifecycle callbacks will be cached by +the EntityManager and the current transaction is rolled back. + +Of course you can do any type of primitive checks, not null, +email-validation, string size, integer and date ranges in your +validation callbacks. + +.. code-block:: php + + plannedShipDate instanceof DateTime)) { + throw new ValidateException(); + } + + if ($this->plannedShipDate->format('U') < time()) { + throw new ValidateException(); + } + + if ($this->customer == null) { + throw new OrderRequiresCustomerException(); + } + } + } + +What is nice about lifecycle events is, you can also re-use the +methods at other places in your domain, for example in combination +with your form library. Additionally there is no limitation in the +number of methods you register on one particular event, i.e. you +can register multiple methods for validation in "PrePersist" or +"PreUpdate" or mix and share them in any combinations between those +two events. + +There is no limit to what you can and can't validate in +"PrePersist" and "PreUpdate" as long as you don't create new entity +instances. This was already discussed in the previous blog post on +the Versionable extension, which requires another type of event +called "onFlush". + +Further readings: :doc:`Lifecycle Events <../reference/events>` diff --git a/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst b/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst new file mode 100644 index 0000000..fc548da --- /dev/null +++ b/vendor/doctrine/orm/docs/en/cookbook/working-with-datetime.rst @@ -0,0 +1,168 @@ +Working with DateTime Instances +=============================== + +There are many nitty gritty details when working with PHPs DateTime instances. You have know their inner +workings pretty well not to make mistakes with date handling. This cookbook entry holds several +interesting pieces of information on how to work with PHP DateTime instances in Doctrine 2. + +DateTime changes are detected by Reference +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When calling ``EntityManager#flush()`` Doctrine computes the changesets of all the currently managed entities +and saves the differences to the database. In case of object properties (@Column(type="datetime") or @Column(type="object")) +these comparisons are always made **BY REFERENCE**. That means the following change will **NOT** be saved into the database: + +.. code-block:: php + + updated->modify("now"); + } + } + +The way to go would be: + +.. code-block:: php + + updated = new \DateTime("now"); + } + } + +Default Timezone Gotcha +~~~~~~~~~~~~~~~~~~~~~~~ + +By default Doctrine assumes that you are working with a default timezone. Each DateTime instance that +is created by Doctrine will be assigned the timezone that is currently the default, either through +the ``date.timezone`` ini setting or by calling ``date_default_timezone_set()``. + +This is very important to handle correctly if your application runs on different serves or is moved from one to another server +(with different timezone settings). You have to make sure that the timezone is the correct one +on all this systems. + +Handling different Timezones with the DateTime Type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you first come across the requirement to save different you are still optimistic to manage this mess, +however let me crush your expectations fast. There is not a single database out there (supported by Doctrine 2) +that supports timezones correctly. Correctly here means that you can cover all the use-cases that +can come up with timezones. If you don't believe me you should read up on `Storing DateTime +in Databases `_. + +The problem is simple. Not a single database vendor saves the timezone, only the differences to UTC. +However with frequent daylight saving and political timezone changes you can have a UTC offset that moves +in different offset directions depending on the real location. + +The solution for this dilemma is simple. Don't use timezones with DateTime and Doctrine 2. However there is a workaround +that even allows correct date-time handling with timezones: + +1. Always convert any DateTime instance to UTC. +2. Only set Timezones for displaying purposes +3. Save the Timezone in the Entity for persistence. + +Say we have an application for an international postal company and employees insert events regarding postal-package +around the world, in their current timezones. To determine the exact time an event occurred means to save both +the UTC time at the time of the booking and the timezone the event happened in. + +.. code-block:: php + + format($platform->getDateTimeFormatString(), + (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC')) + ); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + $val = \DateTime::createFromFormat( + $platform->getDateTimeFormatString(), + $value, + (self::$utc) ? self::$utc : (self::$utc = new \DateTimeZone('UTC')) + ); + if (!$val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + return $val; + } + } + +This database type makes sure that every DateTime instance is always saved in UTC, relative +to the current timezone that the passed DateTime instance has. To be able to transform these values +back into their real timezone you have to save the timezone in a separate field of the entity +requiring timezoned datetimes: + +.. code-block:: php + + localized = true; + $this->created = $createDate; + $this->timezone = $createDate->getTimeZone()->getName(); + } + + public function getCreated() + { + if (!$this->localized) { + $this->created->setTimeZone(new \DateTimeZone($this->timezone)); + } + return $this->created; + } + } + +This snippet makes use of the previously discussed "changeset by reference only" property of +objects. That means a new DateTime will only be used during updating if the reference +changes between retrieval and flush operation. This means we can easily go and modify +the instance by setting the previous local timezone. diff --git a/vendor/doctrine/orm/docs/en/index.rst b/vendor/doctrine/orm/docs/en/index.rst new file mode 100644 index 0000000..8e6afc8 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/index.rst @@ -0,0 +1,131 @@ +Welcome to Doctrine 2 ORM's documentation! +========================================== + +The Doctrine documentation is comprised of tutorials, a reference section and +cookbook articles that explain different parts of the Object Relational mapper. + +Doctrine DBAL and Doctrine Common both have their own documentation. + +Getting Help +------------ + +If this documentation is not helping to answer questions you have about +Doctrine ORM don't panic. You can get help from different sources: + +- There is a :doc:`FAQ ` with answers to frequent questions. +- The `Doctrine Mailing List `_ +- Internet Relay Chat (IRC) in #doctrine on Freenode +- Report a bug on `JIRA `_. +- On `Twitter `_ with ``#doctrine2`` +- On `StackOverflow `_ + +If you need more structure over the different topics you can browse the :doc:`table +of contents `. + +Getting Started +--------------- + +* **Tutorial**: + :doc:`Getting Started with Doctrine ` + +* **Setup**: + :doc:`Installation & Configuration ` + +Mapping Objects onto a Database +------------------------------- + +* **Mapping**: + :doc:`Objects ` | + :doc:`Associations ` | + :doc:`Inheritance ` + +* **Drivers**: + :doc:`Docblock Annotations ` | + :doc:`XML ` | + :doc:`YAML ` | + :doc:`PHP ` + +Working with Objects +-------------------- + +* **Basic Reference**: + :doc:`Entities ` | + :doc:`Associations ` | + :doc:`Events ` + +* **Query Reference**: + :doc:`DQL ` | + :doc:`QueryBuilder ` | + :doc:`Native SQL ` + +* **Internals**: + :doc:`Internals explained ` | + :doc:`Associations ` + +Advanced Topics +--------------- + +* :doc:`Architecture ` +* :doc:`Advanced Configuration ` +* :doc:`Limitations and known issues ` +* :doc:`Commandline Tools ` +* :doc:`Transactions and Concurrency ` +* :doc:`Filters ` +* :doc:`NamingStrategy ` +* :doc:`Improving Performance ` +* :doc:`Caching ` +* :doc:`Partial Objects ` +* :doc:`Change Tracking Policies ` +* :doc:`Best Practices ` +* :doc:`Metadata Drivers ` +* :doc:`Batch Processing ` +* :doc:`Second Level Cache ` + +Tutorials +--------- + +* :doc:`Indexed associations ` +* :doc:`Extra Lazy Associations ` +* :doc:`Composite Primary Keys ` +* :doc:`Ordered associations ` +* :doc:`Pagination ` +* :doc:`Override Field/Association Mappings In Subclasses ` +* :doc:`Embeddables ` + +Changelogs +---------- + +* :doc:`Migration to 2.5 ` + +Cookbook +-------- + +* **Patterns**: + :doc:`Aggregate Fields ` | + :doc:`Decorator Pattern ` | + :doc:`Strategy Pattern ` + +* **DQL Extension Points**: + :doc:`DQL Custom Walkers ` | + :doc:`DQL User-Defined-Functions ` + +* **Implementation**: + :doc:`Array Access ` | + :doc:`Notify ChangeTracking Example ` | + :doc:`Using Wakeup Or Clone ` | + :doc:`Working with DateTime ` | + :doc:`Validation ` | + :doc:`Entities in the Session ` | + :doc:`Keeping your Modules independent ` + +* **Integration into Frameworks/Libraries** + :doc:`CodeIgniter ` + +* **Hidden Gems** + :doc:`Prefixing Table Name ` + +* **Custom Datatypes** + :doc:`MySQL Enums ` + :doc:`Advanced Field Value Conversion ` + +.. include:: toc.rst diff --git a/vendor/doctrine/orm/docs/en/make.bat b/vendor/doctrine/orm/docs/en/make.bat new file mode 100644 index 0000000..53c40c9 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/make.bat @@ -0,0 +1,113 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +set SPHINXBUILD=sphinx-build +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Doctrine2ORM.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Doctrine2ORM.ghc + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst b/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst new file mode 100644 index 0000000..9d200f8 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/advanced-configuration.rst @@ -0,0 +1,468 @@ +Advanced Configuration +====================== + +The configuration of the EntityManager requires a +``Doctrine\ORM\Configuration`` instance as well as some database +connection parameters. This example shows all the potential +steps of configuration. + +.. code-block:: php + + setMetadataCacheImpl($cache); + $driverImpl = $config->newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $config->setMetadataDriverImpl($driverImpl); + $config->setQueryCacheImpl($cache); + $config->setProxyDir('/path/to/myproject/lib/MyProject/Proxies'); + $config->setProxyNamespace('MyProject\Proxies'); + + if ($applicationMode == "development") { + $config->setAutoGenerateProxyClasses(true); + } else { + $config->setAutoGenerateProxyClasses(false); + } + + $connectionOptions = array( + 'driver' => 'pdo_sqlite', + 'path' => 'database.sqlite' + ); + + $em = EntityManager::create($connectionOptions, $config); + +.. note:: + + Do not use Doctrine without a metadata and query cache! + Doctrine is optimized for working with caches. The main + parts in Doctrine that are optimized for caching are the metadata + mapping information with the metadata cache and the DQL to SQL + conversions with the query cache. These 2 caches require only an + absolute minimum of memory yet they heavily improve the runtime + performance of Doctrine. The recommended cache driver to use with + Doctrine is `APC `_. APC provides you with + an opcode-cache (which is highly recommended anyway) and a very + fast in-memory cache storage that you can use for the metadata and + query caches as seen in the previous code snippet. + +Configuration Options +--------------------- + +The following sections describe all the configuration options +available on a ``Doctrine\ORM\Configuration`` instance. + +Proxy Directory (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setProxyDir($dir); + $config->getProxyDir(); + +Gets or sets the directory where Doctrine generates any proxy +classes. For a detailed explanation on proxy classes and how they +are used in Doctrine, refer to the "Proxy Objects" section further +down. + +Proxy Namespace (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setProxyNamespace($namespace); + $config->getProxyNamespace(); + +Gets or sets the namespace to use for generated proxy classes. For +a detailed explanation on proxy classes and how they are used in +Doctrine, refer to the "Proxy Objects" section further down. + +Metadata Driver (***REQUIRED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setMetadataDriverImpl($driver); + $config->getMetadataDriverImpl(); + +Gets or sets the metadata driver implementation that is used by +Doctrine to acquire the object-relational metadata for your +classes. + +There are currently 4 available implementations: + + +- ``Doctrine\ORM\Mapping\Driver\AnnotationDriver`` +- ``Doctrine\ORM\Mapping\Driver\XmlDriver`` +- ``Doctrine\ORM\Mapping\Driver\YamlDriver`` +- ``Doctrine\ORM\Mapping\Driver\DriverChain`` + +Throughout the most part of this manual the AnnotationDriver is +used in the examples. For information on the usage of the XmlDriver +or YamlDriver please refer to the dedicated chapters +``XML Mapping`` and ``YAML Mapping``. + +The annotation driver can be configured with a factory method on +the ``Doctrine\ORM\Configuration``: + +.. code-block:: php + + newDefaultAnnotationDriver('/path/to/lib/MyProject/Entities'); + $config->setMetadataDriverImpl($driverImpl); + +The path information to the entities is required for the annotation +driver, because otherwise mass-operations on all entities through +the console could not work correctly. All of metadata drivers +accept either a single directory as a string or an array of +directories. With this feature a single driver can support multiple +directories of Entities. + +Metadata Cache (***RECOMMENDED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setMetadataCacheImpl($cache); + $config->getMetadataCacheImpl(); + +Gets or sets the cache implementation to use for caching metadata +information, that is, all the information you supply via +annotations, xml or yaml, so that they do not need to be parsed and +loaded from scratch on every single request which is a waste of +resources. The cache implementation must implement the +``Doctrine\Common\Cache\Cache`` interface. + +Usage of a metadata cache is highly recommended. + +The recommended implementations for production are: + + +- ``Doctrine\Common\Cache\ApcCache`` +- ``Doctrine\Common\Cache\MemcacheCache`` +- ``Doctrine\Common\Cache\XcacheCache`` +- ``Doctrine\Common\Cache\RedisCache`` + +For development you should use the +``Doctrine\Common\Cache\ArrayCache`` which only caches data on a +per-request basis. + +Query Cache (***RECOMMENDED***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setQueryCacheImpl($cache); + $config->getQueryCacheImpl(); + +Gets or sets the cache implementation to use for caching DQL +queries, that is, the result of a DQL parsing process that includes +the final SQL as well as meta information about how to process the +SQL result set of a query. Note that the query cache does not +affect query results. You do not get stale data. This is a pure +optimization cache without any negative side-effects (except some +minimal memory usage in your cache). + +Usage of a query cache is highly recommended. + +The recommended implementations for production are: + + +- ``Doctrine\Common\Cache\ApcCache`` +- ``Doctrine\Common\Cache\MemcacheCache`` +- ``Doctrine\Common\Cache\XcacheCache`` +- ``Doctrine\Common\Cache\RedisCache`` + +For development you should use the +``Doctrine\Common\Cache\ArrayCache`` which only caches data on a +per-request basis. + +SQL Logger (***Optional***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + setSQLLogger($logger); + $config->getSQLLogger(); + +Gets or sets the logger to use for logging all SQL statements +executed by Doctrine. The logger class must implement the +``Doctrine\DBAL\Logging\SQLLogger`` interface. A simple default +implementation that logs to the standard output using ``echo`` and +``var_dump`` can be found at +``Doctrine\DBAL\Logging\EchoSQLLogger``. + +Auto-generating Proxy Classes (***OPTIONAL***) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Proxy classes can either be generated manually through the Doctrine +Console or automatically at runtime by Doctrine. The configuration +option that controls this behavior is: + +.. code-block:: php + + setAutoGenerateProxyClasses($mode); + +Possible values for ``$mode`` are: + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_NEVER`` + +Never autogenerate a proxy. You will need to generate the proxies +manually, for this use the Doctrine Console like so: + +.. code-block:: php + + $ ./doctrine orm:generate-proxies + +When you do this in a development environment, +be aware that you may get class/file not found errors if certain proxies +are not yet generated. You may also get failing lazy-loads if new +methods were added to the entity class that are not yet in the proxy class. +In such a case, simply use the Doctrine Console to (re)generate the +proxy classes. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_ALWAYS`` + +Always generates a new proxy in every request and writes it to disk. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS`` + +Generate the proxy class when the proxy file does not exist. +This strategy causes a file exists call whenever any proxy is +used the first time in a request. + +- ``Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_EVAL`` + +Generate the proxy classes and evaluate them on the fly via eval(), +avoiding writing the proxies to disk. +This strategy is only sane for development. + +In a production environment, it is highly recommended to use +AUTOGENERATE_NEVER to allow for optimal performances. The other +options are interesting in development environment. + +Before v2.4, ``setAutoGenerateProxyClasses`` would accept a boolean +value. This is still possible, ``FALSE`` being equivalent to +AUTOGENERATE_NEVER and ``TRUE`` to AUTOGENERATE_ALWAYS. + +Development vs Production Configuration +--------------------------------------- + +You should code your Doctrine2 bootstrapping with two different +runtime models in mind. There are some serious benefits of using +APC or Memcache in production. In development however this will +frequently give you fatal errors, when you change your entities and +the cache still keeps the outdated metadata. That is why we +recommend the ``ArrayCache`` for development. + +Furthermore you should have the Auto-generating Proxy Classes +option to true in development and to false in production. If this +option is set to ``TRUE`` it can seriously hurt your script +performance if several proxy classes are re-generated during script +execution. Filesystem calls of that magnitude can even slower than +all the database queries Doctrine issues. Additionally writing a +proxy sets an exclusive file lock which can cause serious +performance bottlenecks in systems with regular concurrent +requests. + +Connection Options +------------------ + +The ``$connectionOptions`` passed as the first argument to +``EntityManager::create()`` has to be either an array or an +instance of ``Doctrine\DBAL\Connection``. If an array is passed it +is directly passed along to the DBAL Factory +``Doctrine\DBAL\DriverManager::getConnection()``. The DBAL +configuration is explained in the +`DBAL section <./../../../../../projects/doctrine-dbal/en/latest/reference/configuration.html>`_. + +Proxy Objects +------------- + +A proxy object is an object that is put in place or used instead of +the "real" object. A proxy object can add behavior to the object +being proxied without that object being aware of it. In Doctrine 2, +proxy objects are used to realize several features but mainly for +transparent lazy-loading. + +Proxy objects with their lazy-loading facilities help to keep the +subset of objects that are already in memory connected to the rest +of the objects. This is an essential property as without it there +would always be fragile partial objects at the outer edges of your +object graph. + +Doctrine 2 implements a variant of the proxy pattern where it +generates classes that extend your entity classes and adds +lazy-loading capabilities to them. Doctrine can then give you an +instance of such a proxy class whenever you request an object of +the class being proxied. This happens in two situations: + +Reference Proxies +~~~~~~~~~~~~~~~~~ + +The method ``EntityManager#getReference($entityName, $identifier)`` +lets you obtain a reference to an entity for which the identifier +is known, without loading that entity from the database. This is +useful, for example, as a performance enhancement, when you want to +establish an association to an entity for which you have the +identifier. You could simply do this: + +.. code-block:: php + + getReference('MyProject\Model\Item', $itemId); + $cart->addItem($item); + +Here, we added an Item to a Cart without loading the Item from the +database. If you invoke any method on the Item instance, it would +fully initialize its state transparently from the database. Here +$item is actually an instance of the proxy class that was generated +for the Item class but your code does not need to care. In fact it +**should not care**. Proxy objects should be transparent to your +code. + +Association proxies +~~~~~~~~~~~~~~~~~~~ + +The second most important situation where Doctrine uses proxy +objects is when querying for objects. Whenever you query for an +object that has a single-valued association to another object that +is configured LAZY, without joining that association in the same +query, Doctrine puts proxy objects in place where normally the +associated object would be. Just like other proxies it will +transparently initialize itself on first access. + +.. note:: + + Joining an association in a DQL or native query + essentially means eager loading of that association in that query. + This will override the 'fetch' option specified in the mapping for + that association, but only for that query. + + +Generating Proxy classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +In a production environment, it is highly recommended to use +``AUTOGENERATE_NEVER`` to allow for optimal performances. +However you will be required to generate the proxies manually +using the Doctrine Console: + +.. code-block:: php + + $ ./doctrine orm:generate-proxies + +The other options are interesting in development environment: + +- ``AUTOGENERATE_ALWAYS`` will require you to create and configure + a proxy directory. Proxies will be generated and written to file + on each request, so any modification to your code will be acknowledged. + +- ``AUTOGENERATE_FILE_NOT_EXISTS`` will not overwrite an existing + proxy file. If your code changes, you will need to regenerate the + proxies manually. + +- ``AUTOGENERATE_EVAL`` will regenerate each proxy on each request, + but without writing them to disk. + +Autoloading Proxies +------------------- + +When you deserialize proxy objects from the session or any other storage +it is necessary to have an autoloading mechanism in place for these classes. +For implementation reasons Proxy class names are not PSR-0 compliant. This +means that you have to register a special autoloader for these classes: + +.. code-block:: php + + addDriver($xmlDriver, 'Doctrine\Tests\Models\Company'); + $chain->addDriver($yamlDriver, 'Doctrine\Tests\ORM\Mapping'); + +Based on the namespace of the entity the loading of entities is +delegated to the appropriate driver. The chain semantics come from +the fact that the driver loops through all namespaces and matches +the entity class name against the namespace using a +``strpos() === 0`` call. This means you need to order the drivers +correctly if sub-namespaces use different metadata driver +implementations. + + +Default Repository (***OPTIONAL***) +----------------------------------- + +Specifies the FQCN of a subclass of the EntityRepository. +That will be available for all entities without a custom repository class. + +.. code-block:: php + + setDefaultRepositoryClassName($fqcn); + $config->getDefaultRepositoryClassName(); + +The default value is ``Doctrine\ORM\EntityRepository``. +Any repository class must be a subclass of EntityRepository otherwise you got an ORMException + +Setting up the Console +---------------------- + +Doctrine uses the Symfony Console component for generating the command +line interface. You can take a look at the ``vendor/bin/doctrine.php`` +script and the ``Doctrine\ORM\Tools\Console\ConsoleRunner`` command +for inspiration how to setup the cli. + +In general the required code looks like this: + +.. code-block:: php + + setCatchExceptions(true); + $cli->setHelperSet($helperSet); + Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($cli); + $cli->run(); + diff --git a/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst b/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst new file mode 100644 index 0000000..d6d700b --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/annotations-reference.rst @@ -0,0 +1,1231 @@ +Annotations Reference +===================== + +You've probably used docblock annotations in some form already, +most likely to provide documentation metadata for a tool like +``PHPDocumentor`` (@author, @link, ...). Docblock annotations are a +tool to embed metadata inside the documentation section which can +then be processed by some tool. Doctrine 2 generalizes the concept +of docblock annotations so that they can be used for any kind of +metadata and so that it is easy to define new docblock annotations. +In order to allow more involved annotation values and to reduce the +chances of clashes with other docblock annotations, the Doctrine 2 +docblock annotations feature an alternative syntax that is heavily +inspired by the Annotation syntax introduced in Java 5. + +The implementation of these enhanced docblock annotations is +located in the ``Doctrine\Common\Annotations`` namespace and +therefore part of the Common package. Doctrine 2 docblock +annotations support namespaces and nested annotations among other +things. The Doctrine 2 ORM defines its own set of docblock +annotations for supplying object-relational mapping metadata. + +.. note:: + + If you're not comfortable with the concept of docblock + annotations, don't worry, as mentioned earlier Doctrine 2 provides + XML and YAML alternatives and you could easily implement your own + favourite mechanism for defining ORM metadata. + +In this chapter a reference of every Doctrine 2 Annotation is given +with short explanations on their context and usage. + +Index +----- + +- :ref:`@Column ` +- :ref:`@ColumnResult ` +- :ref:`@Cache ` +- :ref:`@ChangeTrackingPolicy ` +- :ref:`@DiscriminatorColumn ` +- :ref:`@DiscriminatorMap ` +- :ref:`@Entity ` +- :ref:`@EntityResult ` +- :ref:`@FieldResult ` +- :ref:`@GeneratedValue ` +- :ref:`@HasLifecycleCallbacks ` +- :ref:`@Index ` +- :ref:`@Id ` +- :ref:`@InheritanceType ` +- :ref:`@JoinColumn ` +- :ref:`@JoinColumns ` +- :ref:`@JoinTable ` +- :ref:`@ManyToOne ` +- :ref:`@ManyToMany ` +- :ref:`@MappedSuperclass ` +- :ref:`@NamedNativeQuery ` +- :ref:`@OneToOne ` +- :ref:`@OneToMany ` +- :ref:`@OrderBy ` +- :ref:`@PostLoad ` +- :ref:`@PostPersist ` +- :ref:`@PostRemove ` +- :ref:`@PostUpdate ` +- :ref:`@PrePersist ` +- :ref:`@PreRemove ` +- :ref:`@PreUpdate ` +- :ref:`@SequenceGenerator ` +- :ref:`@SqlResultSetMapping ` +- :ref:`@Table ` +- :ref:`@UniqueConstraint ` +- :ref:`@Version ` + +Reference +--------- + +.. _annref_column: + +@Column +~~~~~~~ + +Marks an annotated instance variable as "persistent". It has to be +inside the instance variables PHP DocBlock comment. Any value hold +inside this variable will be saved to and loaded from the database +as part of the lifecycle of the instance variables entity-class. + +Required attributes: + +- **type**: Name of the Doctrine Type which is converted between PHP + and Database representation. + +Optional attributes: + +- **name**: By default the property name is used for the database + column name also, however the 'name' attribute allows you to + determine the column name. + +- **length**: Used by the "string" type to determine its maximum + length in the database. Doctrine does not validate the length of a + string values for you. + +- **precision**: The precision for a decimal (exact numeric) column + (applies only for decimal column), which is the maximum number of + digits that are stored for the values. + +- **scale**: The scale for a decimal (exact numeric) column (applies + only for decimal column), which represents the number of digits + to the right of the decimal point and must not be greater than + *precision*. + +- **unique**: Boolean value to determine if the value of the column + should be unique across all rows of the underlying entities table. + +- **nullable**: Determines if NULL values allowed for this column. + +- **options**: Array of additional options: + + - ``default``: The default value to set for the column if no value + is supplied. + + - ``unsigned``: Boolean value to determine if the column should + be capable of representing only non-negative integers + (applies only for integer column and might not be supported by + all vendors). + + - ``fixed``: Boolean value to determine if the specified length of + a string column should be fixed or varying (applies only for + string/binary column and might not be supported by all vendors). + + - ``comment``: The comment of the column in the schema (might not + be supported by all vendors). + + - ``collation``: The collation of the column (only supported by Drizzle, Mysql, PostgreSQL>=9.1, Sqlite and SQLServer). + +- **columnDefinition**: DDL SQL snippet that starts after the column + name and specifies the complete (non-portable!) column definition. + This attribute allows to make use of advanced RMDBS features. + However you should make careful use of this feature and the + consequences. SchemaTool will not detect changes on the column correctly + anymore if you use "columnDefinition". + + Additionally you should remember that the "type" + attribute still handles the conversion between PHP and Database + values. If you use this attribute on a column that is used for + joins between tables you should also take a look at + :ref:`@JoinColumn `. + +.. note:: + + For more detailed information on each attribute, please refer to + the DBAL ``Schema-Representation`` documentation. + +Examples: + +.. code-block:: php + + ` +can be found in the configuration section. + +Example: + +.. code-block:: php + + = 2.1) Specifies that this entity is marked as read only and not + considered for change-tracking. Entities of this type can be persisted + and removed though. + +Example: + +.. code-block:: php + + `. This +annotation is optional and only has meaning when used in +conjunction with @Id. + +If this annotation is not specified with @Id the NONE strategy is +used as default. + +Required attributes: + + +- **strategy**: Set the name of the identifier generation strategy. + Valid values are AUTO, SEQUENCE, TABLE, IDENTITY, UUID, CUSTOM and NONE. + +Example: + +.. code-block:: php + + ` annotation on +the entity-class level. It provides a hint to the SchemaTool to +generate a database index on the specified table columns. It only +has meaning in the SchemaTool schema generation context. + +Required attributes: + + +- **name**: Name of the Index +- **columns**: Array of columns. + +Optional attributes: + +- **options**: Array of platform specific options: + + - ``where``: SQL WHERE condition to be used for partial indexes. It will + only have effect on supported platforms. + +Basic example: + +.. code-block:: php + + ` and +:ref:`@DiscriminatorColumn ` annotations. + +Examples: + +.. code-block:: php + + `, :ref:`@OneToOne ` fields +and in the Context of :ref:`@JoinTable ` nested inside +a @ManyToMany. This annotation is not required. If it is not +specified the attributes *name* and *referencedColumnName* are +inferred from the table and primary key names. + +Required attributes: + + +- **name**: Column name that holds the foreign key identifier for + this relation. In the context of @JoinTable it specifies the column + name in the join table. +- **referencedColumnName**: Name of the primary key identifier that + is used for joining of this relation. + +Optional attributes: + + +- **unique**: Determines whether this relation is exclusive between the + affected entities and should be enforced as such on the database + constraint level. Defaults to false. +- **nullable**: Determine whether the related entity is required, or if + null is an allowed state for the relation. Defaults to true. +- **onDelete**: Cascade Action (Database-level) +- **columnDefinition**: DDL SQL snippet that starts after the column + name and specifies the complete (non-portable!) column definition. + This attribute enables the use of advanced RMDBS features. Using + this attribute on @JoinColumn is necessary if you need slightly + different column definitions for joining columns, for example + regarding NULL/NOT NULL defaults. However by default a + "columnDefinition" attribute on :ref:`@Column ` also sets + the related @JoinColumn's columnDefinition. This is necessary to + make foreign keys work. + +Example: + +.. code-block:: php + + ` or :ref:`@OneToOne ` +relation with an entity that has multiple identifiers. + +.. _annref_jointable: + +@JoinTable +~~~~~~~~~~~~~~ + +Using :ref:`@OneToMany ` or +:ref:`@ManyToMany ` on the owning side of the relation +requires to specify the @JoinTable annotation which describes the +details of the database join table. If you do not specify +@JoinTable on these relations reasonable mapping defaults apply +using the affected table and the column names. + +Optional attributes: + + +- **name**: Database name of the join-table +- **joinColumns**: An array of @JoinColumn annotations describing the + join-relation between the owning entities table and the join table. +- **inverseJoinColumns**: An array of @JoinColumn annotations + describing the join-relation between the inverse entities table and + the join table. + +Example: + +.. code-block:: php + + ` is an +additional, optional annotation that has reasonable default +configuration values using the table and names of the two related +entities. + +Required attributes: + + +- **targetEntity**: FQCN of the referenced target entity. Can be the + unqualified class name if both classes are in the same namespace. + *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- **mappedBy**: This option specifies the property name on the + targetEntity that is the owning side of this relation. It is a + required attribute for the inverse side of a relationship. +- **inversedBy**: The inversedBy attribute designates the field in the + entity that is the inverse side of the relationship. +- **cascade**: Cascade Option +- **fetch**: One of LAZY, EXTRA_LAZY or EAGER +- **indexBy**: Index the collection by a field on the target entity. + +.. note:: + + For ManyToMany bidirectional relationships either side may + be the owning side (the side that defines the @JoinTable and/or + does not make use of the mappedBy attribute, thus using a default + join table). + +Example: + +.. code-block:: php + + `. + +Optional attributes: + + +- **repositoryClass**: (>= 2.2) Specifies the FQCN of a subclass of the EntityRepository. + That will be inherited for all subclasses of that Mapped Superclass. + +Example: + +.. code-block:: php + + ` with one additional option which can +be specified. The configuration defaults for +:ref:`@JoinColumn ` using the target entity table and +primary key column names apply here too. + +Required attributes: + + +- **targetEntity**: FQCN of the referenced target entity. Can be the + unqualified class name if both classes are in the same namespace. + *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- **cascade**: Cascade Option +- **fetch**: One of LAZY or EAGER +- **orphanRemoval**: Boolean that specifies if orphans, inverse + OneToOne entities that are not connected to any owning instance, + should be removed by Doctrine. Defaults to false. +- **inversedBy**: The inversedBy attribute designates the field in the + entity that is the inverse side of the relationship. + +Example: + +.. code-block:: php + + ` or :ref:`@OneToMany ` +annotation to specify by which criteria the collection should be +retrieved from the database by using an ORDER BY clause. + +This annotation requires a single non-attributed value with an DQL +snippet: + +Example: + +.. code-block:: php + + = 2.5) Name of the schema the table lies in. + +Example: + +.. code-block:: php + + ` annotation on +the entity-class level. It allows to hint the SchemaTool to +generate a database unique constraint on the specified table +columns. It only has meaning in the SchemaTool schema generation +context. + +Required attributes: + + +- **name**: Name of the Index +- **columns**: Array of columns. + +Optional attributes: + +- **options**: Array of platform specific options: + + - ``where``: SQL WHERE condition to be used for partial indexes. It will + only have effect on supported platforms. + +Basic example: + +.. code-block:: php + + ` annotations that have the type integer or +datetime. Combining @Version with :ref:`@Id ` is not supported. + +Example: + +.. code-block:: php + + `. +- An entity class must not implement ``__wakeup`` or + :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`. + Also consider implementing + `Serializable `_ + instead. +- Any two entity classes in a class hierarchy that inherit + directly or indirectly from one another must not have a mapped + property with the same name. That is, if B inherits from A then B + must not have a mapped field with the same name as an already + mapped field that is inherited from A. +- An entity cannot make use of func_get_args() to implement variable parameters. + Generated proxies do not support this for performance reasons and your code might + actually fail to work when violating this restriction. + +Entities support inheritance, polymorphic associations, and +polymorphic queries. Both abstract and concrete classes can be +entities. Entities may extend non-entity classes as well as entity +classes, and non-entity classes may extend entity classes. + +.. note:: + + The constructor of an entity is only ever invoked when + *you* construct a new instance with the *new* keyword. Doctrine + never calls entity constructors, thus you are free to use them as + you wish and even have it require arguments of any type. + + +Entity states +~~~~~~~~~~~~~ + +An entity instance can be characterized as being NEW, MANAGED, +DETACHED or REMOVED. + + +- A NEW entity instance has no persistent identity, and is not yet + associated with an EntityManager and a UnitOfWork (i.e. those just + created with the "new" operator). +- A MANAGED entity instance is an instance with a persistent + identity that is associated with an EntityManager and whose + persistence is thus managed. +- A DETACHED entity instance is an instance with a persistent + identity that is not (or no longer) associated with an + EntityManager and a UnitOfWork. +- A REMOVED entity instance is an instance with a persistent + identity, associated with an EntityManager, that will be removed + from the database upon transaction commit. + +.. _architecture_persistent_fields: + +Persistent fields +~~~~~~~~~~~~~~~~~ + +The persistent state of an entity is represented by instance +variables. An instance variable must be directly accessed only from +within the methods of the entity by the entity instance itself. +Instance variables must not be accessed by clients of the entity. +The state of the entity is available to clients only through the +entity’s methods, i.e. accessor methods (getter/setter methods) or +other business methods. + +Collection-valued persistent fields and properties must be defined +in terms of the ``Doctrine\Common\Collections\Collection`` +interface. The collection implementation type may be used by the +application to initialize fields or properties before the entity is +made persistent. Once the entity becomes managed (or detached), +subsequent access must be through the interface type. + +Serializing entities +~~~~~~~~~~~~~~~~~~~~ + +Serializing entities can be problematic and is not really +recommended, at least not as long as an entity instance still holds +references to proxy objects or is still managed by an +EntityManager. If you intend to serialize (and unserialize) entity +instances that still hold references to proxy objects you may run +into problems with private properties because of technical +limitations. Proxy objects implement ``__sleep`` and it is not +possible for ``__sleep`` to return names of private properties in +parent classes. On the other hand it is not a solution for proxy +objects to implement ``Serializable`` because Serializable does not +work well with any potential cyclic object references (at least we +did not find a way yet, if you did, please contact us). + +The EntityManager +~~~~~~~~~~~~~~~~~ + +The ``EntityManager`` class is a central access point to the ORM +functionality provided by Doctrine 2. The ``EntityManager`` API is +used to manage the persistence of your objects and to query for +persistent objects. + +Transactional write-behind +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An ``EntityManager`` and the underlying ``UnitOfWork`` employ a +strategy called "transactional write-behind" that delays the +execution of SQL statements in order to execute them in the most +efficient way and to execute them at the end of a transaction so +that all write locks are quickly released. You should see Doctrine +as a tool to synchronize your in-memory objects with the database +in well defined units of work. Work with your objects and modify +them as usual and when you're done call ``EntityManager#flush()`` +to make your changes persistent. + +The Unit of Work +~~~~~~~~~~~~~~~~ + +Internally an ``EntityManager`` uses a ``UnitOfWork``, which is a +typical implementation of the +`Unit of Work pattern `_, +to keep track of all the things that need to be done the next time +``flush`` is invoked. You usually do not directly interact with a +``UnitOfWork`` but with the ``EntityManager`` instead. + + diff --git a/vendor/doctrine/orm/docs/en/reference/association-mapping.rst b/vendor/doctrine/orm/docs/en/reference/association-mapping.rst new file mode 100644 index 0000000..c212e57 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/association-mapping.rst @@ -0,0 +1,1094 @@ +Association Mapping +=================== + +This chapter explains mapping associations between objects. + +Instead of working with foreign keys in your code, you will always work with +references to objects instead and Doctrine will convert those references +to foreign keys internally. + +- A reference to a single object is represented by a foreign key. +- A collection of objects is represented by many foreign keys pointing to the object holding the collection + +This chapter is split into three different sections. + +- A list of all the possible association mapping use-cases is given. +- :ref:`association_mapping_defaults` are explained that simplify the use-case examples. +- :ref:`collections` are introduced that contain entities in associations. + +To gain a full understanding of associations you should also read about :doc:`owning and +inverse sides of associations ` + +Many-To-One, Unidirectional +--------------------------- + +A many-to-one association is the most common association between objects. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + + +.. note:: + + The above ``@JoinColumn`` is optional as it would default + to ``address_id`` and ``id`` anyways. You can omit it and let it + use the defaults. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + address_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE Address ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id); + +One-To-One, Unidirectional +-------------------------- + +Here is an example of a one-to-one association with a ``Product`` entity that +references one ``Shipping`` entity. The ``Shipping`` does not reference back to +the ``Product`` so that the reference is said to be unidirectional, in one +direction only. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + joinColumn: + name: shipping_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + shipping_id INT DEFAULT NULL, + UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id), + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Shipping ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Product ADD FOREIGN KEY (shipping_id) REFERENCES Shipping(id); + +One-To-One, Bidirectional +------------------------- + +Here is a one-to-one relationship between a ``Customer`` and a +``Cart``. The ``Cart`` has a reference back to the ``Customer`` so +it is bidirectional. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + .. code-block:: yaml + + Customer: + oneToOne: + cart: + targetEntity: Cart + mappedBy: customer + Cart: + oneToOne: + customer: + targetEntity: Customer + inversedBy: cart + joinColumn: + name: customer_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Cart ( + id INT AUTO_INCREMENT NOT NULL, + customer_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Customer ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Cart ADD FOREIGN KEY (customer_id) REFERENCES Customer(id); + +See how the foreign key is defined on the owning side of the +relation, the table ``Cart``. + +One-To-One, Self-referencing +---------------------------- + +You can define a self-referencing one-to-one relationships like +below. + +.. code-block:: php + + features = new ArrayCollection(); + } + } + + /** @Entity **/ + class Feature + { + // ... + /** + * @ManyToOne(targetEntity="Product", inversedBy="features") + * @JoinColumn(name="product_id", referencedColumnName="id") + **/ + private $product; + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToMany: + features: + targetEntity: Feature + mappedBy: product + Feature: + type: entity + manyToOne: + product: + targetEntity: Product + inversedBy: features + joinColumn: + name: product_id + referencedColumnName: id + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Product ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE Feature ( + id INT AUTO_INCREMENT NOT NULL, + product_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id); + +One-To-Many, Unidirectional with Join Table +------------------------------------------- + +A unidirectional one-to-many association can be mapped through a +join table. From Doctrine's point of view, it is simply mapped as a +unidirectional many-to-many whereby a unique constraint on one of +the join columns enforces the one-to-many cardinality. + +The following example sets up such a unidirectional one-to-many association: + +.. configuration-block:: + + .. code-block:: php + + phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity **/ + class Phonenumber + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + phonenumbers: + targetEntity: Phonenumber + joinTable: + name: users_phonenumbers + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + phonenumber_id: + referencedColumnName: id + unique: true + + +Generates the following MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + CREATE TABLE users_phonenumbers ( + user_id INT NOT NULL, + phonenumber_id INT NOT NULL, + UNIQUE INDEX users_phonenumbers_phonenumber_id_uniq (phonenumber_id), + PRIMARY KEY(user_id, phonenumber_id) + ) ENGINE = InnoDB; + + CREATE TABLE Phonenumber ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + + ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id); + +One-To-Many, Self-referencing +----------------------------- + +You can also setup a one-to-many association that is +self-referencing. In this example we setup a hierarchy of +``Category`` objects by creating a self referencing relationship. +This effectively models a hierarchy of categories and from the +database perspective is known as an adjacency list approach. + +.. configuration-block:: + + .. code-block:: php + + children = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + .. code-block:: xml + + + + + + + + + .. code-block:: yaml + + Category: + type: entity + oneToMany: + children: + targetEntity: Category + mappedBy: parent + manyToOne: + parent: + targetEntity: Category + inversedBy: children + +Note that the @JoinColumn is not really necessary in this example, +as the defaults would be the same. + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE Category ( + id INT AUTO_INCREMENT NOT NULL, + parent_id INT DEFAULT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE Category ADD FOREIGN KEY (parent_id) REFERENCES Category(id); + +Many-To-Many, Unidirectional +---------------------------- + +Real many-to-many associations are less common. The following +example shows a unidirectional association between User and Group +entities: + +.. configuration-block:: + + .. code-block:: php + + groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + } + + /** @Entity **/ + class Group + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE users_groups ( + user_id INT NOT NULL, + group_id INT NOT NULL, + PRIMARY KEY(user_id, group_id) + ) ENGINE = InnoDB; + CREATE TABLE Group ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + ALTER TABLE users_groups ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE users_groups ADD FOREIGN KEY (group_id) REFERENCES Group(id); + +.. note:: + + Why are many-to-many associations less common? Because + frequently you want to associate additional attributes with an + association, in which case you introduce an association class. + Consequently, the direct many-to-many association disappears and is + replaced by one-to-many/many-to-one associations between the 3 + participating classes. + +Many-To-Many, Bidirectional +--------------------------- + +Here is a similar many-to-many relationship as above except this +one is bidirectional. + +.. configuration-block:: + + .. code-block:: php + + groups = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + /** @Entity **/ + class Group + { + // ... + /** + * @ManyToMany(targetEntity="User", mappedBy="groups") + **/ + private $users; + + public function __construct() { + $this->users = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + inversedBy: users + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + + Group: + type: entity + manyToMany: + users: + targetEntity: User + mappedBy: groups + +The MySQL schema is exactly the same as for the Many-To-Many +uni-directional case above. + +Owning and Inverse Side on a ManyToMany association +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Many-To-Many associations you can chose which entity is the +owning and which the inverse side. There is a very simple semantic +rule to decide which side is more suitable to be the owning side +from a developers perspective. You only have to ask yourself, which +entity is responsible for the connection management and pick that +as the owning side. + +Take an example of two entities ``Article`` and ``Tag``. Whenever +you want to connect an Article to a Tag and vice-versa, it is +mostly the Article that is responsible for this relation. Whenever +you add a new article, you want to connect it with existing or new +tags. Your create Article form will probably support this notion +and allow to specify the tags directly. This is why you should pick +the Article as owning side, as it makes the code more +understandable: + +.. code-block:: php + + addArticle($this); // synchronously updating inverse side + $this->tags[] = $tag; + } + } + + class Tag + { + private $articles; + + public function addArticle(Article $article) + { + $this->articles[] = $article; + } + } + +This allows to group the tag adding on the ``Article`` side of the +association: + +.. code-block:: php + + addTag($tagA); + $article->addTag($tagB); + +Many-To-Many, Self-referencing +------------------------------ + +You can even have a self-referencing many-to-many association. A +common scenario is where a ``User`` has friends and the target +entity of that relationship is a ``User`` so it is self +referencing. In this example it is bidirectional so ``User`` has a +field named ``$friendsWithMe`` and ``$myFriends``. + +.. code-block:: php + + friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection(); + $this->myFriends = new \Doctrine\Common\Collections\ArrayCollection(); + } + + // ... + } + +Generated MySQL Schema: + +.. code-block:: sql + + CREATE TABLE User ( + id INT AUTO_INCREMENT NOT NULL, + PRIMARY KEY(id) + ) ENGINE = InnoDB; + CREATE TABLE friends ( + user_id INT NOT NULL, + friend_user_id INT NOT NULL, + PRIMARY KEY(user_id, friend_user_id) + ) ENGINE = InnoDB; + ALTER TABLE friends ADD FOREIGN KEY (user_id) REFERENCES User(id); + ALTER TABLE friends ADD FOREIGN KEY (friend_user_id) REFERENCES User(id); + +.. _association_mapping_defaults: + +Mapping Defaults +---------------- + +The ``@JoinColumn`` and ``@JoinTable`` definitions are usually optional and have +sensible default values. The defaults for a join column in a +one-to-one/many-to-one association is as follows: + +:: + + name: "_id" + referencedColumnName: "id" + +As an example, consider this mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + +This is essentially the same as the following, more verbose, +mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Product: + type: entity + oneToOne: + shipping: + targetEntity: Shipping + joinColumn: + name: shipping_id + referencedColumnName: id + +The @JoinTable definition used for many-to-many mappings has +similar defaults. As an example, consider this mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + +This is essentially the same as the following, more verbose, mapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + targetEntity: Group + joinTable: + name: User_Group + joinColumns: + User_id: + referencedColumnName: id + inverseJoinColumns: + Group_id: + referencedColumnName: id + +In that case, the name of the join table defaults to a combination +of the simple, unqualified class names of the participating +classes, separated by an underscore character. The names of the +join columns default to the simple, unqualified class name of the +targeted class followed by "\_id". The referencedColumnName always +defaults to "id", just as in one-to-one or many-to-one mappings. + +If you accept these defaults, you can reduce the mapping code to a +minimum. + +.. _collections: + +Collections +----------- + +Unfortunately, PHP arrays, while being great for many things, are missing +features that make them suitable for lazy loading in the context of an ORM. +This is why in all the examples of many-valued associations in this manual we +will make use of a ``Collection`` interface and its +default implementation ``ArrayCollection`` that are both defined in the +``Doctrine\Common\Collections`` namespace. A collection implements +the PHP interfaces ``ArrayAccess``, ``Traversable`` and ``Countable``. + +.. note:: + + The Collection interface and ArrayCollection class, + like everything else in the Doctrine namespace, are neither part of + the ORM, nor the DBAL, it is a plain PHP class that has no outside + dependencies apart from dependencies on PHP itself (and the SPL). + Therefore using this class in your model and elsewhere + does not introduce a coupling to the ORM. + +Initializing Collections +------------------------ + +You should always initialize the collections of your ``@OneToMany`` +and ``@ManyToMany`` associations in the constructor of your entities: + +.. code-block:: php + + groups = new ArrayCollection(); + } + + public function getGroups() + { + return $this->groups; + } + } + +The following code will then work even if the Entity hasn't +been associated with an EntityManager yet: + +.. code-block:: php + + getGroups()->add($group); diff --git a/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst b/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst new file mode 100644 index 0000000..4ece676 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/basic-mapping.rst @@ -0,0 +1,499 @@ +Basic Mapping +============= + +This guide explains the basic mapping of entities and properties. +After working through this guide you should know: + +- How to create PHP objects that can be saved to the database with Doctrine; +- How to configure the mapping between columns on tables and properties on + entities; +- What Doctrine mapping types are; +- Defining primary keys and how identifiers are generated by Doctrine; +- How quoting of reserved symbols works in Doctrine. + +Mapping of associations will be covered in the next chapter on +:doc:`Association Mapping `. + +Guide Assumptions +----------------- + +You should have already :doc:`installed and configure ` +Doctrine. + +Creating Classes for the Database +--------------------------------- + +Every PHP object that you want to save in the database using Doctrine +is called an "Entity". The term "Entity" describes objects +that have an identity over many independent requests. This identity is +usually achieved by assigning a unique identifier to an entity. +In this tutorial the following ``Message`` PHP class will serve as the +example Entity: + +.. code-block:: php + + ` +- :doc:`XML ` +- :doc:`YAML ` +- :doc:`PHP code ` + +This manual will usually show mapping metadata via docblock annotations, though +many examples also show the equivalent configuration in YAML and XML. + +.. note:: + + All metadata drivers perform equally. Once the metadata of a class has been + read from the source (annotations, xml or yaml) it is stored in an instance + of the ``Doctrine\ORM\Mapping\ClassMetadata`` class and these instances are + stored in the metadata cache. If you're not using a metadata cache (not + recommended!) then the XML driver is the fastest. + +Marking our ``Message`` class as an entity for Doctrine is straightforward: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Message: + type: entity + # ... + +With no additional information, Doctrine expects the entity to be saved +into a table with the same name as the class in our case ``Message``. +You can change this by configuring information about the table: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + .. code-block:: yaml + + Message: + type: entity + table: message + # ... + +Now the class ``Message`` will be saved and fetched from the table ``message``. + +Property Mapping +---------------- + +The next step after marking a PHP class as an entity is mapping its properties +to columns in a table. + +To configure a property use the ``@Column`` docblock annotation. The ``type`` +attribute specifies the :ref:`Doctrine Mapping Type ` +to use for the field. If the type is not specified, ``string`` is used as the +default. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + fields: + id: + type: integer + text: + length: 140 + postedAt: + type: datetime + column: posted_at + +When we don't explicitly specify a column name via the ``name`` option, Doctrine +assumes the field name is also the column name. This means that: + +* the ``id`` property will map to the column ``id`` using the type ``integer``; +* the ``text`` property will map to the column ``text`` with the default mapping type ``string``; +* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type. + +The Column annotation has some more attributes. Here is a complete +list: + +- ``type``: (optional, defaults to 'string') The mapping type to + use for the column. +- ``name``: (optional, defaults to field name) The name of the + column in the database. +- ``length``: (optional, default 255) The length of the column in + the database. (Applies only if a string-valued column is used). +- ``unique``: (optional, default FALSE) Whether the column is a + unique key. +- ``nullable``: (optional, default FALSE) Whether the database + column is nullable. +- ``precision``: (optional, default 0) The precision for a decimal + (exact numeric) column (applies only for decimal column), + which is the maximum number of digits that are stored for the values. +- ``scale``: (optional, default 0) The scale for a decimal (exact + numeric) column (applies only for decimal column), which represents + the number of digits to the right of the decimal point and must + not be greater than *precision*. +- ``columnDefinition``: (optional) Allows to define a custom + DDL snippet that is used to create the column. Warning: This normally + confuses the SchemaTool to always detect the column as changed. +- ``options``: (optional) Key-value pairs of options that get passed + to the underlying database platform when generating DDL statements. + +.. _reference-mapping-types: + +Doctrine Mapping Types +---------------------- + +The ``type`` option used in the ``@Column`` accepts any of the existing +Doctrine types or even your own custom types. A Doctrine type defines +the conversion between PHP and SQL types, independent from the database vendor +you are using. All Mapping Types that ship with Doctrine are fully portable +between the supported database systems. + +As an example, the Doctrine Mapping Type ``string`` defines the +mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. +depending on the RDBMS brand). Here is a quick overview of the +built-in mapping types: + +- ``string``: Type that maps a SQL VARCHAR to a PHP string. +- ``integer``: Type that maps a SQL INT to a PHP integer. +- ``smallint``: Type that maps a database SMALLINT to a PHP + integer. +- ``bigint``: Type that maps a database BIGINT to a PHP string. +- ``boolean``: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean. +- ``decimal``: Type that maps a SQL DECIMAL to a PHP string. +- ``date``: Type that maps a SQL DATETIME to a PHP DateTime + object. +- ``time``: Type that maps a SQL TIME to a PHP DateTime object. +- ``datetime``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP + DateTime object. +- ``datetimetz``: Type that maps a SQL DATETIME/TIMESTAMP to a PHP + DateTime object with timezone. +- ``text``: Type that maps a SQL CLOB to a PHP string. +- ``object``: Type that maps a SQL CLOB to a PHP object using + ``serialize()`` and ``unserialize()`` +- ``array``: Type that maps a SQL CLOB to a PHP array using + ``serialize()`` and ``unserialize()`` +- ``simple_array``: Type that maps a SQL CLOB to a PHP array using + ``implode()`` and ``explode()``, with a comma as delimiter. *IMPORTANT* + Only use this type if you are sure that your values cannot contain a ",". +- ``json_array``: Type that maps a SQL CLOB to a PHP array using + ``json_encode()`` and ``json_decode()`` +- ``float``: Type that maps a SQL Float (Double Precision) to a + PHP double. *IMPORTANT*: Works only with locale settings that use + decimal points as separator. +- ``guid``: Type that maps a database GUID/UUID to a PHP string. Defaults to + varchar but uses a specific type if the platform supports it. +- ``blob``: Type that maps a SQL BLOB to a PHP resource stream + +A cookbook article shows how to define :doc:`your own custom mapping types +<../cookbook/custom-mapping-types>`. + +.. note:: + + DateTime and Object types are compared by reference, not by value. Doctrine + updates this values if the reference changes and therefore behaves as if + these objects are immutable value objects. + +.. warning:: + + All Date types assume that you are exclusively using the default timezone + set by `date_default_timezone_set() `_ + or by the php.ini configuration ``date.timezone``. Working with + different timezones will cause troubles and unexpected behavior. + + If you need specific timezone handling you have to handle this + in your domain, converting all the values back and forth from UTC. + There is also a :doc:`cookbook entry <../cookbook/working-with-datetime>` + on working with datetimes that gives hints for implementing + multi timezone applications. + +Identifiers / Primary Keys +-------------------------- + +Every entity class must have an identifier/primary key. You can select +the field that serves as the identifier with the ``@Id`` +annotation. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + # fields here + +In most cases using the automatic generator strategy (``@GeneratedValue``) is +what you want. It defaults to the identifier generation mechanism your current +database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL, +Sequences with Oracle and so on. + +Identifier Generation Strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The previous example showed how to use the default identifier +generation strategy without knowing the underlying database with +the AUTO-detection strategy. It is also possible to specify the +identifier generation strategy more explicitly, which allows you to +make use of some additional features. + +Here is the list of possible generation strategies: + +- ``AUTO`` (default): Tells Doctrine to pick the strategy that is + preferred by the used database platform. The preferred strategies + are IDENTITY for MySQL, SQLite, MsSQL and SQL Anywhere and SEQUENCE + for Oracle and PostgreSQL. This strategy provides full portability. +- ``SEQUENCE``: Tells Doctrine to use a database sequence for ID + generation. This strategy does currently not provide full + portability. Sequences are supported by Oracle, PostgreSql and + SQL Anywhere. +- ``IDENTITY``: Tells Doctrine to use special identity columns in + the database that generate a value on insertion of a row. This + strategy does currently not provide full portability and is + supported by the following platforms: MySQL/SQLite/SQL Anywhere + (AUTO\_INCREMENT), MSSQL (IDENTITY) and PostgreSQL (SERIAL). +- ``TABLE``: Tells Doctrine to use a separate table for ID + generation. This strategy provides full portability. + ***This strategy is not yet implemented!*** +- ``NONE``: Tells Doctrine that the identifiers are assigned (and + thus generated) by your code. The assignment must take place before + a new entity is passed to ``EntityManager#persist``. NONE is the + same as leaving off the @GeneratedValue entirely. + +Sequence Generator +^^^^^^^^^^^^^^^^^^ + +The Sequence Generator can currently be used in conjunction with +Oracle or Postgres and allows some additional configuration options +besides specifying the sequence's name: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Message: + type: entity + id: + id: + type: integer + generator: + strategy: SEQUENCE + sequenceGenerator: + sequenceName: message_seq + allocationSize: 100 + initialValue: 1 + +The initial value specifies at which value the sequence should +start. + +The allocationSize is a powerful feature to optimize INSERT +performance of Doctrine. The allocationSize specifies by how much +values the sequence is incremented whenever the next value is +retrieved. If this is larger than 1 (one) Doctrine can generate +identifier values for the allocationSizes amount of entities. In +the above example with ``allocationSize=100`` Doctrine 2 would only +need to access the sequence once to generate the identifiers for +100 new entities. + +*The default allocationSize for a @SequenceGenerator is currently 10.* + +.. caution:: + + The allocationSize is detected by SchemaTool and + transformed into an "INCREMENT BY " clause in the CREATE SEQUENCE + statement. For a database schema created manually (and not + SchemaTool) you have to make sure that the allocationSize + configuration option is never larger than the actual sequences + INCREMENT BY value, otherwise you may get duplicate keys. + + +.. note:: + + It is possible to use strategy="AUTO" and at the same time + specifying a @SequenceGenerator. In such a case, your custom + sequence settings are used in the case where the preferred strategy + of the underlying platform is SEQUENCE, such as for Oracle and + PostgreSQL. + + +Composite Keys +~~~~~~~~~~~~~~ + +with Doctrine 2 you can use composite primary keys, using ``@Id`` on more then +one column. Some restrictions exist opposed to using a single identifier in +this case: The use of the ``@GeneratedValue`` annotation is not supported, +which means you can only use composite keys if you generate the primary key +values yourself before calling ``EntityManager#persist()`` on the entity. + +More details on composite primary keys are discussed in a :doc:`dedicated tutorial +<../tutorials/composite-primary-keys>`. + +Quoting Reserved Words +---------------------- + +Sometimes it is necessary to quote a column or table name because of reserved +word conflicts. Doctrine does not quote identifiers automatically, because it +leads to more problems than it would solve. Quoting tables and column names +needs to be done explicitly using ticks in the definition. + +.. code-block:: php + + setQuoteStrategy(new AnsiQuoteStrategy()); diff --git a/vendor/doctrine/orm/docs/en/reference/batch-processing.rst b/vendor/doctrine/orm/docs/en/reference/batch-processing.rst new file mode 100644 index 0000000..5d3cf18 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/batch-processing.rst @@ -0,0 +1,187 @@ +Batch Processing +================ + +This chapter shows you how to accomplish bulk inserts, updates and +deletes with Doctrine in an efficient way. The main problem with +bulk operations is usually not to run out of memory and this is +especially what the strategies presented here provide help with. + +.. warning:: + + An ORM tool is not primarily well-suited for mass + inserts, updates or deletions. Every RDBMS has its own, most + effective way of dealing with such operations and if the options + outlined below are not sufficient for your purposes we recommend + you use the tools for your particular RDBMS for these bulk + operations. + + +Bulk Inserts +------------ + +Bulk inserts in Doctrine are best performed in batches, taking +advantage of the transactional write-behind behavior of an +``EntityManager``. The following code shows an example for +inserting 10000 objects with a batch size of 20. You may need to +experiment with the batch size to find the size that works best for +you. Larger batch sizes mean more prepared statement reuse +internally but also mean more work during ``flush``. + +.. code-block:: php + + setStatus('user'); + $user->setUsername('user' . $i); + $user->setName('Mr.Smith-' . $i); + $em->persist($user); + if (($i % $batchSize) === 0) { + $em->flush(); + $em->clear(); // Detaches all objects from Doctrine! + } + } + $em->flush(); //Persist objects that did not make up an entire batch + $em->clear(); + +Bulk Updates +------------ + +There are 2 possibilities for bulk updates with Doctrine. + +DQL UPDATE +~~~~~~~~~~ + +The by far most efficient way for bulk updates is to use a DQL +UPDATE query. Example: + +.. code-block:: php + + createQuery('update MyProject\Model\Manager m set m.salary = m.salary * 0.9'); + $numUpdated = $q->execute(); + +Iterating results +~~~~~~~~~~~~~~~~~ + +An alternative solution for bulk updates is to use the +``Query#iterate()`` facility to iterate over the query results step +by step instead of loading the whole result into memory at once. +The following example shows how to do this, combining the iteration +with the batching strategy that was already used for bulk inserts: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + foreach ($iterableResult as $row) { + $user = $row[0]; + $user->increaseCredit(); + $user->calculateNewBonuses(); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all updates. + $em->clear(); // Detaches all objects from Doctrine! + } + ++$i; + } + $em->flush(); + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + +.. note:: + + Results may be fully buffered by the database client/ connection allocating + additional memory not visible to the PHP process. For large sets this + may easily kill the process for no apparant reason. + + +Bulk Deletes +------------ + +There are two possibilities for bulk deletes with Doctrine. You can +either issue a single DQL DELETE query or you can iterate over +results removing them one at a time. + +DQL DELETE +~~~~~~~~~~ + +The by far most efficient way for bulk deletes is to use a DQL +DELETE query. + +Example: + +.. code-block:: php + + createQuery('delete from MyProject\Model\Manager m where m.salary > 100000'); + $numDeleted = $q->execute(); + +Iterating results +~~~~~~~~~~~~~~~~~ + +An alternative solution for bulk deletes is to use the +``Query#iterate()`` facility to iterate over the query results step +by step instead of loading the whole result into memory at once. +The following example shows how to do this: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + while (($row = $iterableResult->next()) !== false) { + $em->remove($row[0]); + if (($i % $batchSize) === 0) { + $em->flush(); // Executes all deletions. + $em->clear(); // Detaches all objects from Doctrine! + } + ++$i; + } + $em->flush(); + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + +Iterating Large Results for Data-Processing +------------------------------------------- + +You can use the ``iterate()`` method just to iterate over a large +result and no UPDATE or DELETE intention. The ``IterableResult`` +instance returned from ``$query->iterate()`` implements the +Iterator interface so you can process a large result without memory +problems using the following approach: + +.. code-block:: php + + _em->createQuery('select u from MyProject\Model\User u'); + $iterableResult = $q->iterate(); + foreach ($iterableResult as $row) { + // do stuff with the data in the row, $row[0] is always the object + + // detach from Doctrine, so that it can be Garbage-Collected immediately + $this->_em->detach($row[0]); + } + +.. note:: + + Iterating results is not possible with queries that + fetch-join a collection-valued association. The nature of such SQL + result sets is not suitable for incremental hydration. + + + diff --git a/vendor/doctrine/orm/docs/en/reference/best-practices.rst b/vendor/doctrine/orm/docs/en/reference/best-practices.rst new file mode 100644 index 0000000..6937e99 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/best-practices.rst @@ -0,0 +1,111 @@ +Best Practices +============== + +The best practices mentioned here that affect database +design generally refer to best practices when working with Doctrine +and do not necessarily reflect best practices for database design +in general. + +Constrain relationships as much as possible +------------------------------------------- + +It is important to constrain relationships as much as possible. +This means: + + +- Impose a traversal direction (avoid bidirectional associations + if possible) +- Eliminate nonessential associations + +This has several benefits: + + +- Reduced coupling in your domain model +- Simpler code in your domain model (no need to maintain + bidirectionality properly) +- Less work for Doctrine + +Avoid composite keys +-------------------- + +Even though Doctrine fully supports composite keys it is best not +to use them if possible. Composite keys require additional work by +Doctrine and thus have a higher probability of errors. + +Use events judiciously +---------------------- + +The event system of Doctrine is great and fast. Even though making +heavy use of events, especially lifecycle events, can have a +negative impact on the performance of your application. Thus you +should use events judiciously. + +Use cascades judiciously +------------------------ + +Automatic cascades of the persist/remove/merge/etc. operations are +very handy but should be used wisely. Do NOT simply add all +cascades to all associations. Think about which cascades actually +do make sense for you for a particular association, given the +scenarios it is most likely used in. + +Don't use special characters +---------------------------- + +Avoid using any non-ASCII characters in class, field, table or +column names. Doctrine itself is not unicode-safe in many places +and will not be until PHP itself is fully unicode-aware (PHP6). + +Don't use identifier quoting +---------------------------- + +Identifier quoting is a workaround for using reserved words that +often causes problems in edge cases. Do not use identifier quoting +and avoid using reserved words as table or column names. + +Initialize collections in the constructor +----------------------------------------- + +It is recommended best practice to initialize any business +collections in entities in the constructor. Example: + +.. code-block:: php + + addresses = new ArrayCollection; + $this->articles = new ArrayCollection; + } + } + +Don't map foreign keys to fields in an entity +--------------------------------------------- + +Foreign keys have no meaning whatsoever in an object model. Foreign +keys are how a relational database establishes relationships. Your +object model establishes relationships through object references. +Thus mapping foreign keys to object fields heavily leaks details of +the relational model into the object model, something you really +should not do. + +Use explicit transaction demarcation +------------------------------------ + +While Doctrine will automatically wrap all DML operations in a +transaction on flush(), it is considered best practice to +explicitly set the transaction boundaries yourself. Otherwise every +single query is wrapped in a small transaction (Yes, SELECT +queries, too) since you can not talk to your database outside of a +transaction. While such short transactions for read-only (SELECT) +queries generally don't have any noticeable performance impact, it +is still preferable to use fewer, well-defined transactions that +are established through explicit transaction boundaries. + + diff --git a/vendor/doctrine/orm/docs/en/reference/caching.rst b/vendor/doctrine/orm/docs/en/reference/caching.rst new file mode 100644 index 0000000..6881349 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/caching.rst @@ -0,0 +1,420 @@ +Caching +======= + +Doctrine provides cache drivers in the ``Common`` package for some +of the most popular caching implementations such as APC, Memcache +and Xcache. We also provide an ``ArrayCache`` driver which stores +the data in a PHP array. Obviously, when using ``ArrayCache``, the +cache does not persist between requests, but this is useful for +testing in a development environment. + +Cache Drivers +------------- + +The cache drivers follow a simple interface that is defined in +``Doctrine\Common\Cache\Cache``. All the cache drivers extend a +base class ``Doctrine\Common\Cache\AbstractCache`` which implements +this interface. + +The interface defines the following public methods for you to implement: + + +- fetch($id) - Fetches an entry from the cache +- contains($id) - Test if an entry exists in the cache +- save($id, $data, $lifeTime = false) - Puts data into the cache +- delete($id) - Deletes a cache entry + +Each driver extends the ``AbstractCache`` class which defines a few +abstract protected methods that each of the drivers must +implement: + + +- \_doFetch($id) +- \_doContains($id) +- \_doSave($id, $data, $lifeTime = false) +- \_doDelete($id) + +The public methods ``fetch()``, ``contains()`` etc. use the +above protected methods which are implemented by the drivers. The +code is organized this way so that the protected methods in the +drivers do the raw interaction with the cache implementation and +the ``AbstractCache`` can build custom functionality on top of +these methods. + +APC +~~~ + +In order to use the APC cache driver you must have it compiled and +enabled in your php.ini. You can read about APC +`in the PHP Documentation `_. It will give +you a little background information about what it is and how you +can use it as well as how to install it. + +Below is a simple example of how you could use the APC cache driver +by itself. + +.. code-block:: php + + save('cache_id', 'my_data'); + +Memcache +~~~~~~~~ + +In order to use the Memcache cache driver you must have it compiled +and enabled in your php.ini. You can read about Memcache +`on the PHP website `_. It will +give you a little background information about what it is and how +you can use it as well as how to install it. + +Below is a simple example of how you could use the Memcache cache +driver by itself. + +.. code-block:: php + + connect('memcache_host', 11211); + + $cacheDriver = new \Doctrine\Common\Cache\MemcacheCache(); + $cacheDriver->setMemcache($memcache); + $cacheDriver->save('cache_id', 'my_data'); + +Memcached +~~~~~~~~ + +Memcached is a more recent and complete alternative extension to +Memcache. + +In order to use the Memcached cache driver you must have it compiled +and enabled in your php.ini. You can read about Memcached +`on the PHP website `_. It will +give you a little background information about what it is and how +you can use it as well as how to install it. + +Below is a simple example of how you could use the Memcached cache +driver by itself. + +.. code-block:: php + + addServer('memcache_host', 11211); + + $cacheDriver = new \Doctrine\Common\Cache\MemcachedCache(); + $cacheDriver->setMemcached($memcached); + $cacheDriver->save('cache_id', 'my_data'); + +Xcache +~~~~~~ + +In order to use the Xcache cache driver you must have it compiled +and enabled in your php.ini. You can read about Xcache +`here `_. It will give you a little +background information about what it is and how you can use it as +well as how to install it. + +Below is a simple example of how you could use the Xcache cache +driver by itself. + +.. code-block:: php + + save('cache_id', 'my_data'); + +Redis +~~~~~ + +In order to use the Redis cache driver you must have it compiled +and enabled in your php.ini. You can read about what Redis is +`from here `_. Also check +`A PHP extension for Redis `_ for how you can use +and install the Redis PHP extension. + +Below is a simple example of how you could use the Redis cache +driver by itself. + +.. code-block:: php + + connect('redis_host', 6379); + + $cacheDriver = new \Doctrine\Common\Cache\RedisCache(); + $cacheDriver->setRedis($redis); + $cacheDriver->save('cache_id', 'my_data'); + +Using Cache Drivers +------------------- + +In this section we'll describe how you can fully utilize the API of +the cache drivers to save data to a cache, check if some cached data +exists, fetch the cached data and delete the cached data. We'll use the +``ArrayCache`` implementation as our example here. + +.. code-block:: php + + save('cache_id', 'my_data'); + +The ``save()`` method accepts three arguments which are described +below: + + +- ``$id`` - The cache id +- ``$data`` - The cache entry/data. +- ``$lifeTime`` - The lifetime. If != false, sets a specific + lifetime for this cache entry (null => infinite lifeTime). + +You can save any type of data whether it be a string, array, +object, etc. + +.. code-block:: php + + 'value1', + 'key2' => 'value2' + ); + $cacheDriver->save('my_array', $array); + +Checking +~~~~~~~~ + +Checking whether cached data exists is very simple: just use the +``contains()`` method. It accepts a single argument which is the ID +of the cache entry. + +.. code-block:: php + + contains('cache_id')) { + echo 'cache exists'; + } else { + echo 'cache does not exist'; + } + +Fetching +~~~~~~~~ + +Now if you want to retrieve some cache entry you can use the +``fetch()`` method. It also accepts a single argument just like +``contains()`` which is again the ID of the cache entry. + +.. code-block:: php + + fetch('my_array'); + +Deleting +~~~~~~~~ + +As you might guess, deleting is just as easy as saving, checking +and fetching. You can delete by an individual ID, or you can +delete all entries. + +By Cache ID +^^^^^^^^^^^ + +.. code-block:: php + + delete('my_array'); + +All +^^^ + +If you simply want to delete all cache entries you can do so with +the ``deleteAll()`` method. + +.. code-block:: php + + deleteAll(); + +Namespaces +~~~~~~~~~~ + +If you heavily use caching in your application and use it in +multiple parts of your application, or use it in different +applications on the same server you may have issues with cache +naming collisions. This can be worked around by using namespaces. +You can set the namespace a cache driver should use by using the +``setNamespace()`` method. + +.. code-block:: php + + setNamespace('my_namespace_'); + +Integrating with the ORM +------------------------ + +The Doctrine ORM package is tightly integrated with the cache +drivers to allow you to improve the performance of various aspects of +Doctrine by simply making some additional configurations and +method calls. + +Query Cache +~~~~~~~~~~~ + +It is highly recommended that in a production environment you cache +the transformation of a DQL query to its SQL counterpart. It +doesn't make sense to do this parsing multiple times as it doesn't +change unless you alter the DQL query. + +This can be done by configuring the query cache implementation to +use on your ORM configuration. + +.. code-block:: php + + setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Result Cache +~~~~~~~~~~~~ + +The result cache can be used to cache the results of your queries +so that we don't have to query the database or hydrate the data +again after the first time. You just need to configure the result +cache implementation. + +.. code-block:: php + + setResultCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Now when you're executing DQL queries you can configure them to use +the result cache. + +.. code-block:: php + + createQuery('select u from \Entities\User u'); + $query->useResultCache(true); + +You can also configure an individual query to use a different +result cache driver. + +.. code-block:: php + + setResultCacheDriver(new \Doctrine\Common\Cache\ApcCache()); + +.. note:: + + Setting the result cache driver on the query will + automatically enable the result cache for the query. If you want to + disable it pass false to ``useResultCache()``. + + :: + + useResultCache(false); + + +If you want to set the time the cache has to live you can use the +``setResultCacheLifetime()`` method. + +.. code-block:: php + + setResultCacheLifetime(3600); + +The ID used to store the result set cache is a hash which is +automatically generated for you if you don't set a custom ID +yourself with the ``setResultCacheId()`` method. + +.. code-block:: php + + setResultCacheId('my_custom_id'); + +You can also set the lifetime and cache ID by passing the values as +the second and third argument to ``useResultCache()``. + +.. code-block:: php + + useResultCache(true, 3600, 'my_custom_id'); + +Metadata Cache +~~~~~~~~~~~~~~ + +Your class metadata can be parsed from a few different sources like +YAML, XML, Annotations, etc. Instead of parsing this information on +each request we should cache it using one of the cache drivers. + +Just like the query and result cache we need to configure it +first. + +.. code-block:: php + + setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache()); + +Now the metadata information will only be parsed once and stored in +the cache driver. + +Clearing the Cache +------------------ + +We've already shown you how you can use the API of the +cache drivers to manually delete cache entries. For your +convenience we offer command line tasks to help you with +clearing the query, result and metadata cache. + +From the Doctrine command line you can run the following commands: + +To clear the query cache use the ``orm:clear-cache:query`` task. + +.. code-block:: php + + $ ./doctrine orm:clear-cache:query + +To clear the metadata cache use the ``orm:clear-cache:metadata`` task. + +.. code-block:: php + + $ ./doctrine orm:clear-cache:metadata + +To clear the result cache use the ``orm:clear-cache:result`` task. + +.. code-block:: php + + $ ./doctrine orm:clear-cache:result + +All these tasks accept a ``--flush`` option to flush the entire +contents of the cache instead of invalidating the entries. + +Cache Slams +----------- + +Something to be careful of when using the cache drivers is +"cache slams". Imagine you have a heavily trafficked website with some +code that checks for the existence of a cache record and if it does +not exist it generates the information and saves it to the cache. +Now, if 100 requests were issued all at the same time and each one +sees the cache does not exist and they all try to insert the same +cache entry it could lock up APC, Xcache, etc. and cause problems. +Ways exist to work around this, like pre-populating your cache and +not letting your users' requests populate the cache. + +You can read more about cache slams +`in this blog post `_. + + diff --git a/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst b/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst new file mode 100644 index 0000000..d0f0998 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/change-tracking-policies.rst @@ -0,0 +1,151 @@ +Change Tracking Policies +======================== + +Change tracking is the process of determining what has changed in +managed entities since the last time they were synchronized with +the database. + +Doctrine provides 3 different change tracking policies, each having +its particular advantages and disadvantages. The change tracking +policy can be defined on a per-class basis (or more precisely, +per-hierarchy). + +Deferred Implicit +~~~~~~~~~~~~~~~~~ + +The deferred implicit policy is the default change tracking policy +and the most convenient one. With this policy, Doctrine detects the +changes by a property-by-property comparison at commit time and +also detects changes to entities or new entities that are +referenced by other managed entities ("persistence by +reachability"). Although the most convenient policy, it can have +negative effects on performance if you are dealing with large units +of work (see "Understanding the Unit of Work"). Since Doctrine +can't know what has changed, it needs to check all managed entities +for changes every time you invoke EntityManager#flush(), making +this operation rather costly. + +Deferred Explicit +~~~~~~~~~~~~~~~~~ + +The deferred explicit policy is similar to the deferred implicit +policy in that it detects changes through a property-by-property +comparison at commit time. The difference is that Doctrine 2 only +considers entities that have been explicitly marked for change detection +through a call to EntityManager#persist(entity) or through a save +cascade. All other entities are skipped. This policy therefore +gives improved performance for larger units of work while +sacrificing the behavior of "automatic dirty checking". + +Therefore, flush() operations are potentially cheaper with this +policy. The negative aspect this has is that if you have a rather +large application and you pass your objects through several layers +for processing purposes and business tasks you may need to track +yourself which entities have changed on the way so you can pass +them to EntityManager#persist(). + +This policy can be configured as follows: + +.. code-block:: php + + _listeners[] = $listener; + } + } + +Then, in each property setter of this class or derived classes, you +need to notify all the ``PropertyChangedListener`` instances. As an +example we add a convenience method on ``MyEntity`` that shows this +behaviour: + +.. code-block:: php + + _listeners) { + foreach ($this->_listeners as $listener) { + $listener->propertyChanged($this, $propName, $oldValue, $newValue); + } + } + } + + public function setData($data) + { + if ($data != $this->data) { + $this->_onPropertyChanged('data', $this->data, $data); + $this->data = $data; + } + } + } + +You have to invoke ``_onPropertyChanged`` inside every method that +changes the persistent state of ``MyEntity``. + +The check whether the new value is different from the old one is +not mandatory but recommended. That way you also have full control +over when you consider a property changed. + +The negative point of this policy is obvious: You need implement an +interface and write some plumbing code. But also note that we tried +hard to keep this notification functionality abstract. Strictly +speaking, it has nothing to do with the persistence layer and the +Doctrine ORM or DBAL. You may find that property notification +events come in handy in many other scenarios as well. As mentioned +earlier, the ``Doctrine\Common`` namespace is not that evil and +consists solely of very small classes and interfaces that have +almost no external dependencies (none to the DBAL and none to the +ORM) and that you can easily take with you should you want to swap +out the persistence layer. This change tracking policy does not +introduce a dependency on the Doctrine DBAL/ORM or the persistence +layer. + +The positive point and main advantage of this policy is its +effectiveness. It has the best performance characteristics of the 3 +policies with larger units of work and a flush() operation is very +cheap when nothing has changed. + + diff --git a/vendor/doctrine/orm/docs/en/reference/configuration.rst b/vendor/doctrine/orm/docs/en/reference/configuration.rst new file mode 100644 index 0000000..5837f0f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/configuration.rst @@ -0,0 +1,141 @@ +Installation and Configuration +============================== + +Doctrine can be installed with `Composer `_. For +older versions we still have `PEAR packages +`_. + +Define the following requirement in your ``composer.json`` file: + +:: + + { + "require": { + "doctrine/orm": "*" + } + } + +Then call ``composer install`` from your command line. If you don't know +how Composer works, check out their `Getting Started +`_ to set up. + +Class loading +------------- + +Autoloading is taken care of by Composer. You just have to include the composer autoload file in your project: + +.. code-block:: php + + 'pdo_mysql', + 'user' => 'root', + 'password' => '', + 'dbname' => 'foo', + ); + + $config = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode); + $entityManager = EntityManager::create($dbParams, $config); + +Or if you prefer XML: + +.. code-block:: php + + ` section. + +.. note:: + + You can learn more about the database connection configuration in the + `Doctrine DBAL connection configuration reference `_. + +Setting up the Commandline Tool +------------------------------- + +Doctrine ships with a number of command line tools that are very helpful +during development. You can call this command from the Composer binary +directory: + +.. code-block:: sh + + $ php vendor/bin/doctrine + +You need to register your applications EntityManager to the console tool +to make use of the tasks by creating a ``cli-config.php`` file with the +following content: + +On Doctrine 2.4 and above: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); diff --git a/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst b/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst new file mode 100644 index 0000000..ea5f60d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/dql-doctrine-query-language.rst @@ -0,0 +1,1696 @@ +Doctrine Query Language +=========================== + +DQL stands for Doctrine Query Language and is an Object +Query Language derivate that is very similar to the Hibernate +Query Language (HQL) or the Java Persistence Query Language (JPQL). + +In essence, DQL provides powerful querying capabilities over your +object model. Imagine all your objects lying around in some storage +(like an object database). When writing DQL queries, think about +querying that storage to pick a certain subset of your objects. + +.. note:: + + A common mistake for beginners is to mistake DQL for + being just some form of SQL and therefore trying to use table names + and column names or join arbitrary tables together in a query. You + need to think about DQL as a query language for your object model, + not for your relational schema. + + +DQL is case in-sensitive, except for namespace, class and field +names, which are case sensitive. + +Types of DQL queries +-------------------- + +DQL as a query language has SELECT, UPDATE and DELETE constructs +that map to their corresponding SQL statement types. INSERT +statements are not allowed in DQL, because entities and their +relations have to be introduced into the persistence context +through ``EntityManager#persist()`` to ensure consistency of your +object model. + +DQL SELECT statements are a very powerful way of retrieving parts +of your domain model that are not accessible via associations. +Additionally they allow to retrieve entities and their associations +in one single SQL select statement which can make a huge difference +in performance in contrast to using several queries. + +DQL UPDATE and DELETE statements offer a way to execute bulk +changes on the entities of your domain model. This is often +necessary when you cannot load all the affected entities of a bulk +update into memory. + +SELECT queries +-------------- + +DQL SELECT clause +~~~~~~~~~~~~~~~~~ + +The select clause of a DQL query specifies what appears in the +query result. The composition of all the expressions in the select +clause also influences the nature of the query result. + +Here is an example that selects all users with an age > 20: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u WHERE u.age > 20'); + $users = $query->getResult(); + +Lets examine the query: + + +- ``u`` is a so called identification variable or alias that + refers to the ``MyProject\Model\User`` class. By placing this alias + in the SELECT clause we specify that we want all instances of the + User class that are matched by this query to appear in the query + result. +- The FROM keyword is always followed by a fully-qualified class + name which in turn is followed by an identification variable or + alias for that class name. This class designates a root of our + query from which we can navigate further via joins (explained + later) and path expressions. +- The expression ``u.age`` in the WHERE clause is a path + expression. Path expressions in DQL are easily identified by the + use of the '.' operator that is used for constructing paths. The + path expression ``u.age`` refers to the ``age`` field on the User + class. + +The result of this query would be a list of User objects where all +users are older than 20. + +The SELECT clause allows to specify both class identification +variables that signal the hydration of a complete entity class or +just fields of the entity using the syntax ``u.name``. Combinations +of both are also allowed and it is possible to wrap both fields and +identification values into aggregation and DQL functions. Numerical +fields can be part of computations using mathematical operations. +See the sub-section on `Functions, Operators, Aggregates`_ for +more information. + +Joins +~~~~~ + +A SELECT query can contain joins. There are 2 types of JOINs: +"Regular" Joins and "Fetch" Joins. + +**Regular Joins**: Used to limit the results and/or compute +aggregate values. + +**Fetch Joins**: In addition to the uses of regular joins: Used to +fetch related entities and include them in the hydrated result of a +query. + +There is no special DQL keyword that distinguishes a regular join +from a fetch join. A join (be it an inner or outer join) becomes a +"fetch join" as soon as fields of the joined entity appear in the +SELECT part of the DQL query outside of an aggregate function. +Otherwise its a "regular join". + +Example: + +Regular join of the address: + +.. code-block:: php + + createQuery("SELECT u FROM User u JOIN u.address a WHERE a.city = 'Berlin'"); + $users = $query->getResult(); + +Fetch join of the address: + +.. code-block:: php + + createQuery("SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'"); + $users = $query->getResult(); + +When Doctrine hydrates a query with fetch-join it returns the class +in the FROM clause on the root level of the result array. In the +previous example an array of User instances is returned and the +address of each user is fetched and hydrated into the +``User#address`` variable. If you access the address Doctrine does +not need to lazy load the association with another query. + +.. note:: + + Doctrine allows you to walk all the associations between + all the objects in your domain model. Objects that were not already + loaded from the database are replaced with lazy load proxy + instances. Non-loaded Collections are also replaced by lazy-load + instances that fetch all the contained objects upon first access. + However relying on the lazy-load mechanism leads to many small + queries executed against the database, which can significantly + affect the performance of your application. **Fetch Joins** are the + solution to hydrate most or all of the entities that you need in a + single SELECT query. + + +Named and Positional Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +DQL supports both named and positional parameters, however in +contrast to many SQL dialects positional parameters are specified +with numbers, for example "?1", "?2" and so on. Named parameters +are specified with ":name1", ":name2" and so on. + +When referencing the parameters in ``Query#setParameter($param, $value)`` +both named and positional parameters are used **without** their prefixes. + +DQL SELECT Examples +~~~~~~~~~~~~~~~~~~~ + +This section contains a large set of DQL queries and some +explanations of what is happening. The actual result also depends +on the hydration mode. + +Hydrate all User entities: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u'); + $users = $query->getResult(); // array of User objects + +Retrieve the IDs of all CmsUsers: + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u'); + $ids = $query->getResult(); // array of CmsUser ids + +Retrieve the IDs of all users that have written an article: + +.. code-block:: php + + createQuery('SELECT DISTINCT u.id FROM CmsArticle a JOIN a.user u'); + $ids = $query->getResult(); // array of CmsUser ids + +Retrieve all articles and sort them by the name of the articles +users instance: + +.. code-block:: php + + createQuery('SELECT a FROM CmsArticle a JOIN a.user u ORDER BY u.name ASC'); + $articles = $query->getResult(); // array of CmsArticle objects + +Retrieve the Username and Name of a CmsUser: + +.. code-block:: php + + createQuery('SELECT u.username, u.name FROM CmsUser u'); + $users = $query->getResult(); // array of CmsUser username and name values + echo $users[0]['username']; + +Retrieve a ForumUser and his single associated entity: + +.. code-block:: php + + createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a'); + $users = $query->getResult(); // array of ForumUser objects with the avatar association loaded + echo get_class($users[0]->getAvatar()); + +Retrieve a CmsUser and fetch join all the phonenumbers he has: + +.. code-block:: php + + createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p'); + $users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded + $phonenumbers = $users[0]->getPhonenumbers(); + +Hydrate a result in Ascending: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u ORDER BY u.id ASC'); + $users = $query->getResult(); // array of ForumUser objects + +Or in Descending Order: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u ORDER BY u.id DESC'); + $users = $query->getResult(); // array of ForumUser objects + +Using Aggregate Functions: + +.. code-block:: php + + createQuery('SELECT COUNT(u.id) FROM Entities\User u'); + $count = $query->getSingleScalarResult(); + + $query = $em->createQuery('SELECT u, count(g.id) FROM Entities\User u JOIN u.groups g GROUP BY u.id'); + $result = $query->getResult(); + +With WHERE Clause and Positional Parameter: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE u.id = ?1'); + $query->setParameter(1, 321); + $users = $query->getResult(); // array of ForumUser objects + +With WHERE Clause and Named Parameter: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE u.username = :name'); + $query->setParameter('name', 'Bob'); + $users = $query->getResult(); // array of ForumUser objects + +With Nested Conditions in WHERE Clause: + +.. code-block:: php + + createQuery('SELECT u FROM ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id'); + $query->setParameters(array( + 'name' => 'Bob', + 'name2' => 'Alice', + 'id' => 321, + )); + $users = $query->getResult(); // array of ForumUser objects + +With COUNT DISTINCT: + +.. code-block:: php + + createQuery('SELECT COUNT(DISTINCT u.name) FROM CmsUser'); + $users = $query->getResult(); // array of ForumUser objects + +With Arithmetic Expression in WHERE clause: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000'); + $users = $query->getResult(); // array of ForumUser objects + +Retrieve user entities with Arithmetic Expression in ORDER clause, using the ``HIDDEN`` keyword: + +.. code-block:: php + + createQuery('SELECT u, u.posts_count + u.likes_count AS HIDDEN score FROM CmsUser u ORDER BY score'); + $users = $query->getResult(); // array of User objects + +Using a LEFT JOIN to hydrate all user-ids and optionally associated +article-ids: + +.. code-block:: php + + createQuery('SELECT u.id, a.id as article_id FROM CmsUser u LEFT JOIN u.articles a'); + $results = $query->getResult(); // array of user ids and every article_id for each user + +Restricting a JOIN clause by additional conditions: + +.. code-block:: php + + createQuery("SELECT u FROM CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE :foo"); + $query->setParameter('foo', '%foo%'); + $users = $query->getResult(); + +Using several Fetch JOINs: + +.. code-block:: php + + createQuery('SELECT u, a, p, c FROM CmsUser u JOIN u.articles a JOIN u.phonenumbers p JOIN a.comments c'); + $users = $query->getResult(); + +BETWEEN in WHERE clause: + +.. code-block:: php + + createQuery('SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ?1 AND ?2'); + $query->setParameter(1, 123); + $query->setParameter(2, 321); + $usernames = $query->getResult(); + +DQL Functions in WHERE clause: + +.. code-block:: php + + createQuery("SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'"); + $usernames = $query->getResult(); + +IN() Expression: + +.. code-block:: php + + createQuery('SELECT u.name FROM CmsUser u WHERE u.id IN(46)'); + $usernames = $query->getResult(); + + $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id IN (1, 2)'); + $users = $query->getResult(); + + $query = $em->createQuery('SELECT u FROM CmsUser u WHERE u.id NOT IN (1)'); + $users = $query->getResult(); + +CONCAT() DQL Function: + +.. code-block:: php + + createQuery("SELECT u.id FROM CmsUser u WHERE CONCAT(u.name, 's') = ?1"); + $query->setParameter(1, 'Jess'); + $ids = $query->getResult(); + + $query = $em->createQuery('SELECT CONCAT(u.id, u.name) FROM CmsUser u WHERE u.id = ?1'); + $query->setParameter(1, 321); + $idUsernames = $query->getResult(); + +EXISTS in WHERE clause with correlated Subquery + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u WHERE EXISTS (SELECT p.phonenumber FROM CmsPhonenumber p WHERE p.user = u.id)'); + $ids = $query->getResult(); + +Get all users who are members of $group. + +.. code-block:: php + + createQuery('SELECT u.id FROM CmsUser u WHERE :groupId MEMBER OF u.groups'); + $query->setParameter('groupId', $group); + $ids = $query->getResult(); + +Get all users that have more than 1 phonenumber + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE SIZE(u.phonenumbers) > 1'); + $users = $query->getResult(); + +Get all users that have no phonenumber + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u WHERE u.phonenumbers IS EMPTY'); + $users = $query->getResult(); + +Get all instances of a specific type, for use with inheritance +hierarchies: + +.. versionadded:: 2.1 + +.. code-block:: php + + createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee'); + $query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1'); + $query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1'); + +Get all users visible on a given website that have chosen certain gender: + +.. versionadded:: 2.2 + +.. code-block:: php + + createQuery('SELECT u FROM User u WHERE u.gender IN (SELECT IDENTITY(agl.gender) FROM Site s JOIN s.activeGenderList agl WHERE s.id = ?1)'); + +.. versionadded:: 2.4 + +Starting with 2.4, the IDENTITY() DQL function also works for composite primary keys: + +.. code-block:: php + + createQuery("SELECT IDENTITY(c.location, 'latitude') AS latitude, IDENTITY(c.location, 'longitude') AS longitude FROM Checkpoint c WHERE c.user = ?1"); + +Joins between entities without associations were not possible until version +2.4, where you can generate an arbitrary join with the following syntax: + +.. code-block:: php + + createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email'); + +Partial Object Syntax +^^^^^^^^^^^^^^^^^^^^^ + +By default when you run a DQL query in Doctrine and select only a +subset of the fields for a given entity, you do not receive objects +back. Instead, you receive only arrays as a flat rectangular result +set, similar to how you would if you were just using SQL directly +and joining some data. + +If you want to select partial objects you can use the ``partial`` +DQL keyword: + +.. code-block:: php + + createQuery('SELECT partial u.{id, username} FROM CmsUser u'); + $users = $query->getResult(); // array of partially loaded CmsUser objects + +You use the partial syntax when joining as well: + +.. code-block:: php + + createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a'); + $users = $query->getResult(); // array of partially loaded CmsUser objects + +"NEW" Operator Syntax +^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 2.4 + +Using the ``NEW`` operator you can construct Data Transfer Objects (DTOs) directly from DQL queries. + +- When using ``SELECT NEW`` you don't need to specify a mapped entity. +- You can specify any PHP class, it's only require that the constructor of this class matches the ``NEW`` statement. +- This approach involves determining exactly which columns you really need, + and instantiating data-transfer object that containing a constructor with those arguments. + +If you want to select data-transfer objects you should create a class: + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city) FROM Customer c JOIN c.email e JOIN c.address a'); + $users = $query->getResult(); // array of CustomerDTO + +.. code-block:: php + + createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c'); + $users = $query->getResult(); // array of CustomerDTO + +Note that you can only pass scalar expressions to the constructor. + +Using INDEX BY +~~~~~~~~~~~~~~ + +The INDEX BY construct is nothing that directly translates into SQL +but that affects object and array hydration. After each FROM and +JOIN clause you specify by which field this class should be indexed +in the result. By default a result is incremented by numerical keys +starting with 0. However with INDEX BY you can specify any other +column to be the key of your result, it really only makes sense +with primary or unique fields though: + +.. code-block:: sql + + SELECT u.id, u.status, upper(u.name) nameUpper FROM User u INDEX BY u.id + JOIN u.phonenumbers p INDEX BY p.phonenumber + +Returns an array of the following kind, indexed by both user-id +then phonenumber-id: + +.. code-block:: php + + array + 0 => + array + 1 => + object(stdClass)[299] + public '__CLASS__' => string 'Doctrine\Tests\Models\CMS\CmsUser' (length=33) + public 'id' => int 1 + .. + 'nameUpper' => string 'ROMANB' (length=6) + 1 => + array + 2 => + object(stdClass)[298] + public '__CLASS__' => string 'Doctrine\Tests\Models\CMS\CmsUser' (length=33) + public 'id' => int 2 + ... + 'nameUpper' => string 'JWAGE' (length=5) + +UPDATE queries +-------------- + +DQL not only allows to select your Entities using field names, you +can also execute bulk updates on a set of entities using an +DQL-UPDATE query. The Syntax of an UPDATE query works as expected, +as the following example shows: + +.. code-block:: sql + + UPDATE MyProject\Model\User u SET u.password = 'new' WHERE u.id IN (1, 2, 3) + +References to related entities are only possible in the WHERE +clause and using sub-selects. + +.. warning:: + + DQL UPDATE statements are ported directly into a + Database UPDATE statement and therefore bypass any locking scheme, events + and do not increment the version column. Entities that are already + loaded into the persistence context will *NOT* be synced with the + updated database state. It is recommended to call + ``EntityManager#clear()`` and retrieve new instances of any + affected entity. + + +DELETE queries +-------------- + +DELETE queries can also be specified using DQL and their syntax is +as simple as the UPDATE syntax: + +.. code-block:: sql + + DELETE MyProject\Model\User u WHERE u.id = 4 + +The same restrictions apply for the reference of related entities. + +.. warning:: + + DQL DELETE statements are ported directly into a + Database DELETE statement and therefore bypass any events and checks for the + version column if they are not explicitly added to the WHERE clause + of the query. Additionally Deletes of specifies entities are *NOT* + cascaded to related entities even if specified in the metadata. + + +Functions, Operators, Aggregates +-------------------------------- + +DQL Functions +~~~~~~~~~~~~~ + +The following functions are supported in SELECT, WHERE and HAVING +clauses: + + +- IDENTITY(single\_association\_path\_expression [, fieldMapping]) - Retrieve the foreign key column of association of the owning side +- ABS(arithmetic\_expression) +- CONCAT(str1, str2) +- CURRENT\_DATE() - Return the current date +- CURRENT\_TIME() - Returns the current time +- CURRENT\_TIMESTAMP() - Returns a timestamp of the current date + and time. +- LENGTH(str) - Returns the length of the given string +- LOCATE(needle, haystack [, offset]) - Locate the first + occurrence of the substring in the string. +- LOWER(str) - returns the string lowercased. +- MOD(a, b) - Return a MOD b. +- SIZE(collection) - Return the number of elements in the + specified collection +- SQRT(q) - Return the square-root of q. +- SUBSTRING(str, start [, length]) - Return substring of given + string. +- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim + the string by the given trim char, defaults to whitespaces. +- UPPER(str) - Return the upper-case of the given string. +- DATE_ADD(date, days, unit) - Add the number of days to a given date. (Supported units are DAY, MONTH) +- DATE_SUB(date, days, unit) - Substract the number of days from a given date. (Supported units are DAY, MONTH) +- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2. + +Arithmetic operators +~~~~~~~~~~~~~~~~~~~~ + +You can do math in DQL using numeric values, for example: + +.. code-block:: sql + + SELECT person.salary * 1.5 FROM CompanyPerson person WHERE person.salary < 100000 + +Aggregate Functions +~~~~~~~~~~~~~~~~~~~ + +The following aggregate functions are allowed in SELECT and GROUP +BY clauses: AVG, COUNT, MIN, MAX, SUM + +Other Expressions +~~~~~~~~~~~~~~~~~ + +DQL offers a wide-range of additional expressions that are known +from SQL, here is a list of all the supported constructs: + + +- ``ALL/ANY/SOME`` - Used in a WHERE clause followed by a + sub-select this works like the equivalent constructs in SQL. +- ``BETWEEN a AND b`` and ``NOT BETWEEN a AND b`` can be used to + match ranges of arithmetic values. +- ``IN (x1, x2, ...)`` and ``NOT IN (x1, x2, ..)`` can be used to + match a set of given values. +- ``LIKE ..`` and ``NOT LIKE ..`` match parts of a string or text + using % as a wildcard. +- ``IS NULL`` and ``IS NOT NULL`` to check for null values +- ``EXISTS`` and ``NOT EXISTS`` in combination with a sub-select + +Adding your own functions to the DQL language +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default DQL comes with functions that are part of a large basis +of underlying databases. However you will most likely choose a +database platform at the beginning of your project and most likely +never change it. For this cases you can easily extend the DQL +parser with own specialized platform functions. + +You can register custom DQL functions in your ORM Configuration: + +.. code-block:: php + + addCustomStringFunction($name, $class); + $config->addCustomNumericFunction($name, $class); + $config->addCustomDatetimeFunction($name, $class); + + $em = EntityManager::create($dbParams, $config); + +The functions have to return either a string, numeric or datetime +value depending on the registered function type. As an example we +will add a MySQL specific FLOOR() functionality. All the given +classes have to implement the base class : + +.. code-block:: php + + walkSimpleArithmeticExpression( + $this->simpleArithmeticExpression + ) . ')'; + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + } + +We will register the function by calling and can then use it: + +.. code-block:: php + + getConfiguration(); + $config->registerNumericFunction('FLOOR', 'MyProject\Query\MysqlFloor'); + + $dql = "SELECT FLOOR(person.salary * 1.75) FROM CompanyPerson person"; + +Querying Inherited Classes +-------------------------- + +This section demonstrates how you can query inherited classes and +what type of results to expect. + +Single Table +~~~~~~~~~~~~ + +`Single Table Inheritance `_ +is an inheritance mapping strategy where all classes of a hierarchy +are mapped to a single database table. In order to distinguish +which row represents which type in the hierarchy a so-called +discriminator column is used. + +First we need to setup an example set of entities to use. In this +scenario it is a generic Person and Employee example: + +.. code-block:: php + + setName('test'); + $employee->setDepartment('testing'); + $em->persist($employee); + $em->flush(); + +Now lets run a simple query to retrieve the ``Employee`` we just +created: + +.. code-block:: sql + + SELECT e FROM Entities\Employee e WHERE e.name = 'test' + +If we check the generated SQL you will notice it has some special +conditions added to ensure that we will only get back ``Employee`` +entities: + +.. code-block:: sql + + SELECT p0_.id AS id0, p0_.name AS name1, p0_.department AS department2, + p0_.discr AS discr3 FROM Person p0_ + WHERE (p0_.name = ?) AND p0_.discr IN ('employee') + +Class Table Inheritance +~~~~~~~~~~~~~~~~~~~~~~~ + +`Class Table Inheritance `_ +is an inheritance mapping strategy where each class in a hierarchy +is mapped to several tables: its own table and the tables of all +parent classes. The table of a child class is linked to the table +of a parent class through a foreign key constraint. Doctrine 2 +implements this strategy through the use of a discriminator column +in the topmost table of the hierarchy because this is the easiest +way to achieve polymorphic queries with Class Table Inheritance. + +The example for class table inheritance is the same as single +table, you just need to change the inheritance type from +``SINGLE_TABLE`` to ``JOINED``: + +.. code-block:: php + + createQuery('select u from MyProject\Model\User u'); + + // example2: using setDql + $q = $em->createQuery(); + $q->setDql('select u from MyProject\Model\User u'); + +Query Result Formats +~~~~~~~~~~~~~~~~~~~~ + +The format in which the result of a DQL SELECT query is returned +can be influenced by a so-called ``hydration mode``. A hydration +mode specifies a particular way in which a SQL result set is +transformed. Each hydration mode has its own dedicated method on +the Query class. Here they are: + + +- ``Query#getResult()``: Retrieves a collection of objects. The + result is either a plain collection of objects (pure) or an array + where the objects are nested in the result rows (mixed). +- ``Query#getSingleResult()``: Retrieves a single object. If the + result contains more than one object, an ``NonUniqueResultException`` + is thrown. If the result contains no objects, an ``NoResultException`` + is thrown. The pure/mixed distinction does not apply. +- ``Query#getOneOrNullResult()``: Retrieve a single object. If no + object is found null will be returned. +- ``Query#getArrayResult()``: Retrieves an array graph (a nested + array) that is largely interchangeable with the object graph + generated by ``Query#getResult()`` for read-only purposes. + + .. note:: + + An array graph can differ from the corresponding object + graph in certain scenarios due to the difference of the identity + semantics between arrays and objects. + + + +- ``Query#getScalarResult()``: Retrieves a flat/rectangular result + set of scalar values that can contain duplicate data. The + pure/mixed distinction does not apply. +- ``Query#getSingleScalarResult()``: Retrieves a single scalar + value from the result returned by the dbms. If the result contains + more than a single scalar value, an exception is thrown. The + pure/mixed distinction does not apply. + +Instead of using these methods, you can alternatively use the +general-purpose method +``Query#execute(array $params = array(), $hydrationMode = Query::HYDRATE_OBJECT)``. +Using this method you can directly supply the hydration mode as the +second parameter via one of the Query constants. In fact, the +methods mentioned earlier are just convenient shortcuts for the +execute method. For example, the method ``Query#getResult()`` +internally invokes execute, passing in ``Query::HYDRATE_OBJECT`` as +the hydration mode. + +The use of the methods mentioned earlier is generally preferred as +it leads to more concise code. + +Pure and Mixed Results +~~~~~~~~~~~~~~~~~~~~~~ + +The nature of a result returned by a DQL SELECT query retrieved +through ``Query#getResult()`` or ``Query#getArrayResult()`` can be +of 2 forms: **pure** and **mixed**. In the previous simple +examples, you already saw a "pure" query result, with only objects. +By default, the result type is **pure** but +**as soon as scalar values, such as aggregate values or other scalar values that do not belong to an entity, appear in the SELECT part of the DQL query, the result becomes mixed**. +A mixed result has a different structure than a pure result in +order to accommodate for the scalar values. + +A pure result usually looks like this: + +.. code-block:: php + + $dql = "SELECT u FROM User u"; + + array + [0] => Object + [1] => Object + [2] => Object + ... + +A mixed result on the other hand has the following general +structure: + +.. code-block:: php + + $dql = "SELECT u, 'some scalar string', count(u.groups) AS num FROM User u JOIN u.groups g GROUP BY u.id"; + + array + [0] + [0] => Object + [1] => "some scalar string" + ['num'] => 42 + // ... more scalar values, either indexed numerically or with a name + [1] + [0] => Object + [1] => "some scalar string" + ['num'] => 42 + // ... more scalar values, either indexed numerically or with a name + +To better understand mixed results, consider the following DQL +query: + +.. code-block:: sql + + SELECT u, UPPER(u.name) nameUpper FROM MyProject\Model\User u + +This query makes use of the ``UPPER`` DQL function that returns a +scalar value and because there is now a scalar value in the SELECT +clause, we get a mixed result. + +Conventions for mixed results are as follows: + + +- The object fetched in the FROM clause is always positioned with the key '0'. +- Every scalar without a name is numbered in the order given in the query, starting with 1. +- Every aliased scalar is given with its alias-name as the key. The case of the name is kept. +- If several objects are fetched from the FROM clause they alternate every row. + + +Here is how the result could look like: + +.. code-block:: php + + array + array + [0] => User (Object) + ['nameUpper'] => "ROMAN" + array + [0] => User (Object) + ['nameUpper'] => "JONATHAN" + ... + +And here is how you would access it in PHP code: + +.. code-block:: php + + getName(); + echo "Name UPPER: " . $row['nameUpper']; + } + +Fetching Multiple FROM Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you fetch multiple entities that are listed in the FROM clause then the hydration +will return the rows iterating the different top-level entities. + +.. code-block:: php + + $dql = "SELECT u, g FROM User u, Group g"; + + array + [0] => Object (User) + [1] => Object (Group) + [2] => Object (User) + [3] => Object (Group) + + +Hydration Modes +~~~~~~~~~~~~~~~ + +Each of the Hydration Modes makes assumptions about how the result +is returned to user land. You should know about all the details to +make best use of the different result formats: + +The constants for the different hydration modes are: + + +- Query::HYDRATE\_OBJECT +- Query::HYDRATE\_ARRAY +- Query::HYDRATE\_SCALAR +- Query::HYDRATE\_SINGLE\_SCALAR + +Object Hydration +^^^^^^^^^^^^^^^^ + +Object hydration hydrates the result set into the object graph: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_OBJECT); + +Array Hydration +^^^^^^^^^^^^^^^ + +You can run the same query with array hydration and the result set +is hydrated into an array that represents the object graph: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_ARRAY); + +You can use the ``getArrayResult()`` shortcut as well: + +.. code-block:: php + + getArrayResult(); + +Scalar Hydration +^^^^^^^^^^^^^^^^ + +If you want to return a flat rectangular result set instead of an +object graph you can use scalar hydration: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $users = $query->getResult(Query::HYDRATE_SCALAR); + echo $users[0]['u_id']; + +The following assumptions are made about selected fields using +Scalar Hydration: + + +1. Fields from classes are prefixed by the DQL alias in the result. + A query of the kind 'SELECT u.name ..' returns a key 'u\_name' in + the result rows. + +Single Scalar Hydration +^^^^^^^^^^^^^^^^^^^^^^^ + +If you have a query which returns just a single scalar value you can use +single scalar hydration: + +.. code-block:: php + + createQuery('SELECT COUNT(a.id) FROM CmsUser u LEFT JOIN u.articles a WHERE u.username = ?1 GROUP BY u.id'); + $query->setParameter(1, 'jwage'); + $numArticles = $query->getResult(Query::HYDRATE_SINGLE_SCALAR); + +You can use the ``getSingleScalarResult()`` shortcut as well: + +.. code-block:: php + + getSingleScalarResult(); + +Custom Hydration Modes +^^^^^^^^^^^^^^^^^^^^^^ + +You can easily add your own custom hydration modes by first +creating a class which extends ``AbstractHydrator``: + +.. code-block:: php + + _stmt->fetchAll(PDO::FETCH_ASSOC); + } + } + +Next you just need to add the class to the ORM configuration: + +.. code-block:: php + + getConfiguration()->addCustomHydrationMode('CustomHydrator', 'MyProject\Hydrators\CustomHydrator'); + +Now the hydrator is ready to be used in your queries: + +.. code-block:: php + + createQuery('SELECT u FROM CmsUser u'); + $results = $query->getResult('CustomHydrator'); + +Iterating Large Result Sets +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are situations when a query you want to execute returns a +very large result-set that needs to be processed. All the +previously described hydration modes completely load a result-set +into memory which might not be feasible with large result sets. See +the `Batch Processing `_ section on details how +to iterate large result sets. + +Functions +~~~~~~~~~ + +The following methods exist on the ``AbstractQuery`` which both +``Query`` and ``NativeQuery`` extend from. + +Parameters +^^^^^^^^^^ + +Prepared Statements that use numerical or named wildcards require +additional parameters to be executable against the database. To +pass parameters to the query the following methods can be used: + + +- ``AbstractQuery::setParameter($param, $value)`` - Set the + numerical or named wildcard to the given value. +- ``AbstractQuery::setParameters(array $params)`` - Set an array + of parameter key-value pairs. +- ``AbstractQuery::getParameter($param)`` +- ``AbstractQuery::getParameters()`` + +Both named and positional parameters are passed to these methods without their ? or : prefix. + +Cache related API +^^^^^^^^^^^^^^^^^ + +You can cache query results based either on all variables that +define the result (SQL, Hydration Mode, Parameters and Hints) or on +user-defined cache keys. However by default query results are not +cached at all. You have to enable the result cache on a per query +basis. The following example shows a complete workflow using the +Result Cache API: + +.. code-block:: php + + createQuery('SELECT u FROM MyProject\Model\User u WHERE u.id = ?1'); + $query->setParameter(1, 12); + + $query->setResultCacheDriver(new ApcCache()); + + $query->useResultCache(true) + ->setResultCacheLifeTime($seconds = 3600); + + $result = $query->getResult(); // cache miss + + $query->expireResultCache(true); + $result = $query->getResult(); // forced expire, cache miss + + $query->setResultCacheId('my_query_result'); + $result = $query->getResult(); // saved in given result cache id. + + // or call useResultCache() with all parameters: + $query->useResultCache(true, $seconds = 3600, 'my_query_result'); + $result = $query->getResult(); // cache hit! + + // Introspection + $queryCacheProfile = $query->getQueryCacheProfile(); + $cacheDriver = $query->getResultCacheDriver(); + $lifetime = $query->getLifetime(); + $key = $query->getCacheKey(); + +.. note:: + + You can set the Result Cache Driver globally on the + ``Doctrine\ORM\Configuration`` instance so that it is passed to + every ``Query`` and ``NativeQuery`` instance. + + +Query Hints +^^^^^^^^^^^ + +You can pass hints to the query parser and hydrators by using the +``AbstractQuery::setHint($name, $value)`` method. Currently there +exist mostly internal query hints that are not be consumed in +userland. However the following few hints are to be used in +userland: + + +- Query::HINT\_FORCE\_PARTIAL\_LOAD - Allows to hydrate objects + although not all their columns are fetched. This query hint can be + used to handle memory consumption problems with large result-sets + that contain char or binary data. Doctrine has no way of implicitly + reloading this data. Partially loaded objects have to be passed to + ``EntityManager::refresh()`` if they are to be reloaded fully from + the database. +- Query::HINT\_REFRESH - This query is used internally by + ``EntityManager::refresh()`` and can be used in userland as well. + If you specify this hint and a query returns the data for an entity + that is already managed by the UnitOfWork, the fields of the + existing entity will be refreshed. In normal operation a result-set + that loads data of an already existing entity is discarded in favor + of the already existing entity. +- Query::HINT\_CUSTOM\_TREE\_WALKERS - An array of additional + ``Doctrine\ORM\Query\TreeWalker`` instances that are attached to + the DQL query parsing process. + +Query Cache (DQL Query Only) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Parsing a DQL query and converting it into a SQL query against the +underlying database platform obviously has some overhead in +contrast to directly executing Native SQL queries. That is why +there is a dedicated Query Cache for caching the DQL parser +results. In combination with the use of wildcards you can reduce +the number of parsed queries in production to zero. + +The Query Cache Driver is passed from the +``Doctrine\ORM\Configuration`` instance to each +``Doctrine\ORM\Query`` instance by default and is also enabled by +default. This also means you don't regularly need to fiddle with +the parameters of the Query Cache, however if you do there are +several methods to interact with it: + + +- ``Query::setQueryCacheDriver($driver)`` - Allows to set a Cache + instance +- ``Query::setQueryCacheLifeTime($seconds = 3600)`` - Set lifetime + of the query caching. +- ``Query::expireQueryCache($bool)`` - Enforce the expiring of the + query cache if set to true. +- ``Query::getExpireQueryCache()`` +- ``Query::getQueryCacheDriver()`` +- ``Query::getQueryCacheLifeTime()`` + +First and Max Result Items (DQL Query Only) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can limit the number of results returned from a DQL query as +well as specify the starting offset, Doctrine then uses a strategy +of manipulating the select query to return only the requested +number of results: + + +- ``Query::setMaxResults($maxResults)`` +- ``Query::setFirstResult($offset)`` + +.. note:: + + If your query contains a fetch-joined collection + specifying the result limit methods are not working as you would + expect. Set Max Results restricts the number of database result + rows, however in the case of fetch-joined collections one root + entity might appear in many rows, effectively hydrating less than + the specified number of results. + +.. _dql-temporarily-change-fetch-mode: + +Temporarily change fetch mode in DQL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While normally all your associations are marked as lazy or extra lazy you will have cases where you are using DQL and don't want to +fetch join a second, third or fourth level of entities into your result, because of the increased cost of the SQL JOIN. You +can mark a many-to-one or one-to-one association as fetched temporarily to batch fetch these entities using a WHERE .. IN query. + +.. code-block:: php + + createQuery("SELECT u FROM MyProject\User u"); + $query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER); + $query->execute(); + +Given that there are 10 users and corresponding addresses in the database the executed queries will look something like: + +.. code-block:: sql + + SELECT * FROM users; + SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + +.. note:: + Changing the fetch mode during a query is only possible for one-to-one and many-to-one relations. + + +EBNF +---- + +The following context-free grammar, written in an EBNF variant, +describes the Doctrine Query Language. You can consult this grammar +whenever you are unsure about what is possible with DQL or what the +correct syntax for a particular query should be. + +Document syntax: +~~~~~~~~~~~~~~~~ + + +- non-terminals begin with an upper case character +- terminals begin with a lower case character +- parentheses (...) are used for grouping +- square brackets [...] are used for defining an optional part, + e.g. zero or one time +- curly brackets {...} are used for repetition, e.g. zero or more + times +- double quotation marks "..." define a terminal string +- a vertical bar \| represents an alternative + +Terminals +~~~~~~~~~ + + +- identifier (name, email, ...) +- string ('foo', 'bar''s house', '%ninja%', ...) +- char ('/', '\\', ' ', ...) +- integer (-1, 0, 1, 34, ...) +- float (-0.23, 0.007, 1.245342E+8, ...) +- boolean (false, true) + +Query Language +~~~~~~~~~~~~~~ + +.. code-block:: php + + QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement + +Statements +~~~~~~~~~~ + +.. code-block:: php + + SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + UpdateStatement ::= UpdateClause [WhereClause] + DeleteStatement ::= DeleteClause [WhereClause] + +Identifiers +~~~~~~~~~~~ + +.. code-block:: php + + /* Alias Identification usage (the "u" of "u.name") */ + IdentificationVariable ::= identifier + + /* Alias Identification declaration (the "u" of "FROM User u") */ + AliasIdentificationVariable :: = identifier + + /* identifier that must be a class name (the "User" of "FROM User u") */ + AbstractSchemaName ::= identifier + + /* Alias ResultVariable declaration (the "total" of "COUNT(*) AS total") */ + AliasResultVariable = identifier + + /* ResultVariable identifier usage of mapped field aliases (the "total" of "COUNT(*) AS total") */ + ResultVariable = identifier + + /* identifier that must be a field (the "name" of "u.name") */ + /* This is responsible to know if the field exists in Object, no matter if it's a relation or a simple field */ + FieldIdentificationVariable ::= identifier + + /* identifier that must be a collection-valued association field (to-many) (the "Phonenumbers" of "u.Phonenumbers") */ + CollectionValuedAssociationField ::= FieldIdentificationVariable + + /* identifier that must be a single-valued association field (to-one) (the "Group" of "u.Group") */ + SingleValuedAssociationField ::= FieldIdentificationVariable + + /* identifier that must be an embedded class state field */ + EmbeddedClassStateField ::= FieldIdentificationVariable + + /* identifier that must be a simple state field (name, email, ...) (the "name" of "u.name") */ + /* The difference between this and FieldIdentificationVariable is only semantical, because it points to a single field (not mapping to a relation) */ + SimpleStateField ::= FieldIdentificationVariable + +Path Expressions +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /* "u.Group" or "u.Phonenumbers" declarations */ + JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + + /* "u.Group" or "u.Phonenumbers" usages */ + AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + + /* "u.name" or "u.Group" */ + SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + + /* "u.name" or "u.Group.name" */ + StateFieldPathExpression ::= IdentificationVariable "." StateField + + /* "u.Group" */ + SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + + /* "u.Group.Permissions" */ + CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + + /* "name" */ + StateField ::= {EmbeddedClassStateField "."}* SimpleStateField + +Clauses +~~~~~~~ + +.. code-block:: php + + SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}* + SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* + DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable + FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* + SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + WhereClause ::= "WHERE" ConditionalExpression + HavingClause ::= "HAVING" ConditionalExpression + GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* + OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + +Items +~~~~~ + +.. code-block:: php + + UpdateItem ::= SingleValuedPathExpression "=" NewValue + OrderByItem ::= (SimpleArithmeticExpression | SingleValuedPathExpression | ScalarExpression | ResultVariable | FunctionDeclaration) ["ASC" | "DESC"] + GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression + NewValue ::= SimpleArithmeticExpression | "NULL" + +From, Join and Index by +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* + SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration + RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] + Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" (JoinAssociationDeclaration | RangeVariableDeclaration) ["WITH" ConditionalExpression] + IndexBy ::= "INDEX" "BY" StateFieldPathExpression + +Select Expressions +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable] + SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable] + PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet + PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" + NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + NewObjectArg ::= ScalarExpression | "(" Subselect ")" + +Conditional Expressions +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + ConditionalFactor ::= ["NOT"] ConditionalPrimary + ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression | + InExpression | NullComparisonExpression | ExistsExpression | + EmptyCollectionComparisonExpression | CollectionMemberExpression | + InstanceOfExpression + + +Collection Expressions +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + +Literal Values +~~~~~~~~~~~~~~ + +.. code-block:: php + + Literal ::= string | char | integer | float | boolean + InParameter ::= Literal | InputParameter + +Input Parameter +~~~~~~~~~~~~~~~ + +.. code-block:: php + + InputParameter ::= PositionalParameter | NamedParameter + PositionalParameter ::= "?" integer + NamedParameter ::= ":" string + +Arithmetic Expressions +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")" + | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + | FunctionsReturningDatetime | IdentificationVariable | ResultVariable + | InputParameter | CaseExpression + +Scalar and Type Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression | BooleanPrimary | CaseExpression | InstanceOfExpression + StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" + StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression + BooleanExpression ::= BooleanPrimary | "(" Subselect ")" + BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter + EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + SimpleEntityExpression ::= IdentificationVariable | InputParameter + DatetimeExpression ::= DatetimePrimary | "(" Subselect ")" + DatetimePrimary ::= StateFieldPathExpression | InputParameter | FunctionsReturningDatetime | AggregateExpression + +.. note:: + + Parts of CASE expressions are not yet implemented. + +Aggregate Expressions +~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")" + +Case Expressions +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression + GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + +Other Expressions +~~~~~~~~~~~~~~~~~ + +QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS + +.. code-block:: php + + QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression + ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) + InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + InstanceOfParameter ::= AbstractSchemaName | InputParameter + LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] + NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" + ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" + +Functions +~~~~~~~~~ + +.. code-block:: php + + FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDateTime + + FunctionsReturningNumerics ::= + "LENGTH" "(" StringPrimary ")" | + "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | + "ABS" "(" SimpleArithmeticExpression ")" | + "SQRT" "(" SimpleArithmeticExpression ")" | + "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + "SIZE" "(" CollectionValuedPathExpression ")" | + "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + + FunctionsReturningDateTime ::= + "CURRENT_DATE" | + "CURRENT_TIME" | + "CURRENT_TIMESTAMP" | + "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | + "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + + FunctionsReturningStrings ::= + "CONCAT" "(" StringPrimary "," StringPrimary ")" | + "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + "LOWER" "(" StringPrimary ")" | + "UPPER" "(" StringPrimary ")" | + "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + + diff --git a/vendor/doctrine/orm/docs/en/reference/events.rst b/vendor/doctrine/orm/docs/en/reference/events.rst new file mode 100644 index 0000000..6ec2f09 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/events.rst @@ -0,0 +1,983 @@ +Events +====== + +Doctrine 2 features a lightweight event system that is part of the +Common package. Doctrine uses it to dispatch system events, mainly +:ref:`lifecycle events `. +You can also use it for your own custom events. + +The Event System +---------------- + +The event system is controlled by the ``EventManager``. It is the +central point of Doctrine's event listener system. Listeners are +registered on the manager and events are dispatched through the +manager. + +.. code-block:: php + + addEventListener(array(self::preFoo, self::postFoo), $this); + } + + public function preFoo(EventArgs $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(EventArgs $e) + { + $this->postFooInvoked = true; + } + } + + // Create a new instance + $test = new TestEvent($evm); + +Events can be dispatched by using the ``dispatchEvent()`` method. + +.. code-block:: php + + dispatchEvent(TestEvent::preFoo); + $evm->dispatchEvent(TestEvent::postFoo); + +You can easily remove a listener with the ``removeEventListener()`` +method. + +.. code-block:: php + + removeEventListener(array(self::preFoo, self::postFoo), $this); + +The Doctrine 2 event system also has a simple concept of event +subscribers. We can define a simple ``TestEventSubscriber`` class +which implements the ``\Doctrine\Common\EventSubscriber`` interface +and implements a ``getSubscribedEvents()`` method which returns an +array of events it should be subscribed to. + +.. code-block:: php + + preFooInvoked = true; + } + + public function getSubscribedEvents() + { + return array(TestEvent::preFoo); + } + } + + $eventSubscriber = new TestEventSubscriber(); + $evm->addEventSubscriber($eventSubscriber); + +.. note:: + + The array to return in the ``getSubscribedEvents`` method is a simple array + with the values being the event names. The subscriber must have a method + that is named exactly like the event. + +Now when you dispatch an event, any event subscribers will be +notified for that event. + +.. code-block:: php + + dispatchEvent(TestEvent::preFoo); + +Now you can test the ``$eventSubscriber`` instance to see if the +``preFoo()`` method was invoked. + +.. code-block:: php + + preFooInvoked) { + echo 'pre foo invoked!'; + } + +Naming convention +~~~~~~~~~~~~~~~~~ + +Events being used with the Doctrine 2 EventManager are best named +with camelcase and the value of the corresponding constant should +be the name of the constant itself, even with spelling. This has +several reasons: + + +- It is easy to read. +- Simplicity. +- Each method within an EventSubscriber is named after the + corresponding constant's value. If the constant's name and value differ + it contradicts the intention of using the constant and makes your code + harder to maintain. + +An example for a correct notation can be found in the example +``TestEvent`` above. + +.. _reference-events-lifecycle-events: + +Lifecycle Events +---------------- + +The EntityManager and UnitOfWork trigger a bunch of events during +the life-time of their registered entities. + + +- preRemove - The preRemove event occurs for a given entity before + the respective EntityManager remove operation for that entity is + executed. It is not called for a DQL DELETE statement. +- postRemove - The postRemove event occurs for an entity after the + entity has been deleted. It will be invoked after the database + delete operations. It is not called for a DQL DELETE statement. +- prePersist - The prePersist event occurs for a given entity + before the respective EntityManager persist operation for that + entity is executed. It should be noted that this event is only triggered on + *initial* persist of an entity (i.e. it does not trigger on future updates). +- postPersist - The postPersist event occurs for an entity after + the entity has been made persistent. It will be invoked after the + database insert operations. Generated primary key values are + available in the postPersist event. +- preUpdate - The preUpdate event occurs before the database + update operations to entity data. It is not called for a DQL UPDATE statement. +- postUpdate - The postUpdate event occurs after the database + update operations to entity data. It is not called for a DQL UPDATE statement. +- postLoad - The postLoad event occurs for an entity after the + entity has been loaded into the current EntityManager from the + database or after the refresh operation has been applied to it. +- loadClassMetadata - The loadClassMetadata event occurs after the + mapping metadata for a class has been loaded from a mapping source + (annotations/xml/yaml). This event is not a lifecycle callback. +- onClassMetadataNotFound - Loading class metadata for a particular + requested class name failed. Manipulating the given event args instance + allows providing fallback metadata even when no actual metadata exists + or could be found. This event is not a lifecycle callback. +- preFlush - The preFlush event occurs at the very beginning of a flush + operation. This event is not a lifecycle callback. +- onFlush - The onFlush event occurs after the change-sets of all + managed entities are computed. This event is not a lifecycle + callback. +- postFlush - The postFlush event occurs at the end of a flush operation. This + event is not a lifecycle callback. +- onClear - The onClear event occurs when the EntityManager#clear() operation is + invoked, after all references to entities have been removed from the unit of + work. This event is not a lifecycle callback. + +.. warning:: + + Note that, when using ``Doctrine\ORM\AbstractQuery#iterate()``, ``postLoad`` + events will be executed immediately after objects are being hydrated, and therefore + associations are not guaranteed to be initialized. It is not safe to combine + usage of ``Doctrine\ORM\AbstractQuery#iterate()`` and ``postLoad`` event + handlers. + +.. warning:: + + Note that the postRemove event or any events triggered after an entity removal + can receive an uninitializable proxy in case you have configured an entity to + cascade remove relations. In this case, you should load yourself the proxy in + the associated pre event. + +You can access the Event constants from the ``Events`` class in the +ORM package. + +.. code-block:: php + + createdAt = date('Y-m-d H:i:s'); + } + + /** @PrePersist */ + public function doOtherStuffOnPrePersist() + { + $this->value = 'changed from prePersist callback!'; + } + + /** @PostPersist */ + public function doStuffOnPostPersist() + { + $this->value = 'changed from postPersist callback!'; + } + + /** @PostLoad */ + public function doStuffOnPostLoad() + { + $this->value = 'changed from postLoad callback!'; + } + + /** @PreUpdate */ + public function doStuffOnPreUpdate() + { + $this->value = 'changed from preUpdate callback!'; + } + } + +Note that the methods set as lifecycle callbacks need to be public and, +when using these annotations, you have to apply the +``@HasLifecycleCallbacks`` marker annotation on the entity class. + +If you want to register lifecycle callbacks from YAML or XML you +can do it with the following. + +.. code-block:: yaml + + User: + type: entity + fields: + # ... + name: + type: string(50) + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersist ] + postPersist: [ doStuffOnPostPersist ] + +In YAML the ``key`` of the lifecycleCallbacks entry is the event that you +are triggering on and the value is the method (or methods) to call. The allowed +event types are the ones listed in the previous Lifecycle Events section. + +XML would look something like this: + +.. code-block:: xml + + + + + + + + + + + + + + + + +In XML the ``type`` of the lifecycle-callback entry is the event that you +are triggering on and the ``method`` is the method to call. The allowed event +types are the ones listed in the previous Lifecycle Events section. + +When using YAML or XML you need to remember to create public methods to match the +callback names you defined. E.g. in these examples ``doStuffOnPrePersist()``, +``doOtherStuffOnPrePersist()`` and ``doStuffOnPostPersist()`` methods need to be +defined on your ``User`` model. + +.. code-block:: php + + hasChangedField('username')) { + // Do something when the username is changed. + } + } + } + +Listening and subscribing to Lifecycle Events +--------------------------------------------- + +Lifecycle event listeners are much more powerful than the simple +lifecycle callbacks that are defined on the entity classes. They +sit at a level above the entities and allow you to implement re-usable +behaviors across different entity classes. + +Note that they require much more detailed knowledge about the inner +workings of the EntityManager and UnitOfWork. Please read the +*Implementing Event Listeners* section carefully if you are trying +to write your own listener. + +For event subscribers, there are no surprises. They declare the +lifecycle events in their ``getSubscribedEvents`` method and provide +public methods that expect the relevant arguments. + +A lifecycle event listener looks like the following: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + } + +A lifecycle event subscriber may looks like this: + +.. code-block:: php + + getObject(); + $entityManager = $args->getObjectManager(); + + // perhaps you only want to act on some "Product" entity + if ($entity instanceof Product) { + // do something with the Product + } + } + +.. note:: + + Lifecycle events are triggered for all entities. It is the responsibility + of the listeners and subscribers to check if the entity is of a type + it wants to handle. + +To register an event listener or subscriber, you have to hook it into the +EventManager that is passed to the EntityManager factory: + +.. code-block:: php + + addEventListener(array(Events::preUpdate), new MyEventListener()); + $eventManager->addEventSubscriber(new MyEventSubscriber()); + + $entityManager = EntityManager::create($dbOpts, $config, $eventManager); + +You can also retrieve the event manager instance after the +EntityManager was created: + +.. code-block:: php + + getEventManager()->addEventListener(array(Events::preUpdate), new MyEventListener()); + $entityManager->getEventManager()->addEventSubscriber(new MyEventSubscriber()); + +.. _reference-events-implementing-listeners: + +Implementing Event Listeners +---------------------------- + +This section explains what is and what is not allowed during +specific lifecycle events of the UnitOfWork. Although you get +passed the EntityManager in all of these events, you have to follow +these restrictions very carefully since operations in the wrong +event may produce lots of different errors, such as inconsistent +data and lost updates/persists/removes. + +For the described events that are also lifecycle callback events +the restrictions apply as well, with the additional restriction +that (prior to version 2.4) you do not have access to the +EntityManager or UnitOfWork APIs inside these events. + +prePersist +~~~~~~~~~~ + +There are two ways for the ``prePersist`` event to be triggered. +One is obviously when you call ``EntityManager#persist()``. The +event is also called for all cascaded associations. + +There is another way for ``prePersist`` to be called, inside the +``flush()`` method when changes to associations are computed and +this association is marked as cascade persist. Any new entity found +during this operation is also persisted and ``prePersist`` called +on it. This is called "persistence by reachability". + +In both cases you get passed a ``LifecycleEventArgs`` instance +which has access to the entity and the entity manager. + +The following restrictions apply to ``prePersist``: + + +- If you are using a PrePersist Identity Generator such as + sequences the ID value will *NOT* be available within any + PrePersist events. +- Doctrine will not recognize changes made to relations in a prePersist + event. This includes modifications to + collections such as additions, removals or replacement. + +preRemove +~~~~~~~~~ + +The ``preRemove`` event is called on every entity when its passed +to the ``EntityManager#remove()`` method. It is cascaded for all +associations that are marked as cascade delete. + +There are no restrictions to what methods can be called inside the +``preRemove`` event, except when the remove method itself was +called during a flush operation. + +preFlush +~~~~~~~~ + +``preFlush`` is called at ``EntityManager#flush()`` before +anything else. ``EntityManager#flush()`` can be called safely +inside its listeners. + +.. code-block:: php + + getEntityManager(); + $uow = $em->getUnitOfWork(); + + foreach ($uow->getScheduledEntityInsertions() as $entity) { + + } + + foreach ($uow->getScheduledEntityUpdates() as $entity) { + + } + + foreach ($uow->getScheduledEntityDeletions() as $entity) { + + } + + foreach ($uow->getScheduledCollectionDeletions() as $col) { + + } + + foreach ($uow->getScheduledCollectionUpdates() as $col) { + + } + } + } + +The following restrictions apply to the onFlush event: + + +- If you create and persist a new entity in ``onFlush``, then + calling ``EntityManager#persist()`` is not enough. + You have to execute an additional call to + ``$unitOfWork->computeChangeSet($classMetadata, $entity)``. +- Changing primitive fields or associations requires you to + explicitly trigger a re-computation of the changeset of the + affected entity. This can be done by calling + ``$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)``. + +postFlush +~~~~~~~~~ + +``postFlush`` is called at the end of ``EntityManager#flush()``. +``EntityManager#flush()`` can **NOT** be called safely inside its listeners. + +.. code-block:: php + + getEntity() instanceof User) { + if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') { + $eventArgs->setNewValue('name', 'Bob'); + } + } + } + } + +You could also use this listener to implement validation of all the +fields that have changed. This is more efficient than using a +lifecycle callback when there are expensive validations to call: + +.. code-block:: php + + getEntity() instanceof Account) { + if ($eventArgs->hasChangedField('creditCard')) { + $this->validateCreditCard($eventArgs->getNewValue('creditCard')); + } + } + } + + private function validateCreditCard($no) + { + // throw an exception to interrupt flush event. Transaction will be rolled back. + } + } + +Restrictions for this event: + + +- Changes to associations of the passed entities are not + recognized by the flush operation anymore. +- Changes to fields of the passed entities are not recognized by + the flush operation anymore, use the computed change-set passed to + the event to modify primitive field values, e.g. use + ``$eventArgs->setNewValue($field, $value);`` as in the Alice to Bob example above. +- Any calls to ``EntityManager#persist()`` or + ``EntityManager#remove()``, even in combination with the UnitOfWork + API are strongly discouraged and don't work as expected outside the + flush operation. + +postUpdate, postRemove, postPersist +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The three post events are called inside ``EntityManager#flush()``. +Changes in here are not relevant to the persistence in the +database, but you can use these events to alter non-persistable items, +like non-mapped fields, logging or even associated classes that are +directly mapped by Doctrine. + +postLoad +~~~~~~~~ + +This event is called after an entity is constructed by the +EntityManager. + +Entity listeners +---------------- + +.. versionadded:: 2.4 + +An entity listener is a lifecycle listener class used for an entity. + +- The entity listener's mapping may be applied to an entity class or mapped superclass. +- An entity listener is defined by mapping the entity class with the corresponding mapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + .. code-block:: yaml + + MyProject\Entity\User: + type: entity + entityListeners: + UserListener: + # .... + +.. _reference-entity-listeners: + +Entity listeners class +~~~~~~~~~~~~~~~~~~~~~~ + +An ``Entity Listener`` could be any class, by default it should be a class with a no-arg constructor. + +- Different from :ref:`reference-events-implementing-listeners` an ``Entity Listener`` is invoked just to the specified entity +- An entity listener method receives two arguments, the entity instance and the lifecycle event. +- The callback method can be defined by naming convention or specifying a method mapping. +- When a listener mapping is not given the parser will use the naming convention to look for a matching method, + e.g. it will look for a public ``preUpdate()`` method if you are listening to the ``preUpdate`` event. +- When a listener mapping is given the parser will not look for any methods using the naming convention. + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Entity\User: + type: entity + entityListeners: + UserListener: + preFlush: [preFlushHandler] + postLoad: [postLoadHandler] + + postPersist: [postPersistHandler] + prePersist: [prePersistHandler] + + postUpdate: [postUpdateHandler] + preUpdate: [preUpdateHandler] + + postRemove: [postRemoveHandler] + preRemove: [preRemoveHandler] + # .... + + + +Entity listeners resolver +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Doctrine invokes the listener resolver to get the listener instance. + +- A resolver allows you register a specific entity listener instance. +- You can also implement your own resolver by extending ``Doctrine\ORM\Mapping\DefaultEntityListenerResolver`` or implementing ``Doctrine\ORM\Mapping\EntityListenerResolver`` + +Specifying an entity listener instance : + +.. code-block:: php + + service = $service; + } + + public function preUpdate(User $user, PreUpdateEventArgs $event) + { + $this->service->doSomething($user); + } + } + + // register a entity listener. + $listener = $container->get('user_listener'); + $em->getConfiguration()->getEntityListenerResolver()->register($listener); + +Implementing your own resolver : + +.. code-block:: php + + container = $container; + } + + public function resolve($className) + { + // resolve the service id by the given class name; + $id = 'user_listener'; + + return $this->container->get($id); + } + } + + // Configure the listener resolver only before instantiating the EntityManager + $configurations->setEntityListenerResolver(new MyEntityListenerResolver); + EntityManager::create(.., $configurations, ..); + +Load ClassMetadata Event +------------------------ + +When the mapping information for an entity is read, it is populated +in to a ``ClassMetadataInfo`` instance. You can hook in to this +process and manipulate the instance. + +.. code-block:: php + + getMetadataFactory(); + $evm = $em->getEventManager(); + $evm->addEventListener(Events::loadClassMetadata, $test); + + class TestEvent + { + public function loadClassMetadata(\Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs) + { + $classMetadata = $eventArgs->getClassMetadata(); + $fieldMapping = array( + 'fieldName' => 'about', + 'type' => 'string', + 'length' => 255 + ); + $classMetadata->mapField($fieldMapping); + } + } + + diff --git a/vendor/doctrine/orm/docs/en/reference/faq.rst b/vendor/doctrine/orm/docs/en/reference/faq.rst new file mode 100644 index 0000000..45fde18 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/faq.rst @@ -0,0 +1,224 @@ +Frequently Asked Questions +========================== + +.. note:: + + This FAQ is a work in progress. We will add lots of questions and not answer them right away just to remember + what is often asked. If you stumble across an unanswered question please write a mail to the mailing-list or + join the #doctrine channel on Freenode IRC. + +Database Schema +--------------- + +How do I set the charset and collation for MySQL tables? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can't set these values inside the annotations, yml or xml mapping files. To make a database +work with the default charset and collation you should configure MySQL to use it as default charset, +or create the database with charset and collation details. This way they get inherited to all newly +created database tables and columns. + +Entity Classes +-------------- + +I access a variable and its null, what is wrong? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If this variable is a public variable then you are violating one of the criteria for entities. +All properties have to be protected or private for the proxy object pattern to work. + +How can I add default values to a column? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine does not support to set the default values in columns through the "DEFAULT" keyword in SQL. +This is not necessary however, you can just use your class properties as default values. These are then used +upon insert: + +.. code-block:: php + + class User + { + const STATUS_DISABLED = 0; + const STATUS_ENABLED = 1; + + private $algorithm = "sha1"; + private $status = self:STATUS_DISABLED; + } + +. + +Mapping +------- + +Why do I get exceptions about unique constraint failures during ``$em->flush()``? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine does not check if you are re-adding entities with a primary key that already exists +or adding entities to a collection twice. You have to check for both conditions yourself +in the code before calling ``$em->flush()`` if you know that unique constraint failures +can occur. + +In `Symfony2 `_ for example there is a Unique Entity Validator +to achieve this task. + +For collections you can check with ``$collection->contains($entity)`` if an entity is already +part of this collection. For a FETCH=LAZY collection this will initialize the collection, +however for FETCH=EXTRA_LAZY this method will use SQL to determine if this entity is already +part of the collection. + +Associations +------------ + +What is wrong when I get an InvalidArgumentException "A new entity was found through the relationship.."? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This exception is thrown during ``EntityManager#flush()`` when there exists an object in the identity map +that contains a reference to an object that Doctrine does not know about. Say for example you grab +a "User"-entity from the database with a specific id and set a completely new object into one of the associations +of the User object. If you then call ``EntityManager#flush()`` without letting Doctrine know about +this new object using ``EntityManager#persist($newObject)`` you will see this exception. + +You can solve this exception by: + +* Calling ``EntityManager#persist($newObject)`` on the new object +* Using cascade=persist on the association that contains the new object + +How can I filter an association? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Natively you can't filter associations in 2.0 and 2.1. You should use DQL queries to query for the filtered set of entities. + +I call clear() on a One-To-Many collection but the entities are not deleted +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is an expected behavior that has to do with the inverse/owning side handling of Doctrine. +By definition a One-To-Many association is on the inverse side, that means changes to it +will not be recognized by Doctrine. + +If you want to perform the equivalent of the clear operation you have to iterate the +collection and set the owning side many-to-one reference to NULL as well to detach all entities +from the collection. This will trigger the appropriate UPDATE statements on the database. + +How can I add columns to a many-to-many table? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The many-to-many association is only supporting foreign keys in the table definition +To work with many-to-many tables containing extra columns you have to use the +foreign keys as primary keys feature of Doctrine introduced in version 2.1. + +See :doc:`the tutorial on composite primary keys for more information<../tutorials/composite-primary-keys>`. + + +How can i paginate fetch-joined collections? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are issuing a DQL statement that fetches a collection as well you cannot easily iterate +over this collection using a LIMIT statement (or vendor equivalent). + +Doctrine does not offer a solution for this out of the box but there are several extensions +that do: + +* `DoctrineExtensions `_ +* `Pagerfanta `_ + +Why does pagination not work correctly with fetch joins? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pagination in Doctrine uses a LIMIT clause (or vendor equivalent) to restrict the results. +However when fetch-joining this is not returning the correct number of results since joining +with a one-to-many or many-to-many association multiplies the number of rows by the number +of associated entities. + +See the previous question for a solution to this task. + +Inheritance +----------- + +Can I use Inheritance with Doctrine 2? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2. + +See the documentation chapter on :doc:`inheritance mapping ` for +the details. + +Why does Doctrine not create proxy objects for my inheritance hierarchy? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you set a many-to-one or one-to-one association target-entity to any parent class of +an inheritance hierarchy Doctrine does not know what PHP class the foreign is actually of. +To find this out it has to execute a SQL query to look this information up in the database. + +EntityGenerator +--------------- + +Why does the EntityGenerator not do X? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation +is not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator +is supposed to kick-start you, but not towards 100%. + +Why does the EntityGenerator not generate inheritance correctly? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierarchy. +This is why the generation of inherited entities does not fully work. You have to adjust some additional +code to get this one working correctly. + +Performance +----------- + +Why is an extra SQL query executed every time I fetch an entity with a one-to-one relation? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If Doctrine detects that you are fetching an inverse side one-to-one association +it has to execute an additional query to load this object, because it cannot know +if there is no such object (setting null) or if it should set a proxy and which id this proxy has. + +To solve this problem currently a query has to be executed to find out this information. + +Doctrine Query Language +----------------------- + +What is DQL? +~~~~~~~~~~~~ + +DQL stands for Doctrine Query Language, a query language that very much looks like SQL +but has some important benefits when using Doctrine: + +- It uses class names and fields instead of tables and columns, separating concerns between backend and your object model. +- It utilizes the metadata defined to offer a range of shortcuts when writing. For example you do not have to specify the ON clause of joins, since Doctrine already knows about them. +- It adds some functionality that is related to object management and transforms them into SQL. + +It also has some drawbacks of course: + +- The syntax is slightly different to SQL so you have to learn and remember the differences. +- To be vendor independent it can only implement a subset of all the existing SQL dialects. Vendor specific functionality and optimizations cannot be used through DQL unless implemented by you explicitly. +- For some DQL constructs subselects are used which are known to be slow in MySQL. + +Can I sort by a function (for example ORDER BY RAND()) in DQL? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No, it is not supported to sort by function in DQL. If you need this functionality you should either +use a native-query or come up with another solution. As a side note: Sorting with ORDER BY RAND() is painfully slow +starting with 1000 rows. + +A Query fails, how can I debug it? +---------------------------------- + +First, if you are using the QueryBuilder you can use +``$queryBuilder->getDQL()`` to get the DQL string of this query. The +corresponding SQL you can get from the Query instance by calling +``$query->getSQL()``. + +.. code-block:: php + + createQuery($dql); + var_dump($query->getSQL()); + + $qb = $entityManager->createQueryBuilder(); + $qb->select('u')->from('User', 'u'); + var_dump($qb->getDQL()); diff --git a/vendor/doctrine/orm/docs/en/reference/filters.rst b/vendor/doctrine/orm/docs/en/reference/filters.rst new file mode 100644 index 0000000..a5c0ee4 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/filters.rst @@ -0,0 +1,93 @@ +Filters +======= + +.. versionadded:: 2.2 + +Doctrine 2.2 features a filter system that allows the developer to add SQL to +the conditional clauses of queries, regardless the place where the SQL is +generated (e.g. from a DQL query, or by loading associated entities). + +The filter functionality works on SQL level. Whether a SQL query is generated +in a Persister, during lazy loading, in extra lazy collections or from DQL. +Each time the system iterates over all the enabled filters, adding a new SQL +part as a filter returns. + +By adding SQL to the conditional clauses of queries, the filter system filters +out rows belonging to the entities at the level of the SQL result set. This +means that the filtered entities are never hydrated (which can be expensive). + + +Example filter class +-------------------- +Throughout this document the example ``MyLocaleFilter`` class will be used to +illustrate how the filter feature works. A filter class must extend the base +``Doctrine\ORM\Query\Filter\SQLFilter`` class and implement the ``addFilterConstraint`` +method. The method receives the ``ClassMetadata`` of the filtered entity and the +table alias of the SQL table of the entity. + +.. note:: + + In the case of joined or single table inheritance, you always get passed the ClassMetadata of the + inheritance root. This is necessary to avoid edge cases that would break the SQL when applying the filters. + +Parameters for the query should be set on the filter object by +``SQLFilter#setParameter()``. Only parameters set via this function can be used +in filters. The ``SQLFilter#getParameter()`` function takes care of the +proper quoting of parameters. + +.. code-block:: php + + reflClass->implementsInterface('LocaleAware')) { + return ""; + } + + return $targetTableAlias.'.locale = ' . $this->getParameter('locale'); // getParameter applies quoting automatically + } + } + + +Configuration +------------- +Filter classes are added to the configuration as following: + +.. code-block:: php + + addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter"); + + +The ``Configuration#addFilter()`` method takes a name for the filter and the name of the +class responsible for the actual filtering. + + +Disabling/Enabling Filters and Setting Parameters +--------------------------------------------------- +Filters can be disabled and enabled via the ``FilterCollection`` which is +stored in the ``EntityManager``. The ``FilterCollection#enable($name)`` method +will retrieve the filter object. You can set the filter parameters on that +object. + +.. code-block:: php + + getFilters()->enable("locale"); + $filter->setParameter('locale', 'en'); + + // Disable it + $filter = $em->getFilters()->disable("locale"); + +.. warning:: + Disabling and enabling filters has no effect on managed entities. If you + want to refresh or reload an object after having modified a filter or the + FilterCollection, then you should clear the EntityManager and re-fetch your + entities, having the new rules for filtering applied. diff --git a/vendor/doctrine/orm/docs/en/reference/improving-performance.rst b/vendor/doctrine/orm/docs/en/reference/improving-performance.rst new file mode 100644 index 0000000..d9de6c8 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/improving-performance.rst @@ -0,0 +1,68 @@ +Improving Performance +===================== + +Bytecode Cache +-------------- + +It is highly recommended to make use of a bytecode cache like APC. +A bytecode cache removes the need for parsing PHP code on every +request and can greatly improve performance. + + "If you care about performance and don't use a bytecode + cache then you don't really care about performance. Please get one + and start using it." + + *Stas Malyshev, Core Contributor to PHP and Zend Employee* + + +Metadata and Query caches +------------------------- + +As already mentioned earlier in the chapter about configuring +Doctrine, it is strongly discouraged to use Doctrine without a +Metadata and Query cache (preferably with APC or Memcache as the +cache driver). Operating Doctrine without these caches means +Doctrine will need to load your mapping information on every single +request and has to parse each DQL query on every single request. +This is a waste of resources. + +Alternative Query Result Formats +-------------------------------- + +Make effective use of the available alternative query result +formats like nested array graphs or pure scalar results, especially +in scenarios where data is loaded for read-only purposes. + +Read-Only Entities +------------------ + +Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping +references for details). This means that the entity marked as read only is never considered +for updates, which means when you call flush on the EntityManager these entities are skipped +even if properties changed. Read-Only allows to persist new entities of a kind and remove existing +ones, they are just not considered for updates. + +Extra-Lazy Collections +---------------------- + +If entities hold references to large collections you will get performance and memory problems initializing them. +To solve this issue you can use the EXTRA_LAZY fetch-mode feature for collections. See the :doc:`tutorial <../tutorials/extra-lazy-associations>` +for more information on how this fetch mode works. + +Temporarily change fetch mode in DQL +------------------------------------ + +See :ref:`Doctrine Query Language chapter ` + + +Apply Best Practices +-------------------- + +A lot of the points mentioned in the Best Practices chapter will +also positively affect the performance of Doctrine. + + +Change Tracking policies +------------------------ + +See: :doc:`Change Tracking Policies ` diff --git a/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst b/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst new file mode 100644 index 0000000..9a0d3c2 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/inheritance-mapping.rst @@ -0,0 +1,605 @@ +Inheritance Mapping +=================== + +Mapped Superclasses +------------------- + +A mapped superclass is an abstract or concrete class that provides +persistent entity state and mapping information for its subclasses, +but which is not itself an entity. Typically, the purpose of such a +mapped superclass is to define state and mapping information that +is common to multiple entity classes. + +Mapped superclasses, just as regular, non-mapped classes, can +appear in the middle of an otherwise mapped inheritance hierarchy +(through Single Table Inheritance or Class Table Inheritance). + +.. note:: + + A mapped superclass cannot be an entity, it is not query-able and + persistent relationships defined by a mapped superclass must be + unidirectional (with an owning side only). This means that One-To-Many + associations are not possible on a mapped superclass at all. + Furthermore Many-To-Many associations are only possible if the + mapped superclass is only used in exactly one entity at the moment. + For further support of inheritance, the single or + joined table inheritance features have to be used. + + +Example: + +.. code-block:: php + + `_ +is an inheritance mapping strategy where all classes of a hierarchy +are mapped to a single database table. In order to distinguish +which row represents which type in the hierarchy a so-called +discriminator column is used. + +Example: + +.. configuration-block:: + + .. code-block:: php + + `_ +is an inheritance mapping strategy where each class in a hierarchy +is mapped to several tables: its own table and the tables of all +parent classes. The table of a child class is linked to the table +of a parent class through a foreign key constraint. Doctrine 2 +implements this strategy through the use of a discriminator column +in the topmost table of the hierarchy because this is the easiest +way to achieve polymorphic queries with Class Table Inheritance. + +Example: + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # user mapping + MyProject\Model\User: + type: mappedSuperclass + # other fields mapping + manyToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + cascade: [ persist, merge ] + manyToMany: + groups: + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + cascade: [ persist, merge, detach ] + + # admin mapping + MyProject\Model\Admin: + type: entity + associationOverride: + address: + joinColumn: + adminaddress_id: + name: adminaddress_id + referencedColumnName: id + groups: + joinTable: + name: users_admingroups + joinColumns: + adminuser_id: + referencedColumnName: id + inverseJoinColumns: + admingroup_id: + referencedColumnName: id + + +Things to note: + +- The "association override" specifies the overrides base on the property name. +- This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany) +- The association type *CANNOT* be changed. +- The override could redefine the joinTables or joinColumns depending on the association type. + +Attribute Override +~~~~~~~~~~~~~~~~~~~~ +Override the mapping of a field. + +Could be used by an entity that extends a mapped superclass to override a field mapping defined by the mapped superclass. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # user mapping + MyProject\Model\User: + type: mappedSuperclass + id: + id: + type: integer + column: user_id + length: 150 + generator: + strategy: AUTO + fields: + name: + type: string + column: user_name + length: 250 + nullable: true + unique: false + #other fields mapping + + + # guest mapping + MyProject\Model\Guest: + type: entity + attributeOverride: + id: + column: guest_id + type: integer + length: 140 + name: + column: guest_name + type: string + length: 240 + nullable: false + unique: true + +Things to note: + +- The "attribute override" specifies the overrides base on the property name. +- The column type *CANNOT* be changed. If the column type is not equal you get a ``MappingException`` +- The override can redefine all the columns except the type. + +Query the Type +-------------- + +It may happen that the entities of a special type should be queried. Because there +is no direct access to the discriminator column, Doctrine provides the +``INSTANCE OF`` construct. + +The following example shows how to use ``INSTANCE OF``. There is a three level hierarchy +with a base entity ``NaturalPerson`` which is extended by ``Staff`` which in turn +is extended by ``Technician``. + +Querying for the staffs without getting any technicians can be achieved by this DQL: + +.. code-block:: php + + createQuery("SELECT staff FROM MyProject\Model\Staff staff WHERE staff NOT INSTANCE OF MyProject\Model\Technician"); + $staffs = $query->getResult(); diff --git a/vendor/doctrine/orm/docs/en/reference/installation.rst b/vendor/doctrine/orm/docs/en/reference/installation.rst new file mode 100644 index 0000000..8b732ad --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/installation.rst @@ -0,0 +1,5 @@ +Installation +============ + +The installation chapter has moved to `Installation and Configuration +`_. diff --git a/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst b/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst new file mode 100644 index 0000000..49976f1 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/limitations-and-known-issues.rst @@ -0,0 +1,184 @@ +Limitations and Known Issues +============================ + +We try to make using Doctrine2 a very pleasant experience. +Therefore we think it is very important to be honest about the +current limitations to our users. Much like every other piece of +software Doctrine2 is not perfect and far from feature complete. +This section should give you an overview of current limitations of +Doctrine 2 as well as critical known issues that you should know +about. + +Current Limitations +------------------- + +There is a set of limitations that exist currently which might be +solved in the future. Any of this limitations now stated has at +least one ticket in the Tracker and is discussed for future +releases. + +Join-Columns with non-primary keys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to use join columns pointing to non-primary keys. Doctrine will think these are the primary +keys and create lazy-loading proxies with the data, which can lead to unexpected results. Doctrine can for performance +reasons not validate the correctness of this settings at runtime but only through the Validate Schema command. + +Mapping Arrays to a Join Table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Related to the previous limitation with "Foreign Keys as +Identifier" you might be interested in mapping the same table +structure as given above to an array. However this is not yet +possible either. See the following example: + +.. code-block:: sql + + CREATE TABLE product ( + id INTEGER, + name VARCHAR, + PRIMARY KEY(id) + ); + + CREATE TABLE product_attributes ( + product_id INTEGER, + attribute_name VARCHAR, + attribute_value VARCHAR, + PRIMARY KEY (product_id, attribute_name) + ); + +This schema should be mapped to a Product Entity as follows: + +.. code-block:: php + + class Product + { + private $id; + private $name; + private $attributes = array(); + } + +Where the ``attribute_name`` column contains the key and +``attribute_value`` contains the value of each array element in +``$attributes``. + +The feature request for persistence of primitive value arrays +`is described in the DDC-298 ticket `_. + +Cascade Merge with Bi-directional Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two bugs now that concern the use of cascade merge in combination with bi-directional associations. +Make sure to study the behavior of cascade merge if you are using it: + +- `DDC-875 `_ Merge can sometimes add the same entity twice into a collection +- `DDC-763 `_ Cascade merge on associated entities can insert too many rows through "Persistence by Reachability" + +Custom Persisters +~~~~~~~~~~~~~~~~~ + +A Persister in Doctrine is an object that is responsible for the +hydration and write operations of an entity against the database. +Currently there is no way to overwrite the persister implementation +for a given entity, however there are several use-cases that can +benefit from custom persister implementations: + + +- `Add Upsert Support `_ +- `Evaluate possible ways in which stored-procedures can be used `_ +- The previous Filter Rules Feature Request + +Persist Keys of Collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PHP Arrays are ordered hash-maps and so should be the +``Doctrine\Common\Collections\Collection`` interface. We plan to +evaluate a feature that optionally persists and hydrates the keys +of a Collection instance. + +`Ticket DDC-213 `_ + +Mapping many tables to one entity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is not possible to map several equally looking tables onto one +entity. For example if you have a production and an archive table +of a certain business concept then you cannot have both tables map +to the same entity. + +Behaviors +~~~~~~~~~ + +Doctrine 2 will **never** include a behavior system like Doctrine 1 +in the core library. We don't think behaviors add more value than +they cost pain and debugging hell. Please see the many different +blog posts we have written on this topics: + +- `Doctrine2 "Behaviors" in a Nutshell `_ +- `A re-usable Versionable behavior for Doctrine2 `_ +- `Write your own ORM on top of Doctrine2 `_ +- `Doctrine 2 Behavioral Extensions `_ +- `Doctrator _ + +Doctrine 2 has enough hooks and extension points so that **you** can +add whatever you want on top of it. None of this will ever become +core functionality of Doctrine2 however, you will have to rely on +third party extensions for magical behaviors. + +Nested Set +~~~~~~~~~~ + +NestedSet was offered as a behavior in Doctrine 1 and will not be +included in the core of Doctrine 2. However there are already two +extensions out there that offer support for Nested Set with +Doctrine 2: + + +- `Doctrine2 Hierarchical-Structural Behavior `_ +- `Doctrine2 NestedSet `_ + +Known Issues +------------ + +The Known Issues section describes critical/blocker bugs and other +issues that are either complicated to fix, not fixable due to +backwards compatibility issues or where no simple fix exists (yet). +We don't plan to add every bug in the tracker there, just those +issues that can potentially cause nightmares or pain of any sort. + +See the Open Bugs on Jira for more details on `bugs, improvement and feature +requests +`_. + +Identifier Quoting and Legacy Databases +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For compatibility reasons between all the supported vendors and +edge case problems Doctrine 2 does **NOT** do automatic identifier +quoting. This can lead to problems when trying to get +legacy-databases to work with Doctrine 2. + + +- You can quote column-names as described in the + :doc:`Basic-Mapping ` section. +- You cannot quote join column names. +- You cannot use non [a-zA-Z0-9\_]+ characters, they will break + several SQL statements. + +Having problems with these kind of column names? Many databases +support all CRUD operations on views that semantically map to +certain tables. You can create views for all your problematic +tables and column names to avoid the legacy quoting nightmare. + +Microsoft SQL Server and Doctrine "datetime" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine assumes that you use ``DateTime2`` data-types. If your legacy database contains DateTime +datatypes then you have to add your own data-type (see Basic Mapping for an example). + +MySQL with MyISAM tables +~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine cannot provide atomic operations when calling ``EntityManager#flush()`` if one +of the tables involved uses the storage engine MyISAM. You must use InnoDB or +other storage engines that support transactions if you need integrity. diff --git a/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst b/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst new file mode 100644 index 0000000..6b9cb31 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/metadata-drivers.rst @@ -0,0 +1,194 @@ +Metadata Drivers +================ + +The heart of an object relational mapper is the mapping information +that glues everything together. It instructs the EntityManager how +it should behave when dealing with the different entities. + +Core Metadata Drivers +--------------------- + +Doctrine provides a few different ways for you to specify your +metadata: + + +- **XML files** (XmlDriver) +- **Class DocBlock Annotations** (AnnotationDriver) +- **YAML files** (YamlDriver) +- **PHP Code in files or static functions** (PhpDriver) + +Something important to note about the above drivers is they are all +an intermediate step to the same end result. The mapping +information is populated to ``Doctrine\ORM\Mapping\ClassMetadata`` +instances. So in the end, Doctrine only ever has to work with the +API of the ``ClassMetadata`` class to get mapping information for +an entity. + +.. note:: + + The populated ``ClassMetadata`` instances are also cached + so in a production environment the parsing and populating only ever + happens once. You can configure the metadata cache implementation + using the ``setMetadataCacheImpl()`` method on the + ``Doctrine\ORM\Configuration`` class: + + .. code-block:: php + + getConfiguration()->setMetadataCacheImpl(new ApcCache()); + + +If you want to use one of the included core metadata drivers you +just need to configure it. All the drivers are in the +``Doctrine\ORM\Mapping\Driver`` namespace: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Implementing Metadata Drivers +----------------------------- + +In addition to the included metadata drivers you can very easily +implement your own. All you need to do is define a class which +implements the ``Driver`` interface: + +.. code-block:: php + + _loadMappingFile($file); + + // populate ClassMetadataInfo instance from $data + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + // parse contents of $file and return php data structure + } + } + +.. note:: + + When using the ``AbstractFileDriver`` it requires that you + only have one entity defined per file and the file named after the + class described inside where namespace separators are replaced by + periods. So if you have an entity named ``Entities\User`` and you + wanted to write a mapping file for your driver above you would need + to name the file ``Entities.User.dcm.ext`` for it to be + recognized. + + +Now you can use your ``MyMetadataDriver`` implementation by setting +it with the ``setMetadataDriverImpl()`` method: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +ClassMetadata +------------- + +The last piece you need to know and understand about metadata in +Doctrine 2 is the API of the ``ClassMetadata`` classes. You need to +be familiar with them in order to implement your own drivers but +more importantly to retrieve mapping information for a certain +entity when needed. + +You have all the methods you need to manually specify the mapping +information instead of using some mapping file to populate it from. +The base ``ClassMetadataInfo`` class is responsible for only data +storage and is not meant for runtime use. It does not require that +the class actually exists yet so it is useful for describing some +entity before it exists and using that information to generate for +example the entities themselves. The class ``ClassMetadata`` +extends ``ClassMetadataInfo`` and adds some functionality required +for runtime usage and requires that the PHP class is present and +can be autoloaded. + +You can read more about the API of the ``ClassMetadata`` classes in +the PHP Mapping chapter. + +Getting ClassMetadata Instances +------------------------------- + +If you want to get the ``ClassMetadata`` instance for an entity in +your project to programmatically use some mapping information to +generate some HTML or something similar you can retrieve it through +the ``ClassMetadataFactory``: + +.. code-block:: php + + getMetadataFactory(); + $class = $cmf->getMetadataFor('MyEntityName'); + +Now you can learn about the entity and use the data stored in the +``ClassMetadata`` instance to get all mapped fields for example and +iterate over them: + +.. code-block:: php + + fieldMappings as $fieldMapping) { + echo $fieldMapping['fieldName'] . "\n"; + } + + diff --git a/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst b/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst new file mode 100644 index 0000000..9d82d8d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/namingstrategy.rst @@ -0,0 +1,150 @@ +Implementing a NamingStrategy +============================== + +.. versionadded:: 2.3 + +Using a naming strategy you can provide rules for automatically generating +database identifiers, columns and tables names +when the table/column name is not given. +This feature helps reduce the verbosity of the mapping document, +eliminating repetitive noise (eg: ``TABLE_``). + + +Configuring a naming strategy +----------------------------- +The default strategy used by Doctrine is quite minimal. + +By default the ``Doctrine\ORM\Mapping\DefaultNamingStrategy`` +uses the simple class name and the attributes names to generate tables and columns + +You can specify a different strategy by calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` : + +.. code-block:: php + + setNamingStrategy($namingStrategy); + +Underscore naming strategy +--------------------------- + +``\Doctrine\ORM\Mapping\UnderscoreNamingStrategy`` is a built-in strategy +that might be a useful if you want to use a underlying convention. + +.. code-block:: php + + setNamingStrategy($namingStrategy); + +Then SomeEntityName will generate the table SOME_ENTITY_NAME when CASE_UPPER +or some_entity_name using CASE_LOWER is given. + + +Naming strategy interface +------------------------- +The interface ``Doctrine\ORM\Mapping\NamingStrategy`` allows you to specify +a "naming standard" for database tables and columns. + +.. code-block:: php + + referenceColumnName(); + } + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return strtolower($this->classToTableName($sourceEntity) . '_' . + $this->classToTableName($targetEntity)); + } + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return strtolower($this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName())); + } + } + +Configuring the namingstrategy is easy if. +Just set your naming strategy calling ``Doctrine\ORM\Configuration#setNamingStrategy()`` :. + +.. code-block:: php + + setNamingStrategy($namingStrategy); diff --git a/vendor/doctrine/orm/docs/en/reference/native-sql.rst b/vendor/doctrine/orm/docs/en/reference/native-sql.rst new file mode 100644 index 0000000..75231b7 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/native-sql.rst @@ -0,0 +1,905 @@ +Native SQL +========== + +With ``NativeQuery`` you can execute native SELECT SQL statements +and map the results to Doctrine entities or any other result format +supported by Doctrine. + +In order to make this mapping possible, you need to describe +to Doctrine what columns in the result map to which entity property. +This description is represented by a ``ResultSetMapping`` object. + +With this feature you can map arbitrary SQL code to objects, such as highly +vendor-optimized SQL or stored-procedures. + +Writing ``ResultSetMapping`` from scratch is complex, but there is a convenience +wrapper around it called a ``ResultSetMappingBuilder``. It can generate +the mappings for you based on Entities and even generates the ``SELECT`` +clause based on this information for you. + +.. note:: + + If you want to execute DELETE, UPDATE or INSERT statements + the Native SQL API cannot be used and will probably throw errors. + Use ``EntityManager#getConnection()`` to access the native database + connection and call the ``executeUpdate()`` method for these + queries. + +The NativeQuery class +--------------------- + +To create a ``NativeQuery`` you use the method +``EntityManager#createNativeQuery($sql, $resultSetMapping)``. As you can see in +the signature of this method, it expects 2 ingredients: The SQL you want to +execute and the ``ResultSetMapping`` that describes how the results will be +mapped. + +Once you obtained an instance of a ``NativeQuery``, you can bind parameters to +it with the same API that ``Query`` has and execute it. + +.. code-block:: php + + createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +ResultSetMappingBuilder +----------------------- + +An easy start into ResultSet mapping is the ``ResultSetMappingBuilder`` object. +This has several benefits: + +- The builder takes care of automatically updating your ``ResultSetMapping`` + when the fields or associations change on the metadata of an entity. +- You can generate the required ``SELECT`` expression for a builder + by converting it to a string. +- The API is much simpler than the usual ``ResultSetMapping`` API. + +One downside is that the builder API does not yet support entities +with inheritance hierachies. + +.. code-block:: php + + addRootEntityFromClassMetadata('MyProject\User', 'u'); + $rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', 'u', 'address', array('id' => 'address_id')); + +The builder extends the ``ResultSetMapping`` class and as such has all the functionality of it as well. + +.. versionadded:: 2.4 + +Starting with Doctrine ORM 2.4 you can generate the ``SELECT`` clause +from a ``ResultSetMappingBuilder``. You can either cast the builder +object to ``(string)`` and the DQL aliases are used as SQL table aliases +or use the ``generateSelectClause($tableAliases)`` method and pass +a mapping from DQL alias (key) to SQL alias (value) + +.. code-block:: php + + generateSelectClause(array( + 'u' => 't1', + 'g' => 't2' + )); + $sql = "SELECT " . $selectClause . " FROM users t1 JOIN groups t2 ON t1.group_id = t2.id"; + + +The ResultSetMapping +-------------------- + +Understanding the ``ResultSetMapping`` is the key to using a +``NativeQuery``. A Doctrine result can contain the following +components: + + +- Entity results. These represent root result elements. +- Joined entity results. These represent joined entities in + associations of root entity results. +- Field results. These represent a column in the result set that + maps to a field of an entity. A field result always belongs to an + entity result or joined entity result. +- Scalar results. These represent scalar values in the result set + that will appear in each result row. Adding scalar results to a + ResultSetMapping can also cause the overall result to become + **mixed** (see DQL - Doctrine Query Language) if the same + ResultSetMapping also contains entity results. +- Meta results. These represent columns that contain + meta-information, such as foreign keys and discriminator columns. + When querying for objects (``getResult()``), all meta columns of + root entities or joined entities must be present in the SQL query + and mapped accordingly using ``ResultSetMapping#addMetaResult``. + +.. note:: + + It might not surprise you that Doctrine uses + ``ResultSetMapping`` internally when you create DQL queries. As + the query gets parsed and transformed to SQL, Doctrine fills a + ``ResultSetMapping`` that describes how the results should be + processed by the hydration routines. + + +We will now look at each of the result types that can appear in a +ResultSetMapping in detail. + +Entity results +~~~~~~~~~~~~~~ + +An entity result describes an entity type that appears as a root +element in the transformed result. You add an entity result through +``ResultSetMapping#addEntityResult()``. Let's take a look at the +method signature in detail: + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + + $query = $this->_em->createNativeQuery('SELECT id, name FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +The result would look like this: + +.. code-block:: php + + array( + [0] => User (Object) + ) + +Note that this would be a partial object if the entity has more +fields than just id and name. In the example above the column and +field names are identical but that is not necessary, of course. +Also note that the query string passed to createNativeQuery is +**real native SQL**. Doctrine does not touch this SQL in any way. + +In the previous basic example, a User had no relations and the +table the class is mapped to owns no foreign keys. The next example +assumes User has a unidirectional or bidirectional one-to-one +association to a CmsAddress, where the User is the owning side and +thus owns the foreign key. + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addMetaResult('u', 'address_id', 'address_id'); + + $query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +Foreign keys are used by Doctrine for lazy-loading purposes when +querying for objects. In the previous example, each user object in +the result will have a proxy (a "ghost") in place of the address +that contains the address\_id. When the ghost proxy is accessed, it +loads itself based on this key. + +Consequently, associations that are *fetch-joined* do not require +the foreign keys to be present in the SQL result set, only +associations that are lazy. + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addJoinedEntityResult('Address' , 'a', 'u', 'address'); + $rsm->addFieldResult('a', 'address_id', 'id'); + $rsm->addFieldResult('a', 'street', 'street'); + $rsm->addFieldResult('a', 'city', 'city'); + + $sql = 'SELECT u.id, u.name, a.id AS address_id, a.street, a.city FROM users u ' . + 'INNER JOIN address a ON u.address_id = a.id WHERE u.name = ?'; + $query = $this->_em->createNativeQuery($sql, $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +In this case the nested entity ``Address`` is registered with the +``ResultSetMapping#addJoinedEntityResult`` method, which notifies +Doctrine that this entity is not hydrated at the root level, but as +a joined entity somewhere inside the object graph. In this case we +specify the alias 'u' as third parameter and ``address`` as fourth +parameter, which means the ``Address`` is hydrated into the +``User::$address`` property. + +If a fetched entity is part of a mapped hierarchy that requires a +discriminator column, this column must be present in the result set +as a meta column so that Doctrine can create the appropriate +concrete type. This is shown in the following example where we +assume that there are one or more subclasses that extend User and +either Class Table Inheritance or Single Table Inheritance is used +to map the hierarchy (both use a discriminator column). + +.. code-block:: php + + addEntityResult('User', 'u'); + $rsm->addFieldResult('u', 'id', 'id'); + $rsm->addFieldResult('u', 'name', 'name'); + $rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column + $rsm->setDiscriminatorColumn('u', 'discr'); + + $query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + +Note that in the case of Class Table Inheritance, an example as +above would result in partial objects if any objects in the result +are actually a subtype of User. When using DQL, Doctrine +automatically includes the necessary joins for this mapping +strategy but with native SQL it is your responsibility. + +Named Native Query +------------------ + +You can also map a native query using a named native query mapping. + +To achieve that, you must describe the SQL resultset structure +using named native query (and sql resultset mappings if is a several resultset mappings). + +Like named query, a named native query can be defined at class level or in a XML or YAML file. + + +A resultSetMapping parameter is defined in @NamedNativeQuery, +it represents the name of a defined @SqlResultSetMapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\User: + type: entity + namedNativeQueries: + fetchMultipleJoinsEntityResults: + name: fetchMultipleJoinsEntityResults + resultSetMapping: mappingMultipleJoinsEntityResults + query: SELECT u.id AS u_id, u.name AS u_name, u.status AS u_status, a.id AS a_id, a.zip AS a_zip, a.country AS a_country, COUNT(p.phonenumber) AS numphones FROM users u INNER JOIN addresses a ON u.id = a.user_id INNER JOIN phonenumbers p ON u.id = p.user_id GROUP BY u.id, u.name, u.status, u.username, a.id, a.zip, a.country ORDER BY u.username + sqlResultSetMappings: + mappingMultipleJoinsEntityResults: + name: mappingMultipleJoinsEntityResults + columnResult: + 0: + name: numphones + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + column: u_id + 1: + name: name + column: u_name + 2: + name: status + column: u_status + 1: + entityClass: Address + fieldResult: + 0: + name: id + column: a_id + 1: + name: zip + column: a_zip + 2: + name: country + column: a_country + + +Things to note: + - The resultset mapping declares the entities retrieved by this native query. + - Each field of the entity is bound to a SQL alias (or column name). + - All fields of the entity including the ones of subclasses + and the foreign key columns of related entities have to be present in the SQL query. + - Field definitions are optional provided that they map to the same + column name as the one declared on the class property. + - ``__CLASS__`` is an alias for the mapped class + + +In the above example, +the ``fetchJoinedAddress`` named query use the joinMapping result set mapping. +This mapping returns 2 entities, User and Address, each property is declared and associated to a column name, +actually the column name retrieved by the query. + +Let's now see an implicit declaration of the property / column. + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT * FROM addresses + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + findAll: + resultSetMapping: mappingFindAll + query: SELECT * FROM addresses + sqlResultSetMappings: + mappingFindAll: + name: mappingFindAll + entityResult: + address: + entityClass: Address + + +In this example, we only describe the entity member of the result set mapping. +The property / column mappings is done using the entity mapping values. +In this case the model property is bound to the model_txt column. +If the association to a related entity involve a composite primary key, +a @FieldResult element should be used for each foreign key column. +The @FieldResult name is composed of the property name for the relationship, +followed by a dot ("."), followed by the name or the field or property of the primary key. + + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + MyProject\Model\User: + type: entity + namedNativeQueries: + fetchJoinedAddress: + name: fetchJoinedAddress + resultSetMapping: mappingJoinedAddress + query: SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM users u INNER JOIN addresses a ON u.id = a.user_id WHERE u.username = ? + sqlResultSetMappings: + mappingJoinedAddress: + entityResult: + 0: + entityClass: __CLASS__ + fieldResult: + 0: + name: id + 1: + name: name + 2: + name: status + 3: + name: address.id + column: a_id + 4: + name: address.zip + column: a_zip + 5: + name: address.city + column: a_city + 6: + name: address.country + column: a_country + + + +If you retrieve a single entity and if you use the default mapping, +you can use the resultClass attribute instead of resultSetMapping: + +.. configuration-block:: + + .. code-block:: php + + + + + + SELECT * FROM addresses WHERE id = ? + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + findAll: + name: findAll + resultClass: Address + query: SELECT * FROM addresses + + +In some of your native queries, you'll have to return scalar values, +for example when building report queries. +You can map them in the @SqlResultsetMapping through @ColumnResult. +You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though). + +.. configuration-block:: + + .. code-block:: php + + + + + SELECT COUNT(*) AS count FROM addresses + + + + + + + + + .. code-block:: yaml + + MyProject\Model\Address: + type: entity + namedNativeQueries: + count: + name: count + resultSetMapping: mappingCount + query: SELECT COUNT(*) AS count FROM addresses + sqlResultSetMappings: + mappingCount: + name: mappingCount + columnResult: + count: + name: count diff --git a/vendor/doctrine/orm/docs/en/reference/partial-objects.rst b/vendor/doctrine/orm/docs/en/reference/partial-objects.rst new file mode 100644 index 0000000..396eecc --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/partial-objects.rst @@ -0,0 +1,90 @@ +Partial Objects +=============== + +A partial object is an object whose state is not fully initialized +after being reconstituted from the database and that is +disconnected from the rest of its data. The following section will +describe why partial objects are problematic and what the approach +of Doctrine2 to this problem is. + +.. note:: + + The partial object problem in general does not apply to + methods or queries where you do not retrieve the query result as + objects. Examples are: ``Query#getArrayResult()``, + ``Query#getScalarResult()``, ``Query#getSingleScalarResult()``, + etc. + +.. warning:: + + Use of partial objects is tricky. Fields that are not retrieved + from the database will not be updated by the UnitOfWork even if they + get changed in your objects. You can only promote a partial object + to a fully-loaded object by calling ``EntityManager#refresh()`` + or a DQL query with the refresh flag. + + +What is the problem? +-------------------- + +In short, partial objects are problematic because they are usually +objects with broken invariants. As such, code that uses these +partial objects tends to be very fragile and either needs to "know" +which fields or methods can be safely accessed or add checks around +every field access or method invocation. The same holds true for +the internals, i.e. the method implementations, of such objects. +You usually simply assume the state you need in the method is +available, after all you properly constructed this object before +you pushed it into the database, right? These blind assumptions can +quickly lead to null reference errors when working with such +partial objects. + +It gets worse with the scenario of an optional association (0..1 to +1). When the associated field is NULL, you don't know whether this +object does not have an associated object or whether it was simply +not loaded when the owning object was loaded from the database. + +These are reasons why many ORMs do not allow partial objects at all +and instead you always have to load an object with all its fields +(associations being proxied). One secure way to allow partial +objects is if the programming language/platform allows the ORM tool +to hook deeply into the object and instrument it in such a way that +individual fields (not only associations) can be loaded lazily on +first access. This is possible in Java, for example, through +bytecode instrumentation. In PHP though this is not possible, so +there is no way to have "secure" partial objects in an ORM with +transparent persistence. + +Doctrine, by default, does not allow partial objects. That means, +any query that only selects partial object data and wants to +retrieve the result as objects (i.e. ``Query#getResult()``) will +raise an exception telling you that partial objects are dangerous. +If you want to force a query to return you partial objects, +possibly as a performance tweak, you can use the ``partial`` +keyword as follows: + +.. code-block:: php + + createQuery("select partial u.{id,name} from MyApp\Domain\User u"); + +You can also get a partial reference instead of a proxy reference by +calling: + +.. code-block:: php + + getPartialReference('MyApp\Domain\User', 1); + +Partial references are objects with only the identifiers set as they +are passed to the second argument of the ``getPartialReference()`` method. +All other fields are null. + +When should I force partial objects? +------------------------------------ + +Mainly for optimization purposes, but be careful of premature +optimization as partial objects lead to potentially more fragile +code. + + diff --git a/vendor/doctrine/orm/docs/en/reference/php-mapping.rst b/vendor/doctrine/orm/docs/en/reference/php-mapping.rst new file mode 100644 index 0000000..78a7214 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/php-mapping.rst @@ -0,0 +1,325 @@ +PHP Mapping +=========== + +Doctrine 2 also allows you to provide the ORM metadata in the form +of plain PHP code using the ``ClassMetadata`` API. You can write +the code in PHP files or inside of a static function named +``loadMetadata($class)`` on the entity class itself. + +PHP Files +--------- + +If you wish to write your mapping information inside PHP files that +are named after the entity and included to populate the metadata +for an entity you can do so by using the ``PHPDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Now imagine we had an entity named ``Entities\User`` and we wanted +to write a mapping file for it using the above configured +``PHPDriver`` instance: + +.. code-block:: php + + mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string', + 'options' => array( + 'fixed' => true, + 'comment' => "User's login name" + ) + )); + + $metadata->mapField(array( + 'fieldName' => 'login_count', + 'type' => 'integer', + 'nullable' => false, + 'options' => array( + 'unsigned' => true, + 'default' => 0 + ) + )); + +Now we can easily retrieve the populated ``ClassMetadata`` instance +where the ``PHPDriver`` includes the file and the +``ClassMetadataFactory`` caches it for later retrieval: + +.. code-block:: php + + getClassMetadata('Entities\User'); + // or + $class = $em->getMetadataFactory()->getMetadataFor('Entities\User'); + +Static Function +--------------- + +In addition to the PHP files you can also specify your mapping +information inside of a static function defined on the entity class +itself. This is useful for cases where you want to keep your entity +and mapping information together but don't want to use annotations. +For this you just need to use the ``StaticPHPDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl($driver); + +Now you just need to define a static function named +``loadMetadata($metadata)`` on your entity: + +.. code-block:: php + + mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string' + )); + } + } + +ClassMetadataBuilder +-------------------- + +To ease the use of the ClassMetadata API (which is very raw) there is a ``ClassMetadataBuilder`` that you can use. + +.. code-block:: php + + createField('id', 'integer')->isPrimaryKey()->generatedValue()->build(); + $builder->addField('username', 'string'); + } + } + +The API of the ClassMetadataBuilder has the following methods with a fluent interface: + +- ``addField($name, $type, array $mapping)`` +- ``setMappedSuperclass()`` +- ``setReadOnly()`` +- ``setCustomRepositoryClass($className)`` +- ``setTable($name)`` +- ``addIndex(array $columns, $indexName)`` +- ``addUniqueConstraint(array $columns, $constraintName)`` +- ``addNamedQuery($name, $dqlQuery)`` +- ``setJoinedTableInheritance()`` +- ``setSingleTableInheritance()`` +- ``setDiscriminatorColumn($name, $type = 'string', $length = 255)`` +- ``addDiscriminatorMapClass($name, $class)`` +- ``setChangeTrackingPolicyDeferredExplicit()`` +- ``setChangeTrackingPolicyNotify()`` +- ``addLifecycleEvent($methodName, $event)`` +- ``addManyToOne($name, $targetEntity, $inversedBy = null)`` +- ``addInverseOneToOne($name, $targetEntity, $mappedBy)`` +- ``addOwningOneToOne($name, $targetEntity, $inversedBy = null)`` +- ``addOwningManyToMany($name, $targetEntity, $inversedBy = null)`` +- ``addInverseManyToMany($name, $targetEntity, $mappedBy)`` +- ``addOneToMany($name, $targetEntity, $mappedBy)`` + +It also has several methods that create builders (which are necessary for advanced mappings): + +- ``createField($name, $type)`` returns a ``FieldBuilder`` instance +- ``createManyToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance +- ``createOneToOne($name, $targetEntity)`` returns an ``AssociationBuilder`` instance +- ``createManyToMany($name, $targetEntity)`` returns an ``ManyToManyAssociationBuilder`` instance +- ``createOneToMany($name, $targetEntity)`` returns an ``OneToManyAssociationBuilder`` instance + +ClassMetadataInfo API +--------------------- + +The ``ClassMetadataInfo`` class is the base data object for storing +the mapping metadata for a single entity. It contains all the +getters and setters you need populate and retrieve information for +an entity. + +General Setters +~~~~~~~~~~~~~~~ + + +- ``setTableName($tableName)`` +- ``setPrimaryTable(array $primaryTableDefinition)`` +- ``setCustomRepositoryClass($repositoryClassName)`` +- ``setIdGeneratorType($generatorType)`` +- ``setIdGenerator($generator)`` +- ``setSequenceGeneratorDefinition(array $definition)`` +- ``setChangeTrackingPolicy($policy)`` +- ``setIdentifier(array $identifier)`` + +Inheritance Setters +~~~~~~~~~~~~~~~~~~~ + + +- ``setInheritanceType($type)`` +- ``setSubclasses(array $subclasses)`` +- ``setParentClasses(array $classNames)`` +- ``setDiscriminatorColumn($columnDef)`` +- ``setDiscriminatorMap(array $map)`` + +Field Mapping Setters +~~~~~~~~~~~~~~~~~~~~~ + + +- ``mapField(array $mapping)`` +- ``mapOneToOne(array $mapping)`` +- ``mapOneToMany(array $mapping)`` +- ``mapManyToOne(array $mapping)`` +- ``mapManyToMany(array $mapping)`` + +Lifecycle Callback Setters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``addLifecycleCallback($callback, $event)`` +- ``setLifecycleCallbacks(array $callbacks)`` + +Versioning Setters +~~~~~~~~~~~~~~~~~~ + + +- ``setVersionMapping(array &$mapping)`` +- ``setVersioned($bool)`` +- ``setVersionField()`` + +General Getters +~~~~~~~~~~~~~~~ + + +- ``getTableName()`` +- ``getSchemaName()`` +- ``getTemporaryIdTableName()`` + +Identifier Getters +~~~~~~~~~~~~~~~~~~ + + +- ``getIdentifierColumnNames()`` +- ``usesIdGenerator()`` +- ``isIdentifier($fieldName)`` +- ``isIdGeneratorIdentity()`` +- ``isIdGeneratorSequence()`` +- ``isIdGeneratorTable()`` +- ``isIdentifierNatural()`` +- ``getIdentifierFieldNames()`` +- ``getSingleIdentifierFieldName()`` +- ``getSingleIdentifierColumnName()`` + +Inheritance Getters +~~~~~~~~~~~~~~~~~~~ + + +- ``isInheritanceTypeNone()`` +- ``isInheritanceTypeJoined()`` +- ``isInheritanceTypeSingleTable()`` +- ``isInheritanceTypeTablePerClass()`` +- ``isInheritedField($fieldName)`` +- ``isInheritedAssociation($fieldName)`` + +Change Tracking Getters +~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``isChangeTrackingDeferredExplicit()`` +- ``isChangeTrackingDeferredImplicit()`` +- ``isChangeTrackingNotify()`` + +Field & Association Getters +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``isUniqueField($fieldName)`` +- ``isNullable($fieldName)`` +- ``getColumnName($fieldName)`` +- ``getFieldMapping($fieldName)`` +- ``getAssociationMapping($fieldName)`` +- ``getAssociationMappings()`` +- ``getFieldName($columnName)`` +- ``hasField($fieldName)`` +- ``getColumnNames(array $fieldNames = null)`` +- ``getTypeOfField($fieldName)`` +- ``getTypeOfColumn($columnName)`` +- ``hasAssociation($fieldName)`` +- ``isSingleValuedAssociation($fieldName)`` +- ``isCollectionValuedAssociation($fieldName)`` + +Lifecycle Callback Getters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +- ``hasLifecycleCallbacks($lifecycleEvent)`` +- ``getLifecycleCallbacks($event)`` + +ClassMetadata API +----------------- + +The ``ClassMetadata`` class extends ``ClassMetadataInfo`` and adds +the runtime functionality required by Doctrine. It adds a few extra +methods related to runtime reflection for working with the entities +themselves. + + +- ``getReflectionClass()`` +- ``getReflectionProperties()`` +- ``getReflectionProperty($name)`` +- ``getSingleIdReflectionProperty()`` +- ``getIdentifierValues($entity)`` +- ``setIdentifierValues($entity, $id)`` +- ``setFieldValue($entity, $field, $value)`` +- ``getFieldValue($entity, $field)`` + + diff --git a/vendor/doctrine/orm/docs/en/reference/query-builder.rst b/vendor/doctrine/orm/docs/en/reference/query-builder.rst new file mode 100644 index 0000000..57eee17 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/query-builder.rst @@ -0,0 +1,565 @@ +The QueryBuilder +================ + +A ``QueryBuilder`` provides an API that is designed for +conditionally constructing a DQL query in several steps. + +It provides a set of classes and methods that is able to +programmatically build queries, and also provides a fluent API. +This means that you can change between one methodology to the other +as you want, and also pick one if you prefer. + +Constructing a new QueryBuilder object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The same way you build a normal Query, you build a ``QueryBuilder`` +object, just providing the correct method name. Here is an example +how to build a ``QueryBuilder`` object: + +.. code-block:: php + + createQueryBuilder(); + +Once you have created an instance of QueryBuilder, it provides a +set of useful informative functions that you can use. One good +example is to inspect what type of object the ``QueryBuilder`` is. + +.. code-block:: php + + getType(); // Prints: 0 + +There're currently 3 possible return values for ``getType()``: + + +- ``QueryBuilder::SELECT``, which returns value 0 +- ``QueryBuilder::DELETE``, returning value 1 +- ``QueryBuilder::UPDATE``, which returns value 2 + +It is possible to retrieve the associated ``EntityManager`` of the +current ``QueryBuilder``, its DQL and also a ``Query`` object when +you finish building your DQL. + +.. code-block:: php + + getEntityManager(); + + // example4: retrieve the DQL string of what was defined in QueryBuilder + $dql = $qb->getDql(); + + // example5: retrieve the associated Query object with the processed DQL + $q = $qb->getQuery(); + +Internally, ``QueryBuilder`` works with a DQL cache to increase +performance. Any changes that may affect the generated DQL actually +modifies the state of ``QueryBuilder`` to a stage we call +STATE\_DIRTY. One ``QueryBuilder`` can be in two different states: + + +- ``QueryBuilder::STATE_CLEAN``, which means DQL haven't been + altered since last retrieval or nothing were added since its + instantiation +- ``QueryBuilder::STATE_DIRTY``, means DQL query must (and will) + be processed on next retrieval + +Working with QueryBuilder +~~~~~~~~~~~~~~~~~~~~~~~~~ + + +High level API methods +^^^^^^^^^^^^^^^^^^^^^^ + +To simplify even more the way you build a query in Doctrine, we can take +advantage of what we call Helper methods. For all base code, there +is a set of useful methods to simplify a programmer's life. To +illustrate how to work with them, here is the same example 6 +re-written using ``QueryBuilder`` helper methods: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = ?1') + ->orderBy('u.name', 'ASC'); + +``QueryBuilder`` helper methods are considered the standard way to +build DQL queries. Although it is supported, it should be avoided +to use string based queries and greatly encouraged to use +``$qb->expr()->*`` methods. Here is a converted example 8 to +suggested standard way to build queries: + +.. code-block:: php + + select(array('u')) // string 'u' is converted to array internally + ->from('User', 'u') + ->where($qb->expr()->orX( + $qb->expr()->eq('u.id', '?1'), + $qb->expr()->like('u.nickname', '?2') + )) + ->orderBy('u.surname', 'ASC')); + +Here is a complete list of helper methods available in ``QueryBuilder``: + +.. code-block:: php + + select('u') + // Example - $qb->select(array('u', 'p')) + // Example - $qb->select($qb->expr()->select('u', 'p')) + public function select($select = null); + + // addSelect does not override previous calls to select + // + // Example - $qb->select('u'); + // ->addSelect('p.area_code'); + public function addSelect($select = null); + + // Example - $qb->delete('User', 'u') + public function delete($delete = null, $alias = null); + + // Example - $qb->update('Group', 'g') + public function update($update = null, $alias = null); + + // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold')) + // Example - $qb->set('u.numChilds', 'u.numChilds + ?1') + // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1')) + public function set($key, $value); + + // Example - $qb->from('Phonenumber', 'p') + // Example - $qb->from('Phonenumber', 'p', 'p.id') + public function from($from, $alias, $indexBy = null); + + // Example - $qb->join('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1')) + // Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1') + // Example - $qb->join('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id') + public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null); + + // Example - $qb->innerJoin('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1')) + // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1') + // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id') + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null); + + // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55)) + // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55') + // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55', 'p.id') + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null); + + // NOTE: ->where() overrides all previously set conditions + // + // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2')) + // Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2'))) + // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2') + public function where($where); + + // NOTE: ->andWhere() can be used directly, without any ->where() before + // + // Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0')) + public function andWhere($where); + + // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10)); + public function orWhere($where); + + // NOTE: -> groupBy() overrides all previously set grouping conditions + // + // Example - $qb->groupBy('u.id') + public function groupBy($groupBy); + + // Example - $qb->addGroupBy('g.name') + public function addGroupBy($groupBy); + + // NOTE: -> having() overrides all previously set having conditions + // + // Example - $qb->having('u.salary >= ?1') + // Example - $qb->having($qb->expr()->gte('u.salary', '?1')) + public function having($having); + + // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0)) + public function andHaving($having); + + // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100')) + public function orHaving($having); + + // NOTE: -> orderBy() overrides all previously set ordering conditions + // + // Example - $qb->orderBy('u.surname', 'DESC') + public function orderBy($sort, $order = null); + + // Example - $qb->addOrderBy('u.firstName') + public function addOrderBy($sort, $order = null); // Default $order = 'ASC' + } + +Binding parameters to your query +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Doctrine supports dynamic binding of parameters to your query, +similar to preparing queries. You can use both strings and numbers +as placeholders, although both have a slightly different syntax. +Additionally, you must make your choice: Mixing both styles is not +allowed. Binding parameters can simply be achieved as follows: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = ?1') + ->orderBy('u.name', 'ASC') + ->setParameter(1, 100); // Sets ?1 to 100, and thus we will fetch a user with u.id = 100 + +You are not forced to enumerate your placeholders as the +alternative syntax is available: + +.. code-block:: php + + select('u') + ->from('User', 'u') + ->where('u.id = :identifier') + ->orderBy('u.name', 'ASC') + ->setParameter('identifier', 100); // Sets :identifier to 100, and thus we will fetch a user with u.id = 100 + +Note that numeric placeholders start with a ? followed by a number +while the named placeholders start with a : followed by a string. + +Calling ``setParameter()`` automatically infers which type you are setting as +value. This works for integers, arrays of strings/integers, DateTime instances +and for managed entities. If you want to set a type explicitly you can call +the third argument to ``setParameter()`` explicitly. It accepts either a PDO +type or a DBAL Type name for conversion. + +If you've got several parameters to bind to your query, you can +also use setParameters() instead of setParameter() with the +following syntax: + +.. code-block:: php + + setParameters(array(1 => 'value for ?1', 2 => 'value for ?2')); + +Getting already bound parameters is easy - simply use the above +mentioned syntax with "getParameter()" or "getParameters()": + +.. code-block:: php + + getParameters(); + // $params instanceof \Doctrine\Common\Collections\ArrayCollection + + // Equivalent to + $param = $qb->getParameter(1); + // $param instanceof \Doctrine\ORM\Query\Parameter + +Note: If you try to get a parameter that was not bound yet, +getParameter() simply returns NULL. + +The API of a Query Parameter is: + +.. code-block:: php + + namespace Doctrine\ORM\Query; + + class Parameter + { + public function getName(); + public function getValue(); + public function getType(); + public function setValue($value, $type = null); + } + +Limiting the Result +^^^^^^^^^^^^^^^^^^^ + +To limit a result the query builder has some methods in common with +the Query object which can be retrieved from ``EntityManager#createQuery()``. + +.. code-block:: php + + add('select', 'u') + ->add('from', 'User u') + ->add('orderBy', 'u.name ASC') + ->setFirstResult( $offset ) + ->setMaxResults( $limit ); + +Executing a Query +^^^^^^^^^^^^^^^^^ + +The QueryBuilder is a builder object only, it has no means of actually +executing the Query. Additionally a set of parameters such as query hints +cannot be set on the QueryBuilder itself. This is why you always have to convert +a querybuilder instance into a Query object: + +.. code-block:: php + + getQuery(); + + // Set additional Query options + $query->setQueryHint('foo', 'bar'); + $query->useResultCache('my_cache_id'); + + // Execute Query + $result = $query->getResult(); + $single = $query->getSingleResult(); + $array = $query->getArrayResult(); + $scalar = $query->getScalarResult(); + $singleScalar = $query->getSingleScalarResult(); + +The Expr class +^^^^^^^^^^^^^^ + +To workaround some of the issues that ``add()`` method may cause, +Doctrine created a class that can be considered as a helper for +building expressions. This class is called ``Expr``, which provides a +set of useful methods to help build expressions: + +.. code-block:: php + + add('select', new Expr\Select(array('u'))) + ->add('from', new Expr\From('User', 'u')) + ->add('where', $qb->expr()->orX( + $qb->expr()->eq('u.id', '?1'), + $qb->expr()->like('u.nickname', '?2') + )) + ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Although it still sounds complex, the ability to programmatically +create conditions are the main feature of ``Expr``. Here it is a +complete list of supported helper methods available: + +.. code-block:: php + + expr()->andX($cond1 [, $condN])->add(...)->... + public function andX($x = null); // Returns Expr\AndX instance + + // Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->... + public function orX($x = null); // Returns Expr\OrX instance + + + /** Comparison objects **/ + + // Example - $qb->expr()->eq('u.id', '?1') => u.id = ?1 + public function eq($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->neq('u.id', '?1') => u.id <> ?1 + public function neq($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->lt('u.id', '?1') => u.id < ?1 + public function lt($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->lte('u.id', '?1') => u.id <= ?1 + public function lte($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->gt('u.id', '?1') => u.id > ?1 + public function gt($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->gte('u.id', '?1') => u.id >= ?1 + public function gte($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->isNull('u.id') => u.id IS NULL + public function isNull($x); // Returns string + + // Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL + public function isNotNull($x); // Returns string + + + /** Arithmetic objects **/ + + // Example - $qb->expr()->prod('u.id', '2') => u.id * 2 + public function prod($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->diff('u.id', '2') => u.id - 2 + public function diff($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->sum('u.id', '2') => u.id + 2 + public function sum($x, $y); // Returns Expr\Math instance + + // Example - $qb->expr()->quot('u.id', '2') => u.id / 2 + public function quot($x, $y); // Returns Expr\Math instance + + + /** Pseudo-function objects **/ + + // Example - $qb->expr()->exists($qb2->getDql()) + public function exists($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->all($qb2->getDql()) + public function all($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->some($qb2->getDql()) + public function some($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->any($qb2->getDql()) + public function any($subquery); // Returns Expr\Func instance + + // Example - $qb->expr()->not($qb->expr()->eq('u.id', '?1')) + public function not($restriction); // Returns Expr\Func instance + + // Example - $qb->expr()->in('u.id', array(1, 2, 3)) + // Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception. + // Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above) + public function in($x, $y); // Returns Expr\Func instance + + // Example - $qb->expr()->notIn('u.id', '2') + public function notIn($x, $y); // Returns Expr\Func instance + + // Example - $qb->expr()->like('u.firstname', $qb->expr()->literal('Gui%')) + public function like($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->notLike('u.firstname', $qb->expr()->literal('Gui%')) + public function notLike($x, $y); // Returns Expr\Comparison instance + + // Example - $qb->expr()->between('u.id', '1', '10') + public function between($val, $x, $y); // Returns Expr\Func + + + /** Function objects **/ + + // Example - $qb->expr()->trim('u.firstname') + public function trim($x); // Returns Expr\Func + + // Example - $qb->expr()->concat('u.firstname', $qb->expr()->concat($qb->expr()->literal(' '), 'u.lastname')) + public function concat($x, $y); // Returns Expr\Func + + // Example - $qb->expr()->substring('u.firstname', 0, 1) + public function substring($x, $from, $len); // Returns Expr\Func + + // Example - $qb->expr()->lower('u.firstname') + public function lower($x); // Returns Expr\Func + + // Example - $qb->expr()->upper('u.firstname') + public function upper($x); // Returns Expr\Func + + // Example - $qb->expr()->length('u.firstname') + public function length($x); // Returns Expr\Func + + // Example - $qb->expr()->avg('u.age') + public function avg($x); // Returns Expr\Func + + // Example - $qb->expr()->max('u.age') + public function max($x); // Returns Expr\Func + + // Example - $qb->expr()->min('u.age') + public function min($x); // Returns Expr\Func + + // Example - $qb->expr()->abs('u.currentBalance') + public function abs($x); // Returns Expr\Func + + // Example - $qb->expr()->sqrt('u.currentBalance') + public function sqrt($x); // Returns Expr\Func + + // Example - $qb->expr()->count('u.firstname') + public function count($x); // Returns Expr\Func + + // Example - $qb->expr()->countDistinct('u.surname') + public function countDistinct($x); // Returns Expr\Func + } + + +Low Level API +^^^^^^^^^^^^^ + +Now we have describe the low level (thought of as the +hardcore method) of creating queries. It may be useful to work at +this level for optimization purposes, but most of the time it is +preferred to work at a higher level of abstraction. + +All helper methods in ``QueryBuilder`` actually rely on a single +one: ``add()``. This method is responsible of building every piece +of DQL. It takes 3 parameters: ``$dqlPartName``, ``$dqlPart`` and +``$append`` (default=false) + + +- ``$dqlPartName``: Where the ``$dqlPart`` should be placed. + Possible values: select, from, where, groupBy, having, orderBy +- ``$dqlPart``: What should be placed in ``$dqlPartName``. Accepts + a string or any instance of ``Doctrine\ORM\Query\Expr\*`` +- ``$append``: Optional flag (default=false) if the ``$dqlPart`` + should override all previously defined items in ``$dqlPartName`` or + not (no effect on the ``where`` and ``having`` DQL query parts, + which always override all previously defined items) + +- + +.. code-block:: php + + add('select', 'u') + ->add('from', 'User u') + ->add('where', 'u.id = ?1') + ->add('orderBy', 'u.name ASC'); + +Expr\* classes +^^^^^^^^^^^^^^ + +When you call ``add()`` with string, it internally evaluates to an +instance of ``Doctrine\ORM\Query\Expr\Expr\*`` class. Here is the +same query of example 6 written using +``Doctrine\ORM\Query\Expr\Expr\*`` classes: + +.. code-block:: php + + add('select', new Expr\Select(array('u'))) + ->add('from', new Expr\From('User', 'u')) + ->add('where', new Expr\Comparison('u.id', '=', '?1')) + ->add('orderBy', new Expr\OrderBy('u.name', 'ASC')); + +Of course this is the hardest way to build a DQL query in Doctrine. +To simplify some of these efforts, we introduce what we call as +``Expr`` helper class. + diff --git a/vendor/doctrine/orm/docs/en/reference/second-level-cache.rst b/vendor/doctrine/orm/docs/en/reference/second-level-cache.rst new file mode 100644 index 0000000..f69314d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/second-level-cache.rst @@ -0,0 +1,731 @@ +The Second Level Cache +====================== + +.. note:: + + The second level cache functionality is marked as experimental for now. It + is a very complex feature and we cannot guarantee yet that it works stable + in all cases. + +The Second Level Cache is designed to reduce the amount of necessary database access. +It sits between your application and the database to avoid the number of database hits as much as possible. + +When turned on, entities will be first searched in cache and if they are not found, +a database query will be fired an then the entity result will be stored in a cache provider. + +There are some flavors of caching available, but is better to cache read-only data. + +Be aware that caches are not aware of changes made to the persistent store by another application. +They can, however, be configured to regularly expire cached data. + + +Caching Regions +--------------- + +Second level cache does not store instances of an entity, instead it caches only entity identifier and values. +Each entity class, collection association and query has its region, where values of each instance are stored. + +Caching Regions are specific region into the cache provider that might store entities, collection or queries. +Each cache region resides in a specific cache namespace and has its own lifetime configuration. + +Notice that when caching collection and queries only identifiers are stored. +The entity values will be stored in its own region + +Something like below for an entity region : + +.. code-block:: php + + ['id'=> 1, 'name' => 'FooBar', 'associationName'=>null], + 'region_name:entity_2_hash' => ['id'=> 2, 'name' => 'Foo', 'associationName'=>['id'=>11]], + 'region_name:entity_3_hash' => ['id'=> 3, 'name' => 'Bar', 'associationName'=>['id'=>22]] + ]; + + +If the entity holds a collection that also needs to be cached. +An collection region could look something like : + +.. code-block:: php + + ['ownerId'=> 1, 'list' => [1, 2, 3]], + 'region_name:entity_2_coll_assoc_name_hash' => ['ownerId'=> 2, 'list' => [2, 3]], + 'region_name:entity_3_coll_assoc_name_hash' => ['ownerId'=> 3, 'list' => [2, 4]] + ]; + +A query region might be something like : + +.. code-block:: php + + ['list' => [1, 2, 3]], + 'region_name:query_2_hash' => ['list' => [2, 3]], + 'region_name:query_3_hash' => ['list' => [2, 4]] + ]; + + +.. note:: + + The following data structures represents now the cache will looks like, this is not actual cached data. + + +.. _reference-second-level-cache-regions: + +Cache Regions +------------- + +``Doctrine\ORM\Cache\Region\DefaultRegion`` It's the default implementation. + A simplest cache region compatible with all doctrine-cache drivers but does not support locking. + +``Doctrine\ORM\Cache\Region`` and ``Doctrine\ORM\Cache\ConcurrentRegion`` +Defines contracts that should be implemented by a cache provider. + +It allows you to provide your own cache implementation that might take advantage of specific cache driver. + +If you want to support locking for ``READ_WRITE`` strategies you should implement ``ConcurrentRegion``; ``CacheRegion`` otherwise. + + +Cache region +~~~~~~~~~~~~ + +Defines a contract for accessing a particular region. + +``Doctrine\ORM\Cache\Region`` + +Defines a contract for accessing a particular cache region. + +`See API Doc `_. + +Concurrent cache region +~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Doctrine\ORM\Cache\ConcurrentRegion`` is designed to store concurrently managed data region. +By default, Doctrine provides a very simple implementation based on file locks ``Doctrine\ORM\Cache\Region\FileLockRegion``. + +If you want to use an ``READ_WRITE`` cache, you should consider providing your own cache region. + +``Doctrine\ORM\Cache\ConcurrentRegion`` + +Defines contract for concurrently managed data region. + +`See API Doc `_. + +Timestamp region +~~~~~~~~~~~~~~~~ + +``Doctrine\ORM\Cache\TimestampRegion`` + +Tracks the timestamps of the most recent updates to particular entity. + +`See API Doc `_. + +.. _reference-second-level-cache-mode: + +Caching mode +------------ + +* ``READ_ONLY`` (DEFAULT) + + * Can do reads, inserts and deletes, cannot perform updates or employ any locks. + * Useful for data that is read frequently but never updated. + * Best performer. + * It is Simple. + +* ``NONSTRICT_READ_WRITE`` + + * Read Write Cache doesn’t employ any locks but can do reads, inserts, updates and deletes. + * Good if the application needs to update data rarely. + + +* ``READ_WRITE`` + + * Read Write cache employs locks before update/delete. + * Use if data needs to be updated. + * Slowest strategy. + * To use it a the cache region implementation must support locking. + + +Built-in cached persisters +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Cached persisters are responsible to access cache regions. + + +-----------------------+-------------------------------------------------------------------------------+ + | Cache Usage | Persister | + +=======================+===============================================================================+ + | READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\ReadOnlyCachedEntityPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\ReadWriteCachedEntityPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\NonStrictReadWriteCachedEntityPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | READ_ONLY | Doctrine\\ORM\\Cache\\Persister\\ReadOnlyCachedCollectionPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\ReadWriteCachedCollectionPersister | + +-----------------------+-------------------------------------------------------------------------------+ + | NONSTRICT_READ_WRITE | Doctrine\\ORM\\Cache\\Persister\\NonStrictReadWriteCacheCollectionPersister | + +-----------------------+-------------------------------------------------------------------------------+ + +Configuration +------------- +Doctrine allows you to specify configurations and some points of extension for the second-level-cache + + +Enable Second Level Cache +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To enable the second-level-cache, you should provide a cache factory +``\Doctrine\ORM\Cache\DefaultCacheFactory`` is the default implementation. + +.. code-block:: php + + setSecondLevelCacheEnabled(); + + // Cache factory + $config->getSecondLevelCacheConfiguration() + ->setCacheFactory($factory); + + +Cache Factory +~~~~~~~~~~~~~ + +Cache Factory is the main point of extension. + +It allows you to provide a specific implementation of the following components : + +* ``QueryCache`` Store and retrieve query cache results. +* ``CachedEntityPersister`` Store and retrieve entity results. +* ``CachedCollectionPersister`` Store and retrieve query results. +* ``EntityHydrator`` Transform an entity into a cache entry and cache entry into entities +* ``CollectionHydrator`` Transform a collection into a cache entry and cache entry into collection + +`See API Doc `_. + +Region Lifetime +~~~~~~~~~~~~~~~ + +To specify a default lifetime for all regions or specify a different lifetime for a specific region. + +.. code-block:: php + + getSecondLevelCacheConfiguration(); + $regionConfig = $cacheConfig->getRegionsConfiguration(); + + // Cache Region lifetime + $regionConfig->setLifetime('my_entity_region', 3600); // Time to live for a specific region; In seconds + $regionConfig->setDefaultLifetime(7200); // Default time to live; In seconds + + +Cache Log +~~~~~~~~~ +By providing a cache logger you should be able to get information about all cache operations such as hits, misses and puts. + +``\Doctrine\ORM\Cache\Logging\StatisticsCacheLogger`` is a built-in implementation that provides basic statistics. + + .. code-block:: php + + setSecondLevelCacheEnabled(true); + $config->getSecondLevelCacheConfiguration() + ->setCacheLogger($logger); + + + // Collect cache statistics + + // Get the number of entries successfully retrieved from a specific region. + $logger->getRegionHitCount('my_entity_region'); + + // Get the number of cached entries *not* found in a specific region. + $logger->getRegionMissCount('my_entity_region'); + + // Get the number of cacheable entries put in cache. + $logger->getRegionPutCount('my_entity_region'); + + // Get the total number of put in all regions. + $logger->getPutCount(); + + // Get the total number of entries successfully retrieved from all regions. + $logger->getHitCount(); + + // Get the total number of cached entries *not* found in all regions. + $logger->getMissCount(); + +If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``. +and collect all information you want. + +`See API Doc `_. + + +Entity cache definition +----------------------- +* Entity cache configuration allows you to define the caching strategy and region for an entity. + + * ``usage`` Specifies the caching strategy: ``READ_ONLY``, ``NONSTRICT_READ_WRITE``, ``READ_WRITE``. see :ref:`reference-second-level-cache-mode` + * ``region`` Optional value that specifies the name of the second level cache region. + + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + .. code-block:: yaml + + Country: + type: entity + cache: + usage : READ_ONLY + region : my_entity_region + id: + id: + type: integer + id: true + generator: + strategy: IDENTITY + fields: + name: + type: string + + +Association cache definition +---------------------------- +The most common use case is to cache entities. But we can also cache relationships. +It caches the primary keys of association and cache each element will be cached into its region. + + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + State: + type: entity + cache: + usage : NONSTRICT_READ_WRITE + id: + id: + type: integer + id: true + generator: + strategy: IDENTITY + fields: + name: + type: string + + manyToOne: + state: + targetEntity: Country + joinColumns: + country_id: + referencedColumnName: id + cache: + usage : NONSTRICT_READ_WRITE + + oneToMany: + cities: + targetEntity:City + mappedBy: state + cache: + usage : NONSTRICT_READ_WRITE + + +> Note: for this to work, the target entity must also be marked as cacheable. + +Cache usage +~~~~~~~~~~~ + +Basic entity cache + +.. code-block:: php + + persist(new Country($name)); + $em->flush(); // Hit database to insert the row and put into cache + + $em->clear(); // Clear entity manager + + $country1 = $em->find('Country', 1); // Retrieve item from cache + + $country->setName("New Name"); + $em->persist($country); + $em->flush(); // Hit database to update the row and update cache + + $em->clear(); // Clear entity manager + + $country2 = $em->find('Country', 1); // Retrieve item from cache + // Notice that $country1 and $country2 are not the same instance. + + +Association cache + +.. code-block:: php + + persist(new State($name, $country)); + $em->flush(); + + // Clear entity manager + $em->clear(); + + // Retrieve item from cache + $state = $em->find('State', 1); + + // Hit database to update the row and update cache entry + $state->setName("New Name"); + $em->persist($state); + $em->flush(); + + // Create a new collection item + $city = new City($name, $state); + $state->addCity($city); + + // Hit database to insert new collection item, + // put entity and collection cache into cache. + $em->persist($city); + $em->persist($state); + $em->flush(); + + // Clear entity manager + $em->clear(); + + // Retrieve item from cache + $state = $em->find('State', 1); + + // Retrieve association from cache + $country = $state->getCountry(); + + // Retrieve collection from cache + $cities = $state->getCities(); + + echo $country->getName(); + echo $state->getName(); + + // Retrieve each collection item from cache + foreach ($cities as $city) { + echo $city->getName(); + } + +.. note:: + + Notice that all entities should be marked as cacheable. + +Using the query cache +--------------------- + +The second level cache stores the entities, associations and collections. +The query cache stores the results of the query but as identifiers, entity values are actually stored in the 2nd level cache. + +.. note:: + + Query cache should always be used in conjunction with the second-level-cache for those entities which should be cached. + +.. code-block:: php + + createQuery('SELECT c FROM Country c ORDER BY c.name') + ->setCacheable(true) + ->getResult(); + + $em->clear() + + // Check if query result is valid and load entities from cache + $result2 = $em->createQuery('SELECT c FROM Country c ORDER BY c.name') + ->setCacheable(true) + ->getResult(); + +Cache mode +~~~~~~~~~~ + +The Cache Mode controls how a particular query interacts with the second-level cache: + +* ``Cache::MODE_GET`` - May read items from the cache, but will not add items. +* ``Cache::MODE_PUT`` - Will never read items from the cache, but will add items to the cache as it reads them from the database. +* ``Cache::MODE_NORMAL`` - May read items from the cache, and add items to the cache. +* ``Cache::MODE_REFRESH`` - The query will never read items from the cache, but will refresh items to the cache as it reads them from the database. + +.. code-block:: php + + createQuery('SELECT c FROM Country c ORDER BY c.name') + ->setCacheMode(Cache::MODE_GET) + ->setCacheable(true) + ->getResult(); + +.. note:: + + The the default query cache mode is ```Cache::MODE_NORMAL``` + +DELETE / UPDATE queries +~~~~~~~~~~~~~~~~~~~~~~~ + +DQL UPDATE / DELETE statements are ported directly into a database and bypass the second-level cache, +Entities that are already cached will NOT be invalidated. +However the cached data could be evicted using the cache API or an special query hint. + + +Execute the ``UPDATE`` and invalidate ``all cache entries`` using ``Query::HINT_CACHE_EVICT`` + +.. code-block:: php + + _em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1") + ->setHint(Query::HINT_CACHE_EVICT, true) + ->execute(); + + +Execute the ``UPDATE`` and invalidate ``all cache entries`` using the cache API + +.. code-block:: php + + _em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1") + ->execute(); + // Invoke Cache API + $em->getCache()->evictEntityRegion('Entity\Country'); + + +Execute the ``UPDATE`` and invalidate ``a specific cache entry`` using the cache API + +.. code-block:: php + + _em->createQuery("UPDATE Entity\Country u SET u.name = 'unknown' WHERE u.id = 1") + ->execute(); + // Invoke Cache API + $em->getCache()->evictEntity('Entity\Country', 1); + +Using the repository query cache +--------------------- + +As well as ``Query Cache`` all persister queries store only identifier values for an individual query. +All persister use a single timestamps cache region keeps track of the last update for each persister, +When a query is loaded from cache, the timestamp region is checked for the last update for that persister. +Using the last update timestamps as part of the query key invalidate the cache key when an update occurs. + +.. code-block:: php + + getRepository('Entity\Country')->findAll(); + + // load from query and entities from cache.. + $entities = $em->getRepository('Entity\Country')->findAll(); + + // update the timestamp cache region for Country + $em->persist(new Country('zombieland')); + $em->flush(); + $em->clear(); + + // Reload from database. + // At this point the query cache key if not logger valid, the select goes straight + $entities = $em->getRepository('Entity\Country')->findAll(); + +Cache API +--------- + +Caches are not aware of changes made by another application. +However, you can use the cache API to check / invalidate cache entries. + +.. code-block:: php + + getCache(); + + $cache->containsEntity('Entity\State', 1) // Check if the cache exists + $cache->evictEntity('Entity\State', 1); // Remove an entity from cache + $cache->evictEntityRegion('Entity\State'); // Remove all entities from cache + + $cache->containsCollection('Entity\State', 'cities', 1); // Check if the cache exists + $cache->evictCollection('Entity\State', 'cities', 1); // Remove an entity collection from cache + $cache->evictCollectionRegion('Entity\State', 'cities'); // Remove all collections from cache + +Limitations +----------- + +Composite primary key +~~~~~~~~~~~~~~~~~~~~~ + +Composite primary key are supported by second level cache, +however when one of the keys is an association the cached entity should always be retrieved using the association identifier. +For performance reasons the cache API does not extract from composite primary key. + +.. code-block:: php + + find('Article', 1); + + // Supported + /* @var $article Article */ + $article = $em->find('Article', $article); + + // Supported + $id = array('source' => 1, 'target' => 2); + $reference = $em->find('Reference', $id); + + // NOT Supported + $id = array('source' => new Article(1), 'target' => new Article(2)); + $reference = $em->find('Reference', $id); + +Distributed environments +~~~~~~~~~~~~~~~~~~~~~~~~ + +Some cache driver are not meant to be used in a distributed environment. +Load-balancer for distributing workloads across multiple computing resources +should be used in conjunction with distributed caching system such as memcached, redis, riak ... + +Caches should be used with care when using a load-balancer if you don't share the cache. +While using APC or any file based cache update occurred in a specific machine would not reflect to the cache in other machines. + + +Paginator +~~~~~~~~~ + +Count queries generated by ``Doctrine\ORM\Tools\Pagination\Paginator`` are not cached by second-level cache. +Although entities and query result are cached count queries will hit the database every time. diff --git a/vendor/doctrine/orm/docs/en/reference/security.rst b/vendor/doctrine/orm/docs/en/reference/security.rst new file mode 100644 index 0000000..efc0989 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/security.rst @@ -0,0 +1,151 @@ +Security +======== + +The Doctrine library is operating very close to your database and as such needs +to handle and make assumptions about SQL injection vulnerabilities. + +It is vital that you understand how Doctrine approaches security, because +we cannot protect you from SQL injection. + +Please also read the documentation chapter on Security in Doctrine DBAL. This +page only handles Security issues in the ORM. + +- [DBAL Security Page](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst) + +If you find a Security bug in Doctrine, please report it on Jira and change the +Security Level to "Security Issues". It will be visible to Doctrine Core +developers and you only. + +User input and Doctrine ORM +--------------------------- + +The ORM is much better at protecting against SQL injection than the DBAL alone. +You can consider the following APIs to be safe from SQL injection: + +- ``\Doctrine\ORM\EntityManager#find()`` and ``getReference()``. +- All values on Objects inserted and updated through ``Doctrine\ORM\EntityManager#persist()`` +- All find methods on ``Doctrine\ORM\EntityRepository``. +- User Input set to DQL Queries or QueryBuilder methods through + - ``setParameter()`` or variants + - ``setMaxResults()`` + - ``setFirstResult()`` +- Queries through the Criteria API on ``Doctrine\ORM\PersistentCollection`` and + ``Doctrine\ORM\EntityRepository``. + +You are **NOT** save from SQL injection when using user input with: + +- Expression API of ``Doctrine\ORM\QueryBuilder`` +- Concatenating user input into DQL SELECT, UPDATE or DELETE statements or + Native SQL. + +This means SQL injections can only occur with Doctrine ORM when working with +Query Objects of any kind. The safe rule is to always use prepared statement +parameters for user objects when using a Query object. + +.. warning:: + + Insecure code follows, don't copy paste this. + +The following example shows insecure DQL usage: + +.. code-block:: php + + createQuery($dql); + $query->setParameter(1, $_GET['status']); + + +Preventing Mass Assignment Vulnerabilities +------------------------------------------ + +ORMs are very convenient for CRUD applications and Doctrine is no exception. +However CRUD apps are often vulnerable to mass assignment security problems +when implemented naively. + +Doctrine is not vulnerable to this problem out of the box, but you can easily +make your entities vulnerable to mass assignment when you add methods of +the kind ``updateFromArray()`` or ``updateFromJson()`` to them. A vulnerable +entity might look like this: + +.. code-block:: php + + $value) { + $this->$key = $value; + } + } + } + +Now the possiblity of mass-asignment exists on this entity and can +be exploitet by attackers to set the "isAdmin" flag to true on any +object when you pass the whole request data to this method like: + +.. code-block:: php + + fromArray($_POST); + + $entityManager->persist($entity); + $entityManager->flush(); + +You can spot this problem in this very simple example easily. However +in combination with frameworks and form libraries it might not be +so obvious when this issue arises. Be careful to avoid this +kind of mistake. + +How to fix this problem? You should always have a whitelist +of allowed key to set via mass assignment functions. + +.. code-block:: php + + public function fromArray(array $userInput, $allowedFields = array()) + { + foreach ($userInput as $key => $value) { + if (in_array($key, $allowedFields)) { + $this->$key = $value; + } + } + } diff --git a/vendor/doctrine/orm/docs/en/reference/tools.rst b/vendor/doctrine/orm/docs/en/reference/tools.rst new file mode 100644 index 0000000..51f4573 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/tools.rst @@ -0,0 +1,528 @@ +Tools +===== + +Doctrine Console +---------------- + +The Doctrine Console is a Command Line Interface tool for simplifying common +administration tasks during the development of a project that uses Doctrine 2. + +Take a look at the :doc:`Installation and Configuration ` +chapter for more information how to setup the console command. + +Display Help Information +~~~~~~~~~~~~~~~~~~~~~~~~ + +Type ``php vendor/bin/doctrine`` on the command line and you should see an +overview of the available commands or use the --help flag to get +information on the available commands. If you want to know more +about the use of generate entities for example, you can call: + +.. code-block:: php + + $> php vendor/bin/doctrine orm:generate-entities --help + + +Configuration +~~~~~~~~~~~~~ + +Whenever the ``doctrine`` command line tool is invoked, it can +access all Commands that were registered by developer. There is no +auto-detection mechanism at work. The Doctrine binary +already registers all the commands that currently ship with +Doctrine DBAL and ORM. If you want to use additional commands you +have to register them yourself. + +All the commands of the Doctrine Console require access to the ``EntityManager`` +or ``DBAL`` Connection. You have to inject them into the console application +using so called Helper-Sets. This requires either the ``db`` +or the ``em`` helpers to be defined in order to work correctly. + +Whenever you invoke the Doctrine binary the current folder is searched for a +``cli-config.php`` file. This file contains the project specific configuration: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($conn) + )); + $cli->setHelperSet($helperSet); + +When dealing with the ORM package, the EntityManagerHelper is +required: + +.. code-block:: php + + new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + $cli->setHelperSet($helperSet); + +The HelperSet instance has to be generated in a separate file (i.e. +``cli-config.php``) that contains typical Doctrine bootstrap code +and predefines the needed HelperSet attributes mentioned above. A +sample ``cli-config.php`` file looks as follows: + +.. code-block:: php + + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em) + )); + +It is important to define a correct HelperSet that Doctrine binary +script will ultimately use. The Doctrine Binary will automatically +find the first instance of HelperSet in the global variable +namespace and use this. + +.. note:: + + You have to adjust this snippet for your specific application or framework + and use their facilities to access the Doctrine EntityManager and + Connection Resources. + +Command Overview +~~~~~~~~~~~~~~~~ + +The following Commands are currently available: + + +- ``help`` Displays help for a command (?) +- ``list`` Lists commands +- ``dbal:import`` Import SQL file(s) directly to Database. +- ``dbal:run-sql`` Executes arbitrary SQL directly from the + command line. +- ``orm:clear-cache:metadata`` Clear all metadata cache of the + various cache drivers. +- ``orm:clear-cache:query`` Clear all query cache of the various + cache drivers. +- ``orm:clear-cache:result`` Clear result cache of the various + cache drivers. +- ``orm:convert-d1-schema`` Converts Doctrine 1.X schema into a + Doctrine 2.X schema. +- ``orm:convert-mapping`` Convert mapping information between + supported formats. +- ``orm:ensure-production-settings`` Verify that Doctrine is + properly configured for a production environment. +- ``orm:generate-entities`` Generate entity classes and method + stubs from your mapping information. +- ``orm:generate-proxies`` Generates proxy classes for entity + classes. +- ``orm:generate-repositories`` Generate repository classes from + your mapping information. +- ``orm:run-dql`` Executes arbitrary DQL directly from the command + line. +- ``orm:schema-tool:create`` Processes the schema and either + create it directly on EntityManager Storage Connection or generate + the SQL output. +- ``orm:schema-tool:drop`` Processes the schema and either drop + the database schema of EntityManager Storage Connection or generate + the SQL output. +- ``orm:schema-tool:update`` Processes the schema and either + update the database schema of EntityManager Storage Connection or + generate the SQL output. + +For these commands are also available aliases: + + +- ``orm:convert:d1-schema`` is alias for ``orm:convert-d1-schema``. +- ``orm:convert:mapping`` is alias for ``orm:convert-mapping``. +- ``orm:generate:entities`` is alias for ``orm:generate-entities``. +- ``orm:generate:proxies`` is alias for ``orm:generate-proxies``. +- ``orm:generate:repositories`` is alias for ``orm:generate-repositories``. + +.. note:: + + Console also supports auto completion, for example, instead of + ``orm:clear-cache:query`` you can use just ``o:c:q``. + +Database Schema Generation +-------------------------- + +.. note:: + + SchemaTool can do harm to your database. It will drop or alter + tables, indexes, sequences and such. Please use this tool with + caution in development and not on a production server. It is meant + for helping you develop your Database Schema, but NOT with + migrating schema from A to B in production. A safe approach would + be generating the SQL on development server and saving it into SQL + Migration files that are executed manually on the production + server. + + SchemaTool assumes your Doctrine Project uses the given database on + its own. Update and Drop commands will mess with other tables if + they are not related to the current project that is using Doctrine. + Please be careful! + + +To generate your database schema from your Doctrine mapping files +you can use the ``SchemaTool`` class or the ``schema-tool`` Console +Command. + +When using the SchemaTool class directly, create your schema using +the ``createSchema()`` method. First create an instance of the +``SchemaTool`` and pass it an instance of the ``EntityManager`` +that you want to use to create the schema. This method receives an +array of ``ClassMetadataInfo`` instances. + +.. code-block:: php + + getClassMetadata('Entities\User'), + $em->getClassMetadata('Entities\Profile') + ); + $tool->createSchema($classes); + +To drop the schema you can use the ``dropSchema()`` method. + +.. code-block:: php + + dropSchema($classes); + +This drops all the tables that are currently used by your metadata +model. When you are changing your metadata a lot during development +you might want to drop the complete database instead of only the +tables of the current model to clean up with orphaned tables. + +.. code-block:: php + + dropSchema($classes, \Doctrine\ORM\Tools\SchemaTool::DROP_DATABASE); + +You can also use database introspection to update your schema +easily with the ``updateSchema()`` method. It will compare your +existing database schema to the passed array of +``ClassMetdataInfo`` instances. + +.. code-block:: php + + updateSchema($classes); + +If you want to use this functionality from the command line you can +use the ``schema-tool`` command. + +To create the schema use the ``create`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:create + +To drop the schema use the ``drop`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:drop + +If you want to drop and then recreate the schema then use both +options: + +.. code-block:: php + + $ php doctrine orm:schema-tool:drop + $ php doctrine orm:schema-tool:create + +As you would think, if you want to update your schema use the +``update`` command: + +.. code-block:: php + + $ php doctrine orm:schema-tool:update + +All of the above commands also accept a ``--dump-sql`` option that +will output the SQL for the ran operation. + +.. code-block:: php + + $ php doctrine orm:schema-tool:create --dump-sql + +Before using the orm:schema-tool commands, remember to configure +your cli-config.php properly. + +.. note:: + + When using the Annotation Mapping Driver you have to either setup + your autoloader in the cli-config.php correctly to find all the + entities, or you can use the second argument of the + ``EntityManagerHelper`` to specify all the paths of your entities + (or mapping files), i.e. + ``new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em, $mappingPaths);`` + +Entity Generation +----------------- + +Generate entity classes and method stubs from your mapping information. + +.. code-block:: php + + $ php doctrine orm:generate-entities + $ php doctrine orm:generate-entities --update-entities + $ php doctrine orm:generate-entities --regenerate-entities + +This command is not suited for constant usage. It is a little helper and does +not support all the mapping edge cases very well. You still have to put work +in your entities after using this command. + +It is possible to use the EntityGenerator on code that you have already written. It will +not be lost. The EntityGenerator will only append new code to your +file and will not delete the old code. However this approach may still be prone +to error and we suggest you use code repositories such as GIT or SVN to make +backups of your code. + +It makes sense to generate the entity code if you are using entities as Data +Access Objects only and don't put much additional logic on them. If you are +however putting much more logic on the entities you should refrain from using +the entity-generator and code your entities manually. + +.. note:: + + Even if you specified Inheritance options in your + XML or YAML Mapping files the generator cannot generate the base and + child classes for you correctly, because it doesn't know which + class is supposed to extend which. You have to adjust the entity + code manually for inheritance to work! + + +Convert Mapping Information +--------------------------- + +Convert mapping information between supported formats. + +This is an **execute one-time** command. It should not be necessary for +you to call this method multiple times, especially when using the ``--from-database`` +flag. + +Converting an existing database schema into mapping files only solves about 70-80% +of the necessary mapping information. Additionally the detection from an existing +database cannot detect inverse associations, inheritance types, +entities with foreign keys as primary keys and many of the +semantical operations on associations such as cascade. + +.. note:: + + There is no need to convert YAML or XML mapping files to annotations + every time you make changes. All mapping drivers are first class citizens + in Doctrine 2 and can be used as runtime mapping for the ORM. See the + docs on XML and YAML Mapping for an example how to register this metadata + drivers as primary mapping source. + +To convert some mapping information between the various supported +formats you can use the ``ClassMetadataExporter`` to get exporter +instances for the different formats: + +.. code-block:: php + + getExporter('yml', '/path/to/export/yml'); + +Now you can export some ``ClassMetadata`` instances: + +.. code-block:: php + + getClassMetadata('Entities\User'), + $em->getClassMetadata('Entities\Profile') + ); + $exporter->setMetadata($classes); + $exporter->export(); + +This functionality is also available from the command line to +convert your loaded mapping information to another format. The +``orm:convert-mapping`` command accepts two arguments, the type to +convert to and the path to generate it: + +.. code-block:: php + + $ php doctrine orm:convert-mapping xml /path/to/mapping-path-converted-to-xml + +Reverse Engineering +------------------- + +You can use the ``DatabaseDriver`` to reverse engineer a database +to an array of ``ClassMetadataInfo`` instances and generate YAML, +XML, etc. from them. + +.. note:: + + Reverse Engineering is a **one-time** process that can get you started with a project. + Converting an existing database schema into mapping files only detects about 70-80% + of the necessary mapping information. Additionally the detection from an existing + database cannot detect inverse associations, inheritance types, + entities with foreign keys as primary keys and many of the + semantical operations on associations such as cascade. + +First you need to retrieve the metadata instances with the +``DatabaseDriver``: + +.. code-block:: php + + getConfiguration()->setMetadataDriverImpl( + new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( + $em->getConnection()->getSchemaManager() + ) + ); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + +Now you can get an exporter instance and export the loaded metadata +to yml: + +.. code-block:: php + + getExporter('yml', '/path/to/export/yml'); + $exporter->setMetadata($metadata); + $exporter->export(); + +You can also reverse engineer a database using the +``orm:convert-mapping`` command: + +.. code-block:: php + + $ php doctrine orm:convert-mapping --from-database yml /path/to/mapping-path-converted-to-yml + +.. note:: + + Reverse Engineering is not always working perfectly + depending on special cases. It will only detect Many-To-One + relations (even if they are One-To-One) and will try to create + entities from Many-To-Many tables. It also has problems with naming + of foreign keys that have multiple column names. Any Reverse + Engineered Database-Schema needs considerable manual work to become + a useful domain model. + + +Runtime vs Development Mapping Validation +----------------------------------------- + +For performance reasons Doctrine 2 has to skip some of the +necessary validation of metadata mappings. You have to execute +this validation in your development workflow to verify the +associations are correctly defined. + +You can either use the Doctrine Command Line Tool: + +.. code-block:: php + + doctrine orm:validate-schema + +Or you can trigger the validation manually: + +.. code-block:: php + + validateMapping(); + + if (count($errors) > 0) { + // Lots of errors! + echo implode("\n\n", $errors); + } + +If the mapping is invalid the errors array contains a positive +number of elements with error messages. + +.. warning:: + + One mapping option that is not validated is the use of the referenced column name. + It has to point to the equivalent primary key otherwise Doctrine will not work. + +.. note:: + + One common error is to use a backlash in front of the + fully-qualified class-name. Whenever a FQCN is represented inside a + string (such as in your mapping definitions) you have to drop the + prefix backslash. PHP does this with ``get_class()`` or Reflection + methods for backwards compatibility reasons. + + +Adding own commands +------------------- + +You can also add your own commands on-top of the Doctrine supported +tools if you are using a manually built console script. + +To include a new command on Doctrine Console, you need to do modify the +``doctrine.php`` file a little: + +.. code-block:: php + + setCatchExceptions(true); + $cli->setHelperSet($helperSet); + + // Register All Doctrine Commands + ConsoleRunner::addCommands($cli); + + // Register your own command + $cli->addCommand(new \MyProject\Tools\Console\Commands\MyCustomCommand); + + // Runs console application + $cli->run(); + +Additionally, include multiple commands (and overriding previously +defined ones) is possible through the command: + +.. code-block:: php + + addCommands(array( + new \MyProject\Tools\Console\Commands\MyCustomCommand(), + new \MyProject\Tools\Console\Commands\SomethingCommand(), + new \MyProject\Tools\Console\Commands\AnotherCommand(), + new \MyProject\Tools\Console\Commands\OneMoreCommand(), + )); + + +Re-use console application +-------------------------- + +You are also able to retrieve and re-use the default console application. +Just call ``ConsoleRunner::createApplication(...)`` with an appropriate +HelperSet, like it is described in the configuration section. + +.. code-block:: php + + run(); + diff --git a/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst b/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst new file mode 100644 index 0000000..4dc1831 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/transactions-and-concurrency.rst @@ -0,0 +1,350 @@ +Transactions and Concurrency +============================ + +Transaction Demarcation +----------------------- + +Transaction demarcation is the task of defining your transaction +boundaries. Proper transaction demarcation is very important +because if not done properly it can negatively affect the +performance of your application. Many databases and database +abstraction layers like PDO by default operate in auto-commit mode, +which means that every single SQL statement is wrapped in a small +transaction. Without any explicit transaction demarcation from your +side, this quickly results in poor performance because transactions +are not cheap. + +For the most part, Doctrine 2 already takes care of proper +transaction demarcation for you: All the write operations +(INSERT/UPDATE/DELETE) are queued until ``EntityManager#flush()`` +is invoked which wraps all of these changes in a single +transaction. + +However, Doctrine 2 also allows (and encourages) you to take over +and control transaction demarcation yourself. + +These are two ways to deal with transactions when using the +Doctrine ORM and are now described in more detail. + +Approach 1: Implicitly +~~~~~~~~~~~~~~~~~~~~~~ + +The first approach is to use the implicit transaction handling +provided by the Doctrine ORM EntityManager. Given the following +code snippet, without any explicit transaction demarcation: + +.. code-block:: php + + setName('George'); + $em->persist($user); + $em->flush(); + +Since we do not do any custom transaction demarcation in the above +code, ``EntityManager#flush()`` will begin and commit/rollback a +transaction. This behavior is made possible by the aggregation of +the DML operations by the Doctrine ORM and is sufficient if all the +data manipulation that is part of a unit of work happens through +the domain model and thus the ORM. + +Approach 2: Explicitly +~~~~~~~~~~~~~~~~~~~~~~ + +The explicit alternative is to use the ``Doctrine\DBAL\Connection`` +API directly to control the transaction boundaries. The code then +looks like this: + +.. code-block:: php + + getConnection()->beginTransaction(); // suspend auto-commit + try { + //... do some work + $user = new User; + $user->setName('George'); + $em->persist($user); + $em->flush(); + $em->getConnection()->commit(); + } catch (Exception $e) { + $em->getConnection()->rollback(); + throw $e; + } + +Explicit transaction demarcation is required when you want to +include custom DBAL operations in a unit of work or when you want +to make use of some methods of the ``EntityManager`` API that +require an active transaction. Such methods will throw a +``TransactionRequiredException`` to inform you of that +requirement. + +A more convenient alternative for explicit transaction demarcation is the use +of provided control abstractions in the form of +``Connection#transactional($func)`` and ``EntityManager#transactional($func)``. +When used, these control abstractions ensure that you never forget to rollback +the transaction, in addition to the obvious code reduction. An example that is +functionally equivalent to the previously shown code looks as follows: + +.. code-block:: php + + transactional(function($em) { + //... do some work + $user = new User; + $user->setName('George'); + $em->persist($user); + }); + +The difference between ``Connection#transactional($func)`` and +``EntityManager#transactional($func)`` is that the latter +abstraction flushes the ``EntityManager`` prior to transaction +commit and rolls back the transaction when an +exception occurs. + +Exception Handling +~~~~~~~~~~~~~~~~~~ + +When using implicit transaction demarcation and an exception occurs +during ``EntityManager#flush()``, the transaction is automatically +rolled back and the ``EntityManager`` closed. + +When using explicit transaction demarcation and an exception +occurs, the transaction should be rolled back immediately and the +``EntityManager`` closed by invoking ``EntityManager#close()`` and +subsequently discarded, as demonstrated in the example above. This +can be handled elegantly by the control abstractions shown earlier. +Note that when catching ``Exception`` you should generally re-throw +the exception. If you intend to recover from some exceptions, catch +them explicitly in earlier catch blocks (but do not forget to +rollback the transaction and close the ``EntityManager`` there as +well). All other best practices of exception handling apply +similarly (i.e. either log or re-throw, not both, etc.). + +As a result of this procedure, all previously managed or removed +instances of the ``EntityManager`` become detached. The state of +the detached objects will be the state at the point at which the +transaction was rolled back. The state of the objects is in no way +rolled back and thus the objects are now out of synch with the +database. The application can continue to use the detached objects, +knowing that their state is potentially no longer accurate. + +If you intend to start another unit of work after an exception has +occurred you should do that with a new ``EntityManager``. + +Locking Support +--------------- + +Doctrine 2 offers support for Pessimistic- and Optimistic-locking +strategies natively. This allows to take very fine-grained control +over what kind of locking is required for your Entities in your +application. + +Optimistic Locking +~~~~~~~~~~~~~~~~~~ + +Database transactions are fine for concurrency control during a +single request. However, a database transaction should not span +across requests, the so-called "user think time". Therefore a +long-running "business transaction" that spans multiple requests +needs to involve several database transactions. Thus, database +transactions alone can no longer control concurrency during such a +long-running business transaction. Concurrency control becomes the +partial responsibility of the application itself. + +Doctrine has integrated support for automatic optimistic locking +via a version field. In this approach any entity that should be +protected against concurrent modifications during long-running +business transactions gets a version field that is either a simple +number (mapping type: integer) or a timestamp (mapping type: +datetime). When changes to such an entity are persisted at the end +of a long-running conversation the version of the entity is +compared to the version in the database and if they don't match, an +``OptimisticLockException`` is thrown, indicating that the entity +has been modified by someone else already. + +You designate a version field in an entity as follows. In this +example we'll use an integer. + +.. code-block:: php + + find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion); + + // do the work + + $em->flush(); + } catch(OptimisticLockException $e) { + echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; + } + +Or you can use ``EntityManager#lock()`` to find out: + +.. code-block:: php + + find('User', $theEntityId); + + try { + // assert version + $em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion); + + } catch(OptimisticLockException $e) { + echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; + } + +Important Implementation Notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can easily get the optimistic locking workflow wrong if you +compare the wrong versions. Say you have Alice and Bob editing a +hypothetical blog post: + +- Alice reads the headline of the blog post being "Foo", at + optimistic lock version 1 (GET Request) +- Bob reads the headline of the blog post being "Foo", at + optimistic lock version 1 (GET Request) +- Bob updates the headline to "Bar", upgrading the optimistic lock + version to 2 (POST Request of a Form) +- Alice updates the headline to "Baz", ... (POST Request of a + Form) + +Now at the last stage of this scenario the blog post has to be read +again from the database before Alice's headline can be applied. At +this point you will want to check if the blog post is still at +version 1 (which it is not in this scenario). + +Using optimistic locking correctly, you *have* to add the version +as an additional hidden field (or into the SESSION for more +safety). Otherwise you cannot verify the version is still the one +being originally read from the database when Alice performed her +GET request for the blog post. If this happens you might see lost +updates you wanted to prevent with Optimistic Locking. + +See the example code, The form (GET Request): + +.. code-block:: php + + find('BlogPost', 123456); + + echo ''; + echo ''; + +And the change headline action (POST Request): + +.. code-block:: php + + find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion); + +Pessimistic Locking +~~~~~~~~~~~~~~~~~~~ + +Doctrine 2 supports Pessimistic Locking at the database level. No +attempt is being made to implement pessimistic locking inside +Doctrine, rather vendor-specific and ANSI-SQL commands are used to +acquire row-level locks. Every Entity can be part of a pessimistic +lock, there is no special metadata required to use this feature. + +However for Pessimistic Locking to work you have to disable the +Auto-Commit Mode of your Database and start a transaction around +your pessimistic lock use-case using the "Approach 2: Explicit +Transaction Demarcation" described above. Doctrine 2 will throw an +Exception if you attempt to acquire an pessimistic lock and no +transaction is running. + +Doctrine 2 currently supports two pessimistic lock modes: + + +- Pessimistic Write + (``Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE``), locks the + underlying database rows for concurrent Read and Write Operations. +- Pessimistic Read (``Doctrine\DBAL\LockMode::PESSIMISTIC_READ``), + locks other concurrent requests that attempt to update or lock rows + in write mode. + +You can use pessimistic locks in three different scenarios: + + +1. Using + ``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``EntityManager#find($className, $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` +2. Using + ``EntityManager#lock($entity, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``EntityManager#lock($entity, \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` +3. Using + ``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE)`` + or + ``Query#setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ)`` + + diff --git a/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst b/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst new file mode 100644 index 0000000..da3cc9d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/unitofwork-associations.rst @@ -0,0 +1,60 @@ +Association Updates: Owning Side and Inverse Side +================================================= + +When mapping bidirectional associations it is important to +understand the concept of the owning and inverse sides. The +following general rules apply: + +- Relationships may be bidirectional or unidirectional. +- A bidirectional relationship has both an owning side and an inverse side +- A unidirectional relationship only has an owning side. +- Doctrine will **only** check the owning side of an association for changes. + +Bidirectional Associations +-------------------------- + +The following rules apply to **bidirectional** associations: + +- The inverse side has to use the ``mappedBy`` attribute of the OneToOne, + OneToMany, or ManyToMany mapping declaration. The mappedBy + attribute contains the name of the association-field on the owning side. +- The owning side has to use the ``inversedBy`` attribute of the + OneToOne, ManyToOne, or ManyToMany mapping declaration. + The inversedBy attribute contains the name of the association-field + on the inverse-side. +- ManyToOne is always the owning side of a bidirectional association. +- OneToMany is always the inverse side of a bidirectional association. +- The owning side of a OneToOne association is the entity with the table + containing the foreign key. +- You can pick the owning side of a many-to-many association yourself. + +Important concepts +------------------ + +**Doctrine will only check the owning side of an association for changes.** + +To fully understand this, remember how bidirectional associations +are maintained in the object world. There are 2 references on each +side of the association and these 2 references both represent the +same association but can change independently of one another. Of +course, in a correct application the semantics of the bidirectional +association are properly maintained by the application developer +(that's his responsibility). Doctrine needs to know which of these +two in-memory references is the one that should be persisted and +which not. This is what the owning/inverse concept is mainly used +for. + +**Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine's point of view)** + +The owning side of a bidirectional association is the side Doctrine +"looks at" when determining the state of the association, and +consequently whether there is anything to do to update the +association in the database. + +.. note:: + + "Owning side" and "inverse side" are technical concepts of + the ORM technology, not concepts of your domain model. What you + consider as the owning side in your domain model can be different + from what the owning side is for Doctrine. These are unrelated. + diff --git a/vendor/doctrine/orm/docs/en/reference/unitofwork.rst b/vendor/doctrine/orm/docs/en/reference/unitofwork.rst new file mode 100644 index 0000000..cdfb6e0 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/unitofwork.rst @@ -0,0 +1,201 @@ +Doctrine Internals explained +============================ + +Object relational mapping is a complex topic and sufficiently understanding how Doctrine works internally helps you use its full power. + +How Doctrine keeps track of Objects +----------------------------------- + +Doctrine uses the Identity Map pattern to track objects. Whenever you fetch an +object from the database, Doctrine will keep a reference to this object inside +its UnitOfWork. The array holding all the entity references is two-levels deep +and has the keys "root entity name" and "id". Since Doctrine allows composite +keys the id is a sorted, serialized version of all the key columns. + +This allows Doctrine room for optimizations. If you call the EntityManager and +ask for an entity with a specific ID twice, it will return the same instance: + +.. code-block:: php + + public function testIdentityMap() + { + $objectA = $this->entityManager->find('EntityName', 1); + $objectB = $this->entityManager->find('EntityName', 1); + + $this->assertSame($objectA, $objectB) + } + +Only one SELECT query will be fired against the database here. In the second +``EntityManager#find()`` call Doctrine will check the identity map first and +doesn't need to make that database roundtrip. + +Even if you get a proxy object first then fetch the object by the same id you +will still end up with the same reference: + +.. code-block:: php + + public function testIdentityMapReference() + { + $objectA = $this->entityManager->getReference('EntityName', 1); + // check for proxyinterface + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $objectA); + + $objectB = $this->entityManager->find('EntityName', 1); + + $this->assertSame($objectA, $objectB) + } + +The identity map being indexed by primary keys only allows shortcuts when you +ask for objects by primary key. Assume you have the following ``persons`` +table: + +:: + + id | name + ------------- + 1 | Benjamin + 2 | Bud + +Take the following example where two +consecutive calls are made against a repository to fetch an entity by a set of +criteria: + +.. code-block:: php + + public function testIdentityMapRepositoryFindBy() + { + $repository = $this->entityManager->getRepository('Person'); + $objectA = $repository->findOneBy(array('name' => 'Benjamin')); + $objectB = $repository->findOneBy(array('name' => 'Benjamin')); + + $this->assertSame($objectA, $objectB); + } + +This query will still return the same references and `$objectA` and `$objectB` +are indeed referencing the same object. However when checking your SQL logs you +will realize that two queries have been executed against the database. Doctrine +only knows objects by id, so a query for different criteria has to go to the +database, even if it was executed just before. + +But instead of creating a second Person object Doctrine first gets the primary +key from the row and check if it already has an object inside the UnitOfWork +with that primary key. In our example it finds an object and decides to return +this instead of creating a new one. + +The identity map has a second use-case. When you call ``EntityManager#flush`` +Doctrine will ask the identity map for all objects that are currently managed. +This means you don't have to call ``EntityManager#persist`` over and over again +to pass known objects to the EntityManager. This is a NO-OP for known entities, +but leads to much code written that is confusing to other developers. + +The following code WILL update your database with the changes made to the +``Person`` object, even if you did not call ``EntityManager#persist``: + +.. code-block:: php + + find("Person", 1); + $user->setName("Guilherme"); + $entityManager->flush(); + +How Doctrine Detects Changes +---------------------------- + +Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI). +This means you map php objects into a relational database that don't +necessarily know about the database at all. A natural question would now be, +"how does Doctrine even detect objects have changed?". + +For this Doctrine keeps a second map inside the UnitOfWork. Whenever you fetch +an object from the database Doctrine will keep a copy of all the properties and +associations inside the UnitOfWork. Because variables in the PHP language are +subject to "copy-on-write" the memory usage of a PHP request that only reads +objects from the database is the same as if Doctrine did not keep this variable +copy. Only if you start changing variables PHP will create new variables internally +that consume new memory. + +Now whenever you call ``EntityManager#flush`` Doctrine will iterate over the +Identity Map and for each object compares the original property and association +values with the values that are currently set on the object. If changes are +detected then the object is queued for a SQL UPDATE operation. Only the fields +that actually changed are updated. + +This process has an obvious performance impact. The larger the size of the +UnitOfWork is, the longer this computation takes. There are several ways to +optimize the performance of the Flush Operation: + +- Mark entities as read only. These entities can only be inserted or removed, + but are never updated. They are omitted in the changeset calculation. +- Temporarily mark entities as read only. If you have a very large UnitOfWork + but know that a large set of entities has not changed, just mark them as read + only with ``$entityManager->getUnitOfWork()->markReadOnly($entity)``. +- Flush only a single entity with ``$entityManager->flush($entity)``. +- Use :doc:`Change Tracking Policies ` to use more + explicit strategies of notifying the UnitOfWork what objects/properties + changed. + + +Query Internals +--------------- + +The different ORM Layers +------------------------ + +Doctrine ships with a set of layers with different responsibilities. This +section gives a short explanation of each layer. + +Hydration +~~~~~~~~~ + +Responsible for creating a final result from a raw database statement and a +result-set mapping object. The developer can choose which kind of result he +wishes to be hydrated. Default result-types include: + +- SQL to Entities +- SQL to structured Arrays +- SQL to simple scalar result arrays +- SQL to a single result variable + +Hydration to entities and arrays is one of most complex parts of Doctrine +algorithm-wise. It can build results with for example: + +- Single table selects +- Joins with n:1 or 1:n cardinality, grouping belonging to the same parent. +- Mixed results of objects and scalar values +- Hydration of results by a given scalar value as key. + +Persisters +~~~~~~~~~~ + +tbr + +UnitOfWork +~~~~~~~~~~ + +tbr + +ResultSetMapping +~~~~~~~~~~~~~~~~ + +tbr + +DQL Parser +~~~~~~~~~~ + +tbr + +SQLWalker +~~~~~~~~~ + +tbr + +EntityManager +~~~~~~~~~~~~~ + +tbr + +ClassMetadataFactory +~~~~~~~~~~~~~~~~~~~~ + +tbr + diff --git a/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst b/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst new file mode 100644 index 0000000..85ffaee --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/working-with-associations.rst @@ -0,0 +1,712 @@ +Working with Associations +========================= + +Associations between entities are represented just like in regular +object-oriented PHP code using references to other objects or +collections of objects. + +Changes to associations in your code are not synchronized to the +database directly, only when calling ``EntityManager#flush()``. + +There are other concepts you should know about when working +with associations in Doctrine: + +- If an entity is removed from a collection, the association is + removed, not the entity itself. A collection of entities always + only represents the association to the containing entities, not the + entity itself. +- When a bidirectional assocation is updated, Doctrine only checks + on one of both sides for these changes. This is called the :doc:`owning side ` + of the association. +- A property with a reference to many entities has to be instances of the + ``Doctrine\Common\Collections\Collection`` interface. + +Association Example Entities +---------------------------- + +We will use a simple comment system with Users and Comments as +entities to show examples of association management. See the PHP +docblocks of each association in the following example for +information about its type and if it's the owning or inverse side. + +.. code-block:: php + + commentsRead; + } + + public function setFirstComment(Comment $c) { + $this->firstComment = $c; + } + } + +The interaction code would then look like in the following snippet +(``$em`` here is an instance of the EntityManager): + +.. code-block:: php + + find('User', $userId); + + // unidirectional many to many + $comment = $em->find('Comment', $readCommentId); + $user->getReadComments()->add($comment); + + $em->flush(); + + // unidirectional many to one + $myFirstComment = new Comment(); + $user->setFirstComment($myFirstComment); + + $em->persist($myFirstComment); + $em->flush(); + +In the case of bi-directional associations you have to update the +fields on both sides: + +.. code-block:: php + + commentsAuthored; + } + + public function getFavoriteComments() { + return $this->favorites; + } + } + + class Comment + { + // ... + + public function getUserFavorites() { + return $this->userFavorites; + } + + public function setAuthor(User $author = null) { + $this->author = $author; + } + } + + // Many-to-Many + $user->getFavorites()->add($favoriteComment); + $favoriteComment->getUserFavorites()->add($user); + + $em->flush(); + + // Many-To-One / One-To-Many Bidirectional + $newComment = new Comment(); + $user->getAuthoredComments()->add($newComment); + $newComment->setAuthor($user); + + $em->persist($newComment); + $em->flush(); + +Notice how always both sides of the bidirectional association are +updated. The previous unidirectional associations were simpler to +handle. + +Removing Associations +--------------------- + +Removing an association between two entities is similarly +straight-forward. There are two strategies to do so, by key and by +element. Here are some examples: + +.. code-block:: php + + getComments()->removeElement($comment); + $comment->setAuthor(null); + + $user->getFavorites()->removeElement($comment); + $comment->getUserFavorites()->removeElement($user); + + // Remove by Key + $user->getComments()->remove($ithComment); + $comment->setAuthor(null); + +You need to call ``$em->flush()`` to make persist these changes in +the database permanently. + +Notice how both sides of the bidirectional association are always +updated. Unidirectional associations are consequently simpler to +handle. Also note that if you use type-hinting in your methods, i.e. +``setAddress(Address $address)``, PHP will only allow null +values if ``null`` is set as default value. Otherwise +setAddress(null) will fail for removing the association. If you +insist on type-hinting a typical way to deal with this is to +provide a special method, like ``removeAddress()``. This can also +provide better encapsulation as it hides the internal meaning of +not having an address. + +When working with collections, keep in mind that a Collection is +essentially an ordered map (just like a PHP array). That is why the +``remove`` operation accepts an index/key. ``removeElement`` is a +separate method that has O(n) complexity using ``array_search``, +where n is the size of the map. + +.. note:: + + Since Doctrine always only looks at the owning side of a + bidirectional association for updates, it is not necessary for + write operations that an inverse collection of a bidirectional + one-to-many or many-to-many association is updated. This knowledge + can often be used to improve performance by avoiding the loading of + the inverse collection. + + +You can also clear the contents of a whole collection using the +``Collections::clear()`` method. You should be aware that using +this method can lead to a straight and optimized database delete or +update call during the flush operation that is not aware of +entities that have been re-added to the collection. + +Say you clear a collection of tags by calling +``$post->getTags()->clear();`` and then call +``$post->getTags()->add($tag)``. This will not recognize the tag having +already been added previously and will consequently issue two separate database +calls. + +Association Management Methods +------------------------------ + +It is generally a good idea to encapsulate proper association +management inside the entity classes. This makes it easier to use +the class correctly and can encapsulate details about how the +association is maintained. + +The following code shows updates to the previous User and Comment +example that encapsulate much of the association management code: + +.. code-block:: php + + commentsRead[] = $comment; + } + + public function addComment(Comment $comment) { + if (count($this->commentsAuthored) == 0) { + $this->setFirstComment($comment); + } + $this->comments[] = $comment; + $comment->setAuthor($this); + } + + private function setFirstComment(Comment $c) { + $this->firstComment = $c; + } + + public function addFavorite(Comment $comment) { + $this->favorites->add($comment); + $comment->addUserFavorite($this); + } + + public function removeFavorite(Comment $comment) { + $this->favorites->removeElement($comment); + $comment->removeUserFavorite($this); + } + } + + class Comment + { + // .. + + public function addUserFavorite(User $user) { + $this->userFavorites[] = $user; + } + + public function removeUserFavorite(User $user) { + $this->userFavorites->removeElement($user); + } + } + +You will notice that ``addUserFavorite`` and ``removeUserFavorite`` +do not call ``addFavorite`` and ``removeFavorite``, thus the +bidirectional association is strictly-speaking still incomplete. +However if you would naively add the ``addFavorite`` in +``addUserFavorite``, you end up with an infinite loop, so more work +is needed. As you can see, proper bidirectional association +management in plain OOP is a non-trivial task and encapsulating all +the details inside the classes can be challenging. + +.. note:: + + If you want to make sure that your collections are perfectly + encapsulated you should not return them from a + ``getCollectionName()`` method directly, but call + ``$collection->toArray()``. This way a client programmer for the + entity cannot circumvent the logic you implement on your entity for + association management. For example: + + +.. code-block:: php + + commentsRead->toArray(); + } + } + +This will however always initialize the collection, with all the +performance penalties given the size. In some scenarios of large +collections it might even be a good idea to completely hide the +read access behind methods on the EntityRepository. + +There is no single, best way for association management. It greatly +depends on the requirements of your concrete domain model as well +as your preferences. + +Synchronizing Bidirectional Collections +--------------------------------------- + +In the case of Many-To-Many associations you as the developer have the +responsibility of keeping the collections on the owning and inverse side +in sync when you apply changes to them. Doctrine can only +guarantee a consistent state for the hydration, not for your client +code. + +Using the User-Comment entities from above, a very simple example +can show the possible caveats you can encounter: + +.. code-block:: php + + getFavorites()->add($favoriteComment); + // not calling $favoriteComment->getUserFavorites()->add($user); + + $user->getFavorites()->contains($favoriteComment); // TRUE + $favoriteComment->getUserFavorites()->contains($user); // FALSE + +There are two approaches to handle this problem in your code: + + +1. Ignore updating the inverse side of bidirectional collections, + BUT never read from them in requests that changed their state. In + the next Request Doctrine hydrates the consistent collection state + again. +2. Always keep the bidirectional collections in sync through + association management methods. Reads of the Collections directly + after changes are consistent then. + +Transitive persistence / Cascade Operations +------------------------------------------- + +Persisting, removing, detaching, refreshing and merging individual entities can +become pretty cumbersome, especially when a highly interweaved object graph +is involved. Therefore Doctrine 2 provides a +mechanism for transitive persistence through cascading of these +operations. Each association to another entity or a collection of +entities can be configured to automatically cascade certain +operations. By default, no operations are cascaded. + +The following cascade options exist: + + +- persist : Cascades persist operations to the associated + entities. +- remove : Cascades remove operations to the associated entities. +- merge : Cascades merge operations to the associated entities. +- detach : Cascades detach operations to the associated entities. +- refresh : Cascades refresh operations to the associated entities. +- all : Cascades persist, remove, merge, refresh and detach operations to + associated entities. + +.. note:: + + Cascade operations are performed in memory. That means collections and related entities + are fetched into memory, even if they are still marked as lazy when + the cascade operation is about to be performed. However this approach allows + entity lifecycle events to be performed for each of these operations. + + However, pulling objects graph into memory on cascade can cause considerable performance + overhead, especially when cascading collections are large. Makes sure + to weigh the benefits and downsides of each cascade operation that you define. + + To rely on the database level cascade operations for the delete operation instead, you can + configure each join column with the **onDelete** option. See the respective + mapping driver chapters for more information. + +The following example is an extension to the User-Comment example +of this chapter. Suppose in our application a user is created +whenever he writes his first comment. In this case we would use the +following code: + +.. code-block:: php + + addComment($myFirstComment); + + $em->persist($user); + $em->persist($myFirstComment); + $em->flush(); + +Even if you *persist* a new User that contains our new Comment this +code would fail if you removed the call to +``EntityManager#persist($myFirstComment)``. Doctrine 2 does not +cascade the persist operation to all nested entities that are new +as well. + +More complicated is the deletion of all of a user's comments when he is +removed from the system: + +.. code-block:: php + + find('User', $deleteUserId); + + foreach ($user->getAuthoredComments() as $comment) { + $em->remove($comment); + } + $em->remove($user); + $em->flush(); + +Without the loop over all the authored comments Doctrine would use +an UPDATE statement only to set the foreign key to NULL and only +the User would be deleted from the database during the +flush()-Operation. + +To have Doctrine handle both cases automatically we can change the +``User#commentsAuthored`` property to cascade both the "persist" +and the "remove" operation. + +.. code-block:: php + + addresses = new ArrayCollection(); + } + + public function newStandingData(StandingData $sd) + { + $this->standingData = $sd; + } + + public function removeAddress($pos) + { + unset($this->addresses[$pos]); + } + } + +Now two examples of what happens when you remove the references: + +.. code-block:: php + + find("Addressbook\Contact", $contactId); + $contact->newStandingData(new StandingData("Firstname", "Lastname", "Street")); + $contact->removeAddress(1); + + $em->flush(); + +In this case you have not only changed the ``Contact`` entity itself but +you have also removed the references for standing data and as well as one +address reference. When flush is called not only are the references removed +but both the old standing data and the one address entity are also deleted +from the database. + +Filtering Collections +--------------------- + +.. filtering-collections: + +Collections have a filtering API that allows to slice parts of data from +a collection. If the collection has not been loaded from the database yet, +the filtering API can work on the SQL level to make optimized access to +large collections. + +.. code-block:: php + + find('Group', $groupId); + $userCollection = $group->getUsers(); + + $criteria = Criteria::create() + ->where(Criteria::expr()->eq("birthday", "1982-02-17")) + ->orderBy(array("username" => Criteria::ASC)) + ->setFirstResult(0) + ->setMaxResults(20) + ; + + $birthdayUsers = $userCollection->matching($criteria); + +.. tip:: + + You can move the access of slices of collections into dedicated methods of + an entity. For example ``Group#getTodaysBirthdayUsers()``. + +The Criteria has a limited matching language that works both on the +SQL and on the PHP collection level. This means you can use collection matching +interchangeably, independent of in-memory or sql-backed collections. + +.. code-block:: php + + find('CMS\Article', 1234); + $article->setHeadline('Hello World dude!'); + + $article2 = $entityManager->find('CMS\Article', 1234); + echo $article2->getHeadline(); + +In this case the Article is accessed from the entity manager twice, +but modified in between. Doctrine 2 realizes this and will only +ever give you access to one instance of the Article with ID 1234, +no matter how often do you retrieve it from the EntityManager and +even no matter what kind of Query method you are using (find, +Repository Finder or DQL). This is called "Identity Map" pattern, +which means Doctrine keeps a map of each entity and ids that have +been retrieved per PHP request and keeps returning you the same +instances. + +In the previous example the echo prints "Hello World dude!" to the +screen. You can even verify that ``$article`` and ``$article2`` are +indeed pointing to the same instance by running the following +code: + +.. code-block:: php + + comments = new ArrayCollection(); + } + + public function getAuthor() { return $this->author; } + public function getComments() { return $this->comments; } + } + + $article = $em->find('Article', 1); + +This code only retrieves the ``Article`` instance with id 1 executing +a single SELECT statement against the articles table in the database. +You can still access the associated properties author and comments +and the associated objects they contain. + +This works by utilizing the lazy loading pattern. Instead of +passing you back a real Author instance and a collection of +comments Doctrine will create proxy instances for you. Only if you +access these proxies for the first time they will go through the +EntityManager and load their state from the database. + +This lazy-loading process happens behind the scenes, hidden from +your code. See the following code: + +.. code-block:: php + + find('Article', 1); + + // accessing a method of the user instance triggers the lazy-load + echo "Author: " . $article->getAuthor()->getName() . "\n"; + + // Lazy Loading Proxies pass instanceof tests: + if ($article->getAuthor() instanceof User) { + // a User Proxy is a generated "UserProxy" class + } + + // accessing the comments as an iterator triggers the lazy-load + // retrieving ALL the comments of this article from the database + // using a single SELECT statement + foreach ($article->getComments() as $comment) { + echo $comment->getText() . "\n\n"; + } + + // Article::$comments passes instanceof tests for the Collection interface + // But it will NOT pass for the ArrayCollection interface + if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) { + echo "This will always be true!"; + } + +A slice of the generated proxy classes code looks like the +following piece of code. A real proxy class override ALL public +methods along the lines of the ``getName()`` method shown below: + +.. code-block:: php + + _load(); + return parent::getName(); + } + // .. other public methods of User + } + +.. warning:: + + Traversing the object graph for parts that are lazy-loaded will + easily trigger lots of SQL queries and will perform badly if used + to heavily. Make sure to use DQL to fetch-join all the parts of the + object-graph that you need as efficiently as possible. + + +Persisting entities +------------------- + +An entity can be made persistent by passing it to the +``EntityManager#persist($entity)`` method. By applying the persist +operation on some entity, that entity becomes MANAGED, which means +that its persistence is from now on managed by an EntityManager. As +a result the persistent state of such an entity will subsequently +be properly synchronized with the database when +``EntityManager#flush()`` is invoked. + +.. note:: + + Invoking the ``persist`` method on an entity does NOT + cause an immediate SQL INSERT to be issued on the database. + Doctrine applies a strategy called "transactional write-behind", + which means that it will delay most SQL commands until + ``EntityManager#flush()`` is invoked which will then issue all + necessary SQL statements to synchronize your objects with the + database in the most efficient way and a single, short transaction, + taking care of maintaining referential integrity. + + +Example: + +.. code-block:: php + + setName('Mr.Right'); + $em->persist($user); + $em->flush(); + +.. note:: + + Generated entity identifiers / primary keys are + guaranteed to be available after the next successful flush + operation that involves the entity in question. You can not rely on + a generated identifier to be available directly after invoking + ``persist``. The inverse is also true. You can not rely on a + generated identifier being not available after a failed flush + operation. + + +The semantics of the persist operation, applied on an entity X, are +as follows: + + +- If X is a new entity, it becomes managed. The entity X will be + entered into the database as a result of the flush operation. +- If X is a preexisting managed entity, it is ignored by the + persist operation. However, the persist operation is cascaded to + entities referenced by X, if the relationships from X to these + other entities are mapped with cascade=PERSIST or cascade=ALL (see + "Transitive Persistence"). +- If X is a removed entity, it becomes managed. +- If X is a detached entity, an exception will be thrown on + flush. + +Removing entities +----------------- + +An entity can be removed from persistent storage by passing it to +the ``EntityManager#remove($entity)`` method. By applying the +``remove`` operation on some entity, that entity becomes REMOVED, +which means that its persistent state will be deleted once +``EntityManager#flush()`` is invoked. + +.. note:: + + Just like ``persist``, invoking ``remove`` on an entity + does NOT cause an immediate SQL DELETE to be issued on the + database. The entity will be deleted on the next invocation of + ``EntityManager#flush()`` that involves that entity. This + means that entities scheduled for removal can still be queried + for and appear in query and collection results. See + the section on :ref:`Database and UnitOfWork Out-Of-Sync ` + for more information. + + +Example: + +.. code-block:: php + + remove($user); + $em->flush(); + +The semantics of the remove operation, applied to an entity X are +as follows: + + +- If X is a new entity, it is ignored by the remove operation. + However, the remove operation is cascaded to entities referenced by + X, if the relationship from X to these other entities is mapped + with cascade=REMOVE or cascade=ALL (see "Transitive Persistence"). +- If X is a managed entity, the remove operation causes it to + become removed. The remove operation is cascaded to entities + referenced by X, if the relationships from X to these other + entities is mapped with cascade=REMOVE or cascade=ALL (see + "Transitive Persistence"). +- If X is a detached entity, an InvalidArgumentException will be + thrown. +- If X is a removed entity, it is ignored by the remove operation. +- A removed entity X will be removed from the database as a result + of the flush operation. + +After an entity has been removed its in-memory state is the same as +before the removal, except for generated identifiers. + +Removing an entity will also automatically delete any existing +records in many-to-many join tables that link this entity. The +action taken depends on the value of the ``@joinColumn`` mapping +attribute "onDelete". Either Doctrine issues a dedicated ``DELETE`` +statement for records of each join table or it depends on the +foreign key semantics of onDelete="CASCADE". + +Deleting an object with all its associated objects can be achieved +in multiple ways with very different performance impacts. + + +1. If an association is marked as ``CASCADE=REMOVE`` Doctrine 2 + will fetch this association. If its a Single association it will + pass this entity to + ´EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()\`. + In both cases the cascade remove semantics are applied recursively. + For large object graphs this removal strategy can be very costly. +2. Using a DQL ``DELETE`` statement allows you to delete multiple + entities of a type with a single command and without hydrating + these entities. This can be very efficient to delete large object + graphs from the database. +3. Using foreign key semantics ``onDelete="CASCADE"`` can force the + database to remove all associated objects internally. This strategy + is a bit tricky to get right but can be very powerful and fast. You + should be aware however that using strategy 1 (``CASCADE=REMOVE``) + completely by-passes any foreign key ``onDelete=CASCADE`` option, + because Doctrine will fetch and remove all associated entities + explicitly nevertheless. + +Detaching entities +------------------ + +An entity is detached from an EntityManager and thus no longer +managed by invoking the ``EntityManager#detach($entity)`` method on +it or by cascading the detach operation to it. Changes made to the +detached entity, if any (including removal of the entity), will not +be synchronized to the database after the entity has been +detached. + +Doctrine will not hold on to any references to a detached entity. + +Example: + +.. code-block:: php + + detach($entity); + +The semantics of the detach operation, applied to an entity X are +as follows: + + +- If X is a managed entity, the detach operation causes it to + become detached. The detach operation is cascaded to entities + referenced by X, if the relationships from X to these other + entities is mapped with cascade=DETACH or cascade=ALL (see + "Transitive Persistence"). Entities which previously referenced X + will continue to reference X. +- If X is a new or detached entity, it is ignored by the detach + operation. +- If X is a removed entity, the detach operation is cascaded to + entities referenced by X, if the relationships from X to these + other entities is mapped with cascade=DETACH or cascade=ALL (see + "Transitive Persistence"). Entities which previously referenced X + will continue to reference X. + +There are several situations in which an entity is detached +automatically without invoking the ``detach`` method: + + +- When ``EntityManager#clear()`` is invoked, all entities that are + currently managed by the EntityManager instance become detached. +- When serializing an entity. The entity retrieved upon subsequent + unserialization will be detached (This is the case for all entities + that are serialized and stored in some cache, i.e. when using the + Query Result Cache). + +The ``detach`` operation is usually not as frequently needed and +used as ``persist`` and ``remove``. + +Merging entities +---------------- + +Merging entities refers to the merging of (usually detached) +entities into the context of an EntityManager so that they become +managed again. To merge the state of an entity into an +EntityManager use the ``EntityManager#merge($entity)`` method. The +state of the passed entity will be merged into a managed copy of +this entity and this copy will subsequently be returned. + +Example: + +.. code-block:: php + + merge($detachedEntity); + // $entity now refers to the fully managed copy returned by the merge operation. + // The EntityManager $em now manages the persistence of $entity as usual. + +.. note:: + + When you want to serialize/unserialize entities you + have to make all entity properties protected, never private. The + reason for this is, if you serialize a class that was a proxy + instance before, the private variables won't be serialized and a + PHP Notice is thrown. + + +The semantics of the merge operation, applied to an entity X, are +as follows: + + +- If X is a detached entity, the state of X is copied onto a + pre-existing managed entity instance X' of the same identity. +- If X is a new entity instance, a new managed copy X' will be + created and the state of X is copied onto this managed instance. +- If X is a removed entity instance, an InvalidArgumentException + will be thrown. +- If X is a managed entity, it is ignored by the merge operation, + however, the merge operation is cascaded to entities referenced by + relationships from X if these relationships have been mapped with + the cascade element value MERGE or ALL (see "Transitive + Persistence"). +- For all entities Y referenced by relationships from X having the + cascade element value MERGE or ALL, Y is merged recursively as Y'. + For all such Y referenced by X, X' is set to reference Y'. (Note + that if X is managed then X is the same object as X'.) +- If X is an entity merged to X', with a reference to another + entity Y, where cascade=MERGE or cascade=ALL is not specified, then + navigation of the same association from X' yields a reference to a + managed object Y' with the same persistent identity as Y. + +The ``merge`` operation will throw an ``OptimisticLockException`` +if the entity being merged uses optimistic locking through a +version field and the versions of the entity being merged and the +managed copy don't match. This usually means that the entity has +been modified while being detached. + +The ``merge`` operation is usually not as frequently needed and +used as ``persist`` and ``remove``. The most common scenario for +the ``merge`` operation is to reattach entities to an EntityManager +that come from some cache (and are therefore detached) and you want +to modify and persist such an entity. + +.. warning:: + + If you need to perform multiple merges of entities that share certain subparts + of their object-graphs and cascade merge, then you have to call ``EntityManager#clear()`` between the + successive calls to ``EntityManager#merge()``. Otherwise you might end up with + multiple copies of the "same" object in the database, however with different ids. + +.. note:: + + If you load some detached entities from a cache and you do + not need to persist or delete them or otherwise make use of them + without the need for persistence services there is no need to use + ``merge``. I.e. you can simply pass detached objects from a cache + directly to the view. + + +Synchronization with the Database +--------------------------------- + +The state of persistent entities is synchronized with the database +on flush of an ``EntityManager`` which commits the underlying +``UnitOfWork``. The synchronization involves writing any updates to +persistent entities and their relationships to the database. +Thereby bidirectional relationships are persisted based on the +references held by the owning side of the relationship as explained +in the Association Mapping chapter. + +When ``EntityManager#flush()`` is called, Doctrine inspects all +managed, new and removed entities and will perform the following +operations. + +.. _workingobjects_database_uow_outofsync: + +Effects of Database and UnitOfWork being Out-Of-Sync +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As soon as you begin to change the state of entities, call persist or remove the +contents of the UnitOfWork and the database will drive out of sync. They can +only be synchronized by calling ``EntityManager#flush()``. This section +describes the effects of database and UnitOfWork being out of sync. + +- Entities that are scheduled for removal can still be queried from the database. + They are returned from DQL and Repository queries and are visible in collections. +- Entities that are passed to ``EntityManager#persist`` do not turn up in query + results. +- Entities that have changed will not be overwritten with the state from the database. + This is because the identity map will detect the construction of an already existing + entity and assumes its the most up to date version. + +``EntityManager#flush()`` is never called implicitly by Doctrine. You always have to trigger it manually. + +Synchronizing New and Managed Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The flush operation applies to a managed entity with the following +semantics: + + +- The entity itself is synchronized to the database using a SQL + UPDATE statement, only if at least one persistent field has + changed. +- No SQL updates are executed if the entity did not change. + +The flush operation applies to a new entity with the following +semantics: + + +- The entity itself is synchronized to the database using a SQL + INSERT statement. + +For all (initialized) relationships of the new or managed entity +the following semantics apply to each associated entity X: + + +- If X is new and persist operations are configured to cascade on + the relationship, X will be persisted. +- If X is new and no persist operations are configured to cascade + on the relationship, an exception will be thrown as this indicates + a programming error. +- If X is removed and persist operations are configured to cascade + on the relationship, an exception will be thrown as this indicates + a programming error (X would be re-persisted by the cascade). +- If X is detached and persist operations are configured to + cascade on the relationship, an exception will be thrown (This is + semantically the same as passing X to persist()). + +Synchronizing Removed Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The flush operation applies to a removed entity by deleting its +persistent state from the database. No cascade options are relevant +for removed entities on flush, the cascade remove option is already +executed during ``EntityManager#remove($entity)``. + +The size of a Unit of Work +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The size of a Unit of Work mainly refers to the number of managed +entities at a particular point in time. + +The cost of flushing +~~~~~~~~~~~~~~~~~~~~ + +How costly a flush operation is, mainly depends on two factors: + + +- The size of the EntityManager's current UnitOfWork. +- The configured change tracking policies + +You can get the size of a UnitOfWork as follows: + +.. code-block:: php + + getUnitOfWork()->size(); + +The size represents the number of managed entities in the Unit of +Work. This size affects the performance of flush() operations due +to change tracking (see "Change Tracking Policies") and, of course, +memory consumption, so you may want to check it from time to time +during development. + +.. note:: + + Do not invoke ``flush`` after every change to an entity + or every single invocation of persist/remove/merge/... This is an + anti-pattern and unnecessarily reduces the performance of your + application. Instead, form units of work that operate on your + objects and call ``flush`` when you are done. While serving a + single HTTP request there should be usually no need for invoking + ``flush`` more than 0-2 times. + + +Direct access to a Unit of Work +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get direct access to the Unit of Work by calling +``EntityManager#getUnitOfWork()``. This will return the UnitOfWork +instance the EntityManager is currently using. + +.. code-block:: php + + getUnitOfWork(); + +.. note:: + + Directly manipulating a UnitOfWork is not recommended. + When working directly with the UnitOfWork API, respect methods + marked as INTERNAL by not using them and carefully read the API + documentation. + + +Entity State +~~~~~~~~~~~~ + +As outlined in the architecture overview an entity can be in one of +four possible states: NEW, MANAGED, REMOVED, DETACHED. If you +explicitly need to find out what the current state of an entity is +in the context of a certain ``EntityManager`` you can ask the +underlying ``UnitOfWork``: + +.. code-block:: php + + getUnitOfWork()->getEntityState($entity)) { + case UnitOfWork::STATE_MANAGED: + ... + case UnitOfWork::STATE_REMOVED: + ... + case UnitOfWork::STATE_DETACHED: + ... + case UnitOfWork::STATE_NEW: + ... + } + +An entity is in MANAGED state if it is associated with an +``EntityManager`` and it is not REMOVED. + +An entity is in REMOVED state after it has been passed to +``EntityManager#remove()`` until the next flush operation of the +same EntityManager. A REMOVED entity is still associated with an +``EntityManager`` until the next flush operation. + +An entity is in DETACHED state if it has persistent state and +identity but is currently not associated with an +``EntityManager``. + +An entity is in NEW state if has no persistent state and identity +and is not associated with an ``EntityManager`` (for example those +just created via the "new" operator). + +Querying +-------- + +Doctrine 2 provides the following ways, in increasing level of +power and flexibility, to query for persistent objects. You should +always start with the simplest one that suits your needs. + +By Primary Key +~~~~~~~~~~~~~~ + +The most basic way to query for a persistent object is by its +identifier / primary key using the +``EntityManager#find($entityName, $id)`` method. Here is an +example: + +.. code-block:: php + + find('MyProject\Domain\User', $id); + +The return value is either the found entity instance or null if no +instance could be found with the given identifier. + +Essentially, ``EntityManager#find()`` is just a shortcut for the +following: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->find($id); + +``EntityManager#getRepository($entityName)`` returns a repository +object which provides many ways to retrieve entities of the +specified type. By default, the repository instance is of type +``Doctrine\ORM\EntityRepository``. You can also use custom +repository classes as shown later. + +By Simple Conditions +~~~~~~~~~~~~~~~~~~~~ + +To query for one or more entities based on several conditions that +form a logical conjunction, use the ``findBy`` and ``findOneBy`` +methods on a repository as follows: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => 20)); + + // All users that are 20 years old and have a surname of 'Miller' + $users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller')); + + // A single user by its nickname + $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb')); + +You can also load by owning side associations through the repository: + +.. code-block:: php + + find('MyProject\Domain\Phonenumber', 1234); + $user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId())); + +Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself. + +The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0); + +If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findBy(array('age' => array(20, 30, 40))); + // translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40) + +An EntityRepository also provides a mechanism for more concise +calls through its use of ``__call``. Thus, the following two +examples are equivalent: + +.. code-block:: php + + getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb')); + + // A single user by its nickname (__call magic) + $user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb'); + +By Criteria +~~~~~~~~~~~ + +.. versionadded:: 2.3 + +The Repository implement the ``Doctrine\Common\Collections\Selectable`` +interface. That means you can build ``Doctrine\Common\Collections\Criteria`` +and pass them to the ``matching($criteria)`` method. + +See the :ref:`Working with Associations: Filtering collections +`. + +By Eager Loading +~~~~~~~~~~~~~~~~ + +Whenever you query for an entity that has persistent associations +and these associations are mapped as EAGER, they will automatically +be loaded together with the entity being queried and is thus +immediately available to your application. + +By Lazy Loading +~~~~~~~~~~~~~~~ + +Whenever you have a managed entity instance at hand, you can +traverse and use any associations of that entity that are +configured LAZY as if they were in-memory already. Doctrine will +automatically load the associated objects on demand through the +concept of lazy-loading. + +By DQL +~~~~~~ + +The most powerful and flexible method to query for persistent +objects is the Doctrine Query Language, an object query language. +DQL enables you to query for persistent objects in the language of +objects. DQL understands classes, fields, inheritance and +associations. DQL is syntactically very similar to the familiar SQL +but *it is not SQL*. + +A DQL query is represented by an instance of the +``Doctrine\ORM\Query`` class. You create a query using +``EntityManager#createQuery($dql)``. Here is a simple example: + +.. code-block:: php + + createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30"); + $users = $q->getResult(); + +Note that this query contains no knowledge about the relational +schema, only about the object model. DQL supports positional as +well as named parameters, many functions, (fetch) joins, +aggregates, subqueries and much more. Detailed information about +DQL and its syntax as well as the Doctrine class can be found in +:doc:`the dedicated chapter `. +For programmatically building up queries based on conditions that +are only known at runtime, Doctrine provides the special +``Doctrine\ORM\QueryBuilder`` class. More information on +constructing queries with a QueryBuilder can be found +:doc:`in Query Builder chapter `. + +By Native Queries +~~~~~~~~~~~~~~~~~ + +As an alternative to DQL or as a fallback for special SQL +statements native queries can be used. Native queries are built by +using a hand-crafted SQL query and a ResultSetMapping that +describes how the SQL result set should be transformed by Doctrine. +More information about native queries can be found in +:doc:`the dedicated chapter `. + +Custom Repositories +~~~~~~~~~~~~~~~~~~~ + +By default the EntityManager returns a default implementation of +``Doctrine\ORM\EntityRepository`` when you call +``EntityManager#getRepository($entityClass)``. You can overwrite +this behaviour by specifying the class name of your own Entity +Repository in the Annotation, XML or YAML metadata. In large +applications that require lots of specialized DQL queries using a +custom repository is one recommended way of grouping these queries +in a central location. + +.. code-block:: php + + _em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"') + ->getResult(); + } + } + +You can access your repository now by calling: + +.. code-block:: php + + getRepository('MyDomain\Model\User')->getAllAdminUsers(); + + diff --git a/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst b/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst new file mode 100644 index 0000000..dbf97dc --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/xml-mapping.rst @@ -0,0 +1,782 @@ +XML Mapping +=========== + +The XML mapping driver enables you to provide the ORM metadata in +form of XML documents. + +The XML driver is backed by an XML Schema document that describes +the structure of a mapping document. The most recent version of the +XML Schema document is available online at +`http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd `_. +In order to point to the latest version of the document of a +particular stable release branch, just append the release number, +i.e.: doctrine-mapping-2.0.xsd The most convenient way to work with +XML mapping files is to use an IDE/editor that can provide +code-completion based on such an XML Schema document. The following +is an outline of a XML mapping document with the proper xmlns/xsi +setup for the latest code in trunk. + +.. code-block:: xml + + + + ... + + + +The XML mapping document of a class is loaded on-demand the first +time it is requested and subsequently stored in the metadata cache. +In order to work, this requires certain conventions: + + +- Each entity/mapped superclass must get its own dedicated XML + mapping document. +- The name of the mapping document must consist of the fully + qualified name of the class, where namespace separators are + replaced by dots (.). For example an Entity with the fully + qualified class-name "MyProject" would require a mapping file + "MyProject.Entities.User.dcm.xml" unless the extension is changed. +- All mapping documents should get the extension ".dcm.xml" to + identify it as a Doctrine mapping file. This is more of a + convention and you are not forced to do this. You can change the + file extension easily enough. + +.. code-block:: php + + setFileExtension('.xml'); + +It is recommended to put all XML mapping documents in a single +folder but you can spread the documents over several folders if you +want to. In order to tell the XmlDriver where to look for your +mapping documents, supply an array of paths as the first argument +of the constructor, like this: + +.. code-block:: php + + setMetadataDriverImpl($driver); + +.. warning:: + + Note that Doctrine ORM does not modify any settings for ``libxml``, + therefore, external XML entities may or may not be enabled or + configured correctly. + XML mappings are not XXE/XEE attack vectors since they are not + related with user input, but it is recommended that you do not + use external XML entities in your mapping files to avoid running + into unexpected behaviour. + +Simplified XML Driver +~~~~~~~~~~~~~~~~~~~~~ + +The Symfony project sponsored a driver that simplifies usage of the XML Driver. +The changes between the original driver are: + +1. File Extension is .orm.xml +2. Filenames are shortened, "MyProject\Entities\User" will become User.orm.xml +3. You can add a global file and add multiple entities in this file. + +Configuration of this client works a little bit different: + +.. code-block:: php + + 'MyProject\Entities', + '/path/to/files2' => 'OtherProject\Entities' + ); + $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver($namespaces); + $driver->setGlobalBasename('global'); // global.orm.xml + +Example +------- + +As a quick start, here is a small example document that makes use +of several common elements: + +.. code-block:: xml + + // Doctrine.Tests.ORM.Mapping.User.dcm.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Be aware that class-names specified in the XML files should be +fully qualified. + +XML-Element Reference +--------------------- + +The XML-Element reference explains all the tags and attributes that +the Doctrine Mapping XSD Schema defines. You should read the +Basic-, Association- and Inheritance Mapping chapters to understand +what each of this definitions means in detail. + +Defining an Entity +~~~~~~~~~~~~~~~~~~ + +Each XML Mapping File contains the definition of one entity, +specified as the ```` element as a direct child of the +```` element: + +.. code-block:: xml + + + + + + + +Required attributes: + + +- name - The fully qualified class-name of the entity. + +Optional attributes: + + +- **table** - The Table-Name to be used for this entity. Otherwise the + Unqualified Class-Name is used by default. +- **repository-class** - The fully qualified class-name of an + alternative ``Doctrine\ORM\EntityRepository`` implementation to be + used with this entity. +- **inheritance-type** - The type of inheritance, defaults to none. A + more detailed description follows in the + *Defining Inheritance Mappings* section. +- **read-only** - (>= 2.1) Specifies that this entity is marked as read only and not + considered for change-tracking. Entities of this type can be persisted + and removed though. +- **schema** - (>= 2.5) The schema the table lies in, for platforms that support schemas + +Defining Fields +~~~~~~~~~~~~~~~ + +Each entity class can contain zero to infinite fields that are +managed by Doctrine. You can define them using the ```` +element as a children to the ```` element. The field +element is only used for primitive types that are not the ID of the +entity. For the ID mapping you have to use the ```` element. + +.. code-block:: xml + + + + + + + + + + + + + + + + +Required attributes: + + +- name - The name of the Property/Field on the given Entity PHP + class. + +Optional attributes: + + +- type - The ``Doctrine\DBAL\Types\Type`` name, defaults to + "string" +- column - Name of the column in the database, defaults to the + field name. +- length - The length of the given type, for use with strings + only. +- unique - Should this field contain a unique value across the + table? Defaults to false. +- nullable - Should this field allow NULL as a value? Defaults to + false. +- version - Should this field be used for optimistic locking? Only + works on fields with type integer or datetime. +- scale - Scale of a decimal type. +- precision - Precision of a decimal type. +- options - Array of additional options: + + - default - The default value to set for the column if no value + is supplied. + - unsigned - Boolean value to determine if the column should + be capable of representing only non-negative integers + (applies only for integer column and might not be supported by + all vendors). + - fixed - Boolean value to determine if the specified length of + a string column should be fixed or varying (applies only for + string/binary column and might not be supported by all vendors). + - comment - The comment of the column in the schema (might not + be supported by all vendors). + - customSchemaOptions - Array of additional schema options + which are mostly vendor specific. +- column-definition - Optional alternative SQL representation for + this column. This definition begin after the field-name and has to + specify the complete column definition. Using this feature will + turn this field dirty for Schema-Tool update commands at all + times. + +.. note:: + + For more detailed information on each attribute, please refer to + the DBAL ``Schema-Representation`` documentation. + +Defining Identity and Generator Strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An entity has to have at least one ```` element. For +composite keys you can specify more than one id-element, however +surrogate keys are recommended for use with Doctrine 2. The Id +field allows to define properties of the identifier and allows a +subset of the ```` element attributes: + +.. code-block:: xml + + + + + +Required attributes: + + +- name - The name of the Property/Field on the given Entity PHP + class. +- type - The ``Doctrine\DBAL\Types\Type`` name, preferably + "string" or "integer". + +Optional attributes: + + +- column - Name of the column in the database, defaults to the + field name. + +Using the simplified definition above Doctrine will use no +identifier strategy for this entity. That means you have to +manually set the identifier before calling +``EntityManager#persist($entity)``. This is the so called +``ASSIGNED`` strategy. + +If you want to switch the identifier generation strategy you have +to nest a ```` element inside the id-element. This of +course only works for surrogate keys. For composite keys you always +have to use the ``ASSIGNED`` strategy. + +.. code-block:: xml + + + + + + + +The following values are allowed for the ```` strategy +attribute: + + +- AUTO - Automatic detection of the identifier strategy based on + the preferred solution of the database vendor. +- IDENTITY - Use of a IDENTIFY strategy such as Auto-Increment IDs + available to Doctrine AFTER the INSERT statement has been executed. +- SEQUENCE - Use of a database sequence to retrieve the + entity-ids. This is possible before the INSERT statement is + executed. + +If you are using the SEQUENCE strategy you can define an additional +element to describe the sequence: + +.. code-block:: xml + + + + + + + + +Required attributes for ````: + + +- sequence-name - The name of the sequence + +Optional attributes for ````: + + +- allocation-size - By how much steps should the sequence be + incremented when a value is retrieved. Defaults to 1 +- initial-value - What should the initial value of the sequence + be. + + **NOTE** + + If you want to implement a cross-vendor compatible application you + have to specify and additionally define the + element, if Doctrine chooses the sequence strategy for a + platform. + + +Defining a Mapped Superclass +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you want to define a class that multiple entities inherit +from, which itself is not an entity however. The chapter on +*Inheritance Mapping* describes a Mapped Superclass in detail. You +can define it in XML using the ```` tag. + +.. code-block:: xml + + + + + + + + +Required attributes: + + +- name - Class name of the mapped superclass. + +You can nest any number of ```` and unidirectional +```` or ```` associations inside a +mapped superclass. + +Defining Inheritance Mappings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are currently two inheritance persistence strategies that you +can choose from when defining entities that inherit from each +other. Single Table inheritance saves the fields of the complete +inheritance hierarchy in a single table, joined table inheritance +creates a table for each entity combining the fields using join +conditions. + +You can specify the inheritance type in the ```` element +and then use the ```` and +```` attributes. + +.. code-block:: xml + + + + + + + + + + +The allowed values for inheritance-type attribute are ``JOINED`` or +``SINGLE_TABLE``. + +.. note:: + + All inheritance related definitions have to be defined on the root + entity of the hierarchy. + + +Defining Lifecycle Callbacks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define the lifecycle callback methods on your entities +using the ```` element: + +.. code-block:: xml + + + + + + + + +Defining One-To-One Relations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define One-To-One Relations/Associations using the +```` element. The required and optional attributes +depend on the associations being on the inverse or owning side. + +For the inverse side the mapping is as simple as: + +.. code-block:: xml + + + + + +Required attributes for inverse One-To-One: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! +- mapped-by - Name of the field on the owning side (here Address + entity) that contains the owning side association. + +For the owning side this mapping would look like: + +.. code-block:: xml + + + + + +Required attributes for owning One-to-One: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes for owning One-to-One: + + +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- orphan-removal - If true, the inverse side entity is always + deleted when the owning side entity is. Defaults to false. +- fetch - Either LAZY or EAGER, defaults to LAZY. This attribute + makes only sense on the owning side, the inverse side *ALWAYS* has + to use the ``FETCH`` strategy. + +The definition for the owning side relies on a bunch of mapping +defaults for the join column names. Without the nested +```` element Doctrine assumes to foreign key to be +called ``user_id`` on the Address Entities table. This is because +the ``MyProject\Address`` entity is the owning side of this +association, which means it contains the foreign key. + +The completed explicitly defined mapping is: + +.. code-block:: xml + + + + + + + +Defining Many-To-One Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The many-to-one association is *ALWAYS* the owning side of any +bidirectional association. This simplifies the mapping compared to +the one-to-one case. The minimal mapping for this association looks +like: + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- orphan-removal - If true the entity on the inverse side is + always deleted when the owning side entity is and it is not + connected to any other owning side entity anymore. Defaults to + false. +- fetch - Either LAZY or EAGER, defaults to LAZY. + +This definition relies on a bunch of mapping defaults with regards +to the naming of the join-column/foreign key. The explicitly +defined mapping includes a ```` tag nested inside +the many-to-one association tag: + +.. code-block:: xml + + + + + + + +The join-column attribute ``name`` specifies the column name of the +foreign key and the ``referenced-column-name`` attribute specifies +the name of the primary key column on the User entity. + +Defining One-To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The one-to-many association is *ALWAYS* the inverse side of any +association. There exists no such thing as a uni-directional +one-to-many association, which means this association only ever +exists for bi-directional associations. + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! +- mapped-by - Name of the field on the owning side (here + Phonenumber entity) that contains the owning side association. + +Optional attributes: + + +- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY. +- index-by: Index the collection by a field on the target entity. + +Defining Many-To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +From all the associations the many-to-many has the most complex +definition. When you rely on the mapping defaults you can omit many +definitions and rely on their implicit values. + +.. code-block:: xml + + + + + +Required attributes: + + +- field - Name of the property/field on the entity's PHP class. +- target-entity - Name of the entity associated entity class. If + this is not qualified the namespace of the current class is + prepended. *IMPORTANT:* No leading backslash! + +Optional attributes: + + +- mapped-by - Name of the field on the owning side that contains + the owning side association if the defined many-to-many association + is on the inverse side. +- inversed-by - If the association is bidirectional the + inversed-by attribute has to be specified with the name of the + field on the inverse entity that contains the back-reference. +- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY. +- index-by: Index the collection by a field on the target entity. + +The mapping defaults would lead to a join-table with the name +"User\_Group" being created that contains two columns "user\_id" +and "group\_id". The explicit definition of this mapping would be: + +.. code-block:: xml + + + + + + + + + + + + + + +Here both the ```` and ```` +tags are necessary to tell Doctrine for which side the specified +join-columns apply. These are nested inside a ```` +attribute which allows to specify the table name of the +many-to-many join-table. + +Cascade Element +~~~~~~~~~~~~~~~ + +Doctrine allows cascading of several UnitOfWork operations to +related entities. You can specify the cascade operations in the +```` element inside any of the association mapping +tags. + +.. code-block:: xml + + + + + + + + + +Besides ```` the following operations can be +specified by their respective tags: + + +- ```` +- ```` +- ```` +- ```` + +Join Column Element +~~~~~~~~~~~~~~~~~~~ + +In any explicitly defined association mapping you will need the +```` tag. It defines how the foreign key and primary +key names are called that are used for joining two entities. + +Required attributes: + + +- name - The column name of the foreign key. +- referenced-column-name - The column name of the associated + entities primary key + +Optional attributes: + + +- unique - If the join column should contain a UNIQUE constraint. + This makes sense for Many-To-Many join-columns only to simulate a + one-to-many unidirectional using a join-table. +- nullable - should the join column be nullable, defaults to true. +- on-delete - Foreign Key Cascade action to perform when entity is + deleted, defaults to NO ACTION/RESTRICT but can be set to + "CASCADE". + +Defining Order of To-Many Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can require one-to-many or many-to-many associations to be +retrieved using an additional ``ORDER BY``. + +.. code-block:: xml + + + + + + + + + +Defining Indexes or Unique Constraints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To define additional indexes or unique constraints on the entities +table you can use the ```` and +```` elements: + +.. code-block:: xml + + + + + + + + + + + + + +You have to specify the column and not the entity-class field names +in the index and unique-constraint definitions. + +Derived Entities ID syntax +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the primary key of an entity contains a foreign key to another entity we speak of a derived +entity relationship. You can define this in XML with the "association-key" attribute in the ```` tag. + +.. code-block:: xml + + + + + + + + + + + + + diff --git a/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst b/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst new file mode 100644 index 0000000..ea54e27 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/reference/yaml-mapping.rst @@ -0,0 +1,154 @@ +YAML Mapping +============ + +The YAML mapping driver enables you to provide the ORM metadata in +form of YAML documents. + +The YAML mapping document of a class is loaded on-demand the first +time it is requested and subsequently stored in the metadata cache. +In order to work, this requires certain conventions: + + +- Each entity/mapped superclass must get its own dedicated YAML + mapping document. +- The name of the mapping document must consist of the fully + qualified name of the class, where namespace separators are + replaced by dots (.). +- All mapping documents should get the extension ".dcm.yml" to + identify it as a Doctrine mapping file. This is more of a + convention and you are not forced to do this. You can change the + file extension easily enough. + +.. code-block:: php + + setFileExtension('.yml'); + +It is recommended to put all YAML mapping documents in a single +folder but you can spread the documents over several folders if you +want to. In order to tell the YamlDriver where to look for your +mapping documents, supply an array of paths as the first argument +of the constructor, like this: + +.. code-block:: php + + setMetadataDriverImpl($driver); + +Simplified YAML Driver +~~~~~~~~~~~~~~~~~~~~~~ + +The Symfony project sponsored a driver that simplifies usage of the YAML Driver. +The changes between the original driver are: + +- File Extension is .orm.yml +- Filenames are shortened, "MyProject\\Entities\\User" will become User.orm.yml +- You can add a global file and add multiple entities in this file. + +Configuration of this client works a little bit different: + +.. code-block:: php + + 'MyProject\Entities', + '/path/to/files2' => 'OtherProject\Entities' + ); + $driver = new \Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver($namespaces); + $driver->setGlobalBasename('global'); // global.orm.yml + +Example +------- + +As a quick start, here is a small example document that makes use +of several common elements: + +.. code-block:: yaml + + # Doctrine.Tests.ORM.Mapping.User.dcm.yml + Doctrine\Tests\ORM\Mapping\User: + type: entity + repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository + table: cms_users + schema: schema_name # The schema the table lies in, for platforms that support schemas (Optional, >= 2.5) + readOnly: true + indexes: + name_index: + columns: [ name ] + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + length: 50 + email: + type: string + length: 32 + column: user_email + unique: true + options: + fixed: true + comment: User's email address + loginCount: + type: integer + column: login_count + nullable: false + options: + unsigned: true + default: 0 + oneToOne: + address: + targetEntity: Address + joinColumn: + name: address_id + referencedColumnName: id + onDelete: CASCADE + oneToMany: + phonenumbers: + targetEntity: Phonenumber + mappedBy: user + cascade: ["persist", "merge"] + manyToMany: + groups: + targetEntity: Group + joinTable: + name: cms_users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + lifecycleCallbacks: + prePersist: [ doStuffOnPrePersist, doOtherStuffOnPrePersistToo ] + postPersist: [ doStuffOnPostPersist ] + +Be aware that class-names specified in the YAML files should be +fully qualified. + +Reference +~~~~~~~~~~~~~~~~~~~~~~ + +Unique Constraints +------------------ + +It is possible to define unique constraints by the following declaration: + +.. code-block:: yaml + + # ECommerceProduct.orm.yml + ECommerceProduct: + type: entity + fields: + # definition of some fields + uniqueConstraints: + search_idx: + columns: [ name, email ] + diff --git a/vendor/doctrine/orm/docs/en/toc.rst b/vendor/doctrine/orm/docs/en/toc.rst new file mode 100644 index 0000000..0df7041 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/toc.rst @@ -0,0 +1,87 @@ +Welcome to Doctrine 2 ORM's documentation! +========================================== + +Tutorials +--------- + +.. toctree:: + :maxdepth: 1 + + tutorials/getting-started + tutorials/getting-started-database + tutorials/getting-started-models + tutorials/working-with-indexed-associations + tutorials/extra-lazy-associations + tutorials/composite-primary-keys + tutorials/ordered-associations + tutorials/override-field-association-mappings-in-subclasses + tutorials/pagination.rst + tutorials/embeddables.rst + +Reference Guide +--------------- + +.. toctree:: + :maxdepth: 1 + :numbered: + + reference/architecture + reference/configuration.rst + reference/faq + reference/basic-mapping + reference/association-mapping + reference/inheritance-mapping + reference/working-with-objects + reference/working-with-associations + reference/events + reference/unitofwork + reference/unitofwork-associations + reference/transactions-and-concurrency + reference/batch-processing + reference/dql-doctrine-query-language + reference/query-builder + reference/native-sql + reference/change-tracking-policies + reference/partial-objects + reference/xml-mapping + reference/yaml-mapping + reference/annotations-reference + reference/php-mapping + reference/caching + reference/improving-performance + reference/tools + reference/metadata-drivers + reference/best-practices + reference/limitations-and-known-issues + tutorials/pagination + reference/filters + reference/namingstrategy + reference/advanced-configuration + reference/second-level-cache + reference/security + + +Cookbook +-------- + +.. toctree:: + :maxdepth: 1 + + cookbook/aggregate-fields + cookbook/custom-mapping-types + cookbook/decorator-pattern + cookbook/dql-custom-walkers + cookbook/dql-user-defined-functions + cookbook/implementing-arrayaccess-for-domain-objects + cookbook/implementing-the-notify-changetracking-policy + cookbook/implementing-wakeup-or-clone + cookbook/integrating-with-codeigniter + cookbook/resolve-target-entity-listener + cookbook/sql-table-prefixes + cookbook/strategy-cookbook-introduction + cookbook/validation-of-entities + cookbook/working-with-datetime + cookbook/mysql-enums + cookbook/advanced-field-value-conversion-using-custom-mapping-types + cookbook/entities-in-session + diff --git a/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst b/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst new file mode 100644 index 0000000..dd4e49e --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst @@ -0,0 +1,375 @@ +Composite and Foreign Keys as Primary Key +========================================= + +.. versionadded:: 2.1 + +Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept +and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases. +For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as +primary keys are supported. + +This tutorial shows how the semantics of composite primary keys work and how they map to the database. + +General Considerations +~~~~~~~~~~~~~~~~~~~~~~ + +Every entity with a composite key cannot use an id generator other than "ASSIGNED". That means +the ID fields have to have their values set before you call ``EntityManager#persist($entity)``. + +Primitive Types only +~~~~~~~~~~~~~~~~~~~~ + +Even in version 2.0 you can have composite keys as long as they only consist of the primitive types +``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name +and year of production as primary keys: + +.. configuration-block:: + + .. code-block:: php + + name = $name; + $this->year = $year; + } + + public function getModelName() + { + return $this->name; + } + + public function getYearOfProduction() + { + return $this->year; + } + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: yaml + + VehicleCatalogue\Model\Car: + type: entity + id: + name: + type: string + year: + type: integer + +Now you can use this entity: + +.. code-block:: php + + persist($car); + $em->flush(); + +And for querying you can use arrays to both DQL and EntityRepositories: + +.. code-block:: php + + find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010)); + + $dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1"; + $audi = $em->createQuery($dql) + ->setParameter(1, array("name" => "Audi A8", "year" => 2010)) + ->getSingleResult(); + +You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name`` +and to ``year`` to the related entities. + +.. note:: + + This example shows how you can nicely solve the requirement for existing + values before ``EntityManager#persist()``: By adding them as mandatory values for the constructor. + +Identity through foreign Entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + Identity through foreign entities is only supported with Doctrine 2.1 + +There are tons of use-cases where the identity of an Entity should be determined by the entity +of one or many parent entities. + +- Dynamic Attributes of an Entity (for example Article). Each Article has many + attributes with primary key "article_id" and "attribute_name". +- Address object of a Person, the primary key of the address is "user_id". This is not a case of a composite primary + key, but the identity is derived through a foreign entity and a foreign key. +- Join Tables with metadata can be modelled as Entity, for example connections between two articles + with a little description and a score. + +The semantics of mapping identity through foreign entities are easy: + +- Only allowed on Many-To-One or One-To-One associations. +- Plug an ``@Id`` annotation onto every association. +- Set an attribute ``association-key`` with the field name of the association in XML. +- Set a key ``associationKey:`` with the field name of the association in YAML. + +Use-Case 1: Dynamic Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We keep up the example of an Article with arbitrary attributes, the mapping looks like this: + +.. configuration-block:: + + .. code-block:: php + + attributes[$name] = new ArticleAttribute($name, $value, $this); + } + } + + /** + * @Entity + */ + class ArticleAttribute + { + /** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */ + private $article; + + /** @Id @Column(type="string") */ + private $attribute; + + /** @Column(type="string") */ + private $value; + + public function __construct($name, $value, $article) + { + $this->attribute = $name; + $this->value = $value; + $this->article = $article; + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: yaml + + Application\Model\ArticleAttribute: + type: entity + id: + article: + associationKey: true + attribute: + type: string + fields: + value: + type: string + manyToOne: + article: + targetEntity: Article + inversedBy: attributes + + +Use-Case 2: Simple Derived Identity +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you have the requirement that two objects are related by a One-To-One association +and that the dependent class should re-use the primary key of the class it depends on. +One good example for this is a user-address relationship: + +.. configuration-block:: + + .. code-block:: php + + customer = $customer; + $this->items = new ArrayCollection(); + $this->created = new \DateTime("now"); + } + } + + /** @Entity */ + class Product + { + /** @Id @Column(type="integer") @GeneratedValue */ + private $id; + + /** @Column(type="string") */ + private $name; + + /** @Column(type="decimal") */ + private $currentPrice; + + public function getCurrentPrice() + { + return $this->currentPrice; + } + } + + /** @Entity */ + class OrderItem + { + /** @Id @ManyToOne(targetEntity="Order") */ + private $order; + + /** @Id @ManyToOne(targetEntity="Product") */ + private $product; + + /** @Column(type="integer") */ + private $amount = 1; + + /** @Column(type="decimal") */ + private $offeredPrice; + + public function __construct(Order $order, Product $product, $amount = 1) + { + $this->order = $order; + $this->product = $product; + $this->offeredPrice = $product->getCurrentPrice(); + } + } + + +Performance Considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using composite keys always comes with a performance hit compared to using entities with +a simple surrogate key. This performance impact is mostly due to additional PHP code that is +necessary to handle this kind of keys, most notably when using derived identifiers. + +On the SQL side there is not much overhead as no additional or unexpected queries have to be +executed to manage entities with derived foreign keys. diff --git a/vendor/doctrine/orm/docs/en/tutorials/embeddables.rst b/vendor/doctrine/orm/docs/en/tutorials/embeddables.rst new file mode 100644 index 0000000..60265d6 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/embeddables.rst @@ -0,0 +1,162 @@ +Separating Concerns using Embeddables +------------------------------------- + +Embeddables are classes which are not entities themself, but are embedded +in entities and can also be queried in DQL. You'll mostly want to use them +to reduce duplication or separating concerns. Value objects such as date range +or address are the primary use case for this feature. Embeddables can only +contain properties with basic ``@Column`` mapping. + +For the purposes of this tutorial, we will assume that you have a ``User`` +class in your application and you would like to store an address in +the ``User`` class. We will model the ``Address`` class as an embeddable +instead of simply adding the respective columns to the ``User`` class. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + embedded: + address: + class: Address + + Address: + type: embeddable + fields: + street: { type: string } + postalCode: { type: string } + city: { type: string } + country: { type: string } + +In terms of your database schema, Doctrine will automatically inline all +columns from the ``Address`` class into the table of the ``User`` class, +just as if you had declared them directly there. + +Column Prefixing +---------------- + +By default, Doctrine names your columns by prefixing them, using the value +object name. + +Following the example above, your columns would be named as ``address_street``, +``address_postalCode``... + +You can change this behaviour to meet your needs by changing the +``columnPrefix`` attribute in the ``@Embedded`` notation. + +The following example shows you how to set your prefix to ``myPrefix_``: + +.. configuration-block:: + + .. code-block:: php + + + + + + .. code-block:: yaml + + User: + type: entity + embedded: + address: + class: Address + columnPrefix: myPrefix_ + +To have Doctrine drop the prefix and use the value object's property name +directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML): + +.. configuration-block:: + + .. code-block:: php + + + + + + +DQL +--- + +You can also use mapped fields of embedded classes in DQL queries, just +as if they were declared in the ``User`` class: + +.. code-block:: sql + + SELECT u FROM User u WHERE u.address.city = :myCity + diff --git a/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst new file mode 100644 index 0000000..456c8c1 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst @@ -0,0 +1,86 @@ +Extra Lazy Associations +======================= + +.. versionadded:: 2.1 + +In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. +where posts can be commented, you always have to assume that a post draws hundreds of comments. +In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This +can lead to pretty serious performance problems, if your associations contain several hundreds or thousands +of entities. + +With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations +are marked as **Lazy** by default, which means the whole collection object for an association is populated +the first time its accessed. If you mark an association as extra lazy the following methods on collections +can be called without triggering a full load of the collection: + +- ``Collection#contains($entity)`` +- ``Collection#containsKey($key)`` (available with Doctrine 2.5) +- ``Collection#count()`` +- ``Collection#get($key)`` (available with Doctrine 2.4) +- ``Collection#slice($offset, $length = null)`` + +For each of the above methods the following semantics apply: + +- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database. +- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed. + +Additionally even with Doctrine 2.0 the following methods do not trigger the collection load: + +- ``Collection#add($entity)`` +- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does + not work when setting specific keys like ``$coll[0] = $entity``. + +With extra lazy collections you can now not only add entities to large collections but also paginate them +easily using a combination of ``count`` and ``slice``. + + +Enabling Extra-Lazy Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The mapping configuration is simple. Instead of using the default value of ``fetch="LAZY"`` you have to +switch to extra lazy as shown in these examples: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\CMS\CmsGroup: + type: entity + # ... + manyToMany: + users: + targetEntity: CmsUser + mappedBy: groups + fetch: EXTRA_LAZY + diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst new file mode 100644 index 0000000..c625193 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst @@ -0,0 +1,27 @@ +Getting Started: Database First +=============================== + +.. note:: *Development Workflows* + + When you :doc:`Code First `, you + start with developing Objects and then map them onto your database. When + you :doc:`Model First `, you are modelling your application using tools (for + example UML) and generate database schema and PHP code from this model. + When you have a :doc:`Database First `, you already have a database schema + and generate the corresponding PHP code from it. + +.. note:: + + This getting started guide is in development. + +Development of new applications often starts with an existing database schema. +When the database schema is the starting point for your application, then +development is said to use the *Database First* approach to Doctrine. + +In this workflow you would modify the database schema first and then +regenerate the PHP code to use with this schema. You need a flexible +code-generator for this task and up to Doctrine 2.2, the code generator hasn't +been flexible enough to achieve this. + +We spinned off a subproject, Doctrine CodeGenerator, that will fill this gap and +allow you to do *Database First* development. diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst new file mode 100644 index 0000000..e844b4d --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst @@ -0,0 +1,24 @@ +Getting Started: Model First +============================ + +.. note:: *Development Workflows* + + When you :doc:`Code First `, you + start with developing Objects and then map them onto your database. When + you :doc:`Model First `, you are modelling your application using tools (for + example UML) and generate database schema and PHP code from this model. + When you have a :doc:`Database First `, then you already have a database schema + and generate the corresponding PHP code from it. + +.. note:: + + This getting started guide is in development. + +There are applications when you start with a high-level description of the +model using modelling tools such as UML. Modelling tools could also be Excel, +XML or CSV files that describe the model in some structured way. If your +application is using a modelling tool, then the development workflow is said to +be a *Model First* approach to Doctrine2. + +In this workflow you always change the model description and then regenerate +both PHP code and database schema from this model. diff --git a/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst b/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst new file mode 100644 index 0000000..1abcfb5 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/getting-started.rst @@ -0,0 +1,1549 @@ +Getting Started with Doctrine +============================= + +This guide covers getting started with the Doctrine ORM. After working +through the guide you should know: + +- How to install and configure Doctrine by connecting it to a database +- Mapping PHP objects to database tables +- Generating a database schema from PHP objects +- Using the ``EntityManager`` to insert, update, delete and find + objects in the database. + +Guide Assumptions +----------------- + +This guide is designed for beginners that haven't worked with Doctrine ORM +before. There are some prerequesites for the tutorial that have to be +installed: + +- PHP 5.4 or above +- Composer Package Manager (`Install Composer + `_) + +The code of this tutorial is `available on Github `_. + +.. note:: + + This tutorial assumes you work with **Doctrine 2.4** and above. + Some of the code will not work with lower versions. + +What is Doctrine? +----------------- + +Doctrine 2 is an `object-relational mapper (ORM) +`_ for PHP 5.4+ that +provides transparent persistence for PHP objects. It uses the Data Mapper +pattern at the heart, aiming for a complete separation of your domain/business +logic from the persistence in a relational database management system. + +The benefit of Doctrine for the programmer is the ability to focus +on the object-oriented business logic and worry about persistence only +as a secondary problem. This doesn't mean persistence is downplayed by Doctrine +2, however it is our belief that there are considerable benefits for +object-oriented programming if persistence and entities are kept +separated. + +What are Entities? +~~~~~~~~~~~~~~~~~~ + +Entities are PHP Objects that can be identified over many requests +by a unique identifier or primary key. These classes don't need to extend any +abstract base class or interface. An entity class must not be final +or contain final methods. Additionally it must not implement +**clone** nor **wakeup** or :doc:`do so safely <../cookbook/implementing-wakeup-or-clone>`. + +An entity contains persistable properties. A persistable property +is an instance variable of the entity that is saved into and retrieved from the database +by Doctrine's data mapping capabilities. + +An Example Model: Bug Tracker +----------------------------- + +For this Getting Started Guide for Doctrine we will implement the +Bug Tracker domain model from the +`Zend\_Db\_Table `_ +documentation. Reading their documentation we can extract the +requirements: + +- A Bug has a description, creation date, status, reporter and + engineer +- A Bug can occur on different Products (platforms) +- A Product has a name. +- Bug reporters and engineers are both Users of the system. +- A User can create new Bugs. +- The assigned engineer can close a Bug. +- A User can see all his reported or assigned Bugs. +- Bugs can be paginated through a list-view. + +Project Setup +------------- + +Create a new empty folder for this tutorial project, for example +``doctrine2-tutorial`` and create a new file ``composer.json`` with +the following contents: + +:: + + { + "require": { + "doctrine/orm": "2.4.*", + "symfony/yaml": "2.*" + }, + "autoload": { + "psr-0": {"": "src/"} + } + } + + +Install Doctrine using the Composer Dependency Management tool, by calling: + +:: + + $ composer install + +This will install the packages Doctrine Common, Doctrine DBAL, Doctrine ORM, +Symfony YAML and Symfony Console into the `vendor` directory. The Symfony +dependencies are not required by Doctrine but will be used in this tutorial. + +Add the following directories: +:: + + doctrine2-tutorial + |-- config + | |-- xml + | `-- yaml + `-- src + +Obtaining the EntityManager +--------------------------- + +Doctrine's public interface is the EntityManager, it provides the +access point to the complete lifecycle management of your entities +and transforms entities from and back to persistence. You have to +configure and create it to use your entities with Doctrine 2. I +will show the configuration steps and then discuss them step by +step: + +.. code-block:: php + + 'pdo_sqlite', + 'path' => __DIR__ . '/db.sqlite', + ); + + // obtaining the entity manager + $entityManager = EntityManager::create($conn, $config); + +The first require statement sets up the autoloading capabilities of Doctrine +using the Composer autoload. + +The second block consists of the instantiation of the ORM +``Configuration`` object using the Setup helper. It assumes a bunch +of defaults that you don't have to bother about for now. You can +read up on the configuration details in the +:doc:`reference chapter on configuration <../reference/configuration>`. + +The third block shows the configuration options required to connect +to a database, in my case a file-based sqlite database. All the +configuration options for all the shipped drivers are given in the +`DBAL Configuration section of the manual `_. + +The last block shows how the ``EntityManager`` is obtained from a +factory method. + +Generating the Database Schema +------------------------------ + +Now that we have defined the Metadata mappings and bootstrapped the +EntityManager we want to generate the relational database schema +from it. Doctrine has a Command-Line Interface that allows you to +access the SchemaTool, a component that generates the required +tables to work with the metadata. + +For the command-line tool to work a cli-config.php file has to be +present in the project root directory, where you will execute the +doctrine command. Its a fairly simple file: + +.. code-block:: php + + id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + } + +Note that all fields are set to protected (not public) with a +mutator (getter and setter) defined for every field except $id. +The use of mutators allows Doctrine to hook into calls which +manipulate the entities in ways that it could not if you just +directly set the values with ``entity#field = foo;`` + +The id field has no setter since, generally speaking, your code +should not set this value since it represents a database id value. +(Note that Doctrine itself can still set the value using the +Reflection API instead of a defined setter function) + +The next step for persistence with Doctrine is to describe the +structure of the ``Product`` entity to Doctrine using a metadata +language. The metadata language describes how entities, their +properties and references should be persisted and what constraints +should be applied to them. + +Metadata for entities are configured using a XML, YAML or Docblock Annotations. +This Getting Started Guide will show the mappings for all Mapping Drivers. +References in the text will be made to the XML mapping. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Product.dcm.yml + Product: + type: entity + table: products + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + +The top-level ``entity`` definition tag specifies information about +the class and table-name. The primitive type ``Product#name`` is +defined as a ``field`` attribute. The ``id`` property is defined with +the ``id`` tag, this has a ``generator`` tag nested inside which +defines that the primary key generation mechanism automatically +uses the database platforms native id generation strategy (for +example AUTO INCREMENT in the case of MySql or Sequences in the +case of PostgreSql and Oracle). + +Now that we have defined our first entity, let's update the database: + +:: + + $ vendor/bin/doctrine orm:schema-tool:update --force --dump-sql + +Specifying both flags ``--force`` and ``-dump-sql`` prints and executes the DDL +statements. + +Now create a new script that will insert products into the database: + +.. code-block:: php + + setName($newProductName); + + $entityManager->persist($product); + $entityManager->flush(); + + echo "Created Product with ID " . $product->getId() . "\n"; + +Call this script from the command-line to see how new products are created: + +:: + + $ php create_product.php ORM + $ php create_product.php DBAL + +What is happening here? Using the ``Product`` is pretty standard OOP. +The interesting bits are the use of the ``EntityManager`` service. To +notify the EntityManager that a new entity should be inserted into the database +you have to call ``persist()``. To initiate a transaction to actually perform +the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``. + +This distinction between persist and flush is allows to aggregate all writes +(INSERT, UPDATE, DELETE) into one single transaction, which is executed when +flush is called. Using this approach the write-performance is significantly +better than in a scenario where updates are done for each entity in isolation. + +Doctrine follows the UnitOfWork pattern which additionally detects all entities +that were fetched and have changed during the request. You don't have to keep track of +entities yourself, when Doctrine already knows about them. + +As a next step we want to fetch a list of all the Products. Let's create a +new script for this: + +.. code-block:: php + + getRepository('Product'); + $products = $productRepository->findAll(); + + foreach ($products as $product) { + echo sprintf("-%s\n", $product->getName()); + } + +The ``EntityManager#getRepository()`` method can create a finder object (called +a repository) for every entity. It is provided by Doctrine and contains some +finder methods such as ``findAll()``. + +Let's continue with displaying the name of a product based on its ID: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "No product found.\n"; + exit(1); + } + + echo sprintf("-%s\n", $product->getName()); + +Updating a product name demonstrates the functionality UnitOfWork of pattern +discussed before. We only need to find a product entity and all changes to its +properties are written to the database: + +.. code-block:: php + + + require_once "bootstrap.php"; + + $id = $argv[1]; + $newName = $argv[2]; + + $product = $entityManager->find('Product', $id); + + if ($product === null) { + echo "Product $id does not exist.\n"; + exit(1); + } + + $product->setName($newName); + + $entityManager->flush(); + +After calling this script on one of the existing products, you can verify the +product name changed by calling the ``show_product.php`` script. + +Adding Bug and User Entities +---------------------------- + +We continue with the bug tracker domain, by creating the missing classes +``Bug`` and ``User`` and putting them into ``src/Bug.php`` and +``src/User.php`` respectively. + +.. code-block:: php + + id; + } + + public function getDescription() + { + return $this->description; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function setCreated(DateTime $created) + { + $this->created = $created; + } + + public function getCreated() + { + return $this->created; + } + + public function setStatus($status) + { + $this->status = $status; + } + + public function getStatus() + { + return $this->status; + } + } + +.. code-block:: php + + id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + } + +All of the properties discussed so far are simple string and integer values, +for example the id fields of the entities, their names, description, status and +change dates. Next we will model the dynamic relationships between the entities +by defining the references between entities. + +References between objects are foreign keys in the database. You never have to +(and never should) work with the foreign keys directly, only with the objects +that represent the foreign key through their own identity. + +For every foreign key you either have a Doctrine ManyToOne or OneToOne +association. On the inverse sides of these foreign keys you can have +OneToMany associations. Obviously you can have ManyToMany associations +that connect two tables with each other through a join table with +two foreign keys. + +Now that you know the basics about references in Doctrine, we can extend the +domain model to match the requirements: + +.. code-block:: php + + products = new ArrayCollection(); + } + } + +.. code-block:: php + + reportedBugs = new ArrayCollection(); + $this->assignedBugs = new ArrayCollection(); + } + } + +Whenever an entity is recreated from the database, an Collection +implementation of the type Doctrine is injected into your entity +instead of an array. Compared to the ArrayCollection this +implementation helps the Doctrine ORM understand the changes that +have happened to the collection which are noteworthy for +persistence. + +.. warning:: + + Lazy load proxies always contain an instance of + Doctrine's EntityManager and all its dependencies. Therefore a + var\_dump() will possibly dump a very large recursive structure + which is impossible to render and read. You have to use + ``Doctrine\Common\Util\Debug::dump()`` to restrict the dumping to a + human readable level. Additionally you should be aware that dumping + the EntityManager to a Browser may take several minutes, and the + Debug::dump() method just ignores any occurrences of it in Proxy + instances. + +Because we only work with collections for the references we must be +careful to implement a bidirectional reference in the domain model. +The concept of owning or inverse side of a relation is central to +this notion and should always be kept in mind. The following +assumptions are made about relations and have to be followed to be +able to work with Doctrine 2. These assumptions are not unique to +Doctrine 2 but are best practices in handling database relations +and Object-Relational Mapping. + + +- Changes to Collections are saved or updated, when the entity on + the *owning* side of the collection is saved or updated. +- Saving an Entity at the inverse side of a relation never + triggers a persist operation to changes to the collection. +- In a one-to-one relation the entity holding the foreign key of + the related entity on its own database table is *always* the owning + side of the relation. +- In a many-to-many relation, both sides can be the owning side of + the relation. However in a bi-directional many-to-many relation + only one is allowed to be. +- In a many-to-one relation the Many-side is the owning side by + default, because it holds the foreign key. +- The OneToMany side of a relation is inverse by default, since + the foreign key is saved on the Many side. A OneToMany relation can + only be the owning side, if its implemented using a ManyToMany + relation with join table and restricting the one side to allow only + UNIQUE values per database constraint. + +.. note:: + + Consistency of bi-directional references on the inverse side of a + relation have to be managed in userland application code. Doctrine + cannot magically update your collections to be consistent. + + +In the case of Users and Bugs we have references back and forth to +the assigned and reported bugs from a user, making this relation +bi-directional. We have to change the code to ensure consistency of +the bi-directional reference: + +.. code-block:: php + + assignedToBug($this); + $this->engineer = $engineer; + } + + public function setReporter($reporter) + { + $reporter->addReportedBug($this); + $this->reporter = $reporter; + } + + public function getEngineer() + { + return $this->engineer; + } + + public function getReporter() + { + return $this->reporter; + } + } + +.. code-block:: php + + reportedBugs[] = $bug; + } + + public function assignedToBug($bug) + { + $this->assignedBugs[] = $bug; + } + } + +I chose to name the inverse methods in past-tense, which should +indicate that the actual assigning has already taken place and the +methods are only used for ensuring consistency of the references. +This approach is my personal preference, you can choose whatever +method to make this work. + +You can see from ``User#addReportedBug()`` and +``User#assignedToBug()`` that using this method in userland alone +would not add the Bug to the collection of the owning side in +``Bug#reporter`` or ``Bug#engineer``. Using these methods and +calling Doctrine for persistence would not update the collections +representation in the database. + +Only using ``Bug#setEngineer()`` or ``Bug#setReporter()`` +correctly saves the relation information. + +The ``Bug#reporter`` and ``Bug#engineer`` properties are +Many-To-One relations, which point to a User. In a normalized +relational model the foreign key is saved on the Bug's table, hence +in our object-relation model the Bug is at the owning side of the +relation. You should always make sure that the use-cases of your +domain model should drive which side is an inverse or owning one in +your Doctrine mapping. In our example, whenever a new bug is saved +or an engineer is assigned to the bug, we don't want to update the +User to persist the reference, but the Bug. This is the case with +the Bug being at the owning side of the relation. + +Bugs reference Products by an uni-directional ManyToMany relation in +the database that points from Bugs to Products. + +.. code-block:: php + + products[] = $product; + } + + public function getProducts() + { + return $this->products; + } + } + +We are now finished with the domain model given the requirements. +Lets add metadata mappings for the ``User`` and ``Bug`` as we did for +the ``Product`` before: + +.. configuration-block:: + .. code-block:: php + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # config/yaml/Bug.dcm.yml + Bug: + type: entity + table: bugs + id: + id: + type: integer + generator: + strategy: AUTO + fields: + description: + type: text + created: + type: datetime + status: + type: string + manyToOne: + reporter: + targetEntity: User + inversedBy: reportedBugs + engineer: + targetEntity: User + inversedBy: assignedBugs + manyToMany: + products: + targetEntity: Product + + +Here we have the entity, id and primitive type definitions. +For the "created" field we have used the ``datetime`` type, +which translates the YYYY-mm-dd HH:mm:ss database format +into a PHP DateTime instance and back. + +After the field definitions the two qualified references to the +user entity are defined. They are created by the ``many-to-one`` +tag. The class name of the related entity has to be specified with +the ``target-entity`` attribute, which is enough information for +the database mapper to access the foreign-table. Since +``reporter`` and ``engineer`` are on the owning side of a +bi-directional relation we also have to specify the ``inversed-by`` +attribute. They have to point to the field names on the inverse +side of the relationship. We will see in the next example that the ``inversed-by`` +attribute has a counterpart ``mapped-by`` which makes that +the inverse side. + +The last definition is for the ``Bug#products`` collection. It +holds all products where the specific bug occurs. Again +you have to define the ``target-entity`` and ``field`` attributes +on the ``many-to-many`` tag. + +The last missing definition is that of the User entity: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + + + + + + .. code-block:: yaml + + # config/xml/User.dcm.yml + User: + type: entity + table: users + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + oneToMany: + reportedBugs: + targetEntity: Bug + mappedBy: reporter + assignedBugs: + targetEntity: Bug + mappedBy: engineer + +Here are some new things to mention about the ``one-to-many`` tags. +Remember that we discussed about the inverse and owning side. Now +both reportedBugs and assignedBugs are inverse relations, which +means the join details have already been defined on the owning +side. Therefore we only have to specify the property on the Bug +class that holds the owning sides. + +This example has a fair overview of the most basic features of the +metadata definition language. + +Update your database running: +:: + + $ vendor/bin/doctrine orm:schema-tool:update --force + + +Implementing more Requirements +------------------------------ + +For starters we need a create user entities: + +.. code-block:: php + + setName($newUsername); + + $entityManager->persist($user); + $entityManager->flush(); + + echo "Created User with ID " . $user->getId() . "\n"; + +Now call: + +:: + + $ php create_user.php beberlei + +We now have the data to create a bug and the code for this scenario may look +like this: + +.. code-block:: php + + find("User", $theReporterId); + $engineer = $entityManager->find("User", $theDefaultEngineerId); + if (!$reporter || !$engineer) { + echo "No reporter and/or engineer found for the input.\n"; + exit(1); + } + + $bug = new Bug(); + $bug->setDescription("Something does not work!"); + $bug->setCreated(new DateTime("now")); + $bug->setStatus("OPEN"); + + foreach ($productIds as $productId) { + $product = $entityManager->find("Product", $productId); + $bug->assignToProduct($product); + } + + $bug->setReporter($reporter); + $bug->setEngineer($engineer); + + $entityManager->persist($bug); + $entityManager->flush(); + + echo "Your new Bug Id: ".$bug->getId()."\n"; + +Since we only have one user and product, probably with the ID of 1, we can call this script with: + +:: + + php create_bug.php 1 1 1 + +This is the first contact with the read API of the EntityManager, +showing that a call to ``EntityManager#find($name, $id)`` returns a +single instance of an entity queried by primary key. Besides this +we see the persist + flush pattern again to save the Bug into the +database. + +See how simple relating Bug, Reporter, Engineer and Products is +done by using the discussed methods in the "A first prototype" +section. The UnitOfWork will detect this relations when flush is +called and relate them in the database appropriately. + +Queries for Application Use-Cases +--------------------------------- + +List of Bugs +~~~~~~~~~~~~ + +Using the previous examples we can fill up the database quite a +bit, however we now need to discuss how to query the underlying +mapper for the required view representations. When opening the +application, bugs can be paginated through a list-view, which is +the first read-only use-case: + +.. code-block:: php + + createQuery($dql); + $query->setMaxResults(30); + $bugs = $query->getResult(); + + foreach ($bugs as $bug) { + echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$bug->getReporter()->getName()."\n"; + echo " Assigned to: ".$bug->getEngineer()->getName()."\n"; + foreach ($bug->getProducts() as $product) { + echo " Platform: ".$product->getName()."\n"; + } + echo "\n"; + } + +The DQL Query in this example fetches the 30 most recent bugs with +their respective engineer and reporter in one single SQL statement. +The console output of this script is then: + +:: + + Something does not work! - 02.04.2010 + Reported by: beberlei + Assigned to: beberlei + Platform: My Product + +.. note:: + + **DQL is not SQL** + + You may wonder why we start writing SQL at the beginning of this + use-case. Don't we use an ORM to get rid of all the endless + hand-writing of SQL? Doctrine introduces DQL which is best + described as **object-query-language** and is a dialect of + `OQL `_ and + similar to `HQL `_ or + `JPQL `_. + It does not know the concept of columns and tables, but only those + of Entity-Class and property. Using the Metadata we defined before + it allows for very short distinctive and powerful queries. + + + An important reason why DQL is favourable to the Query API of most + ORMs is its similarity to SQL. The DQL language allows query + constructs that most ORMs don't, GROUP BY even with HAVING, + Sub-selects, Fetch-Joins of nested classes, mixed results with + entities and scalar data such as COUNT() results and much more. + Using DQL you should seldom come to the point where you want to + throw your ORM into the dumpster, because it doesn't support some + the more powerful SQL concepts. + + + Instead of handwriting DQL you can use the ``QueryBuilder`` retrieved + by calling ``$entityManager->createQueryBuilder()``. There are more + details about this in the relevant part of the documentation. + + + As a last resort you can still use Native SQL and a description of the + result set to retrieve entities from the database. DQL boils down to a + Native SQL statement and a ``ResultSetMapping`` instance itself. Using + Native SQL you could even use stored procedures for data retrieval, or + make use of advanced non-portable database queries like PostgreSql's + recursive queries. + + +Array Hydration of the Bug List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the previous use-case we retrieved the results as their +respective object instances. We are not limited to retrieving +objects only from Doctrine however. For a simple list view like the +previous one we only need read access to our entities and can +switch the hydration from objects to simple PHP arrays instead. + +Hydration can be an expensive process so only retrieving what you need can +yield considerable performance benefits for read-only requests. + +Implementing the same list view as before using array hydration we +can rewrite our code: + +.. code-block:: php + + createQuery($dql); + $bugs = $query->getArrayResult(); + + foreach ($bugs as $bug) { + echo $bug['description'] . " - " . $bug['created']->format('d.m.Y')."\n"; + echo " Reported by: ".$bug['reporter']['name']."\n"; + echo " Assigned to: ".$bug['engineer']['name']."\n"; + foreach ($bug['products'] as $product) { + echo " Platform: ".$product['name']."\n"; + } + echo "\n"; + } + +There is one significant difference in the DQL query however, we +have to add an additional fetch-join for the products connected to +a bug. The resulting SQL query for this single select statement is +pretty large, however still more efficient to retrieve compared to +hydrating objects. + +Find by Primary Key +~~~~~~~~~~~~~~~~~~~ + +The next Use-Case is displaying a Bug by primary key. This could be +done using DQL as in the previous example with a where clause, +however there is a convenience method on the ``EntityManager`` that +handles loading by primary key, which we have already seen in the +write scenarios: + +.. code-block:: php + + find("Bug", (int)$theBugId); + + echo "Bug: ".$bug->getDescription()."\n"; + echo "Engineer: ".$bug->getEngineer()->getName()."\n"; + +The output of the engineer’s name is fetched from the database! What is happening? + +Since we only retrieved the bug by primary key both the engineer and reporter +are not immediately loaded from the database but are replaced by LazyLoading +proxies. These proxies will load behind the scenes, when the first method +is called on them. + +Sample code of this proxy generated code can be found in the specified Proxy +Directory, it looks like: + +.. code-block:: php + + _load(); + return parent::addReportedBug($bug); + } + + public function assignedToBug($bug) + { + $this->_load(); + return parent::assignedToBug($bug); + } + } + +See how upon each method call the proxy is lazily loaded from the +database? + +The call prints: + +:: + + $ php show_bug.php 1 + Bug: Something does not work! + Engineer: beberlei + +.. warning:: + + Lazy loading additional data can be very convenient but the additional + queries create an overhead. If you know that certain fields will always + (or usually) be required by the query then you will get better performance + by explicitly retrieving them all in the first query. + + +Dashboard of the User +--------------------- + +For the next use-case we want to retrieve the dashboard view, a +list of all open bugs the user reported or was assigned to. This +will be achieved using DQL again, this time with some WHERE clauses +and usage of bound parameters: + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $theUserId) + ->setMaxResults(15) + ->getResult(); + + echo "You have created or assigned to " . count($myBugs) . " open bugs:\n\n"; + + foreach ($myBugs as $bug) { + echo $bug->getId() . " - " . $bug->getDescription()."\n"; + } + +Number of Bugs +-------------- + +Until now we only retrieved entities or their array representation. +Doctrine also supports the retrieval of non-entities through DQL. +These values are called "scalar result values" and may even be +aggregate values using COUNT, SUM, MIN, MAX or AVG functions. + +We will need this knowledge to retrieve the number of open bugs +grouped by product: + +.. code-block:: php + + createQuery($dql)->getScalarResult(); + + foreach ($productBugs as $productBug) { + echo $productBug['name']." has " . $productBug['openBugs'] . " open bugs!\n"; + } + +Updating Entities +----------------- + +There is a single use-case missing from the requirements, Engineers +should be able to close a bug. This looks like: + +.. code-block:: php + + status = "CLOSE"; + } + } + +.. code-block:: php + + find("Bug", (int)$theBugId); + $bug->close(); + + $entityManager->flush(); + +When retrieving the Bug from the database it is inserted into the +IdentityMap inside the UnitOfWork of Doctrine. This means your Bug +with exactly this id can only exist once during the whole request +no matter how often you call ``EntityManager#find()``. It even +detects entities that are hydrated using DQL and are already +present in the Identity Map. + +When flush is called the EntityManager loops over all the entities +in the identity map and performs a comparison between the values +originally retrieved from the database and those values the entity +currently has. If at least one of these properties is different the +entity is scheduled for an UPDATE against the database. Only the +changed columns are updated, which offers a pretty good performance +improvement compared to updating all the properties. + +Entity Repositories +------------------- + +For now we have not discussed how to separate the Doctrine query logic from your model. +In Doctrine 1 there was the concept of ``Doctrine_Table`` instances for this +separation. The similar concept in Doctrine2 is called Entity Repositories, integrating +the `repository pattern `_ at the heart of Doctrine. + +Every Entity uses a default repository by default and offers a bunch of convenience +methods that you can use to query for instances of that Entity. Take for example +our Product entity. If we wanted to Query by name, we can use: + +.. code-block:: php + + getRepository('Product') + ->findOneBy(array('name' => $productName)); + +The method ``findOneBy()`` takes an array of fields or association keys and the values to match against. + +If you want to find all entities matching a condition you can use ``findBy()``, for +example querying for all closed bugs: + +.. code-block:: php + + getRepository('Bug') + ->findBy(array('status' => 'CLOSED')); + + foreach ($bugs as $bug) { + // do stuff + } + +Compared to DQL these query methods are falling short of functionality very fast. +Doctrine offers you a convenient way to extend the functionalities of the default ``EntityRepository`` +and put all the specialized DQL query logic on it. For this you have to create a subclass +of ``Doctrine\ORM\EntityRepository``, in our case a ``BugRepository`` and group all +the previously discussed query functionality in it: + +.. code-block:: php + + getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getResult(); + } + + public function getRecentBugsArray($number = 30) + { + $dql = "SELECT b, e, r, p FROM Bug b JOIN b.engineer e ". + "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC"; + $query = $this->getEntityManager()->createQuery($dql); + $query->setMaxResults($number); + return $query->getArrayResult(); + } + + public function getUsersBugs($userId, $number = 15) + { + $dql = "SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ". + "WHERE b.status = 'OPEN' AND e.id = ?1 OR r.id = ?1 ORDER BY b.created DESC"; + + return $this->getEntityManager()->createQuery($dql) + ->setParameter(1, $userId) + ->setMaxResults($number) + ->getResult(); + } + + public function getOpenBugsByProduct() + { + $dql = "SELECT p.id, p.name, count(b.id) AS openBugs FROM Bug b ". + "JOIN b.products p WHERE b.status = 'OPEN' GROUP BY p.id"; + return $this->getEntityManager()->createQuery($dql)->getScalarResult(); + } + } + +To be able to use this query logic through ``$this->getEntityManager()->getRepository('Bug')`` +we have to adjust the metadata slightly. + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + .. code-block:: yaml + + Bug: + type: entity + repositoryClass: BugRepository + +Now we can remove our query logic in all the places and instead use them through the EntityRepository. +As an example here is the code of the first use case "List of Bugs": + +.. code-block:: php + + getRepository('Bug')->getRecentBugs(); + + foreach ($bugs as $bug) { + echo $bug->getDescription()." - ".$bug->getCreated()->format('d.m.Y')."\n"; + echo " Reported by: ".$bug->getReporter()->getName()."\n"; + echo " Assigned to: ".$bug->getEngineer()->getName()."\n"; + foreach ($bug->getProducts() as $product) { + echo " Platform: ".$product->getName()."\n"; + } + echo "\n"; + } + +Using EntityRepositories you can avoid coupling your model with specific query logic. +You can also re-use query logic easily throughout your application. + +Conclusion +---------- + +This tutorial is over here, I hope you had fun. Additional content +will be added to this tutorial incrementally, topics will include: + +- More on Association Mappings +- Lifecycle Events triggered in the UnitOfWork +- Ordering of Collections + +Additional details on all the topics discussed here can be found in +the respective manual chapters. diff --git a/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst new file mode 100644 index 0000000..121bd14 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst @@ -0,0 +1,110 @@ +Ordering To-Many Associations +----------------------------- + +There are use-cases when you'll want to sort collections when they are +retrieved from the database. In userland you do this as long as you +haven't initially saved an entity with its associations into the +database. To retrieve a sorted collection from the database you can +use the ``@OrderBy`` annotation with an collection that specifies +an DQL snippet that is appended to all queries with this +collection. + +Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you +can specify the ``@OrderBy`` in the following way: + +.. configuration-block:: + + .. code-block:: php + + + + + + + + + + + + .. code-block:: yaml + + User: + type: entity + manyToMany: + groups: + orderBy: { 'name': 'ASC' } + targetEntity: Group + joinTable: + name: users_groups + joinColumns: + user_id: + referencedColumnName: id + inverseJoinColumns: + group_id: + referencedColumnName: id + +The DQL Snippet in OrderBy is only allowed to consist of +unqualified, unquoted field names and of an optional ASC/DESC +positional statement. Multiple Fields are separated by a comma (,). +The referenced field names have to exist on the ``targetEntity`` +class of the ``@ManyToMany`` or ``@OneToMany`` annotation. + +The semantics of this feature can be described as follows. + + +- ``@OrderBy`` acts as an implicit ORDER BY clause for the given + fields, that is appended to all the explicitly given ORDER BY + items. +- All collections of the ordered type are always retrieved in an + ordered fashion. +- To keep the database impact low, these implicit ORDER BY items + are only added to an DQL Query if the collection is fetch joined in + the DQL query. + +Given our previously defined example, the following would not add +ORDER BY, since g is not fetch joined: + +.. code-block:: sql + + SELECT u FROM User u JOIN u.groups g WHERE SIZE(g) > 10 + +However the following: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 + +...would internally be rewritten to: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name ASC + +You can reverse the order with an explicit DQL ORDER BY: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC + +...is internally rewritten to: + +.. code-block:: sql + + SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC + + diff --git a/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst b/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst new file mode 100644 index 0000000..aef663f --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst @@ -0,0 +1,90 @@ +Override Field Association Mappings In Subclasses +------------------------------------------------- + +Sometimes there is a need to persist entities but override all or part of the +mapping metadata. Sometimes also the mapping to override comes from entities +using traits where the traits have mapping metadata. +This tutorial explains how to override mapping metadata, +i.e. attributes and associations metadata in particular. The example here shows +the overriding of a class that uses a trait but is similar when extending a base +class as shown at the end of this tutorial. + +Suppose we have a class ExampleEntityWithOverride. This class uses trait ExampleTrait: + +.. code-block:: php + + `). diff --git a/vendor/doctrine/orm/docs/en/tutorials/pagination.rst b/vendor/doctrine/orm/docs/en/tutorials/pagination.rst new file mode 100644 index 0000000..3971383 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/pagination.rst @@ -0,0 +1,43 @@ +Pagination +========== + +.. versionadded:: 2.2 + +Starting with version 2.2 Doctrine ships with a Paginator for DQL queries. It +has a very simple API and implements the SPL interfaces ``Countable`` and +``IteratorAggregate``. + +.. code-block:: php + + createQuery($dql) + ->setFirstResult(0) + ->setMaxResults(100); + + $paginator = new Paginator($query, $fetchJoinCollection = true); + + $c = count($paginator); + foreach ($paginator as $post) { + echo $post->getHeadline() . "\n"; + } + +Paginating Doctrine queries is not as simple as you might think in the +beginning. If you have complex fetch-join scenarios with one-to-many or +many-to-many associations using the "default" LIMIT functionality of database +vendors is not sufficient to get the correct results. + +By default the pagination extension does the following steps to compute the +correct result: + +- Perform a Count query using `DISTINCT` keyword. +- Perform a Limit Subquery with `DISTINCT` to find all ids of the entity in from on the current page. +- Perform a WHERE IN query to get all results for the current page. + +This behavior is only necessary if you actually fetch join a to-many +collection. You can disable this behavior by setting the +``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries +described are executed. We hope to automate the detection for this in +the future. diff --git a/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst b/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst new file mode 100644 index 0000000..3536a34 --- /dev/null +++ b/vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst @@ -0,0 +1,298 @@ +Working with Indexed Associations +================================= + +.. note:: + + This feature is scheduled for version 2.1 of Doctrine and not included in the 2.0.x series. + +Doctrine 2 collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in +the first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY`` +was used. Starting with Doctrine 2.1 you can index your collections by a value in the related entity. +This is a first step towards full ordered hashmap support through the Doctrine ORM. +The feature works like an implicit ``INDEX BY`` for the selected association but has several +downsides also: + +- You have to manage both the key and field if you want to change the index by field value. +- On each request the keys are regenerated from the field value not from the previous collection key. +- Values of the Index-By keys are never considered during persistence, it only exists for accessing purposes. +- Fields that are used for the index by feature **HAVE** to be unique in the database. The behavior for multiple entities + with the same index-by field value is undefined. + +As an example we will design a simple stock exchange list view. The domain consists of the entity ``Stock`` +and ``Market`` where each Stock has a symbol and is traded on a single market. Instead of having a numerical +list of stocks traded on a market they will be indexed by their symbol, which is unique across all markets. + +Mapping Indexed Associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can map indexed associations by adding: + + * ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation. + * ``index-by`` attribute to any ```` or ```` xml element. + * ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files. + +The code and mappings for the Market entity looks like this: + +.. configuration-block:: + .. code-block:: php + + name = $name; + $this->stocks = new ArrayCollection(); + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + + public function addStock(Stock $stock) + { + $this->stocks[$stock->getSymbol()] = $stock; + } + + public function getStock($symbol) + { + if (!isset($this->stocks[$symbol])) { + throw new \InvalidArgumentException("Symbol is not traded on this market."); + } + + return $this->stocks[$symbol]; + } + + public function getStocks() + { + return $this->stocks->toArray(); + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\StockExchange\Market: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type:string + oneToMany: + stocks: + targetEntity: Stock + mappedBy: market + indexBy: symbol + +Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol, +so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)`` +we pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown. + +The ``Stock`` entity doesn't contain any special instructions that are new, but for completeness +here are the code and mappings for it: + +.. configuration-block:: + .. code-block:: php + + symbol = $symbol; + $this->market = $market; + $market->addStock($this); + } + + public function getSymbol() + { + return $this->symbol; + } + } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: yaml + + Doctrine\Tests\Models\StockExchange\Stock: + type: entity + id: + id: + type: integer + generator: + strategy: AUTO + fields: + symbol: + type: string + manyToOne: + market: + targetEntity: Market + inversedBy: stocks + +Querying indexed associations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that we defined the stocks collection to be indexed by symbol we can take a look at some code, +that makes use of the indexing. + +First we will populate our database with two example stocks traded on a single market: + +.. code-block:: php + + persist($market); + $em->persist($stock1); + $em->persist($stock2); + $em->flush(); + +This code is not particular interesting since the indexing feature is not yet used. In a new request we could +now query for the market: + +.. code-block:: php + + find("Doctrine\Tests\Models\StockExchange\Market", $marketId); + + // Access the stocks by symbol now: + $stock = $market->getStock($symbol); + + echo $stock->getSymbol(); // will print "AAPL" + +The implementation ``Market::addStock()`` in combination with ``indexBy`` allows to access the collection +consistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not. + +The same applies to DQL queries: The ``indexBy`` configuration acts as implicit "INDEX BY" to a join association. + +.. code-block:: php + + createQuery($dql) + ->setParameter(1, $marketId) + ->getSingleResult(); + + // Access the stocks by symbol now: + $stock = $market->getStock($symbol); + + echo $stock->getSymbol(); // will print "AAPL" + +If you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally +indexed associations also work with the ``Collection::slice()`` functionality, no matter if marked as +LAZY or EXTRA_LAZY. + +Outlook into the Future +~~~~~~~~~~~~~~~~~~~~~~~ + +For the inverse side of a many-to-many associations there will be a way to persist the keys and the order +as a third and fourth parameter into the join table. This feature is discussed in `DDC-213 `_ +This feature cannot be implemented for One-To-Many associations, because they are never the owning side. + diff --git a/vendor/doctrine/orm/doctrine-mapping.xsd b/vendor/doctrine/orm/doctrine-mapping.xsd new file mode 100644 index 0000000..6a7e2a2 --- /dev/null +++ b/vendor/doctrine/orm/doctrine-mapping.xsd @@ -0,0 +1,603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php new file mode 100644 index 0000000..e1667ad --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php @@ -0,0 +1,1120 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\ORM\Query\Parameter; +use Doctrine\ORM\Cache\QueryCacheKey; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +use Doctrine\ORM\Cache; +use Doctrine\ORM\Query\QueryException; + +/** + * Base contract for ORM queries. Base class for Query and NativeQuery. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Konsta Vesterinen + */ +abstract class AbstractQuery +{ + /* Hydration mode constants */ + + /** + * Hydrates an object graph. This is the default behavior. + */ + const HYDRATE_OBJECT = 1; + + /** + * Hydrates an array graph. + */ + const HYDRATE_ARRAY = 2; + + /** + * Hydrates a flat, rectangular result set with scalar values. + */ + const HYDRATE_SCALAR = 3; + + /** + * Hydrates a single scalar value. + */ + const HYDRATE_SINGLE_SCALAR = 4; + + /** + * Very simple object hydrator (optimized for performance). + */ + const HYDRATE_SIMPLEOBJECT = 5; + + /** + * The parameter map of this query. + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + protected $parameters; + + /** + * The user-specified ResultSetMapping to use. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + protected $_resultSetMapping; + + /** + * The entity manager used by this query object. + * + * @var EntityManagerInterface + */ + protected $_em; + + /** + * The map of query hints. + * + * @var array + */ + protected $_hints = array(); + + /** + * The hydration mode. + * + * @var integer + */ + protected $_hydrationMode = self::HYDRATE_OBJECT; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_queryCacheProfile; + + /** + * Whether or not expire the result cache. + * + * @var boolean + */ + protected $_expireResultCache = false; + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile + */ + protected $_hydrationCacheProfile; + + /** + * Whether to use second level cache, if available. + * + * @var boolean + */ + protected $cacheable = false; + + /** + * @var boolean + */ + protected $hasCache = false; + + /** + * Second level cache region name. + * + * @var string|null + */ + protected $cacheRegion; + + /** + * Second level query cache mode. + * + * @var integer|null + */ + protected $cacheMode; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + protected $cacheLogger; + + /** + * @var integer + */ + protected $lifetime = 0; + + /** + * Initializes a new instance of a class derived from AbstractQuery. + * + * @param \Doctrine\ORM\EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + $this->parameters = new ArrayCollection(); + $this->_hints = $em->getConfiguration()->getDefaultQueryHints(); + $this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled(); + + if ($this->hasCache) { + $this->cacheLogger = $em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheLogger(); + } + } + + /** + * + * Enable/disable second level query (result) caching for this query. + * + * @param boolean $cacheable + * + * @return static This query instance. + */ + public function setCacheable($cacheable) + { + $this->cacheable = (boolean) $cacheable; + + return $this; + } + + /** + * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise. + */ + public function isCacheable() + { + return $this->cacheable; + } + + /** + * @param string $cacheRegion + * + * @return static This query instance. + */ + public function setCacheRegion($cacheRegion) + { + $this->cacheRegion = (string) $cacheRegion; + + return $this; + } + + /** + * Obtain the name of the second level query cache region in which query results will be stored + * + * @return The cache region name; NULL indicates the default region. + */ + public function getCacheRegion() + { + return $this->cacheRegion; + } + + /** + * @return boolean TRUE if the query cache and second level cache are enabled, FALSE otherwise. + */ + protected function isCacheEnabled() + { + return $this->cacheable && $this->hasCache; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * Sets the life-time for this query into second level cache. + * + * @param integer $lifetime + * + * @return static This query instance. + */ + public function setLifetime($lifetime) + { + $this->lifetime = (integer) $lifetime; + + return $this; + } + + /** + * @return integer + */ + public function getCacheMode() + { + return $this->cacheMode; + } + + /** + * @param integer $cacheMode + * + * @return static This query instance. + */ + public function setCacheMode($cacheMode) + { + $this->cacheMode = (integer) $cacheMode; + + return $this; + } + + /** + * Gets the SQL query that corresponds to this query object. + * The returned SQL syntax depends on the connection driver that is used + * by this query object at the time of this method call. + * + * @return string SQL query + */ + abstract public function getSQL(); + + /** + * Retrieves the associated EntityManager of this Query instance. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Frees the resources used by the query object. + * + * Resets Parameters, Parameter Types and Query Hints. + * + * @return void + */ + public function free() + { + $this->parameters = new ArrayCollection(); + + $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints(); + } + + /** + * Get all defined parameters. + * + * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters. + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets a query parameter. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return Query\Parameter|null The value of the bound parameter, or NULL if not available. + */ + public function getParameter($key) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + return count($filteredParameters) ? $filteredParameters->first() : null; + } + + /** + * Sets a collection of query parameters. + * + * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters + * + * @return static This query instance. + */ + public function setParameters($parameters) + { + // BC compatibility with 2.3- + if (is_array($parameters)) { + $parameterCollection = new ArrayCollection(); + + foreach ($parameters as $key => $value) { + $parameterCollection->add(new Parameter($key, $value)); + } + + $parameters = $parameterCollection; + } + + $this->parameters = $parameters; + + return $this; + } + + /** + * Sets a query parameter. + * + * @param string|int $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. + * + * @return static This query instance. + */ + public function setParameter($key, $value, $type = null) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + if (count($filteredParameters)) { + $parameter = $filteredParameters->first(); + $parameter->setValue($value, $type); + + return $this; + } + + $this->parameters->add(new Parameter($key, $value, $type)); + + return $this; + } + + /** + * Processes an individual parameter value. + * + * @param mixed $value + * + * @return array + * + * @throws \Doctrine\ORM\ORMInvalidArgumentException + */ + public function processParameterValue($value) + { + if (is_scalar($value)) { + return $value; + } + + if ($value instanceof Collection) { + $value = $value->toArray(); + } + + if (is_array($value)) { + foreach ($value as $key => $paramValue) { + $paramValue = $this->processParameterValue($paramValue); + $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue; + } + + return $value; + } + + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value); + + if ($value === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + + if ($value instanceof Mapping\ClassMetadata) { + return $value->name; + } + + return $value; + } + + /** + * Sets the ResultSetMapping that should be used for hydration. + * + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * + * @return static This query instance. + */ + public function setResultSetMapping(Query\ResultSetMapping $rsm) + { + $this->translateNamespaces($rsm); + $this->_resultSetMapping = $rsm; + + return $this; + } + + /** + * Gets the ResultSetMapping used for hydration. + * + * @return \Doctrine\ORM\Query\ResultSetMapping + */ + protected function getResultSetMapping() + { + return $this->_resultSetMapping; + } + + /** + * Allows to translate entity namespaces to full qualified names. + * + * @param Query\ResultSetMapping $rsm + * + * @return void + */ + private function translateNamespaces(Query\ResultSetMapping $rsm) + { + $translate = function ($alias) { + return $this->_em->getClassMetadata($alias)->getName(); + }; + + $rsm->aliasMap = array_map($translate, $rsm->aliasMap); + $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses); + } + + /** + * Set a cache profile for hydration caching. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * Important: Hydration caching does NOT register entities in the + * UnitOfWork when retrieved from the cache. Never use result cached + * entities for requests that also flush the EntityManager. If you want + * some form of caching with UnitOfWork registration you should use + * {@see AbstractQuery::setResultCacheProfile()}. + * + * @example + * $lifetime = 100; + * $resultKey = "abc"; + * $query->setHydrationCacheProfile(new QueryCacheProfile()); + * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey)); + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * + * @return static This query instance. + */ + public function setHydrationCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); + } + + $this->_hydrationCacheProfile = $profile; + + return $this; + } + + /** + * @return \Doctrine\DBAL\Cache\QueryCacheProfile + */ + public function getHydrationCacheProfile() + { + return $this->_hydrationCacheProfile; + } + + /** + * Set a cache profile for the result cache. + * + * If no result cache driver is set in the QueryCacheProfile, the default + * result cache driver is used from the configuration. + * + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile + * + * @return static This query instance. + */ + public function setResultCacheProfile(QueryCacheProfile $profile = null) + { + if ( ! $profile->getResultCacheDriver()) { + $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl(); + $profile = $profile->setResultCacheDriver($resultCacheDriver); + } + + $this->_queryCacheProfile = $profile; + + return $this; + } + + /** + * Defines a cache driver to be used for caching result sets and implicitly enables caching. + * + * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver + * + * @return static This query instance. + * + * @throws ORMException + */ + public function setResultCacheDriver($resultCacheDriver = null) + { + if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { + throw ORMException::invalidResultCacheDriver(); + } + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver) + : new QueryCacheProfile(0, null, $resultCacheDriver); + + return $this; + } + + /** + * Returns the cache driver used for caching result sets. + * + * @deprecated + * + * @return \Doctrine\Common\Cache\Cache Cache driver + */ + public function getResultCacheDriver() + { + if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) { + return $this->_queryCacheProfile->getResultCacheDriver(); + } + + return $this->_em->getConfiguration()->getResultCacheImpl(); + } + + /** + * Set whether or not to cache the results of this query and if so, for + * how long and which ID to use for the cache entry. + * + * @param boolean $bool + * @param integer $lifetime + * @param string $resultCacheId + * + * @return static This query instance. + */ + public function useResultCache($bool, $lifetime = null, $resultCacheId = null) + { + if ($bool) { + $this->setResultCacheLifetime($lifetime); + $this->setResultCacheId($resultCacheId); + + return $this; + } + + $this->_queryCacheProfile = null; + + return $this; + } + + /** + * Defines how long the result cache will be active before expire. + * + * @param integer $lifetime How long the cache entry is valid. + * + * @return static This query instance. + */ + public function setResultCacheLifetime($lifetime) + { + $lifetime = ($lifetime !== null) ? (int) $lifetime : 0; + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setLifetime($lifetime) + : new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl()); + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @deprecated + * + * @return integer + */ + public function getResultCacheLifetime() + { + return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0; + } + + /** + * Defines if the result cache is active or not. + * + * @param boolean $expire Whether or not to force resultset cache expiration. + * + * @return static This query instance. + */ + public function expireResultCache($expire = true) + { + $this->_expireResultCache = $expire; + + return $this; + } + + /** + * Retrieves if the resultset cache is active or not. + * + * @return boolean + */ + public function getExpireResultCache() + { + return $this->_expireResultCache; + } + + /** + * @return QueryCacheProfile + */ + public function getQueryCacheProfile() + { + return $this->_queryCacheProfile; + } + + /** + * Change the default fetch mode of an association for this query. + * + * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY + * + * @param string $class + * @param string $assocName + * @param int $fetchMode + * + * @return static This query instance. + */ + public function setFetchMode($class, $assocName, $fetchMode) + { + if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) { + $fetchMode = Mapping\ClassMetadata::FETCH_LAZY; + } + + $this->_hints['fetchMode'][$class][$assocName] = $fetchMode; + + return $this; + } + + /** + * Defines the processing mode to be used during hydration / result set transformation. + * + * @param integer $hydrationMode Doctrine processing mode to be used during hydration process. + * One of the Query::HYDRATE_* constants. + * + * @return static This query instance. + */ + public function setHydrationMode($hydrationMode) + { + $this->_hydrationMode = $hydrationMode; + + return $this; + } + + /** + * Gets the hydration mode currently used by the query. + * + * @return integer + */ + public function getHydrationMode() + { + return $this->_hydrationMode; + } + + /** + * Gets the list of results for the query. + * + * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT). + * + * @param int $hydrationMode + * + * @return array + */ + public function getResult($hydrationMode = self::HYDRATE_OBJECT) + { + return $this->execute(null, $hydrationMode); + } + + /** + * Gets the array of results for the query. + * + * Alias for execute(null, HYDRATE_ARRAY). + * + * @return array + */ + public function getArrayResult() + { + return $this->execute(null, self::HYDRATE_ARRAY); + } + + /** + * Gets the scalar results for the query. + * + * Alias for execute(null, HYDRATE_SCALAR). + * + * @return array + */ + public function getScalarResult() + { + return $this->execute(null, self::HYDRATE_SCALAR); + } + + /** + * Get exactly one result or null. + * + * @param int $hydrationMode + * + * @return mixed + * + * @throws NonUniqueResultException + */ + public function getOneOrNullResult($hydrationMode = null) + { + try { + $result = $this->execute(null, $hydrationMode); + } catch (NoResultException $e) { + return null; + } + + + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + return null; + } + + if ( ! is_array($result)) { + return $result; + } + + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); + } + + /** + * Gets the single result of the query. + * + * Enforces the presence as well as the uniqueness of the result. + * + * If the result is not unique, a NonUniqueResultException is thrown. + * If there is no result, a NoResultException is thrown. + * + * @param integer $hydrationMode + * + * @return mixed + * + * @throws NonUniqueResultException If the query result is not unique. + * @throws NoResultException If the query returned no result. + */ + public function getSingleResult($hydrationMode = null) + { + $result = $this->execute(null, $hydrationMode); + + if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { + throw new NoResultException; + } + + if ( ! is_array($result)) { + return $result; + } + + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); + } + + /** + * Gets the single scalar result of the query. + * + * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). + * + * @return mixed + * + * @throws NonUniqueResultException If the query result is not unique. + * @throws NoResultException If the query returned no result. + */ + public function getSingleScalarResult() + { + return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); + } + + /** + * Sets a query hint. If the hint name is not recognized, it is silently ignored. + * + * @param string $name The name of the hint. + * @param mixed $value The value of the hint. + * + * @return static This query instance. + */ + public function setHint($name, $value) + { + $this->_hints[$name] = $value; + + return $this; + } + + /** + * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned. + * + * @param string $name The name of the hint. + * + * @return mixed The value of the hint or FALSE, if the hint name is not recognized. + */ + public function getHint($name) + { + return isset($this->_hints[$name]) ? $this->_hints[$name] : false; + } + + /** + * Check if the query has a hint + * + * @param string $name The name of the hint + * + * @return bool False if the query does not have any hint + */ + public function hasHint($name) + { + return isset($this->_hints[$name]); + } + + /** + * Return the key value map of query hints that are currently set. + * + * @return array + */ + public function getHints() + { + return $this->_hints; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterate over the result. + * + * @param ArrayCollection|array|null $parameters The query parameters. + * @param integer|null $hydrationMode The hydration mode to use. + * + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate($parameters = null, $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ( ! empty($parameters)) { + $this->setParameters($parameters); + } + + $rsm = $this->getResultSetMapping(); + $stmt = $this->_doExecute(); + + return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints); + } + + /** + * Executes the query. + * + * @param ArrayCollection|array|null $parameters Query parameters. + * @param integer|null $hydrationMode Processing mode to be used during the hydration process. + * + * @return mixed + */ + public function execute($parameters = null, $hydrationMode = null) + { + if ($this->cacheable && $this->isCacheEnabled()) { + return $this->executeUsingQueryCache($parameters, $hydrationMode); + } + + return $this->executeIgnoreQueryCache($parameters, $hydrationMode); + } + + /** + * Execute query ignoring second level cache. + * + * @param ArrayCollection|array|null $parameters + * @param integer|null $hydrationMode + * + * @return mixed + */ + private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null) + { + if ($hydrationMode !== null) { + $this->setHydrationMode($hydrationMode); + } + + if ( ! empty($parameters)) { + $this->setParameters($parameters); + } + + $setCacheEntry = function() {}; + + if ($this->_hydrationCacheProfile !== null) { + list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); + + $queryCacheProfile = $this->getHydrationCacheProfile(); + $cache = $queryCacheProfile->getResultCacheDriver(); + $result = $cache->fetch($cacheKey); + + if (isset($result[$realCacheKey])) { + return $result[$realCacheKey]; + } + + if ( ! $result) { + $result = array(); + } + + $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) { + $result[$realCacheKey] = $data; + + $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime()); + }; + } + + $stmt = $this->_doExecute(); + + if (is_numeric($stmt)) { + $setCacheEntry($stmt); + + return $stmt; + } + + $rsm = $this->getResultSetMapping(); + $data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt, $rsm, $this->_hints); + + $setCacheEntry($data); + + return $data; + } + + /** + * Load from second level cache or executes the query and put into cache. + * + * @param ArrayCollection|array|null $parameters + * @param integer|null $hydrationMode + * + * @return mixed + */ + private function executeUsingQueryCache($parameters = null, $hydrationMode = null) + { + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL); + $queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion); + $result = $queryCache->get($querykey, $rsm, $this->_hints); + + if ($result !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $querykey); + } + + return $result; + } + + $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode); + $cached = $queryCache->put($querykey, $rsm, $result, $this->_hints); + + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $querykey); + + if ($cached) { + $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $querykey); + } + } + + return $result; + } + + /** + * Get the result cache id to use to store the result set cache entry. + * Will return the configured id if it exists otherwise a hash will be + * automatically generated for you. + * + * @return array ($key, $hash) + */ + protected function getHydrationCacheId() + { + $parameters = array(); + + foreach ($this->getParameters() as $parameter) { + $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue()); + } + + $sql = $this->getSQL(); + $queryCacheProfile = $this->getHydrationCacheProfile(); + $hints = $this->getHints(); + $hints['hydrationMode'] = $this->getHydrationMode(); + + ksort($hints); + + return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints); + } + + /** + * Set the result cache id to use to store the result set cache entry. + * If this is not explicitly set by the developer then a hash is automatically + * generated for you. + * + * @param string $id + * + * @return static This query instance. + */ + public function setResultCacheId($id) + { + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setCacheKey($id) + : new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl()); + + return $this; + } + + /** + * Get the result cache id to use to store the result set cache entry if set. + * + * @deprecated + * + * @return string + */ + public function getResultCacheId() + { + return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null; + } + + /** + * Executes the query and returns a the resulting Statement object. + * + * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results. + */ + abstract protected function _doExecute(); + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + $this->parameters = new ArrayCollection(); + + $this->_hints = array(); + $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints(); + } + + /** + * Generates a string of currently query to use for the cache second level cache. + * + * @return string + */ + protected function getHash() + { + $query = $this->getSQL(); + $hints = $this->getHints(); + $params = array_map(function(Parameter $parameter) { + // Small optimization + // Does not invoke processParameterValue for scalar values + if (is_scalar($value = $parameter->getValue())) { + return $value; + } + + return $this->processParameterValue($value); + }, $this->parameters->getValues()); + + ksort($hints); + + return sha1($query . '-' . serialize($params) . '-' . serialize($hints)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache.php new file mode 100644 index 0000000..3da7c05 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache.php @@ -0,0 +1,178 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Provides an API for querying/managing the second level cache regions. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface Cache +{ + const DEFAULT_QUERY_REGION_NAME = 'query_cache_region'; + + const DEFAULT_TIMESTAMP_REGION_NAME = 'timestamp_cache_region'; + + /** + * May read items from the cache, but will not add items. + */ + const MODE_GET = 1; + + /** + * Will never read items from the cache, + * but will add items to the cache as it reads them from the database. + */ + const MODE_PUT = 2; + + /** + * May read items from the cache, and add items to the cache. + */ + const MODE_NORMAL = 3; + + /** + * The query will never read items from the cache, + * but will refresh items to the cache as it reads them from the database. + */ + const MODE_REFRESH = 4; + + /** + * @param string $className The entity class. + * + * @return \Doctrine\ORM\Cache\Region|null + */ + public function getEntityCacheRegion($className); + + /** + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * + * @return \Doctrine\ORM\Cache\Region|null + */ + public function getCollectionCacheRegion($className, $association); + + /** + * Determine whether the cache contains data for the given entity "instance". + * + * @param string $className The entity class. + * @param mixed $identifier The entity identifier + * + * @return boolean true if the underlying cache contains corresponding data; false otherwise. + */ + public function containsEntity($className, $identifier); + + /** + * Evicts the entity data for a particular entity "instance". + * + * @param string $className The entity class. + * @param mixed $identifier The entity identifier. + * + * @return void + */ + public function evictEntity($className, $identifier); + + /** + * Evicts all entity data from the given region. + * + * @param string $className The entity metadata. + * + * @return void + */ + public function evictEntityRegion($className); + + /** + * Evict data from all entity regions. + * + * @return void + */ + public function evictEntityRegions(); + + /** + * Determine whether the cache contains data for the given collection. + * + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * @param mixed $ownerIdentifier The identifier of the owning entity. + * + * @return boolean true if the underlying cache contains corresponding data; false otherwise. + */ + public function containsCollection($className, $association, $ownerIdentifier); + + /** + * Evicts the cache data for the given identified collection instance. + * + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * @param mixed $ownerIdentifier The identifier of the owning entity. + * + * @return void + */ + public function evictCollection($className, $association, $ownerIdentifier); + + /** + * Evicts all entity data from the given region. + * + * @param string $className The entity class. + * @param string $association The field name that represents the association. + * + * @return void + */ + public function evictCollectionRegion($className, $association); + + /** + * Evict data from all collection regions. + * + * @return void + */ + public function evictCollectionRegions(); + + /** + * Determine whether the cache contains data for the given query. + * + * @param string $regionName The cache name given to the query. + * + * @return boolean true if the underlying cache contains corresponding data; false otherwise. + */ + public function containsQuery($regionName); + + /** + * Evicts all cached query results under the given name, or default query cache if the region name is NULL. + * + * @param string|null $regionName The cache name associated to the queries being cached. + */ + public function evictQueryRegion($regionName = null); + + /** + * Evict data from all query regions. + * + * @return void + */ + public function evictQueryRegions(); + + /** + * Get query cache by region name or create a new one if none exist. + * + * @param string|null $regionName Query cache region name, or default query cache if the region name is NULL. + * + * @return \Doctrine\ORM\Cache\QueryCache The Query Cache associated with the region name. + */ + public function getQueryCache($regionName = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/AssociationCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/AssociationCacheEntry.php new file mode 100644 index 0000000..3aee109 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/AssociationCacheEntry.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Association cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class AssociationCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The entity identifier + */ + public $identifier; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The entity class name + */ + public $class; + + /** + * @param string $class The entity class. + * @param array $identifier The entity identifier. + */ + public function __construct($class, array $identifier) + { + $this->class = $class; + $this->identifier = $identifier; + } + + /** + * Creates a new AssociationCacheEntry + * + * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + */ + public static function __set_state(array $values) + { + return new self($values['class'], $values['identifier']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheConfiguration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheConfiguration.php new file mode 100644 index 0000000..4891cac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheConfiguration.php @@ -0,0 +1,126 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Cache\Logging\CacheLogger; + +/** + * Configuration container for second-level cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CacheConfiguration +{ + /** + * @var \Doctrine\ORM\Cache\CacheFactory|null + */ + private $cacheFactory; + + /** + * @var \Doctrine\ORM\Cache\RegionsConfiguration|null + */ + private $regionsConfig; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + private $cacheLogger; + + /** + * @var \Doctrine\ORM\Cache\QueryCacheValidator|null + */ + private $queryValidator; + + /** + * @return \Doctrine\ORM\Cache\CacheFactory|null + */ + public function getCacheFactory() + { + return $this->cacheFactory; + } + + /** + * @param \Doctrine\ORM\Cache\CacheFactory $factory + * + * @return void + */ + public function setCacheFactory(CacheFactory $factory) + { + $this->cacheFactory = $factory; + } + + /** + * @return \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + public function getCacheLogger() + { + return $this->cacheLogger; + } + + /** + * @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger + */ + public function setCacheLogger(CacheLogger $logger) + { + $this->cacheLogger = $logger; + } + + /** + * @return \Doctrine\ORM\Cache\RegionsConfiguration + */ + public function getRegionsConfiguration() + { + if ($this->regionsConfig === null) { + $this->regionsConfig = new RegionsConfiguration(); + } + + return $this->regionsConfig; + } + + /** + * @param \Doctrine\ORM\Cache\RegionsConfiguration $regionsConfig + */ + public function setRegionsConfiguration(RegionsConfiguration $regionsConfig) + { + $this->regionsConfig = $regionsConfig; + } + + /** + * @return \Doctrine\ORM\Cache\QueryCacheValidator + */ + public function getQueryValidator() + { + if ($this->queryValidator === null) { + $this->queryValidator = new TimestampQueryCacheValidator(); + } + + return $this->queryValidator; + } + + /** + * @param \Doctrine\ORM\Cache\QueryCacheValidator $validator + */ + public function setQueryValidator(QueryCacheValidator $validator) + { + $this->queryValidator = $validator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheEntry.php new file mode 100644 index 0000000..c34b0ff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheEntry.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache entry interface + * + * IMPORTANT NOTE: + * + * Fields of classes that implement CacheEntry are public for performance reason. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CacheEntry +{ + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheException.php new file mode 100644 index 0000000..5b548fe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheException.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\ORMException; + +/** + * Exception for cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CacheException extends ORMException +{ + /** + * @param string $sourceEntity + * @param string $fieldName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function updateReadOnlyCollection($sourceEntity, $fieldName) + { + return new self(sprintf('Cannot update a readonly collection "%s#%s"', $sourceEntity, $fieldName)); + } + + /** + * @param string $entityName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function updateReadOnlyEntity($entityName) + { + return new self(sprintf('Cannot update a readonly entity "%s"', $entityName)); + } + + /** + * @param string $entityName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function nonCacheableEntity($entityName) + { + return new self(sprintf('Entity "%s" not configured as part of the second-level cache.', $entityName)); + } + + /** + * @param string $entityName + * + * @return \Doctrine\ORM\Cache\CacheException + */ + public static function nonCacheableEntityAssociation($entityName, $field) + { + return new self(sprintf('Entity association field "%s#%s" not configured as part of the second-level cache.', $entityName, $field)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheFactory.php new file mode 100644 index 0000000..b915100 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheFactory.php @@ -0,0 +1,112 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * Contract for building second level cache regions components. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CacheFactory +{ + /** + * Build an entity persister for the given entity metadata. + * + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister that will be cached. + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * + * @return \Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister + */ + public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata); + + /** + * Build a collection persister for the given relation mapping. + * + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached. + * @param array $mapping The association mapping. + * + * @return \Doctrine\ORM\Cache\Persister\Collection\CachedCollectionPersister + */ + public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping); + + /** + * Build a query cache based on the given region name + * + * @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager. + * @param string $regionName The region name. + * + * @return \Doctrine\ORM\Cache\QueryCache The built query cache. + */ + public function buildQueryCache(EntityManagerInterface $em, $regionName = null); + + /** + * Build an entity hydrator + * + * @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager. + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * + * @return \Doctrine\ORM\Cache\EntityHydrator The built entity hydrator. + */ + public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata); + + /** + * Build a collection hydrator + * + * @param \Doctrine\ORM\EntityManagerInterface $em The Entity manager. + * @param array $mapping The association mapping. + * + * @return \Doctrine\ORM\Cache\CollectionHydrator The built collection hydrator. + */ + public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping); + + /** + * Build a cache region + * + * @param array $cache The cache configuration. + * + * @return \Doctrine\ORM\Cache\Region The cache region. + */ + public function getRegion(array $cache); + + /** + * Build timestamp cache region + * + * @return \Doctrine\ORM\Cache\TimestampRegion The timestamp region. + */ + public function getTimestampRegion(); + + /** + * Build \Doctrine\ORM\Cache + * + * @param EntityManagerInterface $entityManager + * + * @return \Doctrine\ORM\Cache + */ + public function createCache(EntityManagerInterface $entityManager); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheKey.php new file mode 100644 index 0000000..1641c99 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CacheKey.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines entity / collection / query key to be stored in the cache region. + * Allows multiple roles to be stored in the same cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +abstract class CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string Unique identifier + */ + public $hash; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheEntry.php new file mode 100644 index 0000000..6c634ce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheEntry.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Collection cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CollectionCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var CacheKey[] The list of entity identifiers hold by the collection + */ + public $identifiers; + + /** + * @param CacheKey[] $identifiers List of entity identifiers hold by the collection + */ + public function __construct(array $identifiers) + { + $this->identifiers = $identifiers; + } + + /** + * Creates a new CollectionCacheEntry + * + * This method allows for Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + * + * @return self + */ + public static function __set_state(array $values) + { + return new self($values['identifiers']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheKey.php new file mode 100644 index 0000000..6b63145 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionCacheKey.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines entity collection roles to be stored in the cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CollectionCacheKey extends CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The owner entity identifier + */ + public $ownerIdentifier; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The owner entity class + */ + public $entityClass; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The association name + */ + public $association; + + /** + * @param string $entityClass The entity class. + * @param string $association The field name that represents the association. + * @param array $ownerIdentifier The identifier of the owning entity. + */ + public function __construct($entityClass, $association, array $ownerIdentifier) + { + ksort($ownerIdentifier); + + $this->ownerIdentifier = $ownerIdentifier; + $this->entityClass = (string) $entityClass; + $this->association = (string) $association; + $this->hash = str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionHydrator.php new file mode 100644 index 0000000..04cdde1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/CollectionHydrator.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Hydrator cache entry for collections + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CollectionHydrator +{ + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key. + * @param array|\Doctrine\Common\Collections\Collection $collection The collection. + * + * @return \Doctrine\ORM\Cache\CollectionCacheEntry + */ + public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection); + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The owning entity metadata. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cached collection key. + * @param \Doctrine\ORM\Cache\CollectionCacheEntry $entry The cached collection entry. + * @param \Doctrine\ORM\PersistentCollection $collection The collection to load the cache into. + * + * @return array + */ + public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/ConcurrentRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/ConcurrentRegion.php new file mode 100644 index 0000000..12da6d6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/ConcurrentRegion.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines contract for concurrently managed data region. + * It should be able to lock an specific cache entry in an atomic operation. + * + * When a entry is locked another process should not be able to read or write the entry. + * All evict operation should not consider locks, even though an entry is locked evict should be able to delete the entry and its lock. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface ConcurrentRegion extends Region +{ + /** + * Attempts to read lock the mapping for the given key. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to lock. + * + * @return \Doctrine\ORM\Cache\Lock A lock instance or NULL if the lock already exists. + * + * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. + */ + public function lock(CacheKey $key); + + /** + * Attempts to read unlock the mapping for the given key. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to unlock. + * @param \Doctrine\ORM\Cache\Lock $lock The lock previously obtained from {@link readLock} + * + * @return void + * + * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. + */ + public function unlock(CacheKey $key, Lock $lock); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCache.php new file mode 100644 index 0000000..78a22a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCache.php @@ -0,0 +1,342 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Cache; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\ORMInvalidArgumentException; + +/** + * Provides an API for querying/managing the second level cache regions. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultCache implements Cache +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * @var \Doctrine\ORM\Cache\CacheFactory + */ + private $cacheFactory; + + /** + * @var \Doctrine\ORM\Cache\QueryCache[] + */ + private $queryCaches = array(); + + /** + * @var \Doctrine\ORM\Cache\QueryCache + */ + private $defaultQueryCache; + + /** + * {@inheritdoc} + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->cacheFactory = $em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheFactory(); + } + + /** + * {@inheritdoc} + */ + public function getEntityCacheRegion($className) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return null; + } + + return $persister->getCacheRegion(); + } + + /** + * {@inheritdoc} + */ + public function getCollectionCacheRegion($className, $association) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return null; + } + + return $persister->getCacheRegion(); + } + + /** + * {@inheritdoc} + */ + public function containsEntity($className, $identifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return false; + } + + return $persister->getCacheRegion()->contains($this->buildEntityCacheKey($metadata, $identifier)); + } + + /** + * {@inheritdoc} + */ + public function evictEntity($className, $identifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evict($this->buildEntityCacheKey($metadata, $identifier)); + } + + /** + * {@inheritdoc} + */ + public function evictEntityRegion($className) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evictAll(); + } + + /** + * {@inheritdoc} + */ + public function evictEntityRegions() + { + $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); + + foreach ($metadatas as $metadata) { + $persister = $this->uow->getEntityPersister($metadata->rootEntityName); + + if ( ! ($persister instanceof CachedPersister)) { + continue; + } + + $persister->getCacheRegion()->evictAll(); + } + } + + /** + * {@inheritdoc} + */ + public function containsCollection($className, $association, $ownerIdentifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return false; + } + + return $persister->getCacheRegion()->contains($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier)); + } + + /** + * {@inheritdoc} + */ + public function evictCollection($className, $association, $ownerIdentifier) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evict($this->buildCollectionCacheKey($metadata, $association, $ownerIdentifier)); + } + + /** + * {@inheritdoc} + */ + public function evictCollectionRegion($className, $association) + { + $metadata = $this->em->getClassMetadata($className); + $persister = $this->uow->getCollectionPersister($metadata->getAssociationMapping($association)); + + if ( ! ($persister instanceof CachedPersister)) { + return; + } + + $persister->getCacheRegion()->evictAll(); + } + + /** + * {@inheritdoc} + */ + public function evictCollectionRegions() + { + $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); + + foreach ($metadatas as $metadata) { + + foreach ($metadata->associationMappings as $association) { + + if ( ! $association['type'] & ClassMetadata::TO_MANY) { + continue; + } + + $persister = $this->uow->getCollectionPersister($association); + + if ( ! ($persister instanceof CachedPersister)) { + continue; + } + + $persister->getCacheRegion()->evictAll(); + } + } + } + + /** + * {@inheritdoc} + */ + public function containsQuery($regionName) + { + return isset($this->queryCaches[$regionName]); + } + + /** + * {@inheritdoc} + */ + public function evictQueryRegion($regionName = null) + { + if ($regionName === null && $this->defaultQueryCache !== null) { + $this->defaultQueryCache->clear(); + + return; + } + + if (isset($this->queryCaches[$regionName])) { + $this->queryCaches[$regionName]->clear(); + } + } + + /** + * {@inheritdoc} + */ + public function evictQueryRegions() + { + $this->getQueryCache()->clear(); + + foreach ($this->queryCaches as $queryCache) { + $queryCache->clear(); + } + } + + /** + * {@inheritdoc} + */ + public function getQueryCache($regionName = null) + { + if ($regionName === null) { + return $this->defaultQueryCache ?: + $this->defaultQueryCache = $this->cacheFactory->buildQueryCache($this->em); + } + + if ( ! isset($this->queryCaches[$regionName])) { + $this->queryCaches[$regionName] = $this->cacheFactory->buildQueryCache($this->em, $regionName); + } + + return $this->queryCaches[$regionName]; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param mixed $identifier The entity identifier. + * + * @return \Doctrine\ORM\Cache\EntityCacheKey + */ + private function buildEntityCacheKey(ClassMetadata $metadata, $identifier) + { + if ( ! is_array($identifier)) { + $identifier = $this->toIdentifierArray($metadata, $identifier); + } + + return new EntityCacheKey($metadata->rootEntityName, $identifier); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $association The field name that represents the association. + * @param mixed $ownerIdentifier The identifier of the owning entity. + * + * @return \Doctrine\ORM\Cache\CollectionCacheKey + */ + private function buildCollectionCacheKey(ClassMetadata $metadata, $association, $ownerIdentifier) + { + if ( ! is_array($ownerIdentifier)) { + $ownerIdentifier = $this->toIdentifierArray($metadata, $ownerIdentifier);; + } + + return new CollectionCacheKey($metadata->rootEntityName, $association, $ownerIdentifier); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param mixed $identifier The entity identifier. + * + * @return array + */ + private function toIdentifierArray(ClassMetadata $metadata, $identifier) + { + if (is_object($identifier) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($identifier))) { + $identifier = $this->uow->getSingleIdentifierValue($identifier); + + if ($identifier === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + + return array($metadata->identifier[0] => $identifier); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php new file mode 100644 index 0000000..82843d3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCacheFactory.php @@ -0,0 +1,254 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\Common\Cache\Cache as CacheAdapter; +use Doctrine\Common\Cache\CacheProvider; +use Doctrine\Common\Cache\MultiGetCache; +use Doctrine\ORM\Cache; +use Doctrine\ORM\Cache\Persister\Collection\NonStrictReadWriteCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Collection\ReadOnlyCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister; +use Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister; +use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister; +use Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\Region\DefaultMultiGetRegion; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache\Region\FileLockRegion; +use Doctrine\ORM\Cache\Region\UpdateTimestampCache; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultCacheFactory implements CacheFactory +{ + /** + * @var CacheAdapter + */ + private $cache; + + /** + * @var \Doctrine\ORM\Cache\RegionsConfiguration + */ + private $regionsConfig; + + /** + * @var \Doctrine\ORM\Cache\TimestampRegion|null + */ + private $timestampRegion; + + /** + * @var \Doctrine\ORM\Cache\Region[] + */ + private $regions = array(); + + /** + * @var string|null + */ + private $fileLockRegionDirectory; + + /** + * @param RegionsConfiguration $cacheConfig + * @param CacheAdapter $cache + */ + public function __construct(RegionsConfiguration $cacheConfig, CacheAdapter $cache) + { + $this->cache = $cache; + $this->regionsConfig = $cacheConfig; + } + + /** + * @param string $fileLockRegionDirectory + */ + public function setFileLockRegionDirectory($fileLockRegionDirectory) + { + $this->fileLockRegionDirectory = (string) $fileLockRegionDirectory; + } + + /** + * @return string + */ + public function getFileLockRegionDirectory() + { + return $this->fileLockRegionDirectory; + } + + /** + * @param \Doctrine\ORM\Cache\Region $region + */ + public function setRegion(Region $region) + { + $this->regions[$region->getName()] = $region; + } + + /** + * @param \Doctrine\ORM\Cache\TimestampRegion $region + */ + public function setTimestampRegion(TimestampRegion $region) + { + $this->timestampRegion = $region; + } + + /** + * {@inheritdoc} + */ + public function buildCachedEntityPersister(EntityManagerInterface $em, EntityPersister $persister, ClassMetadata $metadata) + { + $region = $this->getRegion($metadata->cache); + $usage = $metadata->cache['usage']; + + if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) { + return new ReadOnlyCachedEntityPersister($persister, $region, $em, $metadata); + } + + if ($usage === ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE) { + return new NonStrictReadWriteCachedEntityPersister($persister, $region, $em, $metadata); + } + + if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) { + return new ReadWriteCachedEntityPersister($persister, $region, $em, $metadata); + } + + throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage)); + } + + /** + * {@inheritdoc} + */ + public function buildCachedCollectionPersister(EntityManagerInterface $em, CollectionPersister $persister, array $mapping) + { + $usage = $mapping['cache']['usage']; + $region = $this->getRegion($mapping['cache']); + + if ($usage === ClassMetadata::CACHE_USAGE_READ_ONLY) { + return new ReadOnlyCachedCollectionPersister($persister, $region, $em, $mapping); + } + + if ($usage === ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE) { + return new NonStrictReadWriteCachedCollectionPersister($persister, $region, $em, $mapping); + } + + if ($usage === ClassMetadata::CACHE_USAGE_READ_WRITE) { + return new ReadWriteCachedCollectionPersister($persister, $region, $em, $mapping); + } + + throw new \InvalidArgumentException(sprintf("Unrecognized access strategy type [%s]", $usage)); + } + + /** + * {@inheritdoc} + */ + public function buildQueryCache(EntityManagerInterface $em, $regionName = null) + { + return new DefaultQueryCache( + $em, + $this->getRegion( + array( + 'region' => $regionName ?: Cache::DEFAULT_QUERY_REGION_NAME, + 'usage' => ClassMetadata::CACHE_USAGE_NONSTRICT_READ_WRITE + ) + ) + ); + } + + /** + * {@inheritdoc} + */ + public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping) + { + return new DefaultCollectionHydrator($em); + } + + /** + * {@inheritdoc} + */ + public function buildEntityHydrator(EntityManagerInterface $em, ClassMetadata $metadata) + { + return new DefaultEntityHydrator($em); + } + + /** + * {@inheritdoc} + */ + public function getRegion(array $cache) + { + if (isset($this->regions[$cache['region']])) { + return $this->regions[$cache['region']]; + } + + $cacheAdapter = clone $this->cache; + + if ($cacheAdapter instanceof CacheProvider) { + $cacheAdapter->setNamespace($cache['region']); + } + + $name = $cache['region']; + $lifetime = $this->regionsConfig->getLifetime($cache['region']); + + $region = ($cacheAdapter instanceof MultiGetCache) + ? new DefaultMultiGetRegion($name, $cacheAdapter, $lifetime) + : new DefaultRegion($name, $cacheAdapter, $lifetime); + + if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) { + + if ( ! $this->fileLockRegionDirectory) { + throw new \LogicException( + 'If you what to use a "READ_WRITE" cache an implementation of "Doctrine\ORM\Cache\ConcurrentRegion" is required, ' . + 'The default implementation provided by doctrine is "Doctrine\ORM\Cache\Region\FileLockRegion" if you what to use it please provide a valid directory, DefaultCacheFactory#setFileLockRegionDirectory(). ' + ); + } + + $directory = $this->fileLockRegionDirectory . DIRECTORY_SEPARATOR . $cache['region']; + $region = new FileLockRegion($region, $directory, $this->regionsConfig->getLockLifetime($cache['region'])); + } + + return $this->regions[$cache['region']] = $region; + } + + /** + * {@inheritdoc} + */ + public function getTimestampRegion() + { + if ($this->timestampRegion === null) { + $name = Cache::DEFAULT_TIMESTAMP_REGION_NAME; + $lifetime = $this->regionsConfig->getLifetime($name); + + $this->timestampRegion = new UpdateTimestampCache($name, clone $this->cache, $lifetime); + } + + return $this->timestampRegion; + } + + /** + * {@inheritdoc} + */ + public function createCache(EntityManagerInterface $em) + { + return new DefaultCache($em); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php new file mode 100644 index 0000000..f6583cb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultCollectionHydrator.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Query; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Default hydrator cache for collections + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultCollectionHydrator implements CollectionHydrator +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * @var array + */ + private static $hints = array(Query::HINT_CACHE_ENABLED => true); + + /** + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + } + + /** + * {@inheritdoc} + */ + public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection) + { + $data = array(); + + foreach ($collection as $index => $entity) { + $data[$index] = new EntityCacheKey($metadata->name, $this->uow->getEntityIdentifier($entity)); + } + return new CollectionCacheEntry($data); + } + + /** + * {@inheritdoc} + */ + public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection) + { + $assoc = $metadata->associationMappings[$key->association]; + /* @var $targetPersister \Doctrine\ORM\Cache\Persister\CachedPersister */ + $targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $targetRegion = $targetPersister->getCacheRegion(); + $list = array(); + + $entityEntries = $targetRegion->getMultiple($entry); + + if ($entityEntries === null) { + return null; + } + + /* @var $entityEntries \Doctrine\ORM\Cache\EntityCacheEntry[] */ + foreach ($entityEntries as $index => $entityEntry) { + $list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); + } + + array_walk($list, function($entity, $index) use ($collection) { + $collection->hydrateSet($index, $entity); + }); + + $this->uow->hydrationComplete(); + + return $list; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php new file mode 100644 index 0000000..200af9c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php @@ -0,0 +1,192 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\Common\Util\ClassUtils; + +use Doctrine\ORM\Query; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Utility\IdentifierFlattener; + +/** + * Default hydrator cache for entities + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultEntityHydrator implements EntityHydrator +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * @var array + */ + private static $hints = array(Query::HINT_CACHE_ENABLED => true); + + /** + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); + } + + /** + * {@inheritdoc} + */ + public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity) + { + $data = $this->uow->getOriginalEntityData($entity); + $data = array_merge($data, $key->identifier); // why update has no identifier values ? + + foreach ($metadata->associationMappings as $name => $assoc) { + + if ( ! isset($data[$name])) { + continue; + } + + if (! ($assoc['type'] & ClassMetadata::TO_ONE)) { + unset($data[$name]); + continue; + } + + if ( ! isset($assoc['cache'])) { + $targetClassMetadata = $this->em->getClassMetadata($assoc['targetEntity']); + $associationIds = $this->identifierFlattener->flattenIdentifier($targetClassMetadata, $targetClassMetadata->getIdentifierValues($data[$name])); + unset($data[$name]); + + foreach ($associationIds as $fieldName => $fieldValue) { + + if (isset($targetClassMetadata->associationMappings[$fieldName])){ + $targetAssoc = $targetClassMetadata->associationMappings[$fieldName]; + + foreach($assoc['targetToSourceKeyColumns'] as $referencedColumn => $localColumn) { + + if (isset($targetAssoc['sourceToTargetKeyColumns'][$referencedColumn])) { + $data[$localColumn] = $fieldValue; + } + } + }else{ + $data[$assoc['targetToSourceKeyColumns'][$targetClassMetadata->columnNames[$fieldName]]] = $fieldValue; + } + } + + continue; + } + + if ( ! isset($assoc['id'])) { + $targetClass = ClassUtils::getClass($data[$name]); + $targetId = $this->uow->getEntityIdentifier($data[$name]); + $data[$name] = new AssociationCacheEntry($targetClass, $targetId); + + continue; + } + + // handle association identifier + $targetId = is_object($data[$name]) && $this->uow->isInIdentityMap($data[$name]) + ? $this->uow->getEntityIdentifier($data[$name]) + : $data[$name]; + + // @TODO - fix it ! + // handle UnitOfWork#createEntity hash generation + if ( ! is_array($targetId)) { + + $data[reset($assoc['joinColumnFieldNames'])] = $targetId; + + $targetEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $targetId = array($targetEntity->identifier[0] => $targetId); + } + + $data[$name] = new AssociationCacheEntry($assoc['targetEntity'], $targetId); + } + + return new EntityCacheEntry($metadata->name, $data); + } + + /** + * {@inheritdoc} + */ + public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null) + { + $data = $entry->data; + $hints = self::$hints; + + if ($entity !== null) { + $hints[Query::HINT_REFRESH] = true; + $hints[Query::HINT_REFRESH_ENTITY] = $entity; + } + + foreach ($metadata->associationMappings as $name => $assoc) { + if ( ! isset($assoc['cache']) || ! isset($data[$name])) { + continue; + } + + $assocClass = $data[$name]->class; + $assocId = $data[$name]->identifier; + $isEagerLoad = ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ($assoc['type'] === ClassMetadata::ONE_TO_ONE && ! $assoc['isOwningSide'])); + + if ( ! $isEagerLoad) { + $data[$name] = $this->em->getReference($assocClass, $assocId); + + continue; + } + + $assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId); + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocRegion = $assocPersister->getCacheRegion(); + $assocEntry = $assocRegion->get($assocKey); + + if ($assocEntry === null) { + return null; + } + + $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), $hints); + } + + if ($entity !== null) { + $this->uow->registerManaged($entity, $key->identifier, $data); + } + + $result = $this->uow->createEntity($entry->class, $data, $hints); + + $this->uow->hydrationComplete(); + + return $result; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php new file mode 100644 index 0000000..87bb136 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php @@ -0,0 +1,342 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\Common\Proxy\Proxy; +use Doctrine\ORM\Cache; +use Doctrine\ORM\Query; + +/** + * Default query cache implementation. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultQueryCache implements QueryCache +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork + */ + private $uow; + + /** + * @var \Doctrine\ORM\Cache\Region + */ + private $region; + + /** + * @var \Doctrine\ORM\Cache\QueryCacheValidator + */ + private $validator; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger + */ + protected $cacheLogger; + + /** + * @var array + */ + private static $hints = array(Query::HINT_CACHE_ENABLED => true); + + /** + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Cache\Region $region The query region. + */ + public function __construct(EntityManagerInterface $em, Region $region) + { + $cacheConfig = $em->getConfiguration()->getSecondLevelCacheConfiguration(); + + $this->em = $em; + $this->region = $region; + $this->uow = $em->getUnitOfWork(); + $this->cacheLogger = $cacheConfig->getCacheLogger(); + $this->validator = $cacheConfig->getQueryValidator(); + } + + /** + * {@inheritdoc} + */ + public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = array()) + { + if ( ! ($key->cacheMode & Cache::MODE_GET)) { + return null; + } + + $entry = $this->region->get($key); + + if ( ! $entry instanceof QueryCacheEntry) { + return null; + } + + if ( ! $this->validator->isValid($key, $entry)) { + $this->region->evict($key); + + return null; + } + + $result = array(); + $entityName = reset($rsm->aliasMap); + $hasRelation = ( ! empty($rsm->relationMap)); + $persister = $this->uow->getEntityPersister($entityName); + $region = $persister->getCacheRegion(); + $regionName = $region->getName(); + + // @TODO - move to cache hydration component + foreach ($entry->result as $index => $entry) { + + if (($entityEntry = $region->get($entityKey = new EntityCacheKey($entityName, $entry['identifier']))) === null) { + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheMiss($regionName, $entityKey); + } + + return null; + } + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheHit($regionName, $entityKey); + } + + if ( ! $hasRelation) { + + $result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints); + + continue; + } + + $data = $entityEntry->data; + + foreach ($entry['associations'] as $name => $assoc) { + + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocRegion = $assocPersister->getCacheRegion(); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + + if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assoc['identifier']))) === null) { + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey); + } + + $this->uow->hydrationComplete(); + + return null; + } + + $data[$name] = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey); + } + + continue; + } + + if ( ! isset($assoc['list']) || empty($assoc['list'])) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $collection = new PersistentCollection($this->em, $targetClass, new ArrayCollection()); + + foreach ($assoc['list'] as $assocIndex => $assocId) { + + if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId))) === null) { + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey); + } + + $this->uow->hydrationComplete(); + + return null; + } + + $element = $this->uow->createEntity($assocEntry->class, $assocEntry->resolveAssociationEntries($this->em), self::$hints); + + $collection->hydrateSet($assocIndex, $element); + + if ($this->cacheLogger !== null) { + $this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey); + } + } + + $data[$name] = $collection; + + $collection->setInitialized(true); + } + + $result[$index] = $this->uow->createEntity($entityEntry->class, $data, self::$hints); + } + + $this->uow->hydrationComplete(); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = array()) + { + if ($rsm->scalarMappings) { + throw new CacheException("Second level cache does not support scalar results."); + } + + if (count($rsm->entityMappings) > 1) { + throw new CacheException("Second level cache does not support multiple root entities."); + } + + if ( ! $rsm->isSelect) { + throw new CacheException("Second-level cache query supports only select statements."); + } + + if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD]) && $hints[Query::HINT_FORCE_PARTIAL_LOAD]) { + throw new CacheException("Second level cache does not support partial entities."); + } + + if ( ! ($key->cacheMode & Cache::MODE_PUT)) { + return false; + } + + $data = array(); + $entityName = reset($rsm->aliasMap); + $hasRelation = ( ! empty($rsm->relationMap)); + $metadata = $this->em->getClassMetadata($entityName); + $persister = $this->uow->getEntityPersister($entityName); + + if ( ! ($persister instanceof CachedPersister)) { + throw CacheException::nonCacheableEntity($entityName); + } + + $region = $persister->getCacheRegion(); + + foreach ($result as $index => $entity) { + $identifier = $this->uow->getEntityIdentifier($entity); + $data[$index]['identifier'] = $identifier; + $data[$index]['associations'] = array(); + + if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey = new EntityCacheKey($entityName, $identifier))) { + // Cancel put result if entity put fail + if ( ! $persister->storeEntityCache($entity, $entityKey)) { + return false; + } + } + + if ( ! $hasRelation) { + continue; + } + + // @TODO - move to cache hydration components + foreach ($rsm->relationMap as $name) { + $assoc = $metadata->associationMappings[$name]; + + if (($assocValue = $metadata->getFieldValue($entity, $name)) === null || $assocValue instanceof Proxy) { + continue; + } + + if ( ! isset($assoc['cache'])) { + throw CacheException::nonCacheableEntityAssociation($entityName, $name); + } + + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + $assocRegion = $assocPersister->getCacheRegion(); + $assocMetadata = $assocPersister->getClassMetadata(); + + // Handle *-to-one associations + if ($assoc['type'] & ClassMetadata::TO_ONE) { + + $assocIdentifier = $this->uow->getEntityIdentifier($assocValue); + + if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier))) { + + // Cancel put result if association entity put fail + if ( ! $assocPersister->storeEntityCache($assocValue, $entityKey)) { + return false; + } + } + + $data[$index]['associations'][$name] = array( + 'targetEntity' => $assocMetadata->rootEntityName, + 'identifier' => $assocIdentifier, + 'type' => $assoc['type'] + ); + + continue; + } + + // Handle *-to-many associations + $list = array(); + + foreach ($assocValue as $assocItemIndex => $assocItem) { + $assocIdentifier = $this->uow->getEntityIdentifier($assocItem); + + if (($key->cacheMode & Cache::MODE_REFRESH) || ! $assocRegion->contains($entityKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocIdentifier))) { + + // Cancel put result if entity put fail + if ( ! $assocPersister->storeEntityCache($assocItem, $entityKey)) { + return false; + } + } + + $list[$assocItemIndex] = $assocIdentifier; + } + + $data[$index]['associations'][$name] = array( + 'targetEntity' => $assocMetadata->rootEntityName, + 'type' => $assoc['type'], + 'list' => $list, + ); + } + } + + return $this->region->put($key, new QueryCacheEntry($data)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->region->evictAll(); + } + + /** + * {@inheritdoc} + */ + public function getRegion() + { + return $this->region; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheEntry.php new file mode 100644 index 0000000..ece7fae --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheEntry.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Entity cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class EntityCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The entity map data + */ + public $data; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The entity class name + */ + public $class; + + /** + * @param string $class The entity class. + * @param array $data The entity data. + */ + public function __construct($class, array $data) + { + $this->class = $class; + $this->data = $data; + } + + /** + * Creates a new EntityCacheEntry + * + * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + */ + public static function __set_state(array $values) + { + return new self($values['class'], $values['data']); + } + + /** + * Retrieves the entity data resolving cache entries + * + * @param \Doctrine\ORM\EntityManagerInterfac $em + * + * @return array + */ + public function resolveAssociationEntries(EntityManagerInterface $em) + { + return array_map(function($value) use ($em) { + if ( ! ($value instanceof AssociationCacheEntry)) { + return $value; + } + + return $em->getReference($value->class, $value->identifier); + }, $this->data); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php new file mode 100644 index 0000000..281e610 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines entity classes roles to be stored in the cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class EntityCacheKey extends CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array The entity identifier + */ + public $identifier; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var string The entity class name + */ + public $entityClass; + + /** + * @param string $entityClass The entity class name. In a inheritance hierarchy it should always be the root entity class. + * @param array $identifier The entity identifier + */ + public function __construct($entityClass, array $identifier) + { + ksort($identifier); + + $this->identifier = $identifier; + $this->entityClass = $entityClass; + $this->hash = str_replace('\\', '.', strtolower($entityClass) . '_' . implode(' ', $identifier)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityHydrator.php new file mode 100644 index 0000000..05a394d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityHydrator.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Hydrator cache entry for entities + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface EntityHydrator +{ + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key. + * @param object $entity The entity. + * + * @return \Doctrine\ORM\Cache\EntityCacheEntry + */ + public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity); + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The entity cache key. + * @param \Doctrine\ORM\Cache\EntityCacheEntry $entry The entity cache entry. + * @param object $entity The entity to load the cache into. If not specified, a new entity is created. + */ + public function loadCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, EntityCacheEntry $entry, $entity = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Lock.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Lock.php new file mode 100644 index 0000000..5346aa3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Lock.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache Lock + * + * @since 2.5 + * @author Fabio B. Silva + */ +class Lock +{ + /** + * @var string + */ + public $value; + + /** + * @var integer + */ + public $time; + + /** + * @param string $value + * @param integer $time + */ + public function __construct($value, $time = null) + { + $this->value = $value; + $this->time = $time ? : time(); + } + + /** + * @return \Doctrine\ORM\Cache\Lock + */ + public static function createLockRead() + { + return new self(uniqid(time())); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/LockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/LockException.php new file mode 100644 index 0000000..d4c7624 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/LockException.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Lock exception for cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class LockException extends CacheException +{ + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLogger.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLogger.php new file mode 100644 index 0000000..bdae4ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLogger.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Cache\Logging; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; + +/** + * Interface for logging. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CacheLogger +{ + /** + * Log an entity put into second level cache. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity. + */ + public function entityCachePut($regionName, EntityCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a hit. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity. + */ + public function entityCacheHit($regionName, EntityCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a miss. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\EntityCacheKey $key The cache key of the entity. + */ + public function entityCacheMiss($regionName, EntityCacheKey $key); + + /** + * Log an entity put into second level cache. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection. + */ + public function collectionCachePut($regionName, CollectionCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a hit. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection. + */ + public function collectionCacheHit($regionName, CollectionCacheKey $key); + + /** + * Log an entity get from second level cache resulted in a miss. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key The cache key of the collection. + */ + public function collectionCacheMiss($regionName, CollectionCacheKey $key); + + /** + * Log a query put into the query cache. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query. + */ + public function queryCachePut($regionName, QueryCacheKey $key); + + /** + * Log a query get from the query cache resulted in a hit. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query. + */ + public function queryCacheHit($regionName, QueryCacheKey $key); + + /** + * Log a query get from the query cache resulted in a miss. + * + * @param string $regionName The name of the cache region. + * @param \Doctrine\ORM\Cache\QueryCacheKey $key The cache key of the query. + */ + public function queryCacheMiss($regionName, QueryCacheKey $key); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php new file mode 100644 index 0000000..694b35c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/CacheLoggerChain.php @@ -0,0 +1,156 @@ +. + */ + +namespace Doctrine\ORM\Cache\Logging; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; + +/** + * Cache logger chain + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CacheLoggerChain implements CacheLogger +{ + /** + * @var array<\Doctrine\ORM\Cache\Logging\CacheLogger> + */ + private $loggers = array(); + + /** + * @param string $name + * @param \Doctrine\ORM\Cache\Logging\CacheLogger $logger + */ + public function setLogger($name, CacheLogger $logger) + { + $this->loggers[$name] = $logger; + } + + /** + * @param string $name + * + * @return \Doctrine\ORM\Cache\Logging\CacheLogger|null + */ + public function getLogger($name) + { + return isset($this->loggers[$name]) ? $this->loggers[$name] : null; + } + + /** + * @return array<\Doctrine\ORM\Cache\Logging\CacheLogger> + */ + public function getLoggers() + { + return $this->loggers; + } + + /** + * {@inheritdoc} + */ + public function collectionCacheHit($regionName, CollectionCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->collectionCacheHit($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function collectionCacheMiss($regionName, CollectionCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->collectionCacheMiss($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function collectionCachePut($regionName, CollectionCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->collectionCachePut($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function entityCacheHit($regionName, EntityCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->entityCacheHit($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function entityCacheMiss($regionName, EntityCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->entityCacheMiss($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function entityCachePut($regionName, EntityCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->entityCachePut($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function queryCacheHit($regionName, QueryCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->queryCacheHit($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function queryCacheMiss($regionName, QueryCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->queryCacheMiss($regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function queryCachePut($regionName, QueryCacheKey $key) + { + foreach ($this->loggers as $logger) { + $logger->queryCachePut($regionName, $key); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/StatisticsCacheLogger.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/StatisticsCacheLogger.php new file mode 100644 index 0000000..2fbba40 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Logging/StatisticsCacheLogger.php @@ -0,0 +1,251 @@ +. + */ + +namespace Doctrine\ORM\Cache\Logging; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; + +/** + * Provide basic second level cache statistics. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class StatisticsCacheLogger implements CacheLogger +{ + /** + * @var array + */ + private $cacheMissCountMap = array(); + + /** + * @var array + */ + private $cacheHitCountMap = array(); + + /** + * @var array + */ + private $cachePutCountMap = array(); + + /** + * {@inheritdoc} + */ + public function collectionCacheMiss($regionName, CollectionCacheKey $key) + { + $this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName]) + ? $this->cacheMissCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function collectionCacheHit($regionName, CollectionCacheKey $key) + { + $this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName]) + ? $this->cacheHitCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function collectionCachePut($regionName, CollectionCacheKey $key) + { + $this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName]) + ? $this->cachePutCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function entityCacheMiss($regionName, EntityCacheKey $key) + { + $this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName]) + ? $this->cacheMissCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function entityCacheHit($regionName, EntityCacheKey $key) + { + $this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName]) + ? $this->cacheHitCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function entityCachePut($regionName, EntityCacheKey $key) + { + $this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName]) + ? $this->cachePutCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function queryCacheHit($regionName, QueryCacheKey $key) + { + $this->cacheHitCountMap[$regionName] = isset($this->cacheHitCountMap[$regionName]) + ? $this->cacheHitCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function queryCacheMiss($regionName, QueryCacheKey $key) + { + $this->cacheMissCountMap[$regionName] = isset($this->cacheMissCountMap[$regionName]) + ? $this->cacheMissCountMap[$regionName] + 1 + : 1; + } + + /** + * {@inheritdoc} + */ + public function queryCachePut($regionName, QueryCacheKey $key) + { + $this->cachePutCountMap[$regionName] = isset($this->cachePutCountMap[$regionName]) + ? $this->cachePutCountMap[$regionName] + 1 + : 1; + } + + /** + * Get the number of entries successfully retrieved from cache. + * + * @param string $regionName The name of the cache region. + * + * @return integer + */ + public function getRegionHitCount($regionName) + { + return isset($this->cacheHitCountMap[$regionName]) ? $this->cacheHitCountMap[$regionName] : 0; + } + + /** + * Get the number of cached entries *not* found in cache. + * + * @param string $regionName The name of the cache region. + * + * @return integer + */ + public function getRegionMissCount($regionName) + { + return isset($this->cacheMissCountMap[$regionName]) ? $this->cacheMissCountMap[$regionName] : 0; + } + + /** + * Get the number of cacheable entries put in cache. + * + * @param string $regionName The name of the cache region. + * + * @return integer + */ + public function getRegionPutCount($regionName) + { + return isset($this->cachePutCountMap[$regionName]) ? $this->cachePutCountMap[$regionName] : 0; + } + + /** + * @return array + */ + public function getRegionsMiss() + { + return $this->cacheMissCountMap; + } + + /** + * @return array + */ + public function getRegionsHit() + { + return $this->cacheHitCountMap; + } + + /** + * @return array + */ + public function getRegionsPut() + { + return $this->cachePutCountMap; + } + + /** + * Clear region statistics + * + * @param string $regionName The name of the cache region. + */ + public function clearRegionStats($regionName) + { + $this->cachePutCountMap[$regionName] = 0; + $this->cacheHitCountMap[$regionName] = 0; + $this->cacheMissCountMap[$regionName] = 0; + } + + /** + * Clear all statistics + */ + public function clearStats() + { + $this->cachePutCountMap = array(); + $this->cacheHitCountMap = array(); + $this->cacheMissCountMap = array(); + } + + /** + * Get the total number of put in cache. + * + * @return integer + */ + public function getPutCount() + { + return array_sum($this->cachePutCountMap); + } + + /** + * Get the total number of entries successfully retrieved from cache. + * + * @return integer + */ + public function getHitCount() + { + return array_sum($this->cacheHitCountMap); + } + + /** + * Get the total number of cached entries *not* found in cache. + * + * @return integer + */ + public function getMissCount() + { + return array_sum($this->cacheMissCountMap); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/MultiGetRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/MultiGetRegion.php new file mode 100644 index 0000000..be32b08 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/MultiGetRegion.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines a region that supports multi-get reading. + * + * With one method call we can get multiple items. + * + * @since 2.5 + * @author Asmir Mustafic + */ +interface MultiGetRegion +{ + /** + * Get all items from the cache identified by $keys. + * It returns NULL if some elements can not be found. + * + * @param CollectionCacheEntry $collection The collection of the items to be retrieved. + * + * @return CacheEntry[]|null The cached entries or NULL if one or more entries can not be found + */ + public function getMultiple(CollectionCacheEntry $collection); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/CachedPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/CachedPersister.php new file mode 100644 index 0000000..89afd32 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/CachedPersister.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister; + +/** + * Interface for persister that support second level cache. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface CachedPersister +{ + /** + * Perform whatever processing is encapsulated here after completion of the transaction. + */ + public function afterTransactionComplete(); + + /** + * Perform whatever processing is encapsulated here after completion of the rolled-back. + */ + public function afterTransactionRolledBack(); + + /** + * Gets the The region access. + * + * @return \Doctrine\ORM\Cache\Region + */ + public function getCacheRegion(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/AbstractCollectionPersister.php new file mode 100644 index 0000000..3172fe9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/AbstractCollectionPersister.php @@ -0,0 +1,304 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\Persister\Entity\CachedEntityPersister; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\Region; +use Doctrine\Common\Util\ClassUtils; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +abstract class AbstractCollectionPersister implements CachedCollectionPersister +{ + /** + * @var \Doctrine\ORM\UnitOfWork + */ + protected $uow; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + protected $metadataFactory; + + /** + * @var \Doctrine\ORM\Persisters\Collection\CollectionPersister + */ + protected $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $sourceEntity; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $targetEntity; + + /** + * @var array + */ + protected $association; + + /** + * @var array + */ + protected $queuedCache = array(); + + /** + * @var \Doctrine\ORM\Cache\Region + */ + protected $region; + + /** + * @var string + */ + protected $regionName; + + /** + * @var \Doctrine\ORM\Cache\CollectionHydrator + */ + protected $hydrator; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger + */ + protected $cacheLogger; + + /** + * @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached. + * @param \Doctrine\ORM\Cache\Region $region The collection region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param array $association The association mapping. + */ + public function __construct(CollectionPersister $persister, Region $region, EntityManagerInterface $em, array $association) + { + $configuration = $em->getConfiguration(); + $cacheConfig = $configuration->getSecondLevelCacheConfiguration(); + $cacheFactory = $cacheConfig->getCacheFactory(); + + $this->region = $region; + $this->persister = $persister; + $this->association = $association; + $this->regionName = $region->getName(); + $this->uow = $em->getUnitOfWork(); + $this->metadataFactory = $em->getMetadataFactory(); + $this->cacheLogger = $cacheConfig->getCacheLogger(); + $this->hydrator = $cacheFactory->buildCollectionHydrator($em, $association); + $this->sourceEntity = $em->getClassMetadata($association['sourceEntity']); + $this->targetEntity = $em->getClassMetadata($association['targetEntity']); + } + + /** + * {@inheritdoc} + */ + public function getCacheRegion() + { + return $this->region; + } + + /** + * {@inheritdoc} + */ + public function getSourceEntityMetadata() + { + return $this->sourceEntity; + } + + /** + * {@inheritdoc} + */ + public function getTargetEntityMetadata() + { + return $this->targetEntity; + } + + /** + * @param \Doctrine\ORM\PersistentCollection $collection + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key + * + * @return \Doctrine\ORM\PersistentCollection|null + */ + public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key) + { + if (($cache = $this->region->get($key)) === null) { + return null; + } + + if (($cache = $this->hydrator->loadCacheEntry($this->sourceEntity, $key, $cache, $collection)) === null) { + return null; + } + + return $cache; + } + + /** + * {@inheritdoc} + */ + public function storeCollectionCache(CollectionCacheKey $key, $elements) + { + /* @var $targetPersister CachedEntityPersister */ + $targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName); + $targetRegion = $targetPersister->getCacheRegion(); + $targetHydrator = $targetPersister->getEntityHydrator(); + $entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements); + + foreach ($entry->identifiers as $index => $entityKey) { + if ($targetRegion->contains($entityKey)) { + continue; + } + + $class = $this->targetEntity; + $className = ClassUtils::getClass($elements[$index]); + + if ($className !== $this->targetEntity->name) { + $class = $this->metadataFactory->getMetadataFor($className); + } + + $entity = $elements[$index]; + $entityEntry = $targetHydrator->buildCacheEntry($class, $entityKey, $entity); + + $targetRegion->put($entityKey, $entityEntry); + } + + $cached = $this->region->put($key, $entry); + + if ($this->cacheLogger && $cached) { + $this->cacheLogger->collectionCachePut($this->regionName, $key); + } + } + + /** + * {@inheritdoc} + */ + public function contains(PersistentCollection $collection, $element) + { + return $this->persister->contains($collection, $element); + } + + /** + * {@inheritdoc} + */ + public function containsKey(PersistentCollection $collection, $key) + { + return $this->persister->containsKey($collection, $key); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $collection) + { + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + $entry = $this->region->get($key); + + if ($entry !== null) { + return count($entry->identifiers); + } + + return $this->persister->count($collection); + } + + /** + * {@inheritdoc} + */ + public function get(PersistentCollection $collection, $index) + { + return $this->persister->get($collection, $index); + } + + /** + * {@inheritdoc} + */ + public function removeElement(PersistentCollection $collection, $element) + { + if ($persisterResult = $this->persister->removeElement($collection, $element)) { + $this->evictCollectionCache($collection); + $this->evictElementCache($this->sourceEntity->rootEntityName, $collection->getOwner()); + $this->evictElementCache($this->targetEntity->rootEntityName, $element); + } + + return $persisterResult; + } + + /** + * {@inheritdoc} + */ + public function slice(PersistentCollection $collection, $offset, $length = null) + { + return $this->persister->slice($collection, $offset, $length); + } + + /** + * {@inheritDoc} + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + { + return $this->persister->loadCriteria($collection, $criteria); + } + + /** + * Clears cache entries related to the current collection + * + * @param PersistentCollection $collection + */ + protected function evictCollectionCache(PersistentCollection $collection) + { + $key = new CollectionCacheKey( + $this->sourceEntity->rootEntityName, + $this->association['fieldName'], + $this->uow->getEntityIdentifier($collection->getOwner()) + ); + + $this->region->evict($key); + + if ($this->cacheLogger) { + $this->cacheLogger->collectionCachePut($this->regionName, $key); + } + } + + /** + * @param string $targetEntity + * @param object $element + */ + protected function evictElementCache($targetEntity, $element) + { + /* @var $targetPersister CachedEntityPersister */ + $targetPersister = $this->uow->getEntityPersister($targetEntity); + $targetRegion = $targetPersister->getCacheRegion(); + $key = new EntityCacheKey($targetEntity, $this->uow->getEntityIdentifier($element)); + + $targetRegion->evict($key); + + if ($this->cacheLogger) { + $this->cacheLogger->entityCachePut($targetRegion->getName(), $key); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/CachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/CachedCollectionPersister.php new file mode 100644 index 0000000..5722027 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/CachedCollectionPersister.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\PersistentCollection; + +/** + * Interface for second level cache collection persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface CachedCollectionPersister extends CachedPersister, CollectionPersister +{ + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getSourceEntityMetadata(); + + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getTargetEntityMetadata(); + + /** + * Loads a collection from cache + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key + * + * @return \Doctrine\ORM\PersistentCollection|null + */ + public function loadCollectionCache(PersistentCollection $collection, CollectionCacheKey $key); + + /** + * Stores a collection into cache + * + * @param \Doctrine\ORM\Cache\CollectionCacheKey $key + * @param array|\Doctrine\Common\Collections\Collection $elements + * + * @return void + */ + public function storeCollectionCache(CollectionCacheKey $key, $elements); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php new file mode 100644 index 0000000..86e5c8f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/NonStrictReadWriteCachedCollectionPersister.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\PersistentCollection; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +class NonStrictReadWriteCachedCollectionPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->storeCollectionCache($item['key'], $item['list']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $key) { + $this->region->evict($key); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + + $this->persister->delete($collection); + + $this->queuedCache['delete'][spl_object_hash($collection)] = $key; + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + $isInitialized = $collection->isInitialized(); + $isDirty = $collection->isDirty(); + + if ( ! $isInitialized && ! $isDirty) { + return; + } + + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + + // Invalidate non initialized collections OR ordered collection + if ($isDirty && ! $isInitialized || isset($this->association['orderBy'])) { + $this->persister->update($collection); + + $this->queuedCache['delete'][spl_object_hash($collection)] = $key; + + return; + } + + $this->persister->update($collection); + + $this->queuedCache['update'][spl_object_hash($collection)] = array( + 'key' => $key, + 'list' => $collection + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php new file mode 100644 index 0000000..4f35ceb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadOnlyCachedCollectionPersister.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Cache\CacheException; +use Doctrine\Common\Util\ClassUtils; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadOnlyCachedCollectionPersister extends NonStrictReadWriteCachedCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + if ($collection->isDirty() && count($collection->getSnapshot()) > 0) { + throw CacheException::updateReadOnlyCollection(ClassUtils::getClass($collection->getOwner()), $this->association['fieldName']); + } + + parent::update($collection); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php new file mode 100644 index 0000000..a92ed20 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Collection/ReadWriteCachedCollectionPersister.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Collection; + +use Doctrine\ORM\Persisters\Collection\CollectionPersister; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\ConcurrentRegion; +use Doctrine\ORM\PersistentCollection; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadWriteCachedCollectionPersister extends AbstractCollectionPersister +{ + /** + * @param \Doctrine\ORM\Persisters\Collection\CollectionPersister $persister The collection persister that will be cached. + * @param \Doctrine\ORM\Cache\ConcurrentRegion $region The collection region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param array $association The association mapping. + */ + public function __construct(CollectionPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, array $association) + { + parent::__construct($persister, $region, $em, $association); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + $lock = $this->region->lock($key); + + $this->persister->delete($collection); + + if ($lock === null) { + return; + } + + $this->queuedCache['delete'][spl_object_hash($collection)] = array( + 'key' => $key, + 'lock' => $lock + ); + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + $isInitialized = $collection->isInitialized(); + $isDirty = $collection->isDirty(); + + if ( ! $isInitialized && ! $isDirty) { + return; + } + + $this->persister->update($collection); + + $ownerId = $this->uow->getEntityIdentifier($collection->getOwner()); + $key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association['fieldName'], $ownerId); + $lock = $this->region->lock($key); + + if ($lock === null) { + return; + } + + $this->queuedCache['update'][spl_object_hash($collection)] = array( + 'key' => $key, + 'lock' => $lock + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php new file mode 100644 index 0000000..eaead7c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php @@ -0,0 +1,646 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Cache; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\CollectionCacheKey; +use Doctrine\ORM\Cache\TimestampCacheKey; +use Doctrine\ORM\Cache\QueryCacheKey; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Cache\CacheException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Common\Collections\Criteria; + +/** + * @author Fabio B. Silva + * @since 2.5 + */ +abstract class AbstractEntityPersister implements CachedEntityPersister +{ + /** + * @var \Doctrine\ORM\UnitOfWork + */ + protected $uow; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + protected $metadataFactory; + + /** + * @var \Doctrine\ORM\Persisters\Entity\EntityPersister + */ + protected $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $class; + + /** + * @var array + */ + protected $queuedCache = array(); + + /** + * @var \Doctrine\ORM\Cache\Region + */ + protected $region; + + /** + * @var \Doctrine\ORM\Cache\TimestampRegion + */ + protected $timestampRegion; + + /** + * @var \Doctrine\ORM\Cache\TimestampCacheKey + */ + protected $timestampKey; + + /** + * @var \Doctrine\ORM\Cache\EntityHydrator + */ + protected $hydrator; + + /** + * @var \Doctrine\ORM\Cache + */ + protected $cache; + + /** + * @var \Doctrine\ORM\Cache\Logging\CacheLogger + */ + protected $cacheLogger; + + /** + * @var string + */ + protected $regionName; + + /** + * Associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. + * + * @var array + */ + protected $joinedAssociations; + + /** + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache. + * @param \Doctrine\ORM\Cache\Region $region The entity cache region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Mapping\ClassMetadata $class The entity metadata. + */ + public function __construct(EntityPersister $persister, Region $region, EntityManagerInterface $em, ClassMetadata $class) + { + $configuration = $em->getConfiguration(); + $cacheConfig = $configuration->getSecondLevelCacheConfiguration(); + $cacheFactory = $cacheConfig->getCacheFactory(); + + $this->class = $class; + $this->region = $region; + $this->persister = $persister; + $this->cache = $em->getCache(); + $this->regionName = $region->getName(); + $this->uow = $em->getUnitOfWork(); + $this->metadataFactory = $em->getMetadataFactory(); + $this->cacheLogger = $cacheConfig->getCacheLogger(); + $this->timestampRegion = $cacheFactory->getTimestampRegion(); + $this->hydrator = $cacheFactory->buildEntityHydrator($em, $class); + $this->timestampKey = new TimestampCacheKey($this->class->getTableName()); + } + + /** + * {@inheritdoc} + */ + public function addInsert($entity) + { + $this->persister->addInsert($entity); + } + + /** + * {@inheritdoc} + */ + public function getInserts() + { + return $this->persister->getInserts(); + } + + /** + * {@inheritdoc} + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) + { + return $this->persister->getSelectSQL($criteria, $assoc, $lockMode, $limit, $offset, $orderBy); + } + + /** + * {@inheritDoc} + */ + public function getCountSQL($criteria = array()) + { + return $this->persister->getCountSQL($criteria); + } + + /** + * {@inheritdoc} + */ + public function getInsertSQL() + { + return $this->persister->getInsertSQL(); + } + + /** + * {@inheritdoc} + */ + public function getResultSetMapping() + { + return $this->persister->getResultSetMapping(); + } + + /** + * {@inheritdoc} + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) + { + return $this->persister->getSelectConditionStatementSQL($field, $value, $assoc, $comparison); + } + + /** + * {@inheritdoc} + */ + public function exists($entity, Criteria $extraConditions = null) + { + if (null === $extraConditions) { + $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity)); + + if ($this->region->contains($key)) { + return true; + } + } + + return $this->persister->exists($entity, $extraConditions); + } + + /** + * {@inheritdoc} + */ + public function getCacheRegion() + { + return $this->region; + } + + /** + * @return \Doctrine\ORM\Cache\EntityHydrator + */ + public function getEntityHydrator() + { + return $this->hydrator; + } + + /** + * {@inheritdoc} + */ + public function storeEntityCache($entity, EntityCacheKey $key) + { + $class = $this->class; + $className = ClassUtils::getClass($entity); + + if ($className !== $this->class->name) { + $class = $this->metadataFactory->getMetadataFor($className); + } + + if ($class->containsForeignIdentifier) { + foreach ($class->associationMappings as $name => $assoc) { + if (!empty($assoc['id']) && !isset($assoc['cache'])) { + throw CacheException::nonCacheableEntityAssociation($class->name, $name); + } + } + } + + $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); + $cached = $this->region->put($key, $entry); + + if ($this->cacheLogger && $cached) { + $this->cacheLogger->entityCachePut($this->regionName, $key); + } + + return $cached; + } + + /** + * @param object $entity + */ + private function storeJoinedAssociations($entity) + { + if ($this->joinedAssociations === null) { + $associations = array(); + + foreach ($this->class->associationMappings as $name => $assoc) { + if (isset($assoc['cache']) && + ($assoc['type'] & ClassMetadata::TO_ONE) && + ($assoc['fetch'] === ClassMetadata::FETCH_EAGER || ! $assoc['isOwningSide'])) { + + $associations[] = $name; + } + } + + $this->joinedAssociations = $associations; + } + + foreach ($this->joinedAssociations as $name) { + $assoc = $this->class->associationMappings[$name]; + $assocEntity = $this->class->getFieldValue($entity, $name); + + if ($assocEntity === null) { + continue; + } + + $assocId = $this->uow->getEntityIdentifier($assocEntity); + $assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId); + $assocPersister = $this->uow->getEntityPersister($assoc['targetEntity']); + + $assocPersister->storeEntityCache($assocEntity, $assocKey); + } + } + + /** + * Generates a string of currently query + * + * @param array $query + * @param string $criteria + * @param array $orderBy + * @param integer $limit + * @param integer $offset + * @param integer $timestamp + * + * @return string + */ + protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null) + { + list($params) = ($criteria instanceof Criteria) + ? $this->persister->expandCriteriaParameters($criteria) + : $this->persister->expandParameters($criteria); + + return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp); + } + + /** + * {@inheritdoc} + */ + public function expandParameters($criteria) + { + return $this->persister->expandParameters($criteria); + } + + /** + * {@inheritdoc} + */ + public function expandCriteriaParameters(Criteria $criteria) + { + return $this->persister->expandCriteriaParameters($criteria); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata() + { + return $this->persister->getClassMetadata(); + } + + /** + * {@inheritdoc} + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + return $this->persister->getManyToManyCollection($assoc, $sourceEntity, $offset, $limit); + } + + /** + * {@inheritdoc} + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + return $this->persister->getOneToManyCollection($assoc, $sourceEntity, $offset, $limit); + } + + /** + * {@inheritdoc} + */ + public function getOwningTable($fieldName) + { + return $this->persister->getOwningTable($fieldName); + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + $this->queuedCache['insert'] = $this->persister->getInserts(); + + return $this->persister->executeInserts(); + } + + /** + * {@inheritdoc} + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = null, $limit = null, array $orderBy = null) + { + if ($entity !== null || $assoc !== null || ! empty($hints) || $lockMode !== null) { + return $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy); + } + + //handle only EntityRepository#findOneBy + $timestamp = $this->timestampRegion->get($this->timestampKey); + $query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $result = $queryCache->get($querykey, $rsm); + + if ($result !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $result[0]; + } + + if (($result = $this->persister->load($criteria, $entity, $assoc, $hints, $lockMode, $limit, $orderBy)) === null) { + return null; + } + + $cached = $queryCache->put($querykey, $rsm, array($result)); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $timestamp = $this->timestampRegion->get($this->timestampKey); + $query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $result = $queryCache->get($querykey, $rsm); + + if ($result !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $result; + } + + $result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset); + $cached = $queryCache->put($querykey, $rsm, $result); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadById(array $identifier, $entity = null) + { + $cacheKey = new EntityCacheKey($this->class->rootEntityName, $identifier); + $cacheEntry = $this->region->get($cacheKey); + $class = $this->class; + + if ($cacheEntry !== null) { + if ($cacheEntry->class !== $this->class->name) { + $class = $this->metadataFactory->getMetadataFor($cacheEntry->class); + } + + if (($entity = $this->hydrator->loadCacheEntry($class, $cacheKey, $cacheEntry, $entity)) !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->entityCacheHit($this->regionName, $cacheKey); + } + + return $entity; + } + } + + $entity = $this->persister->loadById($identifier, $entity); + + if ($entity === null) { + return null; + } + + $class = $this->class; + $className = ClassUtils::getClass($entity); + + if ($className !== $this->class->name) { + $class = $this->metadataFactory->getMetadataFor($className); + } + + $cacheEntry = $this->hydrator->buildCacheEntry($class, $cacheKey, $entity); + $cached = $this->region->put($cacheKey, $cacheEntry); + + if ($cached && ($this->joinedAssociations === null || count($this->joinedAssociations) > 0)) { + $this->storeJoinedAssociations($entity); + } + + if ($this->cacheLogger) { + if ($cached) { + $this->cacheLogger->entityCachePut($this->regionName, $cacheKey); + } + + $this->cacheLogger->entityCacheMiss($this->regionName, $cacheKey); + } + + return $entity; + } + + /** + * {@inheritDoc} + */ + public function count($criteria = array()) + { + return $this->persister->count($criteria); + } + + /** + * {@inheritdoc} + */ + public function loadCriteria(Criteria $criteria) + { + $query = $this->persister->getSelectSQL($criteria); + $timestamp = $this->timestampRegion->get($this->timestampKey); + $hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null); + $rsm = $this->getResultSetMapping(); + $querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL); + $queryCache = $this->cache->getQueryCache($this->regionName); + $cacheResult = $queryCache->get($querykey, $rsm); + + if ($cacheResult !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->queryCacheHit($this->regionName, $querykey); + } + + return $cacheResult; + } + + $result = $this->persister->loadCriteria($criteria); + $cached = $queryCache->put($querykey, $rsm, $result); + + if ($this->cacheLogger) { + if ($result) { + $this->cacheLogger->queryCacheMiss($this->regionName, $querykey); + } + + if ($cached) { + $this->cacheLogger->queryCachePut($this->regionName, $querykey); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $persister = $this->uow->getCollectionPersister($assoc); + $hasCache = ($persister instanceof CachedPersister); + $key = null; + + if ($hasCache) { + $ownerId = $this->uow->getEntityIdentifier($coll->getOwner()); + $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId); + $list = $persister->loadCollectionCache($coll, $key); + + if ($list !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); + } + + return $list; + } + } + + $list = $this->persister->loadManyToManyCollection($assoc, $sourceEntity, $coll); + + if ($hasCache) { + $persister->storeCollectionCache($key, $list); + + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $persister = $this->uow->getCollectionPersister($assoc); + $hasCache = ($persister instanceof CachedPersister); + + if ($hasCache) { + $ownerId = $this->uow->getEntityIdentifier($coll->getOwner()); + $key = new CollectionCacheKey($assoc['sourceEntity'], $assoc['fieldName'], $ownerId); + $list = $persister->loadCollectionCache($coll, $key); + + if ($list !== null) { + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheHit($persister->getCacheRegion()->getName(), $key); + } + + return $list; + } + } + + $list = $this->persister->loadOneToManyCollection($assoc, $sourceEntity, $coll); + + if ($hasCache) { + $persister->storeCollectionCache($key, $list); + + if ($this->cacheLogger) { + $this->cacheLogger->collectionCacheMiss($persister->getCacheRegion()->getName(), $key); + } + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) + { + return $this->persister->loadOneToOneEntity($assoc, $sourceEntity, $identifier); + } + + /** + * {@inheritdoc} + */ + public function lock(array $criteria, $lockMode) + { + $this->persister->lock($criteria, $lockMode); + } + + /** + * {@inheritdoc} + */ + public function refresh(array $id, $entity, $lockMode = null) + { + $this->persister->refresh($id, $entity, $lockMode); + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/CachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/CachedEntityPersister.php new file mode 100644 index 0000000..0c9078c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/CachedEntityPersister.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Cache\EntityCacheKey; +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * Interface for second level cache entity persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface CachedEntityPersister extends CachedPersister, EntityPersister +{ + /** + * @return \Doctrine\ORM\Cache\EntityHydrator + */ + public function getEntityHydrator(); + + /** + * @param object $entity + * @param \Doctrine\ORM\Cache\EntityCacheKey $key + * @return boolean + */ + public function storeEntityCache($entity, EntityCacheKey $key); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php new file mode 100644 index 0000000..d50d8c5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/NonStrictReadWriteCachedEntityPersister.php @@ -0,0 +1,114 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\Cache\EntityCacheKey; + +/** + * Specific non-strict read/write cached entity persister + * + * @author Fabio B. Silva + * @since 2.5 + */ +class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister +{ + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + $isChanged = false; + + if (isset($this->queuedCache['insert'])) { + foreach ($this->queuedCache['insert'] as $entity) { + $isChanged = $this->updateCache($entity, $isChanged); + } + } + + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $entity) { + $isChanged = $this->updateCache($entity, $isChanged); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $key) { + $this->region->evict($key); + + $isChanged = true; + } + } + + if ($isChanged) { + $this->timestampRegion->update($this->timestampKey); + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + + if ($this->persister->delete($entity)) { + $this->region->evict($key); + } + + $this->queuedCache['delete'][] = $key; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $this->persister->update($entity); + + $this->queuedCache['update'][] = $entity; + } + + private function updateCache($entity, $isChanged) + { + $class = $this->metadataFactory->getMetadataFor(get_class($entity)); + $key = new EntityCacheKey($class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + $entry = $this->hydrator->buildCacheEntry($class, $key, $entity); + $cached = $this->region->put($key, $entry); + $isChanged = $isChanged ?: $cached; + + if ($this->cacheLogger && $cached) { + $this->cacheLogger->entityCachePut($this->regionName, $key); + } + + return $isChanged; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php new file mode 100644 index 0000000..ce93d7b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadOnlyCachedEntityPersister.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Cache\CacheException; +use Doctrine\Common\Util\ClassUtils; + +/** + * Specific read-only region entity persister + * + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadOnlyCachedEntityPersister extends NonStrictReadWriteCachedEntityPersister +{ + /** + * {@inheritdoc} + */ + public function update($entity) + { + throw CacheException::updateReadOnlyEntity(ClassUtils::getClass($entity)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php new file mode 100644 index 0000000..8413082 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/ReadWriteCachedEntityPersister.php @@ -0,0 +1,139 @@ +. + */ + +namespace Doctrine\ORM\Cache\Persister\Entity; + +use Doctrine\ORM\Persisters\Entity\EntityPersister; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Cache\ConcurrentRegion; +use Doctrine\ORM\Cache\EntityCacheKey; + +/** + * Specific read-write entity persister + * + * @author Fabio B. Silva + * @since 2.5 + */ +class ReadWriteCachedEntityPersister extends AbstractEntityPersister +{ + /** + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $persister The entity persister to cache. + * @param \Doctrine\ORM\Cache\ConcurrentRegion $region The entity cache region. + * @param \Doctrine\ORM\EntityManagerInterface $em The entity manager. + * @param \Doctrine\ORM\Mapping\ClassMetadata $class The entity metadata. + */ + public function __construct(EntityPersister $persister, ConcurrentRegion $region, EntityManagerInterface $em, ClassMetadata $class) + { + parent::__construct($persister, $region, $em, $class); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionComplete() + { + $isChanged = true; + + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + + $isChanged = true; + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + + $isChanged = true; + } + } + + if ($isChanged) { + $this->timestampRegion->update($this->timestampKey); + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function afterTransactionRolledBack() + { + if (isset($this->queuedCache['update'])) { + foreach ($this->queuedCache['update'] as $item) { + $this->region->evict($item['key']); + } + } + + if (isset($this->queuedCache['delete'])) { + foreach ($this->queuedCache['delete'] as $item) { + $this->region->evict($item['key']); + } + } + + $this->queuedCache = array(); + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + $lock = $this->region->lock($key); + + if ($this->persister->delete($entity)) { + $this->region->evict($key); + } + + if ($lock === null) { + return; + } + + $this->queuedCache['delete'][] = array( + 'lock' => $lock, + 'key' => $key + ); + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity)); + $lock = $this->region->lock($key); + + $this->persister->update($entity); + + if ($lock === null) { + return; + } + + $this->queuedCache['update'][] = array( + 'lock' => $lock, + 'key' => $key + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCache.php new file mode 100644 index 0000000..dd5ef3b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCache.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * Defines the contract for caches capable of storing query results. + * These caches should only concern themselves with storing the matching result ids. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface QueryCache +{ + /** + * @return boolean + */ + public function clear(); + + /** + * @param \Doctrine\ORM\Cache\QueryCacheKey $key + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * @param mixed $result + * @param array $hints + * + * @return boolean + */ + public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $hints = array()); + + /** + * @param \Doctrine\ORM\Cache\QueryCacheKey $key + * @param \Doctrine\ORM\Query\ResultSetMapping $rsm + * @param array $hints + * + * @return array|null + */ + public function get(QueryCacheKey $key, ResultSetMapping $rsm, array $hints = array()); + + /** + * @return \Doctrine\ORM\Cache\Region + */ + public function getRegion(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheEntry.php new file mode 100644 index 0000000..1ba61bc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheEntry.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Query cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class QueryCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var array List of entity identifiers + */ + public $result; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var integer Time creation of this cache entry + */ + public $time; + + /** + * @param array $result + * @param integer $time + */ + public function __construct($result, $time = null) + { + $this->result = $result; + $this->time = $time ?: time(); + } + + /** + * @param array $values + */ + public static function __set_state(array $values) + { + return new self($values['result'], $values['time']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheKey.php new file mode 100644 index 0000000..9a7d2b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheKey.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +use Doctrine\ORM\Cache; + +/** + * A cache key that identifies a particular query. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class QueryCacheKey extends CacheKey +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var integer Cache key lifetime + */ + public $lifetime; + + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var integer Cache mode (Doctrine\ORM\Cache::MODE_*) + */ + public $cacheMode; + + /** + * @param string $hash Result cache id + * @param integer $lifetime Query lifetime + * @param integer $cacheMode Query cache mode + */ + public function __construct($hash, $lifetime = 0, $cacheMode = Cache::MODE_NORMAL) + { + $this->hash = $hash; + $this->lifetime = $lifetime; + $this->cacheMode = $cacheMode; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheValidator.php new file mode 100644 index 0000000..682a41e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/QueryCacheValidator.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache query validator interface. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface QueryCacheValidator +{ + /** + * Checks if the query entry is valid + * + * @param \Doctrine\ORM\Cache\QueryCacheKey $key + * @param \Doctrine\ORM\Cache\QueryCacheEntry $entry + * + * @return boolean + */ + public function isValid(QueryCacheKey $key, QueryCacheEntry $entry); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region.php new file mode 100644 index 0000000..3bffbbc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines a contract for accessing a particular named region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface Region extends MultiGetRegion +{ + /** + * Retrieve the name of this region. + * + * @return string The region name + */ + public function getName(); + + /** + * Determine whether this region contains data for the given key. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The cache key + * + * @return boolean TRUE if the underlying cache contains corresponding data; FALSE otherwise. + */ + public function contains(CacheKey $key); + + /** + * Get an item from the cache. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to be retrieved. + * + * @return \Doctrine\ORM\Cache\CacheEntry|null The cached entry or NULL + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the item or region. + */ + public function get(CacheKey $key); + + /** + * Put an item into the cache. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key under which to cache the item. + * @param \Doctrine\ORM\Cache\CacheEntry $entry The entry to cache. + * @param \Doctrine\ORM\Cache\Lock $lock The lock previously obtained. + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the region. + */ + public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null); + + /** + * Remove an item from the cache. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key under which to cache the item. + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates a problem accessing the region. + */ + public function evict(CacheKey $key); + + /** + * Remove all contents of this particular cache region. + * + * @throws \Doctrine\ORM\Cache\CacheException Indicates problem accessing the region. + */ + public function evictAll(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php new file mode 100644 index 0000000..7ecf733 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php @@ -0,0 +1,77 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\Common\Cache\MultiGetCache; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\CollectionCacheEntry; + +/** + * A cache region that enables the retrieval of multiple elements with one call + * + * @since 2.5 + * @author Asmir Mustafic + */ +class DefaultMultiGetRegion extends DefaultRegion +{ + /** + * Note that the multiple type is due to doctrine/cache not integrating the MultiGetCache interface + * in its signature due to BC in 1.x + * + * @var MultiGetCache|\Doctrine\Common\Cache\Cache + */ + protected $cache; + + /** + * {@inheritDoc} + * + * @param MultiGetCache $cache + */ + public function __construct($name, MultiGetCache $cache, $lifetime = 0) + { + /* @var $cache \Doctrine\Common\Cache\Cache */ + parent::__construct($name, $cache, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function getMultiple(CollectionCacheEntry $collection) + { + $keysToRetrieve = array(); + + foreach ($collection->identifiers as $index => $key) { + $keysToRetrieve[$index] = $this->getCacheEntryKey($key); + } + + $items = $this->cache->fetchMultiple($keysToRetrieve); + if (count($items) !== count($keysToRetrieve)) { + return null; + } + + $returnableItems = array(); + foreach ($keysToRetrieve as $index => $key) { + $returnableItems[$index] = $items[$key]; + } + + return $returnableItems; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultRegion.php new file mode 100644 index 0000000..3f214d0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/DefaultRegion.php @@ -0,0 +1,160 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\Common\Cache\Cache as CacheAdapter; +use Doctrine\Common\Cache\ClearableCache; +use Doctrine\ORM\Cache\CacheEntry; +use Doctrine\ORM\Cache\CacheKey; +use Doctrine\ORM\Cache\CollectionCacheEntry; +use Doctrine\ORM\Cache\Lock; +use Doctrine\ORM\Cache\Region; + +/** + * The simplest cache region compatible with all doctrine-cache drivers. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class DefaultRegion implements Region +{ + const REGION_KEY_SEPARATOR = '_'; + + /** + * @var CacheAdapter + */ + protected $cache; + + /** + * @var string + */ + protected $name; + + /** + * @var integer + */ + protected $lifetime = 0; + + /** + * @param string $name + * @param CacheAdapter $cache + * @param integer $lifetime + */ + public function __construct($name, CacheAdapter $cache, $lifetime = 0) + { + $this->cache = $cache; + $this->name = (string) $name; + $this->lifetime = (integer) $lifetime; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * @return \Doctrine\Common\Cache\CacheProvider + */ + public function getCache() + { + return $this->cache; + } + + /** + * {@inheritdoc} + */ + public function contains(CacheKey $key) + { + return $this->cache->contains($this->getCacheEntryKey($key)); + } + + /** + * {@inheritdoc} + */ + public function get(CacheKey $key) + { + return $this->cache->fetch($this->getCacheEntryKey($key)) ?: null; + } + + /** + * {@inheritdoc} + */ + public function getMultiple(CollectionCacheEntry $collection) + { + $result = array(); + + foreach ($collection->identifiers as $key) { + $entryKey = $this->getCacheEntryKey($key); + $entryValue = $this->cache->fetch($entryKey); + + if ($entryValue === false) { + return null; + } + + $result[] = $entryValue; + } + + return $result; + } + + /** + * @param CacheKey $key + * @return string + */ + protected function getCacheEntryKey(CacheKey $key) + { + return $this->name . self::REGION_KEY_SEPARATOR . $key->hash; + } + + /** + * {@inheritdoc} + */ + public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null) + { + return $this->cache->save($this->getCacheEntryKey($key), $entry, $this->lifetime); + } + + /** + * {@inheritdoc} + */ + public function evict(CacheKey $key) + { + return $this->cache->delete($this->getCacheEntryKey($key)); + } + + /** + * {@inheritdoc} + */ + public function evictAll() + { + if (! $this->cache instanceof ClearableCache) { + throw new \BadMethodCallException(sprintf( + 'Clearing all cache entries is not supported by the supplied cache adapter of type %s', + get_class($this->cache) + )); + } + + return $this->cache->deleteAll(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/FileLockRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/FileLockRegion.php new file mode 100644 index 0000000..d8d4b26 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/FileLockRegion.php @@ -0,0 +1,265 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\ORM\Cache\CollectionCacheEntry; +use Doctrine\ORM\Cache\Lock; +use Doctrine\ORM\Cache\Region; +use Doctrine\ORM\Cache\CacheKey; +use Doctrine\ORM\Cache\CacheEntry; +use Doctrine\ORM\Cache\ConcurrentRegion; + +/** + * Very naive concurrent region, based on file locks. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class FileLockRegion implements ConcurrentRegion +{ + const LOCK_EXTENSION = 'lock'; + + /** + * var \Doctrine\ORM\Cache\Region + */ + private $region; + + /** + * @var string + */ + private $directory; + + /** + * var integer + */ + private $lockLifetime; + + /** + * @param \Doctrine\ORM\Cache\Region $region + * @param string $directory + * @param string $lockLifetime + * + * @throws \InvalidArgumentException + */ + public function __construct(Region $region, $directory, $lockLifetime) + { + if ( ! is_dir($directory) && ! @mkdir($directory, 0775, true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $directory)); + } + + if ( ! is_writable($directory)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable.', $directory)); + } + + $this->region = $region; + $this->directory = $directory; + $this->lockLifetime = $lockLifetime; + } + + /** + * @param \Doctrine\ORM\Cache\CacheKey $key + * @param \Doctrine\ORM\Cache\Lock $lock + * + * @return boolean + */ + private function isLocked(CacheKey $key, Lock $lock = null) + { + $filename = $this->getLockFileName($key); + + if ( ! is_file($filename)) { + return false; + } + + $time = $this->getLockTime($filename); + $content = $this->getLockContent($filename); + + if ( ! $content || ! $time) { + @unlink($filename); + + return false; + } + + if ($lock && $content === $lock->value) { + return false; + } + + // outdated lock + if (($time + $this->lockLifetime) <= time()) { + @unlink($filename); + + return false; + } + + return true; + } + + /** + * @param \Doctrine\ORM\Cache\CacheKey $key + * + * return string + */ + private function getLockFileName(CacheKey $key) + { + return $this->directory . DIRECTORY_SEPARATOR . $key->hash . '.' . self::LOCK_EXTENSION; + } + + /** + * @param string $filename + * + * return string + */ + private function getLockContent($filename) + { + return @file_get_contents($filename); + } + + /** + * @param string $filename + * + * return integer + */ + private function getLockTime($filename) + { + return @fileatime($filename); + } + + /** + * {inheritdoc} + */ + public function getName() + { + return $this->region->getName(); + } + + /** + * {inheritdoc} + */ + public function contains(CacheKey $key) + { + if ($this->isLocked($key)) { + return false; + } + + return $this->region->contains($key); + } + + /** + * {inheritdoc} + */ + public function get(CacheKey $key) + { + if ($this->isLocked($key)) { + return null; + } + + return $this->region->get($key); + } + + /** + * {@inheritdoc} + */ + public function getMultiple(CollectionCacheEntry $collection) + { + if (array_filter(array_map([$this, 'isLocked'], $collection->identifiers))) { + return null; + } + + return $this->region->getMultiple($collection); + } + + /** + * {inheritdoc} + */ + public function put(CacheKey $key, CacheEntry $entry, Lock $lock = null) + { + if ($this->isLocked($key, $lock)) { + return false; + } + + return $this->region->put($key, $entry); + } + + /** + * {inheritdoc} + */ + public function evict(CacheKey $key) + { + if ($this->isLocked($key)) { + @unlink($this->getLockFileName($key)); + } + + return $this->region->evict($key); + } + + /** + * {inheritdoc} + */ + public function evictAll() + { + // The check below is necessary because on some platforms glob returns false + // when nothing matched (even though no errors occurred) + $filenames = glob(sprintf("%s/*.%s" , $this->directory, self::LOCK_EXTENSION)); + + if ($filenames) { + foreach ($filenames as $filename) { + @unlink($filename); + } + } + + return $this->region->evictAll(); + } + + /** + * {inheritdoc} + */ + public function lock(CacheKey $key) + { + if ($this->isLocked($key)) { + return null; + } + + $lock = Lock::createLockRead(); + $filename = $this->getLockFileName($key); + + if ( ! @file_put_contents($filename, $lock->value, LOCK_EX)) { + return null; + } + chmod($filename, 0664); + + return $lock; + } + + /** + * {inheritdoc} + */ + public function unlock(CacheKey $key, Lock $lock) + { + if ($this->isLocked($key, $lock)) { + return false; + } + + if ( ! @unlink($this->getLockFileName($key))) { + return false; + } + + return true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php new file mode 100644 index 0000000..dfdf906 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Region/UpdateTimestampCache.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Cache\Region; + +use Doctrine\ORM\Cache\TimestampCacheEntry; +use Doctrine\ORM\Cache\TimestampRegion; +use Doctrine\ORM\Cache\CacheKey; + +/** + * Tracks the timestamps of the most recent updates to particular keys. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class UpdateTimestampCache extends DefaultRegion implements TimestampRegion +{ + /** + * {@inheritdoc} + */ + public function update(CacheKey $key) + { + $this->put($key, new TimestampCacheEntry); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/RegionsConfiguration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/RegionsConfiguration.php new file mode 100644 index 0000000..0d06063 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/RegionsConfiguration.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Cache regions configuration + * + * @since 2.5 + * @author Fabio B. Silva + */ +class RegionsConfiguration +{ + /** + * @var array + */ + private $lifetimes = array(); + + /** + * @var array + */ + private $lockLifetimes = array(); + + /** + * @var integer + */ + private $defaultLifetime; + + /** + * @var integer + */ + private $defaultLockLifetime; + + /** + * @param integer $defaultLifetime + * @param integer $defaultLockLifetime + */ + public function __construct($defaultLifetime = 3600, $defaultLockLifetime = 60) + { + $this->defaultLifetime = (integer) $defaultLifetime; + $this->defaultLockLifetime = (integer) $defaultLockLifetime; + } + + /** + * @return integer + */ + public function getDefaultLifetime() + { + return $this->defaultLifetime; + } + + /** + * @param integer $defaultLifetime + */ + public function setDefaultLifetime($defaultLifetime) + { + $this->defaultLifetime = (integer) $defaultLifetime; + } + + /** + * @return integer + */ + public function getDefaultLockLifetime() + { + return $this->defaultLockLifetime; + } + + /** + * @param integer $defaultLockLifetime + */ + public function setDefaultLockLifetime($defaultLockLifetime) + { + $this->defaultLockLifetime = (integer) $defaultLockLifetime; + } + + /** + * @param string $regionName + * + * @return integer + */ + public function getLifetime($regionName) + { + return isset($this->lifetimes[$regionName]) + ? $this->lifetimes[$regionName] + : $this->defaultLifetime; + } + + /** + * @param string $name + * @param integer $lifetime + */ + public function setLifetime($name, $lifetime) + { + $this->lifetimes[$name] = (integer) $lifetime; + } + + /** + * @param string $regionName + * + * @return integer + */ + public function getLockLifetime($regionName) + { + return isset($this->lockLifetimes[$regionName]) + ? $this->lockLifetimes[$regionName] + : $this->defaultLockLifetime; + } + + /** + * @param string $name + * @param integer $lifetime + */ + public function setLockLifetime($name, $lifetime) + { + $this->lockLifetimes[$name] = (integer) $lifetime; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheEntry.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheEntry.php new file mode 100644 index 0000000..9d15c84 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheEntry.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Timestamp cache entry + * + * @since 2.5 + * @author Fabio B. Silva + */ +class TimestampCacheEntry implements CacheEntry +{ + /** + * READ-ONLY: Public only for performance reasons, it should be considered immutable. + * + * @var float + */ + public $time; + + /** + * @param float $time + */ + public function __construct($time = null) + { + $this->time = $time ? (float)$time : microtime(true); + } + + /** + * Creates a new TimestampCacheEntry + * + * This method allow Doctrine\Common\Cache\PhpFileCache compatibility + * + * @param array $values array containing property values + */ + public static function __set_state(array $values) + { + return new self($values['time']); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheKey.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheKey.php new file mode 100644 index 0000000..dfa7227 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampCacheKey.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * A key that identifies a timestamped space. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class TimestampCacheKey extends CacheKey +{ + /** + * @param string $space Result cache id + */ + public function __construct($space) + { + $this->hash = (string) $space; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php new file mode 100644 index 0000000..4e504e4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampQueryCacheValidator.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * @since 2.5 + * @author Fabio B. Silva + */ +class TimestampQueryCacheValidator implements QueryCacheValidator +{ + /** + * {@inheritdoc} + */ + public function isValid(QueryCacheKey $key, QueryCacheEntry $entry) + { + if ($key->lifetime == 0) { + return true; + } + + return ($entry->time + $key->lifetime) > time(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampRegion.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampRegion.php new file mode 100644 index 0000000..9e0c25c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/TimestampRegion.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Cache; + +/** + * Defines the contract for a cache region which will specifically be used to store entity "update timestamps". + * + * @since 2.5 + * @author Fabio B. Silva + */ +interface TimestampRegion extends Region +{ + /** + * Update an specific key into the cache region. + * + * @param \Doctrine\ORM\Cache\CacheKey $key The key of the item to update the timestamp. + * + * @throws \Doctrine\ORM\Cache\LockException Indicates a problem accessing the region. + */ + public function update(CacheKey $key); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php new file mode 100644 index 0000000..c99c404 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Configuration.php @@ -0,0 +1,927 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Doctrine\Common\Annotations\CachedReader; +use Doctrine\Common\Annotations\SimpleAnnotationReader; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\Common\Cache\Cache as CacheDriver; +use Doctrine\Common\Proxy\AbstractProxyFactory; +use Doctrine\ORM\Cache\CacheConfiguration; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; +use Doctrine\ORM\Mapping\DefaultNamingStrategy; +use Doctrine\ORM\Mapping\DefaultQuoteStrategy; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\EntityListenerResolver; +use Doctrine\ORM\Mapping\NamingStrategy; +use Doctrine\ORM\Mapping\QuoteStrategy; +use Doctrine\ORM\Repository\DefaultRepositoryFactory; +use Doctrine\ORM\Repository\RepositoryFactory; + +/** + * Configuration container for all configuration options of Doctrine. + * It combines all configuration options from DBAL & ORM. + * + * Internal note: When adding a new configuration option just write a getter/setter pair. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Configuration extends \Doctrine\DBAL\Configuration +{ + /** + * Sets the directory where Doctrine generates any necessary proxy class files. + * + * @param string $dir + * + * @return void + */ + public function setProxyDir($dir) + { + $this->_attributes['proxyDir'] = $dir; + } + + /** + * Gets the directory where Doctrine generates any necessary proxy class files. + * + * @return string|null + */ + public function getProxyDir() + { + return isset($this->_attributes['proxyDir']) + ? $this->_attributes['proxyDir'] + : null; + } + + /** + * Gets the strategy for automatically generating proxy classes. + * + * @return int Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory. + */ + public function getAutoGenerateProxyClasses() + { + return isset($this->_attributes['autoGenerateProxyClasses']) + ? $this->_attributes['autoGenerateProxyClasses'] + : AbstractProxyFactory::AUTOGENERATE_ALWAYS; + } + + /** + * Sets the strategy for automatically generating proxy classes. + * + * @param boolean|int $autoGenerate Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory. + * True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER. + * + * @return void + */ + public function setAutoGenerateProxyClasses($autoGenerate) + { + $this->_attributes['autoGenerateProxyClasses'] = (int)$autoGenerate; + } + + /** + * Gets the namespace where proxy classes reside. + * + * @return string|null + */ + public function getProxyNamespace() + { + return isset($this->_attributes['proxyNamespace']) + ? $this->_attributes['proxyNamespace'] + : null; + } + + /** + * Sets the namespace where proxy classes reside. + * + * @param string $ns + * + * @return void + */ + public function setProxyNamespace($ns) + { + $this->_attributes['proxyNamespace'] = $ns; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param MappingDriver $driverImpl + * + * @return void + * + * @todo Force parameter to be a Closure to ensure lazy evaluation + * (as soon as a metadata cache is in effect, the driver never needs to initialize). + */ + public function setMetadataDriverImpl(MappingDriver $driverImpl) + { + $this->_attributes['metadataDriverImpl'] = $driverImpl; + } + + /** + * Adds a new default annotation driver with a correctly configured annotation reader. If $useSimpleAnnotationReader + * is true, the notation `@Entity` will work, otherwise, the notation `@ORM\Entity` will be supported. + * + * @param array $paths + * @param bool $useSimpleAnnotationReader + * + * @return AnnotationDriver + */ + public function newDefaultAnnotationDriver($paths = array(), $useSimpleAnnotationReader = true) + { + AnnotationRegistry::registerFile(__DIR__ . '/Mapping/Driver/DoctrineAnnotations.php'); + + if ($useSimpleAnnotationReader) { + // Register the ORM Annotations in the AnnotationRegistry + $reader = new SimpleAnnotationReader(); + $reader->addNamespace('Doctrine\ORM\Mapping'); + $cachedReader = new CachedReader($reader, new ArrayCache()); + + return new AnnotationDriver($cachedReader, (array) $paths); + } + + return new AnnotationDriver( + new CachedReader(new AnnotationReader(), new ArrayCache()), + (array) $paths + ); + } + + /** + * Adds a namespace under a certain alias. + * + * @param string $alias + * @param string $namespace + * + * @return void + */ + public function addEntityNamespace($alias, $namespace) + { + $this->_attributes['entityNamespaces'][$alias] = $namespace; + } + + /** + * Resolves a registered namespace alias to the full namespace. + * + * @param string $entityNamespaceAlias + * + * @return string + * + * @throws ORMException + */ + public function getEntityNamespace($entityNamespaceAlias) + { + if ( ! isset($this->_attributes['entityNamespaces'][$entityNamespaceAlias])) { + throw ORMException::unknownEntityNamespace($entityNamespaceAlias); + } + + return trim($this->_attributes['entityNamespaces'][$entityNamespaceAlias], '\\'); + } + + /** + * Sets the entity alias map. + * + * @param array $entityNamespaces + * + * @return void + */ + public function setEntityNamespaces(array $entityNamespaces) + { + $this->_attributes['entityNamespaces'] = $entityNamespaces; + } + + /** + * Retrieves the list of registered entity namespace aliases. + * + * @return array + */ + public function getEntityNamespaces() + { + return $this->_attributes['entityNamespaces']; + } + + /** + * Gets the cache driver implementation that is used for the mapping metadata. + * + * @return MappingDriver|null + * + * @throws ORMException + */ + public function getMetadataDriverImpl() + { + return isset($this->_attributes['metadataDriverImpl']) + ? $this->_attributes['metadataDriverImpl'] + : null; + } + + /** + * Gets the cache driver implementation that is used for the query cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getQueryCacheImpl() + { + return isset($this->_attributes['queryCacheImpl']) + ? $this->_attributes['queryCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for the query cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setQueryCacheImpl(CacheDriver $cacheImpl) + { + $this->_attributes['queryCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getHydrationCacheImpl() + { + return isset($this->_attributes['hydrationCacheImpl']) + ? $this->_attributes['hydrationCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for the hydration cache (SQL cache). + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setHydrationCacheImpl(CacheDriver $cacheImpl) + { + $this->_attributes['hydrationCacheImpl'] = $cacheImpl; + } + + /** + * Gets the cache driver implementation that is used for metadata caching. + * + * @return \Doctrine\Common\Cache\Cache|null + */ + public function getMetadataCacheImpl() + { + return isset($this->_attributes['metadataCacheImpl']) + ? $this->_attributes['metadataCacheImpl'] + : null; + } + + /** + * Sets the cache driver implementation that is used for metadata caching. + * + * @param \Doctrine\Common\Cache\Cache $cacheImpl + * + * @return void + */ + public function setMetadataCacheImpl(CacheDriver $cacheImpl) + { + $this->_attributes['metadataCacheImpl'] = $cacheImpl; + } + + /** + * Adds a named DQL query to the configuration. + * + * @param string $name The name of the query. + * @param string $dql The DQL query string. + * + * @return void + */ + public function addNamedQuery($name, $dql) + { + $this->_attributes['namedQueries'][$name] = $dql; + } + + /** + * Gets a previously registered named DQL query. + * + * @param string $name The name of the query. + * + * @return string The DQL query. + * + * @throws ORMException + */ + public function getNamedQuery($name) + { + if ( ! isset($this->_attributes['namedQueries'][$name])) { + throw ORMException::namedQueryNotFound($name); + } + + return $this->_attributes['namedQueries'][$name]; + } + + /** + * Adds a named native query to the configuration. + * + * @param string $name The name of the query. + * @param string $sql The native SQL query string. + * @param Query\ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query. + * + * @return void + */ + public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm) + { + $this->_attributes['namedNativeQueries'][$name] = array($sql, $rsm); + } + + /** + * Gets the components of a previously registered named native query. + * + * @param string $name The name of the query. + * + * @return array A tuple with the first element being the SQL string and the second + * element being the ResultSetMapping. + * + * @throws ORMException + */ + public function getNamedNativeQuery($name) + { + if ( ! isset($this->_attributes['namedNativeQueries'][$name])) { + throw ORMException::namedNativeQueryNotFound($name); + } + + return $this->_attributes['namedNativeQueries'][$name]; + } + + /** + * Ensures that this Configuration instance contains settings that are + * suitable for a production environment. + * + * @return void + * + * @throws ORMException If a configuration setting has a value that is not + * suitable for a production environment. + */ + public function ensureProductionSettings() + { + $queryCacheImpl = $this->getQueryCacheImpl(); + + if ( ! $queryCacheImpl) { + throw ORMException::queryCacheNotConfigured(); + } + + if ($queryCacheImpl instanceof ArrayCache) { + throw ORMException::queryCacheUsesNonPersistentCache($queryCacheImpl); + } + + $metadataCacheImpl = $this->getMetadataCacheImpl(); + + if ( ! $metadataCacheImpl) { + throw ORMException::metadataCacheNotConfigured(); + } + + if ($metadataCacheImpl instanceof ArrayCache) { + throw ORMException::metadataCacheUsesNonPersistentCache($metadataCacheImpl); + } + + if ($this->getAutoGenerateProxyClasses()) { + throw ORMException::proxyClassesAlwaysRegenerating(); + } + } + + /** + * Registers a custom DQL function that produces a string value. + * Such a function can then be used in any DQL statement in any place where string + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name Function name. + * @param string|callable $className Class name or a callable that returns the function. + * + * @return void + * + * @throws ORMException + */ + public function addCustomStringFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customStringFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom string DQL function. + * + * @param string $name + * + * @return string|null + */ + public function getCustomStringFunction($name) + { + $name = strtolower($name); + + return isset($this->_attributes['customStringFunctions'][$name]) + ? $this->_attributes['customStringFunctions'][$name] + : null; + } + + /** + * Sets a map of custom DQL string functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added string functions are discarded. + * + * @param array $functions The map of custom DQL string functions. + * + * @return void + */ + public function setCustomStringFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomStringFunction($name, $className); + } + } + + /** + * Registers a custom DQL function that produces a numeric value. + * Such a function can then be used in any DQL statement in any place where numeric + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name Function name. + * @param string|callable $className Class name or a callable that returns the function. + * + * @return void + * + * @throws ORMException + */ + public function addCustomNumericFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customNumericFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom numeric DQL function. + * + * @param string $name + * + * @return string|null + */ + public function getCustomNumericFunction($name) + { + $name = strtolower($name); + + return isset($this->_attributes['customNumericFunctions'][$name]) + ? $this->_attributes['customNumericFunctions'][$name] + : null; + } + + /** + * Sets a map of custom DQL numeric functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added numeric functions are discarded. + * + * @param array $functions The map of custom DQL numeric functions. + * + * @return void + */ + public function setCustomNumericFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomNumericFunction($name, $className); + } + } + + /** + * Registers a custom DQL function that produces a date/time value. + * Such a function can then be used in any DQL statement in any place where date/time + * functions are allowed. + * + * DQL function names are case-insensitive. + * + * @param string $name Function name. + * @param string|callable $className Class name or a callable that returns the function. + * + * @return void + * + * @throws ORMException + */ + public function addCustomDatetimeFunction($name, $className) + { + if (Query\Parser::isInternalFunction($name)) { + throw ORMException::overwriteInternalDQLFunctionNotAllowed($name); + } + + $this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className; + } + + /** + * Gets the implementation class name of a registered custom date/time DQL function. + * + * @param string $name + * + * @return string|null + */ + public function getCustomDatetimeFunction($name) + { + $name = strtolower($name); + + return isset($this->_attributes['customDatetimeFunctions'][$name]) + ? $this->_attributes['customDatetimeFunctions'][$name] + : null; + } + + /** + * Sets a map of custom DQL date/time functions. + * + * Keys must be function names and values the FQCN of the implementing class. + * The function names will be case-insensitive in DQL. + * + * Any previously added date/time functions are discarded. + * + * @param array $functions The map of custom DQL date/time functions. + * + * @return void + */ + public function setCustomDatetimeFunctions(array $functions) + { + foreach ($functions as $name => $className) { + $this->addCustomDatetimeFunction($name, $className); + } + } + + /** + * Sets the custom hydrator modes in one pass. + * + * @param array $modes An array of ($modeName => $hydrator). + * + * @return void + */ + public function setCustomHydrationModes($modes) + { + $this->_attributes['customHydrationModes'] = array(); + + foreach ($modes as $modeName => $hydrator) { + $this->addCustomHydrationMode($modeName, $hydrator); + } + } + + /** + * Gets the hydrator class for the given hydration mode name. + * + * @param string $modeName The hydration mode name. + * + * @return string|null The hydrator class name. + */ + public function getCustomHydrationMode($modeName) + { + return isset($this->_attributes['customHydrationModes'][$modeName]) + ? $this->_attributes['customHydrationModes'][$modeName] + : null; + } + + /** + * Adds a custom hydration mode. + * + * @param string $modeName The hydration mode name. + * @param string $hydrator The hydrator class name. + * + * @return void + */ + public function addCustomHydrationMode($modeName, $hydrator) + { + $this->_attributes['customHydrationModes'][$modeName] = $hydrator; + } + + /** + * Sets a class metadata factory. + * + * @param string $cmfName + * + * @return void + */ + public function setClassMetadataFactoryName($cmfName) + { + $this->_attributes['classMetadataFactoryName'] = $cmfName; + } + + /** + * @return string + */ + public function getClassMetadataFactoryName() + { + if ( ! isset($this->_attributes['classMetadataFactoryName'])) { + $this->_attributes['classMetadataFactoryName'] = 'Doctrine\ORM\Mapping\ClassMetadataFactory'; + } + + return $this->_attributes['classMetadataFactoryName']; + } + + /** + * Adds a filter to the list of possible filters. + * + * @param string $name The name of the filter. + * @param string $className The class name of the filter. + */ + public function addFilter($name, $className) + { + $this->_attributes['filters'][$name] = $className; + } + + /** + * Gets the class name for a given filter name. + * + * @param string $name The name of the filter. + * + * @return string The class name of the filter, or null of it is not + * defined. + */ + public function getFilterClassName($name) + { + return isset($this->_attributes['filters'][$name]) + ? $this->_attributes['filters'][$name] + : null; + } + + /** + * Sets default repository class. + * + * @since 2.2 + * + * @param string $className + * + * @return void + * + * @throws ORMException If not is a \Doctrine\Common\Persistence\ObjectRepository + */ + public function setDefaultRepositoryClassName($className) + { + $reflectionClass = new \ReflectionClass($className); + + if ( ! $reflectionClass->implementsInterface('Doctrine\Common\Persistence\ObjectRepository')) { + throw ORMException::invalidEntityRepository($className); + } + + $this->_attributes['defaultRepositoryClassName'] = $className; + } + + /** + * Get default repository class. + * + * @since 2.2 + * + * @return string + */ + public function getDefaultRepositoryClassName() + { + return isset($this->_attributes['defaultRepositoryClassName']) + ? $this->_attributes['defaultRepositoryClassName'] + : 'Doctrine\ORM\EntityRepository'; + } + + /** + * Sets naming strategy. + * + * @since 2.3 + * + * @param NamingStrategy $namingStrategy + * + * @return void + */ + public function setNamingStrategy(NamingStrategy $namingStrategy) + { + $this->_attributes['namingStrategy'] = $namingStrategy; + } + + /** + * Gets naming strategy.. + * + * @since 2.3 + * + * @return NamingStrategy + */ + public function getNamingStrategy() + { + if ( ! isset($this->_attributes['namingStrategy'])) { + $this->_attributes['namingStrategy'] = new DefaultNamingStrategy(); + } + + return $this->_attributes['namingStrategy']; + } + + /** + * Sets quote strategy. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\QuoteStrategy $quoteStrategy + * + * @return void + */ + public function setQuoteStrategy(QuoteStrategy $quoteStrategy) + { + $this->_attributes['quoteStrategy'] = $quoteStrategy; + } + + /** + * Gets quote strategy. + * + * @since 2.3 + * + * @return \Doctrine\ORM\Mapping\QuoteStrategy + */ + public function getQuoteStrategy() + { + if ( ! isset($this->_attributes['quoteStrategy'])) { + $this->_attributes['quoteStrategy'] = new DefaultQuoteStrategy(); + } + + return $this->_attributes['quoteStrategy']; + } + + /** + * Set the entity listener resolver. + * + * @since 2.4 + * @param \Doctrine\ORM\Mapping\EntityListenerResolver $resolver + */ + public function setEntityListenerResolver(EntityListenerResolver $resolver) + { + $this->_attributes['entityListenerResolver'] = $resolver; + } + + /** + * Get the entity listener resolver. + * + * @since 2.4 + * @return \Doctrine\ORM\Mapping\EntityListenerResolver + */ + public function getEntityListenerResolver() + { + if ( ! isset($this->_attributes['entityListenerResolver'])) { + $this->_attributes['entityListenerResolver'] = new DefaultEntityListenerResolver(); + } + + return $this->_attributes['entityListenerResolver']; + } + + /** + * Set the entity repository factory. + * + * @since 2.4 + * @param \Doctrine\ORM\Repository\RepositoryFactory $repositoryFactory + */ + public function setRepositoryFactory(RepositoryFactory $repositoryFactory) + { + $this->_attributes['repositoryFactory'] = $repositoryFactory; + } + + /** + * Get the entity repository factory. + * + * @since 2.4 + * @return \Doctrine\ORM\Repository\RepositoryFactory + */ + public function getRepositoryFactory() + { + return isset($this->_attributes['repositoryFactory']) + ? $this->_attributes['repositoryFactory'] + : new DefaultRepositoryFactory(); + } + + /** + * @since 2.5 + * + * @return boolean + */ + public function isSecondLevelCacheEnabled() + { + return isset($this->_attributes['isSecondLevelCacheEnabled']) + ? $this->_attributes['isSecondLevelCacheEnabled'] + : false; + } + + /** + * @since 2.5 + * + * @param boolean $flag + * + * @return void + */ + public function setSecondLevelCacheEnabled($flag = true) + { + $this->_attributes['isSecondLevelCacheEnabled'] = (boolean) $flag; + } + + /** + * @since 2.5 + * + * @param \Doctrine\ORM\Cache\CacheConfiguration $cacheConfig + * + * @return void + */ + public function setSecondLevelCacheConfiguration(CacheConfiguration $cacheConfig) + { + $this->_attributes['secondLevelCacheConfiguration'] = $cacheConfig; + } + + /** + * @since 2.5 + * + * @return \Doctrine\ORM\Cache\CacheConfiguration|null + */ + public function getSecondLevelCacheConfiguration() + { + if ( ! isset($this->_attributes['secondLevelCacheConfiguration']) && $this->isSecondLevelCacheEnabled()) { + $this->_attributes['secondLevelCacheConfiguration'] = new CacheConfiguration(); + } + + return isset($this->_attributes['secondLevelCacheConfiguration']) + ? $this->_attributes['secondLevelCacheConfiguration'] + : null; + } + + /** + * Returns query hints, which will be applied to every query in application + * + * @since 2.5 + * + * @return array + */ + public function getDefaultQueryHints() + { + return isset($this->_attributes['defaultQueryHints']) ? $this->_attributes['defaultQueryHints'] : array(); + } + + /** + * Sets array of query hints, which will be applied to every query in application + * + * @since 2.5 + * + * @param array $defaultQueryHints + */ + public function setDefaultQueryHints(array $defaultQueryHints) + { + $this->_attributes['defaultQueryHints'] = $defaultQueryHints; + } + + /** + * Gets the value of a default query hint. If the hint name is not recognized, FALSE is returned. + * + * @since 2.5 + * + * @param string $name The name of the hint. + * + * @return mixed The value of the hint or FALSE, if the hint name is not recognized. + */ + public function getDefaultQueryHint($name) + { + return isset($this->_attributes['defaultQueryHints'][$name]) + ? $this->_attributes['defaultQueryHints'][$name] + : false; + } + + /** + * Sets a default query hint. If the hint name is not recognized, it is silently ignored. + * + * @since 2.5 + * + * @param string $name The name of the hint. + * @param mixed $value The value of the hint. + */ + public function setDefaultQueryHint($name, $value) + { + $this->_attributes['defaultQueryHints'][$name] = $value; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php new file mode 100644 index 0000000..69cc6f5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php @@ -0,0 +1,278 @@ +. + */ + +namespace Doctrine\ORM\Decorator; + +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Common\Persistence\ObjectManagerDecorator; + +/** + * Base class for EntityManager decorators + * + * @since 2.4 + * @author Lars Strojny wrapped = $wrapped; + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + return $this->wrapped->getConnection(); + } + + /** + * {@inheritdoc} + */ + public function getExpressionBuilder() + { + return $this->wrapped->getExpressionBuilder(); + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + return $this->wrapped->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function transactional($func) + { + return $this->wrapped->transactional($func); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->wrapped->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollback() + { + return $this->wrapped->rollback(); + } + + /** + * {@inheritdoc} + */ + public function createQuery($dql = '') + { + return $this->wrapped->createQuery($dql); + } + + /** + * {@inheritdoc} + */ + public function createNamedQuery($name) + { + return $this->wrapped->createNamedQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + return $this->wrapped->createNativeQuery($sql, $rsm); + } + + /** + * {@inheritdoc} + */ + public function createNamedNativeQuery($name) + { + return $this->wrapped->createNamedNativeQuery($name); + } + + /** + * {@inheritdoc} + */ + public function createQueryBuilder() + { + return $this->wrapped->createQueryBuilder(); + } + + /** + * {@inheritdoc} + */ + public function getReference($entityName, $id) + { + return $this->wrapped->getReference($entityName, $id); + } + + /** + * {@inheritdoc} + */ + public function getPartialReference($entityName, $identifier) + { + return $this->wrapped->getPartialReference($entityName, $identifier); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->wrapped->close(); + } + + /** + * {@inheritdoc} + */ + public function copy($entity, $deep = false) + { + return $this->wrapped->copy($entity, $deep); + } + + /** + * {@inheritdoc} + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + return $this->wrapped->lock($entity, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function find($entityName, $id, $lockMode = null, $lockVersion = null) + { + return $this->wrapped->find($entityName, $id, $lockMode, $lockVersion); + } + + /** + * {@inheritdoc} + */ + public function flush($entity = null) + { + return $this->wrapped->flush($entity); + } + + /** + * {@inheritdoc} + */ + public function getEventManager() + { + return $this->wrapped->getEventManager(); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() + { + return $this->wrapped->getConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function isOpen() + { + return $this->wrapped->isOpen(); + } + + /** + * {@inheritdoc} + */ + public function getUnitOfWork() + { + return $this->wrapped->getUnitOfWork(); + } + + /** + * {@inheritdoc} + */ + public function getHydrator($hydrationMode) + { + return $this->wrapped->getHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function newHydrator($hydrationMode) + { + return $this->wrapped->newHydrator($hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function getProxyFactory() + { + return $this->wrapped->getProxyFactory(); + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return $this->wrapped->getFilters(); + } + + /** + * {@inheritdoc} + */ + public function isFiltersStateClean() + { + return $this->wrapped->isFiltersStateClean(); + } + + /** + * {@inheritdoc} + */ + public function hasFilters() + { + return $this->wrapped->hasFilters(); + } + + /** + * {@inheritdoc} + */ + public function getCache() + { + return $this->wrapped->getCache(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php new file mode 100644 index 0000000..685fc0d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php @@ -0,0 +1,880 @@ +. + */ + +namespace Doctrine\ORM; + +use Exception; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Proxy\ProxyFactory; +use Doctrine\ORM\Query\FilterCollection; +use Doctrine\Common\Util\ClassUtils; + +/** + * The EntityManager is the central access point to ORM functionality. + * + * It is a facade to all different ORM subsystems such as UnitOfWork, + * Query Language and Repository API. Instantiation is done through + * the static create() method. The quickest way to obtain a fully + * configured EntityManager is: + * + * use Doctrine\ORM\Tools\Setup; + * use Doctrine\ORM\EntityManager; + * + * $paths = array('/path/to/entity/mapping/files'); + * + * $config = Setup::createAnnotationMetadataConfiguration($paths); + * $dbParams = array('driver' => 'pdo_sqlite', 'memory' => true); + * $entityManager = EntityManager::create($dbParams, $config); + * + * For more information see + * {@link http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html} + * + * You should never attempt to inherit from the EntityManager: Inheritance + * is not a valid extension point for the EntityManager. Instead you + * should take a look at the {@see \Doctrine\ORM\Decorator\EntityManagerDecorator} + * and wrap your entity manager in a decorator. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +/* final */class EntityManager implements EntityManagerInterface +{ + /** + * The used Configuration. + * + * @var \Doctrine\ORM\Configuration + */ + private $config; + + /** + * The database connection used by the EntityManager. + * + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * The metadata factory, used to retrieve the ORM metadata of entity classes. + * + * @var \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var \Doctrine\ORM\UnitOfWork + */ + private $unitOfWork; + + /** + * The event manager that is the central point of the event system. + * + * @var \Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * The proxy factory used to create dynamic proxies. + * + * @var \Doctrine\ORM\Proxy\ProxyFactory + */ + private $proxyFactory; + + /** + * The repository factory used to create dynamic repositories. + * + * @var \Doctrine\ORM\Repository\RepositoryFactory + */ + private $repositoryFactory; + + /** + * The expression builder instance used to generate query expressions. + * + * @var \Doctrine\ORM\Query\Expr + */ + private $expressionBuilder; + + /** + * Whether the EntityManager is closed or not. + * + * @var bool + */ + private $closed = false; + + /** + * Collection of query filters. + * + * @var \Doctrine\ORM\Query\FilterCollection + */ + private $filterCollection; + + /** + * @var \Doctrine\ORM\Cache The second level cache regions API. + */ + private $cache; + + /** + * Creates a new EntityManager that operates on the given database connection + * and uses the given Configuration and EventManager implementations. + * + * @param \Doctrine\DBAL\Connection $conn + * @param \Doctrine\ORM\Configuration $config + * @param \Doctrine\Common\EventManager $eventManager + */ + protected function __construct(Connection $conn, Configuration $config, EventManager $eventManager) + { + $this->conn = $conn; + $this->config = $config; + $this->eventManager = $eventManager; + + $metadataFactoryClassName = $config->getClassMetadataFactoryName(); + + $this->metadataFactory = new $metadataFactoryClassName; + $this->metadataFactory->setEntityManager($this); + $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); + + $this->repositoryFactory = $config->getRepositoryFactory(); + $this->unitOfWork = new UnitOfWork($this); + $this->proxyFactory = new ProxyFactory( + $this, + $config->getProxyDir(), + $config->getProxyNamespace(), + $config->getAutoGenerateProxyClasses() + ); + + if ($config->isSecondLevelCacheEnabled()) { + $cacheConfig = $config->getSecondLevelCacheConfiguration(); + $cacheFactory = $cacheConfig->getCacheFactory(); + $this->cache = $cacheFactory->createCache($this); + } + } + + /** + * {@inheritDoc} + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return \Doctrine\ORM\Mapping\ClassMetadataFactory + */ + public function getMetadataFactory() + { + return $this->metadataFactory; + } + + /** + * {@inheritDoc} + */ + public function getExpressionBuilder() + { + if ($this->expressionBuilder === null) { + $this->expressionBuilder = new Query\Expr; + } + + return $this->expressionBuilder; + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() + { + $this->conn->beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function getCache() + { + return $this->cache; + } + + /** + * {@inheritDoc} + */ + public function transactional($func) + { + if (!is_callable($func)) { + throw new \InvalidArgumentException('Expected argument of type "callable", got "' . gettype($func) . '"'); + } + + $this->conn->beginTransaction(); + + try { + $return = call_user_func($func, $this); + + $this->flush(); + $this->conn->commit(); + + return $return ?: true; + } catch (Exception $e) { + $this->close(); + $this->conn->rollback(); + + throw $e; + } + } + + /** + * {@inheritDoc} + */ + public function commit() + { + $this->conn->commit(); + } + + /** + * {@inheritDoc} + */ + public function rollback() + { + $this->conn->rollback(); + } + + /** + * Returns the ORM metadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)) or an aliased class name. + * + * Examples: + * MyProject\Domain\User + * sales:PriceRequest + * + * Internal note: Performance-sensitive method. + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata($className) + { + return $this->metadataFactory->getMetadataFor($className); + } + + /** + * {@inheritDoc} + */ + public function createQuery($dql = '') + { + $query = new Query($this); + + if ( ! empty($dql)) { + $query->setDql($dql); + } + + return $query; + } + + /** + * {@inheritDoc} + */ + public function createNamedQuery($name) + { + return $this->createQuery($this->config->getNamedQuery($name)); + } + + /** + * {@inheritDoc} + */ + public function createNativeQuery($sql, ResultSetMapping $rsm) + { + $query = new NativeQuery($this); + + $query->setSql($sql); + $query->setResultSetMapping($rsm); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function createNamedNativeQuery($name) + { + list($sql, $rsm) = $this->config->getNamedNativeQuery($name); + + return $this->createNativeQuery($sql, $rsm); + } + + /** + * {@inheritDoc} + */ + public function createQueryBuilder() + { + return new QueryBuilder($this); + } + + /** + * Flushes all changes to objects that have been queued up to now to the database. + * This effectively synchronizes the in-memory state of managed objects with the + * database. + * + * If an entity is explicitly passed to this method only this entity and + * the cascade-persist semantics + scheduled inserts/removals are synchronized. + * + * @param null|object|array $entity + * + * @return void + * + * @throws \Doctrine\ORM\OptimisticLockException If a version check on an entity that + * makes use of optimistic locking fails. + */ + public function flush($entity = null) + { + $this->errorIfClosed(); + + $this->unitOfWork->commit($entity); + } + + /** + * Finds an Entity by its identifier. + * + * @param string $entityName The class name of the entity to find. + * @param mixed $id The identity of the entity to find. + * @param integer|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. + * @param integer|null $lockVersion The version of the entity to find when using + * optimistic locking. + * + * @return object|null The entity instance or NULL if the entity can not be found. + * + * @throws OptimisticLockException + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws ORMException + */ + public function find($entityName, $id, $lockMode = null, $lockVersion = null) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + if ( ! is_array($id)) { + if ($class->isIdentifierComposite) { + throw ORMInvalidArgumentException::invalidCompositeIdentifier(); + } + + $id = array($class->identifier[0] => $id); + } + + foreach ($id as $i => $value) { + if (is_object($value) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($value))) { + $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value); + + if ($id[$i] === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); + } + } + } + + $sortedId = array(); + + foreach ($class->identifier as $identifier) { + if ( ! isset($id[$identifier])) { + throw ORMException::missingIdentifierField($class->name, $identifier); + } + + $sortedId[$identifier] = $id[$identifier]; + unset($id[$identifier]); + } + + if ($id) { + throw ORMException::unrecognizedIdentifierFields($class->name, array_keys($id)); + } + + $unitOfWork = $this->getUnitOfWork(); + + // Check identity map first + if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) { + if ( ! ($entity instanceof $class->name)) { + return null; + } + + switch (true) { + case LockMode::OPTIMISTIC === $lockMode: + $this->lock($entity, $lockMode, $lockVersion); + break; + + case LockMode::NONE === $lockMode: + case LockMode::PESSIMISTIC_READ === $lockMode: + case LockMode::PESSIMISTIC_WRITE === $lockMode: + $persister = $unitOfWork->getEntityPersister($class->name); + $persister->refresh($sortedId, $entity, $lockMode); + break; + } + + return $entity; // Hit! + } + + $persister = $unitOfWork->getEntityPersister($class->name); + + switch (true) { + case LockMode::OPTIMISTIC === $lockMode: + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + $entity = $persister->load($sortedId); + + $unitOfWork->lock($entity, $lockMode, $lockVersion); + + return $entity; + + case LockMode::NONE === $lockMode: + case LockMode::PESSIMISTIC_READ === $lockMode: + case LockMode::PESSIMISTIC_WRITE === $lockMode: + if ( ! $this->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + return $persister->load($sortedId, null, null, array(), $lockMode); + + default: + return $persister->loadById($sortedId); + } + } + + /** + * {@inheritDoc} + */ + public function getReference($entityName, $id) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + if ( ! is_array($id)) { + $id = array($class->identifier[0] => $id); + } + + $sortedId = array(); + + foreach ($class->identifier as $identifier) { + if ( ! isset($id[$identifier])) { + throw ORMException::missingIdentifierField($class->name, $identifier); + } + + $sortedId[$identifier] = $id[$identifier]; + } + + // Check identity map first, if its already in there just return it. + if (($entity = $this->unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) { + return ($entity instanceof $class->name) ? $entity : null; + } + + if ($class->subClasses) { + return $this->find($entityName, $sortedId); + } + + if ( ! is_array($sortedId)) { + $sortedId = array($class->identifier[0] => $sortedId); + } + + $entity = $this->proxyFactory->getProxy($class->name, $sortedId); + + $this->unitOfWork->registerManaged($entity, $sortedId, array()); + + return $entity; + } + + /** + * {@inheritDoc} + */ + public function getPartialReference($entityName, $identifier) + { + $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\')); + + // Check identity map first, if its already in there just return it. + if (($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) !== false) { + return ($entity instanceof $class->name) ? $entity : null; + } + + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); + } + + $entity = $class->newInstance(); + + $class->setIdentifierValues($entity, $identifier); + + $this->unitOfWork->registerManaged($entity, $identifier, array()); + $this->unitOfWork->markReadOnly($entity); + + return $entity; + } + + /** + * Clears the EntityManager. All entities that are currently managed + * by this EntityManager become detached. + * + * @param string|null $entityName if given, only entities of this type will get detached + * + * @return void + */ + public function clear($entityName = null) + { + $this->unitOfWork->clear($entityName); + } + + /** + * {@inheritDoc} + */ + public function close() + { + $this->clear(); + + $this->closed = true; + } + + /** + * Tells the EntityManager to make an instance managed and persistent. + * + * The entity will be entered into the database at or before transaction + * commit or as a result of the flush operation. + * + * NOTE: The persist operation always considers entities that are not yet known to + * this EntityManager as NEW. Do not pass detached entities to the persist operation. + * + * @param object $entity The instance to make managed and persistent. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function persist($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#persist()' , $entity); + } + + $this->errorIfClosed(); + + $this->unitOfWork->persist($entity); + } + + /** + * Removes an entity instance. + * + * A removed entity will be removed from the database at or before transaction commit + * or as a result of the flush operation. + * + * @param object $entity The entity instance to remove. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function remove($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#remove()' , $entity); + } + + $this->errorIfClosed(); + + $this->unitOfWork->remove($entity); + } + + /** + * Refreshes the persistent state of an entity from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $entity The entity to refresh. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function refresh($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#refresh()' , $entity); + } + + $this->errorIfClosed(); + + $this->unitOfWork->refresh($entity); + } + + /** + * Detaches an entity from the EntityManager, causing a managed entity to + * become detached. Unflushed changes made to the entity if any + * (including removal of the entity), will not be synchronized to the database. + * Entities which previously referenced the detached entity will continue to + * reference it. + * + * @param object $entity The entity to detach. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function detach($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#detach()' , $entity); + } + + $this->unitOfWork->detach($entity); + } + + /** + * Merges the state of a detached entity into the persistence context + * of this EntityManager and returns the managed copy of the entity. + * The entity passed to merge will not become associated/managed with this EntityManager. + * + * @param object $entity The detached entity to merge into the persistence context. + * + * @return object The managed copy of the entity. + * + * @throws ORMInvalidArgumentException + */ + public function merge($entity) + { + if ( ! is_object($entity)) { + throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()' , $entity); + } + + $this->errorIfClosed(); + + return $this->unitOfWork->merge($entity); + } + + /** + * {@inheritDoc} + * + * @todo Implementation need. This is necessary since $e2 = clone $e1; throws an E_FATAL when access anything on $e: + * Fatal error: Maximum function nesting level of '100' reached, aborting! + */ + public function copy($entity, $deep = false) + { + throw new \BadMethodCallException("Not implemented."); + } + + /** + * {@inheritDoc} + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + $this->unitOfWork->lock($entity, $lockMode, $lockVersion); + } + + /** + * Gets the repository for an entity class. + * + * @param string $entityName The name of the entity. + * + * @return \Doctrine\ORM\EntityRepository The repository class. + */ + public function getRepository($entityName) + { + return $this->repositoryFactory->getRepository($this, $entityName); + } + + /** + * Determines whether an entity instance is managed in this EntityManager. + * + * @param object $entity + * + * @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise. + */ + public function contains($entity) + { + return $this->unitOfWork->isScheduledForInsert($entity) + || $this->unitOfWork->isInIdentityMap($entity) + && ! $this->unitOfWork->isScheduledForDelete($entity); + } + + /** + * {@inheritDoc} + */ + public function getEventManager() + { + return $this->eventManager; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration() + { + return $this->config; + } + + /** + * Throws an exception if the EntityManager is closed or currently not active. + * + * @return void + * + * @throws ORMException If the EntityManager is closed. + */ + private function errorIfClosed() + { + if ($this->closed) { + throw ORMException::entityManagerClosed(); + } + } + + /** + * {@inheritDoc} + */ + public function isOpen() + { + return (!$this->closed); + } + + /** + * {@inheritDoc} + */ + public function getUnitOfWork() + { + return $this->unitOfWork; + } + + /** + * {@inheritDoc} + */ + public function getHydrator($hydrationMode) + { + return $this->newHydrator($hydrationMode); + } + + /** + * {@inheritDoc} + */ + public function newHydrator($hydrationMode) + { + switch ($hydrationMode) { + case Query::HYDRATE_OBJECT: + return new Internal\Hydration\ObjectHydrator($this); + + case Query::HYDRATE_ARRAY: + return new Internal\Hydration\ArrayHydrator($this); + + case Query::HYDRATE_SCALAR: + return new Internal\Hydration\ScalarHydrator($this); + + case Query::HYDRATE_SINGLE_SCALAR: + return new Internal\Hydration\SingleScalarHydrator($this); + + case Query::HYDRATE_SIMPLEOBJECT: + return new Internal\Hydration\SimpleObjectHydrator($this); + + default: + if (($class = $this->config->getCustomHydrationMode($hydrationMode)) !== null) { + return new $class($this); + } + } + + throw ORMException::invalidHydrationMode($hydrationMode); + } + + /** + * {@inheritDoc} + */ + public function getProxyFactory() + { + return $this->proxyFactory; + } + + /** + * {@inheritDoc} + */ + public function initializeObject($obj) + { + $this->unitOfWork->initializeObject($obj); + } + + /** + * Factory method to create EntityManager instances. + * + * @param mixed $conn An array with the connection parameters or an existing Connection instance. + * @param Configuration $config The Configuration instance to use. + * @param EventManager $eventManager The EventManager instance to use. + * + * @return EntityManager The created EntityManager. + * + * @throws \InvalidArgumentException + * @throws ORMException + */ + public static function create($conn, Configuration $config, EventManager $eventManager = null) + { + if ( ! $config->getMetadataDriverImpl()) { + throw ORMException::missingMappingDriverImpl(); + } + + switch (true) { + case (is_array($conn)): + $conn = \Doctrine\DBAL\DriverManager::getConnection( + $conn, $config, ($eventManager ?: new EventManager()) + ); + break; + + case ($conn instanceof Connection): + if ($eventManager !== null && $conn->getEventManager() !== $eventManager) { + throw ORMException::mismatchedEventManager(); + } + break; + + default: + throw new \InvalidArgumentException("Invalid argument: " . $conn); + } + + return new EntityManager($conn, $config, $conn->getEventManager()); + } + + /** + * {@inheritDoc} + */ + public function getFilters() + { + if (null === $this->filterCollection) { + $this->filterCollection = new FilterCollection($this); + } + + return $this->filterCollection; + } + + /** + * {@inheritDoc} + */ + public function isFiltersStateClean() + { + return null === $this->filterCollection || $this->filterCollection->isClean(); + } + + /** + * {@inheritDoc} + */ + public function hasFilters() + { + return null !== $this->filterCollection; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php new file mode 100644 index 0000000..016cfd4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManagerInterface.php @@ -0,0 +1,296 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * EntityManager interface + * + * @since 2.4 + * @author Lars Strojny + * + * @method Mapping\ClassMetadata getClassMetadata($className) + */ +interface EntityManagerInterface extends ObjectManager +{ + /** + * Returns the cache API for managing the second level cache regions or NULL if the cache is not enabled. + * + * @return \Doctrine\ORM\Cache|null + */ + public function getCache(); + + /** + * Gets the database connection object used by the EntityManager. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection(); + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * + * Example: + * + * + * $qb = $em->createQueryBuilder(); + * $expr = $em->getExpressionBuilder(); + * $qb->select('u')->from('User', 'u') + * ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2))); + * + * + * @return \Doctrine\ORM\Query\Expr + */ + public function getExpressionBuilder(); + + /** + * Starts a transaction on the underlying database connection. + * + * @return void + */ + public function beginTransaction(); + + /** + * Executes a function in a transaction. + * + * The function gets passed this EntityManager instance as an (optional) parameter. + * + * {@link flush} is invoked prior to transaction commit. + * + * If an exception occurs during execution of the function or flushing or transaction commit, + * the transaction is rolled back, the EntityManager closed and the exception re-thrown. + * + * @param callable $func The function to execute transactionally. + * + * @return mixed The non-empty value returned from the closure or true instead. + */ + public function transactional($func); + + /** + * Commits a transaction on the underlying database connection. + * + * @return void + */ + public function commit(); + + /** + * Performs a rollback on the underlying database connection. + * + * @return void + */ + public function rollback(); + + /** + * Creates a new Query object. + * + * @param string $dql The DQL string. + * + * @return Query + */ + public function createQuery($dql = ''); + + /** + * Creates a Query from a named query. + * + * @param string $name + * + * @return Query + */ + public function createNamedQuery($name); + + /** + * Creates a native SQL query. + * + * @param string $sql + * @param ResultSetMapping $rsm The ResultSetMapping to use. + * + * @return NativeQuery + */ + public function createNativeQuery($sql, ResultSetMapping $rsm); + + /** + * Creates a NativeQuery from a named native query. + * + * @param string $name + * + * @return NativeQuery + */ + public function createNamedNativeQuery($name); + + /** + * Create a QueryBuilder instance + * + * @return QueryBuilder + */ + public function createQueryBuilder(); + + /** + * Gets a reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * @param string $entityName The name of the entity type. + * @param mixed $id The entity identifier. + * + * @return object The entity reference. + * + * @throws ORMException + */ + public function getReference($entityName, $id); + + /** + * Gets a partial reference to the entity identified by the given type and identifier + * without actually loading it, if the entity is not yet loaded. + * + * The returned reference may be a partial object if the entity is not yet loaded/managed. + * If it is a partial object it will not initialize the rest of the entity state on access. + * Thus you can only ever safely access the identifier of an entity obtained through + * this method. + * + * The use-cases for partial references involve maintaining bidirectional associations + * without loading one side of the association or to update an entity without loading it. + * Note, however, that in the latter case the original (persistent) entity data will + * never be visible to the application (especially not event listeners) as it will + * never be loaded in the first place. + * + * @param string $entityName The name of the entity type. + * @param mixed $identifier The entity identifier. + * + * @return object The (partial) entity reference. + */ + public function getPartialReference($entityName, $identifier); + + /** + * Closes the EntityManager. All entities that are currently managed + * by this EntityManager become detached. The EntityManager may no longer + * be used after it is closed. + * + * @return void + */ + public function close(); + + /** + * Creates a copy of the given entity. Can create a shallow or a deep copy. + * + * @param object $entity The entity to copy. + * @param boolean $deep FALSE for a shallow copy, TRUE for a deep copy. + * + * @return object The new entity. + * + * @throws \BadMethodCallException + */ + public function copy($entity, $deep = false); + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int|null $lockVersion + * + * @return void + * + * @throws OptimisticLockException + * @throws PessimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null); + + /** + * Gets the EventManager used by the EntityManager. + * + * @return \Doctrine\Common\EventManager + */ + public function getEventManager(); + + /** + * Gets the Configuration used by the EntityManager. + * + * @return Configuration + */ + public function getConfiguration(); + + /** + * Check if the Entity manager is open or closed. + * + * @return bool + */ + public function isOpen(); + + /** + * Gets the UnitOfWork used by the EntityManager to coordinate operations. + * + * @return UnitOfWork + */ + public function getUnitOfWork(); + + /** + * Gets a hydrator for the given hydration mode. + * + * This method caches the hydrator instances which is used for all queries that don't + * selectively iterate over the result. + * + * @deprecated + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + public function getHydrator($hydrationMode); + + /** + * Create a new instance for the given hydration mode. + * + * @param int $hydrationMode + * + * @return \Doctrine\ORM\Internal\Hydration\AbstractHydrator + * + * @throws ORMException + */ + public function newHydrator($hydrationMode); + + /** + * Gets the proxy factory used by the EntityManager to create entity proxies. + * + * @return \Doctrine\ORM\Proxy\ProxyFactory + */ + public function getProxyFactory(); + + /** + * Gets the enabled filters. + * + * @return \Doctrine\ORM\Query\FilterCollection The active filter collection. + */ + public function getFilters(); + + /** + * Checks whether the state of the filter collection is clean. + * + * @return boolean True, if the filter collection is clean. + */ + public function isFiltersStateClean(); + + /** + * Checks whether the Entity Manager has filters. + * + * @return boolean True, if the EM has a filter collection. + */ + public function hasFilters(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php new file mode 100644 index 0000000..afe2f22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityNotFoundException.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when a Proxy fails to retrieve an Entity result. + * + * @author robo + * @since 2.0 + */ +class EntityNotFoundException extends ORMException +{ + /** + * Static constructor. + * + * @param string $className + * @param string[] $id + * + * @return self + */ + public static function fromClassNameAndIdentifier($className, array $id) + { + $ids = array(); + + foreach ($id as $key => $value) { + $ids[] = $key . '(' . $value . ')'; + } + + + return new self( + 'Entity of type \'' . $className . '\'' . ($ids ? ' for IDs ' . implode(', ', $ids) : '') . ' was not found' + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php new file mode 100644 index 0000000..47d0648 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php @@ -0,0 +1,306 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\ORM\Query\ResultSetMappingBuilder; +use Doctrine\Common\Persistence\ObjectRepository; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * An EntityRepository serves as a repository for entities with generic as well as + * business specific methods for retrieving entities. + * + * This class is designed for inheritance and users can subclass this class to + * write their own repositories with business-specific methods to locate entities. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepository implements ObjectRepository, Selectable +{ + /** + * @var string + */ + protected $_entityName; + + /** + * @var EntityManager + */ + protected $_em; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $_class; + + /** + * Initializes a new EntityRepository. + * + * @param EntityManager $em The EntityManager to use. + * @param Mapping\ClassMetadata $class The class descriptor. + */ + public function __construct($em, Mapping\ClassMetadata $class) + { + $this->_entityName = $class->name; + $this->_em = $em; + $this->_class = $class; + } + + /** + * Creates a new QueryBuilder instance that is prepopulated for this entity name. + * + * @param string $alias + * @param string $indexBy The index for the from. + * + * @return QueryBuilder + */ + public function createQueryBuilder($alias, $indexBy = null) + { + return $this->_em->createQueryBuilder() + ->select($alias) + ->from($this->_entityName, $alias, $indexBy); + } + + /** + * Creates a new result set mapping builder for this entity. + * + * The column naming strategy is "INCREMENT". + * + * @param string $alias + * + * @return ResultSetMappingBuilder + */ + public function createResultSetMappingBuilder($alias) + { + $rsm = new ResultSetMappingBuilder($this->_em, ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT); + $rsm->addRootEntityFromClassMetadata($this->_entityName, $alias); + + return $rsm; + } + + /** + * Creates a new Query instance based on a predefined metadata named query. + * + * @param string $queryName + * + * @return Query + */ + public function createNamedQuery($queryName) + { + return $this->_em->createQuery($this->_class->getNamedQuery($queryName)); + } + + /** + * Creates a native SQL query. + * + * @param string $queryName + * + * @return NativeQuery + */ + public function createNativeNamedQuery($queryName) + { + $queryMapping = $this->_class->getNamedNativeQuery($queryName); + $rsm = new Query\ResultSetMappingBuilder($this->_em); + $rsm->addNamedNativeQueryMapping($this->_class, $queryMapping); + + return $this->_em->createNativeQuery($queryMapping['query'], $rsm); + } + + /** + * Clears the repository, causing all managed entities to become detached. + * + * @return void + */ + public function clear() + { + $this->_em->clear($this->_class->rootEntityName); + } + + /** + * Finds an entity by its primary key / identifier. + * + * @param mixed $id The identifier. + * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * during the search. + * @param int|null $lockVersion The lock version. + * + * @return object|null The entity instance or NULL if the entity can not be found. + */ + public function find($id, $lockMode = null, $lockVersion = null) + { + return $this->_em->find($this->_entityName, $id, $lockMode, $lockVersion); + } + + /** + * Finds all entities in the repository. + * + * @return array The entities. + */ + public function findAll() + { + return $this->findBy(array()); + } + + /** + * Finds entities by a set of criteria. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array The objects. + */ + public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return $persister->loadAll($criteria, $orderBy, $limit, $offset); + } + + /** + * Finds a single entity by a set of criteria. + * + * @param array $criteria + * @param array|null $orderBy + * + * @return object|null The entity instance or NULL if the entity can not be found. + */ + public function findOneBy(array $criteria, array $orderBy = null) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return $persister->load($criteria, null, null, array(), null, 1, $orderBy); + } + + /** + * Adds support for magic finders. + * + * @param string $method + * @param array $arguments + * + * @return array|object The found entity/entities. + * + * @throws ORMException + * @throws \BadMethodCallException If the method called is an invalid find* method + * or no find* method at all and therefore an invalid + * method call. + */ + public function __call($method, $arguments) + { + switch (true) { + case (0 === strpos($method, 'findBy')): + $by = substr($method, 6); + $method = 'findBy'; + break; + + case (0 === strpos($method, 'findOneBy')): + $by = substr($method, 9); + $method = 'findOneBy'; + break; + + default: + throw new \BadMethodCallException( + "Undefined method '$method'. The method name must start with ". + "either findBy or findOneBy!" + ); + } + + if (empty($arguments)) { + throw ORMException::findByRequiresParameter($method . $by); + } + + $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); + + if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { + switch (count($arguments)) { + case 1: + return $this->$method(array($fieldName => $arguments[0])); + + case 2: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1]); + + case 3: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1], $arguments[2]); + + case 4: + return $this->$method(array($fieldName => $arguments[0]), $arguments[1], $arguments[2], $arguments[3]); + + default: + // Do nothing + } + } + + throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); + } + + /** + * @return string + */ + protected function getEntityName() + { + return $this->_entityName; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->getEntityName(); + } + + /** + * @return EntityManager + */ + protected function getEntityManager() + { + return $this->_em; + } + + /** + * @return Mapping\ClassMetadata + */ + protected function getClassMetadata() + { + return $this->_class; + } + + /** + * Select all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return \Doctrine\Common\Collections\Collection + */ + public function matching(Criteria $criteria) + { + $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName); + + return new LazyCriteriaCollection($persister, $criteria); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php new file mode 100644 index 0000000..9a5c8cf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LifecycleEventArgs.php @@ -0,0 +1,54 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LifecycleEventArgs extends BaseLifecycleEventArgs +{ + /** + * Retrieves associated Entity. + * + * @return object + */ + public function getEntity() + { + return $this->getObject(); + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php new file mode 100644 index 0000000..bbcbd41 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php @@ -0,0 +1,120 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * A method invoker based on entity lifecycle. + * + * @author Fabio B. Silva + * @since 2.4 + */ +class ListenersInvoker +{ + const INVOKE_NONE = 0; + const INVOKE_LISTENERS = 1; + const INVOKE_CALLBACKS = 2; + const INVOKE_MANAGER = 4; + + /** + * @var \Doctrine\ORM\Mapping\EntityListenerResolver The Entity listener resolver. + */ + private $resolver; + + /** + * The EventManager used for dispatching events. + * + * @var \Doctrine\Common\EventManager + */ + private $eventManager; + + /** + * Initializes a new ListenersInvoker instance. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->eventManager = $em->getEventManager(); + $this->resolver = $em->getConfiguration()->getEntityListenerResolver(); + } + + /** + * Get the subscribed event systems + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $eventName The entity lifecycle event. + * + * @return integer Bitmask of subscribed event systems. + */ + public function getSubscribedSystems(ClassMetadata $metadata, $eventName) + { + $invoke = self::INVOKE_NONE; + + if (isset($metadata->lifecycleCallbacks[$eventName])) { + $invoke |= self::INVOKE_CALLBACKS; + } + + if (isset($metadata->entityListeners[$eventName])) { + $invoke |= self::INVOKE_LISTENERS; + } + + if ($this->eventManager->hasListeners($eventName)) { + $invoke |= self::INVOKE_MANAGER; + } + + return $invoke; + } + + /** + * Dispatches the lifecycle event of the given entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $eventName The entity lifecycle event. + * @param object $entity The Entity on which the event occurred. + * @param \Doctrine\Common\EventArgs $event The Event args. + * @param integer $invoke Bitmask to invoke listeners. + */ + public function invoke(ClassMetadata $metadata, $eventName, $entity, EventArgs $event, $invoke) + { + if($invoke & self::INVOKE_CALLBACKS) { + foreach ($metadata->lifecycleCallbacks[$eventName] as $callback) { + $entity->$callback($event); + } + } + + if($invoke & self::INVOKE_LISTENERS) { + foreach ($metadata->entityListeners[$eventName] as $listener) { + $class = $listener['class']; + $method = $listener['method']; + $instance = $this->resolver->resolve($class); + + $instance->$method($entity, $event); + } + } + + if($invoke & self::INVOKE_MANAGER) { + $this->eventManager->dispatchEvent($eventName, $event); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 0000000..95e7561 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs as BaseLoadClassMetadataEventArgs; + +/** + * Class that holds event arguments for a loadMetadata event. + * + * @author Jonathan H. Wage + * @since 2.0 + * + * Note: method annotations are used instead of method overrides (due to BC policy) + * + * @method __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata, \Doctrine\ORM\EntityManager $objectManager) + * @method \Doctrine\ORM\EntityManager getClassMetadata() + */ +class LoadClassMetadataEventArgs extends BaseLoadClassMetadataEventArgs +{ + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php new file mode 100644 index 0000000..a044a7e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClassMetadataNotFoundEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\Persistence\Event\ManagerEventArgs; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; + +/** + * Class that holds event arguments for a `onClassMetadataNotFound` event. + * + * This object is mutable by design, allowing callbacks having access to it to set the + * found metadata in it, and therefore "cancelling" a `onClassMetadataNotFound` event + * + * @author Marco Pivetta + * @since 2.5 + */ +class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs +{ + /** + * @var string + */ + private $className; + + /** + * @var ClassMetadata|null + */ + private $foundMetadata; + + /** + * Constructor. + * + * @param string $className + * @param ObjectManager $objectManager + */ + public function __construct($className, ObjectManager $objectManager) + { + $this->className = (string) $className; + + parent::__construct($objectManager); + } + + /** + * @param ClassMetadata|null $classMetadata + */ + public function setFoundMetadata(ClassMetadata $classMetadata = null) + { + $this->foundMetadata = $classMetadata; + } + + /** + * @return ClassMetadata|null + */ + public function getFoundMetadata() + { + return $this->foundMetadata; + } + + /** + * Retrieve class name for which a failed metadata fetch attempt was executed + * + * @return string + */ + public function getClassName() + { + return $this->className; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php new file mode 100644 index 0000000..dd827c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnClearEventArgs.php @@ -0,0 +1,86 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the onClear event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnClearEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * @var string + */ + private $entityClass; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + * @param string|null $entityClass Optional entity class. + */ + public function __construct(EntityManagerInterface $em, $entityClass = null) + { + $this->em = $em; + $this->entityClass = $entityClass; + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Name of the entity class that is cleared, or empty if all are cleared. + * + * @return string|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Checks if event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return ($this->entityClass === null); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php new file mode 100644 index 0000000..6a9c7c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/OnFlushEventArgs.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class OnFlushEventArgs extends EventArgs +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Retrieve associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php new file mode 100644 index 0000000..860f2d3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PostFlushEventArgs.php @@ -0,0 +1,58 @@ +. + */ +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the postFlush event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Daniel Freudenberger + */ +class PostFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Retrieves associated EntityManager. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php new file mode 100644 index 0000000..d01a926 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class PreFlushEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php new file mode 100644 index 0000000..d9a9f9d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php @@ -0,0 +1,137 @@ +. + */ + +namespace Doctrine\ORM\Event; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Class that holds event arguments for a preInsert/preUpdate event. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class PreUpdateEventArgs extends LifecycleEventArgs +{ + /** + * @var array + */ + private $entityChangeSet; + + /** + * Constructor. + * + * @param object $entity + * @param EntityManagerInterface $em + * @param array $changeSet + */ + public function __construct($entity, EntityManagerInterface $em, array &$changeSet) + { + parent::__construct($entity, $em); + + $this->entityChangeSet = &$changeSet; + } + + /** + * Retrieves entity changeset. + * + * @return array + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return boolean + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Gets the old value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][0]; + } + + /** + * Gets the new value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][1]; + } + + /** + * Sets the new value of this field. + * + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Asserts the field exists in changeset. + * + * @param string $field + * + * @return void + * + * @throws \InvalidArgumentException + */ + private function assertValidField($field) + { + if ( ! isset($this->entityChangeSet[$field])) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', + $field, + get_class($this->getEntity()) + )); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php new file mode 100644 index 0000000..e16b47a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Events.php @@ -0,0 +1,167 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Container for all ORM events. + * + * This class cannot be instantiated. + * + * @author Roman Borschel + * @since 2.0 + */ +final class Events +{ + /** + * Private constructor. This class is not meant to be instantiated. + */ + private function __construct() + { + } + + /** + * The preRemove event occurs for a given entity before the respective + * EntityManager remove operation for that entity is executed. + * + * This is an entity lifecycle event. + * + * @var string + */ + const preRemove = 'preRemove'; + + /** + * The postRemove event occurs for an entity after the entity has + * been deleted. It will be invoked after the database delete operations. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postRemove = 'postRemove'; + + /** + * The prePersist event occurs for a given entity before the respective + * EntityManager persist operation for that entity is executed. + * + * This is an entity lifecycle event. + * + * @var string + */ + const prePersist = 'prePersist'; + + /** + * The postPersist event occurs for an entity after the entity has + * been made persistent. It will be invoked after the database insert operations. + * Generated primary key values are available in the postPersist event. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postPersist = 'postPersist'; + + /** + * The preUpdate event occurs before the database update operations to + * entity data. + * + * This is an entity lifecycle event. + * + * @var string + */ + const preUpdate = 'preUpdate'; + + /** + * The postUpdate event occurs after the database update operations to + * entity data. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postUpdate = 'postUpdate'; + + /** + * The postLoad event occurs for an entity after the entity has been loaded + * into the current EntityManager from the database or after the refresh operation + * has been applied to it. + * + * Note that the postLoad event occurs for an entity before any associations have been + * initialized. Therefore it is not safe to access associations in a postLoad callback + * or event handler. + * + * This is an entity lifecycle event. + * + * @var string + */ + const postLoad = 'postLoad'; + + /** + * The loadClassMetadata event occurs after the mapping metadata for a class + * has been loaded from a mapping source (annotations/xml/yaml). + * + * @var string + */ + const loadClassMetadata = 'loadClassMetadata'; + + /** + * The onClassMetadataNotFound event occurs whenever loading metadata for a class + * failed. + * + * @var string + */ + const onClassMetadataNotFound = 'onClassMetadataNotFound'; + + /** + * The preFlush event occurs when the EntityManager#flush() operation is invoked, + * but before any changes to managed entities have been calculated. This event is + * always raised right after EntityManager#flush() call. + */ + const preFlush = 'preFlush'; + + /** + * The onFlush event occurs when the EntityManager#flush() operation is invoked, + * after any changes to managed entities have been determined but before any + * actual database operations are executed. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the onFlush event is not raised. + * + * @var string + */ + const onFlush = 'onFlush'; + + /** + * The postFlush event occurs when the EntityManager#flush() operation is invoked and + * after all actual database operations are executed successfully. The event is only raised if there is + * actually something to do for the underlying UnitOfWork. If nothing needs to be done, + * the postFlush event is not raised. The event won't be raised if an error occurs during the + * flush operation. + * + * @var string + */ + const postFlush = 'postFlush'; + + /** + * The onClear event occurs when the EntityManager#clear() operation is invoked, + * after all references to entities have been removed from the unit of work. + * + * @var string + */ + const onClear = 'onClear'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php new file mode 100644 index 0000000..5ed2872 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AbstractIdGenerator.php @@ -0,0 +1,49 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +abstract class AbstractIdGenerator +{ + /** + * Generates an identifier for an entity. + * + * @param EntityManager|EntityManager $em + * @param \Doctrine\ORM\Mapping\Entity $entity + * @return mixed + */ + abstract public function generate(EntityManager $em, $entity); + + /** + * Gets whether this generator is a post-insert generator which means that + * {@link generate()} must be called after the entity has been inserted + * into the database. + * + * By default, this method returns FALSE. Generators that have this requirement + * must override this method and return TRUE. + * + * @return boolean + */ + public function isPostInsertGenerator() + { + return false; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php new file mode 100644 index 0000000..447dbd6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\ORMException; + +/** + * Special generator for application-assigned identifiers (doesn't really generate anything). + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AssignedGenerator extends AbstractIdGenerator +{ + /** + * Returns the identifier assigned to the given entity. + * + * {@inheritDoc} + * + * @throws \Doctrine\ORM\ORMException + */ + public function generate(EntityManager $em, $entity) + { + $class = $em->getClassMetadata(get_class($entity)); + $idFields = $class->getIdentifierFieldNames(); + $identifier = array(); + + foreach ($idFields as $idField) { + $value = $class->getFieldValue($entity, $idField); + + if ( ! isset($value)) { + throw ORMException::entityMissingAssignedIdForField($entity, $idField); + } + + if (isset($class->associationMappings[$idField])) { + // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. + $value = $em->getUnitOfWork()->getSingleIdentifierValue($value); + } + + $identifier[$idField] = $value; + } + + return $identifier; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php new file mode 100644 index 0000000..01d139f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/BigIntegerIdentityGenerator.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that obtains IDs from special "identity" columns. These are columns + * that automatically get a database-generated, auto-incremented identifier on INSERT. + * This generator obtains the last insert id after such an insert. + */ +class BigIntegerIdentityGenerator extends AbstractIdGenerator +{ + /** + * The name of the sequence to pass to lastInsertId(), if any. + * + * @var string + */ + private $sequenceName; + + /** + * Constructor. + * + * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. + */ + public function __construct($sequenceName = null) + { + $this->sequenceName = $sequenceName; + } + + /** + * {@inheritDoc} + */ + public function generate(EntityManager $em, $entity) + { + return (string) $em->getConnection()->lastInsertId($this->sequenceName); + } + + /** + * {@inheritDoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php new file mode 100644 index 0000000..46cd7db --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/IdentityGenerator.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that obtains IDs from special "identity" columns. These are columns + * that automatically get a database-generated, auto-incremented identifier on INSERT. + * This generator obtains the last insert id after such an insert. + */ +class IdentityGenerator extends AbstractIdGenerator +{ + /** + * The name of the sequence to pass to lastInsertId(), if any. + * + * @var string + */ + private $sequenceName; + + /** + * Constructor. + * + * @param string|null $sequenceName The name of the sequence to pass to lastInsertId() + * to obtain the last generated identifier within the current + * database session/connection, if any. + */ + public function __construct($sequenceName = null) + { + $this->sequenceName = $sequenceName; + } + + /** + * {@inheritDoc} + */ + public function generate( + EntityManager $em, $entity) + { + return (int)$em->getConnection()->lastInsertId($this->sequenceName); + } + + /** + * {@inheritdoc} + */ + public function isPostInsertGenerator() + { + return true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php new file mode 100644 index 0000000..1236d95 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/SequenceGenerator.php @@ -0,0 +1,129 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; +use Serializable; + +/** + * Represents an ID generator that uses a database sequence. + * + * @since 2.0 + * @author Roman Borschel + */ +class SequenceGenerator extends AbstractIdGenerator implements Serializable +{ + /** + * The allocation size of the sequence. + * + * @var int + */ + private $_allocationSize; + + /** + * The name of the sequence. + * + * @var string + */ + private $_sequenceName; + + /** + * @var int + */ + private $_nextValue = 0; + + /** + * @var int|null + */ + private $_maxValue = null; + + /** + * Initializes a new sequence generator. + * + * @param string $sequenceName The name of the sequence. + * @param integer $allocationSize The allocation size of the sequence. + */ + public function __construct($sequenceName, $allocationSize) + { + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + /** + * {@inheritDoc} + */ + public function generate(EntityManager $em, $entity) + { + if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { + // Allocate new values + $conn = $em->getConnection(); + $sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName); + + $this->_nextValue = (int)$conn->fetchColumn($sql); + $this->_maxValue = $this->_nextValue + $this->_allocationSize; + } + + return $this->_nextValue++; + } + + /** + * Gets the maximum value of the currently allocated bag of values. + * + * @return integer|null + */ + public function getCurrentMaxValue() + { + return $this->_maxValue; + } + + /** + * Gets the next value that will be returned by generate(). + * + * @return integer + */ + public function getNextValue() + { + return $this->_nextValue; + } + + /** + * @return string + */ + public function serialize() + { + return serialize(array( + 'allocationSize' => $this->_allocationSize, + 'sequenceName' => $this->_sequenceName + )); + } + + /** + * @param string $serialized + * + * @return void + */ + public function unserialize($serialized) + { + $array = unserialize($serialized); + + $this->_sequenceName = $array['sequenceName']; + $this->_allocationSize = $array['allocationSize']; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php new file mode 100644 index 0000000..abf3ab8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/TableGenerator.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Id generator that uses a single-row database table and a hi/lo algorithm. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class TableGenerator extends AbstractIdGenerator +{ + /** + * @var string + */ + private $_tableName; + + /** + * @var string + */ + private $_sequenceName; + + /** + * @var int + */ + private $_allocationSize; + + /** + * @var int|null + */ + private $_nextValue; + + /** + * @var int|null + */ + private $_maxValue; + + /** + * @param string $tableName + * @param string $sequenceName + * @param int $allocationSize + */ + public function __construct($tableName, $sequenceName = 'default', $allocationSize = 10) + { + $this->_tableName = $tableName; + $this->_sequenceName = $sequenceName; + $this->_allocationSize = $allocationSize; + } + + /** + * {@inheritDoc} + */ + public function generate( + EntityManager $em, $entity) + { + if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) { + // Allocate new values + $conn = $em->getConnection(); + + if ($conn->getTransactionNestingLevel() === 0) { + // use select for update + $sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName); + $currentLevel = $conn->fetchColumn($sql); + + if ($currentLevel != null) { + $this->_nextValue = $currentLevel; + $this->_maxValue = $this->_nextValue + $this->_allocationSize; + + $updateSql = $conn->getDatabasePlatform()->getTableHiLoUpdateNextValSql( + $this->_tableName, $this->_sequenceName, $this->_allocationSize + ); + + if ($conn->executeUpdate($updateSql, array(1 => $currentLevel, 2 => $currentLevel+1)) !== 1) { + // no affected rows, concurrency issue, throw exception + } + } else { + // no current level returned, TableGenerator seems to be broken, throw exception + } + } else { + // only table locks help here, implement this or throw exception? + // or do we want to work with table locks exclusively? + } + } + + return $this->_nextValue++; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php new file mode 100644 index 0000000..a8e92bb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Id/UuidGenerator.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Id; + +use Doctrine\ORM\EntityManager; + +/** + * Represents an ID generator that uses the database UUID expression + * + * @since 2.3 + * @author Maarten de Keizer + */ +class UuidGenerator extends AbstractIdGenerator +{ + /** + * {@inheritDoc} + */ + public function generate(EntityManager $em, $entity) + { + $conn = $em->getConnection(); + $sql = 'SELECT ' . $conn->getDatabasePlatform()->getGuidExpression(); + return $conn->query($sql)->fetchColumn(0); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php new file mode 100644 index 0000000..9e069b0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/CommitOrderCalculator.php @@ -0,0 +1,156 @@ +. + */ + +namespace Doctrine\ORM\Internal; + +/** + * The CommitOrderCalculator is used by the UnitOfWork to sort out the + * correct order in which changes to entities need to be persisted. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class CommitOrderCalculator +{ + const NOT_VISITED = 1; + const IN_PROGRESS = 2; + const VISITED = 3; + + /** + * @var array + */ + private $_nodeStates = array(); + + /** + * The nodes to sort. + * + * @var array + */ + private $_classes = array(); + + /** + * @var array + */ + private $_relatedClasses = array(); + + /** + * @var array + */ + private $_sorted = array(); + + /** + * Clears the current graph. + * + * @return void + */ + public function clear() + { + $this->_classes = array(); + $this->_relatedClasses = array(); + } + + /** + * Gets a valid commit order for all current nodes. + * + * Uses a depth-first search (DFS) to traverse the graph. + * The desired topological sorting is the reverse postorder of these searches. + * + * @return array The list of ordered classes. + */ + public function getCommitOrder() + { + // Check whether we need to do anything. 0 or 1 node is easy. + $nodeCount = count($this->_classes); + + if ($nodeCount <= 1) { + return ($nodeCount == 1) ? array_values($this->_classes) : array(); + } + + // Init + foreach ($this->_classes as $node) { + $this->_nodeStates[$node->name] = self::NOT_VISITED; + } + + // Go + foreach ($this->_classes as $node) { + if ($this->_nodeStates[$node->name] == self::NOT_VISITED) { + $this->_visitNode($node); + } + } + + $sorted = array_reverse($this->_sorted); + + $this->_sorted = $this->_nodeStates = array(); + + return $sorted; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $node + * + * @return void + */ + private function _visitNode($node) + { + $this->_nodeStates[$node->name] = self::IN_PROGRESS; + + if (isset($this->_relatedClasses[$node->name])) { + foreach ($this->_relatedClasses[$node->name] as $relatedNode) { + if ($this->_nodeStates[$relatedNode->name] == self::NOT_VISITED) { + $this->_visitNode($relatedNode); + } + } + } + + $this->_nodeStates[$node->name] = self::VISITED; + $this->_sorted[] = $node; + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $fromClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $toClass + * + * @return void + */ + public function addDependency($fromClass, $toClass) + { + $this->_relatedClasses[$fromClass->name][] = $toClass; + } + + /** + * @param string $className + * + * @return bool + */ + public function hasClass($className) + { + return isset($this->_classes[$className]); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + public function addClass($class) + { + $this->_classes[$class->name] = $class; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php new file mode 100644 index 0000000..af232d1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -0,0 +1,470 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\ClassMetadata; +use PDO; + +/** + * Base class for all hydrators. A hydrator is a class that provides some form + * of transformation of an SQL result set into another structure. + * + * @since 2.0 + * @author Konsta Vesterinen + * @author Roman Borschel + * @author Guilherme Blanco + */ +abstract class AbstractHydrator +{ + /** + * The ResultSetMapping. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + protected $_rsm; + + /** + * The EntityManager instance. + * + * @var EntityManagerInterface + */ + protected $_em; + + /** + * The dbms Platform instance. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $_platform; + + /** + * The UnitOfWork of the associated EntityManager. + * + * @var \Doctrine\ORM\UnitOfWork + */ + protected $_uow; + + /** + * Local ClassMetadata cache to avoid going to the EntityManager all the time. + * + * @var array + */ + protected $_metadataCache = array(); + + /** + * The cache used during row-by-row hydration. + * + * @var array + */ + protected $_cache = array(); + + /** + * The statement that provides the data to hydrate. + * + * @var \Doctrine\DBAL\Driver\Statement + */ + protected $_stmt; + + /** + * The query hints. + * + * @var array + */ + protected $_hints; + + /** + * Initializes a new instance of a class derived from AbstractHydrator. + * + * @param EntityManagerInterface $em The EntityManager to use. + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + $this->_platform = $em->getConnection()->getDatabasePlatform(); + $this->_uow = $em->getUnitOfWork(); + } + + /** + * Initiates a row-by-row hydration. + * + * @param object $stmt + * @param object $resultSetMapping + * @param array $hints + * + * @return IterableResult + */ + public function iterate($stmt, $resultSetMapping, array $hints = array()) + { + $this->_stmt = $stmt; + $this->_rsm = $resultSetMapping; + $this->_hints = $hints; + + $evm = $this->_em->getEventManager(); + $evm->addEventListener(array(Events::onClear), $this); + + $this->prepare(); + + return new IterableResult($this); + } + + /** + * Hydrates all rows returned by the passed statement instance at once. + * + * @param object $stmt + * @param object $resultSetMapping + * @param array $hints + * + * @return array + */ + public function hydrateAll($stmt, $resultSetMapping, array $hints = array()) + { + $this->_stmt = $stmt; + $this->_rsm = $resultSetMapping; + $this->_hints = $hints; + + $this->prepare(); + + $result = $this->hydrateAllData(); + + $this->cleanup(); + + return $result; + } + + /** + * Hydrates a single row returned by the current statement instance during + * row-by-row hydration with {@link iterate()}. + * + * @return mixed + */ + public function hydrateRow() + { + $row = $this->_stmt->fetch(PDO::FETCH_ASSOC); + + if ( ! $row) { + $this->cleanup(); + + return false; + } + + $result = array(); + + $this->hydrateRowData($row, $result); + + return $result; + } + + /** + * When executed in a hydrate() loop we have to clear internal state to + * decrease memory consumption. + * + * @param mixed $eventArgs + * + * @return void + */ + public function onClear($eventArgs) + { + } + + /** + * Executes one-time preparation tasks, once each time hydration is started + * through {@link hydrateAll} or {@link iterate()}. + * + * @return void + */ + protected function prepare() + { + } + + /** + * Executes one-time cleanup tasks at the end of a hydration that was initiated + * through {@link hydrateAll} or {@link iterate()}. + * + * @return void + */ + protected function cleanup() + { + $this->_stmt->closeCursor(); + + $this->_stmt = null; + $this->_rsm = null; + $this->_cache = array(); + $this->_metadataCache = array(); + } + + /** + * Hydrates a single row from the current statement instance. + * + * Template method. + * + * @param array $data The row data. + * @param array $result The result to fill. + * + * @return void + * + * @throws HydrationException + */ + protected function hydrateRowData(array $data, array &$result) + { + throw new HydrationException("hydrateRowData() not implemented by this hydrator."); + } + + /** + * Hydrates all rows from the current statement instance at once. + * + * @return array + */ + abstract protected function hydrateAllData(); + + /** + * Processes a row of the result set. + * + * Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY). + * Puts the elements of a result row into a new array, grouped by the dql alias + * they belong to. The column names in the result set are mapped to their + * field names during this procedure as well as any necessary conversions on + * the values applied. Scalar values are kept in a specific key 'scalars'. + * + * @param array $data SQL Result Row. + * @param array &$id Dql-Alias => ID-Hash. + * @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value? + * + * @return array An array with all the fields (name => value) of the data row, + * grouped by their component alias. + */ + protected function gatherRowData(array $data, array &$id, array &$nonemptyComponents) + { + $rowData = array('data' => array()); + + foreach ($data as $key => $value) { + if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) { + continue; + } + + $fieldName = $cacheKeyInfo['fieldName']; + + switch (true) { + case (isset($cacheKeyInfo['isNewObjectParameter'])): + $argIndex = $cacheKeyInfo['argIndex']; + $objIndex = $cacheKeyInfo['objIndex']; + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); + + $rowData['newObjects'][$objIndex]['class'] = $cacheKeyInfo['class']; + $rowData['newObjects'][$objIndex]['args'][$argIndex] = $value; + break; + + case (isset($cacheKeyInfo['isScalar'])): + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); + + $rowData['scalars'][$fieldName] = $value; + break; + + //case (isset($cacheKeyInfo['isMetaColumn'])): + default: + $dqlAlias = $cacheKeyInfo['dqlAlias']; + $type = $cacheKeyInfo['type']; + + // in an inheritance hierarchy the same field could be defined several times. + // We overwrite this value so long we don't have a non-null value, that value we keep. + // Per definition it cannot be that a field is defined several times and has several values. + if (isset($rowData['data'][$dqlAlias][$fieldName])) { + break; + } + + $rowData['data'][$dqlAlias][$fieldName] = $type + ? $type->convertToPHPValue($value, $this->_platform) + : $value; + + if ($cacheKeyInfo['isIdentifier'] && $value !== null) { + $id[$dqlAlias] .= '|' . $value; + $nonemptyComponents[$dqlAlias] = true; + } + break; + } + } + + return $rowData; + } + + /** + * Processes a row of the result set. + * + * Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that + * simply converts column names to field names and properly converts the + * values according to their types. The resulting row has the same number + * of elements as before. + * + * @param array $data + * + * @return array The processed row. + */ + protected function gatherScalarRowData(&$data) + { + $rowData = array(); + + foreach ($data as $key => $value) { + if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) { + continue; + } + + $fieldName = $cacheKeyInfo['fieldName']; + + // WARNING: BC break! We know this is the desired behavior to type convert values, but this + // erroneous behavior exists since 2.0 and we're forced to keep compatibility. + if ( ! isset($cacheKeyInfo['isScalar'])) { + $dqlAlias = $cacheKeyInfo['dqlAlias']; + $type = $cacheKeyInfo['type']; + $fieldName = $dqlAlias . '_' . $fieldName; + $value = $type + ? $type->convertToPHPValue($value, $this->_platform) + : $value; + } + + $rowData[$fieldName] = $value; + } + + return $rowData; + } + + /** + * Retrieve column information from ResultSetMapping. + * + * @param string $key Column name + * + * @return array|null + */ + protected function hydrateColumnInfo($key) + { + if (isset($this->_cache[$key])) { + return $this->_cache[$key]; + } + + switch (true) { + // NOTE: Most of the times it's a field mapping, so keep it first!!! + case (isset($this->_rsm->fieldMappings[$key])): + $classMetadata = $this->getClassMetadata($this->_rsm->declaringClasses[$key]); + $fieldName = $this->_rsm->fieldMappings[$key]; + $fieldMapping = $classMetadata->fieldMappings[$fieldName]; + + return $this->_cache[$key] = array( + 'isIdentifier' => in_array($fieldName, $classMetadata->identifier), + 'fieldName' => $fieldName, + 'type' => Type::getType($fieldMapping['type']), + 'dqlAlias' => $this->_rsm->columnOwnerMap[$key], + ); + + case (isset($this->_rsm->newObjectMappings[$key])): + // WARNING: A NEW object is also a scalar, so it must be declared before! + $mapping = $this->_rsm->newObjectMappings[$key]; + + return $this->_cache[$key] = array( + 'isScalar' => true, + 'isNewObjectParameter' => true, + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + 'argIndex' => $mapping['argIndex'], + 'objIndex' => $mapping['objIndex'], + 'class' => new \ReflectionClass($mapping['className']), + ); + + case (isset($this->_rsm->scalarMappings[$key])): + return $this->_cache[$key] = array( + 'isScalar' => true, + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + ); + + case (isset($this->_rsm->metaMappings[$key])): + // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). + $fieldName = $this->_rsm->metaMappings[$key]; + $dqlAlias = $this->_rsm->columnOwnerMap[$key]; + $classMetadata = $this->getClassMetadata($this->_rsm->aliasMap[$dqlAlias]); + $type = isset($this->_rsm->typeMappings[$key]) + ? Type::getType($this->_rsm->typeMappings[$key]) + : null; + + return $this->_cache[$key] = array( + 'isIdentifier' => isset($this->_rsm->isIdentifierColumn[$dqlAlias][$key]), + 'isMetaColumn' => true, + 'fieldName' => $fieldName, + 'type' => $type, + 'dqlAlias' => $dqlAlias, + ); + } + + // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2 + // maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping. + return null; + } + + /** + * Retrieve ClassMetadata associated to entity class name. + * + * @param string $className + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + protected function getClassMetadata($className) + { + if ( ! isset($this->_metadataCache[$className])) { + $this->_metadataCache[$className] = $this->_em->getClassMetadata($className); + } + + return $this->_metadataCache[$className]; + } + + /** + * Register entity as managed in UnitOfWork. + * + * @param ClassMetadata $class + * @param object $entity + * @param array $data + * + * @return void + * + * @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow + */ + protected function registerManaged(ClassMetadata $class, $entity, array $data) + { + if ($class->isIdentifierComposite) { + $id = array(); + + foreach ($class->identifier as $fieldName) { + $id[$fieldName] = isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]; + } + } else { + $fieldName = $class->identifier[0]; + $id = array( + $fieldName => isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName] + ); + } + + $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php new file mode 100644 index 0000000..f3e4376 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -0,0 +1,307 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * The ArrayHydrator produces a nested array "graph" that is often (not always) + * interchangeable with the corresponding object graph for read-only access. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class ArrayHydrator extends AbstractHydrator +{ + /** + * @var array + */ + private $_rootAliases = array(); + + /** + * @var bool + */ + private $_isSimpleQuery = false; + + /** + * @var array + */ + private $_identifierMap = array(); + + /** + * @var array + */ + private $_resultPointers = array(); + + /** + * @var array + */ + private $_idTemplate = array(); + + /** + * @var int + */ + private $_resultCounter = 0; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1; + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->_identifierMap[$dqlAlias] = array(); + $this->_resultPointers[$dqlAlias] = array(); + $this->_idTemplate[$dqlAlias] = ''; + } + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $row, array &$result) + { + // 1) Initialize + $id = $this->_idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + $rowData = $this->gatherRowData($row, $id, $nonemptyComponents); + + // 2) Now hydrate the data found in the current row. + foreach ($rowData['data'] as $dqlAlias => $data) { + $index = false; + + if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { + // It's a joined result + + $parent = $this->_rsm->parentAliasMap[$dqlAlias]; + $path = $parent . '.' . $dqlAlias; + + // missing parent data, skipping as RIGHT JOIN hydration is not supported. + if ( ! isset($nonemptyComponents[$parent]) ) { + continue; + } + + // Get a reference to the right element in the result tree. + // This element will get the associated element attached. + if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) { + $first = reset($this->_resultPointers); + // TODO: Exception if $key === null ? + $baseElement =& $this->_resultPointers[$parent][key($first)]; + } else if (isset($this->_resultPointers[$parent])) { + $baseElement =& $this->_resultPointers[$parent]; + } else { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + + continue; + } + + $relationAlias = $this->_rsm->relationMap[$dqlAlias]; + $parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parent]]; + $relation = $parentClass->associationMappings[$relationAlias]; + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + $oneToOne = false; + + if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = array(); + } + + if (isset($nonemptyComponents[$dqlAlias])) { + $indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]); + $index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false; + + if ( ! $indexExists || ! $indexIsValid) { + $element = $data; + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element; + } else { + $baseElement[$relationAlias][] = $element; + } + + end($baseElement[$relationAlias]); + + $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]); + } + } + } else { + $oneToOne = true; + + if ( + ( ! isset($nonemptyComponents[$dqlAlias])) && + ( ! isset($baseElement[$relationAlias])) + ) { + $baseElement[$relationAlias] = null; + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = $data; + } + } + + $coll =& $baseElement[$relationAlias]; + + if (is_array($coll)) { + $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); + } + } else { + // It's a root result element + + $this->_rootAliases[$dqlAlias] = true; // Mark as root + $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + $result[] = $this->_rsm->isMixed + ? array($entityKey => null) + : null; + + $resultKey = $this->_resultCounter; + ++$this->_resultCounter; + + continue; + } + + // Check for an existing element + if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $this->_rsm->isMixed + ? array($entityKey => $data) + : $data; + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; + $result[$resultKey] = $element; + } else { + $resultKey = $this->_resultCounter; + $result[] = $element; + + ++$this->_resultCounter; + } + + $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + } else { + $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; + $resultKey = $index; + } + + $this->updateResultPointer($result, $index, $dqlAlias, false); + } + } + + if ( ! isset($resultKey)) { + $this->_resultCounter++; + } + + // Append scalar values to mixed result sets + if (isset($rowData['scalars'])) { + if ( ! isset($resultKey)) { + // this only ever happens when no object is fetched (scalar result only) + $resultKey = isset($this->_rsm->indexByMap['scalars']) + ? $row[$this->_rsm->indexByMap['scalars']] + : $this->_resultCounter - 1; + } + + foreach ($rowData['scalars'] as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + + // Append new object to mixed result sets + if (isset($rowData['newObjects'])) { + if ( ! isset($resultKey)) { + $resultKey = $this->_resultCounter - 1; + } + + $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0); + + foreach ($rowData['newObjects'] as $objIndex => $newObject) { + $class = $newObject['class']; + $args = $newObject['args']; + $obj = $class->newInstanceArgs($args); + + if (count($args) == $scalarCount || ($scalarCount == 0 && count($rowData['newObjects']) == 1)) { + $result[$resultKey] = $obj; + + continue; + } + + $result[$resultKey][$objIndex] = $obj; + } + } + } + + /** + * Updates the result pointer for an Entity. The result pointers point to the + * last seen instance of each Entity type. This is used for graph construction. + * + * @param array $coll The element. + * @param boolean|integer $index Index of the element in the collection. + * @param string $dqlAlias + * @param boolean $oneToOne Whether it is a single-valued association or not. + * + * @return void + */ + private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne) + { + if ($coll === null) { + unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 + + return; + } + + if ($oneToOne) { + $this->_resultPointers[$dqlAlias] =& $coll; + + return; + } + + if ($index !== false) { + $this->_resultPointers[$dqlAlias] =& $coll[$index]; + + return; + } + + if ( ! $coll) { + return; + } + + end($coll); + $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; + + return; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php new file mode 100644 index 0000000..b35012c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/HydrationException.php @@ -0,0 +1,105 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +class HydrationException extends \Doctrine\ORM\ORMException +{ + /** + * @return HydrationException + */ + public static function nonUniqueResult() + { + return new self("The result returned by the query was not unique."); + } + + /** + * @param string $alias + * @param string $parentAlias + * + * @return HydrationException + */ + public static function parentObjectOfRelationNotFound($alias, $parentAlias) + { + return new self("The parent object of entity result with alias '$alias' was not found." + . " The parent alias is '$parentAlias'."); + } + + /** + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function emptyDiscriminatorValue($dqlAlias) + { + return new self("The DQL alias '" . $dqlAlias . "' contains an entity ". + "of an inheritance hierarchy with an empty discriminator value. This means " . + "that the database contains inconsistent data with an empty " . + "discriminator value in a table row." + ); + } + + /** + * @since 2.3 + * + * @param string $entityName + * @param string $discrColumnName + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function missingDiscriminatorColumn($entityName, $discrColumnName, $dqlAlias) + { + return new self(sprintf( + 'The discriminator column "%s" is missing for "%s" using the DQL alias "%s".', + $discrColumnName, $entityName, $dqlAlias + )); + } + + /** + * @since 2.3 + * + * @param string $entityName + * @param string $discrColumnName + * @param string $dqlAlias + * + * @return HydrationException + */ + public static function missingDiscriminatorMetaMappingColumn($entityName, $discrColumnName, $dqlAlias) + { + return new self(sprintf( + 'The meta mapping for the discriminator column "%s" is missing for "%s" using the DQL alias "%s".', + $discrColumnName, $entityName, $dqlAlias + )); + } + + /** + * @param string $discrValue + * @param array $discrMap + * + * @return HydrationException + */ + public static function invalidDiscriminatorValue($discrValue, $discrMap) + { + return new self(sprintf( + 'The discriminator value "%s" is invalid. It must be one of "%s".', + $discrValue, implode('", "', $discrMap) + )); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php new file mode 100644 index 0000000..3bbf672 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/IterableResult.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +/** + * Represents a result structure that can be iterated over, hydrating row-by-row + * during the iteration. An IterableResult is obtained by AbstractHydrator#iterate(). + * + * @author robo + * @since 2.0 + */ +class IterableResult implements \Iterator +{ + /** + * @var \Doctrine\ORM\Internal\Hydration\AbstractHydrator + */ + private $_hydrator; + + /** + * @var boolean + */ + private $_rewinded = false; + + /** + * @var integer + */ + private $_key = -1; + + /** + * @var object|null + */ + private $_current = null; + + /** + * @param \Doctrine\ORM\Internal\Hydration\AbstractHydrator $hydrator + */ + public function __construct($hydrator) + { + $this->_hydrator = $hydrator; + } + + /** + * @return void + * + * @throws HydrationException + */ + public function rewind() + { + if ($this->_rewinded == true) { + throw new HydrationException("Can only iterate a Result once."); + } else { + $this->_current = $this->next(); + $this->_rewinded = true; + } + } + + /** + * Gets the next set of results. + * + * @return array + */ + public function next() + { + $this->_current = $this->_hydrator->hydrateRow(); + $this->_key++; + return $this->_current; + } + + /** + * @return mixed + */ + public function current() + { + return $this->_current; + } + + /** + * @return int + */ + public function key() + { + return $this->_key; + } + + /** + * @return bool + */ + public function valid() + { + return ($this->_current!=false); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php new file mode 100644 index 0000000..3cedaad --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -0,0 +1,595 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\ORM\UnitOfWork; +use PDO; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Query; +use Doctrine\ORM\Events; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PostLoadEventDispatcher; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Proxy\Proxy; + +/** + * The ObjectHydrator constructs an object graph out of an SQL result set. + * + * Internal note: Highly performance-sensitive code. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + * @author Fabio B. Silva + */ +class ObjectHydrator extends AbstractHydrator +{ + /** + * @var array + */ + private $identifierMap = array(); + + /** + * @var array + */ + private $resultPointers = array(); + + /** + * @var array + */ + private $idTemplate = array(); + + /** + * @var integer + */ + private $resultCounter = 0; + + /** + * @var array + */ + private $rootAliases = array(); + + /** + * @var array + */ + private $initializedCollections = array(); + + /** + * @var array + */ + private $existingCollections = array(); + + /** + * {@inheritdoc} + */ + protected function prepare() + { + if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { + $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; + } + + foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { + $this->identifierMap[$dqlAlias] = array(); + $this->idTemplate[$dqlAlias] = ''; + + // Remember which associations are "fetch joined", so that we know where to inject + // collection stubs or proxies and where not. + if ( ! isset($this->_rsm->relationMap[$dqlAlias])) { + continue; + } + + $parent = $this->_rsm->parentAliasMap[$dqlAlias]; + + if ( ! isset($this->_rsm->aliasMap[$parent])) { + throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent); + } + + $sourceClassName = $this->_rsm->aliasMap[$parent]; + $sourceClass = $this->getClassMetadata($sourceClassName); + $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; + + $this->_hints['fetched'][$parent][$assoc['fieldName']] = true; + + if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) { + continue; + } + + // Mark any non-collection opposite sides as fetched, too. + if ($assoc['mappedBy']) { + $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true; + + continue; + } + + // handle fetch-joined owning side bi-directional one-to-one associations + if ($assoc['inversedBy']) { + $class = $this->getClassMetadata($className); + $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; + + if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true; + } + } + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true; + + parent::cleanup(); + + $this->identifierMap = + $this->initializedCollections = + $this->existingCollections = + $this->resultPointers = array(); + + if ($eagerLoad) { + $this->_uow->triggerEagerLoads(); + } + + $this->_uow->hydrationComplete(); + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $result); + } + + // Take snapshots from all newly initialized collections + foreach ($this->initializedCollections as $coll) { + $coll->takeSnapshot(); + } + + return $result; + } + + /** + * Initializes a related collection. + * + * @param object $entity The entity to which the collection belongs. + * @param ClassMetadata $class + * @param string $fieldName The name of the field on the entity that holds the collection. + * @param string $parentDqlAlias Alias of the parent fetch joining this collection. + * + * @return \Doctrine\ORM\PersistentCollection + */ + private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias) + { + $oid = spl_object_hash($entity); + $relation = $class->associationMappings[$fieldName]; + $value = $class->reflFields[$fieldName]->getValue($entity); + + if ($value === null || is_array($value)) { + $value = new ArrayCollection((array) $value); + } + + if ( ! $value instanceof PersistentCollection) { + $value = new PersistentCollection( + $this->_em, $this->_metadataCache[$relation['targetEntity']], $value + ); + $value->setOwner($entity, $relation); + + $class->reflFields[$fieldName]->setValue($entity, $value); + $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); + + $this->initializedCollections[$oid . $fieldName] = $value; + } else if ( + isset($this->_hints[Query::HINT_REFRESH]) || + isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && + ! $value->isInitialized() + ) { + // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED! + $value->setDirty(false); + $value->setInitialized(true); + $value->unwrap()->clear(); + + $this->initializedCollections[$oid . $fieldName] = $value; + } else { + // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN! + $this->existingCollections[$oid . $fieldName] = $value; + } + + return $value; + } + + /** + * Gets an entity instance. + * + * @param array $data The instance data. + * @param string $dqlAlias The DQL alias of the entity's class. + * + * @return object The entity. + * + * @throws HydrationException + */ + private function getEntity(array $data, $dqlAlias) + { + $className = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) { + $fieldName = $this->_rsm->discriminatorColumns[$dqlAlias]; + + if ( ! isset($this->_rsm->metaMappings[$fieldName])) { + throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias); + } + + $discrColumn = $this->_rsm->metaMappings[$fieldName]; + + if ( ! isset($data[$discrColumn])) { + throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias); + } + + if ($data[$discrColumn] === "") { + throw HydrationException::emptyDiscriminatorValue($dqlAlias); + } + + $discrMap = $this->_metadataCache[$className]->discriminatorMap; + + if ( ! isset($discrMap[$data[$discrColumn]])) { + throw HydrationException::invalidDiscriminatorValue($data[$discrColumn], array_keys($discrMap)); + } + + $className = $discrMap[$data[$discrColumn]]; + + unset($data[$discrColumn]); + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) { + $this->registerManaged($this->_metadataCache[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $this->_hints['fetchAlias'] = $dqlAlias; + + return $this->_uow->createEntity($className, $data, $this->_hints); + } + + /** + * @param string $className + * @param array $data + * + * @return mixed + */ + private function getEntityFromIdentityMap($className, array $data) + { + // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? + $class = $this->_metadataCache[$className]; + + /* @var $class ClassMetadata */ + if ($class->isIdentifierComposite) { + $idHash = ''; + + foreach ($class->identifier as $fieldName) { + $idHash .= ' ' . (isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]); + } + + return $this->_uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName); + } else if (isset($class->associationMappings[$class->identifier[0]])) { + return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName); + } + + return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); + } + + /** + * Hydrates a single row in an SQL result set. + * + * @internal + * First, the data of the row is split into chunks where each chunk contains data + * that belongs to a particular component/class. Afterwards, all these chunks + * are processed, one after the other. For each chunk of class data only one of the + * following code paths is executed: + * + * Path A: The data chunk belongs to a joined/associated object and the association + * is collection-valued. + * Path B: The data chunk belongs to a joined/associated object and the association + * is single-valued. + * Path C: The data chunk belongs to a root result element/object that appears in the topmost + * level of the hydrated result. A typical example are the objects of the type + * specified by the FROM clause in a DQL query. + * + * @param array $row The data of the row to process. + * @param array $result The result array to fill. + * + * @return void + */ + protected function hydrateRowData(array $row, array &$result) + { + // Initialize + $id = $this->idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + // Split the row data into chunks of class data. + $rowData = $this->gatherRowData($row, $id, $nonemptyComponents); + + // Hydrate the data chunks + foreach ($rowData['data'] as $dqlAlias => $data) { + $entityName = $this->_rsm->aliasMap[$dqlAlias]; + + if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { + // It's a joined result + + $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias]; + // we need the $path to save into the identifier map which entities were already + // seen for this parent-child relationship + $path = $parentAlias . '.' . $dqlAlias; + + // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs + if ( ! isset($nonemptyComponents[$parentAlias])) { + // TODO: Add special case code where we hydrate the right join objects into identity map at least + continue; + } + + $parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parentAlias]]; + $relationField = $this->_rsm->relationMap[$dqlAlias]; + $relation = $parentClass->associationMappings[$relationField]; + $reflField = $parentClass->reflFields[$relationField]; + + // Get a reference to the parent object to which the joined element belongs. + if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) { + $first = reset($this->resultPointers); + $parentObject = $first[key($first)]; + } else if (isset($this->resultPointers[$parentAlias])) { + $parentObject = $this->resultPointers[$parentAlias]; + } else { + // Parent object of relation not found, mark as not-fetched again + $element = $this->getEntity($data, $dqlAlias); + + // Update result pointer and provide initial fetch data for parent + $this->resultPointers[$dqlAlias] = $element; + $rowData['data'][$parentAlias][$relationField] = $element; + + // Mark as not-fetched again + unset($this->_hints['fetched'][$parentAlias][$relationField]); + continue; + } + + $oid = spl_object_hash($parentObject); + + // Check the type of the relation (many or single-valued) + if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { + // PATH A: Collection-valued association + $reflFieldValue = $reflField->getValue($parentObject); + + if (isset($nonemptyComponents[$dqlAlias])) { + $collKey = $oid . $relationField; + if (isset($this->initializedCollections[$collKey])) { + $reflFieldValue = $this->initializedCollections[$collKey]; + } else if ( ! isset($this->existingCollections[$collKey])) { + $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + } + + $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); + $index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; + $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; + + if ( ! $indexExists || ! $indexIsValid) { + if (isset($this->existingCollections[$collKey])) { + // Collection exists, only look for the element in the identity map. + if ($element = $this->getEntityFromIdentityMap($entityName, $data)) { + $this->resultPointers[$dqlAlias] = $element; + } else { + unset($this->resultPointers[$dqlAlias]); + } + } else { + $element = $this->getEntity($data, $dqlAlias); + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; + $reflFieldValue->hydrateSet($indexValue, $element); + $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; + } else { + $reflFieldValue->hydrateAdd($element); + $reflFieldValue->last(); + $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); + } + // Update result pointer + $this->resultPointers[$dqlAlias] = $element; + } + } else { + // Update result pointer + $this->resultPointers[$dqlAlias] = $reflFieldValue[$index]; + } + } else if ( ! $reflFieldValue) { + $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); + } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) { + $reflFieldValue->setInitialized(true); + } + + } else { + // PATH B: Single-valued association + $reflFieldValue = $reflField->getValue($parentObject); + + if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) { + // we only need to take action if this value is null, + // we refresh the entity or its an unitialized proxy. + if (isset($nonemptyComponents[$dqlAlias])) { + $element = $this->getEntity($data, $dqlAlias); + $reflField->setValue($parentObject, $element); + $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); + $targetClass = $this->_metadataCache[$relation['targetEntity']]; + + if ($relation['isOwningSide']) { + // TODO: Just check hints['fetched'] here? + // If there is an inverse mapping on the target class its bidirectional + if ($relation['inversedBy']) { + $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; + if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject); + } + } else if ($parentClass === $targetClass && $relation['mappedBy']) { + // Special case: bi-directional self-referencing one-one on the same class + $targetClass->reflFields[$relationField]->setValue($element, $parentObject); + } + } else { + // For sure bidirectional, as there is no inverse side in unidirectional mappings + $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject); + $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject); + } + // Update result pointer + $this->resultPointers[$dqlAlias] = $element; + } else { + $this->_uow->setOriginalEntityProperty($oid, $relationField, null); + $reflField->setValue($parentObject, null); + } + // else leave $reflFieldValue null for single-valued associations + } else { + // Update result pointer + $this->resultPointers[$dqlAlias] = $reflFieldValue; + } + } + } else { + // PATH C: Its a root result element + $this->rootAliases[$dqlAlias] = true; // Mark as root alias + $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array($entityKey => null); + } else { + $result[] = null; + } + $resultKey = $this->resultCounter; + ++$this->resultCounter; + continue; + } + + // check for existing result from the iterations before + if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) { + $element = $this->getEntity($data, $dqlAlias); + + if ($this->_rsm->isMixed) { + $element = array($entityKey => $element); + } + + if (isset($this->_rsm->indexByMap[$dqlAlias])) { + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; + + if (isset($this->_hints['collection'])) { + $this->_hints['collection']->hydrateSet($resultKey, $element); + } + + $result[$resultKey] = $element; + } else { + $resultKey = $this->resultCounter; + ++$this->resultCounter; + + if (isset($this->_hints['collection'])) { + $this->_hints['collection']->hydrateAdd($element); + } + + $result[] = $element; + } + + $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; + + // Update result pointer + $this->resultPointers[$dqlAlias] = $element; + + } else { + // Update result pointer + $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]]; + $this->resultPointers[$dqlAlias] = $result[$index]; + $resultKey = $index; + } + } + + if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { + $this->_uow->hydrationComplete(); + } + } + + if ( ! isset($resultKey) ) { + $this->resultCounter++; + } + + // Append scalar values to mixed result sets + if (isset($rowData['scalars'])) { + if ( ! isset($resultKey) ) { + $resultKey = (isset($this->_rsm->indexByMap['scalars'])) + ? $row[$this->_rsm->indexByMap['scalars']] + : $this->resultCounter - 1; + } + + foreach ($rowData['scalars'] as $name => $value) { + $result[$resultKey][$name] = $value; + } + } + + // Append new object to mixed result sets + if (isset($rowData['newObjects'])) { + if ( ! isset($resultKey) ) { + $resultKey = $this->resultCounter - 1; + } + + + $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0); + + foreach ($rowData['newObjects'] as $objIndex => $newObject) { + $class = $newObject['class']; + $args = $newObject['args']; + $obj = $class->newInstanceArgs($args); + + if ($scalarCount == 0 && count($rowData['newObjects']) == 1 ) { + $result[$resultKey] = $obj; + + continue; + } + + $result[$resultKey][$objIndex] = $obj; + } + } + } + + /** + * When executed in a hydrate() loop we may have to clear internal state to + * decrease memory consumption. + * + * @param mixed $eventArgs + * + * @return void + */ + public function onClear($eventArgs) + { + parent::onClear($eventArgs); + + $aliases = array_keys($this->identifierMap); + $this->identifierMap = array(); + + foreach ($aliases as $alias) { + $this->identifierMap[$alias] = array(); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php new file mode 100644 index 0000000..024ee3b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +/** + * Hydrator that produces flat, rectangular results of scalar data. + * The created result is almost the same as a regular SQL result set, except + * that column names are mapped to field names and data type conversions take place. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class ScalarHydrator extends AbstractHydrator +{ + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { + $this->hydrateRowData($data, $result); + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $data, array &$result) + { + $result[] = $this->gatherScalarRowData($data); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php new file mode 100644 index 0000000..1c21369 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -0,0 +1,152 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use PDO; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; + +class SimpleObjectHydrator extends AbstractHydrator +{ + /** + * @var ClassMetadata + */ + private $class; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + if (count($this->_rsm->aliasMap) !== 1) { + throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result."); + } + + if ($this->_rsm->scalarMappings) { + throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings."); + } + + $this->class = $this->getClassMetadata(reset($this->_rsm->aliasMap)); + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + parent::cleanup(); + + $this->_uow->triggerEagerLoads(); + $this->_uow->hydrationComplete(); + } + + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $result = array(); + + while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { + $this->hydrateRowData($row, $result); + } + + $this->_em->getUnitOfWork()->triggerEagerLoads(); + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function hydrateRowData(array $sqlResult, array &$result) + { + $entityName = $this->class->name; + $data = array(); + + // We need to find the correct entity class name if we have inheritance in resultset + if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) { + $discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']); + + // Find mapped discriminator column from the result set. + if ($metaMappingDiscrColumnName = array_search($discrColumnName, $this->_rsm->metaMappings)) { + $discrColumnName = $metaMappingDiscrColumnName; + } + + if ( ! isset($sqlResult[$discrColumnName])) { + throw HydrationException::missingDiscriminatorColumn($entityName, $discrColumnName, key($this->_rsm->aliasMap)); + } + + if ($sqlResult[$discrColumnName] === '') { + throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap)); + } + + $discrMap = $this->class->discriminatorMap; + + if ( ! isset($discrMap[$sqlResult[$discrColumnName]])) { + throw HydrationException::invalidDiscriminatorValue($sqlResult[$discrColumnName], array_keys($discrMap)); + } + + $entityName = $discrMap[$sqlResult[$discrColumnName]]; + + unset($sqlResult[$discrColumnName]); + } + + foreach ($sqlResult as $column => $value) { + // An ObjectHydrator should be used instead of SimpleObjectHydrator + if (isset($this->_rsm->relationMap[$column])) { + throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column)); + } + + $cacheKeyInfo = $this->hydrateColumnInfo($column); + + if ( ! $cacheKeyInfo) { + continue; + } + + // Convert field to a valid PHP value + if (isset($cacheKeyInfo['type'])) { + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); + } + + $fieldName = $cacheKeyInfo['fieldName']; + + // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator) + if ( ! isset($data[$fieldName]) || $value !== null) { + $data[$fieldName] = $value; + } + } + + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) { + $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + + $uow = $this->_em->getUnitOfWork(); + $entity = $uow->createEntity($entityName, $data, $this->_hints); + + $result[] = $entity; + + if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { + $this->_uow->hydrationComplete(); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php new file mode 100644 index 0000000..b9caeb1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Internal\Hydration; + +use Doctrine\ORM\NoResultException; +use Doctrine\ORM\NonUniqueResultException; + +/** + * Hydrator that hydrates a single scalar value from the result set. + * + * @since 2.0 + * @author Roman Borschel + * @author Guilherme Blanco + */ +class SingleScalarHydrator extends AbstractHydrator +{ + /** + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC); + $numRows = count($data); + + if ($numRows === 0) { + throw new NoResultException(); + } + + if ($numRows > 1) { + throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().'); + } + + if (count($data[key($data)]) > 1) { + throw new NonUniqueResultException('The query returned a row containing multiple columns. Change the query or use a different result function like getScalarResult().'); + } + + $result = $this->gatherScalarRowData($data[key($data)]); + + return array_shift($result); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php new file mode 100644 index 0000000..4da71ce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Internal; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\ListenersInvoker; +use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Class, which can handle completion of hydration cycle and produce some of tasks. + * In current implementation triggers deferred postLoad event. + * + * @author Artur Eshenbrener + * @since 2.5 + */ +final class HydrationCompleteHandler +{ + /** + * @var ListenersInvoker + */ + private $listenersInvoker; + + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * @var array[] + */ + private $deferredPostLoadInvocations = array(); + + /** + * Constructor for this object + * + * @param ListenersInvoker $listenersInvoker + * @param EntityManagerInterface $em + */ + public function __construct(ListenersInvoker $listenersInvoker, EntityManagerInterface $em) + { + $this->listenersInvoker = $listenersInvoker; + $this->em = $em; + } + + /** + * Method schedules invoking of postLoad entity to the very end of current hydration cycle. + * + * @param ClassMetadata $class + * @param object $entity + */ + public function deferPostLoadInvoking(ClassMetadata $class, $entity) + { + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postLoad); + + if ($invoke === ListenersInvoker::INVOKE_NONE) { + return; + } + + $this->deferredPostLoadInvocations[] = array($class, $invoke, $entity); + } + + /** + * This method should me called after any hydration cycle completed. + * + * Method fires all deferred invocations of postLoad events + */ + public function hydrationComplete() + { + $toInvoke = $this->deferredPostLoadInvocations; + $this->deferredPostLoadInvocations = array(); + + foreach ($toInvoke as $classAndEntity) { + list($class, $invoke, $entity) = $classAndEntity; + + $this->listenersInvoker->invoke( + $class, + Events::postLoad, + $entity, + new LifecycleEventArgs($entity, $this->em), + $invoke + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/LazyCriteriaCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/LazyCriteriaCollection.php new file mode 100644 index 0000000..28e2709 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/LazyCriteriaCollection.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\AbstractLazyCollection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Selectable; +use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; +use Doctrine\ORM\Persisters\Entity\EntityPersister; + +/** + * A lazy collection that allow a fast count when using criteria object + * Once count gets executed once without collection being initialized, result + * is cached and returned on subsequent calls until collection gets loaded, + * then returning the number of loaded results. + * + * @since 2.5 + * @author Guilherme Blanco + * @author Michaël Gallego + */ +class LazyCriteriaCollection extends AbstractLazyCollection implements Selectable +{ + /** + * @var BasicEntityPersister + */ + protected $entityPersister; + + /** + * @var Criteria + */ + protected $criteria; + + /** + * @var integer|null + */ + private $count; + + /** + * @param EntityPersister $entityPersister + * @param Criteria $criteria + */ + public function __construct(EntityPersister $entityPersister, Criteria $criteria) + { + $this->entityPersister = $entityPersister; + $this->criteria = $criteria; + } + + /** + * Do an efficient count on the collection + * + * @return integer + */ + public function count() + { + if ($this->isInitialized()) { + return $this->collection->count(); + } + + // Return cached result in case count query was already executed + if ($this->count !== null) { + return $this->count; + } + + return $this->count = $this->entityPersister->count($this->criteria); + } + + /** + * Do an optimized search of an element + * + * @param object $element + * @return bool + */ + public function contains($element) + { + if ($this->isInitialized()) { + return $this->collection->contains($element); + } + + return $this->entityPersister->exists($element, $this->criteria); + } + + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $this->initialize(); + + return $this->collection->matching($criteria); + } + + /** + * {@inheritDoc} + */ + protected function doInitialize() + { + $elements = $this->entityPersister->loadCriteria($this->criteria); + $this->collection = new ArrayCollection($elements); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php new file mode 100644 index 0000000..19374ff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Annotation.php @@ -0,0 +1,24 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +interface Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php new file mode 100644 index 0000000..d18c8be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AnsiQuoteStrategy.php @@ -0,0 +1,96 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * ANSI compliant quote strategy, this strategy does not apply any quote. + * To use this strategy all mapped tables and columns should be ANSI compliant. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class AnsiQuoteStrategy implements QuoteStrategy +{ + /** + * {@inheritdoc} + */ + public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + { + return $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * {@inheritdoc} + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + return $class->table['name']; + } + + /** + * {@inheritdoc} + */ + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + { + return $definition['sequenceName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return $joinColumn['name']; + } + + /** + * {@inheritdoc} + */ + public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return $joinColumn['referencedColumnName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) + { + return $association['joinTable']['name']; + } + + /** + * {@inheritdoc} + */ + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + { + return $class->identifier; + } + + /** + * {@inheritdoc} + */ + public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) + { + return $platform->getSQLResultCasing($columnName . '_' . $counter); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php new file mode 100644 index 0000000..1a9a31f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverride.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mapping of property for an entity relationship. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AssociationOverride implements Annotation +{ + /** + * The name of the relationship property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The join column that is being mapped to the persistent attribute. + * + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $joinColumns; + + /** + * The join table that maps the relationship. + * + * @var \Doctrine\ORM\Mapping\JoinTable + */ + public $joinTable; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php new file mode 100644 index 0000000..217c9e4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AssociationOverrides.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override association mappings of relationship properties. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AssociationOverrides implements Annotation +{ + /** + * Mapping overrides of relationship properties. + * + * @var array<\Doctrine\ORM\Mapping\AssociationOverride> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php new file mode 100644 index 0000000..f86d3a1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverride.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class AttributeOverride implements Annotation +{ + /** + * The name of the property whose mapping is being overridden. + * + * @var string + */ + public $name; + + /** + * The column definition. + * + * @var \Doctrine\ORM\Mapping\Column + */ + public $column; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php new file mode 100644 index 0000000..63b2cc6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/AttributeOverrides.php @@ -0,0 +1,39 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * This annotation is used to override the mapping of a entity property. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class AttributeOverrides implements Annotation +{ + /** + * One or more field or property mapping overrides. + * + * @var array<\Doctrine\ORM\Mapping\AttributeOverride> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php new file mode 100644 index 0000000..5bc75e3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; + +class AssociationBuilder +{ + /** + * @var ClassMetadataBuilder + */ + protected $builder; + + /** + * @var array + */ + protected $mapping; + + /** + * @var array|null + */ + protected $joinColumns; + + /** + * @var int + */ + protected $type; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + * @param int $type + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping, $type) + { + $this->builder = $builder; + $this->mapping = $mapping; + $this->type = $type; + } + + /** + * @param string $fieldName + * + * @return AssociationBuilder + */ + public function mappedBy($fieldName) + { + $this->mapping['mappedBy'] = $fieldName; + return $this; + } + + /** + * @param string $fieldName + * + * @return AssociationBuilder + */ + public function inversedBy($fieldName) + { + $this->mapping['inversedBy'] = $fieldName; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeAll() + { + $this->mapping['cascade'] = array("ALL"); + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadePersist() + { + $this->mapping['cascade'][] = "persist"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeRemove() + { + $this->mapping['cascade'][] = "remove"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeMerge() + { + $this->mapping['cascade'][] = "merge"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeDetach() + { + $this->mapping['cascade'][] = "detach"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function cascadeRefresh() + { + $this->mapping['cascade'][] = "refresh"; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchExtraLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchEager() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_EAGER; + return $this; + } + + /** + * @return AssociationBuilder + */ + public function fetchLazy() + { + $this->mapping['fetch'] = ClassMetadata::FETCH_LAZY; + return $this; + } + + /** + * Add Join Columns. + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string|null $onDelete + * @param string|null $columnDef + * + * @return AssociationBuilder + */ + public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) + { + $this->joinColumns[] = array( + 'name' => $columnName, + 'referencedColumnName' => $referencedColumnName, + 'nullable' => $nullable, + 'unique' => $unique, + 'onDelete' => $onDelete, + 'columnDefinition' => $columnDef, + ); + return $this; + } + + /** + * Sets field as primary key. + * + * @return self + */ + public function makePrimaryKey() + { + $this->mapping['id'] = true; + + return $this; + } + + /** + * Removes orphan entities when detached from their parent. + * + * @return self + */ + public function orphanRemoval() + { + $this->mapping['orphanRemoval'] = true; + + return $this; + } + + /** + * @return ClassMetadataBuilder + * + * @throws \InvalidArgumentException + */ + public function build() + { + $mapping = $this->mapping; + if ($this->joinColumns) { + $mapping['joinColumns'] = $this->joinColumns; + } + $cm = $this->builder->getClassMetadata(); + if ($this->type == ClassMetadata::MANY_TO_ONE) { + $cm->mapManyToOne($mapping); + } else if ($this->type == ClassMetadata::ONE_TO_ONE) { + $cm->mapOneToOne($mapping); + } else { + throw new \InvalidArgumentException("Type should be a ToOne Association here"); + } + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php new file mode 100644 index 0000000..7d771d3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -0,0 +1,547 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Builder Object for ClassMetadata + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + * @author Guilherme Blanco + */ +class ClassMetadataBuilder +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + private $cm; + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $cm + */ + public function __construct(ClassMetadataInfo $cm) + { + $this->cm = $cm; + } + + /** + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->cm; + } + + /** + * Marks the class as mapped superclass. + * + * @return ClassMetadataBuilder + */ + public function setMappedSuperClass() + { + $this->cm->isMappedSuperclass = true; + $this->cm->isEmbeddedClass = false; + + return $this; + } + + /** + * Marks the class as embeddable. + * + * @return ClassMetadataBuilder + */ + public function setEmbeddable() + { + $this->cm->isEmbeddedClass = true; + $this->cm->isMappedSuperclass = false; + + return $this; + } + + /** + * Adds and embedded class + * + * @param string $fieldName + * @param string $class + * @param string|null $columnPrefix + * + * @return $this + */ + public function addEmbedded($fieldName, $class, $columnPrefix = null) + { + $this->cm->mapEmbedded(array( + 'fieldName' => $fieldName, + 'class' => $class, + 'columnPrefix' => $columnPrefix + )); + + return $this; + } + + /** + * Sets custom Repository class name. + * + * @param string $repositoryClassName + * + * @return ClassMetadataBuilder + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->cm->setCustomRepositoryClass($repositoryClassName); + + return $this; + } + + /** + * Marks class read only. + * + * @return ClassMetadataBuilder + */ + public function setReadOnly() + { + $this->cm->markReadOnly(); + + return $this; + } + + /** + * Sets the table name. + * + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function setTable($name) + { + $this->cm->setPrimaryTable(array('name' => $name)); + + return $this; + } + + /** + * Adds Index. + * + * @param array $columns + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function addIndex(array $columns, $name) + { + if (!isset($this->cm->table['indexes'])) { + $this->cm->table['indexes'] = array(); + } + + $this->cm->table['indexes'][$name] = array('columns' => $columns); + + return $this; + } + + /** + * Adds Unique Constraint. + * + * @param array $columns + * @param string $name + * + * @return ClassMetadataBuilder + */ + public function addUniqueConstraint(array $columns, $name) + { + if ( ! isset($this->cm->table['uniqueConstraints'])) { + $this->cm->table['uniqueConstraints'] = array(); + } + + $this->cm->table['uniqueConstraints'][$name] = array('columns' => $columns); + + return $this; + } + + /** + * Adds named query. + * + * @param string $name + * @param string $dqlQuery + * + * @return ClassMetadataBuilder + */ + public function addNamedQuery($name, $dqlQuery) + { + $this->cm->addNamedQuery(array( + 'name' => $name, + 'query' => $dqlQuery, + )); + + return $this; + } + + /** + * Sets class as root of a joined table inheritance hierarchy. + * + * @return ClassMetadataBuilder + */ + public function setJoinedTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED); + + return $this; + } + + /** + * Sets class as root of a single table inheritance hierarchy. + * + * @return ClassMetadataBuilder + */ + public function setSingleTableInheritance() + { + $this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE); + + return $this; + } + + /** + * Sets the discriminator column details. + * + * @param string $name + * @param string $type + * @param int $length + * + * @return ClassMetadataBuilder + */ + public function setDiscriminatorColumn($name, $type = 'string', $length = 255) + { + $this->cm->setDiscriminatorColumn(array( + 'name' => $name, + 'type' => $type, + 'length' => $length, + )); + + return $this; + } + + /** + * Adds a subclass to this inheritance hierarchy. + * + * @param string $name + * @param string $class + * + * @return ClassMetadataBuilder + */ + public function addDiscriminatorMapClass($name, $class) + { + $this->cm->addDiscriminatorMapClass($name, $class); + + return $this; + } + + /** + * Sets deferred explicit change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyDeferredExplicit() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT); + + return $this; + } + + /** + * Sets notify change tracking policy. + * + * @return ClassMetadataBuilder + */ + public function setChangeTrackingPolicyNotify() + { + $this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY); + + return $this; + } + + /** + * Adds lifecycle event. + * + * @param string $methodName + * @param string $event + * + * @return ClassMetadataBuilder + */ + public function addLifecycleEvent($methodName, $event) + { + $this->cm->addLifecycleCallback($methodName, $event); + + return $this; + } + + /** + * Adds Field. + * + * @param string $name + * @param string $type + * @param array $mapping + * + * @return ClassMetadataBuilder + */ + public function addField($name, $type, array $mapping = array()) + { + $mapping['fieldName'] = $name; + $mapping['type'] = $type; + + $this->cm->mapField($mapping); + + return $this; + } + + /** + * Creates a field builder. + * + * @param string $name + * @param string $type + * + * @return FieldBuilder + */ + public function createField($name, $type) + { + return new FieldBuilder( + $this, + array( + 'fieldName' => $name, + 'type' => $type + ) + ); + } + + /** + * Creates an embedded builder. + * + * @param string $fieldName + * @param string $class + * + * @return EmbeddedBuilder + */ + public function createEmbedded($fieldName, $class) + { + return new EmbeddedBuilder( + $this, + array( + 'fieldName' => $fieldName, + 'class' => $class, + 'columnPrefix' => null + ) + ); + } + + /** + * Adds a simple many to one association, optionally with the inversed by field. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addManyToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createManyToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Creates a ManyToOne Association Builder. + * + * Note: This method does not add the association, you have to call build() on the AssociationBuilder. + * + * @param string $name + * @param string $targetEntity + * + * @return AssociationBuilder + */ + public function createManyToOne($name, $targetEntity) + { + return new AssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::MANY_TO_ONE + ); + } + + /** + * Creates a OneToOne Association Builder. + * + * @param string $name + * @param string $targetEntity + * + * @return AssociationBuilder + */ + public function createOneToOne($name, $targetEntity) + { + return new AssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::ONE_TO_ONE + ); + } + + /** + * Adds simple inverse one-to-one association. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * + * @return ClassMetadataBuilder + */ + public function addInverseOneToOne($name, $targetEntity, $mappedBy) + { + $builder = $this->createOneToOne($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } + + /** + * Adds simple owning one-to-one association. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addOwningOneToOne($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createOneToOne($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Creates a ManyToMany Association Builder. + * + * @param string $name + * @param string $targetEntity + * + * @return ManyToManyAssociationBuilder + */ + public function createManyToMany($name, $targetEntity) + { + return new ManyToManyAssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::MANY_TO_MANY + ); + } + + /** + * Adds a simple owning many to many association. + * + * @param string $name + * @param string $targetEntity + * @param string|null $inversedBy + * + * @return ClassMetadataBuilder + */ + public function addOwningManyToMany($name, $targetEntity, $inversedBy = null) + { + $builder = $this->createManyToMany($name, $targetEntity); + + if ($inversedBy) { + $builder->inversedBy($inversedBy); + } + + return $builder->build(); + } + + /** + * Adds a simple inverse many to many association. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * + * @return ClassMetadataBuilder + */ + public function addInverseManyToMany($name, $targetEntity, $mappedBy) + { + $builder = $this->createManyToMany($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } + + /** + * Creates a one to many association builder. + * + * @param string $name + * @param string $targetEntity + * + * @return OneToManyAssociationBuilder + */ + public function createOneToMany($name, $targetEntity) + { + return new OneToManyAssociationBuilder( + $this, + array( + 'fieldName' => $name, + 'targetEntity' => $targetEntity + ), + ClassMetadata::ONE_TO_MANY + ); + } + + /** + * Adds simple OneToMany association. + * + * @param string $name + * @param string $targetEntity + * @param string $mappedBy + * + * @return ClassMetadataBuilder + */ + public function addOneToMany($name, $targetEntity, $mappedBy) + { + $builder = $this->createOneToMany($name, $targetEntity); + $builder->mappedBy($mappedBy); + + return $builder->build(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EmbeddedBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EmbeddedBuilder.php new file mode 100644 index 0000000..de8383c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EmbeddedBuilder.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * Embedded Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.5 + * @author Guido Contreras Woda + */ +class EmbeddedBuilder +{ + /** + * @var ClassMetadataBuilder + */ + private $builder; + + /** + * @var array + */ + private $mapping; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping) + { + $this->builder = $builder; + $this->mapping = $mapping; + } + + /** + * Sets the column prefix for all of the embedded columns. + * + * @param string $columnPrefix + * @return $this + */ + public function setColumnPrefix($columnPrefix) + { + $this->mapping['columnPrefix'] = $columnPrefix; + + return $this; + } + + /** + * Finalizes this embeddable and attach it to the ClassMetadata. + * + * Without this call an EmbeddedBuilder has no effect on the ClassMetadata. + * + * @return ClassMetadataBuilder + */ + public function build() + { + $cm = $this->builder->getClassMetadata(); + + $cm->mapEmbedded($this->mapping); + + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php new file mode 100644 index 0000000..d17abea --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/EntityListenerBuilder.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Events; + +/** + * Builder for entity listeners. + * + * @since 2.4 + * @author Fabio B. Silva + */ +class EntityListenerBuilder +{ + /** + * @var array Hash-map to handle event names. + */ + static private $events = array( + Events::preRemove => true, + Events::postRemove => true, + Events::prePersist => true, + Events::postPersist => true, + Events::preUpdate => true, + Events::postUpdate => true, + Events::postLoad => true, + Events::preFlush => true + ); + + /** + * Lookup the entity class to find methods that match to event lifecycle names + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata The entity metadata. + * @param string $className The listener class name. + * + * @throws \Doctrine\ORM\Mapping\MappingException When the listener class not found. + */ + static public function bindEntityListener(ClassMetadata $metadata, $className) + { + $class = $metadata->fullyQualifiedClassName($className); + + if ( ! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $className); + } + + foreach (get_class_methods($class) as $method) { + if ( ! isset(self::$events[$method])) { + continue; + } + + $metadata->addEntityListener($method, $class, $method); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php new file mode 100644 index 0000000..425840a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php @@ -0,0 +1,257 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * Field Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.2 + * @author Benjamin Eberlei + */ +class FieldBuilder +{ + /** + * @var ClassMetadataBuilder + */ + private $builder; + + /** + * @var array + */ + private $mapping; + + /** + * @var bool + */ + private $version; + + /** + * @var string + */ + private $generatedValue; + + /** + * @var array + */ + private $sequenceDef; + + /** + * @param ClassMetadataBuilder $builder + * @param array $mapping + */ + public function __construct(ClassMetadataBuilder $builder, array $mapping) + { + $this->builder = $builder; + $this->mapping = $mapping; + } + + /** + * Sets length. + * + * @param int $length + * + * @return FieldBuilder + */ + public function length($length) + { + $this->mapping['length'] = $length; + return $this; + } + + /** + * Sets nullable. + * + * @param bool $flag + * + * @return FieldBuilder + */ + public function nullable($flag = true) + { + $this->mapping['nullable'] = (bool)$flag; + return $this; + } + + /** + * Sets Unique. + * + * @param bool $flag + * + * @return FieldBuilder + */ + public function unique($flag = true) + { + $this->mapping['unique'] = (bool)$flag; + return $this; + } + + /** + * Sets column name. + * + * @param string $name + * + * @return FieldBuilder + */ + public function columnName($name) + { + $this->mapping['columnName'] = $name; + return $this; + } + + /** + * Sets Precision. + * + * @param int $p + * + * @return FieldBuilder + */ + public function precision($p) + { + $this->mapping['precision'] = $p; + return $this; + } + + /** + * Sets scale. + * + * @param int $s + * + * @return FieldBuilder + */ + public function scale($s) + { + $this->mapping['scale'] = $s; + return $this; + } + + /** + * Sets field as primary key. + * + * @deprecated Use makePrimaryKey() instead + * @return FieldBuilder + */ + public function isPrimaryKey() + { + return $this->makePrimaryKey(); + } + + /** + * Sets field as primary key. + * + * @return FieldBuilder + */ + public function makePrimaryKey() + { + $this->mapping['id'] = true; + return $this; + } + + /** + * Sets an option. + * + * @param string $name + * @param mixed $value + * + * @return FieldBuilder + */ + public function option($name, $value) + { + $this->mapping['options'][$name] = $value; + return $this; + } + + /** + * @param string $strategy + * + * @return FieldBuilder + */ + public function generatedValue($strategy = 'AUTO') + { + $this->generatedValue = $strategy; + return $this; + } + + /** + * Sets field versioned. + * + * @return FieldBuilder + */ + public function isVersionField() + { + $this->version = true; + return $this; + } + + /** + * Sets Sequence Generator. + * + * @param string $sequenceName + * @param int $allocationSize + * @param int $initialValue + * + * @return FieldBuilder + */ + public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1) + { + $this->sequenceDef = array( + 'sequenceName' => $sequenceName, + 'allocationSize' => $allocationSize, + 'initialValue' => $initialValue, + ); + return $this; + } + + /** + * Sets column definition. + * + * @param string $def + * + * @return FieldBuilder + */ + public function columnDefinition($def) + { + $this->mapping['columnDefinition'] = $def; + return $this; + } + + /** + * Finalizes this field and attach it to the ClassMetadata. + * + * Without this call a FieldBuilder has no effect on the ClassMetadata. + * + * @return ClassMetadataBuilder + */ + public function build() + { + $cm = $this->builder->getClassMetadata(); + if ($this->generatedValue) { + $cm->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue)); + } + if ($this->version) { + $cm->setVersionMapping($this->mapping); + } + $cm->mapField($this->mapping); + if ($this->sequenceDef) { + $cm->setSequenceGeneratorDefinition($this->sequenceDef); + } + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php new file mode 100644 index 0000000..a182e1e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/ManyToManyAssociationBuilder.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * ManyToMany Association Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder +{ + /** + * @var string|null + */ + private $joinTableName; + + /** + * @var array + */ + private $inverseJoinColumns = array(); + + /** + * @param string $name + * + * @return ManyToManyAssociationBuilder + */ + public function setJoinTable($name) + { + $this->joinTableName = $name; + return $this; + } + + /** + * Adds Inverse Join Columns. + * + * @param string $columnName + * @param string $referencedColumnName + * @param bool $nullable + * @param bool $unique + * @param string|null $onDelete + * @param string|null $columnDef + * + * @return ManyToManyAssociationBuilder + */ + public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null) + { + $this->inverseJoinColumns[] = array( + 'name' => $columnName, + 'referencedColumnName' => $referencedColumnName, + 'nullable' => $nullable, + 'unique' => $unique, + 'onDelete' => $onDelete, + 'columnDefinition' => $columnDef, + ); + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + $mapping['joinTable'] = array(); + if ($this->joinColumns) { + $mapping['joinTable']['joinColumns'] = $this->joinColumns; + } + if ($this->inverseJoinColumns) { + $mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns; + } + if ($this->joinTableName) { + $mapping['joinTable']['name'] = $this->joinTableName; + } + $cm = $this->builder->getClassMetadata(); + $cm->mapManyToMany($mapping); + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php new file mode 100644 index 0000000..4ca60f0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Builder/OneToManyAssociationBuilder.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Builder; + +/** + * OneToMany Association Builder + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 2.0 + * @author Benjamin Eberlei + */ +class OneToManyAssociationBuilder extends AssociationBuilder +{ + /** + * @param array $fieldNames + * + * @return OneToManyAssociationBuilder + */ + public function setOrderBy(array $fieldNames) + { + $this->mapping['orderBy'] = $fieldNames; + return $this; + } + + /** + * @param string $fieldName + * + * @return OneToManyAssociationBuilder + */ + public function setIndexBy($fieldName) + { + $this->mapping['indexBy'] = $fieldName; + return $this; + } + + /** + * @return ClassMetadataBuilder + */ + public function build() + { + $mapping = $this->mapping; + if ($this->joinColumns) { + $mapping['joinColumns'] = $this->joinColumns; + } + $cm = $this->builder->getClassMetadata(); + $cm->mapOneToMany($mapping); + return $this->builder; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Cache.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Cache.php new file mode 100644 index 0000000..3226b60 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Cache.php @@ -0,0 +1,44 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Caching to an entity or a collection. + * + * @author Fabio B. Silva + * @since 2.5 + * + * @Annotation + * @Target({"CLASS","PROPERTY"}) + */ +final class Cache implements Annotation +{ + /** + * @Enum({"READ_ONLY", "NONSTRICT_READ_WRITE", "READ_WRITE"}) + * + * @var string The concurrency strategy. + */ + public $usage = 'READ_ONLY'; + + /** + * @var string Cache region name. + */ + public $region; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php new file mode 100644 index 0000000..3657b76 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class ChangeTrackingPolicy implements Annotation +{ + /** + * The change tracking policy. + * + * @var string + * + * @Enum({"DEFERRED_IMPLICIT", "DEFERRED_EXPLICIT", "NOTIFY"}) + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php new file mode 100644 index 0000000..a57f1e1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * {@inheritDoc} + * + * @todo remove or rename ClassMetadataInfo to ClassMetadata + */ +class ClassMetadata extends ClassMetadataInfo +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php new file mode 100644 index 0000000..e0eacfa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -0,0 +1,768 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; +use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; +use Doctrine\Common\Persistence\Mapping\ReflectionService; +use Doctrine\DBAL\Platforms; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; +use Doctrine\ORM\Events; +use Doctrine\ORM\Id\BigIntegerIdentityGenerator; +use Doctrine\ORM\Id\IdentityGenerator; +use Doctrine\ORM\ORMException; +use ReflectionException; + +/** + * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the + * metadata mapping information of a class which describes how a class should be mapped + * to a relational database. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ClassMetadataFactory extends AbstractClassMetadataFactory +{ + /** + * @var EntityManagerInterface|null + */ + private $em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $targetPlatform; + + /** + * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver + */ + private $driver; + + /** + * @var \Doctrine\Common\EventManager + */ + private $evm; + + /** + * @var array + */ + private $embeddablesActiveNesting = array(); + + /** + * {@inheritDoc} + */ + protected function loadMetadata($name) + { + $loaded = parent::loadMetadata($name); + + array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded)); + + return $loaded; + } + + /** + * @param EntityManagerInterface $em + */ + public function setEntityManager(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * {@inheritDoc} + */ + protected function initialize() + { + $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); + $this->evm = $this->em->getEventManager(); + $this->initialized = true; + } + + /** + * {@inheritDoc} + */ + protected function onNotFoundMetadata($className) + { + if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) { + return; + } + + $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em); + + $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); + + return $eventArgs->getFoundMetadata(); + } + + /** + * {@inheritDoc} + */ + protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents) + { + /* @var $class ClassMetadata */ + /* @var $parent ClassMetadata */ + if ($parent) { + $class->setInheritanceType($parent->inheritanceType); + $class->setDiscriminatorColumn($parent->discriminatorColumn); + $class->setIdGeneratorType($parent->generatorType); + $this->addInheritedFields($class, $parent); + $this->addInheritedRelations($class, $parent); + $this->addInheritedEmbeddedClasses($class, $parent); + $class->setIdentifier($parent->identifier); + $class->setVersioned($parent->isVersioned); + $class->setVersionField($parent->versionField); + $class->setDiscriminatorMap($parent->discriminatorMap); + $class->setLifecycleCallbacks($parent->lifecycleCallbacks); + $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); + + if ( ! empty($parent->customGeneratorDefinition)) { + $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition); + } + + if ($parent->isMappedSuperclass) { + $class->setCustomRepositoryClass($parent->customRepositoryClassName); + } + } + + // Invoke driver + try { + $this->driver->loadMetadataForClass($class->getName(), $class); + } catch (ReflectionException $e) { + throw MappingException::reflectionFailure($class->getName(), $e); + } + + // If this class has a parent the id generator strategy is inherited. + // However this is only true if the hierarchy of parents contains the root entity, + // if it consists of mapped superclasses these don't necessarily include the id field. + if ($parent && $rootEntityFound) { + if ($parent->isIdGeneratorSequence()) { + $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition); + } else if ($parent->isIdGeneratorTable()) { + $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition; + } + + if ($parent->generatorType) { + $class->setIdGeneratorType($parent->generatorType); + } + + if ($parent->idGenerator) { + $class->setIdGenerator($parent->idGenerator); + } + } else { + $this->completeIdGeneratorMapping($class); + } + + if (!$class->isMappedSuperclass) { + foreach ($class->embeddedClasses as $property => $embeddableClass) { + + if (isset($embeddableClass['inherited'])) { + continue; + } + + if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) { + throw MappingException::missingEmbeddedClass($property); + } + + if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) { + throw MappingException::infiniteEmbeddableNesting($class->name, $property); + } + + $this->embeddablesActiveNesting[$class->name] = true; + + $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); + + if ($embeddableMetadata->isEmbeddedClass) { + $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property); + } + + $class->inlineEmbeddable($property, $embeddableMetadata); + + unset($this->embeddablesActiveNesting[$class->name]); + } + } + + if ($parent) { + if ($parent->isInheritanceTypeSingleTable()) { + $class->setPrimaryTable($parent->table); + } + + if ($parent) { + $this->addInheritedIndexes($class, $parent); + } + + if ($parent->cache) { + $class->cache = $parent->cache; + } + + if ($parent->containsForeignIdentifier) { + $class->containsForeignIdentifier = true; + } + + if ( ! empty($parent->namedQueries)) { + $this->addInheritedNamedQueries($class, $parent); + } + + if ( ! empty($parent->namedNativeQueries)) { + $this->addInheritedNamedNativeQueries($class, $parent); + } + + if ( ! empty($parent->sqlResultSetMappings)) { + $this->addInheritedSqlResultSetMappings($class, $parent); + } + + if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) { + $class->entityListeners = $parent->entityListeners; + } + } + + $class->setParentClasses($nonSuperclassParents); + + if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) { + $this->addDefaultDiscriminatorMap($class); + } + + if ($this->evm->hasListeners(Events::loadClassMetadata)) { + $eventArgs = new LoadClassMetadataEventArgs($class, $this->em); + $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); + } + + $this->validateRuntimeMetadata($class, $parent); + } + + /** + * Validate runtime metadata is correctly defined. + * + * @param ClassMetadata $class + * @param ClassMetadataInterface|null $parent + * + * @return void + * + * @throws MappingException + */ + protected function validateRuntimeMetadata($class, $parent) + { + if ( ! $class->reflClass ) { + // only validate if there is a reflection class instance + return; + } + + $class->validateIdentifier(); + $class->validateAssociations(); + $class->validateLifecycleCallbacks($this->getReflectionService()); + + // verify inheritance + if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { + if ( ! $parent) { + if (count($class->discriminatorMap) == 0) { + throw MappingException::missingDiscriminatorMap($class->name); + } + if ( ! $class->discriminatorColumn) { + throw MappingException::missingDiscriminatorColumn($class->name); + } + } + } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { + // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy + throw MappingException::noInheritanceOnMappedSuperClass($class->name); + } + } + + /** + * {@inheritDoc} + */ + protected function newClassMetadataInstance($className) + { + return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy()); + } + + /** + * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator + * map classes and looking for a fitting one. + * + * @param ClassMetadata $metadata + * + * @return void + * + * @throws MappingException + */ + private function resolveDiscriminatorValue(ClassMetadata $metadata) + { + if ($metadata->discriminatorValue + || ! $metadata->discriminatorMap + || $metadata->isMappedSuperclass + || ! $metadata->reflClass + || $metadata->reflClass->isAbstract() + ) { + return; + } + + // minor optimization: avoid loading related metadata when not needed + foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { + if ($discriminatorClass === $metadata->name) { + $metadata->discriminatorValue = $discriminatorValue; + + return; + } + } + + // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata + foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { + if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) { + $metadata->discriminatorValue = $discriminatorValue; + + return; + } + } + + throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName); + } + + /** + * Adds a default discriminator map if no one is given + * + * If an entity is of any inheritance type and does not contain a + * discriminator map, then the map is generated automatically. This process + * is expensive computation wise. + * + * The automatically generated discriminator map contains the lowercase short name of + * each class as key. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @throws MappingException + */ + private function addDefaultDiscriminatorMap(ClassMetadata $class) + { + $allClasses = $this->driver->getAllClassNames(); + $fqcn = $class->getName(); + $map = array($this->getShortName($class->name) => $fqcn); + + $duplicates = array(); + foreach ($allClasses as $subClassCandidate) { + if (is_subclass_of($subClassCandidate, $fqcn)) { + $shortName = $this->getShortName($subClassCandidate); + + if (isset($map[$shortName])) { + $duplicates[] = $shortName; + } + + $map[$shortName] = $subClassCandidate; + } + } + + if ($duplicates) { + throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map); + } + + $class->setDiscriminatorMap($map); + } + + /** + * Gets the lower-case short name of a class. + * + * @param string $className + * + * @return string + */ + private function getShortName($className) + { + if (strpos($className, "\\") === false) { + return strtolower($className); + } + + $parts = explode("\\", $className); + return strtolower(end($parts)); + } + + /** + * Adds inherited fields to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->fieldMappings as $mapping) { + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedFieldMapping($mapping); + } + foreach ($parentClass->reflFields as $name => $field) { + $subClass->reflFields[$name] = $field; + } + } + + /** + * Adds inherited association mappings to the subclass mapping. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + * + * @throws MappingException + */ + private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->associationMappings as $field => $mapping) { + if ($parentClass->isMappedSuperclass) { + if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) { + throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field); + } + $mapping['sourceEntity'] = $subClass->name; + } + + //$subclassMapping = $mapping; + if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { + $mapping['inherited'] = $parentClass->name; + } + if ( ! isset($mapping['declared'])) { + $mapping['declared'] = $parentClass->name; + } + $subClass->addInheritedAssociationMapping($mapping); + } + } + + private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->embeddedClasses as $field => $embeddedClass) { + if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) { + $embeddedClass['inherited'] = $parentClass->name; + } + if ( ! isset($embeddedClass['declared'])) { + $embeddedClass['declared'] = $parentClass->name; + } + + $subClass->embeddedClasses[$field] = $embeddedClass; + } + } + + /** + * Adds nested embedded classes metadata to a parent class. + * + * @param ClassMetadata $subClass Sub embedded class metadata to add nested embedded classes metadata from. + * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to. + * @param string $prefix Embedded classes' prefix to use for nested embedded classes field names. + */ + private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix) + { + foreach ($subClass->embeddedClasses as $property => $embeddableClass) { + if (isset($embeddableClass['inherited'])) { + continue; + } + + $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); + + $parentClass->mapEmbedded(array( + 'fieldName' => $prefix . '.' . $property, + 'class' => $embeddableMetadata->name, + 'columnPrefix' => $embeddableClass['columnPrefix'], + 'declaredField' => $embeddableClass['declaredField'] + ? $prefix . '.' . $embeddableClass['declaredField'] + : $prefix, + 'originalField' => $embeddableClass['originalField'] ?: $property, + )); + } + } + + /** + * Copy the table indices from the parent class superclass to the child class + * + * @param ClassMetadata $subClass + * @param ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) + { + if (! $parentClass->isMappedSuperclass) { + return; + } + + foreach (array('uniqueConstraints', 'indexes') as $indexType) { + if (isset($parentClass->table[$indexType])) { + foreach ($parentClass->table[$indexType] as $indexName => $index) { + if (isset($subClass->table[$indexType][$indexName])) { + continue; // Let the inheriting table override indices + } + + $subClass->table[$indexType][$indexName] = $index; + } + } + } + } + + /** + * Adds inherited named queries to the subclass mapping. + * + * @since 2.2 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedQueries as $name => $query) { + if ( ! isset ($subClass->namedQueries[$name])) { + $subClass->addNamedQuery(array( + 'name' => $query['name'], + 'query' => $query['query'] + )); + } + } + } + + /** + * Adds inherited named native queries to the subclass mapping. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->namedNativeQueries as $name => $query) { + if ( ! isset ($subClass->namedNativeQueries[$name])) { + $subClass->addNamedNativeQuery(array( + 'name' => $query['name'], + 'query' => $query['query'], + 'isSelfClass' => $query['isSelfClass'], + 'resultSetMapping' => $query['resultSetMapping'], + 'resultClass' => $query['isSelfClass'] ? $subClass->name : $query['resultClass'], + )); + } + } + } + + /** + * Adds inherited sql result set mappings to the subclass mapping. + * + * @since 2.3 + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass + * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass + * + * @return void + */ + private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) + { + foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { + if ( ! isset ($subClass->sqlResultSetMappings[$name])) { + $entities = array(); + foreach ($mapping['entities'] as $entity) { + $entities[] = array( + 'fields' => $entity['fields'], + 'isSelfClass' => $entity['isSelfClass'], + 'discriminatorColumn' => $entity['discriminatorColumn'], + 'entityClass' => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'], + ); + } + + $subClass->addSqlResultSetMapping(array( + 'name' => $mapping['name'], + 'columns' => $mapping['columns'], + 'entities' => $entities, + )); + } + } + } + + /** + * Completes the ID generator mapping. If "auto" is specified we choose the generator + * most appropriate for the targeted database platform. + * + * @param ClassMetadataInfo $class + * + * @return void + * + * @throws ORMException + */ + private function completeIdGeneratorMapping(ClassMetadataInfo $class) + { + $idGenType = $class->generatorType; + if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) { + if ($this->getTargetPlatform()->prefersSequences()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); + } else if ($this->getTargetPlatform()->prefersIdentityColumns()) { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); + } else { + $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE); + } + } + + // Create & assign an appropriate ID generator instance + switch ($class->generatorType) { + case ClassMetadata::GENERATOR_TYPE_IDENTITY: + $sequenceName = null; + $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; + + // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour. + if ($this->getTargetPlatform()->usesSequenceEmulatedIdentityColumns()) { + $columnName = $class->getSingleIdentifierColumnName(); + $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform()); + $sequenceName = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName); + $definition = array( + 'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName) + ); + + if ($quoted) { + $definition['quoted'] = true; + } + + $sequenceName = $this + ->em + ->getConfiguration() + ->getQuoteStrategy() + ->getSequenceName($definition, $class, $this->getTargetPlatform()); + } + + $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint') + ? new BigIntegerIdentityGenerator($sequenceName) + : new IdentityGenerator($sequenceName); + + $class->setIdGenerator($generator); + + break; + + case ClassMetadata::GENERATOR_TYPE_SEQUENCE: + // If there is no sequence definition yet, create a default definition + $definition = $class->sequenceGeneratorDefinition; + + if ( ! $definition) { + $fieldName = $class->getSingleIdentifierFieldName(); + $sequenceName = $class->getSequenceName($this->getTargetPlatform()); + $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); + + $definition = array( + 'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName), + 'allocationSize' => 1, + 'initialValue' => 1, + ); + + if ($quoted) { + $definition['quoted'] = true; + } + + $class->setSequenceGeneratorDefinition($definition); + } + + $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator( + $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()), + $definition['allocationSize'] + ); + $class->setIdGenerator($sequenceGenerator); + break; + + case ClassMetadata::GENERATOR_TYPE_NONE: + $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator()); + break; + + case ClassMetadata::GENERATOR_TYPE_UUID: + $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator()); + break; + + case ClassMetadata::GENERATOR_TYPE_TABLE: + throw new ORMException("TableGenerator not yet implemented."); + break; + + case ClassMetadata::GENERATOR_TYPE_CUSTOM: + $definition = $class->customGeneratorDefinition; + if ( ! class_exists($definition['class'])) { + throw new ORMException("Can't instantiate custom generator : " . + $definition['class']); + } + $class->setIdGenerator(new $definition['class']); + break; + + default: + throw new ORMException("Unknown generator type: " . $class->generatorType); + } + } + + /** + * {@inheritDoc} + */ + protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + /* @var $class ClassMetadata */ + $class->wakeupReflection($reflService); + } + + /** + * {@inheritDoc} + */ + protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) + { + /* @var $class ClassMetadata */ + $class->initializeReflection($reflService); + } + + /** + * {@inheritDoc} + */ + protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) + { + return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + /** + * {@inheritDoc} + */ + protected function getDriver() + { + return $this->driver; + } + + /** + * {@inheritDoc} + */ + protected function isEntity(ClassMetadataInterface $class) + { + return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false; + } + + /** + * @return Platforms\AbstractPlatform + */ + private function getTargetPlatform() + { + if (!$this->targetPlatform) { + $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform(); + } + + return $this->targetPlatform; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php new file mode 100644 index 0000000..79dcdb2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -0,0 +1,3317 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use BadMethodCallException; +use Doctrine\Instantiator\Instantiator; +use InvalidArgumentException; +use RuntimeException; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use ReflectionClass; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\ClassLoader; + +/** + * A ClassMetadata instance holds all the object-relational mapping metadata + * of an entity and its associations. + * + * Once populated, ClassMetadata instances are usually cached in a serialized form. + * + * IMPORTANT NOTE: + * + * The fields of this class are only public for 2 reasons: + * 1) To allow fast READ access. + * 2) To drastically reduce the size of a serialized instance (private/protected members + * get the whole class name, namespace inclusive, prepended to every property in + * the serialized representation). + * + * @author Roman Borschel + * @author Jonathan H. Wage + * @since 2.0 + */ +class ClassMetadataInfo implements ClassMetadata +{ + /* The inheritance mapping types */ + /** + * NONE means the class does not participate in an inheritance hierarchy + * and therefore does not need an inheritance mapping type. + */ + const INHERITANCE_TYPE_NONE = 1; + + /** + * JOINED means the class will be persisted according to the rules of + * Class Table Inheritance. + */ + const INHERITANCE_TYPE_JOINED = 2; + + /** + * SINGLE_TABLE means the class will be persisted according to the rules of + * Single Table Inheritance. + */ + const INHERITANCE_TYPE_SINGLE_TABLE = 3; + + /** + * TABLE_PER_CLASS means the class will be persisted according to the rules + * of Concrete Table Inheritance. + */ + const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; + + /* The Id generator types. */ + /** + * AUTO means the generator type will depend on what the used platform prefers. + * Offers full portability. + */ + const GENERATOR_TYPE_AUTO = 1; + + /** + * SEQUENCE means a separate sequence object will be used. Platforms that do + * not have native sequence support may emulate it. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_SEQUENCE = 2; + + /** + * TABLE means a separate table is used for id generation. + * Offers full portability. + */ + const GENERATOR_TYPE_TABLE = 3; + + /** + * IDENTITY means an identity column is used for id generation. The database + * will fill in the id column on insertion. Platforms that do not support + * native identity columns may emulate them. Full portability is currently + * not guaranteed. + */ + const GENERATOR_TYPE_IDENTITY = 4; + + /** + * NONE means the class does not have a generated id. That means the class + * must have a natural, manually assigned id. + */ + const GENERATOR_TYPE_NONE = 5; + + /** + * UUID means that a UUID/GUID expression is used for id generation. Full + * portability is currently not guaranteed. + */ + const GENERATOR_TYPE_UUID = 6; + + /** + * CUSTOM means that customer will use own ID generator that supposedly work + */ + const GENERATOR_TYPE_CUSTOM = 7; + + /** + * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done for all entities that are in MANAGED state at commit-time. + * + * This is the default change tracking policy. + */ + const CHANGETRACKING_DEFERRED_IMPLICIT = 1; + + /** + * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time + * by doing a property-by-property comparison with the original data. This will + * be done only for entities that were explicitly saved (through persist() or a cascade). + */ + const CHANGETRACKING_DEFERRED_EXPLICIT = 2; + + /** + * NOTIFY means that Doctrine relies on the entities sending out notifications + * when their properties change. Such entity classes must implement + * the NotifyPropertyChanged interface. + */ + const CHANGETRACKING_NOTIFY = 3; + + /** + * Specifies that an association is to be fetched when it is first accessed. + */ + const FETCH_LAZY = 2; + + /** + * Specifies that an association is to be fetched when the owner of the + * association is fetched. + */ + const FETCH_EAGER = 3; + + /** + * Specifies that an association is to be fetched lazy (on first access) and that + * commands such as Collection#count, Collection#slice are issued directly against + * the database if the collection is not yet initialized. + */ + const FETCH_EXTRA_LAZY = 4; + + /** + * Identifies a one-to-one association. + */ + const ONE_TO_ONE = 1; + + /** + * Identifies a many-to-one association. + */ + const MANY_TO_ONE = 2; + + /** + * Identifies a one-to-many association. + */ + const ONE_TO_MANY = 4; + + /** + * Identifies a many-to-many association. + */ + const MANY_TO_MANY = 8; + + /** + * Combined bitmask for to-one (single-valued) associations. + */ + const TO_ONE = 3; + + /** + * Combined bitmask for to-many (collection-valued) associations. + */ + const TO_MANY = 12; + + /** + * ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks, + */ + const CACHE_USAGE_READ_ONLY = 1; + + /** + * Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes. + */ + const CACHE_USAGE_NONSTRICT_READ_WRITE = 2; + + /** + * Read Write Attempts to lock the entity before update/delete. + */ + const CACHE_USAGE_READ_WRITE = 3; + + /** + * READ-ONLY: The name of the entity class. + * + * @var string + */ + public $name; + + /** + * READ-ONLY: The namespace the entity class is contained in. + * + * @var string + * + * @todo Not really needed. Usage could be localized. + */ + public $namespace; + + /** + * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance + * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same + * as {@link $entityName}. + * + * @var string + */ + public $rootEntityName; + + /** + * READ-ONLY: The definition of custom generator. Only used for CUSTOM + * generator type + * + * The definition has the following structure: + * + * array( + * 'class' => 'ClassName', + * ) + * + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $customGeneratorDefinition; + + /** + * The name of the custom repository class used for the entity class. + * (Optional). + * + * @var string + */ + public $customRepositoryClassName; + + /** + * READ-ONLY: Whether this class describes the mapping of a mapped superclass. + * + * @var boolean + */ + public $isMappedSuperclass = false; + + /** + * READ-ONLY: Whether this class describes the mapping of an embeddable class. + * + * @var boolean + */ + public $isEmbeddedClass = false; + + /** + * READ-ONLY: The names of the parent classes (ancestors). + * + * @var array + */ + public $parentClasses = array(); + + /** + * READ-ONLY: The names of all subclasses (descendants). + * + * @var array + */ + public $subClasses = array(); + + /** + * READ-ONLY: The names of all embedded classes based on properties. + * + * @var array + */ + public $embeddedClasses = array(); + + /** + * READ-ONLY: The named queries allowed to be called directly from Repository. + * + * @var array + */ + public $namedQueries = array(); + + /** + * READ-ONLY: The named native queries allowed to be called directly from Repository. + * + * A native SQL named query definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'query'              => ,
+     *     'resultClass'        => ,
+     *     'resultSetMapping'   => 
+     * )
+     * 
+ * + * @var array + */ + public $namedNativeQueries = array(); + + /** + * READ-ONLY: The mappings of the results of native SQL queries. + * + * A native result mapping definition has the following structure: + *
+     * array(
+     *     'name'               => ,
+     *     'entities'           => array(),
+     *     'columns'            => array()
+     * )
+     * 
+ * + * @var array + */ + public $sqlResultSetMappings = array(); + + /** + * READ-ONLY: The field names of all fields that are part of the identifier/primary key + * of the mapped entity class. + * + * @var array + */ + public $identifier = array(); + + /** + * READ-ONLY: The inheritance mapping type used by the class. + * + * @var integer + */ + public $inheritanceType = self::INHERITANCE_TYPE_NONE; + + /** + * READ-ONLY: The Id generator type used by the class. + * + * @var int + */ + public $generatorType = self::GENERATOR_TYPE_NONE; + + /** + * READ-ONLY: The field mappings of the class. + * Keys are field names and values are mapping definitions. + * + * The mapping definition array has the following values: + * + * - fieldName (string) + * The name of the field in the Entity. + * + * - type (string) + * The type name of the mapped field. Can be one of Doctrine's mapping types + * or a custom mapping type. + * + * - columnName (string, optional) + * The column name. Optional. Defaults to the field name. + * + * - length (integer, optional) + * The database length of the column. Optional. Default value taken from + * the type. + * + * - id (boolean, optional) + * Marks the field as the primary key of the entity. Multiple fields of an + * entity can have the id attribute, forming a composite key. + * + * - nullable (boolean, optional) + * Whether the column is nullable. Defaults to FALSE. + * + * - columnDefinition (string, optional, schema-only) + * The SQL fragment that is used when generating the DDL for the column. + * + * - precision (integer, optional, schema-only) + * The precision of a decimal column. Only valid if the column type is decimal. + * + * - scale (integer, optional, schema-only) + * The scale of a decimal column. Only valid if the column type is decimal. + * + * - 'unique' (string, optional, schema-only) + * Whether a unique constraint should be generated for the column. + * + * @var array + */ + public $fieldMappings = array(); + + /** + * READ-ONLY: An array of field names. Used to look up field names from column names. + * Keys are column names and values are field names. + * This is the reverse lookup map of $_columnNames. + * + * @var array + */ + public $fieldNames = array(); + + /** + * READ-ONLY: A map of field names to column names. Keys are field names and values column names. + * Used to look up column names from field names. + * This is the reverse lookup map of $_fieldNames. + * + * @var array + * + * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName']. + */ + public $columnNames = array(); + + /** + * READ-ONLY: The discriminator value of this class. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * + * @see discriminatorColumn + */ + public $discriminatorValue; + + /** + * READ-ONLY: The discriminator map of all mapped classes in the hierarchy. + * + * This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies + * where a discriminator column is used. + * + * @var mixed + * + * @see discriminatorColumn + */ + public $discriminatorMap = array(); + + /** + * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE + * inheritance mappings. + * + * @var array + */ + public $discriminatorColumn; + + /** + * READ-ONLY: The primary table definition. The definition is an array with the + * following entries: + * + * name => + * schema => + * indexes => array + * uniqueConstraints => array + * + * @var array + */ + public $table; + + /** + * READ-ONLY: The registered lifecycle callbacks for entities of this class. + * + * @var array + */ + public $lifecycleCallbacks = array(); + + /** + * READ-ONLY: The registered entity listeners. + * + * @var array + */ + public $entityListeners = array(); + + /** + * READ-ONLY: The association mappings of this class. + * + * The mapping definition array supports the following keys: + * + * - fieldName (string) + * The name of the field in the entity the association is mapped to. + * + * - targetEntity (string) + * The class name of the target entity. If it is fully-qualified it is used as is. + * If it is a simple, unqualified class name the namespace is assumed to be the same + * as the namespace of the source entity. + * + * - mappedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the owning side. + * This key must be specified on the inverse side of a bidirectional association. + * + * - inversedBy (string, required for bidirectional associations) + * The name of the field that completes the bidirectional association on the inverse side. + * This key must be specified on the owning side of a bidirectional association. + * + * - cascade (array, optional) + * The names of persistence operations to cascade on the association. The set of possible + * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others). + * + * - orderBy (array, one-to-many/many-to-many only) + * A map of field names (of the target entity) to sorting directions (ASC/DESC). + * Example: array('priority' => 'desc') + * + * - fetch (integer, optional) + * The fetching strategy to use for the association, usually defaults to FETCH_LAZY. + * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY. + * + * - joinTable (array, optional, many-to-many only) + * Specification of the join table and its join columns (foreign keys). + * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped + * through a join table by simply mapping the association as many-to-many with a unique + * constraint on the join table. + * + * - indexBy (string, optional, to-many only) + * Specification of a field on target-entity that is used to index the collection by. + * This field HAS to be either the primary key or a unique column. Otherwise the collection + * does not contain all the entities that are actually related. + * + * A join table definition has the following structure: + *
+     * array(
+     *     'name' => ,
+     *      'joinColumns' => array(),
+     *      'inverseJoinColumns' => array()
+     * )
+     * 
+ * + * @var array + */ + public $associationMappings = array(); + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite. + * + * @var boolean + */ + public $isIdentifierComposite = false; + + /** + * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association. + * + * This flag is necessary because some code blocks require special treatment of this cases. + * + * @var boolean + */ + public $containsForeignIdentifier = false; + + /** + * READ-ONLY: The ID generator used for generating IDs for this class. + * + * @var \Doctrine\ORM\Id\AbstractIdGenerator + * + * @todo Remove! + */ + public $idGenerator; + + /** + * READ-ONLY: The definition of the sequence generator of this class. Only used for the + * SEQUENCE generation strategy. + * + * The definition has the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * ) + * + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $sequenceGeneratorDefinition; + + /** + * READ-ONLY: The definition of the table generator of this class. Only used for the + * TABLE generation strategy. + * + * @var array + * + * @todo Merge with tableGeneratorDefinition into generic generatorDefinition + */ + public $tableGeneratorDefinition; + + /** + * READ-ONLY: The policy used for change-tracking on entities of this class. + * + * @var integer + */ + public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; + + /** + * READ-ONLY: A flag for whether or not instances of this class are to be versioned + * with optimistic locking. + * + * @var boolean + */ + public $isVersioned; + + /** + * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any). + * + * @var mixed + */ + public $versionField; + + /** + * @var array + */ + public $cache = null; + + /** + * The ReflectionClass instance of the mapped class. + * + * @var ReflectionClass + */ + public $reflClass; + + /** + * Is this entity marked as "read-only"? + * + * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance + * optimization for entities that are immutable, either in your domain or through the relation database + * (coming from a view, or a history table for example). + * + * @var bool + */ + public $isReadOnly = false; + + /** + * NamingStrategy determining the default column and table names. + * + * @var \Doctrine\ORM\Mapping\NamingStrategy + */ + protected $namingStrategy; + + /** + * The ReflectionProperty instances of the mapped class. + * + * @var \ReflectionProperty[] + */ + public $reflFields = array(); + + /** + * @var \Doctrine\Instantiator\InstantiatorInterface|null + */ + private $instantiator; + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param string $entityName The name of the entity class the new instance is used for. + * @param NamingStrategy|null $namingStrategy + */ + public function __construct($entityName, NamingStrategy $namingStrategy = null) + { + $this->name = $entityName; + $this->rootEntityName = $entityName; + $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy(); + $this->instantiator = new Instantiator(); + } + + /** + * Gets the ReflectionProperties of the mapped class. + * + * @return array An array of ReflectionProperty instances. + */ + public function getReflectionProperties() + { + return $this->reflFields; + } + + /** + * Gets a ReflectionProperty for a specific field of the mapped class. + * + * @param string $name + * + * @return \ReflectionProperty + */ + public function getReflectionProperty($name) + { + return $this->reflFields[$name]; + } + + /** + * Gets the ReflectionProperty for the single identifier field. + * + * @return \ReflectionProperty + * + * @throws BadMethodCallException If the class has a composite identifier. + */ + public function getSingleIdReflectionProperty() + { + if ($this->isIdentifierComposite) { + throw new BadMethodCallException("Class " . $this->name . " has a composite identifier."); + } + return $this->reflFields[$this->identifier[0]]; + } + + /** + * Extracts the identifier values of an entity of this class. + * + * For composite identifiers, the identifier values are returned as an array + * with the same order as the field order in {@link identifier}. + * + * @param object $entity + * + * @return array + */ + public function getIdentifierValues($entity) + { + if ($this->isIdentifierComposite) { + $id = array(); + + foreach ($this->identifier as $idField) { + $value = $this->reflFields[$idField]->getValue($entity); + + if ($value !== null) { + $id[$idField] = $value; + } + } + + return $id; + } + + $id = $this->identifier[0]; + $value = $this->reflFields[$id]->getValue($entity); + + if (null === $value) { + return array(); + } + + return array($id => $value); + } + + /** + * Populates the entity identifier of an entity. + * + * @param object $entity + * @param mixed $id + * + * @return void + * + * @todo Rename to assignIdentifier() + */ + public function setIdentifierValues($entity, array $id) + { + foreach ($id as $idField => $idValue) { + $this->reflFields[$idField]->setValue($entity, $idValue); + } + } + + /** + * Sets the specified field to the specified value on the given entity. + * + * @param object $entity + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setFieldValue($entity, $field, $value) + { + $this->reflFields[$field]->setValue($entity, $value); + } + + /** + * Gets the specified field's value off the given entity. + * + * @param object $entity + * @param string $field + * + * @return mixed + */ + public function getFieldValue($entity, $field) + { + return $this->reflFields[$field]->getValue($entity); + } + + /** + * Creates a string representation of this instance. + * + * @return string The string representation of this instance. + * + * @todo Construct meaningful string representation. + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * Determines which fields get serialized. + * + * It is only serialized what is necessary for best unserialization performance. + * That means any metadata properties that are not set or empty or simply have + * their default value are NOT serialized. + * + * Parts that are also NOT serialized because they can not be properly unserialized: + * - reflClass (ReflectionClass) + * - reflFields (ReflectionProperty array) + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + // This metadata is always serialized/cached. + $serialized = array( + 'associationMappings', + 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName'] + 'fieldMappings', + 'fieldNames', + 'embeddedClasses', + 'identifier', + 'isIdentifierComposite', // TODO: REMOVE + 'name', + 'namespace', // TODO: REMOVE + 'table', + 'rootEntityName', + 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime. + ); + + // The rest of the metadata is only serialized if necessary. + if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) { + $serialized[] = 'changeTrackingPolicy'; + } + + if ($this->customRepositoryClassName) { + $serialized[] = 'customRepositoryClassName'; + } + + if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) { + $serialized[] = 'inheritanceType'; + $serialized[] = 'discriminatorColumn'; + $serialized[] = 'discriminatorValue'; + $serialized[] = 'discriminatorMap'; + $serialized[] = 'parentClasses'; + $serialized[] = 'subClasses'; + } + + if ($this->generatorType != self::GENERATOR_TYPE_NONE) { + $serialized[] = 'generatorType'; + if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) { + $serialized[] = 'sequenceGeneratorDefinition'; + } + } + + if ($this->isMappedSuperclass) { + $serialized[] = 'isMappedSuperclass'; + } + + if ($this->isEmbeddedClass) { + $serialized[] = 'isEmbeddedClass'; + } + + if ($this->containsForeignIdentifier) { + $serialized[] = 'containsForeignIdentifier'; + } + + if ($this->isVersioned) { + $serialized[] = 'isVersioned'; + $serialized[] = 'versionField'; + } + + if ($this->lifecycleCallbacks) { + $serialized[] = 'lifecycleCallbacks'; + } + + if ($this->entityListeners) { + $serialized[] = 'entityListeners'; + } + + if ($this->namedQueries) { + $serialized[] = 'namedQueries'; + } + + if ($this->namedNativeQueries) { + $serialized[] = 'namedNativeQueries'; + } + + if ($this->sqlResultSetMappings) { + $serialized[] = 'sqlResultSetMappings'; + } + + if ($this->isReadOnly) { + $serialized[] = 'isReadOnly'; + } + + if ($this->customGeneratorDefinition) { + $serialized[] = "customGeneratorDefinition"; + } + + if ($this->cache) { + $serialized[] = 'cache'; + } + + return $serialized; + } + + /** + * Creates a new instance of the mapped class, without invoking the constructor. + * + * @return object + */ + public function newInstance() + { + return $this->instantiator->instantiate($this->name); + } + + /** + * Restores some state that can not be serialized/unserialized. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * + * @return void + */ + public function wakeupReflection($reflService) + { + // Restore ReflectionClass and properties + $this->reflClass = $reflService->getClass($this->name); + $this->instantiator = $this->instantiator ?: new Instantiator(); + + $parentReflFields = array(); + + foreach ($this->embeddedClasses as $property => $embeddedClass) { + if (isset($embeddedClass['declaredField'])) { + $parentReflFields[$property] = new ReflectionEmbeddedProperty( + $parentReflFields[$embeddedClass['declaredField']], + $reflService->getAccessibleProperty( + $this->embeddedClasses[$embeddedClass['declaredField']]['class'], + $embeddedClass['originalField'] + ), + $this->embeddedClasses[$embeddedClass['declaredField']]['class'] + ); + + continue; + } + + $parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property); + $this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property); + } + + foreach ($this->fieldMappings as $field => $mapping) { + if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) { + $this->reflFields[$field] = new ReflectionEmbeddedProperty( + $parentReflFields[$mapping['declaredField']], + $reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']), + $mapping['originalClass'] + ); + continue; + } + + $this->reflFields[$field] = isset($mapping['declared']) + ? $reflService->getAccessibleProperty($mapping['declared'], $field) + : $reflService->getAccessibleProperty($this->name, $field); + } + + foreach ($this->associationMappings as $field => $mapping) { + $this->reflFields[$field] = isset($mapping['declared']) + ? $reflService->getAccessibleProperty($mapping['declared'], $field) + : $reflService->getAccessibleProperty($this->name, $field); + } + } + + /** + * Initializes a new ClassMetadata instance that will hold the object-relational mapping + * metadata of the class with the given name. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service. + * + * @return void + */ + public function initializeReflection($reflService) + { + $this->reflClass = $reflService->getClass($this->name); + $this->namespace = $reflService->getClassNamespace($this->name); + + if ($this->reflClass) { + $this->name = $this->rootEntityName = $this->reflClass->getName(); + } + + $this->table['name'] = $this->namingStrategy->classToTableName($this->name); + } + + /** + * Validates Identifier. + * + * @return void + * + * @throws MappingException + */ + public function validateIdentifier() + { + if ($this->isMappedSuperclass || $this->isEmbeddedClass) { + return; + } + + // Verify & complete identifier mapping + if ( ! $this->identifier) { + throw MappingException::identifierRequired($this->name); + } + + if ($this->usesIdGenerator() && $this->isIdentifierComposite) { + throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); + } + } + + /** + * Validates association targets actually exist. + * + * @return void + * + * @throws MappingException + */ + public function validateAssociations() + { + foreach ($this->associationMappings as $mapping) { + if ( ! ClassLoader::classExists($mapping['targetEntity']) ) { + throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); + } + } + } + + /** + * Validates lifecycle callbacks. + * + * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * + * @return void + * + * @throws MappingException + */ + public function validateLifecycleCallbacks($reflService) + { + foreach ($this->lifecycleCallbacks as $callbacks) { + foreach ($callbacks as $callbackFuncName) { + if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) { + throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); + } + } + } + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return $this->reflClass; + } + + /** + * @param array $cache + * + * @return void + */ + public function enableCache(array $cache) + { + if ( ! isset($cache['usage'])) { + $cache['usage'] = self::CACHE_USAGE_READ_ONLY; + } + + if ( ! isset($cache['region'])) { + $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)); + } + + $this->cache = $cache; + } + + /** + * @param string $fieldName + * @param array $cache + * + * @return void + */ + public function enableAssociationCache($fieldName, array $cache) + { + if ( ! isset($cache['usage'])) { + $cache['usage'] = isset($this->cache['usage']) + ? $this->cache['usage'] + : self::CACHE_USAGE_READ_ONLY; + } + + if ( ! isset($cache['region'])) { + $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName; + } + + $this->associationMappings[$fieldName]['cache'] = $cache; + } + + /** + * Sets the change tracking policy used by this class. + * + * @param integer $policy + * + * @return void + */ + public function setChangeTrackingPolicy($policy) + { + $this->changeTrackingPolicy = $policy; + } + + /** + * Whether the change tracking policy of this class is "deferred explicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredExplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT; + } + + /** + * Whether the change tracking policy of this class is "deferred implicit". + * + * @return boolean + */ + public function isChangeTrackingDeferredImplicit() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT; + } + + /** + * Whether the change tracking policy of this class is "notify". + * + * @return boolean + */ + public function isChangeTrackingNotify() + { + return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY; + } + + /** + * Checks whether a field is part of the identifier/primary key field(s). + * + * @param string $fieldName The field name. + * + * @return boolean TRUE if the field is part of the table identifier/primary key field(s), + * FALSE otherwise. + */ + public function isIdentifier($fieldName) + { + if ( ! $this->identifier) { + return false; + } + + if ( ! $this->isIdentifierComposite) { + return $fieldName === $this->identifier[0]; + } + + return in_array($fieldName, $this->identifier); + } + + /** + * Checks if the field is unique. + * + * @param string $fieldName The field name. + * + * @return boolean TRUE if the field is unique, FALSE otherwise. + */ + public function isUniqueField($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['unique']) && $mapping['unique'] == true; + } + return false; + } + + /** + * Checks if the field is not null. + * + * @param string $fieldName The field name. + * + * @return boolean TRUE if the field is not null, FALSE otherwise. + */ + public function isNullable($fieldName) + { + $mapping = $this->getFieldMapping($fieldName); + if ($mapping !== false) { + return isset($mapping['nullable']) && $mapping['nullable'] == true; + } + return false; + } + + /** + * Gets a column name for a field name. + * If the column name for the field cannot be found, the given field name + * is returned. + * + * @param string $fieldName The field name. + * + * @return string The column name. + */ + public function getColumnName($fieldName) + { + return isset($this->columnNames[$fieldName]) ? + $this->columnNames[$fieldName] : $fieldName; + } + + /** + * Gets the mapping of a (regular) field that holds some data but not a + * reference to another object. + * + * @param string $fieldName The field name. + * + * @return array The field mapping. + * + * @throws MappingException + */ + public function getFieldMapping($fieldName) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->fieldMappings[$fieldName]; + } + + /** + * Gets the mapping of an association. + * + * @see ClassMetadataInfo::$associationMappings + * + * @param string $fieldName The field name that represents the association in + * the object model. + * + * @return array The mapping. + * + * @throws MappingException + */ + public function getAssociationMapping($fieldName) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::mappingNotFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]; + } + + /** + * Gets all association mappings of the class. + * + * @return array + */ + public function getAssociationMappings() + { + return $this->associationMappings; + } + + /** + * Gets the field name for a column name. + * If no field name can be found the column name is returned. + * + * @param string $columnName The column name. + * + * @return string The column alias. + */ + public function getFieldName($columnName) + { + return isset($this->fieldNames[$columnName]) ? + $this->fieldNames[$columnName] : $columnName; + } + + /** + * Gets the named query. + * + * @see ClassMetadataInfo::$namedQueries + * + * @param string $queryName The query name. + * + * @return string + * + * @throws MappingException + */ + public function getNamedQuery($queryName) + { + if ( ! isset($this->namedQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + return $this->namedQueries[$queryName]['dql']; + } + + /** + * Gets all named queries of the class. + * + * @return array + */ + public function getNamedQueries() + { + return $this->namedQueries; + } + + /** + * Gets the named native query. + * + * @see ClassMetadataInfo::$namedNativeQueries + * + * @param string $queryName The query name. + * + * @return array + * + * @throws MappingException + */ + public function getNamedNativeQuery($queryName) + { + if ( ! isset($this->namedNativeQueries[$queryName])) { + throw MappingException::queryNotFound($this->name, $queryName); + } + + return $this->namedNativeQueries[$queryName]; + } + + /** + * Gets all named native queries of the class. + * + * @return array + */ + public function getNamedNativeQueries() + { + return $this->namedNativeQueries; + } + + /** + * Gets the result set mapping. + * + * @see ClassMetadataInfo::$sqlResultSetMappings + * + * @param string $name The result set mapping name. + * + * @return array + * + * @throws MappingException + */ + public function getSqlResultSetMapping($name) + { + if ( ! isset($this->sqlResultSetMappings[$name])) { + throw MappingException::resultMappingNotFound($this->name, $name); + } + + return $this->sqlResultSetMappings[$name]; + } + + /** + * Gets all sql result set mappings of the class. + * + * @return array + */ + public function getSqlResultSetMappings() + { + return $this->sqlResultSetMappings; + } + + /** + * Validates & completes the given field mapping. + * + * @param array $mapping The field mapping to validate & complete. + * + * @return array The validated and completed field mapping. + * + * @throws MappingException + */ + protected function _validateAndCompleteFieldMapping(array &$mapping) + { + // Check mandatory fields + if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['type'])) { + // Default to string + $mapping['type'] = 'string'; + } + + // Complete fieldName and columnName mapping + if ( ! isset($mapping['columnName'])) { + $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name); + } + + if ($mapping['columnName'][0] === '`') { + $mapping['columnName'] = trim($mapping['columnName'], '`'); + $mapping['quoted'] = true; + } + + $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; + if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) { + throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); + } + + $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if ($this->versionField == $mapping['fieldName']) { + throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']); + } + + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + $this->identifier[] = $mapping['fieldName']; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) { + if (isset($mapping['id']) && $mapping['id'] === true) { + throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']); + } + + $mapping['requireSQLConversion'] = true; + } + } + + /** + * Validates & completes the basic mapping information that is common to all + * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many). + * + * @param array $mapping The mapping. + * + * @return array The updated mapping. + * + * @throws MappingException If something is wrong with the mapping. + */ + protected function _validateAndCompleteAssociationMapping(array $mapping) + { + if ( ! isset($mapping['mappedBy'])) { + $mapping['mappedBy'] = null; + } + if ( ! isset($mapping['inversedBy'])) { + $mapping['inversedBy'] = null; + } + $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy + + // unset optional indexBy attribute if its empty + if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) { + unset($mapping['indexBy']); + } + + // If targetEntity is unqualified, assume it is in the same namespace as + // the sourceEntity. + $mapping['sourceEntity'] = $this->name; + + if (isset($mapping['targetEntity'])) { + $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']); + $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\'); + } + + if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 && + isset($mapping['orphanRemoval']) && + $mapping['orphanRemoval'] == true) { + + throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']); + } + + // Complete id mapping + if (isset($mapping['id']) && $mapping['id'] === true) { + if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) { + throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']); + } + + if ( ! in_array($mapping['fieldName'], $this->identifier)) { + if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) { + throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId( + $mapping['targetEntity'], $this->name, $mapping['fieldName'] + ); + } + + $this->identifier[] = $mapping['fieldName']; + $this->containsForeignIdentifier = true; + } + // Check for composite key + if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) { + $this->isIdentifierComposite = true; + } + } + + // Mandatory attributes for both sides + // Mandatory: fieldName, targetEntity + if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) { + throw MappingException::missingFieldName($this->name); + } + if ( ! isset($mapping['targetEntity'])) { + throw MappingException::missingTargetEntity($mapping['fieldName']); + } + + // Mandatory and optional attributes for either side + if ( ! $mapping['mappedBy']) { + if (isset($mapping['joinTable']) && $mapping['joinTable']) { + if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') { + $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); + $mapping['joinTable']['quoted'] = true; + } + } + } else { + $mapping['isOwningSide'] = false; + } + + if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) { + throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']); + } + + // Fetch mode. Default fetch mode to LAZY, if not set. + if ( ! isset($mapping['fetch'])) { + $mapping['fetch'] = self::FETCH_LAZY; + } + + // Cascades + $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array(); + + if (in_array('all', $cascades)) { + $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach'); + } + + if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) { + throw MappingException::invalidCascadeOption( + array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))), + $this->name, + $mapping['fieldName'] + ); + } + + $mapping['cascade'] = $cascades; + $mapping['isCascadeRemove'] = in_array('remove', $cascades); + $mapping['isCascadePersist'] = in_array('persist', $cascades); + $mapping['isCascadeRefresh'] = in_array('refresh', $cascades); + $mapping['isCascadeMerge'] = in_array('merge', $cascades); + $mapping['isCascadeDetach'] = in_array('detach', $cascades); + + return $mapping; + } + + /** + * Validates & completes a one-to-one association mapping. + * + * @param array $mapping The mapping to validate & complete. + * + * @return array The validated & completed mapping. + * + * @throws RuntimeException + * @throws MappingException + */ + protected function _validateAndCompleteOneToOneMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { + $mapping['isOwningSide'] = true; + } + + if ($mapping['isOwningSide']) { + if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) { + // Apply default join column + $mapping['joinColumns'] = array(array( + 'name' => $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName() + )); + } + + $uniqueConstraintColumns = array(); + foreach ($mapping['joinColumns'] as &$joinColumn) { + if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) { + if (count($mapping['joinColumns']) == 1) { + if ( ! isset($mapping['id']) || ! $mapping['id']) { + $joinColumn['unique'] = true; + } + } else { + $uniqueConstraintColumns[] = $joinColumn['name']; + } + } + + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name); + } + + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($joinColumn['name'][0] === '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + + if ($joinColumn['referencedColumnName'][0] === '`') { + $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); + $joinColumn['quoted'] = true; + } + + $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName']) + ? $joinColumn['fieldName'] : $joinColumn['name']; + } + + if ($uniqueConstraintColumns) { + if ( ! $this->table) { + throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship."); + } + $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array( + 'columns' => $uniqueConstraintColumns + ); + } + + $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if ($mapping['orphanRemoval']) { + unset($mapping['unique']); + } + + if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) { + throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']); + } + + return $mapping; + } + + /** + * Validates & completes a one-to-many association mapping. + * + * @param array $mapping The mapping to validate and complete. + * + * @return array The validated and completed mapping. + * + * @throws MappingException + * @throws InvalidArgumentException + */ + protected function _validateAndCompleteOneToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + + // OneToMany-side MUST be inverse (must have mappedBy) + if ( ! isset($mapping['mappedBy'])) { + throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove']; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + /** + * Validates & completes a many-to-many association mapping. + * + * @param array $mapping The mapping to validate & complete. + * + * @return array The validated & completed mapping. + * + * @throws \InvalidArgumentException + */ + protected function _validateAndCompleteManyToManyMapping(array $mapping) + { + $mapping = $this->_validateAndCompleteAssociationMapping($mapping); + if ($mapping['isOwningSide']) { + // owning side MUST have a join table + if ( ! isset($mapping['joinTable']['name'])) { + $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']); + } + + $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity'] + && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns']))); + + if ( ! isset($mapping['joinTable']['joinColumns'])) { + $mapping['joinTable']['joinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { + $mapping['joinTable']['inverseJoinColumns'] = array(array( + 'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null), + 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), + 'onDelete' => 'CASCADE')); + } + + $mapping['joinTableColumns'] = array(); + + foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { + if (empty($joinColumn['name'])) { + $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']); + } + + if (empty($joinColumn['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($joinColumn['name'][0] === '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + + if ($joinColumn['referencedColumnName'][0] === '`') { + $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); + $joinColumn['quoted'] = true; + } + + if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + + $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $joinColumn['name']; + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { + if (empty($inverseJoinColumn['name'])) { + $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']); + } + + if (empty($inverseJoinColumn['referencedColumnName'])) { + $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); + } + + if ($inverseJoinColumn['name'][0] === '`') { + $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); + $inverseJoinColumn['quoted'] = true; + } + + if ($inverseJoinColumn['referencedColumnName'][0] === '`') { + $inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`'); + $inverseJoinColumn['quoted'] = true; + } + + if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') { + $mapping['isOnDeleteCascade'] = true; + } + + $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; + $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; + } + } + + $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false; + + if (isset($mapping['orderBy'])) { + if ( ! is_array($mapping['orderBy'])) { + throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy'])); + } + } + + return $mapping; + } + + /** + * {@inheritDoc} + */ + public function getIdentifierFieldNames() + { + return $this->identifier; + } + + /** + * Gets the name of the single id field. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + * + * @throws MappingException If the class has a composite primary key. + */ + public function getSingleIdentifierFieldName() + { + if ($this->isIdentifierComposite) { + throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name); + } + return $this->identifier[0]; + } + + /** + * Gets the column name of the single id column. Note that this only works on + * entity classes that have a single-field pk. + * + * @return string + * + * @throws MappingException If the class has a composite primary key. + */ + public function getSingleIdentifierColumnName() + { + return $this->getColumnName($this->getSingleIdentifierFieldName()); + } + + /** + * INTERNAL: + * Sets the mapped identifier/primary key fields of this class. + * Mainly used by the ClassMetadataFactory to assign inherited identifiers. + * + * @param array $identifier + * + * @return void + */ + public function setIdentifier(array $identifier) + { + $this->identifier = $identifier; + $this->isIdentifierComposite = (count($this->identifier) > 1); + } + + /** + * {@inheritDoc} + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritDoc} + */ + public function hasField($fieldName) + { + return isset($this->fieldMappings[$fieldName]); + } + + /** + * Gets an array containing all the column names. + * + * @param array|null $fieldNames + * + * @return array + */ + public function getColumnNames(array $fieldNames = null) + { + if ($fieldNames === null) { + return array_keys($this->fieldNames); + } else { + $columnNames = array(); + foreach ($fieldNames as $fieldName) { + $columnNames[] = $this->getColumnName($fieldName); + } + return $columnNames; + } + } + + /** + * Returns an array with all the identifier column names. + * + * @return array + */ + public function getIdentifierColumnNames() + { + $columnNames = array(); + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $columnNames[] = $this->fieldMappings[$idProperty]['columnName']; + + continue; + } + + // Association defined as Id field + $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; + $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns); + + $columnNames = array_merge($columnNames, $assocColumnNames); + } + + return $columnNames; + } + + /** + * Sets the type of Id generator to use for the mapped class. + * + * @param int $generatorType + * + * @return void + */ + public function setIdGeneratorType($generatorType) + { + $this->generatorType = $generatorType; + } + + /** + * Checks whether the mapped class uses an Id generator. + * + * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise. + */ + public function usesIdGenerator() + { + return $this->generatorType != self::GENERATOR_TYPE_NONE; + } + + /** + * @return boolean + */ + public function isInheritanceTypeNone() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_NONE; + } + + /** + * Checks whether the mapped class uses the JOINED inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a JOINED inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeJoined() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED; + } + + /** + * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeSingleTable() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE; + } + + /** + * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy. + * + * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping, + * FALSE otherwise. + */ + public function isInheritanceTypeTablePerClass() + { + return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Checks whether the class uses an identity column for the Id generation. + * + * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise. + */ + public function isIdGeneratorIdentity() + { + return $this->generatorType == self::GENERATOR_TYPE_IDENTITY; + } + + /** + * Checks whether the class uses a sequence for id generation. + * + * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise. + */ + public function isIdGeneratorSequence() + { + return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE; + } + + /** + * Checks whether the class uses a table for id generation. + * + * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise. + */ + public function isIdGeneratorTable() + { + return $this->generatorType == self::GENERATOR_TYPE_TABLE; + } + + /** + * Checks whether the class has a natural identifier/pk (which means it does + * not use any Id generator. + * + * @return boolean + */ + public function isIdentifierNatural() + { + return $this->generatorType == self::GENERATOR_TYPE_NONE; + } + + /** + * Checks whether the class use a UUID for id generation. + * + * @return boolean + */ + public function isIdentifierUuid() + { + return $this->generatorType == self::GENERATOR_TYPE_UUID; + } + + /** + * Gets the type of a field. + * + * @param string $fieldName + * + * @return \Doctrine\DBAL\Types\Type|string|null + */ + public function getTypeOfField($fieldName) + { + return isset($this->fieldMappings[$fieldName]) ? + $this->fieldMappings[$fieldName]['type'] : null; + } + + /** + * Gets the type of a column. + * + * @param string $columnName + * + * @return \Doctrine\DBAL\Types\Type|string|null + * + * @deprecated this method is bogous and unreliable, since it cannot resolve the type of a column that is + * derived by a referenced field on a different entity. + */ + public function getTypeOfColumn($columnName) + { + return $this->getTypeOfField($this->getFieldName($columnName)); + } + + /** + * Gets the name of the primary table. + * + * @return string + */ + public function getTableName() + { + return $this->table['name']; + } + + /** + * Gets primary table's schema name. + * + * @return string|null + */ + public function getSchemaName() + { + return isset($this->table['schema']) ? $this->table['schema'] : null; + } + + /** + * Gets the table name to use for temporary identifier tables of this class. + * + * @return string + */ + public function getTemporaryIdTableName() + { + // replace dots with underscores because PostgreSQL creates temporary tables in a special schema + return str_replace('.', '_', $this->getTableName() . '_id_tmp'); + } + + /** + * Sets the mapped subclasses of this class. + * + * @param array $subclasses The names of all mapped subclasses. + * + * @return void + */ + public function setSubclasses(array $subclasses) + { + foreach ($subclasses as $subclass) { + $this->subClasses[] = $this->fullyQualifiedClassName($subclass); + } + } + + /** + * Sets the parent class names. + * Assumes that the class names in the passed array are in the order: + * directParent -> directParentParent -> directParentParentParent ... -> root. + * + * @param array $classNames + * + * @return void + */ + public function setParentClasses(array $classNames) + { + $this->parentClasses = $classNames; + if (count($classNames) > 0) { + $this->rootEntityName = array_pop($classNames); + } + } + + /** + * Sets the inheritance type used by the class and its subclasses. + * + * @param integer $type + * + * @return void + * + * @throws MappingException + */ + public function setInheritanceType($type) + { + if ( ! $this->_isInheritanceType($type)) { + throw MappingException::invalidInheritanceType($this->name, $type); + } + $this->inheritanceType = $type; + } + + /** + * Sets the association to override association mapping of property for an entity relationship. + * + * @param string $fieldName + * @param array $overrideMapping + * + * @return void + * + * @throws MappingException + */ + public function setAssociationOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->associationMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->associationMappings[$fieldName]; + + if (isset($overrideMapping['joinColumns'])) { + $mapping['joinColumns'] = $overrideMapping['joinColumns']; + } + + if (isset($overrideMapping['joinTable'])) { + $mapping['joinTable'] = $overrideMapping['joinTable']; + } + + $mapping['joinColumnFieldNames'] = null; + $mapping['joinTableColumns'] = null; + $mapping['sourceToTargetKeyColumns'] = null; + $mapping['relationToSourceKeyColumns'] = null; + $mapping['relationToTargetKeyColumns'] = null; + + switch ($mapping['type']) { + case self::ONE_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::ONE_TO_MANY: + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + break; + case self::MANY_TO_ONE: + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + break; + case self::MANY_TO_MANY: + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + break; + } + + $this->associationMappings[$fieldName] = $mapping; + } + + /** + * Sets the override for a mapped field. + * + * @param string $fieldName + * @param array $overrideMapping + * + * @return void + * + * @throws MappingException + */ + public function setAttributeOverride($fieldName, array $overrideMapping) + { + if ( ! isset($this->fieldMappings[$fieldName])) { + throw MappingException::invalidOverrideFieldName($this->name, $fieldName); + } + + $mapping = $this->fieldMappings[$fieldName]; + + if (isset($mapping['id'])) { + $overrideMapping['id'] = $mapping['id']; + } + + if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) { + $overrideMapping['type'] = $mapping['type']; + } + + if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) { + $overrideMapping['fieldName'] = $mapping['fieldName']; + } + + if ($overrideMapping['type'] !== $mapping['type']) { + throw MappingException::invalidOverrideFieldType($this->name, $fieldName); + } + + unset($this->fieldMappings[$fieldName]); + unset($this->fieldNames[$mapping['columnName']]); + unset($this->columnNames[$mapping['fieldName']]); + $this->_validateAndCompleteFieldMapping($overrideMapping); + + $this->fieldMappings[$fieldName] = $overrideMapping; + } + + /** + * Checks whether a mapped field is inherited from an entity superclass. + * + * @param string $fieldName + * + * @return bool TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedField($fieldName) + { + return isset($this->fieldMappings[$fieldName]['inherited']); + } + + /** + * Checks if this entity is the root in any entity-inheritance-hierarchy. + * + * @return bool + */ + public function isRootEntity() + { + return $this->name == $this->rootEntityName; + } + + /** + * Checks whether a mapped association field is inherited from a superclass. + * + * @param string $fieldName + * + * @return boolean TRUE if the field is inherited, FALSE otherwise. + */ + public function isInheritedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]['inherited']); + } + + public function isInheritedEmbeddedClass($fieldName) + { + return isset($this->embeddedClasses[$fieldName]['inherited']); + } + + /** + * Sets the name of the primary table the class is mapped to. + * + * @param string $tableName The table name. + * + * @return void + * + * @deprecated Use {@link setPrimaryTable}. + */ + public function setTableName($tableName) + { + $this->table['name'] = $tableName; + } + + /** + * Sets the primary table definition. The provided array supports the + * following structure: + * + * name => (optional, defaults to class name) + * indexes => array of indexes (optional) + * uniqueConstraints => array of constraints (optional) + * + * If a key is omitted, the current value is kept. + * + * @param array $table The table description. + * + * @return void + */ + public function setPrimaryTable(array $table) + { + if (isset($table['name'])) { + // Split schema and table name from a table name like "myschema.mytable" + if (strpos($table['name'], '.') !== false) { + list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2); + } + + if ($table['name'][0] === '`') { + $table['name'] = trim($table['name'], '`'); + $this->table['quoted'] = true; + } + + $this->table['name'] = $table['name']; + } + + if (isset($table['schema'])) { + $this->table['schema'] = $table['schema']; + } + + if (isset($table['indexes'])) { + $this->table['indexes'] = $table['indexes']; + } + + if (isset($table['uniqueConstraints'])) { + $this->table['uniqueConstraints'] = $table['uniqueConstraints']; + } + + if (isset($table['options'])) { + $this->table['options'] = $table['options']; + } + } + + /** + * Checks whether the given type identifies an inheritance type. + * + * @param integer $type + * + * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise. + */ + private function _isInheritanceType($type) + { + return $type == self::INHERITANCE_TYPE_NONE || + $type == self::INHERITANCE_TYPE_SINGLE_TABLE || + $type == self::INHERITANCE_TYPE_JOINED || + $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS; + } + + /** + * Adds a mapped field to the class. + * + * @param array $mapping The field mapping. + * + * @return void + * + * @throws MappingException + */ + public function mapField(array $mapping) + { + $this->_validateAndCompleteFieldMapping($mapping); + $this->assertFieldNotMapped($mapping['fieldName']); + + $this->fieldMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds an association mapping without completing/validating it. + * This is mainly used to add inherited association mappings to derived classes. + * + * @param array $mapping + * + * @return void + * + * @throws MappingException + */ + public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/) + { + if (isset($this->associationMappings[$mapping['fieldName']])) { + throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']); + } + $this->associationMappings[$mapping['fieldName']] = $mapping; + } + + /** + * INTERNAL: + * Adds a field mapping without completing/validating it. + * This is mainly used to add inherited field mappings to derived classes. + * + * @param array $fieldMapping + * + * @return void + */ + public function addInheritedFieldMapping(array $fieldMapping) + { + $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; + $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; + $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; + } + + /** + * INTERNAL: + * Adds a named query to this class. + * + * @param array $queryMapping + * + * @return void + * + * @throws MappingException + */ + public function addNamedQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + + $name = $queryMapping['name']; + $query = $queryMapping['query']; + $dql = str_replace('__CLASS__', $this->name, $query); + $this->namedQueries[$name] = array( + 'name' => $name, + 'query' => $query, + 'dql' => $dql + ); + } + + /** + * INTERNAL: + * Adds a named native query to this class. + * + * @param array $queryMapping + * + * @return void + * + * @throws MappingException + */ + public function addNamedNativeQuery(array $queryMapping) + { + if (!isset($queryMapping['name'])) { + throw MappingException::nameIsMandatoryForQueryMapping($this->name); + } + + if (isset($this->namedNativeQueries[$queryMapping['name']])) { + throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['query'])) { + throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); + } + + if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) { + throw MappingException::missingQueryMapping($this->name, $queryMapping['name']); + } + + $queryMapping['isSelfClass'] = false; + if (isset($queryMapping['resultClass'])) { + + if ($queryMapping['resultClass'] === '__CLASS__') { + + $queryMapping['isSelfClass'] = true; + $queryMapping['resultClass'] = $this->name; + } + + $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']); + $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); + } + + $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; + } + + /** + * INTERNAL: + * Adds a sql result set mapping to this class. + * + * @param array $resultMapping + * + * @return void + * + * @throws MappingException + */ + public function addSqlResultSetMapping(array $resultMapping) + { + if (!isset($resultMapping['name'])) { + throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name); + } + + if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { + throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']); + } + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityResult) { + if (!isset($entityResult['entityClass'])) { + throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']); + } + + $entityResult['isSelfClass'] = false; + if ($entityResult['entityClass'] === '__CLASS__') { + + $entityResult['isSelfClass'] = true; + $entityResult['entityClass'] = $this->name; + + } + + $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']); + + $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); + $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; + + if (isset($entityResult['fields'])) { + foreach ($entityResult['fields'] as $k => $field) { + if (!isset($field['name'])) { + throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']); + } + + if (!isset($field['column'])) { + $fieldName = $field['name']; + if (strpos($fieldName, '.')) { + list(, $fieldName) = explode('.', $fieldName); + } + + $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; + } + } + } + } + } + + $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; + } + + /** + * Adds a one-to-one mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapOneToOne(array $mapping) + { + $mapping['type'] = self::ONE_TO_ONE; + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a one-to-many mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapOneToMany(array $mapping) + { + $mapping['type'] = self::ONE_TO_MANY; + $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-one mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapManyToOne(array $mapping) + { + $mapping['type'] = self::MANY_TO_ONE; + // A many-to-one mapping is essentially a one-one backreference + $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Adds a many-to-many mapping. + * + * @param array $mapping The mapping. + * + * @return void + */ + public function mapManyToMany(array $mapping) + { + $mapping['type'] = self::MANY_TO_MANY; + $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); + $this->_storeAssociationMapping($mapping); + } + + /** + * Stores the association mapping. + * + * @param array $assocMapping + * + * @return void + * + * @throws MappingException + */ + protected function _storeAssociationMapping(array $assocMapping) + { + $sourceFieldName = $assocMapping['fieldName']; + + $this->assertFieldNotMapped($sourceFieldName); + + $this->associationMappings[$sourceFieldName] = $assocMapping; + } + + /** + * Registers a custom repository class for the entity class. + * + * @param string $repositoryClassName The class name of the custom mapper. + * + * @return void + */ + public function setCustomRepositoryClass($repositoryClassName) + { + $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName); + } + + /** + * Dispatches the lifecycle event of the given entity to the registered + * lifecycle callbacks and lifecycle listeners. + * + * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker + * + * @param string $lifecycleEvent The lifecycle event. + * @param object $entity The Entity on which the event occurred. + * + * @return void + */ + public function invokeLifecycleCallbacks($lifecycleEvent, $entity) + { + foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { + $entity->$callback(); + } + } + + /** + * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event. + * + * @param string $lifecycleEvent + * + * @return boolean + */ + public function hasLifecycleCallbacks($lifecycleEvent) + { + return isset($this->lifecycleCallbacks[$lifecycleEvent]); + } + + /** + * Gets the registered lifecycle callbacks for an event. + * + * @param string $event + * + * @return array + */ + public function getLifecycleCallbacks($event) + { + return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array(); + } + + /** + * Adds a lifecycle callback for entities of this class. + * + * @param string $callback + * @param string $event + * + * @return void + */ + public function addLifecycleCallback($callback, $event) + { + if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) { + return; + } + + $this->lifecycleCallbacks[$event][] = $callback; + } + + /** + * Sets the lifecycle callbacks for entities of this class. + * Any previously registered callbacks are overwritten. + * + * @param array $callbacks + * + * @return void + */ + public function setLifecycleCallbacks(array $callbacks) + { + $this->lifecycleCallbacks = $callbacks; + } + + /** + * Adds a entity listener for entities of this class. + * + * @param string $eventName The entity lifecycle event. + * @param string $class The listener class. + * @param string $method The listener callback method. + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function addEntityListener($eventName, $class, $method) + { + $class = $this->fullyQualifiedClassName($class); + $listener = array( + 'class' => $class, + 'method' => $method + ); + + if ( ! class_exists($class)) { + throw MappingException::entityListenerClassNotFound($class, $this->name); + } + + if ( ! method_exists($class, $method)) { + throw MappingException::entityListenerMethodNotFound($class, $method, $this->name); + } + + if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) { + throw MappingException::duplicateEntityListener($class, $method, $this->name); + } + + $this->entityListeners[$eventName][] = $listener; + } + + /** + * Sets the discriminator column definition. + * + * @param array $columnDef + * + * @return void + * + * @throws MappingException + * + * @see getDiscriminatorColumn() + */ + public function setDiscriminatorColumn($columnDef) + { + if ($columnDef !== null) { + if ( ! isset($columnDef['name'])) { + throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); + } + + if (isset($this->fieldNames[$columnDef['name']])) { + throw MappingException::duplicateColumnName($this->name, $columnDef['name']); + } + + if ( ! isset($columnDef['fieldName'])) { + $columnDef['fieldName'] = $columnDef['name']; + } + + if ( ! isset($columnDef['type'])) { + $columnDef['type'] = "string"; + } + + if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) { + throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); + } + + $this->discriminatorColumn = $columnDef; + } + } + + /** + * Sets the discriminator values used by this class. + * Used for JOINED and SINGLE_TABLE inheritance mapping strategies. + * + * @param array $map + * + * @return void + */ + public function setDiscriminatorMap(array $map) + { + foreach ($map as $value => $className) { + $this->addDiscriminatorMapClass($value, $className); + } + } + + /** + * Adds one entry of the discriminator map with a new class and corresponding name. + * + * @param string $name + * @param string $className + * + * @return void + * + * @throws MappingException + */ + public function addDiscriminatorMapClass($name, $className) + { + $className = $this->fullyQualifiedClassName($className); + $className = ltrim($className, '\\'); + $this->discriminatorMap[$name] = $className; + + if ($this->name === $className) { + $this->discriminatorValue = $name; + + return; + } + + if ( ! (class_exists($className) || interface_exists($className))) { + throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); + } + + if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) { + $this->subClasses[] = $className; + } + } + + /** + * Checks whether the class has a named query with the given query name. + * + * @param string $queryName + * + * @return boolean + */ + public function hasNamedQuery($queryName) + { + return isset($this->namedQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $queryName + * + * @return boolean + */ + public function hasNamedNativeQuery($queryName) + { + return isset($this->namedNativeQueries[$queryName]); + } + + /** + * Checks whether the class has a named native query with the given query name. + * + * @param string $name + * + * @return boolean + */ + public function hasSqlResultSetMapping($name) + { + return isset($this->sqlResultSetMappings[$name]); + } + + /** + * {@inheritDoc} + */ + public function hasAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]); + } + + /** + * {@inheritDoc} + */ + public function isSingleValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * {@inheritDoc} + */ + public function isCollectionValuedAssociation($fieldName) + { + return isset($this->associationMappings[$fieldName]) && + ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE); + } + + /** + * Is this an association that only has a single join column? + * + * @param string $fieldName + * + * @return bool + */ + public function isAssociationWithSingleJoinColumn($fieldName) + { + return ( + isset($this->associationMappings[$fieldName]) && + isset($this->associationMappings[$fieldName]['joinColumns'][0]) && + !isset($this->associationMappings[$fieldName]['joinColumns'][1]) + ); + } + + /** + * Returns the single association join column (if any). + * + * @param string $fieldName + * + * @return string + * + * @throws MappingException + */ + public function getSingleAssociationJoinColumnName($fieldName) + { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; + } + + /** + * Returns the single association referenced join column name (if any). + * + * @param string $fieldName + * + * @return string + * + * @throws MappingException + */ + public function getSingleAssociationReferencedJoinColumnName($fieldName) + { + if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) { + throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); + } + return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName']; + } + + /** + * Used to retrieve a fieldname for either field or association from a given column. + * + * This method is used in foreign-key as primary-key contexts. + * + * @param string $columnName + * + * @return string + * + * @throws MappingException + */ + public function getFieldForColumn($columnName) + { + if (isset($this->fieldNames[$columnName])) { + return $this->fieldNames[$columnName]; + } else { + foreach ($this->associationMappings as $assocName => $mapping) { + if ($this->isAssociationWithSingleJoinColumn($assocName) && + $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) { + + return $assocName; + } + } + + throw MappingException::noFieldNameFoundForColumn($this->name, $columnName); + } + } + + /** + * Sets the ID generator used to generate IDs for instances of this class. + * + * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator + * + * @return void + */ + public function setIdGenerator($generator) + { + $this->idGenerator = $generator; + } + + /** + * Sets definition. + * + * @param array $definition + * + * @return void + */ + public function setCustomGeneratorDefinition(array $definition) + { + $this->customGeneratorDefinition = $definition; + } + + /** + * Sets the definition of the sequence ID generator for this class. + * + * The definition must have the following structure: + * + * array( + * 'sequenceName' => 'name', + * 'allocationSize' => 20, + * 'initialValue' => 1 + * 'quoted' => 1 + * ) + * + * + * @param array $definition + * + * @return void + */ + public function setSequenceGeneratorDefinition(array $definition) + { + if ( ! isset($definition['sequenceName'])) { + throw MappingException::missingSequenceName($this->name); + } + + if ($definition['sequenceName'][0] == '`') { + $definition['sequenceName'] = trim($definition['sequenceName'], '`'); + $definition['quoted'] = true; + } + + $this->sequenceGeneratorDefinition = $definition; + } + + /** + * Sets the version field mapping used for versioning. Sets the default + * value to use depending on the column type. + * + * @param array $mapping The version field mapping array. + * + * @return void + * + * @throws MappingException + */ + public function setVersionMapping(array &$mapping) + { + $this->isVersioned = true; + $this->versionField = $mapping['fieldName']; + + if ( ! isset($mapping['default'])) { + if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) { + $mapping['default'] = 1; + } else if ($mapping['type'] == 'datetime') { + $mapping['default'] = 'CURRENT_TIMESTAMP'; + } else { + throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); + } + } + } + + /** + * Sets whether this class is to be versioned for optimistic locking. + * + * @param boolean $bool + * + * @return void + */ + public function setVersioned($bool) + { + $this->isVersioned = $bool; + } + + /** + * Sets the name of the field that is to be used for versioning if this class is + * versioned for optimistic locking. + * + * @param string $versionField + * + * @return void + */ + public function setVersionField($versionField) + { + $this->versionField = $versionField; + } + + /** + * Marks this class as read only, no change tracking is applied to it. + * + * @return void + */ + public function markReadOnly() + { + $this->isReadOnly = true; + } + + /** + * {@inheritDoc} + */ + public function getFieldNames() + { + return array_keys($this->fieldMappings); + } + + /** + * {@inheritDoc} + */ + public function getAssociationNames() + { + return array_keys($this->associationMappings); + } + + /** + * {@inheritDoc} + * + * @throws InvalidArgumentException + */ + public function getAssociationTargetClass($assocName) + { + if ( ! isset($this->associationMappings[$assocName])) { + throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association."); + } + + return $this->associationMappings[$assocName]['targetEntity']; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return array + */ + public function getQuotedIdentifierColumnNames($platform) + { + $quotedColumnNames = array(); + + foreach ($this->identifier as $idProperty) { + if (isset($this->fieldMappings[$idProperty])) { + $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted']) + ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName']) + : $this->fieldMappings[$idProperty]['columnName']; + + continue; + } + + // Association defined as Id field + $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) use ($platform) { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * Gets the (possibly quoted) column name of a mapped field for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param string $field + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedColumnName($field, $platform) + { + return isset($this->fieldMappings[$field]['quoted']) + ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) + : $this->fieldMappings[$field]['columnName']; + } + + /** + * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedTableName($platform) + { + return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name']; + } + + /** + * Gets the (possibly quoted) name of the join table. + * + * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy + * + * @param array $assoc + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * + * @return string + */ + public function getQuotedJoinTableName(array $assoc, $platform) + { + return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name']; + } + + /** + * {@inheritDoc} + */ + public function isAssociationInverseSide($fieldName) + { + return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide']; + } + + /** + * {@inheritDoc} + */ + public function getAssociationMappedByTargetField($fieldName) + { + return $this->associationMappings[$fieldName]['mappedBy']; + } + + /** + * @param string $targetClass + * + * @return array + */ + public function getAssociationsByTargetClass($targetClass) + { + $relations = array(); + foreach ($this->associationMappings as $mapping) { + if ($mapping['targetEntity'] == $targetClass) { + $relations[$mapping['fieldName']] = $mapping; + } + } + return $relations; + } + + /** + * @param string|null $className + * @return string|null null if the input value is null + */ + public function fullyQualifiedClassName($className) + { + if (empty($className)) { + return $className; + } + + if ($className !== null && strpos($className, '\\') === false && strlen($this->namespace) > 0) { + return $this->namespace . '\\' . $className; + } + + return $className; + } + + /** + * @param string $name + * + * @return mixed + */ + public function getMetadataValue($name) { + + if (isset($this->$name)) { + return $this->$name; + } + + return null; + } + + /** + * Map Embedded Class + * + * @param array $mapping + * @throws MappingException + * @return void + */ + public function mapEmbedded(array $mapping) + { + $this->assertFieldNotMapped($mapping['fieldName']); + + $this->embeddedClasses[$mapping['fieldName']] = array( + 'class' => $this->fullyQualifiedClassName($mapping['class']), + 'columnPrefix' => $mapping['columnPrefix'], + 'declaredField' => isset($mapping['declaredField']) ? $mapping['declaredField'] : null, + 'originalField' => isset($mapping['originalField']) ? $mapping['originalField'] : null, + ); + } + + /** + * Inline the embeddable class + * + * @param string $property + * @param ClassMetadataInfo $embeddable + */ + public function inlineEmbeddable($property, ClassMetadataInfo $embeddable) + { + foreach ($embeddable->fieldMappings as $fieldMapping) { + $fieldMapping['originalClass'] = isset($fieldMapping['originalClass']) + ? $fieldMapping['originalClass'] + : $embeddable->name; + $fieldMapping['declaredField'] = isset($fieldMapping['declaredField']) + ? $property . '.' . $fieldMapping['declaredField'] + : $property; + $fieldMapping['originalField'] = isset($fieldMapping['originalField']) + ? $fieldMapping['originalField'] + : $fieldMapping['fieldName']; + $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName']; + + if (! empty($this->embeddedClasses[$property]['columnPrefix'])) { + $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName']; + } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) { + $fieldMapping['columnName'] = $this->namingStrategy + ->embeddedFieldToColumnName( + $property, + $fieldMapping['columnName'], + $this->reflClass->name, + $embeddable->reflClass->name + ); + } + + $this->mapField($fieldMapping); + } + } + + /** + * @param string $fieldName + * @throws MappingException + */ + private function assertFieldNotMapped($fieldName) + { + if (isset($this->fieldMappings[$fieldName]) || + isset($this->associationMappings[$fieldName]) || + isset($this->embeddedClasses[$fieldName])) { + + throw MappingException::duplicateFieldMapping($this->name, $fieldName); + } + } + + /** + * Gets the sequence name based on class metadata. + * + * @param AbstractPlatform $platform + * @return string + * + * @todo Sequence names should be computed in DBAL depending on the platform + */ + public function getSequenceName(AbstractPlatform $platform) + { + $sequencePrefix = $this->getSequencePrefix($platform); + + $columnName = $this->getSingleIdentifierColumnName(); + $sequenceName = $sequencePrefix . '_' . $columnName . '_seq'; + + return $sequenceName; + } + + /** + * Gets the sequence name prefix based on class metadata. + * + * @param AbstractPlatform $platform + * @return string + * + * @todo Sequence names should be computed in DBAL depending on the platform + */ + public function getSequencePrefix(AbstractPlatform $platform) + { + $tableName = $this->getTableName(); + $sequencePrefix = $tableName; + + // Prepend the schema name to the table name if there is one + if ($schemaName = $this->getSchemaName()) { + $sequencePrefix = $schemaName . '.' . $tableName; + + if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) { + $sequencePrefix = $schemaName . '__' . $tableName; + } + } + + return $sequencePrefix; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php new file mode 100644 index 0000000..7033732 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Column.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class Column implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var mixed + */ + public $type = 'string'; + + /** + * @var integer + */ + public $length; + + /** + * The precision for a decimal (exact numeric) column (Applies only for decimal column). + * + * @var integer + */ + public $precision = 0; + + /** + * The scale for a decimal (exact numeric) column (Applies only for decimal column). + * + * @var integer + */ + public $scale = 0; + + /** + * @var boolean + */ + public $unique = false; + + /** + * @var boolean + */ + public $nullable = false; + + /** + * @var array + */ + public $options = array(); + + /** + * @var string + */ + public $columnDefinition; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php new file mode 100644 index 0000000..a164c85 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ColumnResult.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References name of a column in the SELECT clause of a SQL query. + * Scalar result types can be included in the query result by specifying this annotation in the metadata. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class ColumnResult implements Annotation +{ + /** + * The name of a column in the SELECT clause of a SQL query. + * + * @var string + */ + public $name; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php new file mode 100644 index 0000000..41e200e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/CustomIdGenerator.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class CustomIdGenerator implements Annotation +{ + /** + * @var string + */ + public $class; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php new file mode 100644 index 0000000..7565854 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultEntityListenerResolver.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The default DefaultEntityListener + * + * @since 2.4 + * @author Fabio B. Silva + */ +class DefaultEntityListenerResolver implements EntityListenerResolver +{ + /** + * @var array Map to store entity listener instances. + */ + private $instances = array(); + + /** + * {@inheritdoc} + */ + public function clear($className = null) + { + if ($className === null) { + $this->instances = array(); + + return; + } + + if (isset($this->instances[$className = trim($className, '\\')])) { + unset($this->instances[$className]); + } + } + + /** + * {@inheritdoc} + */ + public function register($object) + { + if ( ! is_object($object)) { + throw new \InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object))); + } + + $this->instances[get_class($object)] = $object; + } + + /** + * {@inheritdoc} + */ + public function resolve($className) + { + if (isset($this->instances[$className = trim($className, '\\')])) { + return $this->instances[$className]; + } + + return $this->instances[$className] = new $className(); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php new file mode 100644 index 0000000..1e75f97 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultNamingStrategy.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The default NamingStrategy + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class DefaultNamingStrategy implements NamingStrategy +{ + /** + * {@inheritdoc} + */ + public function classToTableName($className) + { + if (strpos($className, '\\') !== false) { + return substr($className, strrpos($className, '\\') + 1); + } + + return $className; + } + + /** + * {@inheritdoc} + */ + public function propertyToColumnName($propertyName, $className = null) + { + return $propertyName; + } + + /** + * {@inheritdoc} + */ + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) + { + return $propertyName.'_'.$embeddedColumnName; + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName, $className = null) + { + return $propertyName . '_' . $this->referenceColumnName(); + } + + /** + * {@inheritdoc} + */ + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return strtolower($this->classToTableName($sourceEntity) . '_' . + $this->classToTableName($targetEntity)); + } + + /** + * {@inheritdoc} + */ + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return strtolower($this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName())); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php new file mode 100644 index 0000000..dfbded8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the physical column, alias and table quotes + * + * @since 2.3 + * @author Fabio B. Silva + */ +class DefaultQuoteStrategy implements QuoteStrategy +{ + /** + * {@inheritdoc} + */ + public function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($class->fieldMappings[$fieldName]['quoted']) + ? $platform->quoteIdentifier($class->fieldMappings[$fieldName]['columnName']) + : $class->fieldMappings[$fieldName]['columnName']; + } + + /** + * {@inheritdoc} + * + * @todo Table names should be computed in DBAL depending on the platform + */ + public function getTableName(ClassMetadata $class, AbstractPlatform $platform) + { + $tableName = $class->table['name']; + + if ( ! empty($class->table['schema'])) { + $tableName = $class->table['schema'] . '.' . $class->table['name']; + + if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) { + $tableName = $class->table['schema'] . '__' . $class->table['name']; + } + } + + return isset($class->table['quoted']) + ? $platform->quoteIdentifier($tableName) + : $tableName; + } + + /** + * {@inheritdoc} + */ + public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($definition['quoted']) + ? $platform->quoteIdentifier($definition['sequenceName']) + : $definition['sequenceName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + } + + /** + * {@inheritdoc} + */ + public function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['referencedColumnName']) + : $joinColumn['referencedColumnName']; + } + + /** + * {@inheritdoc} + */ + public function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform) + { + $schema = ''; + + if (isset($association['joinTable']['schema'])) { + $schema = $association['joinTable']['schema'] . '.'; + } + + $tableName = $association['joinTable']['name']; + + if (isset($association['joinTable']['quoted'])) { + $tableName = $platform->quoteIdentifier($tableName); + } + + return $schema . $tableName; + } + + /** + * {@inheritdoc} + */ + public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform) + { + $quotedColumnNames = array(); + + foreach ($class->identifier as $fieldName) { + if (isset($class->fieldMappings[$fieldName])) { + $quotedColumnNames[] = $this->getColumnName($fieldName, $class, $platform); + + continue; + } + + // Association defined as Id field + $joinColumns = $class->associationMappings[$fieldName]['joinColumns']; + $assocQuotedColumnNames = array_map( + function ($joinColumn) use ($platform) + { + return isset($joinColumn['quoted']) + ? $platform->quoteIdentifier($joinColumn['name']) + : $joinColumn['name']; + }, + $joinColumns + ); + + $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); + } + + return $quotedColumnNames; + } + + /** + * {@inheritdoc} + */ + public function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null) + { + // 1 ) Concatenate column name and counter + // 2 ) Trim the column alias to the maximum identifier length of the platform. + // If the alias is to long, characters are cut off from the beginning. + // 3 ) Strip non alphanumeric characters + // 4 ) Prefix with "_" if the result its numeric + $columnName = $columnName . '_' . $counter; + $columnName = substr($columnName, -$platform->getMaxIdentifierLength()); + $columnName = preg_replace('/[^A-Za-z0-9_]/', '', $columnName); + $columnName = is_numeric($columnName) ? '_' . $columnName : $columnName; + + return $platform->getSQLResultCasing($columnName); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php new file mode 100644 index 0000000..97ca7e9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorColumn implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var integer + */ + public $length; + + /** + * Field name used in non-object hydration (array/scalar). + * + * @var mixed + */ + public $fieldName; + + /** + * @var string + */ + public $columnDefinition; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php new file mode 100644 index 0000000..09d6194 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DiscriminatorMap.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class DiscriminatorMap implements Annotation +{ + /** + * @var array + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php new file mode 100644 index 0000000..df8b7b8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -0,0 +1,638 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\Mapping\JoinColumn; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; +use Doctrine\ORM\Events; + +/** + * The AnnotationDriver reads the mapping metadata from docblock annotations. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class AnnotationDriver extends AbstractAnnotationDriver +{ + /** + * {@inheritDoc} + */ + protected $entityAnnotationClasses = array( + 'Doctrine\ORM\Mapping\Entity' => 1, + 'Doctrine\ORM\Mapping\MappedSuperclass' => 2, + ); + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + $class = $metadata->getReflectionClass(); + if ( ! $class) { + // this happens when running annotation driver in combination with + // static reflection services. This is not the nicest fix + $class = new \ReflectionClass($metadata->name); + } + + $classAnnotations = $this->reader->getClassAnnotations($class); + + if ($classAnnotations) { + foreach ($classAnnotations as $key => $annot) { + if ( ! is_numeric($key)) { + continue; + } + + $classAnnotations[get_class($annot)] = $annot; + } + } + + // Evaluate Entity annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) { + $entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity']; + if ($entityAnnot->repositoryClass !== null) { + $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); + } + if ($entityAnnot->readOnly) { + $metadata->markReadOnly(); + } + } else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) { + $mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass']; + $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass); + $metadata->isMappedSuperclass = true; + } else if (isset($classAnnotations['Doctrine\ORM\Mapping\Embeddable'])) { + $metadata->isEmbeddedClass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate Table annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) { + $tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table']; + $primaryTable = array( + 'name' => $tableAnnot->name, + 'schema' => $tableAnnot->schema + ); + + if ($tableAnnot->indexes !== null) { + foreach ($tableAnnot->indexes as $indexAnnot) { + $index = array('columns' => $indexAnnot->columns); + + if ( ! empty($indexAnnot->flags)) { + $index['flags'] = $indexAnnot->flags; + } + + if ( ! empty($indexAnnot->options)) { + $index['options'] = $indexAnnot->options; + } + + if ( ! empty($indexAnnot->name)) { + $primaryTable['indexes'][$indexAnnot->name] = $index; + } else { + $primaryTable['indexes'][] = $index; + } + } + } + + if ($tableAnnot->uniqueConstraints !== null) { + foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { + $uniqueConstraint = array('columns' => $uniqueConstraintAnnot->columns); + + if ( ! empty($uniqueConstraintAnnot->options)) { + $uniqueConstraint['options'] = $uniqueConstraintAnnot->options; + } + + if ( ! empty($uniqueConstraintAnnot->name)) { + $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint; + } else { + $primaryTable['uniqueConstraints'][] = $uniqueConstraint; + } + } + } + + if ($tableAnnot->options) { + $primaryTable['options'] = $tableAnnot->options; + } + + $metadata->setPrimaryTable($primaryTable); + } + + // Evaluate @Cache annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\Cache'])) { + $cacheAnnot = $classAnnotations['Doctrine\ORM\Mapping\Cache']; + $cacheMap = array( + 'region' => $cacheAnnot->region, + 'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), + ); + + $metadata->enableCache($cacheMap); + } + + // Evaluate NamedNativeQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries'])) { + $namedNativeQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedNativeQueries']; + + foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) { + $metadata->addNamedNativeQuery(array( + 'name' => $namedNativeQuery->name, + 'query' => $namedNativeQuery->query, + 'resultClass' => $namedNativeQuery->resultClass, + 'resultSetMapping' => $namedNativeQuery->resultSetMapping, + )); + } + } + + // Evaluate SqlResultSetMappings annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings'])) { + $sqlResultSetMappingsAnnot = $classAnnotations['Doctrine\ORM\Mapping\SqlResultSetMappings']; + + foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) { + $entities = array(); + $columns = array(); + foreach ($resultSetMapping->entities as $entityResultAnnot) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => $entityResultAnnot->entityClass, + 'discriminatorColumn' => $entityResultAnnot->discriminatorColumn, + ); + + foreach ($entityResultAnnot->fields as $fieldResultAnnot) { + $entityResult['fields'][] = array( + 'name' => $fieldResultAnnot->name, + 'column' => $fieldResultAnnot->column + ); + } + + $entities[] = $entityResult; + } + + foreach ($resultSetMapping->columns as $columnResultAnnot) { + $columns[] = array( + 'name' => $columnResultAnnot->name, + ); + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping->name, + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + // Evaluate NamedQueries annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\NamedQueries'])) { + $namedQueriesAnnot = $classAnnotations['Doctrine\ORM\Mapping\NamedQueries']; + + if ( ! is_array($namedQueriesAnnot->value)) { + throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); + } + + foreach ($namedQueriesAnnot->value as $namedQuery) { + if ( ! ($namedQuery instanceof \Doctrine\ORM\Mapping\NamedQuery)) { + throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations."); + } + $metadata->addNamedQuery(array( + 'name' => $namedQuery->name, + 'query' => $namedQuery->query + )); + } + } + + // Evaluate InheritanceType annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\InheritanceType'])) { + $inheritanceTypeAnnot = $classAnnotations['Doctrine\ORM\Mapping\InheritanceType']; + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceTypeAnnot->value)); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate DiscriminatorColumn annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn'])) { + $discrColumnAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorColumn']; + $metadata->setDiscriminatorColumn(array( + 'name' => $discrColumnAnnot->name, + 'type' => $discrColumnAnnot->type, + 'length' => $discrColumnAnnot->length, + 'columnDefinition' => $discrColumnAnnot->columnDefinition + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate DiscriminatorMap annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) { + $discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap']; + $metadata->setDiscriminatorMap($discrMapAnnot->value); + } + } + } + + + // Evaluate DoctrineChangeTrackingPolicy annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy'])) { + $changeTrackingAnnot = $classAnnotations['Doctrine\ORM\Mapping\ChangeTrackingPolicy']; + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' . $changeTrackingAnnot->value)); + } + + // Evaluate annotations on properties/fields + /* @var $property \ReflectionProperty */ + foreach ($class->getProperties() as $property) { + if ($metadata->isMappedSuperclass && ! $property->isPrivate() + || + $metadata->isInheritedField($property->name) + || + $metadata->isInheritedAssociation($property->name) + || + $metadata->isInheritedEmbeddedClass($property->name)) { + continue; + } + + $mapping = array(); + $mapping['fieldName'] = $property->getName(); + + // Check for JoinColumn/JoinColumns annotations + $joinColumns = array(); + + if ($joinColumnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumn')) { + $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot); + } else if ($joinColumnsAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinColumns')) { + foreach ($joinColumnsAnnot->value as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + } + + // Field can only be annotated with one of: + // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany + if ($columnAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Column')) { + if ($columnAnnot->type == null) { + throw MappingException::propertyTypeIsRequired($className, $property->getName()); + } + + $mapping = $this->columnToArray($property->getName(), $columnAnnot); + + if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + if ($generatedValueAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\GeneratedValue')) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $generatedValueAnnot->strategy)); + } + + if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Version')) { + $metadata->setVersionMapping($mapping); + } + + $metadata->mapField($mapping); + + // Check for SequenceGenerator/TableGenerator definition + if ($seqGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\SequenceGenerator')) { + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => $seqGeneratorAnnot->sequenceName, + 'allocationSize' => $seqGeneratorAnnot->allocationSize, + 'initialValue' => $seqGeneratorAnnot->initialValue + )); + } else if ($this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\TableGenerator')) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } else if ($customGeneratorAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\CustomIdGenerator')) { + $metadata->setCustomGeneratorDefinition(array( + 'class' => $customGeneratorAnnot->class + )); + } + } else if ($oneToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToOne')) { + if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + $mapping['targetEntity'] = $oneToOneAnnot->targetEntity; + $mapping['joinColumns'] = $joinColumns; + $mapping['mappedBy'] = $oneToOneAnnot->mappedBy; + $mapping['inversedBy'] = $oneToOneAnnot->inversedBy; + $mapping['cascade'] = $oneToOneAnnot->cascade; + $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch); + $metadata->mapOneToOne($mapping); + } else if ($oneToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) { + $mapping['mappedBy'] = $oneToManyAnnot->mappedBy; + $mapping['targetEntity'] = $oneToManyAnnot->targetEntity; + $mapping['cascade'] = $oneToManyAnnot->cascade; + $mapping['indexBy'] = $oneToManyAnnot->indexBy; + $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch); + + if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { + $mapping['orderBy'] = $orderByAnnot->value; + } + + $metadata->mapOneToMany($mapping); + } else if ($manyToOneAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToOne')) { + if ($idAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Id')) { + $mapping['id'] = true; + } + + $mapping['joinColumns'] = $joinColumns; + $mapping['cascade'] = $manyToOneAnnot->cascade; + $mapping['inversedBy'] = $manyToOneAnnot->inversedBy; + $mapping['targetEntity'] = $manyToOneAnnot->targetEntity; + $mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch); + $metadata->mapManyToOne($mapping); + } else if ($manyToManyAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) { + $joinTable = array(); + + if ($joinTableAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\JoinTable')) { + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + } + + $mapping['joinTable'] = $joinTable; + $mapping['targetEntity'] = $manyToManyAnnot->targetEntity; + $mapping['mappedBy'] = $manyToManyAnnot->mappedBy; + $mapping['inversedBy'] = $manyToManyAnnot->inversedBy; + $mapping['cascade'] = $manyToManyAnnot->cascade; + $mapping['indexBy'] = $manyToManyAnnot->indexBy; + $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval; + $mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch); + + if ($orderByAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) { + $mapping['orderBy'] = $orderByAnnot->value; + } + + $metadata->mapManyToMany($mapping); + } else if ($embeddedAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Embedded')) { + $mapping['class'] = $embeddedAnnot->class; + $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix; + $metadata->mapEmbedded($mapping); + } + + // Evaluate @Cache annotation + if (($cacheAnnot = $this->reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\Cache')) !== null) { + $metadata->enableAssociationCache($mapping['fieldName'], array( + 'usage' => constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $cacheAnnot->usage), + 'region' => $cacheAnnot->region, + )); + } + } + + // Evaluate AssociationOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides'])) { + $associationOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AssociationOverrides']; + + foreach ($associationOverridesAnnot->value as $associationOverride) { + $override = array(); + $fieldName = $associationOverride->name; + + // Check for JoinColumn/JoinColumns annotations + if ($associationOverride->joinColumns) { + $joinColumns = array(); + foreach ($associationOverride->joinColumns as $joinColumn) { + $joinColumns[] = $this->joinColumnToArray($joinColumn); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for JoinTable annotations + if ($associationOverride->joinTable) { + $joinTableAnnot = $associationOverride->joinTable; + $joinTable = array( + 'name' => $joinTableAnnot->name, + 'schema' => $joinTableAnnot->schema + ); + + foreach ($joinTableAnnot->joinColumns as $joinColumn) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate AttributeOverrides annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides'])) { + $attributeOverridesAnnot = $classAnnotations['Doctrine\ORM\Mapping\AttributeOverrides']; + foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) { + $attributeOverride = $this->columnToArray($attributeOverrideAnnot->name, $attributeOverrideAnnot->column); + $metadata->setAttributeOverride($attributeOverrideAnnot->name, $attributeOverride); + } + } + + // Evaluate EntityListeners annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\EntityListeners'])) { + $entityListenersAnnot = $classAnnotations['Doctrine\ORM\Mapping\EntityListeners']; + + foreach ($entityListenersAnnot->value as $item) { + $listenerClassName = $metadata->fullyQualifiedClassName($item); + + if ( ! class_exists($listenerClassName)) { + throw MappingException::entityListenerClassNotFound($listenerClassName, $className); + } + + $hasMapping = false; + $listenerClass = new \ReflectionClass($listenerClassName); + /* @var $method \ReflectionMethod */ + foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + // find method callbacks. + $callbacks = $this->getMethodCallbacks($method); + $hasMapping = $hasMapping ?: ( ! empty($callbacks)); + + foreach ($callbacks as $value) { + $metadata->addEntityListener($value[1], $listenerClassName, $value[0]); + } + } + // Evaluate the listener using naming convention. + if ( ! $hasMapping ) { + EntityListenerBuilder::bindEntityListener($metadata, $listenerClassName); + } + } + } + + // Evaluate @HasLifecycleCallbacks annotation + if (isset($classAnnotations['Doctrine\ORM\Mapping\HasLifecycleCallbacks'])) { + /* @var $method \ReflectionMethod */ + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + + foreach ($this->getMethodCallbacks($method) as $value) { + + $metadata->addLifecycleCallback($value[0], $value[1]); + } + } + } + } + + /** + * Attempts to resolve the fetch mode. + * + * @param string $className The class name. + * @param string $fetchMode The fetch mode. + * + * @return integer The fetch mode as defined in ClassMetadata. + * + * @throws MappingException If the fetch mode is not valid. + */ + private function getFetchMode($className, $fetchMode) + { + if( ! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) { + throw MappingException::invalidFetchMode($className, $fetchMode); + } + + return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode); + } + + /** + * Parses the given method. + * + * @param \ReflectionMethod $method + * + * @return array + */ + private function getMethodCallbacks(\ReflectionMethod $method) + { + $callbacks = array(); + $annotations = $this->reader->getMethodAnnotations($method); + + foreach ($annotations as $annot) { + if ($annot instanceof \Doctrine\ORM\Mapping\PrePersist) { + $callbacks[] = array($method->name, Events::prePersist); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostPersist) { + $callbacks[] = array($method->name, Events::postPersist); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreUpdate) { + $callbacks[] = array($method->name, Events::preUpdate); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostUpdate) { + $callbacks[] = array($method->name, Events::postUpdate); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreRemove) { + $callbacks[] = array($method->name, Events::preRemove); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostRemove) { + $callbacks[] = array($method->name, Events::postRemove); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PostLoad) { + $callbacks[] = array($method->name, Events::postLoad); + } + + if ($annot instanceof \Doctrine\ORM\Mapping\PreFlush) { + $callbacks[] = array($method->name, Events::preFlush); + } + } + + return $callbacks; + } + + /** + * Parse the given JoinColumn as array + * + * @param JoinColumn $joinColumn + * @return array + */ + private function joinColumnToArray(JoinColumn $joinColumn) + { + return array( + 'name' => $joinColumn->name, + 'unique' => $joinColumn->unique, + 'nullable' => $joinColumn->nullable, + 'onDelete' => $joinColumn->onDelete, + 'columnDefinition' => $joinColumn->columnDefinition, + 'referencedColumnName' => $joinColumn->referencedColumnName, + ); + } + + /** + * Parse the given Column as array + * + * @param string $fieldName + * @param Column $column + * + * @return array + */ + private function columnToArray($fieldName, Column $column) + { + $mapping = array( + 'fieldName' => $fieldName, + 'type' => $column->type, + 'scale' => $column->scale, + 'length' => $column->length, + 'unique' => $column->unique, + 'nullable' => $column->nullable, + 'precision' => $column->precision + ); + + if ($column->options) { + $mapping['options'] = $column->options; + } + + if (isset($column->name)) { + $mapping['columnName'] = $column->name; + } + + if (isset($column->columnDefinition)) { + $mapping['columnDefinition'] = $column->columnDefinition; + } + + return $mapping; + } + + /** + * Factory method for the Annotation Driver. + * + * @param array|string $paths + * @param AnnotationReader|null $reader + * + * @return AnnotationDriver + */ + static public function create($paths = array(), AnnotationReader $reader = null) + { + if ($reader == null) { + $reader = new AnnotationReader(); + } + + return new self($reader, $paths); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php new file mode 100644 index 0000000..e51c152 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php @@ -0,0 +1,555 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\MappingException; + +/** + * The DatabaseDriver reverse engineers the mapping metadata from a database. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Benjamin Eberlei + */ +class DatabaseDriver implements MappingDriver +{ + /** + * @var AbstractSchemaManager + */ + private $_sm; + + /** + * @var array|null + */ + private $tables = null; + + /** + * @var array + */ + private $classToTableNames = array(); + + /** + * @var array + */ + private $manyToManyTables = array(); + + /** + * @var array + */ + private $classNamesForTables = array(); + + /** + * @var array + */ + private $fieldNamesForColumns = array(); + + /** + * The namespace for the generated entities. + * + * @var string|null + */ + private $namespace; + + /** + * @param AbstractSchemaManager $schemaManager + */ + public function __construct(AbstractSchemaManager $schemaManager) + { + $this->_sm = $schemaManager; + } + + /** + * Set the namespace for the generated entities. + * + * @param string $namespace + * + * @return void + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $this->reverseEngineerMappingFromDatabase(); + + return array_keys($this->classToTableNames); + } + + /** + * Sets class name for a table. + * + * @param string $tableName + * @param string $className + * + * @return void + */ + public function setClassNameForTable($tableName, $className) + { + $this->classNamesForTables[$tableName] = $className; + } + + /** + * Sets field name for a column on a specific table. + * + * @param string $tableName + * @param string $columnName + * @param string $fieldName + * + * @return void + */ + public function setFieldNameForColumn($tableName, $columnName, $fieldName) + { + $this->fieldNamesForColumns[$tableName][$columnName] = $fieldName; + } + + /** + * Sets tables manually instead of relying on the reverse engineering capabilities of SchemaManager. + * + * @param array $entityTables + * @param array $manyToManyTables + * + * @return void + */ + public function setTables($entityTables, $manyToManyTables) + { + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + + foreach ($entityTables as $table) { + $className = $this->getClassNameForTable($table->getName()); + + $this->classToTableNames[$className] = $table->getName(); + $this->tables[$table->getName()] = $table; + } + + foreach ($manyToManyTables as $table) { + $this->manyToManyTables[$table->getName()] = $table; + } + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->reverseEngineerMappingFromDatabase(); + + if ( ! isset($this->classToTableNames[$className])) { + throw new \InvalidArgumentException("Unknown class " . $className); + } + + $tableName = $this->classToTableNames[$className]; + + $metadata->name = $className; + $metadata->table['name'] = $tableName; + + $this->buildIndexes($metadata); + $this->buildFieldMappings($metadata); + $this->buildToOneAssociationMappings($metadata); + + foreach ($this->manyToManyTables as $manyTable) { + foreach ($manyTable->getForeignKeys() as $foreignKey) { + // foreign key maps to the table of the current entity, many to many association probably exists + if ( ! (strtolower($tableName) === strtolower($foreignKey->getForeignTableName()))) { + continue; + } + + $myFk = $foreignKey; + $otherFk = null; + + foreach ($manyTable->getForeignKeys() as $foreignKey) { + if ($foreignKey != $myFk) { + $otherFk = $foreignKey; + break; + } + } + + if ( ! $otherFk) { + // the definition of this many to many table does not contain + // enough foreign key information to continue reverse engineering. + continue; + } + + $localColumn = current($myFk->getColumns()); + + $associationMapping = array(); + $associationMapping['fieldName'] = $this->getFieldNameForColumn($manyTable->getName(), current($otherFk->getColumns()), true); + $associationMapping['targetEntity'] = $this->getClassNameForTable($otherFk->getForeignTableName()); + + if (current($manyTable->getColumns())->getName() == $localColumn) { + $associationMapping['inversedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + $associationMapping['joinTable'] = array( + 'name' => strtolower($manyTable->getName()), + 'joinColumns' => array(), + 'inverseJoinColumns' => array(), + ); + + $fkCols = $myFk->getForeignColumns(); + $cols = $myFk->getColumns(); + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['joinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + + $fkCols = $otherFk->getForeignColumns(); + $cols = $otherFk->getColumns(); + + for ($i = 0; $i < count($cols); $i++) { + $associationMapping['joinTable']['inverseJoinColumns'][] = array( + 'name' => $cols[$i], + 'referencedColumnName' => $fkCols[$i], + ); + } + } else { + $associationMapping['mappedBy'] = $this->getFieldNameForColumn($manyTable->getName(), current($myFk->getColumns()), true); + } + + $metadata->mapManyToMany($associationMapping); + + break; + } + } + } + + /** + * @return void + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function reverseEngineerMappingFromDatabase() + { + if ($this->tables !== null) { + return; + } + + $tables = array(); + + foreach ($this->_sm->listTableNames() as $tableName) { + $tables[$tableName] = $this->_sm->listTableDetails($tableName); + } + + $this->tables = $this->manyToManyTables = $this->classToTableNames = array(); + + foreach ($tables as $tableName => $table) { + $foreignKeys = ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) + ? $table->getForeignKeys() + : array(); + + $allForeignKeyColumns = array(); + + foreach ($foreignKeys as $foreignKey) { + $allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns()); + } + + if ( ! $table->hasPrimaryKey()) { + throw new MappingException( + "Table " . $table->getName() . " has no primary key. Doctrine does not ". + "support reverse engineering from tables that don't have a primary key." + ); + } + + $pkColumns = $table->getPrimaryKey()->getColumns(); + + sort($pkColumns); + sort($allForeignKeyColumns); + + if ($pkColumns == $allForeignKeyColumns && count($foreignKeys) == 2) { + $this->manyToManyTables[$tableName] = $table; + } else { + // lower-casing is necessary because of Oracle Uppercase Tablenames, + // assumption is lower-case + underscore separated. + $className = $this->getClassNameForTable($tableName); + + $this->tables[$tableName] = $table; + $this->classToTableNames[$className] = $tableName; + } + } + } + + /** + * Build indexes from a class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildIndexes(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $indexes = $this->tables[$tableName]->getIndexes(); + + foreach($indexes as $index){ + if ($index->isPrimary()) { + continue; + } + + $indexName = $index->getName(); + $indexColumns = $index->getColumns(); + $constraintType = $index->isUnique() + ? 'uniqueConstraints' + : 'indexes'; + + $metadata->table[$constraintType][$indexName]['columns'] = $indexColumns; + } + } + + /** + * Build field mapping from class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildFieldMappings(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $columns = $this->tables[$tableName]->getColumns(); + $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]); + $foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]); + $allForeignKeys = array(); + + foreach ($foreignKeys as $foreignKey) { + $allForeignKeys = array_merge($allForeignKeys, $foreignKey->getLocalColumns()); + } + + $ids = array(); + $fieldMappings = array(); + + foreach ($columns as $column) { + if (in_array($column->getName(), $allForeignKeys)) { + continue; + } + + $fieldMapping = $this->buildFieldMapping($tableName, $column); + + if ($primaryKeys && in_array($column->getName(), $primaryKeys)) { + $fieldMapping['id'] = true; + $ids[] = $fieldMapping; + } + + $fieldMappings[] = $fieldMapping; + } + + // We need to check for the columns here, because we might have associations as id as well. + if ($ids && count($primaryKeys) == 1) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + + foreach ($fieldMappings as $fieldMapping) { + $metadata->mapField($fieldMapping); + } + } + + /** + * Build field mapping from a schema column definition + * + * @param string $tableName + * @param \Doctrine\DBAL\Schema\Column $column + * + * @return array + */ + private function buildFieldMapping($tableName, Column $column) + { + $fieldMapping = array( + 'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false), + 'columnName' => $column->getName(), + 'type' => $column->getType()->getName(), + 'nullable' => ( ! $column->getNotNull()), + ); + + // Type specific elements + switch ($fieldMapping['type']) { + case Type::TARRAY: + case Type::BLOB: + case Type::GUID: + case Type::JSON_ARRAY: + case Type::OBJECT: + case Type::SIMPLE_ARRAY: + case Type::STRING: + case Type::TEXT: + $fieldMapping['length'] = $column->getLength(); + $fieldMapping['options']['fixed'] = $column->getFixed(); + break; + + case Type::DECIMAL: + case Type::FLOAT: + $fieldMapping['precision'] = $column->getPrecision(); + $fieldMapping['scale'] = $column->getScale(); + break; + + case Type::INTEGER: + case Type::BIGINT: + case Type::SMALLINT: + $fieldMapping['options']['unsigned'] = $column->getUnsigned(); + break; + } + + // Comment + if (($comment = $column->getComment()) !== null) { + $fieldMapping['options']['comment'] = $comment; + } + + // Default + if (($default = $column->getDefault()) !== null) { + $fieldMapping['options']['default'] = $default; + } + + return $fieldMapping; + } + + /** + * Build to one (one to one, many to one) association mapping from class metadata. + * + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + */ + private function buildToOneAssociationMappings(ClassMetadataInfo $metadata) + { + $tableName = $metadata->table['name']; + $primaryKeys = $this->getTablePrimaryKeys($this->tables[$tableName]); + $foreignKeys = $this->getTableForeignKeys($this->tables[$tableName]); + + foreach ($foreignKeys as $foreignKey) { + $foreignTableName = $foreignKey->getForeignTableName(); + $fkColumns = $foreignKey->getColumns(); + $fkForeignColumns = $foreignKey->getForeignColumns(); + $localColumn = current($fkColumns); + $associationMapping = array( + 'fieldName' => $this->getFieldNameForColumn($tableName, $localColumn, true), + 'targetEntity' => $this->getClassNameForTable($foreignTableName), + ); + + if (isset($metadata->fieldMappings[$associationMapping['fieldName']])) { + $associationMapping['fieldName'] .= '2'; // "foo" => "foo2" + } + + if ($primaryKeys && in_array($localColumn, $primaryKeys)) { + $associationMapping['id'] = true; + } + + for ($i = 0; $i < count($fkColumns); $i++) { + $associationMapping['joinColumns'][] = array( + 'name' => $fkColumns[$i], + 'referencedColumnName' => $fkForeignColumns[$i], + ); + } + + // Here we need to check if $fkColumns are the same as $primaryKeys + if ( ! array_diff($fkColumns, $primaryKeys)) { + $metadata->mapOneToOne($associationMapping); + } else { + $metadata->mapManyToOne($associationMapping); + } + } + } + + /** + * Retreive schema table definition foreign keys. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return array + */ + private function getTableForeignKeys(Table $table) + { + return ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) + ? $table->getForeignKeys() + : array(); + } + + /** + * Retreive schema table definition primary keys. + * + * @param \Doctrine\DBAL\Schema\Table $table + * + * @return array + */ + private function getTablePrimaryKeys(Table $table) + { + try { + return $table->getPrimaryKey()->getColumns(); + } catch(SchemaException $e) { + // Do nothing + } + + return array(); + } + + /** + * Returns the mapped class name for a table if it exists. Otherwise return "classified" version. + * + * @param string $tableName + * + * @return string + */ + private function getClassNameForTable($tableName) + { + if (isset($this->classNamesForTables[$tableName])) { + return $this->namespace . $this->classNamesForTables[$tableName]; + } + + return $this->namespace . Inflector::classify(strtolower($tableName)); + } + + /** + * Return the mapped field name for a column, if it exists. Otherwise return camelized version. + * + * @param string $tableName + * @param string $columnName + * @param boolean $fk Whether the column is a foreignkey or not. + * + * @return string + */ + private function getFieldNameForColumn($tableName, $columnName, $fk = false) + { + if (isset($this->fieldNamesForColumns[$tableName]) && isset($this->fieldNamesForColumns[$tableName][$columnName])) { + return $this->fieldNamesForColumns[$tableName][$columnName]; + } + + $columnName = strtolower($columnName); + + // Replace _id if it is a foreignkey column + if ($fk) { + $columnName = str_replace('_id', '', $columnName); + } + return Inflector::camelize($columnName); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php new file mode 100644 index 0000000..8f4a34c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -0,0 +1,69 @@ +. + */ + +require_once __DIR__.'/../Annotation.php'; +require_once __DIR__.'/../Entity.php'; +require_once __DIR__.'/../Embeddable.php'; +require_once __DIR__.'/../Embedded.php'; +require_once __DIR__.'/../MappedSuperclass.php'; +require_once __DIR__.'/../InheritanceType.php'; +require_once __DIR__.'/../DiscriminatorColumn.php'; +require_once __DIR__.'/../DiscriminatorMap.php'; +require_once __DIR__.'/../Id.php'; +require_once __DIR__.'/../GeneratedValue.php'; +require_once __DIR__.'/../Version.php'; +require_once __DIR__.'/../JoinColumn.php'; +require_once __DIR__.'/../JoinColumns.php'; +require_once __DIR__.'/../Column.php'; +require_once __DIR__.'/../OneToOne.php'; +require_once __DIR__.'/../OneToMany.php'; +require_once __DIR__.'/../ManyToOne.php'; +require_once __DIR__.'/../ManyToMany.php'; +require_once __DIR__.'/../Table.php'; +require_once __DIR__.'/../UniqueConstraint.php'; +require_once __DIR__.'/../Index.php'; +require_once __DIR__.'/../JoinTable.php'; +require_once __DIR__.'/../SequenceGenerator.php'; +require_once __DIR__.'/../CustomIdGenerator.php'; +require_once __DIR__.'/../ChangeTrackingPolicy.php'; +require_once __DIR__.'/../OrderBy.php'; +require_once __DIR__.'/../NamedQueries.php'; +require_once __DIR__.'/../NamedQuery.php'; +require_once __DIR__.'/../HasLifecycleCallbacks.php'; +require_once __DIR__.'/../PrePersist.php'; +require_once __DIR__.'/../PostPersist.php'; +require_once __DIR__.'/../PreUpdate.php'; +require_once __DIR__.'/../PostUpdate.php'; +require_once __DIR__.'/../PreRemove.php'; +require_once __DIR__.'/../PostRemove.php'; +require_once __DIR__.'/../PostLoad.php'; +require_once __DIR__.'/../PreFlush.php'; +require_once __DIR__.'/../FieldResult.php'; +require_once __DIR__.'/../ColumnResult.php'; +require_once __DIR__.'/../EntityResult.php'; +require_once __DIR__.'/../NamedNativeQuery.php'; +require_once __DIR__.'/../NamedNativeQueries.php'; +require_once __DIR__.'/../SqlResultSetMapping.php'; +require_once __DIR__.'/../SqlResultSetMappings.php'; +require_once __DIR__.'/../AssociationOverride.php'; +require_once __DIR__.'/../AssociationOverrides.php'; +require_once __DIR__.'/../AttributeOverride.php'; +require_once __DIR__.'/../AttributeOverrides.php'; +require_once __DIR__.'/../EntityListeners.php'; +require_once __DIR__.'/../Cache.php'; diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php new file mode 100644 index 0000000..f4cd8cd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DriverChain.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain instead + */ +class DriverChain extends MappingDriverChain +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php new file mode 100644 index 0000000..28e2dbe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/PHPDriver.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver as CommonPHPDriver; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver instead + */ +class PHPDriver extends CommonPHPDriver +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php new file mode 100644 index 0000000..85221f3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php @@ -0,0 +1,43 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; + +/** + * XmlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedXmlDriver extends XmlDriver +{ + const DEFAULT_FILE_EXTENSION = '.orm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); + parent::__construct($locator, $fileExtension); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php new file mode 100644 index 0000000..d2b8c0f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php @@ -0,0 +1,43 @@ +. +*/ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; + +/** + * YamlDriver that additionally looks for mapping information in a global file. + * + * @author Fabien Potencier + * @author Benjamin Eberlei + * @license MIT + */ +class SimplifiedYamlDriver extends YamlDriver +{ + const DEFAULT_FILE_EXTENSION = '.orm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($prefixes, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + $locator = new SymfonyFileLocator((array) $prefixes, $fileExtension); + parent::__construct($locator, $fileExtension); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 0000000..d6c6ead --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,31 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver as CommonStaticPHPDriver; + +/** + * {@inheritDoc} + * + * @deprecated this driver will be removed. Use Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver instead + */ +class StaticPHPDriver extends CommonStaticPHPDriver +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php new file mode 100644 index 0000000..c917eb2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -0,0 +1,847 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use SimpleXMLElement; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; + +/** + * XmlDriver is a metadata driver that enables mapping through XML files. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class XmlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.xml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + /* @var $xmlRoot SimpleXMLElement */ + $xmlRoot = $this->getElement($className); + + if ($xmlRoot->getName() == 'entity') { + if (isset($xmlRoot['repository-class'])) { + $metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']); + } + if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) { + $metadata->markReadOnly(); + } + } else if ($xmlRoot->getName() == 'mapped-superclass') { + $metadata->setCustomRepositoryClass( + isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null + ); + $metadata->isMappedSuperclass = true; + } else if ($xmlRoot->getName() == 'embeddable') { + $metadata->isEmbeddedClass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate attributes + $primaryTable = array(); + + if (isset($xmlRoot['table'])) { + $primaryTable['name'] = (string) $xmlRoot['table']; + } + + if (isset($xmlRoot['schema'])) { + $primaryTable['schema'] = (string) $xmlRoot['schema']; + } + + $metadata->setPrimaryTable($primaryTable); + + // Evaluate second level cache + if (isset($xmlRoot->cache)) { + $metadata->enableCache($this->cacheToArray($xmlRoot->cache)); + } + + // Evaluate named queries + if (isset($xmlRoot->{'named-queries'})) { + foreach ($xmlRoot->{'named-queries'}->{'named-query'} as $namedQueryElement) { + $metadata->addNamedQuery(array( + 'name' => (string)$namedQueryElement['name'], + 'query' => (string)$namedQueryElement['query'] + )); + } + } + + // Evaluate native named queries + if (isset($xmlRoot->{'named-native-queries'})) { + foreach ($xmlRoot->{'named-native-queries'}->{'named-native-query'} as $nativeQueryElement) { + $metadata->addNamedNativeQuery(array( + 'name' => isset($nativeQueryElement['name']) ? (string)$nativeQueryElement['name'] : null, + 'query' => isset($nativeQueryElement->query) ? (string)$nativeQueryElement->query : null, + 'resultClass' => isset($nativeQueryElement['result-class']) ? (string)$nativeQueryElement['result-class'] : null, + 'resultSetMapping' => isset($nativeQueryElement['result-set-mapping']) ? (string)$nativeQueryElement['result-set-mapping'] : null, + )); + } + } + + // Evaluate sql result set mapping + if (isset($xmlRoot->{'sql-result-set-mappings'})) { + foreach ($xmlRoot->{'sql-result-set-mappings'}->{'sql-result-set-mapping'} as $rsmElement) { + $entities = array(); + $columns = array(); + foreach ($rsmElement as $entityElement) { + // + if (isset($entityElement['entity-class'])) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => (string)$entityElement['entity-class'], + 'discriminatorColumn' => isset($entityElement['discriminator-column']) ? (string)$entityElement['discriminator-column'] : null, + ); + + foreach ($entityElement as $fieldElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldElement['name']) ? (string)$fieldElement['name'] : null, + 'column' => isset($fieldElement['column']) ? (string)$fieldElement['column'] : null, + ); + } + + $entities[] = $entityResult; + } + + // + if (isset($entityElement['name'])) { + $columns[] = array( + 'name' => (string)$entityElement['name'], + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => (string)$rsmElement['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + if (isset($xmlRoot['inheritance-type'])) { + $inheritanceType = (string)$xmlRoot['inheritance-type']; + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType)); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate + if (isset($xmlRoot->{'discriminator-column'})) { + $discrColumn = $xmlRoot->{'discriminator-column'}; + $metadata->setDiscriminatorColumn(array( + 'name' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null, + 'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null, + 'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null, + 'columnDefinition' => isset($discrColumn['column-definition']) ? (string)$discrColumn['column-definition'] : null + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate + if (isset($xmlRoot->{'discriminator-map'})) { + $map = array(); + foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) { + $map[(string)$discrMapElement['value']] = (string)$discrMapElement['class']; + } + $metadata->setDiscriminatorMap($map); + } + } + } + + + // Evaluate + if (isset($xmlRoot['change-tracking-policy'])) { + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper((string)$xmlRoot['change-tracking-policy']))); + } + + // Evaluate + if (isset($xmlRoot->indexes)) { + $metadata->table['indexes'] = array(); + foreach ($xmlRoot->indexes->index as $indexXml) { + $index = array('columns' => explode(',', (string) $indexXml['columns'])); + + if (isset($indexXml['flags'])) { + $index['flags'] = explode(',', (string) $indexXml['flags']); + } + + if (isset($indexXml->options)) { + $index['options'] = $this->_parseOptions($indexXml->options->children()); + } + + if (isset($indexXml['name'])) { + $metadata->table['indexes'][(string) $indexXml['name']] = $index; + } else { + $metadata->table['indexes'][] = $index; + } + } + } + + // Evaluate + if (isset($xmlRoot->{'unique-constraints'})) { + $metadata->table['uniqueConstraints'] = array(); + foreach ($xmlRoot->{'unique-constraints'}->{'unique-constraint'} as $uniqueXml) { + $unique = array('columns' => explode(',', (string) $uniqueXml['columns'])); + + + if (isset($uniqueXml->options)) { + $unique['options'] = $this->_parseOptions($uniqueXml->options->children()); + } + + if (isset($uniqueXml['name'])) { + $metadata->table['uniqueConstraints'][(string)$uniqueXml['name']] = $unique; + } else { + $metadata->table['uniqueConstraints'][] = $unique; + } + } + } + + if (isset($xmlRoot->options)) { + $metadata->table['options'] = $this->_parseOptions($xmlRoot->options->children()); + } + + // The mapping assignment is done in 2 times as a bug might occurs on some php/xml lib versions + // The internal SimpleXmlIterator get resetted, to this generate a duplicate field exception + $mappings = array(); + // Evaluate mappings + if (isset($xmlRoot->field)) { + foreach ($xmlRoot->field as $fieldMapping) { + $mapping = $this->columnToArray($fieldMapping); + + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + unset($mapping['version']); + } + + $metadata->mapField($mapping); + } + } + + if (isset($xmlRoot->embedded)) { + foreach ($xmlRoot->embedded as $embeddedMapping) { + $columnPrefix = isset($embeddedMapping['column-prefix']) + ? (string) $embeddedMapping['column-prefix'] + : null; + + $useColumnPrefix = isset($embeddedMapping['use-column-prefix']) + ? $this->evaluateBoolean($embeddedMapping['use-column-prefix']) + : true; + + $mapping = array( + 'fieldName' => (string) $embeddedMapping['name'], + 'class' => (string) $embeddedMapping['class'], + 'columnPrefix' => $useColumnPrefix ? $columnPrefix : false + ); + + $metadata->mapEmbedded($mapping); + } + } + + foreach ($mappings as $mapping) { + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + } + + $metadata->mapField($mapping); + } + + // Evaluate mappings + $associationIds = array(); + foreach ($xmlRoot->id as $idElement) { + if (isset($idElement['association-key']) && $this->evaluateBoolean($idElement['association-key'])) { + $associationIds[(string)$idElement['name']] = true; + continue; + } + + $mapping = array( + 'id' => true, + 'fieldName' => (string)$idElement['name'] + ); + + if (isset($idElement['type'])) { + $mapping['type'] = (string)$idElement['type']; + } + + if (isset($idElement['length'])) { + $mapping['length'] = (string)$idElement['length']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = (string)$idElement['column']; + } + + if (isset($idElement['column-definition'])) { + $mapping['columnDefinition'] = (string)$idElement['column-definition']; + } + + if (isset($idElement->options)) { + $mapping['options'] = $this->_parseOptions($idElement->options->children()); + } + + $metadata->mapField($mapping); + + if (isset($idElement->generator)) { + $strategy = isset($idElement->generator['strategy']) ? + (string)$idElement->generator['strategy'] : 'AUTO'; + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . $strategy)); + } + + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement->{'sequence-generator'})) { + $seqGenerator = $idElement->{'sequence-generator'}; + $metadata->setSequenceGeneratorDefinition(array( + 'sequenceName' => (string)$seqGenerator['sequence-name'], + 'allocationSize' => (string)$seqGenerator['allocation-size'], + 'initialValue' => (string)$seqGenerator['initial-value'] + )); + } else if (isset($idElement->{'custom-id-generator'})) { + $customGenerator = $idElement->{'custom-id-generator'}; + $metadata->setCustomGeneratorDefinition(array( + 'class' => (string) $customGenerator['class'] + )); + } else if (isset($idElement->{'table-generator'})) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'one-to-one'})) { + foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) { + $mapping = array( + 'fieldName' => (string)$oneToOneElement['field'], + 'targetEntity' => (string)$oneToOneElement['target-entity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($oneToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToOneElement['fetch']); + } + + if (isset($oneToOneElement['mapped-by'])) { + $mapping['mappedBy'] = (string)$oneToOneElement['mapped-by']; + } else { + if (isset($oneToOneElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$oneToOneElement['inversed-by']; + } + $joinColumns = array(); + + if (isset($oneToOneElement->{'join-column'})) { + $joinColumns[] = $this->joinColumnToArray($oneToOneElement->{'join-column'}); + } else if (isset($oneToOneElement->{'join-columns'})) { + foreach ($oneToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToOneElement->cascade); + } + + if (isset($oneToOneElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToOneElement['orphan-removal']); + } + + $metadata->mapOneToOne($mapping); + + // Evaluate second level cache + if (isset($oneToOneElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToOneElement->cache)); + } + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'one-to-many'})) { + foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) { + $mapping = array( + 'fieldName' => (string)$oneToManyElement['field'], + 'targetEntity' => (string)$oneToManyElement['target-entity'], + 'mappedBy' => (string)$oneToManyElement['mapped-by'] + ); + + if (isset($oneToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$oneToManyElement['fetch']); + } + + if (isset($oneToManyElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($oneToManyElement->cascade); + } + + if (isset($oneToManyElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($oneToManyElement['orphan-removal']); + } + + if (isset($oneToManyElement->{'order-by'})) { + $orderBy = array(); + foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) { + $orderBy[(string)$orderByField['name']] = (string)$orderByField['direction']; + } + $mapping['orderBy'] = $orderBy; + } + + if (isset($oneToManyElement['index-by'])) { + $mapping['indexBy'] = (string)$oneToManyElement['index-by']; + } else if (isset($oneToManyElement->{'index-by'})) { + throw new \InvalidArgumentException(" is not a valid tag"); + } + + $metadata->mapOneToMany($mapping); + + // Evaluate second level cache + if (isset($oneToManyElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToManyElement->cache)); + } + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'many-to-one'})) { + foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) { + $mapping = array( + 'fieldName' => (string)$manyToOneElement['field'], + 'targetEntity' => (string)$manyToOneElement['target-entity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($manyToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToOneElement['fetch']); + } + + if (isset($manyToOneElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$manyToOneElement['inversed-by']; + } + + $joinColumns = array(); + + if (isset($manyToOneElement->{'join-column'})) { + $joinColumns[] = $this->joinColumnToArray($manyToOneElement->{'join-column'}); + } else if (isset($manyToOneElement->{'join-columns'})) { + foreach ($manyToOneElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToOneElement->cascade); + } + + $metadata->mapManyToOne($mapping); + + // Evaluate second level cache + if (isset($manyToOneElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToOneElement->cache)); + } + } + } + + // Evaluate mappings + if (isset($xmlRoot->{'many-to-many'})) { + foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) { + $mapping = array( + 'fieldName' => (string)$manyToManyElement['field'], + 'targetEntity' => (string)$manyToManyElement['target-entity'] + ); + + if (isset($manyToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . (string)$manyToManyElement['fetch']); + } + + if (isset($manyToManyElement['orphan-removal'])) { + $mapping['orphanRemoval'] = $this->evaluateBoolean($manyToManyElement['orphan-removal']); + } + + if (isset($manyToManyElement['mapped-by'])) { + $mapping['mappedBy'] = (string)$manyToManyElement['mapped-by']; + } else if (isset($manyToManyElement->{'join-table'})) { + if (isset($manyToManyElement['inversed-by'])) { + $mapping['inversedBy'] = (string)$manyToManyElement['inversed-by']; + } + + $joinTableElement = $manyToManyElement->{'join-table'}; + $joinTable = array( + 'name' => (string)$joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = (string)$joinTableElement['schema']; + } + + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $mapping['joinTable'] = $joinTable; + } + + if (isset($manyToManyElement->cascade)) { + $mapping['cascade'] = $this->_getCascadeMappings($manyToManyElement->cascade); + } + + if (isset($manyToManyElement->{'order-by'})) { + $orderBy = array(); + foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} as $orderByField) { + $orderBy[(string)$orderByField['name']] = (string)$orderByField['direction']; + } + $mapping['orderBy'] = $orderBy; + } + + if (isset($manyToManyElement['index-by'])) { + $mapping['indexBy'] = (string)$manyToManyElement['index-by']; + } else if (isset($manyToManyElement->{'index-by'})) { + throw new \InvalidArgumentException(" is not a valid tag"); + } + + $metadata->mapManyToMany($mapping); + + // Evaluate second level cache + if (isset($manyToManyElement->cache)) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToManyElement->cache)); + } + } + } + + // Evaluate association-overrides + if (isset($xmlRoot->{'attribute-overrides'})) { + foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + foreach ($overrideElement->field as $field) { + $mapping = $this->columnToArray($field); + $mapping['fieldName'] = $fieldName; + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + } + + // Evaluate association-overrides + if (isset($xmlRoot->{'association-overrides'})) { + foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) { + $fieldName = (string) $overrideElement['name']; + $override = array(); + + // Check for join-columns + if (isset($overrideElement->{'join-columns'})) { + $joinColumns = array(); + foreach ($overrideElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for join-table + if ($overrideElement->{'join-table'}) { + $joinTable = null; + $joinTableElement = $overrideElement->{'join-table'}; + + $joinTable = array( + 'name' => (string) $joinTableElement['name'], + 'schema' => (string) $joinTableElement['schema'] + ); + + if (isset($joinTableElement->{'join-columns'})) { + foreach ($joinTableElement->{'join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + if (isset($joinTableElement->{'inverse-join-columns'})) { + foreach ($joinTableElement->{'inverse-join-columns'}->{'join-column'} as $joinColumnElement) { + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate + if (isset($xmlRoot->{'lifecycle-callbacks'})) { + foreach ($xmlRoot->{'lifecycle-callbacks'}->{'lifecycle-callback'} as $lifecycleCallback) { + $metadata->addLifecycleCallback((string)$lifecycleCallback['method'], constant('Doctrine\ORM\Events::' . (string)$lifecycleCallback['type'])); + } + } + + // Evaluate entity listener + if (isset($xmlRoot->{'entity-listeners'})) { + foreach ($xmlRoot->{'entity-listeners'}->{'entity-listener'} as $listenerElement) { + $className = (string) $listenerElement['class']; + // Evaluate the listener using naming convention. + if($listenerElement->count() === 0) { + EntityListenerBuilder::bindEntityListener($metadata, $className); + + continue; + } + + foreach ($listenerElement as $callbackElement) { + $eventName = (string) $callbackElement['type']; + $methodName = (string) $callbackElement['method']; + + $metadata->addEntityListener($eventName, $className, $methodName); + } + } + } + } + + /** + * Parses (nested) option elements. + * + * @param SimpleXMLElement $options The XML element. + * + * @return array The options array. + */ + private function _parseOptions(SimpleXMLElement $options) + { + $array = array(); + + /* @var $option SimpleXMLElement */ + foreach ($options as $option) { + if ($option->count()) { + $value = $this->_parseOptions($option->children()); + } else { + $value = (string) $option; + } + + $attr = $option->attributes(); + + if (isset($attr->name)) { + $array[(string) $attr->name] = $value; + } else { + $array[] = $value; + } + } + + return $array; + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given SimpleXMLElement. + * + * @param SimpleXMLElement $joinColumnElement The XML element. + * + * @return array The mapping array. + */ + private function joinColumnToArray(SimpleXMLElement $joinColumnElement) + { + $joinColumn = array( + 'name' => (string)$joinColumnElement['name'], + 'referencedColumnName' => (string)$joinColumnElement['referenced-column-name'] + ); + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = $this->evaluateBoolean($joinColumnElement['unique']); + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = $this->evaluateBoolean($joinColumnElement['nullable']); + } + + if (isset($joinColumnElement['on-delete'])) { + $joinColumn['onDelete'] = (string)$joinColumnElement['on-delete']; + } + + if (isset($joinColumnElement['column-definition'])) { + $joinColumn['columnDefinition'] = (string)$joinColumnElement['column-definition']; + } + + return $joinColumn; + } + + /** + * Parses the given field as array. + * + * @param SimpleXMLElement $fieldMapping + * + * @return array + */ + private function columnToArray(SimpleXMLElement $fieldMapping) + { + $mapping = array( + 'fieldName' => (string) $fieldMapping['name'], + ); + + if (isset($fieldMapping['type'])) { + $mapping['type'] = (string) $fieldMapping['type']; + } + + if (isset($fieldMapping['column'])) { + $mapping['columnName'] = (string) $fieldMapping['column']; + } + + if (isset($fieldMapping['length'])) { + $mapping['length'] = (int) $fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $mapping['precision'] = (int) $fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $mapping['scale'] = (int) $fieldMapping['scale']; + } + + if (isset($fieldMapping['unique'])) { + $mapping['unique'] = $this->evaluateBoolean($fieldMapping['unique']); + } + + if (isset($fieldMapping['nullable'])) { + $mapping['nullable'] = $this->evaluateBoolean($fieldMapping['nullable']); + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $mapping['version'] = $this->evaluateBoolean($fieldMapping['version']); + } + + if (isset($fieldMapping['column-definition'])) { + $mapping['columnDefinition'] = (string) $fieldMapping['column-definition']; + } + + if (isset($fieldMapping->options)) { + $mapping['options'] = $this->_parseOptions($fieldMapping->options->children()); + } + + return $mapping; + } + + /** + * Parse / Normalize the cache configuration + * + * @param SimpleXMLElement $cacheMapping + * + * @return array + */ + private function cacheToArray(SimpleXMLElement $cacheMapping) + { + $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null; + $usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null; + + if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) { + throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage)); + } + + if ($usage) { + $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage); + } + + return array( + 'usage' => $usage, + 'region' => $region, + ); + } + + /** + * Gathers a list of cascade options found in the given cascade element. + * + * @param SimpleXMLElement $cascadeElement The cascade element. + * + * @return array The list of cascade options. + */ + private function _getCascadeMappings(SimpleXMLElement $cascadeElement) + { + $cascades = array(); + /* @var $action SimpleXmlElement */ + foreach ($cascadeElement->children() as $action) { + // According to the JPA specifications, XML uses "cascade-persist" + // instead of "persist". Here, both variations + // are supported because both YAML and Annotation use "persist" + // and we want to make sure that this driver doesn't need to know + // anything about the supported cascading actions + $cascades[] = str_replace('cascade-', '', $action->getName()); + } + return $cascades; + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + $result = array(); + $xmlElement = simplexml_load_file($file); + + if (isset($xmlElement->entity)) { + foreach ($xmlElement->entity as $entityElement) { + $entityName = (string)$entityElement['name']; + $result[$entityName] = $entityElement; + } + } else if (isset($xmlElement->{'mapped-superclass'})) { + foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) { + $className = (string)$mappedSuperClass['name']; + $result[$className] = $mappedSuperClass; + } + } else if (isset($xmlElement->embeddable)) { + foreach ($xmlElement->embeddable as $embeddableElement) { + $embeddableName = (string) $embeddableElement['name']; + $result[$embeddableName] = $embeddableElement; + } + } + + return $result; + } + + /** + * @param mixed $element + * + * @return bool + */ + protected function evaluateBoolean($element) + { + $flag = (string)$element; + + return ($flag == "true" || $flag == "1"); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php new file mode 100644 index 0000000..eab510b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/YamlDriver.php @@ -0,0 +1,790 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder; +use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Yaml\Yaml; + +/** + * The YamlDriver reads the mapping metadata from yaml schema files. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan H. Wage + * @author Roman Borschel + */ +class YamlDriver extends FileDriver +{ + const DEFAULT_FILE_EXTENSION = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENSION) + { + parent::__construct($locator, $fileExtension); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + $element = $this->getElement($className); + + if ($element['type'] == 'entity') { + if (isset($element['repositoryClass'])) { + $metadata->setCustomRepositoryClass($element['repositoryClass']); + } + if (isset($element['readOnly']) && $element['readOnly'] == true) { + $metadata->markReadOnly(); + } + } else if ($element['type'] == 'mappedSuperclass') { + $metadata->setCustomRepositoryClass( + isset($element['repositoryClass']) ? $element['repositoryClass'] : null + ); + $metadata->isMappedSuperclass = true; + } else if ($element['type'] == 'embeddable') { + $metadata->isEmbeddedClass = true; + } else { + throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); + } + + // Evaluate root level properties + $primaryTable = array(); + + if (isset($element['table'])) { + $primaryTable['name'] = $element['table']; + } + + if (isset($element['schema'])) { + $primaryTable['schema'] = $element['schema']; + } + + // Evaluate second level cache + if (isset($element['cache'])) { + $metadata->enableCache($this->cacheToArray($element['cache'])); + } + + $metadata->setPrimaryTable($primaryTable); + + // Evaluate named queries + if (isset($element['namedQueries'])) { + foreach ($element['namedQueries'] as $name => $queryMapping) { + if (is_string($queryMapping)) { + $queryMapping = array('query' => $queryMapping); + } + + if ( ! isset($queryMapping['name'])) { + $queryMapping['name'] = $name; + } + + $metadata->addNamedQuery($queryMapping); + } + } + + // Evaluate named native queries + if (isset($element['namedNativeQueries'])) { + foreach ($element['namedNativeQueries'] as $name => $mappingElement) { + if (!isset($mappingElement['name'])) { + $mappingElement['name'] = $name; + } + $metadata->addNamedNativeQuery(array( + 'name' => $mappingElement['name'], + 'query' => isset($mappingElement['query']) ? $mappingElement['query'] : null, + 'resultClass' => isset($mappingElement['resultClass']) ? $mappingElement['resultClass'] : null, + 'resultSetMapping' => isset($mappingElement['resultSetMapping']) ? $mappingElement['resultSetMapping'] : null, + )); + } + } + + // Evaluate sql result set mappings + if (isset($element['sqlResultSetMappings'])) { + foreach ($element['sqlResultSetMappings'] as $name => $resultSetMapping) { + if (!isset($resultSetMapping['name'])) { + $resultSetMapping['name'] = $name; + } + + $entities = array(); + $columns = array(); + if (isset($resultSetMapping['entityResult'])) { + foreach ($resultSetMapping['entityResult'] as $entityResultElement) { + $entityResult = array( + 'fields' => array(), + 'entityClass' => isset($entityResultElement['entityClass']) ? $entityResultElement['entityClass'] : null, + 'discriminatorColumn' => isset($entityResultElement['discriminatorColumn']) ? $entityResultElement['discriminatorColumn'] : null, + ); + + if (isset($entityResultElement['fieldResult'])) { + foreach ($entityResultElement['fieldResult'] as $fieldResultElement) { + $entityResult['fields'][] = array( + 'name' => isset($fieldResultElement['name']) ? $fieldResultElement['name'] : null, + 'column' => isset($fieldResultElement['column']) ? $fieldResultElement['column'] : null, + ); + } + } + + $entities[] = $entityResult; + } + } + + + if (isset($resultSetMapping['columnResult'])) { + foreach ($resultSetMapping['columnResult'] as $columnResultAnnot) { + $columns[] = array( + 'name' => isset($columnResultAnnot['name']) ? $columnResultAnnot['name'] : null, + ); + } + } + + $metadata->addSqlResultSetMapping(array( + 'name' => $resultSetMapping['name'], + 'entities' => $entities, + 'columns' => $columns + )); + } + } + + if (isset($element['inheritanceType'])) { + $metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType']))); + + if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) { + // Evaluate discriminatorColumn + if (isset($element['discriminatorColumn'])) { + $discrColumn = $element['discriminatorColumn']; + $metadata->setDiscriminatorColumn(array( + 'name' => isset($discrColumn['name']) ? (string)$discrColumn['name'] : null, + 'type' => isset($discrColumn['type']) ? (string)$discrColumn['type'] : null, + 'length' => isset($discrColumn['length']) ? (string)$discrColumn['length'] : null, + 'columnDefinition' => isset($discrColumn['columnDefinition']) ? (string)$discrColumn['columnDefinition'] : null + )); + } else { + $metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255)); + } + + // Evaluate discriminatorMap + if (isset($element['discriminatorMap'])) { + $metadata->setDiscriminatorMap($element['discriminatorMap']); + } + } + } + + + // Evaluate changeTrackingPolicy + if (isset($element['changeTrackingPolicy'])) { + $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' + . strtoupper($element['changeTrackingPolicy']))); + } + + // Evaluate indexes + if (isset($element['indexes'])) { + foreach ($element['indexes'] as $name => $indexYml) { + if ( ! isset($indexYml['name'])) { + $indexYml['name'] = $name; + } + + if (is_string($indexYml['columns'])) { + $index = array('columns' => array_map('trim', explode(',', $indexYml['columns']))); + } else { + $index = array('columns' => $indexYml['columns']); + } + + if (isset($indexYml['flags'])) { + if (is_string($indexYml['flags'])) { + $index['flags'] = array_map('trim', explode(',', $indexYml['flags'])); + } else { + $index['flags'] = $indexYml['flags']; + } + } + + if (isset($indexYml['options'])) { + $index['options'] = $indexYml['options']; + } + + $metadata->table['indexes'][$indexYml['name']] = $index; + } + } + + // Evaluate uniqueConstraints + if (isset($element['uniqueConstraints'])) { + foreach ($element['uniqueConstraints'] as $name => $uniqueYml) { + if ( ! isset($uniqueYml['name'])) { + $uniqueYml['name'] = $name; + } + + if (is_string($uniqueYml['columns'])) { + $unique = array('columns' => array_map('trim', explode(',', $uniqueYml['columns']))); + } else { + $unique = array('columns' => $uniqueYml['columns']); + } + + if (isset($uniqueYml['options'])) { + $unique['options'] = $uniqueYml['options']; + } + + $metadata->table['uniqueConstraints'][$uniqueYml['name']] = $unique; + } + } + + if (isset($element['options'])) { + $metadata->table['options'] = $element['options']; + } + + $associationIds = array(); + if (isset($element['id'])) { + // Evaluate identifier settings + foreach ($element['id'] as $name => $idElement) { + if (isset($idElement['associationKey']) && $idElement['associationKey'] == true) { + $associationIds[$name] = true; + continue; + } + + $mapping = array( + 'id' => true, + 'fieldName' => $name + ); + + if (isset($idElement['type'])) { + $mapping['type'] = $idElement['type']; + } + + if (isset($idElement['column'])) { + $mapping['columnName'] = $idElement['column']; + } + + if (isset($idElement['length'])) { + $mapping['length'] = $idElement['length']; + } + + if (isset($idElement['columnDefinition'])) { + $mapping['columnDefinition'] = $idElement['columnDefinition']; + } + + if (isset($idElement['options'])) { + $mapping['options'] = $idElement['options']; + } + + $metadata->mapField($mapping); + + if (isset($idElement['generator'])) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . strtoupper($idElement['generator']['strategy']))); + } + // Check for SequenceGenerator/TableGenerator definition + if (isset($idElement['sequenceGenerator'])) { + $metadata->setSequenceGeneratorDefinition($idElement['sequenceGenerator']); + } else if (isset($idElement['customIdGenerator'])) { + $customGenerator = $idElement['customIdGenerator']; + $metadata->setCustomGeneratorDefinition(array( + 'class' => (string) $customGenerator['class'] + )); + } else if (isset($idElement['tableGenerator'])) { + throw MappingException::tableIdGeneratorNotImplemented($className); + } + } + } + + // Evaluate fields + if (isset($element['fields'])) { + foreach ($element['fields'] as $name => $fieldMapping) { + + $mapping = $this->columnToArray($name, $fieldMapping); + + if (isset($fieldMapping['id'])) { + $mapping['id'] = true; + if (isset($fieldMapping['generator']['strategy'])) { + $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' + . strtoupper($fieldMapping['generator']['strategy']))); + } + } + + if (isset($mapping['version'])) { + $metadata->setVersionMapping($mapping); + unset($mapping['version']); + } + + $metadata->mapField($mapping); + } + } + + if (isset($element['embedded'])) { + foreach ($element['embedded'] as $name => $embeddedMapping) { + $mapping = array( + 'fieldName' => $name, + 'class' => $embeddedMapping['class'], + 'columnPrefix' => isset($embeddedMapping['columnPrefix']) ? $embeddedMapping['columnPrefix'] : null, + ); + $metadata->mapEmbedded($mapping); + } + } + + // Evaluate oneToOne relationships + if (isset($element['oneToOne'])) { + foreach ($element['oneToOne'] as $name => $oneToOneElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $oneToOneElement['targetEntity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($oneToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneElement['fetch']); + } + + if (isset($oneToOneElement['mappedBy'])) { + $mapping['mappedBy'] = $oneToOneElement['mappedBy']; + } else { + if (isset($oneToOneElement['inversedBy'])) { + $mapping['inversedBy'] = $oneToOneElement['inversedBy']; + } + + $joinColumns = array(); + + if (isset($oneToOneElement['joinColumn'])) { + $joinColumns[] = $this->joinColumnToArray($oneToOneElement['joinColumn']); + } else if (isset($oneToOneElement['joinColumns'])) { + foreach ($oneToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + } + + if (isset($oneToOneElement['cascade'])) { + $mapping['cascade'] = $oneToOneElement['cascade']; + } + + if (isset($oneToOneElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$oneToOneElement['orphanRemoval']; + } + + $metadata->mapOneToOne($mapping); + + // Evaluate second level cache + if (isset($oneToOneElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToOneElement['cache'])); + } + } + } + + // Evaluate oneToMany relationships + if (isset($element['oneToMany'])) { + foreach ($element['oneToMany'] as $name => $oneToManyElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $oneToManyElement['targetEntity'], + 'mappedBy' => $oneToManyElement['mappedBy'] + ); + + if (isset($oneToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyElement['fetch']); + } + + if (isset($oneToManyElement['cascade'])) { + $mapping['cascade'] = $oneToManyElement['cascade']; + } + + if (isset($oneToManyElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$oneToManyElement['orphanRemoval']; + } + + if (isset($oneToManyElement['orderBy'])) { + $mapping['orderBy'] = $oneToManyElement['orderBy']; + } + + if (isset($oneToManyElement['indexBy'])) { + $mapping['indexBy'] = $oneToManyElement['indexBy']; + } + + $metadata->mapOneToMany($mapping); + + // Evaluate second level cache + if (isset($oneToManyElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($oneToManyElement['cache'])); + } + } + } + + // Evaluate manyToOne relationships + if (isset($element['manyToOne'])) { + foreach ($element['manyToOne'] as $name => $manyToOneElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $manyToOneElement['targetEntity'] + ); + + if (isset($associationIds[$mapping['fieldName']])) { + $mapping['id'] = true; + } + + if (isset($manyToOneElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneElement['fetch']); + } + + if (isset($manyToOneElement['inversedBy'])) { + $mapping['inversedBy'] = $manyToOneElement['inversedBy']; + } + + $joinColumns = array(); + + if (isset($manyToOneElement['joinColumn'])) { + $joinColumns[] = $this->joinColumnToArray($manyToOneElement['joinColumn']); + } else if (isset($manyToOneElement['joinColumns'])) { + foreach ($manyToOneElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinColumns'] = $joinColumns; + + if (isset($manyToOneElement['cascade'])) { + $mapping['cascade'] = $manyToOneElement['cascade']; + } + + $metadata->mapManyToOne($mapping); + + // Evaluate second level cache + if (isset($manyToOneElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToOneElement['cache'])); + } + } + } + + // Evaluate manyToMany relationships + if (isset($element['manyToMany'])) { + foreach ($element['manyToMany'] as $name => $manyToManyElement) { + $mapping = array( + 'fieldName' => $name, + 'targetEntity' => $manyToManyElement['targetEntity'] + ); + + if (isset($manyToManyElement['fetch'])) { + $mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyElement['fetch']); + } + + if (isset($manyToManyElement['mappedBy'])) { + $mapping['mappedBy'] = $manyToManyElement['mappedBy']; + } else if (isset($manyToManyElement['joinTable'])) { + + $joinTableElement = $manyToManyElement['joinTable']; + $joinTable = array( + 'name' => $joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = $joinTableElement['schema']; + } + + if (isset($joinTableElement['joinColumns'])) { + foreach ($joinTableElement['joinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + if (isset($joinTableElement['inverseJoinColumns'])) { + foreach ($joinTableElement['inverseJoinColumns'] as $joinColumnName => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $joinColumnName; + } + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + } + + $mapping['joinTable'] = $joinTable; + } + + if (isset($manyToManyElement['inversedBy'])) { + $mapping['inversedBy'] = $manyToManyElement['inversedBy']; + } + + if (isset($manyToManyElement['cascade'])) { + $mapping['cascade'] = $manyToManyElement['cascade']; + } + + if (isset($manyToManyElement['orderBy'])) { + $mapping['orderBy'] = $manyToManyElement['orderBy']; + } + + if (isset($manyToManyElement['indexBy'])) { + $mapping['indexBy'] = $manyToManyElement['indexBy']; + } + + if (isset($manyToManyElement['orphanRemoval'])) { + $mapping['orphanRemoval'] = (bool)$manyToManyElement['orphanRemoval']; + } + + $metadata->mapManyToMany($mapping); + + // Evaluate second level cache + if (isset($manyToManyElement['cache'])) { + $metadata->enableAssociationCache($mapping['fieldName'], $this->cacheToArray($manyToManyElement['cache'])); + } + } + } + + // Evaluate associationOverride + if (isset($element['associationOverride']) && is_array($element['associationOverride'])) { + + foreach ($element['associationOverride'] as $fieldName => $associationOverrideElement) { + $override = array(); + + // Check for joinColumn + if (isset($associationOverrideElement['joinColumn'])) { + $joinColumns = array(); + foreach ($associationOverrideElement['joinColumn'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + $joinColumns[] = $this->joinColumnToArray($joinColumnElement); + } + $override['joinColumns'] = $joinColumns; + } + + // Check for joinTable + if (isset($associationOverrideElement['joinTable'])) { + + $joinTableElement = $associationOverrideElement['joinTable']; + $joinTable = array( + 'name' => $joinTableElement['name'] + ); + + if (isset($joinTableElement['schema'])) { + $joinTable['schema'] = $joinTableElement['schema']; + } + + foreach ($joinTableElement['joinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + foreach ($joinTableElement['inverseJoinColumns'] as $name => $joinColumnElement) { + if ( ! isset($joinColumnElement['name'])) { + $joinColumnElement['name'] = $name; + } + + $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumnElement); + } + + $override['joinTable'] = $joinTable; + } + + $metadata->setAssociationOverride($fieldName, $override); + } + } + + // Evaluate associationOverride + if (isset($element['attributeOverride']) && is_array($element['attributeOverride'])) { + + foreach ($element['attributeOverride'] as $fieldName => $attributeOverrideElement) { + $mapping = $this->columnToArray($fieldName, $attributeOverrideElement); + $metadata->setAttributeOverride($fieldName, $mapping); + } + } + + // Evaluate lifeCycleCallbacks + if (isset($element['lifecycleCallbacks'])) { + foreach ($element['lifecycleCallbacks'] as $type => $methods) { + foreach ($methods as $method) { + $metadata->addLifecycleCallback($method, constant('Doctrine\ORM\Events::' . $type)); + } + } + } + + // Evaluate entityListeners + if (isset($element['entityListeners'])) { + foreach ($element['entityListeners'] as $className => $entityListener) { + // Evaluate the listener using naming convention. + if (empty($entityListener)) { + EntityListenerBuilder::bindEntityListener($metadata, $className); + + continue; + } + + foreach ($entityListener as $eventName => $callbackElement){ + foreach ($callbackElement as $methodName) { + $metadata->addEntityListener($eventName, $className, $methodName); + } + } + } + } + } + + /** + * Constructs a joinColumn mapping array based on the information + * found in the given join column element. + * + * @param array $joinColumnElement The array join column element. + * + * @return array The mapping array. + */ + private function joinColumnToArray($joinColumnElement) + { + $joinColumn = array(); + if (isset($joinColumnElement['referencedColumnName'])) { + $joinColumn['referencedColumnName'] = (string) $joinColumnElement['referencedColumnName']; + } + + if (isset($joinColumnElement['name'])) { + $joinColumn['name'] = (string) $joinColumnElement['name']; + } + + if (isset($joinColumnElement['fieldName'])) { + $joinColumn['fieldName'] = (string) $joinColumnElement['fieldName']; + } + + if (isset($joinColumnElement['unique'])) { + $joinColumn['unique'] = (bool) $joinColumnElement['unique']; + } + + if (isset($joinColumnElement['nullable'])) { + $joinColumn['nullable'] = (bool) $joinColumnElement['nullable']; + } + + if (isset($joinColumnElement['onDelete'])) { + $joinColumn['onDelete'] = $joinColumnElement['onDelete']; + } + + if (isset($joinColumnElement['columnDefinition'])) { + $joinColumn['columnDefinition'] = $joinColumnElement['columnDefinition']; + } + + return $joinColumn; + } + + /** + * Parses the given column as array. + * + * @param string $fieldName + * @param array $column + * + * @return array + */ + private function columnToArray($fieldName, $column) + { + $mapping = array( + 'fieldName' => $fieldName + ); + + if (isset($column['type'])) { + $params = explode('(', $column['type']); + $column['type'] = $params[0]; + $mapping['type'] = $column['type']; + + if (isset($params[1])) { + $column['length'] = (integer) substr($params[1], 0, strlen($params[1]) - 1); + } + } + + if (isset($column['column'])) { + $mapping['columnName'] = $column['column']; + } + + if (isset($column['length'])) { + $mapping['length'] = $column['length']; + } + + if (isset($column['precision'])) { + $mapping['precision'] = $column['precision']; + } + + if (isset($column['scale'])) { + $mapping['scale'] = $column['scale']; + } + + if (isset($column['unique'])) { + $mapping['unique'] = (bool)$column['unique']; + } + + if (isset($column['options'])) { + $mapping['options'] = $column['options']; + } + + if (isset($column['nullable'])) { + $mapping['nullable'] = $column['nullable']; + } + + if (isset($column['version']) && $column['version']) { + $mapping['version'] = $column['version']; + } + + if (isset($column['columnDefinition'])) { + $mapping['columnDefinition'] = $column['columnDefinition']; + } + + return $mapping; + } + + /** + * Parse / Normalize the cache configuration + * + * @param array $cacheMapping + * + * @return array + */ + private function cacheToArray($cacheMapping) + { + $region = isset($cacheMapping['region']) ? (string) $cacheMapping['region'] : null; + $usage = isset($cacheMapping['usage']) ? strtoupper($cacheMapping['usage']) : null; + + if ($usage && ! defined('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage)) { + throw new \InvalidArgumentException(sprintf('Invalid cache usage "%s"', $usage)); + } + + if ($usage) { + $usage = constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' . $usage); + } + + return array( + 'usage' => $usage, + 'region' => $region, + ); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return Yaml::parse(file_get_contents($file)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embeddable.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embeddable.php new file mode 100644 index 0000000..f14bfac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embeddable.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Embeddable implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embedded.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embedded.php new file mode 100644 index 0000000..3733910 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Embedded.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Embedded implements Annotation +{ + /** + * @Required + * @var string + */ + public $class; + + /** + * @var mixed + */ + public $columnPrefix; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php new file mode 100644 index 0000000..edf6ad5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Entity.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Entity implements Annotation +{ + /** + * @var string + */ + public $repositoryClass; + + /** + * @var boolean + */ + public $readOnly = false; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php new file mode 100644 index 0000000..2d5ecf7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A resolver is used to instantiate an entity listener. + * + * @since 2.4 + * @author Fabio B. Silva + */ +interface EntityListenerResolver +{ + /** + * Clear all instances from the set, or a specific class when given. + * + * @param string $className The fully-qualified class name + * + * @return void + */ + function clear($className = null); + + /** + * Returns a entity listener instance for the given class name. + * + * @param string $className The fully-qualified class name + * + * @return object An entity listener + */ + function resolve($className); + + /** + * Register a entity listener instance. + * + * @param object $object An entity listener + */ + function register($object); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php new file mode 100644 index 0000000..d9478a4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityListeners.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The EntityListeners annotation specifies the callback listener classes to be used for an entity or mapped superclass. + * The EntityListeners annotation may be applied to an entity class or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.4 + * + * @Annotation + * @Target("CLASS") + */ +final class EntityListeners implements Annotation +{ + /** + * Specifies the names of the entity listeners. + * + * @var array + */ + public $value = array(); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php new file mode 100644 index 0000000..63bbed2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/EntityResult.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * References an entity in the SELECT clause of a SQL query. + * If this annotation is used, the SQL statement should select all of the columns that are mapped to the entity object. + * This should include foreign key columns to related entities. + * The results obtained when insufficient data is available are undefined. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class EntityResult implements Annotation +{ + /** + * The class of the result. + * + * @var string + */ + public $entityClass; + + /** + * Maps the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @var array<\Doctrine\ORM\Mapping\FieldResult> + */ + public $fields = array(); + + /** + * Specifies the column name of the column in the SELECT list that is used to determine the type of the entity instance. + * + * @var string + */ + public $discriminatorColumn; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php new file mode 100644 index 0000000..5e8aa0c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/FieldResult.php @@ -0,0 +1,46 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to map the columns specified in the SELECT list of the query to the properties or fields of the entity class. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class FieldResult implements Annotation +{ + /** + * Name of the column in the SELECT clause. + * + * @var string + */ + public $name; + + /** + * Name of the persistent field or property of the class. + * + * @var string + */ + public $column; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php new file mode 100644 index 0000000..27c03d4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/GeneratedValue.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class GeneratedValue implements Annotation +{ + /** + * The type of Id generator. + * + * @var string + * + * @Enum({"AUTO", "SEQUENCE", "TABLE", "IDENTITY", "NONE", "UUID", "CUSTOM"}) + */ + public $strategy = 'AUTO'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php new file mode 100644 index 0000000..313ece3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class HasLifecycleCallbacks implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php new file mode 100644 index 0000000..6c9bcef --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Id.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Id implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php new file mode 100644 index 0000000..45953a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Index.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class Index implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var array + */ + public $columns; + + /** + * @var array + */ + public $flags; + + /** + * @var array + */ + public $options; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php new file mode 100644 index 0000000..de80336 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/InheritanceType.php @@ -0,0 +1,36 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class InheritanceType implements Annotation +{ + /** + * The inheritance type used by the class and its subclasses. + * + * @var string + * + * @Enum({"NONE", "JOINED", "SINGLE_TABLE", "TABLE_PER_CLASS"}) + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php new file mode 100644 index 0000000..febce91 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumn.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class JoinColumn implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $referencedColumnName = 'id'; + + /** + * @var boolean + */ + public $unique = false; + + /** + * @var boolean + */ + public $nullable = true; + + /** + * @var mixed + */ + public $onDelete; + + /** + * @var string + */ + public $columnDefinition; + + /** + * Field name used in non-object hydration (array/scalar). + * + * @var string + */ + public $fieldName; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php new file mode 100644 index 0000000..ae096c2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinColumns.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class JoinColumns implements Annotation +{ + /** + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php new file mode 100644 index 0000000..8a440c6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/JoinTable.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target({"PROPERTY","ANNOTATION"}) + */ +final class JoinTable implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $schema; + + /** + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $joinColumns = array(); + + /** + * @var array<\Doctrine\ORM\Mapping\JoinColumn> + */ + public $inverseJoinColumns = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php new file mode 100644 index 0000000..ca2f53c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToMany.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToMany implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $inversedBy; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var boolean + */ + public $orphanRemoval = false; + + /** + * @var string + */ + public $indexBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php new file mode 100644 index 0000000..d3414e6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ManyToOne.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class ManyToOne implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var string + */ + public $inversedBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php new file mode 100644 index 0000000..7458810 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappedSuperclass.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class MappedSuperclass implements Annotation +{ + /** + * @var string + */ + public $repositoryClass; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php new file mode 100644 index 0000000..9e6b4b3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php @@ -0,0 +1,812 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A MappingException indicates that something is wrong with the mapping setup. + * + * @since 2.0 + */ +class MappingException extends \Doctrine\ORM\ORMException +{ + /** + * @return MappingException + */ + public static function pathRequired() + { + return new self("Specifying the paths to your entities is required ". + "in the AnnotationDriver to retrieve all class names."); + } + + /** + * @param string $entityName + * + * @return MappingException + */ + public static function identifierRequired($entityName) + { + if (false !== ($parent = get_parent_class($entityName))) { + return new self(sprintf( + 'No identifier/primary key specified for Entity "%s" sub class of "%s". Every Entity must have an identifier/primary key.', + $entityName, $parent + )); + } + + return new self(sprintf( + 'No identifier/primary key specified for Entity "%s". Every Entity must have an identifier/primary key.', + $entityName + )); + + } + + /** + * @param string $entityName + * @param string $type + * + * @return MappingException + */ + public static function invalidInheritanceType($entityName, $type) + { + return new self("The inheritance type '$type' specified for '$entityName' does not exist."); + } + + /** + * @return MappingException + */ + public static function generatorNotAllowedWithCompositeId() + { + return new self("Id generators can't be used with a composite id."); + } + + /** + * @param string $entity + * + * @return MappingException + */ + public static function missingFieldName($entity) + { + return new self("The field or association mapping misses the 'fieldName' attribute in entity '$entity'."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingTargetEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'targetEntity' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingSourceEntity($fieldName) + { + return new self("The association mapping '$fieldName' misses the 'sourceEntity' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function missingEmbeddedClass($fieldName) + { + return new self("The embed mapping '$fieldName' misses the 'class' attribute."); + } + + /** + * @param string $entityName + * @param string $fileName + * + * @return MappingException + */ + public static function mappingFileNotFound($entityName, $fileName) + { + return new self("No mapping file found named '$fileName' for class '$entityName'."); + } + + /** + * Exception for invalid property name override. + * + * @param string $className The entity's name. + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidOverrideFieldName($className, $fieldName) + { + return new self("Invalid field override named '$fieldName' for class '$className'."); + } + + /** + * Exception for invalid property type override. + * + * @param string $className The entity's name. + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidOverrideFieldType($className, $fieldName) + { + return new self("The column type of attribute '$fieldName' on class '$className' could not be changed."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return MappingException + */ + public static function mappingNotFound($className, $fieldName) + { + return new self("No mapping found for field '$fieldName' on class '$className'."); + } + + /** + * @param string $className + * @param string $queryName + * + * @return MappingException + */ + public static function queryNotFound($className, $queryName) + { + return new self("No query found named '$queryName' on class '$className'."); + } + + /** + * @param string $className + * @param string $resultName + * + * @return MappingException + */ + public static function resultMappingNotFound($className, $resultName) + { + return new self("No result set mapping found named '$resultName' on class '$className'."); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function emptyQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" could not be empty.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForQueryMapping($className) + { + return new self("Query name on entity class '$className' is not defined."); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function missingQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.' requires a result class or result set mapping.'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function missingResultSetMappingEntity($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a entity class name.'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function missingResultSetMappingFieldName($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.' requires a field name.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForSqlResultSetMapping($className) + { + return new self("Result set mapping name on entity class '$className' is not defined."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function oneToManyRequiresMappedBy($fieldName) + { + return new self("OneToMany mapping on field '$fieldName' requires the 'mappedBy' attribute."); + } + + /** + * @param string $fieldName + * + * @return MappingException + */ + public static function joinTableRequired($fieldName) + { + return new self("The mapping of field '$fieldName' requires an the 'joinTable' attribute."); + } + + /** + * Called if a required option was not found but is required + * + * @param string $field Which field cannot be processed? + * @param string $expectedOption Which option is required + * @param string $hint Can optionally be used to supply a tip for common mistakes, + * e.g. "Did you think of the plural s?" + * + * @return MappingException + */ + static function missingRequiredOption($field, $expectedOption, $hint = '') + { + $message = "The mapping of field '{$field}' is invalid: The option '{$expectedOption}' is required."; + + if ( ! empty($hint)) { + $message .= ' (Hint: ' . $hint . ')'; + } + + return new self($message); + } + + /** + * Generic exception for invalid mappings. + * + * @param string $fieldName + * + * @return MappingException + */ + public static function invalidMapping($fieldName) + { + return new self("The mapping of field '$fieldName' is invalid."); + } + + /** + * Exception for reflection exceptions - adds the entity name, + * because there might be long classnames that will be shortened + * within the stacktrace + * + * @param string $entity The entity's name + * @param \ReflectionException $previousException + * + * @return MappingException + */ + public static function reflectionFailure($entity, \ReflectionException $previousException) + { + return new self('An error occurred in ' . $entity, 0, $previousException); + } + + /** + * @param string $className + * @param string $joinColumn + * + * @return MappingException + */ + public static function joinColumnMustPointToMappedField($className, $joinColumn) + { + return new self('The column ' . $joinColumn . ' must be mapped to a field in class ' + . $className . ' since it is referenced by a join column of another class.'); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function classIsNotAValidEntityOrMappedSuperClass($className) + { + if (false !== ($parent = get_parent_class($className))) { + return new self(sprintf( + 'Class "%s" sub class of "%s" is not a valid entity or mapped super class.', + $className, $parent + )); + } + + return new self(sprintf( + 'Class "%s" is not a valid entity or mapped super class.', + $className + )); + } + + /** + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function propertyTypeIsRequired($className, $propertyName) + { + return new self("The attribute 'type' is required for the column description of property ".$className."::\$".$propertyName."."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function tableIdGeneratorNotImplemented($className) + { + return new self("TableIdGenerator is not yet implemented for use with class ".$className); + } + + /** + * @param string $entity The entity's name. + * @param string $fieldName The name of the field that was already declared. + * + * @return MappingException + */ + public static function duplicateFieldMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $fieldName + * + * @return MappingException + */ + public static function duplicateAssociationMapping($entity, $fieldName) + { + return new self('Property "'.$fieldName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $queryName + * + * @return MappingException + */ + public static function duplicateQueryMapping($entity, $queryName) + { + return new self('Query named "'.$queryName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * @param string $resultName + * + * @return MappingException + */ + public static function duplicateResultSetMapping($entity, $resultName) + { + return new self('Result set mapping named "'.$resultName.'" in "'.$entity.'" was already declared, but it must be declared only once'); + } + + /** + * @param string $entity + * + * @return MappingException + */ + public static function singleIdNotAllowedOnCompositePrimaryKey($entity) + { + return new self('Single id is not allowed on composite primary key in entity '.$entity); + } + + /** + * @param string $entity + * @param string $fieldName + * @param string $unsupportedType + * + * @return MappingException + */ + public static function unsupportedOptimisticLockingType($entity, $fieldName, $unsupportedType) + { + return new self('Locking type "'.$unsupportedType.'" (specified in "'.$entity.'", field "'.$fieldName.'") ' + .'is not supported by Doctrine.' + ); + } + + /** + * @param string|null $path + * + * @return MappingException + */ + public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null) + { + if ( ! empty($path)) { + $path = '[' . $path . ']'; + } + + return new self( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path ' . $path . ' seems to be incorrect!' + ); + } + + /** + * Returns an exception that indicates that a class used in a discriminator map does not exist. + * An example would be an outdated (maybe renamed) classname. + * + * @param string $className The class that could not be found + * @param string $owningClass The class that declares the discriminator map. + * + * @return MappingException + */ + public static function invalidClassInDiscriminatorMap($className, $owningClass) + { + return new self( + "Entity class '$className' used in the discriminator map of class '$owningClass' ". + "does not exist." + ); + } + + /** + * @param string $className + * @param array $entries + * @param array $map + * + * @return MappingException + */ + public static function duplicateDiscriminatorEntry($className, array $entries, array $map) + { + return new self( + "The entries " . implode(', ', $entries) . " in discriminator map of class '" . $className . "' is duplicated. " . + "If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. " . + "The entries of the current map are: @DiscriminatorMap({" . implode(', ', array_map( + function($a, $b) { return "'$a': '$b'"; }, array_keys($map), array_values($map) + )) . "})" + ); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingDiscriminatorMap($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator map was defined."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingDiscriminatorColumn($className) + { + return new self("Entity class '$className' is using inheritance but no discriminator column was defined."); + } + + /** + * @param string $className + * @param string $type + * + * @return MappingException + */ + public static function invalidDiscriminatorColumnType($className, $type) + { + return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!"); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function nameIsMandatoryForDiscriminatorColumns($className) + { + return new self("Discriminator column name on entity class '$className' is not defined."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return MappingException + */ + public static function cannotVersionIdField($className, $fieldName) + { + return new self("Setting Id field '$fieldName' as versionable in entity class '$className' is not supported."); + } + + /** + * @param string $className + * @param string $fieldName + * @param string $type + * + * @return MappingException + */ + public static function sqlConversionNotAllowedForIdentifiers($className, $fieldName, $type) + { + return new self("It is not possible to set id field '$fieldName' to type '$type' in entity class '$className'. The type '$type' requires conversion SQL which is not allowed for identifiers."); + } + + /** + * @param string $className + * @param string $columnName + * + * @return MappingException + */ + public static function duplicateColumnName($className, $columnName) + { + return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalToManyAssociationOnMappedSuperclass($className, $field) + { + return new self("It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'."); + } + + /** + * @param string $className + * @param string $targetEntity + * @param string $targetField + * + * @return MappingException + */ + public static function cannotMapCompositePrimaryKeyEntitiesAsForeignId($className, $targetEntity, $targetField) + { + return new self("It is not possible to map entity '".$className."' with a composite primary key ". + "as part of the primary key of another entity '".$targetEntity."#".$targetField."'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function noSingleAssociationJoinColumnFound($className, $field) + { + return new self("'$className#$field' is not an association with a single join column."); + } + + /** + * @param string $className + * @param string $column + * + * @return MappingException + */ + public static function noFieldNameFoundForColumn($className, $column) + { + return new self("Cannot find a field on '$className' that is mapped to column '$column'. Either the ". + "field does not exist or an association exists but it has multiple join columns."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalOrphanRemovalOnIdentifierAssociation($className, $field) + { + return new self("The orphan removal option is not allowed on an association that is ". + "part of the identifier in '$className#$field'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalOrphanRemoval($className, $field) + { + return new self("Orphan removal is only allowed on one-to-one and one-to-many ". + "associations, but " . $className."#" .$field . " is not."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalInverseIdentifierAssociation($className, $field) + { + return new self("An inverse association is not allowed to be identifier in '$className#$field'."); + } + + /** + * @param string $className + * @param string $field + * + * @return MappingException + */ + public static function illegalToManyIdentifierAssociation($className, $field) + { + return new self("Many-to-many or one-to-many associations are not allowed to be identifier in '$className#$field'."); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function noInheritanceOnMappedSuperClass($className) + { + return new self("It is not supported to define inheritance information on a mapped superclass '" . $className . "'."); + } + + /** + * @param string $className + * @param string $rootClassName + * + * @return MappingException + */ + public static function mappedClassNotPartOfDiscriminatorMap($className, $rootClassName) + { + return new self( + "Entity '" . $className . "' has to be part of the discriminator map of '" . $rootClassName . "' " . + "to be properly mapped in the inheritance hierarchy. Alternatively you can make '".$className."' an abstract class " . + "to avoid this exception from occurring." + ); + } + + /** + * @param string $className + * @param string $methodName + * + * @return MappingException + */ + public static function lifecycleCallbackMethodNotFound($className, $methodName) + { + return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback."); + } + + /** + * @param string $listenerName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function entityListenerClassNotFound($listenerName, $className) + { + return new self(sprintf('Entity Listener "%s" declared on "%s" not found.', $listenerName, $className)); + } + + /** + * @param string $listenerName + * @param string $methodName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function entityListenerMethodNotFound($listenerName, $methodName, $className) + { + return new self(sprintf('Entity Listener "%s" declared on "%s" has no method "%s".', $listenerName, $className, $methodName)); + } + + /** + * @param string $listenerName + * @param string $methodName + * @param string $className + * + * @return \Doctrine\ORM\Mapping\MappingException + */ + public static function duplicateEntityListener($listenerName, $methodName, $className) + { + return new self(sprintf('Entity Listener "%s#%s()" in "%s" was already declared, but it must be declared only once.', $listenerName, $methodName, $className)); + } + + /** + * @param string $className + * @param string $annotation + * + * @return MappingException + */ + public static function invalidFetchMode($className, $annotation) + { + return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'"); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function compositeKeyAssignedIdGeneratorRequired($className) + { + return new self("Entity '". $className . "' has a composite identifier but uses an ID generator other than manually assigning (Identity, Sequence). This is not supported."); + } + + /** + * @param string $targetEntity + * @param string $sourceEntity + * @param string $associationName + * + * @return MappingException + */ + public static function invalidTargetEntityClass($targetEntity, $sourceEntity, $associationName) + { + return new self("The target-entity " . $targetEntity . " cannot be found in '" . $sourceEntity."#".$associationName."'."); + } + + /** + * @param array $cascades + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function invalidCascadeOption(array $cascades, $className, $propertyName) + { + $cascades = implode(", ", array_map(function ($e) { return "'" . $e . "'"; }, $cascades)); + return new self(sprintf( + "You have specified invalid cascade options for %s::$%s: %s; available options: 'remove', 'persist', 'refresh', 'merge', and 'detach'", + $className, + $propertyName, + $cascades + )); + } + + /** + * @param string $className + * + * @return MappingException + */ + public static function missingSequenceName($className) + { + return new self( + sprintf('Missing "sequenceName" attribute for sequence id generator definition on class "%s".', $className) + ); + } + + /** + * @param string $className + * @param string $propertyName + * + * @return MappingException + */ + public static function infiniteEmbeddableNesting($className, $propertyName) + { + return new self( + sprintf( + 'Infinite nesting detected for embedded property %s::%s. ' . + 'You cannot embed an embeddable from the same type inside an embeddable.', + $className, + $propertyName + ) + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php new file mode 100644 index 0000000..7f5460a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQueries.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of native SQL named queries. + * The NamedNativeQueries annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class NamedNativeQueries implements Annotation +{ + /** + * One or more NamedNativeQuery annotations. + * + * @var array<\Doctrine\ORM\Mapping\NamedNativeQuery> + */ + public $value = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php new file mode 100644 index 0000000..f336c99 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedNativeQuery.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify a native SQL named query. + * The NamedNativeQuery annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedNativeQuery implements Annotation +{ + /** + * The name used to refer to the query with the EntityManager methods that create query objects. + * + * @var string + */ + public $name; + + /** + * The SQL query string. + * + * @var string + */ + public $query; + + /** + * The class of the result. + * + * @var string + */ + public $resultClass; + + /** + * The name of a SqlResultSetMapping, as defined in metadata. + * + * @var string + */ + public $resultSetMapping; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php new file mode 100644 index 0000000..5fce072 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQueries.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class NamedQueries implements Annotation +{ + /** + * @var array<\Doctrine\ORM\Mapping\NamedQuery> + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php new file mode 100644 index 0000000..c4e6cd5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamedQuery.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class NamedQuery implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $query; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php new file mode 100644 index 0000000..8845cb1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/NamingStrategy.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * A set of rules for determining the physical column and table names + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +interface NamingStrategy +{ + /** + * Returns a table name for an entity class. + * + * @param string $className The fully-qualified class name. + * + * @return string A table name. + */ + function classToTableName($className); + + /** + * Returns a column name for a property. + * + * @param string $propertyName A property name. + * @param string|null $className The fully-qualified class name. + * + * @return string A column name. + */ + function propertyToColumnName($propertyName, $className = null); + + /** + * Returns a column name for an embedded property. + * + * @param string $propertyName + * @param string $embeddedColumnName + * + * @return string + */ + function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null); + + /** + * Returns the default reference column name. + * + * @return string A column name. + */ + function referenceColumnName(); + + /** + * Returns a join column name for a property. + * + * @param string $propertyName A property name. + * @param string|null $className The fully-qualified class name. + * This parameter is omitted from the signature due to BC + * + * @return string A join column name. + */ + function joinColumnName($propertyName/*, $className = null*/); + + /** + * Returns a join table name. + * + * @param string $sourceEntity The source entity. + * @param string $targetEntity The target entity. + * @param string|null $propertyName A property name. + * + * @return string A join table name. + */ + function joinTableName($sourceEntity, $targetEntity, $propertyName = null); + + /** + * Returns the foreign key column name for the given parameters. + * + * @param string $entityName An entity. + * @param string|null $referencedColumnName A property. + * + * @return string A join column name. + */ + function joinKeyColumnName($entityName, $referencedColumnName = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php new file mode 100644 index 0000000..4b24657 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToMany.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToMany implements Annotation +{ + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $targetEntity; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var boolean + */ + public $orphanRemoval = false; + + /** + * @var string + */ + public $indexBy; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php new file mode 100644 index 0000000..b2ab81f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OneToOne.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OneToOne implements Annotation +{ + /** + * @var string + */ + public $targetEntity; + + /** + * @var string + */ + public $mappedBy; + + /** + * @var string + */ + public $inversedBy; + + /** + * @var array + */ + public $cascade; + + /** + * The fetching strategy to use for the association. + * + * @var string + * + * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"}) + */ + public $fetch = 'LAZY'; + + /** + * @var boolean + */ + public $orphanRemoval = false; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php new file mode 100644 index 0000000..ad1b7a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/OrderBy.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class OrderBy implements Annotation +{ + /** + * @var array + */ + public $value; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php new file mode 100644 index 0000000..2f8e993 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostLoad.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostLoad implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php new file mode 100644 index 0000000..2aea719 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostPersist.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostPersist implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php new file mode 100644 index 0000000..321c4bd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostRemove.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostRemove implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php new file mode 100644 index 0000000..a55f707 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PostUpdate.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PostUpdate implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php new file mode 100644 index 0000000..6697d37 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreFlush.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreFlush implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php new file mode 100644 index 0000000..fea05be --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PrePersist.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PrePersist implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php new file mode 100644 index 0000000..29822ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreRemove.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreRemove implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php new file mode 100644 index 0000000..290df72 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/PreUpdate.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreUpdate implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php new file mode 100644 index 0000000..1a21ff1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/QuoteStrategy.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * A set of rules for determining the column, alias and table quotes. + * + * @since 2.3 + * @author Fabio B. Silva + */ +interface QuoteStrategy +{ + /** + * Gets the (possibly quoted) column name for safe use in an SQL statement. + * + * @param string $fieldName + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getColumnName($fieldName, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) primary table name for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getTableName(ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) sequence name for safe use in an SQL statement. + * + * @param array $definition + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) name of the join table. + * + * @param array $association + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getJoinTableName(array $association, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) join column name. + * + * @param array $joinColumn + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) join column name. + * + * @param array $joinColumn + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return string + */ + function getReferencedJoinColumnName(array $joinColumn, ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the (possibly quoted) identifier column names for safe use in an SQL statement. + * + * @param ClassMetadata $class + * @param AbstractPlatform $platform + * + * @return array + */ + function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform $platform); + + /** + * Gets the column alias. + * + * @param string $columnName + * @param integer $counter + * @param AbstractPlatform $platform + * @param ClassMetadata|null $class + * + * @return string + */ + function getColumnAlias($columnName, $counter, AbstractPlatform $platform, ClassMetadata $class = null); + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php new file mode 100644 index 0000000..6ac7f11 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Mapping\Reflection; + +use Doctrine\Common\Persistence\Mapping\ReflectionService; +use ReflectionClass; +use ReflectionProperty; + +/** + * Utility class to retrieve all reflection instance properties of a given class, including + * private inherited properties and transient properties. + * + * @private This API is for internal use only + * + * @author Marco Pivetta + */ +final class ReflectionPropertiesGetter +{ + /** + * @var ReflectionProperty[][] indexed by class name and property internal name + */ + private $properties = []; + + /** + * @var ReflectionService + */ + private $reflectionService; + + /** + * @param ReflectionService $reflectionService + */ + public function __construct(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * @param $className + * + * @return ReflectionProperty[] indexed by property internal name + */ + public function getProperties($className) + { + if (isset($this->properties[$className])) { + return $this->properties[$className]; + } + + return $this->properties[$className] = call_user_func_array( + 'array_merge', + // first merge because `array_merge` expects >= 1 params + array_merge( + [[]], + array_map( + [$this, 'getClassProperties'], + $this->getHierarchyClasses($className) + ) + ) + ); + } + + /** + * @param string $className + * + * @return ReflectionClass[] + */ + private function getHierarchyClasses($className) + { + $classes = []; + $parentClassName = $className; + + while ($parentClassName && $currentClass = $this->reflectionService->getClass($parentClassName)) { + $classes[] = $currentClass; + $parentClassName = null; + + if ($parentClass = $currentClass->getParentClass()) { + $parentClassName = $parentClass->getName(); + } + } + + return $classes; + } + + /** + * @param ReflectionClass $reflectionClass + * + * @return ReflectionProperty[] + */ + private function getClassProperties(ReflectionClass $reflectionClass) + { + $properties = $reflectionClass->getProperties(); + + return array_filter( + array_filter(array_map( + [$this, 'getAccessibleProperty'], + array_combine( + array_map([$this, 'getLogicalName'], $properties), + $properties + ) + )), + [$this, 'isInstanceProperty'] + ); + } + + /** + * @param ReflectionProperty $reflectionProperty + * + * @return bool + */ + private function isInstanceProperty(ReflectionProperty $reflectionProperty) + { + return ! $reflectionProperty->isStatic(); + } + + /** + * @param ReflectionProperty $property + * + * @return null|ReflectionProperty + */ + private function getAccessibleProperty(ReflectionProperty $property) + { + return $this->reflectionService->getAccessibleProperty( + $property->getDeclaringClass()->getName(), + $property->getName() + ); + } + + /** + * @param ReflectionProperty $property + * + * @return string + */ + private function getLogicalName(ReflectionProperty $property) + { + $propertyName = $property->getName(); + + if ($property->isPublic()) { + return $propertyName; + } + + if ($property->isProtected()) { + return "\0*\0" . $propertyName; + } + + return "\0" . $property->getDeclaringClass()->getName() . "\0" . $propertyName; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php new file mode 100644 index 0000000..b224fff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ReflectionEmbeddedProperty.php @@ -0,0 +1,101 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +use Doctrine\Instantiator\Instantiator; +use ReflectionProperty; + +/** + * Acts as a proxy to a nested Property structure, making it look like + * just a single scalar property. + * + * This way value objects "just work" without UnitOfWork, Persisters or Hydrators + * needing any changes. + * + * TODO: Move this class into Common\Reflection + */ +class ReflectionEmbeddedProperty extends ReflectionProperty +{ + /** + * @var ReflectionProperty reflection property of the class where the embedded object has to be put + */ + private $parentProperty; + + /** + * @var ReflectionProperty reflection property of the embedded object + */ + private $childProperty; + + /** + * @var string name of the embedded class to be eventually instantiated + */ + private $embeddedClass; + + /** + * @var Instantiator|null + */ + private $instantiator; + + /** + * @param ReflectionProperty $parentProperty + * @param ReflectionProperty $childProperty + * @param string $embeddedClass + */ + public function __construct(ReflectionProperty $parentProperty, ReflectionProperty $childProperty, $embeddedClass) + { + $this->parentProperty = $parentProperty; + $this->childProperty = $childProperty; + $this->embeddedClass = (string) $embeddedClass; + + parent::__construct($childProperty->getDeclaringClass()->getName(), $childProperty->getName()); + } + + /** + * {@inheritDoc} + */ + public function getValue($object = null) + { + $embeddedObject = $this->parentProperty->getValue($object); + + if (null === $embeddedObject) { + return null; + } + + return $this->childProperty->getValue($embeddedObject); + } + + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) + { + $embeddedObject = $this->parentProperty->getValue($object); + + if (null === $embeddedObject) { + $this->instantiator = $this->instantiator ?: new Instantiator(); + + $embeddedObject = $this->instantiator->instantiate($this->embeddedClass); + + $this->parentProperty->setValue($object, $embeddedObject); + } + + $this->childProperty->setValue($embeddedObject, $value); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php new file mode 100644 index 0000000..ba1c45b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SequenceGenerator.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class SequenceGenerator implements Annotation +{ + /** + * @var string + */ + public $sequenceName; + + /** + * @var integer + */ + public $allocationSize = 1; + + /** + * @var integer + */ + public $initialValue = 1; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php new file mode 100644 index 0000000..f5ead7c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMapping.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * The SqlResultSetMapping annotation is used to specify the mapping of the result of a native SQL query. + * The SqlResultSetMapping annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("ANNOTATION") + */ +final class SqlResultSetMapping implements Annotation +{ + /** + * The name given to the result set mapping, and used to refer to it in the methods of the Query API. + * + * @var string + */ + public $name; + + /** + * Specifies the result set mapping to entities. + * + * @var array<\Doctrine\ORM\Mapping\EntityResult> + */ + public $entities = array(); + + /** + * Specifies the result set mapping to scalar values. + * + * @var array<\Doctrine\ORM\Mapping\ColumnResult> + */ + public $columns = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php new file mode 100644 index 0000000..c21b2ab --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/SqlResultSetMappings.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Is used to specify an array of mappings. + * The SqlResultSetMappings annotation can be applied to an entity or mapped superclass. + * + * @author Fabio B. Silva + * @since 2.3 + * + * @Annotation + * @Target("CLASS") + */ +final class SqlResultSetMappings implements Annotation +{ + /** + * One or more SqlResultSetMapping annotations. + * + * @var array<\Doctrine\ORM\Mapping\SqlResultSetMapping> + */ + public $value = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php new file mode 100644 index 0000000..f9f8d4a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Table.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("CLASS") + */ +final class Table implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $schema; + + /** + * @var array<\Doctrine\ORM\Mapping\Index> + */ + public $indexes; + + /** + * @var array<\Doctrine\ORM\Mapping\UniqueConstraint> + */ + public $uniqueConstraints; + + /** + * @var array + */ + public $options = array(); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php new file mode 100644 index 0000000..543d92b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UnderscoreNamingStrategy.php @@ -0,0 +1,146 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * Naming strategy implementing the underscore naming convention. + * Converts 'MyEntity' to 'my_entity' or 'MY_ENTITY'. + * + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Fabio B. Silva + */ +class UnderscoreNamingStrategy implements NamingStrategy +{ + /** + * @var integer + */ + private $case; + + /** + * Underscore naming strategy construct. + * + * @param integer $case CASE_LOWER | CASE_UPPER + */ + public function __construct($case = CASE_LOWER) + { + $this->case = $case; + } + + /** + * @return integer CASE_LOWER | CASE_UPPER + */ + public function getCase() + { + return $this->case; + } + + /** + * Sets string case CASE_LOWER | CASE_UPPER. + * Alphabetic characters converted to lowercase or uppercase. + * + * @param integer $case + * + * @return void + */ + public function setCase($case) + { + $this->case = $case; + } + + /** + * {@inheritdoc} + */ + public function classToTableName($className) + { + if (strpos($className, '\\') !== false) { + $className = substr($className, strrpos($className, '\\') + 1); + } + + return $this->underscore($className); + } + + /** + * {@inheritdoc} + */ + public function propertyToColumnName($propertyName, $className = null) + { + return $this->underscore($propertyName); + } + + /** + * {@inheritdoc} + */ + public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null) + { + return $this->underscore($propertyName).'_'.$embeddedColumnName; + } + + /** + * {@inheritdoc} + */ + public function referenceColumnName() + { + return $this->case === CASE_UPPER ? 'ID' : 'id'; + } + + /** + * {@inheritdoc} + */ + public function joinColumnName($propertyName, $className = null) + { + return $this->underscore($propertyName) . '_' . $this->referenceColumnName(); + } + + /** + * {@inheritdoc} + */ + public function joinTableName($sourceEntity, $targetEntity, $propertyName = null) + { + return $this->classToTableName($sourceEntity) . '_' . $this->classToTableName($targetEntity); + } + + /** + * {@inheritdoc} + */ + public function joinKeyColumnName($entityName, $referencedColumnName = null) + { + return $this->classToTableName($entityName) . '_' . + ($referencedColumnName ?: $this->referenceColumnName()); + } + + /** + * @param string $string + * + * @return string + */ + private function underscore($string) + { + $string = preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $string); + + if ($this->case === CASE_UPPER) { + return strtoupper($string); + } + + return strtolower($string); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php new file mode 100644 index 0000000..f117d18 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/UniqueConstraint.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("ANNOTATION") + */ +final class UniqueConstraint implements Annotation +{ + /** + * @var string + */ + public $name; + + /** + * @var array + */ + public $columns; + + /** + * @var array + */ + public $options; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php new file mode 100644 index 0000000..a237702 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Version.php @@ -0,0 +1,28 @@ +. + */ + +namespace Doctrine\ORM\Mapping; + +/** + * @Annotation + * @Target("PROPERTY") + */ +final class Version implements Annotation +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php new file mode 100644 index 0000000..b19f818 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NativeQuery.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Represents a native SQL query. + * + * @author Roman Borschel + * @since 2.0 + */ +final class NativeQuery extends AbstractQuery +{ + /** + * @var string + */ + private $_sql; + + /** + * Sets the SQL of the query. + * + * @param string $sql + * + * @return NativeQuery This query instance. + */ + public function setSQL($sql) + { + $this->_sql = $sql; + + return $this; + } + + /** + * Gets the SQL query. + * + * @return mixed The built SQL query or an array of all SQL queries. + * + * @override + */ + public function getSQL() + { + return $this->_sql; + } + + /** + * {@inheritdoc} + */ + protected function _doExecute() + { + $parameters = array(); + $types = array(); + + foreach ($this->getParameters() as $parameter) { + $name = $parameter->getName(); + $value = $this->processParameterValue($parameter->getValue()); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : Query\ParameterTypeInferer::inferType($value); + + $parameters[$name] = $value; + $types[$name] = $type; + } + + if ($parameters && is_int(key($parameters))) { + ksort($parameters); + ksort($types); + + $parameters = array_values($parameters); + $types = array_values($types); + } + + return $this->_em->getConnection()->executeQuery( + $this->_sql, $parameters, $types, $this->_queryCacheProfile + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php new file mode 100644 index 0000000..2cbac8e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NoResultException.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly does not return any results. + * + * @author robo + * @since 2.0 + */ +class NoResultException extends UnexpectedResultException +{ + /** + * Constructor. + */ + public function __construct() + { + parent::__construct('No result was found for query although at least one row was expected.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php new file mode 100644 index 0000000..55b7130 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/NonUniqueResultException.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception thrown when an ORM query unexpectedly returns more than one result. + * + * @author robo + * @since 2.0 + */ +class NonUniqueResultException extends UnexpectedResultException +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php new file mode 100644 index 0000000..d4a729d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php @@ -0,0 +1,328 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Cache\Cache as CacheDriver; +use Exception; + +/** + * Base exception class for all ORM exceptions. + * + * @author Roman Borschel + * @since 2.0 + */ +class ORMException extends Exception +{ + /** + * @return ORMException + */ + public static function missingMappingDriverImpl() + { + return new self("It's a requirement to specify a Metadata Driver and pass it ". + "to Doctrine\\ORM\\Configuration::setMetadataDriverImpl()."); + } + + /** + * @param string $queryName + * + * @return ORMException + */ + public static function namedQueryNotFound($queryName) + { + return new self('Could not find a named query by the name "' . $queryName . '"'); + } + + /** + * @param string $nativeQueryName + * + * @return ORMException + */ + public static function namedNativeQueryNotFound($nativeQueryName) + { + return new self('Could not find a named native query by the name "' . $nativeQueryName . '"'); + } + + /** + * @param object $entity + * @param object $relatedEntity + * + * @return ORMException + */ + public static function entityMissingForeignAssignedId($entity, $relatedEntity) + { + return new self( + "Entity of type " . get_class($entity) . " has identity through a foreign entity " . get_class($relatedEntity) . ", " . + "however this entity has no identity itself. You have to call EntityManager#persist() on the related entity " . + "and make sure that an identifier was generated before trying to persist '" . get_class($entity) . "'. In case " . + "of Post Insert ID Generation (such as MySQL Auto-Increment) this means you have to call " . + "EntityManager#flush() between both persist operations." + ); + } + + /** + * @param object $entity + * @param string $field + * + * @return ORMException + */ + public static function entityMissingAssignedIdForField($entity, $field) + { + return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " . + "The identifier generation strategy for this entity requires the ID field to be populated before ". + "EntityManager#persist() is called. If you want automatically generated identifiers instead " . + "you need to adjust the metadata mapping accordingly." + ); + } + + /** + * @param string $field + * + * @return ORMException + */ + public static function unrecognizedField($field) + { + return new self("Unrecognized field: $field"); + } + + /** + * + * @param string $class + * @param string $association + * @param string $given + * @param string $expected + * + * @return \Doctrine\ORM\ORMInvalidArgumentException + */ + public static function unexpectedAssociationValue($class, $association, $given, $expected) + { + return new self(sprintf('Found entity of type %s on association %s#%s, but expecting %s', $given, $class, $association, $expected)); + } + + /** + * @param string $className + * @param string $field + * + * @return ORMException + */ + public static function invalidOrientation($className, $field) + { + return new self("Invalid order by orientation specified for " . $className . "#" . $field); + } + + /** + * @param string $mode + * + * @return ORMException + */ + public static function invalidFlushMode($mode) + { + return new self("'$mode' is an invalid flush mode."); + } + + /** + * @return ORMException + */ + public static function entityManagerClosed() + { + return new self("The EntityManager is closed."); + } + + /** + * @param string $mode + * + * @return ORMException + */ + public static function invalidHydrationMode($mode) + { + return new self("'$mode' is an invalid hydration mode."); + } + + /** + * @return ORMException + */ + public static function mismatchedEventManager() + { + return new self("Cannot use different EventManager instances for EntityManager and Connection."); + } + + /** + * @param string $methodName + * + * @return ORMException + */ + public static function findByRequiresParameter($methodName) + { + return new self("You need to pass a parameter to '".$methodName."'"); + } + + /** + * @param string $entityName + * @param string $fieldName + * @param string $method + * + * @return ORMException + */ + public static function invalidFindByCall($entityName, $fieldName, $method) + { + return new self( + "Entity '".$entityName."' has no field '".$fieldName."'. ". + "You can therefore not call '".$method."' on the entities' repository" + ); + } + + /** + * @param string $entityName + * @param string $associationFieldName + * + * @return ORMException + */ + public static function invalidFindByInverseAssociation($entityName, $associationFieldName) + { + return new self( + "You cannot search for the association field '".$entityName."#".$associationFieldName."', ". + "because it is the inverse side of an association. Find methods only work on owning side associations." + ); + } + + /** + * @return ORMException + */ + public static function invalidResultCacheDriver() + { + return new self("Invalid result cache driver; it must implement Doctrine\\Common\\Cache\\Cache."); + } + + /** + * @return ORMException + */ + public static function notSupported() + { + return new self("This behaviour is (currently) not supported by Doctrine 2"); + } + + /** + * @return ORMException + */ + public static function queryCacheNotConfigured() + { + return new self('Query Cache is not configured.'); + } + + /** + * @return ORMException + */ + public static function metadataCacheNotConfigured() + { + return new self('Class Metadata Cache is not configured.'); + } + + /** + * @param \Doctrine\Common\Cache\Cache $cache + * + * @return ORMException + */ + public static function queryCacheUsesNonPersistentCache(CacheDriver $cache) + { + return new self('Query Cache uses a non-persistent cache driver, ' . get_class($cache) . '.'); + } + + /** + * @param \Doctrine\Common\Cache\Cache $cache + * + * @return ORMException + */ + public static function metadataCacheUsesNonPersistentCache(CacheDriver $cache) + { + return new self('Metadata Cache uses a non-persistent cache driver, ' . get_class($cache) . '.'); + } + + /** + * @return ORMException + */ + public static function proxyClassesAlwaysRegenerating() + { + return new self('Proxy Classes are always regenerating.'); + } + + /** + * @param string $entityNamespaceAlias + * + * @return ORMException + */ + public static function unknownEntityNamespace($entityNamespaceAlias) + { + return new self( + "Unknown Entity namespace alias '$entityNamespaceAlias'." + ); + } + + /** + * @param string $className + * + * @return ORMException + */ + public static function invalidEntityRepository($className) + { + return new self("Invalid repository class '".$className."'. It must be a Doctrine\Common\Persistence\ObjectRepository."); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return ORMException + */ + public static function missingIdentifierField($className, $fieldName) + { + return new self("The identifier $fieldName is missing for a query of " . $className); + } + + /** + * @param string $className + * @param string $fieldName + * + * @return ORMException + */ + public static function unrecognizedIdentifierFields($className, $fieldNames) + { + return new self( + "Unrecognized identifier fields: '" . implode("', '", $fieldNames) . "' " . + "are not present on class '" . $className . "'." + ); + } + + /** + * @param string $functionName + * + * @return ORMException + */ + public static function overwriteInternalDQLFunctionNotAllowed($functionName) + { + return new self("It is not allowed to overwrite internal function '$functionName' in the DQL parser through user-defined functions."); + } + + /** + * @return ORMException + */ + public static function cantUseInOperatorOnCompositeKeys() + { + return new self("Can't use IN operator on entities that have composite keys."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php new file mode 100644 index 0000000..02c0f99 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php @@ -0,0 +1,226 @@ +. + */ + +namespace Doctrine\ORM; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * Contains exception messages for all invalid lifecycle state exceptions inside UnitOfWork + * + * @author Benjamin Eberlei + */ +class ORMInvalidArgumentException extends \InvalidArgumentException +{ + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertForManagedEntity($entity) + { + return new self("A managed+dirty entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertForRemovedEntity($entity) + { + return new self("Removed entity " . self::objToStr($entity) . " can not be scheduled for insertion."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function scheduleInsertTwice($entity) + { + return new self("Entity " . self::objToStr($entity) . " can not be scheduled for insertion twice."); + } + + /** + * @param string $className + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function entityWithoutIdentity($className, $entity) + { + return new self( + "The given entity of type '" . $className . "' (".self::objToStr($entity).") has no identity/no " . + "id values set. It cannot be added to the identity map." + ); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function readOnlyRequiresManagedEntity($entity) + { + return new self("Only managed entities can be marked or checked as read only. But " . self::objToStr($entity) . " is not"); + } + + /** + * @param array $assoc + * @param object $entry + * + * @return ORMInvalidArgumentException + */ + static public function newEntityFoundThroughRelationship(array $assoc, $entry) + { + return new self("A new entity was found through the relationship '" + . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' that was not" + . " configured to cascade persist operations for entity: " . self::objToStr($entry) . "." + . " To solve this issue: Either explicitly call EntityManager#persist()" + . " on this unknown entity or configure cascade persist " + . " this association in the mapping for example @ManyToOne(..,cascade={\"persist\"})." + . (method_exists($entry, '__toString') ? + "": + " If you cannot find out which entity causes the problem" + ." implement '" . $assoc['targetEntity'] . "#__toString()' to get a clue.")); + } + + /** + * @param array $assoc + * @param object $entry + * + * @return ORMInvalidArgumentException + */ + static public function detachedEntityFoundThroughRelationship(array $assoc, $entry) + { + return new self("A detached entity of type " . $assoc['targetEntity'] . " (" . self::objToStr($entry) . ") " + . " was found through the relationship '" . $assoc['sourceEntity'] . "#" . $assoc['fieldName'] . "' " + . "during cascading a persist operation."); + } + + /** + * @param object $entity + * + * @return ORMInvalidArgumentException + */ + static public function entityNotManaged($entity) + { + return new self("Entity " . self::objToStr($entity) . " is not managed. An entity is managed if its fetched " . + "from the database or registered as new through EntityManager#persist"); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function entityHasNoIdentity($entity, $operation) + { + return new self("Entity has no identity, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function entityIsRemoved($entity, $operation) + { + return new self("Entity is removed, therefore " . $operation ." cannot be performed. " . self::objToStr($entity)); + } + + /** + * @param object $entity + * @param string $operation + * + * @return ORMInvalidArgumentException + */ + static public function detachedEntityCannot($entity, $operation) + { + return new self("Detached entity " . self::objToStr($entity) . " cannot be " . $operation); + } + + /** + * @param string $context + * @param mixed $given + * @param int $parameterIndex + * + * @return ORMInvalidArgumentException + */ + public static function invalidObject($context, $given, $parameterIndex = 1) + { + return new self($context . ' expects parameter ' . $parameterIndex . + ' to be an entity object, '. gettype($given) . ' given.'); + } + + /** + * @return ORMInvalidArgumentException + */ + public static function invalidCompositeIdentifier() + { + return new self("Binding an entity with a composite primary key to a query is not supported. " . + "You should split the parameter into the explicit fields and bind them separately."); + } + + /** + * @return ORMInvalidArgumentException + */ + public static function invalidIdentifierBindingEntity() + { + return new self("Binding entities to query parameters only allowed for entities that have an identifier."); + } + + /** + * @param ClassMetadata $targetClass + * @param array $assoc + * @param mixed $actualValue + * + * @return self + */ + public static function invalidAssociation(ClassMetadata $targetClass, $assoc, $actualValue) + { + $expectedType = 'Doctrine\Common\Collections\Collection|array'; + + if (($assoc['type'] & ClassMetadata::TO_ONE) > 0) { + $expectedType = $targetClass->getName(); + } + + return new self(sprintf( + 'Expected value of type "%s" for association field "%s#$%s", got "%s" instead.', + $expectedType, + $assoc['sourceEntity'], + $assoc['fieldName'], + is_object($actualValue) ? get_class($actualValue) : gettype($actualValue) + )); + } + + /** + * Helper method to show an object as string. + * + * @param object $obj + * + * @return string + */ + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php new file mode 100644 index 0000000..ecd5445 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/OptimisticLockException.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * An OptimisticLockException is thrown when a version check on an object + * that uses optimistic locking through a version field fails. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +class OptimisticLockException extends ORMException +{ + /** + * @var object|null + */ + private $entity; + + /** + * @param string $msg + * @param object $entity + */ + public function __construct($msg, $entity) + { + parent::__construct($msg); + $this->entity = $entity; + } + + /** + * Gets the entity that caused the exception. + * + * @return object|null + */ + public function getEntity() + { + return $this->entity; + } + + /** + * @param object $entity + * + * @return OptimisticLockException + */ + public static function lockFailed($entity) + { + return new self("The optimistic lock on an entity failed.", $entity); + } + + /** + * @param object $entity + * @param int $expectedLockVersion + * @param int $actualLockVersion + * + * @return OptimisticLockException + */ + public static function lockFailedVersionMismatch($entity, $expectedLockVersion, $actualLockVersion) + { + $expectedLockVersion = ($expectedLockVersion instanceof \DateTime) ? $expectedLockVersion->getTimestamp() : $expectedLockVersion; + $actualLockVersion = ($actualLockVersion instanceof \DateTime) ? $actualLockVersion->getTimestamp() : $actualLockVersion; + return new self("The optimistic lock failed, version " . $expectedLockVersion . " was expected, but is actually ".$actualLockVersion, $entity); + } + + /** + * @param string $entityName + * + * @return OptimisticLockException + */ + public static function notVersioned($entityName) + { + return new self("Cannot obtain optimistic lock on unversioned entity " . $entityName, null); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php new file mode 100644 index 0000000..83eab8c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php @@ -0,0 +1,709 @@ +. + */ + +namespace Doctrine\ORM; + +use Closure; +use Doctrine\Common\Collections\AbstractLazyCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Selectable; +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * A PersistentCollection represents a collection of elements that have persistent state. + * + * Collections of entities represent only the associations (links) to those entities. + * That means, if the collection is part of a many-many mapping and you remove + * entities from the collection, only the links in the relation table are removed (on flush). + * Similarly, if you remove entities from a collection that is part of a one-many + * mapping this will only result in the nulling out of the foreign keys on flush. + * + * @since 2.0 + * @author Konsta Vesterinen + * @author Roman Borschel + * @author Giorgio Sironi + * @author Stefano Rodriguez + */ +final class PersistentCollection extends AbstractLazyCollection implements Selectable +{ + /** + * A snapshot of the collection at the moment it was fetched from the database. + * This is used to create a diff of the collection at commit time. + * + * @var array + */ + private $snapshot = array(); + + /** + * The entity that owns this collection. + * + * @var object + */ + private $owner; + + /** + * The association mapping the collection belongs to. + * This is currently either a OneToManyMapping or a ManyToManyMapping. + * + * @var array + */ + private $association; + + /** + * The EntityManager that manages the persistence of the collection. + * + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * The name of the field on the target entities that points to the owner + * of the collection. This is only set if the association is bi-directional. + * + * @var string + */ + private $backRefFieldName; + + /** + * The class descriptor of the collection's entity type. + * + * @var ClassMetadata + */ + private $typeClass; + + /** + * Whether the collection is dirty and needs to be synchronized with the database + * when the UnitOfWork that manages its persistent state commits. + * + * @var boolean + */ + private $isDirty = false; + + /** + * Creates a new persistent collection. + * + * @param EntityManagerInterface $em The EntityManager the collection will be associated with. + * @param ClassMetadata $class The class descriptor of the entity type of this collection. + * @param Collection $collection The collection elements. + */ + public function __construct(EntityManagerInterface $em, $class, Collection $collection) + { + $this->collection = $collection; + $this->em = $em; + $this->typeClass = $class; + $this->initialized = true; + } + + /** + * INTERNAL: + * Sets the collection's owning entity together with the AssociationMapping that + * describes the association between the owner and the elements of the collection. + * + * @param object $entity + * @param array $assoc + * + * @return void + */ + public function setOwner($entity, array $assoc) + { + $this->owner = $entity; + $this->association = $assoc; + $this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; + } + + /** + * INTERNAL: + * Gets the collection owner. + * + * @return object + */ + public function getOwner() + { + return $this->owner; + } + + /** + * @return Mapping\ClassMetadata + */ + public function getTypeClass() + { + return $this->typeClass; + } + + /** + * INTERNAL: + * Adds an element to a collection during hydration. This will automatically + * complete bidirectional associations in the case of a one-to-many association. + * + * @param mixed $element The element to add. + * + * @return void + */ + public function hydrateAdd($element) + { + $this->collection->add($element); + + // If _backRefFieldName is set and its a one-to-many association, + // we need to set the back reference. + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { + // Set back reference to owner + $this->typeClass->reflFields[$this->backRefFieldName]->setValue( + $element, $this->owner + ); + + $this->em->getUnitOfWork()->setOriginalEntityProperty( + spl_object_hash($element), $this->backRefFieldName, $this->owner + ); + } + } + + /** + * INTERNAL: + * Sets a keyed element in the collection during hydration. + * + * @param mixed $key The key to set. + * @param mixed $element The element to set. + * + * @return void + */ + public function hydrateSet($key, $element) + { + $this->collection->set($key, $element); + + // If _backRefFieldName is set, then the association is bidirectional + // and we need to set the back reference. + if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) { + // Set back reference to owner + $this->typeClass->reflFields[$this->backRefFieldName]->setValue( + $element, $this->owner + ); + } + } + + /** + * Initializes the collection by loading its contents from the database + * if the collection is not yet initialized. + * + * @return void + */ + public function initialize() + { + if ($this->initialized || ! $this->association) { + return; + } + + $this->doInitialize(); + + $this->initialized = true; + } + + /** + * INTERNAL: + * Tells this collection to take a snapshot of its current state. + * + * @return void + */ + public function takeSnapshot() + { + $this->snapshot = $this->collection->toArray(); + $this->isDirty = false; + } + + /** + * INTERNAL: + * Returns the last snapshot of the elements in the collection. + * + * @return array The last snapshot of the elements. + */ + public function getSnapshot() + { + return $this->snapshot; + } + + /** + * INTERNAL: + * getDeleteDiff + * + * @return array + */ + public function getDeleteDiff() + { + return array_udiff_assoc( + $this->snapshot, + $this->collection->toArray(), + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: + * getInsertDiff + * + * @return array + */ + public function getInsertDiff() + { + return array_udiff_assoc( + $this->collection->toArray(), + $this->snapshot, + function($a, $b) { return $a === $b ? 0 : 1; } + ); + } + + /** + * INTERNAL: Gets the association mapping of the collection. + * + * @return array + */ + public function getMapping() + { + return $this->association; + } + + /** + * Marks this collection as changed/dirty. + * + * @return void + */ + private function changed() + { + if ($this->isDirty) { + return; + } + + $this->isDirty = true; + + if ($this->association !== null && + $this->association['isOwningSide'] && + $this->association['type'] === ClassMetadata::MANY_TO_MANY && + $this->owner && + $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) { + $this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner); + } + } + + /** + * Gets a boolean flag indicating whether this collection is dirty which means + * its state needs to be synchronized with the database. + * + * @return boolean TRUE if the collection is dirty, FALSE otherwise. + */ + public function isDirty() + { + return $this->isDirty; + } + + /** + * Sets a boolean flag, indicating whether this collection is dirty. + * + * @param boolean $dirty Whether the collection should be marked dirty or not. + * + * @return void + */ + public function setDirty($dirty) + { + $this->isDirty = $dirty; + } + + /** + * Sets the initialized flag of the collection, forcing it into that state. + * + * @param boolean $bool + * + * @return void + */ + public function setInitialized($bool) + { + $this->initialized = $bool; + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + // TODO: If the keys are persistent as well (not yet implemented) + // and the collection is not initialized and orphanRemoval is + // not used we can issue a straight SQL delete/update on the + // association (table). Without initializing the collection. + $removed = parent::remove($key); + + if ( ! $removed) { + return $removed; + } + + $this->changed(); + + if ($this->association !== null && + $this->association['type'] & ClassMetadata::TO_MANY && + $this->owner && + $this->association['orphanRemoval']) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function removeElement($element) + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + if ($this->collection->contains($element)) { + return $this->collection->removeElement($element); + } + + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + if ($persister->removeElement($this, $element)) { + return $element; + } + + return null; + } + + $removed = parent::removeElement($element); + + if ( ! $removed) { + return $removed; + } + + $this->changed(); + + if ($this->association !== null && + $this->association['type'] & ClassMetadata::TO_MANY && + $this->owner && + $this->association['orphanRemoval']) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($element); + } + + return $removed; + } + + /** + * {@inheritdoc} + */ + public function containsKey($key) + { + if (! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY + && isset($this->association['indexBy'])) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $this->collection->containsKey($key) || $persister->containsKey($this, $key); + } + + return parent::containsKey($key); + } + + /** + * {@inheritdoc} + */ + public function contains($element) + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $this->collection->contains($element) || $persister->contains($this, $element); + } + + return parent::contains($element); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + if ( ! $this->initialized + && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY + && isset($this->association['indexBy']) + ) { + if (!$this->typeClass->isIdentifierComposite && $this->typeClass->isIdentifier($this->association['indexBy'])) { + return $this->em->find($this->typeClass->name, $key); + } + + return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); + } + + return parent::get($key); + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $persister->count($this) + ($this->isDirty ? $this->collection->count() : 0); + } + + return parent::count(); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + parent::set($key, $value); + + $this->changed(); + } + + /** + * {@inheritdoc} + */ + public function add($value) + { + $this->collection->add($value); + + $this->changed(); + + return true; + } + + /* ArrayAccess implementation */ + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + + return $this->set($offset, $value); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + return $this->collection->isEmpty() && $this->count() === 0; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + if ($this->initialized && $this->isEmpty()) { + return; + } + + $uow = $this->em->getUnitOfWork(); + + if ($this->association['type'] & ClassMetadata::TO_MANY && + $this->association['orphanRemoval'] && + $this->owner) { + // we need to initialize here, as orphan removal acts like implicit cascadeRemove, + // hence for event listeners we need the objects in memory. + $this->initialize(); + + foreach ($this->collection as $element) { + $uow->scheduleOrphanRemoval($element); + } + } + + $this->collection->clear(); + + $this->initialized = true; // direct call, {@link initialize()} is too expensive + + if ($this->association['isOwningSide'] && $this->owner) { + $this->changed(); + + $uow->scheduleCollectionDeletion($this); + + $this->takeSnapshot(); + } + } + + /** + * Called by PHP when this collection is serialized. Ensures that only the + * elements are properly serialized. + * + * Internal note: Tried to implement Serializable first but that did not work well + * with circular references. This solution seems simpler and works well. + * + * @return array + */ + public function __sleep() + { + return array('collection', 'initialized'); + } + + /** + * Extracts a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset + * @param int|null $length + * + * @return array + */ + public function slice($offset, $length = null) + { + if ( ! $this->initialized && ! $this->isDirty && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return $persister->slice($this, $offset, $length); + } + + return parent::slice($offset, $length); + } + + /** + * Cleans up internal state of cloned persistent collection. + * + * The following problems have to be prevented: + * 1. Added entities are added to old PC + * 2. New collection is not dirty, if reused on other entity nothing + * changes. + * 3. Snapshot leads to invalid diffs being generated. + * 4. Lazy loading grabs entities from old owner object. + * 5. New collection is connected to old owner and leads to duplicate keys. + * + * @return void + */ + public function __clone() + { + if (is_object($this->collection)) { + $this->collection = clone $this->collection; + } + + $this->initialize(); + + $this->owner = null; + $this->snapshot = array(); + + $this->changed(); + } + + /** + * Selects all elements from a selectable that match the expression and + * return a new collection containing these elements. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return Collection + * + * @throws \RuntimeException + */ + public function matching(Criteria $criteria) + { + if ($this->isDirty) { + $this->initialize(); + } + + if ($this->initialized) { + return $this->collection->matching($criteria); + } + + if ($this->association['type'] === ClassMetadata::MANY_TO_MANY) { + $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association); + + return new ArrayCollection($persister->loadCriteria($this, $criteria)); + } + + $builder = Criteria::expr(); + $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner); + $expression = $criteria->getWhereExpression(); + $expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; + + $criteria = clone $criteria; + $criteria->where($expression); + + $persister = $this->em->getUnitOfWork()->getEntityPersister($this->association['targetEntity']); + + return ($this->association['fetch'] === ClassMetadataInfo::FETCH_EXTRA_LAZY) + ? new LazyCriteriaCollection($persister, $criteria) + : new ArrayCollection($persister->loadCriteria($criteria)); + } + + /** + * Retrieves the wrapped Collection instance. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function unwrap() + { + return $this->collection; + } + + /** + * {@inheritdoc} + */ + protected function doInitialize() + { + // Has NEW objects added through add(). Remember them. + $newObjects = array(); + + if ($this->isDirty) { + $newObjects = $this->collection->toArray(); + } + + $this->collection->clear(); + $this->em->getUnitOfWork()->loadCollection($this); + $this->takeSnapshot(); + + // Reattach NEW objects added through add(), if any. + if ($newObjects) { + foreach ($newObjects as $obj) { + $this->collection->add($obj); + } + + $this->isDirty = true; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/AbstractCollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/AbstractCollectionPersister.php new file mode 100644 index 0000000..0948956 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/AbstractCollectionPersister.php @@ -0,0 +1,99 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\UnitOfWork; + +/** + * Base class for all collection persisters. + * + * @since 2.0 + * @author Roman Borschel + */ +abstract class AbstractCollectionPersister implements CollectionPersister +{ + /** + * @var EntityManagerInterface + */ + protected $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + protected $conn; + + /** + * @var UnitOfWork + */ + protected $uow; + + /** + * The database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * Initializes a new instance of a class derived from AbstractCollectionPersister. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->conn = $em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Check if entity is in a valid state for operations. + * + * @param object $entity + * + * @return bool + */ + protected function isValidEntityState($entity) + { + $entityState = $this->uow->getEntityState($entity, UnitOfWork::STATE_NEW); + + if ($entityState === UnitOfWork::STATE_NEW) { + return false; + } + + // If Entity is scheduled for inclusion, it is not in this collection. + // We can assure that because it would have return true before on array check + if ($entityState === UnitOfWork::STATE_MANAGED && $this->uow->isScheduledForInsert($entity)) { + return false; + } + + return true; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/CollectionPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/CollectionPersister.php new file mode 100644 index 0000000..36b5706 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/CollectionPersister.php @@ -0,0 +1,122 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\PersistentCollection; + +/** + * Collection persister interface + * Define the behavior that should be implemented by all collection persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface CollectionPersister +{ + /** + * Deletes the persistent state represented by the given collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return void + */ + public function delete(PersistentCollection $collection); + + /** + * Updates the given collection, synchronizing its state with the database + * by inserting, updating and deleting individual elements. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return void + */ + public function update(PersistentCollection $collection); + + /** + * Counts the size of this persistent collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return integer + */ + public function count(PersistentCollection $collection); + + /** + * Slices elements. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param integer $offset + * @param integer $length + * + * @return array + */ + public function slice(PersistentCollection $collection, $offset, $length = null); + + /** + * Checks for existence of an element. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * + * @return boolean + */ + public function contains(PersistentCollection $collection, $element); + + /** + * Checks for existence of a key. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $key + * + * @return boolean + */ + public function containsKey(PersistentCollection $collection, $key); + + /** + * Removes an element. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * + * @return mixed + */ + public function removeElement(PersistentCollection $collection, $element); + + /** + * Gets an element by key. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $index + * + * @return mixed + */ + public function get(PersistentCollection $collection, $index); + + /** + * Loads association entities matching the given Criteria object. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php new file mode 100644 index 0000000..bf9f14e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php @@ -0,0 +1,724 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Persisters\SqlExpressionVisitor; +use Doctrine\ORM\Persisters\SqlValueVisitor; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Query; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * Persister for many-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class ManyToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + $this->conn->executeUpdate($this->getDeleteSQL($collection), $this->getDeleteSQLParameters($collection)); + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + + if ( ! $mapping['isOwningSide']) { + return; // ignore inverse side + } + + list($deleteSql, $deleteTypes) = $this->getDeleteRowSQL($collection); + list($insertSql, $insertTypes) = $this->getInsertRowSQL($collection); + + foreach ($collection->getDeleteDiff() as $element) { + $this->conn->executeUpdate( + $deleteSql, + $this->getDeleteRowSQLParameters($collection, $element), + $deleteTypes + ); + } + + foreach ($collection->getInsertDiff() as $element) { + $this->conn->executeUpdate( + $insertSql, + $this->getInsertRowSQLParameters($collection, $element), + $insertTypes + ); + } + } + + /** + * {@inheritdoc} + */ + public function get(PersistentCollection $collection, $index) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mappedKey = $mapping['isOwningSide'] + ? $mapping['inversedBy'] + : $mapping['mappedBy']; + + return $persister->load(array($mappedKey => $collection->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $collection) + { + $conditions = array(); + $params = array(); + $types = array(); + $mapping = $collection->getMapping(); + $id = $this->uow->getEntityIdentifier($collection->getOwner()); + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $association = ( ! $mapping['isOwningSide']) + ? $targetClass->associationMappings[$mapping['mappedBy']] + : $mapping; + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $sourceClass, $this->platform); + $joinColumns = ( ! $mapping['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $sourceClass, $this->platform); + $referencedName = $joinColumn['referencedColumnName']; + $conditions[] = 't.' . $columnName . ' = ?'; + $params[] = $id[$sourceClass->getFieldForColumn($referencedName)]; + $types[] = PersisterHelper::getTypeOfColumn($referencedName, $sourceClass, $this->em); + } + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); + + if ($filterSql) { + $conditions[] = $filterSql; + } + + // If there is a provided criteria, make part of conditions + // @todo Fix this. Current SQL returns something like: + // + /*if ($criteria && ($expression = $criteria->getWhereExpression()) !== null) { + // A join is needed on the target entity + $targetTableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $targetJoinSql = ' JOIN ' . $targetTableName . ' te' + . ' ON' . implode(' AND ', $this->getOnConditionSQL($association)); + + // And criteria conditions needs to be added + $persister = $this->uow->getEntityPersister($targetClass->name); + $visitor = new SqlExpressionVisitor($persister, $targetClass); + $conditions[] = $visitor->dispatch($expression); + + $joinTargetEntitySQL = $targetJoinSql . $joinTargetEntitySQL; + }*/ + + $sql = 'SELECT COUNT(*)' + . ' FROM ' . $joinTableName . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . implode(' AND ', $conditions); + + return $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * {@inheritDoc} + */ + public function slice(PersistentCollection $collection, $offset, $length = null) + { + $mapping = $collection->getMapping(); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getManyToManyCollection($mapping, $collection->getOwner(), $offset, $length); + } + /** + * {@inheritdoc} + */ + public function containsKey(PersistentCollection $collection, $key) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictionsWithKey($collection, $key, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * {@inheritDoc} + */ + public function contains(PersistentCollection $collection, $element) + { + if ( ! $this->isValidEntityState($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, true); + + $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * {@inheritDoc} + */ + public function removeElement(PersistentCollection $collection, $element) + { + if ( ! $this->isValidEntityState($element)) { + return false; + } + + list($quotedJoinTable, $whereClauses, $params, $types) = $this->getJoinTableRestrictions($collection, $element, false); + + $sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); + + return (bool) $this->conn->executeUpdate($sql, $params, $types); + } + + /** + * {@inheritDoc} + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + { + $mapping = $collection->getMapping(); + $owner = $collection->getOwner(); + $ownerMetadata = $this->em->getClassMetadata(get_class($owner)); + $whereClauses = $params = array(); + + foreach ($mapping['relationToSourceKeyColumns'] as $key => $value) { + $whereClauses[] = sprintf('t.%s = ?', $key); + $params[] = $ownerMetadata->getFieldValue($owner, $value); + } + + $parameters = $this->expandCriteriaParameters($criteria); + + foreach ($parameters as $parameter) { + list($name, $value) = $parameter; + $whereClauses[] = sprintf('te.%s = ?', $name); + $params[] = $value; + } + + $mapping = $collection->getMapping(); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $ownerMetadata, $this->platform); + $onConditions = $this->getOnConditionSQL($mapping); + + $rsm = new Query\ResultSetMappingBuilder($this->em); + $rsm->addRootEntityFromClassMetadata($mapping['targetEntity'], 'te'); + + $sql = 'SELECT ' . $rsm->generateSelectClause() + . ' FROM ' . $tableName . ' te' + . ' JOIN ' . $joinTable . ' t ON' + . implode(' AND ', $onConditions) + . ' WHERE ' . implode(' AND ', $whereClauses); + + $stmt = $this->conn->executeQuery($sql, $params); + + return $this + ->em + ->newHydrator(Query::HYDRATE_OBJECT) + ->hydrateAll($stmt, $rsm); + } + + /** + * Generates the filter SQL for a given mapping. + * + * This method is not used for actually grabbing the related entities + * but when the extra-lazy collection methods are called on a filtered + * association. This is why besides the many to many table we also + * have to join in the actual entities table leading to additional + * JOIN. + * + * @param array $mapping Array containing mapping information. + * + * @return string[] ordered tuple: + * - JOIN condition to add to the SQL + * - WHERE condition to add to the SQL + */ + public function getFilterSql($mapping) + { + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); + $filterSql = $this->generateFilterConditionSQL($rootClass, 'te'); + + if ('' === $filterSql) { + return array('', ''); + } + + // A join is needed if there is filtering on the target entity + $tableName = $this->quoteStrategy->getTableName($rootClass, $this->platform); + $joinSql = ' JOIN ' . $tableName . ' te' + . ' ON' . implode(' AND ', $this->getOnConditionSQL($mapping)); + + return array($joinSql, $filterSql); + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterClauses = array(); + + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ($filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + return $filterClauses + ? '(' . implode(' AND ', $filterClauses) . ')' + : ''; + } + + /** + * Generate ON condition + * + * @param array $mapping + * + * @return array + */ + protected function getOnConditionSQL($mapping) + { + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $association = ( ! $mapping['isOwningSide']) + ? $targetClass->associationMappings[$mapping['mappedBy']] + : $mapping; + + $joinColumns = $mapping['isOwningSide'] + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + $conditions = array(); + + foreach ($joinColumns as $joinColumn) { + $joinColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $refColumnName = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = ' t.' . $joinColumnName . ' = ' . 'te.' . $refColumnName; + } + + return $conditions; + } + + /** + * {@inheritdoc} + * + * @override + */ + protected function getDeleteSQL(PersistentCollection $collection) + { + $columns = array(); + $mapping = $collection->getMapping(); + $class = $this->em->getClassMetadata(get_class($collection->getOwner())); + $joinTable = $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + return 'DELETE FROM ' . $joinTable + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; + } + + /** + * {@inheritdoc} + * + * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteSql. + * @override + */ + protected function getDeleteSQLParameters(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $identifier = $this->uow->getEntityIdentifier($collection->getOwner()); + + // Optimization for single column identifier + if (count($mapping['relationToSourceKeyColumns']) === 1) { + return array(reset($identifier)); + } + + // Composite identifier + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $params = array(); + + foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) { + $params[] = isset($sourceClass->fieldNames[$refColumnName]) + ? $identifier[$sourceClass->fieldNames[$refColumnName]] + : $identifier[$sourceClass->getFieldForColumn($columnName)]; + } + + return $params; + } + + /** + * Gets the SQL statement used for deleting a row from the collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array + * of types for bound parameters + */ + protected function getDeleteRowSQL(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $class = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $columns = array(); + $types = array(); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + } + + return array( + 'DELETE FROM ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) + . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?', + $types, + ); + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to delete the given + * element from the given collection. + * + * Internal note: Order of the parameters must be the same as the order of the columns in getDeleteRowSql. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $element + * + * @return array + */ + protected function getDeleteRowSQLParameters(PersistentCollection $collection, $element) + { + return $this->collectJoinTableColumnParameters($collection, $element); + } + + /** + * Gets the SQL statement used for inserting a row in the collection. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * + * @return string[]|string[][] ordered tuple containing the SQL to be executed and an array + * of types for bound parameters + */ + protected function getInsertRowSQL(PersistentCollection $collection) + { + $columns = array(); + $types = array(); + $mapping = $collection->getMapping(); + $class = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $class, $this->em); + } + + foreach ($mapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + } + + return array( + 'INSERT INTO ' . $this->quoteStrategy->getJoinTableName($mapping, $class, $this->platform) + . ' (' . implode(', ', $columns) . ')' + . ' VALUES' + . ' (' . implode(', ', array_fill(0, count($columns), '?')) . ')', + $types, + ); + } + + /** + * Gets the SQL parameters for the corresponding SQL statement to insert the given + * element of the given collection into the database. + * + * Internal note: Order of the parameters must be the same as the order of the columns in getInsertRowSql. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param mixed $element + * + * @return array + */ + protected function getInsertRowSQLParameters(PersistentCollection $collection, $element) + { + return $this->collectJoinTableColumnParameters($collection, $element); + } + + /** + * Collects the parameters for inserting/deleting on the join table in the order + * of the join table columns as specified in ManyToManyMapping#joinTableColumns. + * + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * + * @return array + */ + private function collectJoinTableColumnParameters(PersistentCollection $collection, $element) + { + $params = array(); + $mapping = $collection->getMapping(); + $isComposite = count($mapping['joinTableColumns']) > 2; + + $identifier1 = $this->uow->getEntityIdentifier($collection->getOwner()); + $identifier2 = $this->uow->getEntityIdentifier($element); + + if ($isComposite) { + $class1 = $this->em->getClassMetadata(get_class($collection->getOwner())); + $class2 = $collection->getTypeClass(); + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]); + + if ( ! $isComposite) { + $params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2); + + continue; + } + + if ($isRelationToSource) { + $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; + + continue; + } + + $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; + } + + return $params; + } + + /** + * @param \Doctrine\ORM\PersistentCollection $collection + * @param string $key + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array ordered vector: + * - quoted join table name + * - where clauses to be added for filtering + * - parameters to be bound for filtering + * - types of the parameters to be bound for filtering + */ + private function getJoinTableRestrictionsWithKey(PersistentCollection $collection, $key, $addFilters) + { + $filterMapping = $collection->getMapping(); + $mapping = $filterMapping; + $indexBy = $mapping['indexBy']; + $id = $this->uow->getEntityIdentifier($collection->getOwner()); + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + if (! $mapping['isOwningSide']) { + $associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']]; + $joinColumns = $mapping['joinTable']['joinColumns']; + $sourceRelationMode = 'relationToTargetKeyColumns'; + $targetRelationMode = 'relationToSourceKeyColumns'; + } else { + $associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $joinColumns = $mapping['joinTable']['inverseJoinColumns']; + $sourceRelationMode = 'relationToSourceKeyColumns'; + $targetRelationMode = 'relationToTargetKeyColumns'; + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t'; + $whereClauses = array(); + $params = array(); + $types = array(); + + $joinNeeded = ! in_array($indexBy, $targetClass->identifier); + + if ($joinNeeded) { // extra join needed if indexBy is not a @id + $joinConditions = array(); + + foreach ($joinColumns as $joinTableColumn) { + $joinConditions[] = 't.' . $joinTableColumn['name'] . ' = tr.' . $joinTableColumn['referencedColumnName']; + } + + $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); + $quotedJoinTable .= ' JOIN ' . $tableName . ' tr ON ' . implode(' AND ', $joinConditions); + $columnName = $targetClass->getColumnName($indexBy); + + $whereClauses[] = 'tr.' . $columnName . ' = ?'; + $params[] = $key; + $types[] = PersisterHelper::getTypeOfColumn($columnName, $targetClass, $this->em); + } + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + if (isset($mapping[$sourceRelationMode][$joinTableColumn])) { + $column = $mapping[$sourceRelationMode][$joinTableColumn]; + $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; + $params[] = $sourceClass->containsForeignIdentifier + ? $id[$sourceClass->getFieldForColumn($column)] + : $id[$sourceClass->fieldNames[$column]]; + $types[] = PersisterHelper::getTypeOfColumn($column, $sourceClass, $this->em); + } elseif ( ! $joinNeeded) { + $column = $mapping[$targetRelationMode][$joinTableColumn]; + + $whereClauses[] = 't.' . $joinTableColumn . ' = ?'; + $params[] = $key; + $types[] = PersisterHelper::getTypeOfColumn($column, $targetClass, $this->em); + } + } + + if ($addFilters) { + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + + if ($filterSql) { + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params, $types); + } + + /** + * @param \Doctrine\ORM\PersistentCollection $collection + * @param object $element + * @param boolean $addFilters Whether the filter SQL should be included or not. + * + * @return array ordered vector: + * - quoted join table name + * - where clauses to be added for filtering + * - parameters to be bound for filtering + * - types of the parameters to be bound for filtering + */ + private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters) + { + $filterMapping = $collection->getMapping(); + $mapping = $filterMapping; + + if ( ! $mapping['isOwningSide']) { + $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); + $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $sourceId = $this->uow->getEntityIdentifier($element); + $targetId = $this->uow->getEntityIdentifier($collection->getOwner()); + + $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; + } else { + $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + $sourceId = $this->uow->getEntityIdentifier($collection->getOwner()); + $targetId = $this->uow->getEntityIdentifier($element); + } + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); + $whereClauses = array(); + $params = array(); + $types = array(); + + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; + + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn]; + $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)]; + $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); + + continue; + } + + // relationToSourceKeyColumns + $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn]; + $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)]; + $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em); + } + + if ($addFilters) { + $quotedJoinTable .= ' t'; + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); + + if ($filterSql) { + $quotedJoinTable .= ' ' . $joinTargetEntitySQL; + $whereClauses[] = $filterSql; + } + } + + return array($quotedJoinTable, $whereClauses, $params, $types); + } + + /** + * Expands Criteria Parameters by walking the expressions and grabbing all + * parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + private function expandCriteriaParameters(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return array(); + } + + $valueVisitor = new SqlValueVisitor(); + + $valueVisitor->dispatch($expression); + + list($values, $types) = $valueVisitor->getParamsAndTypes(); + + return $types; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php new file mode 100644 index 0000000..c18fdbb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php @@ -0,0 +1,184 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Collection; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Proxy\Proxy; +use Doctrine\ORM\PersistentCollection; + +/** + * Persister for one-to-many collections. + * + * @author Roman Borschel + * @author Guilherme Blanco + * @author Alexander + * @since 2.0 + */ +class OneToManyPersister extends AbstractCollectionPersister +{ + /** + * {@inheritdoc} + */ + public function delete(PersistentCollection $collection) + { + // This can never happen. One to many can only be inverse side. + // For owning side one to many, it is required to have a join table, + // then classifying it as a ManyToManyPersister. + return; + } + + /** + * {@inheritdoc} + */ + public function update(PersistentCollection $collection) + { + // This can never happen. One to many can only be inverse side. + // For owning side one to many, it is required to have a join table, + // then classifying it as a ManyToManyPersister. + return; + } + + /** + * {@inheritdoc} + */ + public function get(PersistentCollection $collection, $index) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->load( + array( + $mapping['mappedBy'] => $collection->getOwner(), + $mapping['indexBy'] => $index + ), + null, + $mapping, + array(), + null, + 1 + ); + } + + /** + * {@inheritdoc} + */ + public function count(PersistentCollection $collection) + { + $mapping = $collection->getMapping(); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + // only works with single id identifier entities. Will throw an + // exception in Entity Persisters if that is not the case for the + // 'mappedBy' field. + $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + + return $persister->count($criteria); + } + + /** + * {@inheritdoc} + */ + public function slice(PersistentCollection $collection, $offset, $length = null) + { + $mapping = $collection->getMapping(); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->getOneToManyCollection($mapping, $collection->getOwner(), $offset, $length); + } + + /** + * {@inheritdoc} + */ + public function containsKey(PersistentCollection $collection, $key) + { + $mapping = $collection->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + // only works with single id identifier entities. Will throw an + // exception in Entity Persisters if that is not the case for the + // 'mappedBy' field. + $criteria = new Criteria(); + + $criteria->andWhere(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + $criteria->andWhere(Criteria::expr()->eq($mapping['indexBy'], $key)); + + return (bool) $persister->count($criteria); + } + + /** + * {@inheritdoc} + */ + public function contains(PersistentCollection $collection, $element) + { + if ( ! $this->isValidEntityState($element)) { + return false; + } + + $mapping = $collection->getMapping(); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + // only works with single id identifier entities. Will throw an + // exception in Entity Persisters if that is not the case for the + // 'mappedBy' field. + $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $collection->getOwner())); + + return $persister->exists($element, $criteria); + } + + /** + * {@inheritdoc} + */ + public function removeElement(PersistentCollection $collection, $element) + { + $mapping = $collection->getMapping(); + + if ( ! $mapping['orphanRemoval']) { + // no-op: this is not the owning side, therefore no operations should be applied + return false; + } + + if ( ! $this->isValidEntityState($element)) { + return false; + } + + return $this + ->uow + ->getEntityPersister($mapping['targetEntity']) + ->delete($element); + } + + /** + * {@inheritdoc} + */ + public function loadCriteria(PersistentCollection $collection, Criteria $criteria) + { + throw new \BadMethodCallException("Filtering a collection by Criteria is not supported by this CollectionPersister."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php new file mode 100644 index 0000000..6b5e5c4 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/AbstractEntityInheritancePersister.php @@ -0,0 +1,95 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Types\Type; + +/** + * Base class for entity persisters that implement a certain inheritance mapping strategy. + * All these persisters are assumed to use a discriminator column to discriminate entity + * types in the hierarchy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @since 2.0 + */ +abstract class AbstractEntityInheritancePersister extends BasicEntityPersister +{ + /** + * {@inheritdoc} + */ + protected function prepareInsertData($entity) + { + $data = parent::prepareInsertData($entity); + + // Populate the discriminator column + $discColumn = $this->class->discriminatorColumn; + $this->columnTypes[$discColumn['name']] = $discColumn['type']; + $data[$this->getDiscriminatorColumnTableName()][$discColumn['name']] = $this->class->discriminatorValue; + + return $data; + } + + /** + * Gets the name of the table that contains the discriminator column. + * + * @return string The table name. + */ + abstract protected function getDiscriminatorColumnTableName(); + + /** + * {@inheritdoc} + */ + protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $tableAlias = $alias == 'r' ? '' : $alias; + $columnName = $class->columnNames[$field]; + $columnAlias = $this->getSQLColumnAlias($columnName); + $sql = $this->getSQLTableAlias($class->name, $tableAlias) . '.' + . $this->quoteStrategy->getColumnName($field, $class, $this->platform); + + $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field, $class->name); + + if (isset($class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($field)); + $sql = $type->convertToPHPValueSQL($sql, $this->platform); + } + + return $sql . ' AS ' . $columnAlias; + } + + /** + * @param string $tableAlias + * @param string $joinColumnName + * @param string $className + * @param string $type + * + * @return string + */ + protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className, $type) + { + $columnAlias = $this->getSQLColumnAlias($joinColumnName); + + $this->currentPersisterContext->rsm->addMetaResult('r', $columnAlias, $joinColumnName, false, $type); + + return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php new file mode 100644 index 0000000..7ab1d3b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -0,0 +1,2066 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\OptimisticLockException; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Persisters\SqlExpressionVisitor; +use Doctrine\ORM\Persisters\SqlValueVisitor; +use Doctrine\ORM\Query; +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\Utility\IdentifierFlattener; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * A BasicEntityPersister maps an entity to a single table in a relational database. + * + * A persister is always responsible for a single entity type. + * + * EntityPersisters are used during a UnitOfWork to apply any changes to the persistent + * state of entities onto a relational database when the UnitOfWork is committed, + * as well as for basic querying of entities and their associations (not DQL). + * + * The persisting operations that are invoked during a commit of a UnitOfWork to + * persist the persistent entity state are: + * + * - {@link addInsert} : To schedule an entity for insertion. + * - {@link executeInserts} : To execute all scheduled insertions. + * - {@link update} : To update the persistent state of an entity. + * - {@link delete} : To delete the persistent state of an entity. + * + * As can be seen from the above list, insertions are batched and executed all at once + * for increased efficiency. + * + * The querying operations invoked during a UnitOfWork, either through direct find + * requests or lazy-loading, are the following: + * + * - {@link load} : Loads (the state of) a single, managed entity. + * - {@link loadAll} : Loads multiple, managed entities. + * - {@link loadOneToOneEntity} : Loads a one/many-to-one entity association (lazy-loading). + * - {@link loadOneToManyCollection} : Loads a one-to-many entity association (lazy-loading). + * - {@link loadManyToManyCollection} : Loads a many-to-many entity association (lazy-loading). + * + * The BasicEntityPersister implementation provides the default behavior for + * persisting and querying entities that are mapped to a single database table. + * + * Subclasses can be created to provide custom persisting and querying strategies, + * i.e. spanning multiple tables. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @author Benjamin Eberlei + * @author Alexander + * @author Fabio B. Silva + * @author Rob Caiger + * @since 2.0 + */ +class BasicEntityPersister implements EntityPersister +{ + /** + * @var array + */ + static private $comparisonMap = array( + Comparison::EQ => '= %s', + Comparison::IS => '= %s', + Comparison::NEQ => '!= %s', + Comparison::GT => '> %s', + Comparison::GTE => '>= %s', + Comparison::LT => '< %s', + Comparison::LTE => '<= %s', + Comparison::IN => 'IN (%s)', + Comparison::NIN => 'NOT IN (%s)', + Comparison::CONTAINS => 'LIKE %s', + ); + + /** + * Metadata object that describes the mapping of the mapped entity class. + * + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + protected $class; + + /** + * The underlying DBAL Connection of the used EntityManager. + * + * @var \Doctrine\DBAL\Connection $conn + */ + protected $conn; + + /** + * The database platform. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + protected $platform; + + /** + * The EntityManager instance. + * + * @var EntityManagerInterface + */ + protected $em; + + /** + * Queued inserts. + * + * @var array + */ + protected $queuedInserts = array(); + + /** + * The map of column names to DBAL mapping types of all prepared columns used + * when INSERTing or UPDATEing an entity. + * + * @var array + * + * @see prepareInsertData($entity) + * @see prepareUpdateData($entity) + */ + protected $columnTypes = array(); + + /** + * The map of quoted column names. + * + * @var array + * + * @see prepareInsertData($entity) + * @see prepareUpdateData($entity) + */ + protected $quotedColumns = array(); + + /** + * The INSERT SQL statement used for entities handled by this persister. + * This SQL is only generated once per request, if at all. + * + * @var string + */ + private $insertSql; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + protected $quoteStrategy; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * @var CachedPersisterContext + */ + protected $currentPersisterContext; + + /** + * @var CachedPersisterContext + */ + private $limitsHandlingContext; + + /** + * @var CachedPersisterContext + */ + private $noLimitsContext; + + /** + * Initializes a new BasicEntityPersister that uses the given EntityManager + * and persists instances of the class described by the given ClassMetadata descriptor. + * + * @param EntityManagerInterface $em + * @param ClassMetadata $class + */ + public function __construct(EntityManagerInterface $em, ClassMetadata $class) + { + $this->em = $em; + $this->class = $class; + $this->conn = $em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + $this->identifierFlattener = new IdentifierFlattener($em->getUnitOfWork(), $em->getMetadataFactory()); + $this->noLimitsContext = $this->currentPersisterContext = new CachedPersisterContext( + $class, + new Query\ResultSetMapping(), + false + ); + $this->limitsHandlingContext = new CachedPersisterContext( + $class, + new Query\ResultSetMapping(), + true + ); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata() + { + return $this->class; + } + + /** + * {@inheritdoc} + */ + public function getResultSetMapping() + { + return $this->currentPersisterContext->rsm; + } + + /** + * {@inheritdoc} + */ + public function addInsert($entity) + { + $this->queuedInserts[spl_object_hash($entity)] = $entity; + } + + /** + * {@inheritdoc} + */ + public function getInserts() + { + return $this->queuedInserts; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->queuedInserts) { + return array(); + } + + $postInsertIds = array(); + $idGenerator = $this->class->idGenerator; + $isPostInsertId = $idGenerator->isPostInsertGenerator(); + + $stmt = $this->conn->prepare($this->getInsertSQL()); + $tableName = $this->class->getTableName(); + + foreach ($this->queuedInserts as $entity) { + $insertData = $this->prepareInsertData($entity); + + if (isset($insertData[$tableName])) { + $paramIndex = 1; + + foreach ($insertData[$tableName] as $column => $value) { + $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$column]); + } + } + + $stmt->execute(); + + if ($isPostInsertId) { + $generatedId = $idGenerator->generate($this->em, $entity); + $id = array( + $this->class->identifier[0] => $generatedId + ); + $postInsertIds[] = array( + 'generatedId' => $generatedId, + 'entity' => $entity, + ); + } else { + $id = $this->class->getIdentifierValues($entity); + } + + if ($this->class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + } + + $stmt->closeCursor(); + $this->queuedInserts = array(); + + return $postInsertIds; + } + + /** + * Retrieves the default version value which was created + * by the preceding INSERT statement and assigns it back in to the + * entities version field. + * + * @param object $entity + * @param array $id + * + * @return void + */ + protected function assignDefaultVersionValue($entity, array $id) + { + $value = $this->fetchVersionValue($this->class, $id); + + $this->class->setFieldValue($entity, $this->class->versionField, $value); + } + + /** + * Fetches the current version value of a versioned entity. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $versionedClass + * @param array $id + * + * @return mixed + */ + protected function fetchVersionValue($versionedClass, array $id) + { + $versionField = $versionedClass->versionField; + $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); + $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform); + $columnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->platform); + + // FIXME: Order with composite keys might not be correct + $sql = 'SELECT ' . $columnName + . ' FROM ' . $tableName + . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; + + $flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id); + + $value = $this->conn->fetchColumn($sql, array_values($flatId)); + + return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->platform); + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $tableName = $this->class->getTableName(); + $updateData = $this->prepareUpdateData($entity); + + if ( ! isset($updateData[$tableName]) || ! ($data = $updateData[$tableName])) { + return; + } + + $isVersioned = $this->class->isVersioned; + $quotedTableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + $this->updateTable($entity, $quotedTableName, $data, $isVersioned); + + if ($isVersioned) { + $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + + $this->assignDefaultVersionValue($entity, $id); + } + } + + /** + * Performs an UPDATE statement for an entity on a specific table. + * The UPDATE can optionally be versioned, which requires the entity to have a version field. + * + * @param object $entity The entity object being updated. + * @param string $quotedTableName The quoted name of the table to apply the UPDATE on. + * @param array $updateData The map of columns to update (column => value). + * @param boolean $versioned Whether the UPDATE should be versioned. + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + */ + protected final function updateTable($entity, $quotedTableName, array $updateData, $versioned = false) + { + $set = array(); + $types = array(); + $params = array(); + + foreach ($updateData as $columnName => $value) { + $placeholder = '?'; + $column = $columnName; + + switch (true) { + case isset($this->class->fieldNames[$columnName]): + $fieldName = $this->class->fieldNames[$columnName]; + $column = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); + + if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($this->columnTypes[$columnName]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); + } + + break; + + case isset($this->quotedColumns[$columnName]): + $column = $this->quotedColumns[$columnName]; + + break; + } + + $params[] = $value; + $set[] = $column . ' = ' . $placeholder; + $types[] = $this->columnTypes[$columnName]; + } + + $where = array(); + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + + foreach ($this->class->identifier as $idField) { + if ( ! isset($this->class->associationMappings[$idField])) { + + $params[] = $identifier[$idField]; + $types[] = $this->class->fieldMappings[$idField]['type']; + $where[] = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform); + + continue; + } + + $params[] = $identifier[$idField]; + $where[] = $this->class->associationMappings[$idField]['joinColumns'][0]['name']; + $targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']); + + switch (true) { + case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])): + $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + break; + + case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])): + $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + break; + + default: + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + } + + } + + if ($versioned) { + $versionField = $this->class->versionField; + $versionFieldType = $this->class->fieldMappings[$versionField]['type']; + $versionColumn = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform); + + $where[] = $versionColumn; + $types[] = $this->class->fieldMappings[$versionField]['type']; + $params[] = $this->class->reflFields[$versionField]->getValue($entity); + + switch ($versionFieldType) { + case Type::SMALLINT: + case Type::INTEGER: + case Type::BIGINT: + $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; + break; + + case Type::DATETIME: + $set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; + break; + } + } + + $sql = 'UPDATE ' . $quotedTableName + . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; + + $result = $this->conn->executeUpdate($sql, $params, $types); + + if ($versioned && ! $result) { + throw OptimisticLockException::lockFailed($entity); + } + } + + /** + * @todo Add check for platform if it supports foreign keys/cascading. + * + * @param array $identifier + * + * @return void + */ + protected function deleteJoinTableRecords($identifier) + { + foreach ($this->class->associationMappings as $mapping) { + if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY) { + continue; + } + + // @Todo this only covers scenarios with no inheritance or of the same level. Is there something + // like self-referential relationship between different levels of an inheritance hierarchy? I hope not! + $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); + $class = $this->class; + $association = $mapping; + $otherColumns = array(); + $otherKeys = array(); + $keys = array(); + + if ( ! $mapping['isOwningSide']) { + $class = $this->em->getClassMetadata($mapping['targetEntity']); + $association = $class->associationMappings[$mapping['mappedBy']]; + } + + $joinColumns = $mapping['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + + if ($selfReferential) { + $otherColumns = (! $mapping['isOwningSide']) + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + } + + foreach ($joinColumns as $joinColumn) { + $keys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + foreach ($otherColumns as $joinColumn) { + $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + if (isset($mapping['isOnDeleteCascade'])) { + continue; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + + $this->conn->delete($joinTableName, array_combine($keys, $identifier)); + + if ($selfReferential) { + $this->conn->delete($joinTableName, array_combine($otherKeys, $identifier)); + } + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $class = $this->class; + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $tableName = $this->quoteStrategy->getTableName($class, $this->platform); + $idColumns = $this->quoteStrategy->getIdentifierColumnNames($class, $this->platform); + $id = array_combine($idColumns, $identifier); + $types = array_map(function ($identifier) use ($class) { + + if (isset($class->fieldMappings[$identifier])) { + return $class->fieldMappings[$identifier]['type']; + } + + $targetMapping = $this->em->getClassMetadata($class->associationMappings[$identifier]['targetEntity']); + + if (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])) { + return $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type']; + } + + if (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])) { + return $targetMapping->associationMappings[$targetMapping->identifier[0]]['type']; + } + + throw ORMException::unrecognizedField($targetMapping->identifier[0]); + + }, $class->identifier); + + $this->deleteJoinTableRecords($identifier); + + return (bool) $this->conn->delete($tableName, $id, $types); + } + + /** + * Prepares the changeset of an entity for database insertion (UPDATE). + * + * The changeset is obtained from the currently running UnitOfWork. + * + * During this preparation the array that is passed as the second parameter is filled with + * => pairs, grouped by table name. + * + * Example: + * + * array( + * 'foo_table' => array('column1' => 'value1', 'column2' => 'value2', ...), + * 'bar_table' => array('columnX' => 'valueX', 'columnY' => 'valueY', ...), + * ... + * ) + * + * + * @param object $entity The entity for which to prepare the data. + * + * @return array The prepared data. + */ + protected function prepareUpdateData($entity) + { + $versionField = null; + $result = array(); + $uow = $this->em->getUnitOfWork(); + + if (($versioned = $this->class->isVersioned) != false) { + $versionField = $this->class->versionField; + } + + foreach ($uow->getEntityChangeSet($entity) as $field => $change) { + if (isset($versionField) && $versionField == $field) { + continue; + } + + if (isset($this->class->embeddedClasses[$field])) { + continue; + } + + $newVal = $change[1]; + + if ( ! isset($this->class->associationMappings[$field])) { + $columnName = $this->class->columnNames[$field]; + $this->columnTypes[$columnName] = $this->class->fieldMappings[$field]['type']; + $result[$this->getOwningTable($field)][$columnName] = $newVal; + + continue; + } + + $assoc = $this->class->associationMappings[$field]; + + // Only owning side of x-1 associations can have a FK column. + if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + if ($newVal !== null) { + $oid = spl_object_hash($newVal); + + if (isset($this->queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { + // The associated entity $newVal is not yet persisted, so we must + // set $newVal = null, in order to insert a null value and schedule an + // extra update on the UnitOfWork. + $uow->scheduleExtraUpdate($entity, array($field => array(null, $newVal))); + + $newVal = null; + } + } + + $newValId = null; + + if ($newVal !== null) { + $newValId = $uow->getEntityIdentifier($newVal); + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $owningTable = $this->getOwningTable($field); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $sourceColumn = $joinColumn['name']; + $targetColumn = $joinColumn['referencedColumnName']; + $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + + $this->quotedColumns[$sourceColumn] = $quotedColumn; + $this->columnTypes[$sourceColumn] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); + $result[$owningTable][$sourceColumn] = $newValId + ? $newValId[$targetClass->getFieldForColumn($targetColumn)] + : null; + } + } + + return $result; + } + + /** + * Prepares the data changeset of a managed entity for database insertion (initial INSERT). + * The changeset of the entity is obtained from the currently running UnitOfWork. + * + * The default insert data preparation is the same as for updates. + * + * @param object $entity The entity for which to prepare the data. + * + * @return array The prepared data for the tables to update. + * + * @see prepareUpdateData + */ + protected function prepareInsertData($entity) + { + return $this->prepareUpdateData($entity); + } + + /** + * {@inheritdoc} + */ + public function getOwningTable($fieldName) + { + return $this->class->getTableName(); + } + + /** + * {@inheritdoc} + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = null, $limit = null, array $orderBy = null) + { + $this->switchPersisterContext(null, $limit); + + $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + if ($entity !== null) { + $hints[Query::HINT_REFRESH] = true; + $hints[Query::HINT_REFRESH_ENTITY] = $entity; + } + + $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints); + + return $entities ? $entities[0] : null; + } + + /** + * {@inheritdoc} + */ + public function loadById(array $identifier, $entity = null) + { + return $this->load($identifier, $entity); + } + + /** + * {@inheritdoc} + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) + { + if (($foundEntity = $this->em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) { + return $foundEntity; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ($assoc['isOwningSide']) { + $isInverseSingleValued = $assoc['inversedBy'] && ! $targetClass->isCollectionValuedAssociation($assoc['inversedBy']); + + // Mark inverse side as fetched in the hints, otherwise the UoW would + // try to load it in a separate query (remember: to-one inverse sides can not be lazy). + $hints = array(); + + if ($isInverseSingleValued) { + $hints['fetched']["r"][$assoc['inversedBy']] = true; + } + + /* cascade read-only status + if ($this->em->getUnitOfWork()->isReadOnly($sourceEntity)) { + $hints[Query::HINT_READ_ONLY] = true; + } + */ + + $targetEntity = $this->load($identifier, null, $assoc, $hints); + + // Complete bidirectional association, if necessary + if ($targetEntity !== null && $isInverseSingleValued) { + $targetClass->reflFields[$assoc['inversedBy']]->setValue($targetEntity, $sourceEntity); + } + + return $targetEntity; + } + + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); + + // TRICKY: since the association is specular source and target are flipped + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) { + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + + // unset the old value and set the new sql aliased value here. By definition + // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. + $identifier[$this->getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = + $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); + + unset($identifier[$targetKeyColumn]); + } + + $targetEntity = $this->load($identifier, null, $assoc); + + if ($targetEntity !== null) { + $targetClass->setFieldValue($targetEntity, $assoc['mappedBy'], $sourceEntity); + } + + return $targetEntity; + } + + /** + * {@inheritdoc} + */ + public function refresh(array $id, $entity, $lockMode = null) + { + $sql = $this->getSelectSQL($id, null, $lockMode); + list($params, $types) = $this->expandParameters($id); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + $hydrator = $this->em->newHydrator(Query::HYDRATE_OBJECT); + $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, array(Query::HINT_REFRESH => true)); + } + + /** + * {@inheritDoc} + */ + public function count($criteria = array()) + { + $sql = $this->getCountSQL($criteria); + + list($params, $types) = ($criteria instanceof Criteria) + ? $this->expandCriteriaParameters($criteria) + : $this->expandParameters($criteria); + + return $this->conn->executeQuery($sql, $params, $types)->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function loadCriteria(Criteria $criteria) + { + $orderBy = $criteria->getOrderings(); + $limit = $criteria->getMaxResults(); + $offset = $criteria->getFirstResult(); + $query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); + + list($params, $types) = $this->expandCriteriaParameters($criteria); + + $stmt = $this->conn->executeQuery($query, $params, $types); + $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * {@inheritdoc} + */ + public function expandCriteriaParameters(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + $sqlParams = array(); + $sqlTypes = array(); + + if ($expression === null) { + return array($sqlParams, $sqlTypes); + } + + $valueVisitor = new SqlValueVisitor(); + + $valueVisitor->dispatch($expression); + + list($params, $types) = $valueVisitor->getParamsAndTypes(); + + foreach ($params as $param) { + $sqlParams = array_merge($sqlParams, $this->getValues($param)); + } + + foreach ($types as $type) { + list ($field, $value) = $type; + $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class)); + } + + return array($sqlParams, $sqlTypes); + } + + /** + * {@inheritdoc} + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null) + { + $this->switchPersisterContext($offset, $limit); + + $sql = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy); + list($params, $types) = $this->expandParameters($criteria); + $stmt = $this->conn->executeQuery($sql, $params, $types); + + $hydrator = $this->em->newHydrator(($this->currentPersisterContext->selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); + + return $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, array(UnitOfWork::HINT_DEFEREAGERLOAD => true)); + } + + /** + * {@inheritdoc} + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * Loads an array of entities from a given DBAL statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * + * @return array + */ + private function loadArrayFromStatement($assoc, $stmt) + { + $rsm = $this->currentPersisterContext->rsm; + $hints = array(UnitOfWork::HINT_DEFEREAGERLOAD => true); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } + + return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); + } + + /** + * Hydrates a collection from a given DBAL statement. + * + * @param array $assoc + * @param \Doctrine\DBAL\Statement $stmt + * @param PersistentCollection $coll + * + * @return array + */ + private function loadCollectionFromStatement($assoc, $stmt, $coll) + { + $rsm = $this->currentPersisterContext->rsm; + $hints = array( + UnitOfWork::HINT_DEFEREAGERLOAD => true, + 'collection' => $coll + ); + + if (isset($assoc['indexBy'])) { + $rsm = clone ($this->currentPersisterContext->rsm); // this is necessary because the "default rsm" should be changed. + $rsm->addIndexBy('r', $assoc['indexBy']); + } + + return $this->em->newHydrator(Query::HYDRATE_OBJECT)->hydrateAll($stmt, $rsm, $hints); + } + + /** + * {@inheritdoc} + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getManyToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return \Doctrine\DBAL\Driver\Statement + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + $class = $sourceClass; + $association = $assoc; + $criteria = array(); + $parameters = array(); + + if ( ! $assoc['isOwningSide']) { + $class = $this->em->getClassMetadata($assoc['targetEntity']); + $association = $class->associationMappings[$assoc['mappedBy']]; + } + + $joinColumns = $assoc['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + $quotedJoinTable = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + + foreach ($joinColumns as $joinColumn) { + + $sourceKeyColumn = $joinColumn['referencedColumnName']; + $quotedKeyColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + + switch (true) { + case $sourceClass->containsForeignIdentifier: + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + break; + + case isset($sourceClass->fieldNames[$sourceKeyColumn]): + $field = $sourceClass->fieldNames[$sourceKeyColumn]; + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + break; + + default: + throw MappingException::joinColumnMustPointToMappedField( + $sourceClass->name, $sourceKeyColumn + ); + } + + $criteria[$quotedJoinTable . '.' . $quotedKeyColumn] = $value; + $parameters[] = array( + 'value' => $value, + 'field' => $field, + 'class' => $sourceClass, + ); + } + + $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset); + list($params, $types) = $this->expandToManyParameters($parameters); + + return $this->conn->executeQuery($sql, $params, $types); + } + + /** + * {@inheritdoc} + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) + { + $this->switchPersisterContext($offset, $limit); + + $lockSql = ''; + $joinSql = ''; + $orderBySql = ''; + + if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $joinSql = $this->getSelectManyToManyJoinSQL($assoc); + } + + if (isset($assoc['orderBy'])) { + $orderBy = $assoc['orderBy']; + } + + if ($orderBy) { + $orderBySql = $this->getOrderBySQL($orderBy, $this->getSQLTableAlias($this->class->name)); + } + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria, $assoc); + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $lockSql = ' ' . $this->platform->getReadLockSql(); + break; + + case LockMode::PESSIMISTIC_WRITE: + $lockSql = ' ' . $this->platform->getWriteLockSql(); + break; + } + + $columnList = $this->getSelectColumnsSQL(); + $tableAlias = $this->getSQLTableAlias($this->class->name); + $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $select = 'SELECT ' . $columnList; + $from = ' FROM ' . $tableName . ' '. $tableAlias; + $join = $this->currentPersisterContext->selectJoinSql . $joinSql; + $where = ($conditionSql ? ' WHERE ' . $conditionSql : ''); + $lock = $this->platform->appendLockHint($from, $lockMode); + $query = $select + . $lock + . $join + . $where + . $orderBySql; + + return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; + } + + /** + * {@inheritDoc} + */ + public function getCountSQL($criteria = array()) + { + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $tableAlias = $this->getSQLTableAlias($this->class->name); + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria); + + $filterSql = $this->generateFilterConditionSQL($this->class, $tableAlias); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $sql = 'SELECT COUNT(*) ' + . 'FROM ' . $tableName . ' ' . $tableAlias + . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); + + return $sql; + } + + /** + * Gets the ORDER BY SQL snippet for ordered collections. + * + * @param array $orderBy + * @param string $baseTableAlias + * + * @return string + * + * @throws \Doctrine\ORM\ORMException + */ + protected final function getOrderBySQL(array $orderBy, $baseTableAlias) + { + $orderByList = array(); + + foreach ($orderBy as $fieldName => $orientation) { + + $orientation = strtoupper(trim($orientation)); + + if ($orientation != 'ASC' && $orientation != 'DESC') { + throw ORMException::invalidOrientation($this->class->name, $fieldName); + } + + if (isset($this->class->fieldMappings[$fieldName])) { + $tableAlias = isset($this->class->fieldMappings[$fieldName]['inherited']) + ? $this->getSQLTableAlias($this->class->fieldMappings[$fieldName]['inherited']) + : $baseTableAlias; + + $columnName = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform); + $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; + + continue; + } + + if (isset($this->class->associationMappings[$fieldName])) { + + if ( ! $this->class->associationMappings[$fieldName]['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->class->name, $fieldName); + } + + $tableAlias = isset($this->class->associationMappings[$fieldName]['inherited']) + ? $this->getSQLTableAlias($this->class->associationMappings[$fieldName]['inherited']) + : $baseTableAlias; + + foreach ($this->class->associationMappings[$fieldName]['joinColumns'] as $joinColumn) { + $columnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $orderByList[] = $tableAlias . '.' . $columnName . ' ' . $orientation; + } + + continue; + } + + throw ORMException::unrecognizedField($fieldName); + } + + return ' ORDER BY ' . implode(', ', $orderByList); + } + + /** + * Gets the SQL fragment with the list of columns to select when querying for + * an entity in this persister. + * + * Subclasses should override this method to alter or change the select column + * list SQL fragment. Note that in the implementation of BasicEntityPersister + * the resulting SQL fragment is generated only once and cached in {@link selectColumnListSql}. + * Subclasses may or may not do the same. + * + * @return string The SQL fragment. + */ + protected function getSelectColumnsSQL() + { + if ($this->currentPersisterContext->selectColumnListSql !== null) { + return $this->currentPersisterContext->selectColumnListSql; + } + + $columnList = array(); + $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); // r for root + + // Add regular columns to select list + foreach ($this->class->fieldNames as $field) { + $columnList[] = $this->getSelectColumnSQL($field, $this->class); + } + + $this->currentPersisterContext->selectJoinSql = ''; + $eagerAliasCounter = 0; + + foreach ($this->class->associationMappings as $assocField => $assoc) { + $assocColumnSQL = $this->getSelectColumnAssociationSQL($assocField, $assoc, $this->class); + + if ($assocColumnSQL) { + $columnList[] = $assocColumnSQL; + } + + $isAssocToOneInverseSide = $assoc['type'] & ClassMetadata::TO_ONE && ! $assoc['isOwningSide']; + $isAssocFromOneEager = $assoc['type'] !== ClassMetadata::MANY_TO_MANY && $assoc['fetch'] === ClassMetadata::FETCH_EAGER; + + if ( ! ($isAssocFromOneEager || $isAssocToOneInverseSide)) { + continue; + } + + if ((($assoc['type'] & ClassMetadata::TO_MANY) > 0) && $this->currentPersisterContext->handlesLimits) { + continue; + } + + $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); + + if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { + continue; // now this is why you shouldn't use inheritance + } + + $assocAlias = 'e' . ($eagerAliasCounter++); + $this->currentPersisterContext->rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); + + foreach ($eagerEntity->fieldNames as $field) { + $columnList[] = $this->getSelectColumnSQL($field, $eagerEntity, $assocAlias); + } + + foreach ($eagerEntity->associationMappings as $eagerAssocField => $eagerAssoc) { + $eagerAssocColumnSQL = $this->getSelectColumnAssociationSQL( + $eagerAssocField, $eagerAssoc, $eagerEntity, $assocAlias + ); + + if ($eagerAssocColumnSQL) { + $columnList[] = $eagerAssocColumnSQL; + } + } + + $association = $assoc; + $joinCondition = array(); + + if (isset($assoc['indexBy'])) { + $this->currentPersisterContext->rsm->addIndexBy($assocAlias, $assoc['indexBy']); + } + + if ( ! $assoc['isOwningSide']) { + $eagerEntity = $this->em->getClassMetadata($assoc['targetEntity']); + $association = $eagerEntity->getAssociationMapping($assoc['mappedBy']); + } + + $joinTableAlias = $this->getSQLTableAlias($eagerEntity->name, $assocAlias); + $joinTableName = $this->quoteStrategy->getTableName($eagerEntity, $this->platform); + + if ($assoc['isOwningSide']) { + $tableAlias = $this->getSQLTableAlias($association['targetEntity'], $assocAlias); + $this->currentPersisterContext->selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($association['joinColumns']); + + foreach ($association['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity']) + . '.' . $sourceCol . ' = ' . $tableAlias . '.' . $targetCol; + } + + // Add filter SQL + if ($filterSql = $this->generateFilterConditionSQL($eagerEntity, $tableAlias)) { + $joinCondition[] = $filterSql; + } + + } else { + + $this->currentPersisterContext->selectJoinSql .= ' LEFT JOIN'; + + foreach ($association['joinColumns'] as $joinColumn) { + $sourceCol = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $targetCol = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + + $joinCondition[] = $this->getSQLTableAlias($association['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' + . $this->getSQLTableAlias($association['targetEntity']) . '.' . $targetCol; + } + } + + $this->currentPersisterContext->selectJoinSql .= ' ' . $joinTableName . ' ' . $joinTableAlias . ' ON '; + $this->currentPersisterContext->selectJoinSql .= implode(' AND ', $joinCondition); + } + + $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); + + return $this->currentPersisterContext->selectColumnListSql; + } + + /** + * Gets the SQL join fragment used when selecting entities from an association. + * + * @param string $field + * @param array $assoc + * @param ClassMetadata $class + * @param string $alias + * + * @return string + */ + protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') + { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) ) { + return ''; + } + + $columnList = array(); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $type = null; + $isIdentifier = isset($assoc['id']) && $assoc['id'] === true; + $quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $resultColumnName = $this->getSQLColumnAlias($joinColumn['name']); + $columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) + . '.' . $quotedColumn . ' AS ' . $resultColumnName; + $type = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); + + $this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type); + } + + return implode(', ', $columnList); + } + + /** + * Gets the SQL join fragment used when selecting entities from a + * many-to-many association. + * + * @param array $manyToMany + * + * @return string + */ + protected function getSelectManyToManyJoinSQL(array $manyToMany) + { + $conditions = array(); + $association = $manyToMany; + $sourceTableAlias = $this->getSQLTableAlias($this->class->name); + + if ( ! $manyToMany['isOwningSide']) { + $targetEntity = $this->em->getClassMetadata($manyToMany['targetEntity']); + $association = $targetEntity->associationMappings[$manyToMany['mappedBy']]; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + $joinColumns = ($manyToMany['isOwningSide']) + ? $association['joinTable']['inverseJoinColumns'] + : $association['joinTable']['joinColumns']; + + foreach ($joinColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $this->class, $this->platform); + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableName . '.' . $quotedSourceColumn; + } + + return ' INNER JOIN ' . $joinTableName . ' ON ' . implode(' AND ', $conditions); + } + + /** + * {@inheritdoc} + */ + public function getInsertSQL() + { + if ($this->insertSql !== null) { + return $this->insertSql; + } + + $columns = $this->getInsertColumnList(); + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + + if (empty($columns)) { + $identityColumn = $this->quoteStrategy->getColumnName($this->class->identifier[0], $this->class, $this->platform); + $this->insertSql = $this->platform->getEmptyIdentityInsertSQL($tableName, $identityColumn); + + return $this->insertSql; + } + + $values = array(); + $columns = array_unique($columns); + + foreach ($columns as $column) { + $placeholder = '?'; + + if (isset($this->class->fieldNames[$column]) + && isset($this->columnTypes[$this->class->fieldNames[$column]]) + && isset($this->class->fieldMappings[$this->class->fieldNames[$column]]['requireSQLConversion'])) { + + $type = Type::getType($this->columnTypes[$this->class->fieldNames[$column]]); + $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform); + } + + $values[] = $placeholder; + } + + $columns = implode(', ', $columns); + $values = implode(', ', $values); + + $this->insertSql = sprintf('INSERT INTO %s (%s) VALUES (%s)', $tableName, $columns, $values); + + return $this->insertSql; + } + + /** + * Gets the list of columns to put in the INSERT SQL statement. + * + * Subclasses should override this method to alter or change the list of + * columns placed in the INSERT statements used by the persister. + * + * @return array The list of columns. + */ + protected function getInsertColumnList() + { + $columns = array(); + + foreach ($this->class->reflFields as $name => $field) { + if ($this->class->isVersioned && $this->class->versionField == $name) { + continue; + } + + if (isset($this->class->embeddedClasses[$name])) { + continue; + } + + if (isset($this->class->associationMappings[$name])) { + $assoc = $this->class->associationMappings[$name]; + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + foreach ($assoc['joinColumns'] as $joinColumn) { + $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + } + + continue; + } + + if ($this->class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->class->identifier[0] != $name) { + $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); + $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + } + } + + return $columns; + } + + /** + * Gets the SQL snippet of a qualified column name for the given field name. + * + * @param string $field The field name. + * @param ClassMetadata $class The class that declares this field. The table this class is + * mapped to must own the column for the given field. + * @param string $alias + * + * @return string + */ + protected function getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') + { + $root = $alias == 'r' ? '' : $alias ; + $tableAlias = $this->getSQLTableAlias($class->name, $root); + $columnName = $this->quoteStrategy->getColumnName($field, $class, $this->platform); + $sql = $tableAlias . '.' . $columnName; + $columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]); + + $this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field); + + if (isset($class->fieldMappings[$field]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($field)); + $sql = $type->convertToPHPValueSQL($sql, $this->platform); + } + + return $sql . ' AS ' . $columnAlias; + } + + /** + * Gets the SQL table alias for the given class name. + * + * @param string $className + * @param string $assocName + * + * @return string The SQL table alias. + * + * @todo Reconsider. Binding table aliases to class names is not such a good idea. + */ + protected function getSQLTableAlias($className, $assocName = '') + { + if ($assocName) { + $className .= '#' . $assocName; + } + + if (isset($this->currentPersisterContext->sqlTableAliases[$className])) { + return $this->currentPersisterContext->sqlTableAliases[$className]; + } + + $tableAlias = 't' . $this->currentPersisterContext->sqlAliasCounter++; + + $this->currentPersisterContext->sqlTableAliases[$className] = $tableAlias; + + return $tableAlias; + } + + /** + * {@inheritdoc} + */ + public function lock(array $criteria, $lockMode) + { + $lockSql = ''; + $conditionSql = $this->getSelectConditionSQL($criteria); + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + $lockSql = $this->platform->getReadLockSql(); + + break; + case LockMode::PESSIMISTIC_WRITE: + + $lockSql = $this->platform->getWriteLockSql(); + break; + } + + $lock = $this->getLockTablesSql($lockMode); + $where = ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' '; + $sql = 'SELECT 1 ' + . $lock + . $where + . $lockSql; + + list($params, $types) = $this->expandParameters($criteria); + + $this->conn->executeQuery($sql, $params, $types); + } + + /** + * Gets the FROM and optionally JOIN conditions to lock the entity managed by this persister. + * + * @param integer $lockMode One of the Doctrine\DBAL\LockMode::* constants. + * + * @return string + */ + protected function getLockTablesSql($lockMode) + { + return $this->platform->appendLockHint( + 'FROM ' + . $this->quoteStrategy->getTableName($this->class, $this->platform) . ' ' + . $this->getSQLTableAlias($this->class->name), + $lockMode + ); + } + + /** + * Gets the Select Where Condition from a Criteria object. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return string + */ + protected function getSelectConditionCriteriaSQL(Criteria $criteria) + { + $expression = $criteria->getWhereExpression(); + + if ($expression === null) { + return ''; + } + + $visitor = new SqlExpressionVisitor($this, $this->class); + + return $visitor->dispatch($expression); + } + + /** + * {@inheritdoc} + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null) + { + $selectedColumns = array(); + $columns = $this->getSelectConditionStatementColumnSQL($field, $assoc); + + if (count($columns) > 1 && $comparison === Comparison::IN) { + /* + * @todo try to support multi-column IN expressions. + * Example: (col1, col2) IN (('val1A', 'val2A'), ('val1B', 'val2B')) + */ + throw ORMException::cantUseInOperatorOnCompositeKeys(); + } + + foreach ($columns as $column) { + $placeholder = '?'; + + if (isset($this->class->fieldMappings[$field]['requireSQLConversion'])) { + $placeholder = Type::getType($this->class->getTypeOfField($field))->convertToDatabaseValueSQL($placeholder, $this->platform); + } + + if (null !== $comparison) { + // special case null value handling + if (($comparison === Comparison::EQ || $comparison === Comparison::IS) && null ===$value) { + $selectedColumns[] = $column . ' IS NULL'; + continue; + } + + if ($comparison === Comparison::NEQ && null === $value) { + $selectedColumns[] = $column . ' IS NOT NULL'; + continue; + } + + $selectedColumns[] = $column . ' ' . sprintf(self::$comparisonMap[$comparison], $placeholder); + continue; + } + + if (is_array($value)) { + $in = sprintf('%s IN (%s)', $column, $placeholder); + + if (false !== array_search(null, $value, true)) { + $selectedColumns[] = sprintf('(%s OR %s IS NULL)', $in, $column); + continue; + } + + $selectedColumns[] = $in; + continue; + } + + if (null === $value) { + $selectedColumns[] = sprintf('%s IS NULL', $column); + continue; + } + + $selectedColumns[] = sprintf('%s = %s', $column, $placeholder); + } + + return implode(' AND ', $selectedColumns); + } + + /** + * Builds the left-hand-side of a where condition statement. + * + * @param string $field + * @param array|null $assoc + * + * @return string[] + * + * @throws \Doctrine\ORM\ORMException + */ + private function getSelectConditionStatementColumnSQL($field, $assoc = null) + { + if (isset($this->class->columnNames[$field])) { + $className = (isset($this->class->fieldMappings[$field]['inherited'])) + ? $this->class->fieldMappings[$field]['inherited'] + : $this->class->name; + + return array($this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getColumnName($field, $this->class, $this->platform)); + } + + if (isset($this->class->associationMappings[$field])) { + $association = $this->class->associationMappings[$field]; + // Many-To-Many requires join table check for joinColumn + $columns = array(); + $class = $this->class; + + if ($association['type'] === ClassMetadata::MANY_TO_MANY) { + if ( ! $association['isOwningSide']) { + $association = $assoc; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $class, $this->platform); + $joinColumns = $assoc['isOwningSide'] + ? $association['joinTable']['joinColumns'] + : $association['joinTable']['inverseJoinColumns']; + + + foreach ($joinColumns as $joinColumn) { + $columns[] = $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + + } else { + + if ( ! $association['isOwningSide']) { + throw ORMException::invalidFindByInverseAssociation($this->class->name, $field); + } + + $className = (isset($association['inherited'])) + ? $association['inherited'] + : $this->class->name; + + foreach ($association['joinColumns'] as $joinColumn) { + $columns[] = $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + } + return $columns; + } + + if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { + // very careless developers could potentially open up this normally hidden api for userland attacks, + // therefore checking for spaces and function calls which are not allowed. + + // found a join column condition, not really a "field" + return array($field); + } + + throw ORMException::unrecognizedField($field); + } + + /** + * Gets the conditional SQL fragment used in the WHERE clause when selecting + * entities in this persister. + * + * Subclasses are supposed to override this method if they intend to change + * or alter the criteria by which entities are selected. + * + * @param array $criteria + * @param array|null $assoc + * + * @return string + */ + protected function getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditions = array(); + + foreach ($criteria as $field => $value) { + $conditions[] = $this->getSelectConditionStatementSQL($field, $value, $assoc); + } + + return implode(' AND ', $conditions); + } + + /** + * {@inheritdoc} + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); + + return $this->loadArrayFromStatement($assoc, $stmt); + } + + /** + * {@inheritdoc} + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) + { + $stmt = $this->getOneToManyStatement($assoc, $sourceEntity); + + return $this->loadCollectionFromStatement($assoc, $stmt, $coll); + } + + /** + * Builds criteria and execute SQL statement to fetch the one to many entities from. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return \Doctrine\DBAL\Statement + */ + private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null) + { + $this->switchPersisterContext($offset, $limit); + + $criteria = array(); + $parameters = array(); + $owningAssoc = $this->class->associationMappings[$assoc['mappedBy']]; + $sourceClass = $this->em->getClassMetadata($assoc['sourceEntity']); + + $tableAlias = $this->getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->class->name); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { + if ($sourceClass->containsForeignIdentifier) { + $field = $sourceClass->getFieldForColumn($sourceKeyColumn); + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + if (isset($sourceClass->associationMappings[$field])) { + $value = $this->em->getUnitOfWork()->getEntityIdentifier($value); + $value = $value[$this->em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; + } + + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; + $parameters[] = array( + 'value' => $value, + 'field' => $field, + 'class' => $sourceClass, + ); + + continue; + } + + $field = $sourceClass->fieldNames[$sourceKeyColumn]; + $value = $sourceClass->reflFields[$field]->getValue($sourceEntity); + + $criteria[$tableAlias . "." . $targetKeyColumn] = $value; + $parameters[] = array( + 'value' => $value, + 'field' => $field, + 'class' => $sourceClass, + ); + + } + + $sql = $this->getSelectSQL($criteria, $assoc, null, $limit, $offset); + list($params, $types) = $this->expandToManyParameters($parameters); + + return $this->conn->executeQuery($sql, $params, $types); + } + + /** + * {@inheritdoc} + */ + public function expandParameters($criteria) + { + $params = array(); + $types = array(); + + foreach ($criteria as $field => $value) { + if ($value === null) { + continue; // skip null values. + } + + $types = array_merge($types, $this->getTypes($field, $value, $this->class)); + $params = array_merge($params, $this->getValues($value)); + } + + return array($params, $types); + } + + /** + * Expands the parameters from the given criteria and use the correct binding types if found, + * specialized for OneToMany or ManyToMany associations. + * + * @param mixed[][] $criteria an array of arrays containing following: + * - field to which each criterion will be bound + * - value to be bound + * - class to which the field belongs to + * + * + * @return array + */ + private function expandToManyParameters($criteria) + { + $params = array(); + $types = array(); + + foreach ($criteria as $criterion) { + if ($criterion['value'] === null) { + continue; // skip null values. + } + + $types = array_merge($types, $this->getTypes($criterion['field'], $criterion['value'], $criterion['class'])); + $params = array_merge($params, $this->getValues($criterion['value'])); + } + + return array($params, $types); + } + + /** + * Infers field types to be used by parameter type casting. + * + * @param string $field + * @param mixed $value + * + * @return array + * + * @throws \Doctrine\ORM\Query\QueryException + */ + private function getTypes($field, $value, ClassMetadata $class) + { + $types = array(); + + switch (true) { + case (isset($class->fieldMappings[$field])): + $types = array_merge($types, PersisterHelper::getTypeOfField($field, $class, $this->em)); + break; + + case (isset($class->associationMappings[$field])): + $assoc = $class->associationMappings[$field]; + $class = $this->em->getClassMetadata($assoc['targetEntity']); + + if (! $assoc['isOwningSide']) { + $assoc = $class->associationMappings[$assoc['mappedBy']]; + $class = $this->em->getClassMetadata($assoc['targetEntity']); + } + + $columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY + ? $assoc['relationToTargetKeyColumns'] + : $assoc['sourceToTargetKeyColumns']; + + foreach ($columns as $column){ + $types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em); + } + break; + + default: + $types[] = null; + break; + } + + if (is_array($value)) { + return array_map( + function ($type) { + return Type::getType($type)->getBindingType() + Connection::ARRAY_PARAM_OFFSET; + }, + $types + ); + } + + return $types; + } + + /** + * Retrieves the parameters that identifies a value. + * + * @param mixed $value + * + * @return array + */ + private function getValues($value) + { + if (is_array($value)) { + $newValue = array(); + + foreach ($value as $itemValue) { + $newValue = array_merge($newValue, $this->getValues($itemValue)); + } + + return array($newValue); + } + + if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + $class = $this->em->getClassMetadata(get_class($value)); + if ($class->isIdentifierComposite) { + $newValue = array(); + + foreach ($class->getIdentifierValues($value) as $innerValue) { + $newValue = array_merge($newValue, $this->getValues($innerValue)); + } + + return $newValue; + } + } + + return array($this->getIndividualValue($value)); + } + + /** + * Retrieves an individual parameter value. + * + * @param mixed $value + * + * @return mixed + */ + private function getIndividualValue($value) + { + if ( ! is_object($value) || ! $this->em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) { + return $value; + } + + return $this->em->getUnitOfWork()->getSingleIdentifierValue($value); + } + + /** + * {@inheritdoc} + */ + public function exists($entity, Criteria $extraConditions = null) + { + $criteria = $this->class->getIdentifierValues($entity); + + if ( ! $criteria) { + return false; + } + + $alias = $this->getSQLTableAlias($this->class->name); + + $sql = 'SELECT 1 ' + . $this->getLockTablesSql(null) + . ' WHERE ' . $this->getSelectConditionSQL($criteria); + + list($params, $types) = $this->expandParameters($criteria); + + if (null !== $extraConditions) { + $sql .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions); + list($criteriaParams, $criteriaTypes) = $this->expandCriteriaParameters($extraConditions); + + $params = array_merge($params, $criteriaParams); + $types = array_merge($types, $criteriaTypes); + } + + if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) { + $sql .= ' AND ' . $filterSql; + } + + return (bool) $this->conn->fetchColumn($sql, $params, 0, $types); + } + + /** + * Generates the appropriate join SQL for the given join column. + * + * @param array $joinColumns The join columns definition of an association. + * + * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise. + */ + protected function getJoinSQLForJoinColumns($joinColumns) + { + // if one of the join columns is nullable, return left join + foreach ($joinColumns as $joinColumn) { + if ( ! isset($joinColumn['nullable']) || $joinColumn['nullable']) { + return 'LEFT JOIN'; + } + } + + return 'INNER JOIN'; + } + + /** + * {@inheritdoc} + */ + public function getSQLColumnAlias($columnName) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->currentPersisterContext->sqlAliasCounter++, $this->platform); + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterClauses = array(); + + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + $sql = implode(' AND ', $filterClauses); + return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL" + } + + /** + * Switches persister context according to current query offset/limits + * + * This is due to the fact that to-many associations cannot be fetch-joined when a limit is involved + * + * @param null|int $offset + * @param null|int $limit + */ + protected function switchPersisterContext($offset, $limit) + { + if (null === $offset && null === $limit) { + $this->currentPersisterContext = $this->noLimitsContext; + + return; + } + + $this->currentPersisterContext = $this->limitsHandlingContext; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php new file mode 100644 index 0000000..81fde93 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/CachedPersisterContext.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ResultSetMapping; + +/** + * A swappable persister context to use as a container for the current + * generated query/resultSetMapping/type binding information. + * + * This class is a utility class to be used only by the persister API + * + * This object is highly mutable due to performance reasons. Same reasoning + * behind its properties being public. + * + * @author Marco Pivetta + */ +class CachedPersisterContext +{ + /** + * Metadata object that describes the mapping of the mapped entity class. + * + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + public $class; + + /** + * ResultSetMapping that is used for all queries. Is generated lazily once per request. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + public $rsm; + + /** + * The SELECT column list SQL fragment used for querying entities by this persister. + * This SQL fragment is only generated once per request, if at all. + * + * @var string|null + */ + public $selectColumnListSql; + + /** + * The JOIN SQL fragment used to eagerly load all many-to-one and one-to-one + * associations configured as FETCH_EAGER, as well as all inverse one-to-one associations. + * + * @var string + */ + public $selectJoinSql; + + /** + * Counter for creating unique SQL table and column aliases. + * + * @var integer + */ + public $sqlAliasCounter = 0; + + /** + * Map from class names (FQCN) to the corresponding generated SQL table aliases. + * + * @var array + */ + public $sqlTableAliases = array(); + + /** + * Whether this persistent context is considering limit operations applied to the selection queries + * + * @var bool + */ + public $handlesLimits; + + /** + * @param ClassMetadata $class + * @param ResultSetMapping $rsm + * @param bool $handlesLimits + */ + public function __construct( + ClassMetadata $class, + ResultSetMapping $rsm, + $handlesLimits + ) { + $this->class = $class; + $this->rsm = $rsm; + $this->handlesLimits = (bool) $handlesLimits; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php new file mode 100644 index 0000000..55887ee --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php @@ -0,0 +1,329 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\PersistentCollection; +use Doctrine\Common\Collections\Criteria; + +/** + * Entity persister interface + * Define the behavior that should be implemented by all entity persisters. + * + * @author Fabio B. Silva + * @since 2.5 + */ +interface EntityPersister +{ + /** + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + public function getClassMetadata(); + + /** + * Gets the ResultSetMapping used for hydration. + * + * @return \Doctrine\ORM\Query\ResultSetMapping + */ + public function getResultSetMapping(); + + /** + * Get all queued inserts. + * + * @return array + */ + public function getInserts(); + + /** + * @TODO - It should not be here. + * But its necessary since JoinedSubclassPersister#executeInserts invoke the root persister. + * + * Gets the INSERT SQL used by the persister to persist a new entity. + * + * @return string + */ + public function getInsertSQL(); + + /** + * Gets the SELECT SQL to select one or more entities by a set of field criteria. + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * @param array|null $assoc + * @param int|null $lockMode + * @param int|null $limit + * @param int|null $offset + * @param array|null $orderBy + * + * @return string + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null); + + /** + * Get the COUNT SQL to count entities (optionally based on a criteria) + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * @return string + */ + public function getCountSQL($criteria = array()); + + /** + * Expands the parameters from the given criteria and use the correct binding types if found. + * + * @param $criteria + * + * @return array + */ + public function expandParameters($criteria); + + /** + * Expands Criteria Parameters by walking the expressions and grabbing all parameters and types from it. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function expandCriteriaParameters(Criteria $criteria); + + /** + * Gets the SQL WHERE condition for matching a field with a given value. + * + * @param string $field + * @param mixed $value + * @param array|null $assoc + * @param string|null $comparison + * + * @return string + */ + public function getSelectConditionStatementSQL($field, $value, $assoc = null, $comparison = null); + + /** + * Adds an entity to the queued insertions. + * The entity remains queued until {@link executeInserts} is invoked. + * + * @param object $entity The entity to queue for insertion. + * + * @return void + */ + public function addInsert($entity); + + /** + * Executes all queued entity insertions and returns any generated post-insert + * identifiers that were created as a result of the insertions. + * + * If no inserts are queued, invoking this method is a NOOP. + * + * @return array An array of any generated post-insert IDs. This will be an empty array + * if the entity class does not use the IDENTITY generation strategy. + */ + public function executeInserts(); + + /** + * Updates a managed entity. The entity is updated according to its current changeset + * in the running UnitOfWork. If there is no changeset, nothing is updated. + * + * @param object $entity The entity to update. + * + * @return void + */ + public function update($entity); + + /** + * Deletes a managed entity. + * + * The entity to delete must be managed and have a persistent identifier. + * The deletion happens instantaneously. + * + * Subclasses may override this method to customize the semantics of entity deletion. + * + * @param object $entity The entity to delete. + * + * @return bool TRUE if the entity got deleted in the database, FALSE otherwise. + */ + public function delete($entity); + + /** + * Count entities (optionally filtered by a criteria) + * + * @param array|\Doctrine\Common\Collections\Criteria $criteria + * + * @return int + */ + public function count($criteria = array()); + + /** + * Gets the name of the table that owns the column the given field is mapped to. + * + * The default implementation in BasicEntityPersister always returns the name + * of the table the entity type of this persister is mapped to, since an entity + * is always persisted to a single table with a BasicEntityPersister. + * + * @param string $fieldName The field name. + * + * @return string The table name. + */ + public function getOwningTable($fieldName); + + /** + * Loads an entity by a list of field criteria. + * + * @param array $criteria The criteria by which to load the entity. + * @param object|null $entity The entity to load the data into. If not specified, a new entity is created. + * @param array|null $assoc The association that connects the entity to load to another entity, if any. + * @param array $hints Hints for entity creation. + * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * for loading the entity. + * @param int|null $limit Limit number of results. + * @param array|null $orderBy Criteria to order by. + * + * @return object|null The loaded and managed entity instance or NULL if the entity can not be found. + * + * @todo Check identity map? loadById method? Try to guess whether $criteria is the id? + */ + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = null, $limit = null, array $orderBy = null); + + /** + * Loads an entity by identifier. + * + * @param array $identifier The entity identifier. + * @param object|null $entity The entity to load the data into. If not specified, a new entity is created. + * + * @return object The loaded and managed entity instance or NULL if the entity can not be found. + * + * @todo Check parameters + */ + public function loadById(array $identifier, $entity = null); + + /** + * Loads an entity of this persister's mapped class as part of a single-valued + * association from another entity. + * + * @param array $assoc The association to load. + * @param object $sourceEntity The entity that owns the association (not necessarily the "owning side"). + * @param array $identifier The identifier of the entity to load. Must be provided if + * the association to load represents the owning side, otherwise + * the identifier is derived from the $sourceEntity. + * + * @return object The loaded and managed entity instance or NULL if the entity can not be found. + * + * @throws \Doctrine\ORM\Mapping\MappingException + */ + public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()); + + /** + * Refreshes a managed entity. + * + * @param array $id The identifier of the entity as an associative array from + * column or field names to values. + * @param object $entity The entity to refresh. + * @param int|null $lockMode One of the \Doctrine\DBAL\LockMode::* constants + * or NULL if no specific lock mode should be used + * for refreshing the managed entity. + * + * @return void + */ + public function refresh(array $id, $entity, $lockMode = null); + + /** + * Loads Entities matching the given Criteria object. + * + * @param \Doctrine\Common\Collections\Criteria $criteria + * + * @return array + */ + public function loadCriteria(Criteria $criteria); + + /** + * Loads a list of entities by a list of field criteria. + * + * @param array $criteria + * @param array|null $orderBy + * @param int|null $limit + * @param int|null $offset + * + * @return array + */ + public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null); + + /** + * Gets (sliced or full) elements of the given collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return array + */ + public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null); + + /** + * Loads a collection of entities of a many-to-many association. + * + * @param array $assoc The association mapping of the association being loaded. + * @param object $sourceEntity The entity that owns the collection. + * @param PersistentCollection $collection The collection to fill. + * + * @return array + */ + public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection); + + /** + * Loads a collection of entities in a one-to-many association. + * + * @param array $assoc + * @param object $sourceEntity + * @param PersistentCollection $collection The collection to load/fill. + * + * @return array + */ + public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $collection); + + /** + * Locks all rows of this entity matching the given criteria with the specified pessimistic lock mode. + * + * @param array $criteria + * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants. + * + * @return void + */ + public function lock(array $criteria, $lockMode); + + /** + * Returns an array with (sliced or full list) of elements in the specified collection. + * + * @param array $assoc + * @param object $sourceEntity + * @param int|null $offset + * @param int|null $limit + * + * @return array + */ + public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null); + + /** + * Checks whether the given managed entity exists in the database. + * + * @param object $entity + * @param Criteria|null $extraConditions + * + * @return boolean TRUE if the entity exists in the database, FALSE otherwise. + */ + public function exists($entity, Criteria $extraConditions = null); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php new file mode 100644 index 0000000..b807fba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/JoinedSubclassPersister.php @@ -0,0 +1,628 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ResultSetMapping; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * The joined subclass persister maps a single entity instance to several tables in the + * database as it is defined by the Class Table Inheritance strategy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @see http://martinfowler.com/eaaCatalog/classTableInheritance.html + */ +class JoinedSubclassPersister extends AbstractEntityInheritancePersister +{ + /** + * Map that maps column names to the table names that own them. + * This is mainly a temporary cache, used during a single request. + * + * @var array + */ + private $owningTableMap = array(); + + /** + * Map of table to quoted table names. + * + * @var array + */ + private $quotedTableMap = array(); + + /** + * {@inheritdoc} + */ + protected function getDiscriminatorColumnTableName() + { + $class = ($this->class->name !== $this->class->rootEntityName) + ? $this->em->getClassMetadata($this->class->rootEntityName) + : $this->class; + + return $class->getTableName(); + } + + /** + * This function finds the ClassMetadata instance in an inheritance hierarchy + * that is responsible for enabling versioning. + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function getVersionedClassMetadata() + { + if (isset($this->class->fieldMappings[$this->class->versionField]['inherited'])) { + $definingClassName = $this->class->fieldMappings[$this->class->versionField]['inherited']; + + return $this->em->getClassMetadata($definingClassName); + } + + return $this->class; + } + + /** + * Gets the name of the table that owns the column the given field is mapped to. + * + * @param string $fieldName + * + * @return string + * + * @override + */ + public function getOwningTable($fieldName) + { + if (isset($this->owningTableMap[$fieldName])) { + return $this->owningTableMap[$fieldName]; + } + + switch (true) { + case isset($this->class->associationMappings[$fieldName]['inherited']): + $cm = $this->em->getClassMetadata($this->class->associationMappings[$fieldName]['inherited']); + break; + + case isset($this->class->fieldMappings[$fieldName]['inherited']): + $cm = $this->em->getClassMetadata($this->class->fieldMappings[$fieldName]['inherited']); + break; + + default: + $cm = $this->class; + break; + } + + $tableName = $cm->getTableName(); + $quotedTableName = $this->quoteStrategy->getTableName($cm, $this->platform); + + $this->owningTableMap[$fieldName] = $tableName; + $this->quotedTableMap[$tableName] = $quotedTableName; + + return $tableName; + } + + /** + * {@inheritdoc} + */ + public function executeInserts() + { + if ( ! $this->queuedInserts) { + return array(); + } + + $postInsertIds = array(); + $idGenerator = $this->class->idGenerator; + $isPostInsertId = $idGenerator->isPostInsertGenerator(); + $rootClass = ($this->class->name !== $this->class->rootEntityName) + ? $this->em->getClassMetadata($this->class->rootEntityName) + : $this->class; + + // Prepare statement for the root table + $rootPersister = $this->em->getUnitOfWork()->getEntityPersister($rootClass->name); + $rootTableName = $rootClass->getTableName(); + $rootTableStmt = $this->conn->prepare($rootPersister->getInsertSQL()); + + // Prepare statements for sub tables. + $subTableStmts = array(); + + if ($rootClass !== $this->class) { + $subTableStmts[$this->class->getTableName()] = $this->conn->prepare($this->getInsertSQL()); + } + + foreach ($this->class->parentClasses as $parentClassName) { + $parentClass = $this->em->getClassMetadata($parentClassName); + $parentTableName = $parentClass->getTableName(); + + if ($parentClass !== $rootClass) { + $parentPersister = $this->em->getUnitOfWork()->getEntityPersister($parentClassName); + $subTableStmts[$parentTableName] = $this->conn->prepare($parentPersister->getInsertSQL()); + } + } + + // Execute all inserts. For each entity: + // 1) Insert on root table + // 2) Insert on sub tables + foreach ($this->queuedInserts as $entity) { + $insertData = $this->prepareInsertData($entity); + + // Execute insert on root table + $paramIndex = 1; + + foreach ($insertData[$rootTableName] as $columnName => $value) { + $rootTableStmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + } + + $rootTableStmt->execute(); + + if ($isPostInsertId) { + $generatedId = $idGenerator->generate($this->em, $entity); + $id = array( + $this->class->identifier[0] => $generatedId + ); + $postInsertIds[] = array( + 'generatedId' => $generatedId, + 'entity' => $entity, + ); + } else { + $id = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + } + + if ($this->class->isVersioned) { + $this->assignDefaultVersionValue($entity, $id); + } + + // Execute inserts on subtables. + // The order doesn't matter because all child tables link to the root table via FK. + foreach ($subTableStmts as $tableName => $stmt) { + /** @var \Doctrine\DBAL\Statement $stmt */ + $paramIndex = 1; + $data = isset($insertData[$tableName]) + ? $insertData[$tableName] + : array(); + + foreach ((array) $id as $idName => $idVal) { + $type = isset($this->columnTypes[$idName]) ? $this->columnTypes[$idName] : Type::STRING; + + $stmt->bindValue($paramIndex++, $idVal, $type); + } + + foreach ($data as $columnName => $value) { + if (!is_array($id) || !isset($id[$columnName])) { + $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$columnName]); + } + } + + $stmt->execute(); + } + } + + $rootTableStmt->closeCursor(); + + foreach ($subTableStmts as $stmt) { + $stmt->closeCursor(); + } + + $this->queuedInserts = array(); + + return $postInsertIds; + } + + /** + * {@inheritdoc} + */ + public function update($entity) + { + $updateData = $this->prepareUpdateData($entity); + + if ( ! $updateData) { + return; + } + + if (($isVersioned = $this->class->isVersioned) === false) { + return; + } + + $versionedClass = $this->getVersionedClassMetadata(); + $versionedTable = $versionedClass->getTableName(); + + foreach ($updateData as $tableName => $data) { + $tableName = $this->quotedTableMap[$tableName]; + $versioned = $isVersioned && $versionedTable === $tableName; + + $this->updateTable($entity, $tableName, $data, $versioned); + } + + // Make sure the table with the version column is updated even if no columns on that + // table were affected. + if ($isVersioned) { + if ( ! isset($updateData[$versionedTable])) { + $tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform); + $this->updateTable($entity, $tableName, array(), true); + } + + $identifiers = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $this->assignDefaultVersionValue($entity, $identifiers); + } + } + + /** + * {@inheritdoc} + */ + public function delete($entity) + { + $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity); + $id = array_combine($this->class->getIdentifierColumnNames(), $identifier); + + $this->deleteJoinTableRecords($identifier); + + // If the database platform supports FKs, just + // delete the row from the root table. Cascades do the rest. + if ($this->platform->supportsForeignKeyConstraints()) { + $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); + $rootTable = $this->quoteStrategy->getTableName($rootClass, $this->platform); + + return (bool) $this->conn->delete($rootTable, $id); + } + + // Delete from all tables individually, starting from this class' table up to the root table. + $rootTable = $this->quoteStrategy->getTableName($this->class, $this->platform); + + $affectedRows = $this->conn->delete($rootTable, $id); + + foreach ($this->class->parentClasses as $parentClass) { + $parentMetadata = $this->em->getClassMetadata($parentClass); + $parentTable = $this->quoteStrategy->getTableName($parentMetadata, $this->platform); + + $this->conn->delete($parentTable, $id); + } + + return (bool) $affectedRows; + } + + /** + * {@inheritdoc} + */ + public function getSelectSQL($criteria, $assoc = null, $lockMode = null, $limit = null, $offset = null, array $orderBy = null) + { + $this->switchPersisterContext($offset, $limit); + + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $joinSql = $this->getJoinSql($baseTableAlias); + + if ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $joinSql .= $this->getSelectManyToManyJoinSQL($assoc); + } + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria, $assoc); + + // If the current class in the root entity, add the filters + if ($filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName))) { + $conditionSql .= $conditionSql + ? ' AND ' . $filterSql + : $filterSql; + } + + $orderBySql = ''; + + if ($assoc !== null && isset($assoc['orderBy'])) { + $orderBy = $assoc['orderBy']; + } + + if ($orderBy) { + $orderBySql = $this->getOrderBySQL($orderBy, $baseTableAlias); + } + + $lockSql = ''; + + switch ($lockMode) { + case LockMode::PESSIMISTIC_READ: + + $lockSql = ' ' . $this->platform->getReadLockSql(); + + break; + + case LockMode::PESSIMISTIC_WRITE: + + $lockSql = ' ' . $this->platform->getWriteLockSql(); + + break; + } + + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $from = ' FROM ' . $tableName . ' ' . $baseTableAlias; + $where = $conditionSql != '' ? ' WHERE ' . $conditionSql : ''; + $lock = $this->platform->appendLockHint($from, $lockMode); + $columnList = $this->getSelectColumnsSQL(); + $query = 'SELECT ' . $columnList + . $lock + . $joinSql + . $where + . $orderBySql; + + return $this->platform->modifyLimitQuery($query, $limit, $offset) . $lockSql; + } + + /** + * {@inheritDoc} + */ + public function getCountSQL($criteria = array()) + { + $tableName = $this->quoteStrategy->getTableName($this->class, $this->platform); + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $joinSql = $this->getJoinSql($baseTableAlias); + + $conditionSql = ($criteria instanceof Criteria) + ? $this->getSelectConditionCriteriaSQL($criteria) + : $this->getSelectConditionSQL($criteria); + + $filterSql = $this->generateFilterConditionSQL($this->em->getClassMetadata($this->class->rootEntityName), $this->getSQLTableAlias($this->class->rootEntityName)); + + if ('' !== $filterSql) { + $conditionSql = $conditionSql + ? $conditionSql . ' AND ' . $filterSql + : $filterSql; + } + + $sql = 'SELECT COUNT(*) ' + . 'FROM ' . $tableName . ' ' . $baseTableAlias + . $joinSql + . (empty($conditionSql) ? '' : ' WHERE ' . $conditionSql); + + return $sql; + } + + /** + * {@inheritdoc} + */ + protected function getLockTablesSql($lockMode) + { + $joinSql = ''; + $identifierColumns = $this->class->getIdentifierColumnNames(); + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + + // INNER JOIN parent tables + foreach ($this->class->parentClasses as $parentClassName) { + $conditions = array(); + $tableAlias = $this->getSQLTableAlias($parentClassName); + $parentClass = $this->em->getClassMetadata($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + foreach ($identifierColumns as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + return parent::getLockTablesSql($lockMode) . $joinSql; + } + + /** + * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly. + * + * @return string + */ + protected function getSelectColumnsSQL() + { + // Create the column list fragment only once + if ($this->currentPersisterContext->selectColumnListSql !== null) { + return $this->currentPersisterContext->selectColumnListSql; + } + + $columnList = array(); + $discrColumn = $this->class->discriminatorColumn['name']; + $baseTableAlias = $this->getSQLTableAlias($this->class->name); + $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); + + $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); + $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn); + + // Add regular columns + foreach ($this->class->fieldMappings as $fieldName => $mapping) { + $class = isset($mapping['inherited']) + ? $this->em->getClassMetadata($mapping['inherited']) + : $this->class; + + $columnList[] = $this->getSelectColumnSQL($fieldName, $class); + } + + // Add foreign key columns + foreach ($this->class->associationMappings as $mapping) { + if ( ! $mapping['isOwningSide'] || ! ($mapping['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $tableAlias = isset($mapping['inherited']) + ? $this->getSQLTableAlias($mapping['inherited']) + : $baseTableAlias; + + foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($mapping['inherited']) + ? $mapping['inherited'] + : $this->class->name; + + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + $columnList[] = $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + $className, + PersisterHelper::getTypeOfColumn( + $mapping['sourceToTargetKeyColumns'][$srcColumn], + $targetClass, + $this->em + ) + ); + } + } + + // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult). + $tableAlias = ($this->class->rootEntityName == $this->class->name) + ? $baseTableAlias + : $this->getSQLTableAlias($this->class->rootEntityName); + + $columnList[] = $tableAlias . '.' . $discrColumn; + + // sub tables + foreach ($this->class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClassName); + + // Add subclass columns + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); + } + + // Add join columns (foreign keys) + foreach ($subClass->associationMappings as $mapping) { + if ( ! $mapping['isOwningSide'] + || ! ($mapping['type'] & ClassMetadata::TO_ONE) + || isset($mapping['inherited'])) { + continue; + } + + foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($mapping['inherited']) + ? $mapping['inherited'] + : $subClass->name; + + $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); + + $columnList[] = $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + $className, + PersisterHelper::getTypeOfColumn( + $mapping['sourceToTargetKeyColumns'][$srcColumn], + $targetClass, + $this->em + ) + ); + } + } + } + + $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); + + return $this->currentPersisterContext->selectColumnListSql; + } + + /** + * {@inheritdoc} + */ + protected function getInsertColumnList() + { + // Identifier columns must always come first in the column list of subclasses. + $columns = $this->class->parentClasses + ? $this->class->getIdentifierColumnNames() + : array(); + + foreach ($this->class->reflFields as $name => $field) { + if (isset($this->class->fieldMappings[$name]['inherited']) + && ! isset($this->class->fieldMappings[$name]['id']) + || isset($this->class->associationMappings[$name]['inherited']) + || ($this->class->isVersioned && $this->class->versionField == $name) + || isset($this->class->embeddedClasses[$name])) { + continue; + } + + if (isset($this->class->associationMappings[$name])) { + $assoc = $this->class->associationMappings[$name]; + if ($assoc['type'] & ClassMetadata::TO_ONE && $assoc['isOwningSide']) { + foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { + $columns[] = $sourceCol; + } + } + } else if ($this->class->name != $this->class->rootEntityName || + ! $this->class->isIdGeneratorIdentity() || $this->class->identifier[0] != $name) { + $columns[] = $this->quoteStrategy->getColumnName($name, $this->class, $this->platform); + $this->columnTypes[$name] = $this->class->fieldMappings[$name]['type']; + } + } + + // Add discriminator column if it is the topmost class. + if ($this->class->name == $this->class->rootEntityName) { + $columns[] = $this->class->discriminatorColumn['name']; + } + + return $columns; + } + + /** + * {@inheritdoc} + */ + protected function assignDefaultVersionValue($entity, array $id) + { + $value = $this->fetchVersionValue($this->getVersionedClassMetadata(), $id); + $this->class->setFieldValue($entity, $this->class->versionField, $value); + } + + /** + * @param string $baseTableAlias + * @return string + */ + private function getJoinSql($baseTableAlias) + { + $joinSql = ''; + $identifierColumn = $this->class->getIdentifierColumnNames(); + + // INNER JOIN parent tables + foreach ($this->class->parentClasses as $parentClassName) { + $conditions = array(); + $parentClass = $this->em->getClassMetadata($parentClassName); + $tableAlias = $this->getSQLTableAlias($parentClassName); + $joinSql .= ' INNER JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + + foreach ($identifierColumn as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + // OUTER JOIN sub tables + foreach ($this->class->subClasses as $subClassName) { + $conditions = array(); + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClassName); + $joinSql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + foreach ($identifierColumn as $idColumn) { + $conditions[] = $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; + } + + $joinSql .= implode(' AND ', $conditions); + } + + return $joinSql; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php new file mode 100644 index 0000000..f9e4d55 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/SingleTablePersister.php @@ -0,0 +1,198 @@ +. + */ + +namespace Doctrine\ORM\Persisters\Entity; + +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\Collections\Criteria; +use Doctrine\ORM\Utility\PersisterHelper; + +/** + * Persister for entities that participate in a hierarchy mapped with the + * SINGLE_TABLE strategy. + * + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @since 2.0 + * @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html + */ +class SingleTablePersister extends AbstractEntityInheritancePersister +{ + /** + * {@inheritdoc} + */ + protected function getDiscriminatorColumnTableName() + { + return $this->class->getTableName(); + } + + /** + * {@inheritdoc} + */ + protected function getSelectColumnsSQL() + { + if ($this->currentPersisterContext->selectColumnListSql !== null) { + return $this->currentPersisterContext->selectColumnListSql; + } + + $columnList[] = parent::getSelectColumnsSQL(); + + $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); + $tableAlias = $this->getSQLTableAlias($rootClass->name); + + // Append discriminator column + $discrColumn = $this->class->discriminatorColumn['name']; + $columnList[] = $tableAlias . '.' . $discrColumn; + + $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); + + $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); + $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn); + + // Append subclass columns + foreach ($this->class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + + // Regular columns + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); + } + + // Foreign key columns + foreach ($subClass->associationMappings as $assoc) { + if ( ! $assoc['isOwningSide'] + || ! ($assoc['type'] & ClassMetadata::TO_ONE) + || isset($assoc['inherited'])) { + continue; + } + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $className = isset($assoc['inherited']) ? $assoc['inherited'] : $this->class->name; + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + $columnList[] = $this->getSelectJoinColumnSQL( + $tableAlias, + $srcColumn, + $className, + PersisterHelper::getTypeOfColumn( + $assoc['sourceToTargetKeyColumns'][$srcColumn], + $targetClass, + $this->em + ) + ); + } + } + } + + $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); + + return $this->currentPersisterContext->selectColumnListSql; + } + + /** + * {@inheritdoc} + */ + protected function getInsertColumnList() + { + $columns = parent::getInsertColumnList(); + + // Add discriminator column to the INSERT SQL + $columns[] = $this->class->discriminatorColumn['name']; + + return $columns; + } + + /** + * {@inheritdoc} + */ + protected function getSQLTableAlias($className, $assocName = '') + { + return parent::getSQLTableAlias($this->class->rootEntityName, $assocName); + } + + /** + * {@inheritdoc} + */ + protected function getSelectConditionSQL(array $criteria, $assoc = null) + { + $conditionSql = parent::getSelectConditionSQL($criteria, $assoc); + + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); + } + + /** + * {@inheritdoc} + */ + protected function getSelectConditionCriteriaSQL(Criteria $criteria) + { + $conditionSql = parent::getSelectConditionCriteriaSQL($criteria); + + if ($conditionSql) { + $conditionSql .= ' AND '; + } + + return $conditionSql . $this->getSelectConditionDiscriminatorValueSQL(); + } + + /** + * @return string + */ + protected function getSelectConditionDiscriminatorValueSQL() + { + $values = array(); + + if ($this->class->discriminatorValue !== null) { // discriminators can be 0 + $values[] = $this->conn->quote($this->class->discriminatorValue); + } + + $discrValues = array_flip($this->class->discriminatorMap); + + foreach ($this->class->subClasses as $subclassName) { + $values[] = $this->conn->quote($discrValues[$subclassName]); + } + + $values = implode(', ', $values); + $discColumn = $this->class->discriminatorColumn['name']; + $tableAlias = $this->getSQLTableAlias($this->class->name); + + return $tableAlias . '.' . $discColumn . ' IN (' . $values . ')'; + } + + /** + * {@inheritdoc} + */ + protected function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + // Ensure that the filters are applied to the root entity of the inheritance tree + $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName); + // we don't care about the $targetTableAlias, in a STI there is only one table. + + return parent::generateFilterConditionSQL($targetEntity, $targetTableAlias); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php new file mode 100644 index 0000000..111455e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/PersisterException.php @@ -0,0 +1,20 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\ORM\Mapping\ClassMetadata; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\Value; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; + +/** + * Visit Expressions and generate SQL WHERE conditions from them. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class SqlExpressionVisitor extends ExpressionVisitor +{ + /** + * @var \Doctrine\ORM\Persisters\Entity\BasicEntityPersister + */ + private $persister; + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @param \Doctrine\ORM\Persisters\Entity\BasicEntityPersister $persister + * @param \Doctrine\ORM\Mapping\ClassMetadata $classMetadata + */ + public function __construct(BasicEntityPersister $persister, ClassMetadata $classMetadata) + { + $this->persister = $persister; + $this->classMetadata = $classMetadata; + } + + /** + * Converts a comparison expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + if (isset($this->classMetadata->associationMappings[$field]) && + $value !== null && + ! is_object($value) && + ! in_array($comparison->getOperator(), array(Comparison::IN, Comparison::NIN))) { + + throw PersisterException::matchingAssocationFieldRequiresObject($this->classMetadata->name, $field); + } + + return $this->persister->getSelectConditionStatementSQL($field, $value, null, $comparison->getOperator()); + } + + /** + * Converts a composite expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return '(' . implode(' AND ', $expressionList) . ')'; + + case CompositeExpression::TYPE_OR: + return '(' . implode(' OR ', $expressionList) . ')'; + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * Converts a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return '?'; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php new file mode 100644 index 0000000..0e680ad --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -0,0 +1,118 @@ +. + */ + +namespace Doctrine\ORM\Persisters; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\Value; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Extract the values from a criteria/expression + * + * @author Benjamin Eberlei + */ +class SqlValueVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private $values = array(); + + /** + * @var array + */ + private $types = array(); + + /** + * Converts a comparison expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * + * @return mixed + */ + public function walkComparison(Comparison $comparison) + { + $value = $this->getValueFromComparison($comparison); + $field = $comparison->getField(); + $operator = $comparison->getOperator(); + + if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) { + return; + } else if ($operator === Comparison::NEQ && $value === null) { + return; + } + + $this->values[] = $value; + $this->types[] = array($field, $value); + } + + /** + * Converts a composite expression into the target query language output. + * + * @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr + * + * @return mixed + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + foreach ($expr->getExpressionList() as $child) { + $this->dispatch($child); + } + } + + /** + * Converts a value expression into the target query language part. + * + * @param \Doctrine\Common\Collections\Expr\Value $value + * + * @return mixed + */ + public function walkValue(Value $value) + { + return; + } + + /** + * Returns the Parameters and Types necessary for matching the last visited expression. + * + * @return array + */ + public function getParamsAndTypes() + { + return array($this->values, $this->types); + } + + /** + * Returns the value from a Comparison. In case of a CONTAINS comparison, + * the value is wrapped in %-signs, because it will be used in a LIKE clause. + * + * @param \Doctrine\Common\Collections\Expr\Comparison $comparison + * @return mixed + */ + protected function getValueFromComparison(Comparison $comparison) + { + $value = $comparison->getValue()->getValue(); + + return $comparison->getOperator() == Comparison::CONTAINS + ? "%{$value}%" + : $value; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php new file mode 100644 index 0000000..d60f7a8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/PessimisticLockException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Pessimistic Lock Exception + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class PessimisticLockException extends ORMException +{ + /** + * @return PessimisticLockException + */ + public static function lockFailed() + { + return new self("The pessimistic lock failed."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php new file mode 100644 index 0000000..9b2e2cd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Autoloader.php @@ -0,0 +1,29 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Proxy\Autoloader as BaseAutoloader; + +/** + * @deprecated use \Doctrine\Common\Proxy\Autoloader instead + */ +class Autoloader extends BaseAutoloader +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php new file mode 100644 index 0000000..f478d49 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/Proxy.php @@ -0,0 +1,32 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Proxy\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + * + * @author Roman Borschel + * @since 2.0 + */ +interface Proxy extends BaseProxy +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php new file mode 100644 index 0000000..c6f1cbd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -0,0 +1,241 @@ +. + */ + +namespace Doctrine\ORM\Proxy; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Proxy\AbstractProxyFactory; +use Doctrine\Common\Proxy\Proxy as BaseProxy; +use Doctrine\Common\Proxy\ProxyDefinition; +use Doctrine\Common\Proxy\ProxyGenerator; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Persisters\Entity\EntityPersister; +use Doctrine\ORM\EntityNotFoundException; +use Doctrine\ORM\Utility\IdentifierFlattener; + +/** + * This factory is used to create proxy objects for entities at runtime. + * + * @author Roman Borschel + * @author Giorgio Sironi + * @author Marco Pivetta + * @since 2.0 + */ +class ProxyFactory extends AbstractProxyFactory +{ + /** + * @var EntityManagerInterface The EntityManager this factory is bound to. + */ + private $em; + + /** + * @var \Doctrine\ORM\UnitOfWork The UnitOfWork this factory uses to retrieve persisters + */ + private $uow; + + /** + * @var string + */ + private $proxyNs; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param EntityManagerInterface $em The EntityManager the new factory works for. + * @param string $proxyDir The directory to use for the proxy classes. It must exist. + * @param string $proxyNs The namespace to use for the proxy classes. + * @param boolean|int $autoGenerate The strategy for automatically generating proxy classes. Possible + * values are constants of Doctrine\Common\Proxy\AbstractProxyFactory. + */ + public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER) + { + $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs); + + $proxyGenerator->setPlaceholder('baseProxyInterface', 'Doctrine\ORM\Proxy\Proxy'); + parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate); + + $this->em = $em; + $this->uow = $em->getUnitOfWork(); + $this->proxyNs = $proxyNs; + $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory()); + } + + /** + * {@inheritDoc} + */ + protected function skipClass(ClassMetadata $metadata) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ + return $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract(); + } + + /** + * {@inheritDoc} + */ + protected function createProxyDefinition($className) + { + $classMetadata = $this->em->getClassMetadata($className); + $entityPersister = $this->uow->getEntityPersister($className); + + return new ProxyDefinition( + ClassUtils::generateProxyClassName($className, $this->proxyNs), + $classMetadata->getIdentifierFieldNames(), + $classMetadata->getReflectionProperties(), + $this->createInitializer($classMetadata, $entityPersister), + $this->createCloner($classMetadata, $entityPersister) + ); + } + + /** + * Creates a closure capable of initializing a proxy + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $entityPersister + * + * @return \Closure + * + * @throws \Doctrine\ORM\EntityNotFoundException + */ + private function createInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister) + { + if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if ( ! isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + $proxy->__wakeup(); + + $identifier = $classMetadata->getIdentifierValues($proxy); + + if (null === $entityPersister->loadById($identifier, $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw EntityNotFoundException::fromClassNameAndIdentifier( + $classMetadata->getName(), + $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) + ); + } + }; + } + + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + $initializer = $proxy->__getInitializer(); + $cloner = $proxy->__getCloner(); + + $proxy->__setInitializer(null); + $proxy->__setCloner(null); + + if ($proxy->__isInitialized()) { + return; + } + + $properties = $proxy->__getLazyProperties(); + + foreach ($properties as $propertyName => $property) { + if (!isset($proxy->$propertyName)) { + $proxy->$propertyName = $properties[$propertyName]; + } + } + + $proxy->__setInitialized(true); + + $identifier = $classMetadata->getIdentifierValues($proxy); + + if (null === $entityPersister->loadById($identifier, $proxy)) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw EntityNotFoundException::fromClassNameAndIdentifier( + $classMetadata->getName(), + $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) + ); + } + }; + } + + /** + * Creates a closure capable of finalizing state a cloned proxy + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata + * @param \Doctrine\ORM\Persisters\Entity\EntityPersister $entityPersister + * + * @return \Closure + * + * @throws \Doctrine\ORM\EntityNotFoundException + */ + private function createCloner(ClassMetadata $classMetadata, EntityPersister $entityPersister) + { + return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { + if ($proxy->__isInitialized()) { + return; + } + + $proxy->__setInitialized(true); + $proxy->__setInitializer(null); + + $class = $entityPersister->getClassMetadata(); + $identifier = $classMetadata->getIdentifierValues($proxy); + $original = $entityPersister->loadById($identifier); + + if (null === $original) { + throw EntityNotFoundException::fromClassNameAndIdentifier( + $classMetadata->getName(), + $this->identifierFlattener->flattenIdentifier($classMetadata, $identifier) + ); + } + + foreach ($class->getReflectionClass()->getProperties() as $property) { + if ( ! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) { + continue; + } + + $property->setAccessible(true); + $property->setValue($proxy, $property->getValue($original)); + } + }; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php new file mode 100644 index 0000000..1911200 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php @@ -0,0 +1,735 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\ParserResult; +use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ParameterTypeInferer; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * A Query object represents a DQL query. + * + * @since 1.0 + * @author Guilherme Blanco + * @author Konsta Vesterinen + * @author Roman Borschel + */ +final class Query extends AbstractQuery +{ + /** + * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts. + */ + const STATE_CLEAN = 1; + + /** + * A query object is in state DIRTY when it has DQL parts that have not yet been + * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart + * is called. + */ + const STATE_DIRTY = 2; + + /* Query HINTS */ + + /** + * The refresh hint turns any query into a refresh query with the result that + * any local changes in entities are overridden with the fetched values. + * + * @var string + */ + const HINT_REFRESH = 'doctrine.refresh'; + + /** + * @var string + */ + const HINT_CACHE_ENABLED = 'doctrine.cache.enabled'; + + /** + * @var string + */ + const HINT_CACHE_EVICT = 'doctrine.cache.evict'; + + /** + * Internal hint: is set to the proxy entity that is currently triggered for loading + * + * @var string + */ + const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity'; + + /** + * The forcePartialLoad query hint forces a particular query to return + * partial objects. + * + * @var string + * @todo Rename: HINT_OPTIMIZE + */ + const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad'; + + /** + * The includeMetaColumns query hint causes meta columns like foreign keys and + * discriminator columns to be selected and returned as part of the query result. + * + * This hint does only apply to non-object queries. + * + * @var string + */ + const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns'; + + /** + * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and + * are iterated and executed after the DQL has been parsed into an AST. + * + * @var string + */ + const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers'; + + /** + * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker + * and is used for generating the target SQL from any DQL AST tree. + * + * @var string + */ + const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker'; + + //const HINT_READ_ONLY = 'doctrine.readOnly'; + + /** + * @var string + */ + const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration'; + + /** + * @var string + */ + const HINT_LOCK_MODE = 'doctrine.lockMode'; + + /** + * The current state of this query. + * + * @var integer + */ + private $_state = self::STATE_CLEAN; + + /** + * A snapshot of the parameter types the query was parsed with. + * + * @var array + */ + private $_parsedTypes = array(); + + /** + * Cached DQL query. + * + * @var string + */ + private $_dql = null; + + /** + * The parser result that holds DQL => SQL information. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The first result to return (the "offset"). + * + * @var integer + */ + private $_firstResult = null; + + /** + * The maximum number of results to return (the "limit"). + * + * @var integer + */ + private $_maxResults = null; + + /** + * The cache driver used for caching queries. + * + * @var \Doctrine\Common\Cache\Cache|null + */ + private $_queryCache; + + /** + * Whether or not expire the query cache. + * + * @var boolean + */ + private $_expireQueryCache = false; + + /** + * The query cache lifetime. + * + * @var int + */ + private $_queryCacheTTL; + + /** + * Whether to use a query cache, if available. Defaults to TRUE. + * + * @var boolean + */ + private $_useQueryCache = true; + + /** + * Gets the SQL query/queries that correspond to this DQL query. + * + * @return mixed The built sql query or an array of all sql queries. + * + * @override + */ + public function getSQL() + { + return $this->_parse()->getSQLExecutor()->getSQLStatements(); + } + + /** + * Returns the corresponding AST for this DQL query. + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function getAST() + { + $parser = new Parser($this); + + return $parser->getAST(); + } + + /** + * {@inheritdoc} + */ + protected function getResultSetMapping() + { + // parse query or load from cache + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parse()->getResultSetMapping(); + } + + return $this->_resultSetMapping; + } + + /** + * Parses the DQL query, if necessary, and stores the parser result. + * + * Note: Populates $this->_parserResult as a side-effect. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + private function _parse() + { + $types = array(); + + foreach ($this->parameters as $parameter) { + /** @var Query\Parameter $parameter */ + $types[$parameter->getName()] = $parameter->getType(); + } + + // Return previous parser result if the query and the filter collection are both clean + if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) { + return $this->_parserResult; + } + + $this->_state = self::STATE_CLEAN; + $this->_parsedTypes = $types; + + // Check query cache. + if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) { + $parser = new Parser($this); + + $this->_parserResult = $parser->parse(); + + return $this->_parserResult; + } + + $hash = $this->_getQueryCacheId(); + $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash); + + if ($cached instanceof ParserResult) { + // Cache hit. + $this->_parserResult = $cached; + + return $this->_parserResult; + } + + // Cache miss. + $parser = new Parser($this); + + $this->_parserResult = $parser->parse(); + + $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL); + + return $this->_parserResult; + } + + /** + * {@inheritdoc} + */ + protected function _doExecute() + { + $executor = $this->_parse()->getSqlExecutor(); + + if ($this->_queryCacheProfile) { + $executor->setQueryCacheProfile($this->_queryCacheProfile); + } + + if ($this->_resultSetMapping === null) { + $this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); + } + + // Prepare parameters + $paramMappings = $this->_parserResult->getParameterMappings(); + $paramCount = count($this->parameters); + $mappingCount = count($paramMappings); + + if ($paramCount > $mappingCount) { + throw QueryException::tooManyParameters($mappingCount, $paramCount); + } elseif ($paramCount < $mappingCount) { + throw QueryException::tooFewParameters($mappingCount, $paramCount); + } + + // evict all cache for the entity region + if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) { + $this->evictEntityCacheRegion(); + } + + list($sqlParams, $types) = $this->processParameterMappings($paramMappings); + + return $executor->execute($this->_em->getConnection(), $sqlParams, $types); + } + + /** + * Evict entity cache region + */ + private function evictEntityCacheRegion() + { + $AST = $this->getAST(); + + if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) { + throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.'); + } + + $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement) + ? $AST->deleteClause->abstractSchemaName + : $AST->updateClause->abstractSchemaName; + + $this->_em->getCache()->evictEntityRegion($className); + } + + /** + * Processes query parameter mappings. + * + * @param array $paramMappings + * + * @return array + * + * @throws Query\QueryException + */ + private function processParameterMappings($paramMappings) + { + $sqlParams = array(); + $types = array(); + + foreach ($this->parameters as $parameter) { + $key = $parameter->getName(); + $value = $parameter->getValue(); + $rsm = $this->getResultSetMapping(); + + if ( ! isset($paramMappings[$key])) { + throw QueryException::unknownParameter($key); + } + + if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) { + $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]); + } + + $value = $this->processParameterValue($value); + $type = ($parameter->getValue() === $value) + ? $parameter->getType() + : ParameterTypeInferer::inferType($value); + + foreach ($paramMappings[$key] as $position) { + $types[$position] = $type; + } + + $sqlPositions = $paramMappings[$key]; + + // optimized multi value sql positions away for now, + // they are not allowed in DQL anyways. + $value = array($value); + $countValue = count($value); + + for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) { + $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)]; + } + } + + if (count($sqlParams) != count($types)) { + throw QueryException::parameterTypeMismatch(); + } + + if ($sqlParams) { + ksort($sqlParams); + $sqlParams = array_values($sqlParams); + + ksort($types); + $types = array_values($types); + } + + return array($sqlParams, $types); + } + + /** + * Defines a cache driver to be used for caching queries. + * + * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver. + * + * @return Query This query instance. + */ + public function setQueryCacheDriver($queryCache) + { + $this->_queryCache = $queryCache; + + return $this; + } + + /** + * Defines whether the query should make use of a query cache, if available. + * + * @param boolean $bool + * + * @return Query This query instance. + */ + public function useQueryCache($bool) + { + $this->_useQueryCache = $bool; + + return $this; + } + + /** + * Returns the cache driver used for query caching. + * + * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if + * this Query does not use query caching. + */ + public function getQueryCacheDriver() + { + if ($this->_queryCache) { + return $this->_queryCache; + } + + return $this->_em->getConfiguration()->getQueryCacheImpl(); + } + + /** + * Defines how long the query cache will be active before expire. + * + * @param integer $timeToLive How long the cache entry is valid. + * + * @return Query This query instance. + */ + public function setQueryCacheLifetime($timeToLive) + { + if ($timeToLive !== null) { + $timeToLive = (int) $timeToLive; + } + + $this->_queryCacheTTL = $timeToLive; + + return $this; + } + + /** + * Retrieves the lifetime of resultset cache. + * + * @return int + */ + public function getQueryCacheLifetime() + { + return $this->_queryCacheTTL; + } + + /** + * Defines if the query cache is active or not. + * + * @param boolean $expire Whether or not to force query cache expiration. + * + * @return Query This query instance. + */ + public function expireQueryCache($expire = true) + { + $this->_expireQueryCache = $expire; + + return $this; + } + + /** + * Retrieves if the query cache is active or not. + * + * @return bool + */ + public function getExpireQueryCache() + { + return $this->_expireQueryCache; + } + + /** + * @override + */ + public function free() + { + parent::free(); + + $this->_dql = null; + $this->_state = self::STATE_CLEAN; + } + + /** + * Sets a DQL query string. + * + * @param string $dqlQuery DQL Query. + * + * @return \Doctrine\ORM\AbstractQuery + */ + public function setDQL($dqlQuery) + { + if ($dqlQuery !== null) { + $this->_dql = $dqlQuery; + $this->_state = self::STATE_DIRTY; + } + + return $this; + } + + /** + * Returns the DQL query that is represented by this query object. + * + * @return string DQL query. + */ + public function getDQL() + { + return $this->_dql; + } + + /** + * Returns the state of this query object + * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL + * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. + * + * @see AbstractQuery::STATE_CLEAN + * @see AbstractQuery::STATE_DIRTY + * + * @return integer The query state. + */ + public function getState() + { + return $this->_state; + } + + /** + * Method to check if an arbitrary piece of DQL exists + * + * @param string $dql Arbitrary piece of DQL to check for. + * + * @return boolean + */ + public function contains($dql) + { + return stripos($this->getDQL(), $dql) === false ? false : true; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return Query This query object. + */ + public function setFirstResult($firstResult) + { + $this->_firstResult = $firstResult; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this query. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->_firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults + * + * @return Query This query object. + */ + public function setMaxResults($maxResults) + { + $this->_maxResults = $maxResults; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->_maxResults; + } + + /** + * Executes the query and returns an IterableResult that can be used to incrementally + * iterated over the result. + * + * @param ArrayCollection|array|null $parameters The query parameters. + * @param integer $hydrationMode The hydration mode to use. + * + * @return \Doctrine\ORM\Internal\Hydration\IterableResult + */ + public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT) + { + $this->setHint(self::HINT_INTERNAL_ITERATION, true); + + return parent::iterate($parameters, $hydrationMode); + } + + /** + * {@inheritdoc} + */ + public function setHint($name, $value) + { + $this->_state = self::STATE_DIRTY; + + return parent::setHint($name, $value); + } + + /** + * {@inheritdoc} + */ + public function setHydrationMode($hydrationMode) + { + $this->_state = self::STATE_DIRTY; + + return parent::setHydrationMode($hydrationMode); + } + + /** + * Set the lock mode for this Query. + * + * @see \Doctrine\DBAL\LockMode + * + * @param int $lockMode + * + * @return Query + * + * @throws TransactionRequiredException + */ + public function setLockMode($lockMode) + { + if (in_array($lockMode, array(LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE), true)) { + if ( ! $this->_em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + } + + $this->setHint(self::HINT_LOCK_MODE, $lockMode); + + return $this; + } + + /** + * Get the current lock mode for this query. + * + * @return int|null The current lock mode of this query or NULL if no specific lock mode is set. + */ + public function getLockMode() + { + $lockMode = $this->getHint(self::HINT_LOCK_MODE); + + if (false === $lockMode) { + return null; + } + + return $lockMode; + } + + /** + * Generate a cache id for the query cache - reusing the Result-Cache-Id generator. + * + * @return string + */ + protected function _getQueryCacheId() + { + ksort($this->_hints); + + $platform = $this->getEntityManager() + ->getConnection() + ->getDatabasePlatform() + ->getName(); + + return md5( + $this->getDql() . serialize($this->_hints) . + '&platform=' . $platform . + ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . + '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults . + '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT' + ); + } + + /** + * {@inheritdoc} + */ + protected function getHash() + { + return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults); + } + + /** + * Cleanup Query resource when clone is called. + * + * @return void + */ + public function __clone() + { + parent::__clone(); + + $this->_state = self::STATE_DIRTY; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php new file mode 100644 index 0000000..b8f931b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ASTException.php @@ -0,0 +1,38 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +use Doctrine\ORM\Query\QueryException; + +/** + * Base exception class for AST exceptions. + */ +class ASTException extends QueryException +{ + /** + * @param Node $node + * + * @return ASTException + */ + public static function noDispatchForNode($node) + { + return new self("Double-dispatch for node " . get_class($node) . " is not supported."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php new file mode 100644 index 0000000..0966d90 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/AggregateExpression.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of AggregateExpression. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AggregateExpression extends Node +{ + /** + * @var string + */ + public $functionName; + + /** + * @var PathExpression|SimpleArithmeticExpression + */ + public $pathExpression; + + /** + * Some aggregate expressions support distinct, eg COUNT. + * + * @var bool + */ + public $isDistinct = false; + + /** + * @param string $functionName + * @param PathExpression|SimpleArithmeticExpression $pathExpression + * @param bool $isDistinct + */ + public function __construct($functionName, $pathExpression, $isDistinct) + { + $this->functionName = $functionName; + $this->pathExpression = $pathExpression; + $this->isDistinct = $isDistinct; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkAggregateExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php new file mode 100644 index 0000000..b586cba --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticExpression extends Node +{ + /** + * @var SimpleArithmeticExpression|null + */ + public $simpleArithmeticExpression; + + /** + * @var Subselect|null + */ + public $subselect; + + /** + * @return bool + */ + public function isSimpleArithmeticExpression() + { + return (bool) $this->simpleArithmeticExpression; + } + + /** + * @return bool + */ + public function isSubselect() + { + return (bool) $this->subselect; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkArithmeticExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php new file mode 100644 index 0000000..3120466 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticFactor.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticFactor extends Node +{ + /** + * @var mixed + */ + public $arithmeticPrimary; + + /** + * NULL represents no sign, TRUE means positive and FALSE means negative sign. + * + * @var null|boolean + */ + public $sign; + + /** + * @param mixed $arithmeticPrimary + * @param null|bool $sign + */ + public function __construct($arithmeticPrimary, $sign = null) + { + $this->arithmeticPrimary = $arithmeticPrimary; + $this->sign = $sign; + } + + /** + * @return bool + */ + public function isPositiveSigned() + { + return $this->sign === true; + } + + /** + * @return bool + */ + public function isNegativeSigned() + { + return $this->sign === false; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticFactor($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php new file mode 100644 index 0000000..e08ae7f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ArithmeticTerm.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArithmeticTerm extends Node +{ + /** + * @var array + */ + public $arithmeticFactors; + + /** + * @param array $arithmeticFactors + */ + public function __construct(array $arithmeticFactors) + { + $this->arithmeticFactors = $arithmeticFactors; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkArithmeticTerm($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php new file mode 100644 index 0000000..1e31fd1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/BetweenExpression.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of BetweenExpression. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class BetweenExpression extends Node +{ + /** + * @var ArithmeticExpression + */ + public $expression; + + /** + * @var ArithmeticExpression + */ + public $leftBetweenExpression; + + /** + * @var ArithmeticExpression + */ + public $rightBetweenExpression; + + /** + * @var bool + */ + public $not; + + /** + * @param ArithmeticExpression $expr + * @param ArithmeticExpression $leftExpr + * @param ArithmeticExpression $rightExpr + */ + public function __construct($expr, $leftExpr, $rightExpr) + { + $this->expression = $expr; + $this->leftBetweenExpression = $leftExpr; + $this->rightBetweenExpression = $rightExpr; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkBetweenExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php new file mode 100644 index 0000000..194f9ac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CoalesceExpression.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @since 2.1 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CoalesceExpression extends Node +{ + /** + * @var array + */ + public $scalarExpressions = array(); + + /** + * @param array $scalarExpressions + */ + public function __construct(array $scalarExpressions) + { + $this->scalarExpressions = $scalarExpressions; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkCoalesceExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php new file mode 100644 index 0000000..70989a2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/CollectionMemberExpression.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CollectionMemberExpression extends Node +{ + public $entityExpression; + + /** + * @var PathExpression + */ + public $collectionValuedPathExpression; + + /** + * @var bool + */ + public $not; + + /** + * @param mixed $entityExpr + * @param PathExpression $collValuedPathExpr + */ + public function __construct($entityExpr, $collValuedPathExpr) + { + $this->entityExpression = $entityExpr; + $this->collectionValuedPathExpression = $collValuedPathExpr; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkCollectionMemberExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php new file mode 100644 index 0000000..0558392 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ComparisonExpression.php @@ -0,0 +1,72 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) | + * StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) | + * BooleanExpression ("=" | "<>" | "!=") (BooleanExpression | QuantifiedExpression) | + * EnumExpression ("=" | "<>" | "!=") (EnumExpression | QuantifiedExpression) | + * DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) | + * EntityExpression ("=" | "<>") (EntityExpression | QuantifiedExpression) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ComparisonExpression extends Node +{ + /** + * @var Node + */ + public $leftExpression; + + /** + * @var Node + */ + public $rightExpression; + + /** + * @var string + */ + public $operator; + + /** + * @param Node $leftExpr + * @param string $operator + * @param Node $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpression = $leftExpr; + $this->rightExpression = $rightExpr; + $this->operator = $operator; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkComparisonExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php new file mode 100644 index 0000000..62c7b2b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalExpression extends Node +{ + /** + * @var array + */ + public $conditionalTerms = array(); + + /** + * @param array $conditionalTerms + */ + public function __construct(array $conditionalTerms) + { + $this->conditionalTerms = $conditionalTerms; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php new file mode 100644 index 0000000..7c89faa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalFactor.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalFactor extends Node +{ + /** + * @var bool + */ + public $not = false; + + /** + * @var ConditionalPrimary + */ + public $conditionalPrimary; + + /** + * @param ConditionalPrimary $conditionalPrimary + */ + public function __construct($conditionalPrimary) + { + $this->conditionalPrimary = $conditionalPrimary; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalFactor($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php new file mode 100644 index 0000000..1eed41d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalPrimary.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalPrimary extends Node +{ + /** + * @var Node|null + */ + public $simpleConditionalExpression; + + /** + * @var ConditionalExpression|null + */ + public $conditionalExpression; + + /** + * @return bool + */ + public function isSimpleConditionalExpression() + { + return (bool) $this->simpleConditionalExpression; + } + + /** + * @return bool + */ + public function isConditionalExpression() + { + return (bool) $this->conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalPrimary($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php new file mode 100644 index 0000000..bb92db1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ConditionalTerm.php @@ -0,0 +1,52 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConditionalTerm extends Node +{ + /** + * @var array + */ + public $conditionalFactors = array(); + + /** + * @param array $conditionalFactors + */ + public function __construct(array $conditionalFactors) + { + $this->conditionalFactors = $conditionalFactors; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkConditionalTerm($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php new file mode 100644 index 0000000..8ca35c6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteClause.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteClause extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @param string $abstractSchemaName + */ + public function __construct($abstractSchemaName) + { + $this->abstractSchemaName = $abstractSchemaName; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php new file mode 100644 index 0000000..da6859b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/DeleteStatement.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * DeleteStatement = DeleteClause [WhereClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DeleteStatement extends Node +{ + /** + * @var DeleteClause + */ + public $deleteClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @param DeleteClause $deleteClause + */ + public function __construct($deleteClause) + { + $this->deleteClause = $deleteClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkDeleteStatement($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php new file mode 100644 index 0000000..bd978af --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/EmptyCollectionComparisonExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EmptyCollectionComparisonExpression extends Node +{ + /** + * @var PathExpression + */ + public $expression; + + /** + * @var bool + */ + public $not; + + /** + * @param PathExpression $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkEmptyCollectionComparisonExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php new file mode 100644 index 0000000..c53a107 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ExistsExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ExistsExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Subselect + */ + public $subselect; + + /** + * @param Subselect $subselect + */ + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkExistsExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php new file mode 100644 index 0000000..b1d8dfe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/FromClause.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration} + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class FromClause extends Node +{ + /** + * @var array + */ + public $identificationVariableDeclarations = array(); + + /** + * @param array $identificationVariableDeclarations + */ + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFromClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php new file mode 100644 index 0000000..8fade4c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/AbsFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "ABS" "(" SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class AbsFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression( + $this->simpleArithmeticExpression + ) . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php new file mode 100644 index 0000000..0e5edd5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitAndFunction.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabio B. Silva + */ +class BitAndFunction extends FunctionNode +{ + public $firstArithmetic; + public $secondArithmetic; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getBitAndComparisonExpression( + $this->firstArithmetic->dispatch($sqlWalker), + $this->secondArithmetic->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstArithmetic = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondArithmetic = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php new file mode 100644 index 0000000..1d9c2df --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/BitOrFunction.php @@ -0,0 +1,63 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Fabio B. Silva + */ +class BitOrFunction extends FunctionNode +{ + public $firstArithmetic; + public $secondArithmetic; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getBitOrComparisonExpression( + $this->firstArithmetic->dispatch($sqlWalker), + $this->secondArithmetic->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstArithmetic = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->secondArithmetic = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php new file mode 100644 index 0000000..421fe9a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php @@ -0,0 +1,83 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary }* ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ConcatFunction extends FunctionNode +{ + public $firstStringPrimary; + + public $secondStringPrimary; + + public $concatExpressions = array(); + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + + $args = array(); + + foreach ($this->concatExpressions as $expression) { + $args[] = $sqlWalker->walkStringPrimary($expression); + } + + return call_user_func_array(array($platform,'getConcatExpression'), $args); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + $this->concatExpressions[] = $this->firstStringPrimary; + + $parser->match(Lexer::T_COMMA); + + $this->secondStringPrimary = $parser->StringPrimary(); + $this->concatExpressions[] = $this->secondStringPrimary; + + while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + $this->concatExpressions[] = $parser->StringPrimary(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php new file mode 100644 index 0000000..8e8ce1c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentDateFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_DATE" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentDateFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentDateSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php new file mode 100644 index 0000000..4bab247 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimeFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_TIME" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentTimeFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimeSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php new file mode 100644 index 0000000..8a9b418 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/CurrentTimestampFunction.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "CURRENT_TIMESTAMP" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class CurrentTimestampFunction extends FunctionNode +{ + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimestampSQL(); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php new file mode 100644 index 0000000..312a52c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateAddFunction.php @@ -0,0 +1,94 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class DateAddFunction extends FunctionNode +{ + public $firstDateExpression = null; + public $intervalExpression = null; + public $unit = null; + + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + case 'second': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddSecondsExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'hour': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + case 'day': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'month': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + default: + throw QueryException::semanticalError( + 'DATE_ADD() only supports units of type second, hour, day and month.' + ); + } + } + + /** + * @override + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstDateExpression = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->intervalExpression = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->unit = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php new file mode 100644 index 0000000..a33c2d0 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateDiffFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\Parser; + +/** + * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + */ +class DateDiffFunction extends FunctionNode +{ + public $date1; + public $date2; + + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression( + $this->date1->dispatch($sqlWalker), + $this->date2->dispatch($sqlWalker) + ); + } + + /** + * @override + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->date1 = $parser->ArithmeticPrimary(); + $parser->match(Lexer::T_COMMA); + $this->date2 = $parser->ArithmeticPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php new file mode 100644 index 0000000..d0e890f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/DateSubFunction.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\QueryException; + +/** + * "DATE_ADD(date1, interval, unit)" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class DateSubFunction extends DateAddFunction +{ + /** + * @override + */ + public function getSql(SqlWalker $sqlWalker) + { + switch (strtolower($this->unit->value)) { + case 'hour': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + case 'day': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + case 'month': + return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression( + $this->firstDateExpression->dispatch($sqlWalker), + $this->intervalExpression->dispatch($sqlWalker) + ); + + default: + throw QueryException::semanticalError( + 'DATE_SUB() only supports units of type hour, day and month.' + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php new file mode 100644 index 0000000..2f33c9d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -0,0 +1,73 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\AST\Node; + +/** + * Abstract Function Node. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +abstract class FunctionNode extends Node +{ + /** + * @var string + */ + public $name; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + abstract public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker); + + /** + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + * + * @return string + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkFunction($this); + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + abstract public function parse(\Doctrine\ORM\Query\Parser $parser); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php new file mode 100644 index 0000000..242a38b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -0,0 +1,113 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\ORM\Query\QueryException; + +/** + * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + * + * + * @link www.doctrine-project.org + * @since 2.2 + * @author Guilherme Blanco + * @author Benjamin Eberlei + */ +class IdentityFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\PathExpression + */ + public $pathExpression; + + /** + * @var string + */ + public $fieldMapping; + + /** + * {@inheritdoc} + */ + public function getSql(SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform(); + $quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy(); + $dqlAlias = $this->pathExpression->identificationVariable; + $assocField = $this->pathExpression->field; + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $targetEntity = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + $joinColumn = reset($assoc['joinColumns']); + + if ($this->fieldMapping !== null) { + if ( ! isset($targetEntity->fieldMappings[$this->fieldMapping])) { + throw new QueryException(sprintf('Undefined reference field mapping "%s"', $this->fieldMapping)); + } + + $field = $targetEntity->fieldMappings[$this->fieldMapping]; + $joinColumn = null; + + foreach ($assoc['joinColumns'] as $mapping) { + + if($mapping['referencedColumnName'] === $field['columnName']) { + $joinColumn = $mapping; + + break; + } + } + + if ($joinColumn === null) { + throw new QueryException(sprintf('Unable to resolve the reference field mapping "%s"', $this->fieldMapping)); + } + } + + // The table with the relation may be a subclass, so get the table name from the association definition + $tableName = $sqlWalker->getEntityManager()->getClassMetadata($assoc['sourceEntity'])->getTableName(); + + $tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias); + $columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform); + + return $tableAlias . '.' . $columnName; + } + + /** + * {@inheritdoc} + */ + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->pathExpression = $parser->SingleValuedAssociationPathExpression(); + + if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + $parser->match(Lexer::T_STRING); + + $this->fieldMapping = $parser->getLexer()->token['value']; + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php new file mode 100644 index 0000000..deb3066 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LengthFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LENGTH" "(" StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LengthFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php new file mode 100644 index 0000000..cba45bc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LocateFunction.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LocateFunction extends FunctionNode +{ + public $firstStringPrimary; + public $secondStringPrimary; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression|bool + */ + public $simpleArithmeticExpression = false; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + + return $sqlWalker->getConnection()->getDatabasePlatform()->getLocateExpression( + $sqlWalker->walkStringPrimary($this->secondStringPrimary), // its the other way around in platform + $sqlWalker->walkStringPrimary($this->firstStringPrimary), + (($this->simpleArithmeticExpression) + ? $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + : false + ) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstStringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondStringPrimary = $parser->StringPrimary(); + + $lexer = $parser->getLexer(); + if ($lexer->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php new file mode 100644 index 0000000..ffc86b6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/LowerFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "LOWER" "(" StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class LowerFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getLowerExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php new file mode 100644 index 0000000..cf4cf0c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -0,0 +1,74 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class ModFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $firstSimpleArithmeticExpression; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $secondSimpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_COMMA); + + $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php new file mode 100644 index 0000000..7970508 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -0,0 +1,123 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SIZE" "(" CollectionValuedPathExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SizeFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\PathExpression + */ + public $collectionPathExpression; + + /** + * @override + * @todo If the collection being counted is already joined, the SQL can be simpler (more efficient). + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform(); + $quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy(); + $dqlAlias = $this->collectionPathExpression->identificationVariable; + $assocField = $this->collectionPathExpression->field; + + $qComp = $sqlWalker->getQueryComponent($dqlAlias); + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $sql = 'SELECT COUNT(*) FROM '; + + if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) { + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $sql .= $quoteStrategy->getTableName($targetClass, $platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + + $first = true; + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sql .= $targetTableAlias . '.' . $sourceColumn + . ' = ' + . $sourceTableAlias . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $platform); + } + } else { // many-to-many + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); + + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + + // SQL table aliases + $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']); + $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // join to target table + $sql .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] + ? $joinTable['joinColumns'] + : $joinTable['inverseJoinColumns']; + + $first = true; + + foreach ($joinColumns as $joinColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sourceColumnName = $quoteStrategy->getColumnName( + $class->fieldNames[$joinColumn['referencedColumnName']], $class, $platform + ); + + $sql .= $joinTableAlias . '.' . $joinColumn['name'] + . ' = ' + . $sourceTableAlias . '.' . $sourceColumnName; + } + } + + return '(' . $sql . ')'; + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->collectionPathExpression = $parser->CollectionValuedPathExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php new file mode 100644 index 0000000..c9f1038 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SqrtFunction.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SQRT" "(" SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SqrtFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $simpleArithmeticExpression; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getSqrtExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php new file mode 100644 index 0000000..ee80c06 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/SubstringFunction.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class SubstringFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public $firstSimpleArithmeticExpression; + + /** + * @var \Doctrine\ORM\Query\AST\SimpleArithmeticExpression|null + */ + public $secondSimpleArithmeticExpression = null; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + $optionalSecondSimpleArithmeticExpression = null; + if ($this->secondSimpleArithmeticExpression !== null) { + $optionalSecondSimpleArithmeticExpression = $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression); + } + + return $sqlWalker->getConnection()->getDatabasePlatform()->getSubstringExpression( + $sqlWalker->walkStringPrimary($this->stringPrimary), + $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression), + $optionalSecondSimpleArithmeticExpression + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + + $lexer = $parser->getLexer(); + if ($lexer->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php new file mode 100644 index 0000000..0c1f5d8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/TrimFunction.php @@ -0,0 +1,163 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\Query\SqlWalker; +use Doctrine\DBAL\Platforms\AbstractPlatform; + +/** + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class TrimFunction extends FunctionNode +{ + /** + * @var boolean + */ + public $leading; + + /** + * @var boolean + */ + public $trailing; + + /** + * @var boolean + */ + public $both; + + /** + * @var boolean + */ + public $trimChar = false; + + /** + * @var \Doctrine\ORM\Query\AST\Node + */ + public $stringPrimary; + + /** + * {@inheritdoc} + */ + public function getSql(SqlWalker $sqlWalker) + { + $stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary); + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + $trimMode = $this->getTrimMode(); + $trimChar = ($this->trimChar !== false) + ? $sqlWalker->getConnection()->quote($this->trimChar) + : false; + + return $platform->getTrimExpression($stringPrimary, $trimMode, $trimChar); + } + + /** + * {@inheritdoc} + */ + public function parse(Parser $parser) + { + $lexer = $parser->getLexer(); + + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->parseTrimMode($parser); + + if ($lexer->isNextToken(Lexer::T_STRING)) { + $parser->match(Lexer::T_STRING); + + $this->trimChar = $lexer->token['value']; + } + + if ($this->leading || $this->trailing || $this->both || $this->trimChar) { + $parser->match(Lexer::T_FROM); + } + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return integer + */ + private function getTrimMode() + { + if ($this->leading) { + return AbstractPlatform::TRIM_LEADING; + } + + if ($this->trailing) { + return AbstractPlatform::TRIM_TRAILING; + } + + if ($this->both) { + return AbstractPlatform::TRIM_BOTH; + } + + return AbstractPlatform::TRIM_UNSPECIFIED; + } + + /** + * @param \Doctrine\ORM\Query\Parser $parser + * + * @return void + */ + private function parseTrimMode(Parser $parser) + { + $lexer = $parser->getLexer(); + $value = $lexer->lookahead['value']; + + if (strcasecmp('leading', $value) === 0) { + $parser->match(Lexer::T_LEADING); + + $this->leading = true; + + return; + } + + if (strcasecmp('trailing', $value) === 0) { + $parser->match(Lexer::T_TRAILING); + + $this->trailing = true; + + return; + } + + if (strcasecmp('both', $value) === 0) { + $parser->match(Lexer::T_BOTH); + + $this->both = true; + + return; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php new file mode 100644 index 0000000..888009a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Functions/UpperFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST\Functions; + +use Doctrine\ORM\Query\Lexer; + +/** + * "UPPER" "(" StringPrimary ")" + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class UpperFunction extends FunctionNode +{ + public $stringPrimary; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return $sqlWalker->getConnection()->getDatabasePlatform()->getUpperExpression( + $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary) + ); + } + + /** + * @override + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->stringPrimary = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php new file mode 100644 index 0000000..a74eed5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GeneralCaseExpression.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GeneralCaseExpression extends Node +{ + /** + * @var array + */ + public $whenClauses = array(); + + /** + * @var mixed + */ + public $elseScalarExpression = null; + + /** + * @param array $whenClauses + * @param mixed $elseScalarExpression + */ + public function __construct(array $whenClauses, $elseScalarExpression) + { + $this->whenClauses = $whenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGeneralCaseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php new file mode 100644 index 0000000..c05fa58 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/GroupByClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of GroupByClause. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupByClause extends Node +{ + /** + * @var array + */ + public $groupByItems = array(); + + /** + * @param array $groupByItems + */ + public function __construct(array $groupByItems) + { + $this->groupByItems = $groupByItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkGroupByClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php new file mode 100644 index 0000000..1d369ff --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/HavingClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of HavingClause. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class HavingClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $conditionalExpression; + + /** + * @param ConditionalExpression $conditionalExpression + */ + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkHavingClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php new file mode 100644 index 0000000..a8f7f6d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IdentificationVariableDeclaration extends Node +{ + /** + * @var RangeVariableDeclaration|null + */ + public $rangeVariableDeclaration = null; + + /** + * @var IndexBy|null + */ + public $indexBy = null; + + /** + * @var array + */ + public $joins = array(); + + /** + * @param RangeVariableDeclaration|null $rangeVariableDecl + * @param IndexBy|null $indexBy + * @param array $joins + */ + public function __construct($rangeVariableDecl, $indexBy, array $joins) + { + $this->rangeVariableDeclaration = $rangeVariableDecl; + $this->indexBy = $indexBy; + $this->joins = $joins; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIdentificationVariableDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php new file mode 100644 index 0000000..9d0a8b5 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InExpression.php @@ -0,0 +1,67 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var ArithmeticExpression + */ + public $expression; + + /** + * @var array + */ + public $literals = array(); + + /** + * @var Subselect|null + */ + public $subselect; + + /** + * @param ArithmeticExpression $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkInExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php new file mode 100644 index 0000000..c7874b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/IndexBy.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * IndexBy ::= "INDEX" "BY" SimpleStateFieldPathExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class IndexBy extends Node +{ + /** + * @var PathExpression + */ + public $simpleStateFieldPathExpression = null; + + /** + * @param PathExpression $simpleStateFieldPathExpression + */ + public function __construct($simpleStateFieldPathExpression) + { + $this->simpleStateFieldPathExpression = $simpleStateFieldPathExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkIndexBy($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php new file mode 100644 index 0000000..cf50140 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InputParameter.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Description of InputParameter. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InputParameter extends Node +{ + /** + * @var bool + */ + public $isNamed; + + /** + * @var string + */ + public $name; + + /** + * @param string $value + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function __construct($value) + { + if (strlen($value) == 1) { + throw \Doctrine\ORM\Query\QueryException::invalidParameterFormat($value); + } + + $param = substr($value, 1); + $this->isNamed = ! is_numeric($param); + $this->name = $param; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkInputParameter($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php new file mode 100644 index 0000000..c1fd65b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/InstanceOfExpression.php @@ -0,0 +1,64 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class InstanceOfExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $value; + + /** + * @param string $identVariable + */ + public function __construct($identVariable) + { + $this->identificationVariable = $identVariable; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkInstanceOfExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php new file mode 100644 index 0000000..5c203aa --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Join.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression + * ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Join extends Node +{ + const JOIN_TYPE_LEFT = 1; + const JOIN_TYPE_LEFTOUTER = 2; + const JOIN_TYPE_INNER = 3; + + /** + * @var int + */ + public $joinType = self::JOIN_TYPE_INNER; + + /** + * @var Node|null + */ + public $joinAssociationDeclaration = null; + + /** + * @var ConditionalExpression|null + */ + public $conditionalExpression = null; + + /** + * @param int $joinType + * @param Node $joinAssociationDeclaration + */ + public function __construct($joinType, $joinAssociationDeclaration) + { + $this->joinType = $joinType; + $this->joinAssociationDeclaration = $joinAssociationDeclaration; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoin($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php new file mode 100644 index 0000000..a33900a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class JoinAssociationDeclaration extends Node +{ + /** + * @var JoinAssociationPathExpression + */ + public $joinAssociationPathExpression; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var IndexBy|null + */ + public $indexBy; + + /** + * @param JoinAssociationPathExpression $joinAssociationPathExpression + * @param string $aliasIdentificationVariable + * @param IndexBy|null $indexBy + */ + public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy) + { + $this->joinAssociationPathExpression = $joinAssociationPathExpression; + $this->aliasIdentificationVariable = $aliasIdentificationVariable; + $this->indexBy = $indexBy; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkJoinAssociationDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php new file mode 100644 index 0000000..946bbb1 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinAssociationPathExpression ::= IdentificationVariable "." (SingleValuedAssociationField | CollectionValuedAssociationField) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class JoinAssociationPathExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var string + */ + public $associationField; + + /** + * @param string $identificationVariable + * @param string $associationField + */ + public function __construct($identificationVariable, $associationField) + { + $this->identificationVariable = $identificationVariable; + $this->associationField = $associationField; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php new file mode 100644 index 0000000..7e37414 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.3 + * @author Alexander + */ +class JoinClassPathExpression extends Node +{ + /** + * @var mixed + */ + public $abstractSchemaName; + + /** + * @var mixed + */ + public $aliasIdentificationVariable; + + /** + * @param mixed $abstractSchemaName + * @param mixed $aliasIdentificationVar + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkJoinPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php new file mode 100644 index 0000000..89aa83a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * JoinVariableDeclaration ::= Join [IndexBy] + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.org + * @since 2.5 + * @author Guilherme Blanco + */ +class JoinVariableDeclaration extends Node +{ + /** + * @var Join + */ + public $join; + + /** + * @var IndexBy|null + */ + public $indexBy; + + /** + * Constructor. + * + * @param Join $join + * @param IndexBy|null $indexBy + */ + public function __construct($join, $indexBy) + { + $this->join = $join; + $this->indexBy = $indexBy; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkJoinVariableDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php new file mode 100644 index 0000000..e320c51 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/LikeExpression.php @@ -0,0 +1,71 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class LikeExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Node + */ + public $stringExpression; + + /** + * @var InputParameter + */ + public $stringPattern; + + /** + * @var Literal|null + */ + public $escapeChar; + + /** + * @param Node $stringExpression + * @param InputParameter $stringPattern + * @param Literal|null $escapeChar + */ + public function __construct($stringExpression, $stringPattern, $escapeChar = null) + { + $this->stringExpression = $stringExpression; + $this->stringPattern = $stringPattern; + $this->escapeChar = $escapeChar; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkLikeExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php new file mode 100644 index 0000000..43d71ad --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Literal.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class Literal extends Node +{ + const STRING = 1; + const BOOLEAN = 2; + const NUMERIC = 3; + + /** + * @var int + */ + public $type; + + /** + * @var mixed + */ + public $value; + + /** + * @param int $type + * @param mixed $value + */ + public function __construct($type, $value) + { + $this->type = $type; + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkLiteral($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php new file mode 100644 index 0000000..65b94ac --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NewObjectExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Fabio B. Silva + */ +class NewObjectExpression extends Node +{ + /** + * @var string + */ + public $className; + + /** + * @var array + */ + public $args; + + /** + * @param string $className + * @param array $args + */ + public function __construct($className, array $args) + { + $this->className = $className; + $this->args = $args; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNewObject($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php new file mode 100644 index 0000000..a257dc2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Node.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Abstract class of an AST node. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Node +{ + /** + * Double-dispatch method, supposed to dispatch back to the walker. + * + * Implementation is not mandatory for all nodes. + * + * @param \Doctrine\ORM\Query\SqlWalker $walker + * + * @return string + * + * @throws ASTException + */ + public function dispatch($walker) + { + throw ASTException::noDispatchForNode($this); + } + + /** + * Dumps the AST Node into a string representation for information purpose only. + * + * @return string + */ + public function __toString() + { + return $this->dump($this); + } + + /** + * @param object $obj + * + * @return string + */ + public function dump($obj) + { + static $ident = 0; + + $str = ''; + + if ($obj instanceof Node) { + $str .= get_class($obj) . '(' . PHP_EOL; + $props = get_object_vars($obj); + + foreach ($props as $name => $prop) { + $ident += 4; + $str .= str_repeat(' ', $ident) . '"' . $name . '": ' + . $this->dump($prop) . ',' . PHP_EOL; + $ident -= 4; + } + + $str .= str_repeat(' ', $ident) . ')'; + } else if (is_array($obj)) { + $ident += 4; + $str .= 'array('; + $some = false; + + foreach ($obj as $k => $v) { + $str .= PHP_EOL . str_repeat(' ', $ident) . '"' + . $k . '" => ' . $this->dump($v) . ','; + $some = true; + } + + $ident -= 4; + $str .= ($some ? PHP_EOL . str_repeat(' ', $ident) : '') . ')'; + } else if (is_object($obj)) { + $str .= 'instanceof(' . get_class($obj) . ')'; + } else { + $str .= var_export($obj, true); + } + + return $str; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php new file mode 100644 index 0000000..84a1997 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullComparisonExpression extends Node +{ + /** + * @var bool + */ + public $not; + + /** + * @var Node + */ + public $expression; + + /** + * @param Node $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullComparisonExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php new file mode 100644 index 0000000..e33bc72 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/NullIfExpression.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @since 2.1 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class NullIfExpression extends Node +{ + /** + * @var mixed + */ + public $firstExpression; + + /** + * @var mixed + */ + public $secondExpression; + + /** + * @param mixed $firstExpression + * @param mixed $secondExpression + */ + public function __construct($firstExpression, $secondExpression) + { + $this->firstExpression = $firstExpression; + $this->secondExpression = $secondExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkNullIfExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php new file mode 100644 index 0000000..75d16c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByClause extends Node +{ + /** + * @var array + */ + public $orderByItems = array(); + + /** + * @param array $orderByItems + */ + public function __construct(array $orderByItems) + { + $this->orderByItems = $orderByItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php new file mode 100644 index 0000000..bf3288a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/OrderByItem.php @@ -0,0 +1,75 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * OrderByItem ::= (ResultVariable | StateFieldPathExpression) ["ASC" | "DESC"] + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderByItem extends Node +{ + /** + * @var mixed + */ + public $expression; + + /** + * @var string + */ + public $type; + + /** + * @param mixed $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * @return bool + */ + public function isAsc() + { + return strtoupper($this->type) == 'ASC'; + } + + /** + * @return bool + */ + public function isDesc() + { + return strtoupper($this->type) == 'DESC'; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkOrderByItem($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php new file mode 100644 index 0000000..f16db0e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/ParenthesisExpression.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * ParenthesisExpression ::= "(" ArithmeticPrimary ")" + * + * @author Fabio B. Silva + * @since 2.4 + */ +class ParenthesisExpression extends Node +{ + /** + * @var \Doctrine\ORM\Query\AST\Node + */ + public $expression; + + /** + * @param \Doctrine\ORM\Query\AST\Node $expression + */ + public function __construct(Node $expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkParenthesisExpression($this); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php new file mode 100644 index 0000000..e4ffe79 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PartialObjectExpression.php @@ -0,0 +1,43 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +class PartialObjectExpression extends Node +{ + /** + * @var string + */ + public $identificationVariable; + + /** + * @var array + */ + public $partialFieldSet; + + /** + * @param string $identificationVariable + * @param array $partialFieldSet + */ + public function __construct($identificationVariable, array $partialFieldSet) + { + $this->identificationVariable = $identificationVariable; + $this->partialFieldSet = $partialFieldSet; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php new file mode 100644 index 0000000..37674b6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/PathExpression.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * StateField ::= {EmbeddedClassStateField "."}* SimpleStateField + * SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class PathExpression extends Node +{ + const TYPE_COLLECTION_VALUED_ASSOCIATION = 2; + const TYPE_SINGLE_VALUED_ASSOCIATION = 4; + const TYPE_STATE_FIELD = 8; + + /** + * @var int + */ + public $type; + + /** + * @var int + */ + public $expectedType; + + /** + * @var string + */ + public $identificationVariable; + + /** + * @var string|null + */ + public $field; + + /** + * @param int $expectedType + * @param string $identificationVariable + * @param string|null $field + */ + public function __construct($expectedType, $identificationVariable, $field = null) + { + $this->expectedType = $expectedType; + $this->identificationVariable = $identificationVariable; + $this->field = $field; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkPathExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php new file mode 100644 index 0000000..15be952 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php @@ -0,0 +1,81 @@ +. + */ +namespace Doctrine\ORM\Query\AST; + +/** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QuantifiedExpression extends Node +{ + /** + * @var string + */ + public $type; + + /** + * @var Subselect + */ + public $subselect; + + /** + * @param Subselect $subselect + */ + public function __construct($subselect) + { + $this->subselect = $subselect; + } + + /** + * @return bool + */ + public function isAll() + { + return strtoupper($this->type) == 'ALL'; + } + + /** + * @return bool + */ + public function isAny() + { + return strtoupper($this->type) == 'ANY'; + } + + /** + * @return bool + */ + public function isSome() + { + return strtoupper($this->type) == 'SOME'; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkQuantifiedExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php new file mode 100644 index 0000000..0ca5274 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/RangeVariableDeclaration.php @@ -0,0 +1,67 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RangeVariableDeclaration extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var boolean + */ + public $isRoot; + + /** + * @param string $abstractSchemaName + * @param string $aliasIdentificationVar + * @param boolean $isRoot + */ + public function __construct($abstractSchemaName, $aliasIdentificationVar, $isRoot = true) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->aliasIdentificationVariable = $aliasIdentificationVar; + $this->isRoot = $isRoot; + } + + /** + * {@inheritdoc} + */ + public function dispatch($walker) + { + return $walker->walkRangeVariableDeclaration($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php new file mode 100644 index 0000000..1df1436 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectClause.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectClause = "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectClause extends Node +{ + /** + * @var bool + */ + public $isDistinct; + + /** + * @var array + */ + public $selectExpressions = array(); + + /** + * @param array $selectExpressions + * @param bool $isDistinct + */ + public function __construct(array $selectExpressions, $isDistinct) + { + $this->isDistinct = $isDistinct; + $this->selectExpressions = $selectExpressions; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php new file mode 100644 index 0000000..4187013 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectExpression.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | + * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectExpression extends Node +{ + /** + * @var mixed + */ + public $expression; + + /** + * @var string|null + */ + public $fieldIdentificationVariable; + + /** + * @var bool + */ + public $hiddenAliasResultVariable; + + /** + * @param mixed $expression + * @param string|null $fieldIdentificationVariable + * @param bool $hiddenAliasResultVariable + */ + public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false) + { + $this->expression = $expression; + $this->fieldIdentificationVariable = $fieldIdentificationVariable; + $this->hiddenAliasResultVariable = $hiddenAliasResultVariable; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php new file mode 100644 index 0000000..d84f725 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SelectStatement.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SelectStatement extends Node +{ + /** + * @var SelectClause + */ + public $selectClause; + + /** + * @var FromClause + */ + public $fromClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @var GroupByClause|null + */ + public $groupByClause; + + /** + * @var HavingClause|null + */ + public $havingClause; + + /** + * @var OrderByClause|null + */ + public $orderByClause; + + /** + * @param SelectClause $selectClause + * @param FromClause $fromClause + */ + public function __construct($selectClause, $fromClause) + { + $this->selectClause = $selectClause; + $this->fromClause = $fromClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSelectStatement($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php new file mode 100644 index 0000000..9bd4850 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleArithmeticExpression.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleArithmeticExpression extends Node +{ + /** + * @var array + */ + public $arithmeticTerms = array(); + + /** + * @param array $arithmeticTerms + */ + public function __construct(array $arithmeticTerms) + { + $this->arithmeticTerms = $arithmeticTerms; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleArithmeticExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php new file mode 100644 index 0000000..5272f39 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleCaseExpression.php @@ -0,0 +1,69 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleCaseExpression extends Node +{ + /** + * @var PathExpression + */ + public $caseOperand = null; + + /** + * @var array + */ + public $simpleWhenClauses = array(); + + /** + * @var mixed + */ + public $elseScalarExpression = null; + + /** + * @param PathExpression $caseOperand + * @param array $simpleWhenClauses + * @param mixed $elseScalarExpression + */ + public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression) + { + $this->caseOperand = $caseOperand; + $this->simpleWhenClauses = $simpleWhenClauses; + $this->elseScalarExpression = $elseScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleCaseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php new file mode 100644 index 0000000..92361da --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectClause.php @@ -0,0 +1,60 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectClause extends Node +{ + /** + * @var bool + */ + public $isDistinct = false; + + /** + * @var SimpleSelectExpression + */ + public $simpleSelectExpression; + + /** + * @param SimpleSelectExpression $simpleSelectExpression + * @param bool $isDistinct + */ + public function __construct($simpleSelectExpression, $isDistinct) + { + $this->simpleSelectExpression = $simpleSelectExpression; + $this->isDistinct = $isDistinct; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php new file mode 100644 index 0000000..e556835 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleSelectExpression.php @@ -0,0 +1,59 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable + * | (AggregateExpression [["AS"] FieldAliasIdentificationVariable]) + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleSelectExpression extends Node +{ + /** + * @var Node + */ + public $expression; + + /** + * @var string + */ + public $fieldIdentificationVariable; + + /** + * @param Node $expression + */ + public function __construct($expression) + { + $this->expression = $expression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSimpleSelectExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php new file mode 100644 index 0000000..4f60881 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SimpleWhenClause.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SimpleWhenClause extends Node +{ + /** + * @var mixed + */ + public $caseScalarExpression = null; + + /** + * @var mixed + */ + public $thenScalarExpression = null; + + /** + * @param mixed $caseScalarExpression + * @param mixed $thenScalarExpression + */ + public function __construct($caseScalarExpression, $thenScalarExpression) + { + $this->caseScalarExpression = $caseScalarExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php new file mode 100644 index 0000000..ce08266 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/Subselect.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Subselect extends Node +{ + /** + * @var SimpleSelectClause + */ + public $simpleSelectClause; + + /** + * @var SubselectFromClause + */ + public $subselectFromClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @var GroupByClause|null + */ + public $groupByClause; + + /** + * @var HavingClause|null + */ + public $havingClause; + + /** + * @var OrderByClause|null + */ + public $orderByClause; + + /** + * @param SimpleSelectClause $simpleSelectClause + * @param SubselectFromClause $subselectFromClause + */ + public function __construct($simpleSelectClause, $subselectFromClause) + { + $this->simpleSelectClause = $simpleSelectClause; + $this->subselectFromClause = $subselectFromClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselect($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php new file mode 100644 index 0000000..8d009fc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectFromClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SubselectFromClause extends Node +{ + /** + * @var array + */ + public $identificationVariableDeclarations = array(); + + /** + * @param array $identificationVariableDeclarations + */ + public function __construct(array $identificationVariableDeclarations) + { + $this->identificationVariableDeclarations = $identificationVariableDeclarations; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkSubselectFromClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectIdentificationVariableDeclaration.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectIdentificationVariableDeclaration.php new file mode 100644 index 0000000..8fbd9e2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/SubselectIdentificationVariableDeclaration.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * SubselectIdentificationVariableDeclaration ::= AssociationPathExpression ["AS"] AliasIdentificationVariable + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + */ +class SubselectIdentificationVariableDeclaration +{ + /** + * @var PathExpression + */ + public $associationPathExpression; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * Constructor. + * + * @param PathExpression $associationPathExpression + * @param string $aliasIdentificationVariable + */ + public function __construct($associationPathExpression, $aliasIdentificationVariable) + { + $this->associationPathExpression = $associationPathExpression; + $this->aliasIdentificationVariable = $aliasIdentificationVariable; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php new file mode 100644 index 0000000..430ed14 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateClause.php @@ -0,0 +1,65 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}* + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateClause extends Node +{ + /** + * @var string + */ + public $abstractSchemaName; + + /** + * @var string + */ + public $aliasIdentificationVariable; + + /** + * @var array + */ + public $updateItems = array(); + + /** + * @param string $abstractSchemaName + * @param array $updateItems + */ + public function __construct($abstractSchemaName, array $updateItems) + { + $this->abstractSchemaName = $abstractSchemaName; + $this->updateItems = $updateItems; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php new file mode 100644 index 0000000..f1a288c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateItem.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateItem extends Node +{ + /** + * @var PathExpression + */ + public $pathExpression; + + /** + * @var InputParameter|ArithmeticExpression|null + */ + public $newValue; + + /** + * @param PathExpression $pathExpression + * @param InputParameter|ArithmeticExpression|null $newValue + */ + public function __construct($pathExpression, $newValue) + { + $this->pathExpression = $pathExpression; + $this->newValue = $newValue; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateItem($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php new file mode 100644 index 0000000..c578efe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/UpdateStatement.php @@ -0,0 +1,58 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * UpdateStatement = UpdateClause [WhereClause] + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class UpdateStatement extends Node +{ + /** + * @var UpdateClause + */ + public $updateClause; + + /** + * @var WhereClause|null + */ + public $whereClause; + + /** + * @param UpdateClause $updateClause + */ + public function __construct($updateClause) + { + $this->updateClause = $updateClause; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkUpdateStatement($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php new file mode 100644 index 0000000..01c0330 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhenClause.php @@ -0,0 +1,62 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @since 2.2 + * + * @link www.doctrine-project.org + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhenClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $caseConditionExpression = null; + + /** + * @var mixed + */ + public $thenScalarExpression = null; + + /** + * @param ConditionalExpression $caseConditionExpression + * @param mixed $thenScalarExpression + */ + public function __construct($caseConditionExpression, $thenScalarExpression) + { + $this->caseConditionExpression = $caseConditionExpression; + $this->thenScalarExpression = $thenScalarExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhenClauseExpression($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php new file mode 100644 index 0000000..e659775 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/AST/WhereClause.php @@ -0,0 +1,53 @@ +. + */ + +namespace Doctrine\ORM\Query\AST; + +/** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class WhereClause extends Node +{ + /** + * @var ConditionalExpression + */ + public $conditionalExpression; + + /** + * @param ConditionalExpression $conditionalExpression + */ + public function __construct($conditionalExpression) + { + $this->conditionalExpression = $conditionalExpression; + } + + /** + * {@inheritdoc} + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkWhereClause($this); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php new file mode 100644 index 0000000..9be35df --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Cache\QueryCacheProfile; + +/** + * Base class for SQL statement executors. + * + * @author Roman Borschel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://www.doctrine-project.org + * @since 2.0 + * @todo Rename: AbstractSQLExecutor + */ +abstract class AbstractSqlExecutor +{ + /** + * @var array + */ + protected $_sqlStatements; + + /** + * @var QueryCacheProfile + */ + protected $queryCacheProfile; + + /** + * Gets the SQL statements that are executed by the executor. + * + * @return array All the SQL update statements. + */ + public function getSqlStatements() + { + return $this->_sqlStatements; + } + + /** + * @param \Doctrine\DBAL\Cache\QueryCacheProfile $qcp + * + * @return void + */ + public function setQueryCacheProfile(QueryCacheProfile $qcp) + { + $this->queryCacheProfile = $qcp; + } + + /** + * Executes all sql statements. + * + * @param Connection $conn The database connection that is used to execute the queries. + * @param array $params The parameters. + * @param array $types The parameter types. + * + * @return \Doctrine\DBAL\Driver\Statement + */ + abstract public function execute(Connection $conn, array $params, array $types); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php new file mode 100644 index 0000000..4e5303c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST; + +/** + * Executes the SQL statements for bulk DQL DELETE statements on classes in + * Class Table Inheritance (JOINED). + * + * @author Roman Borschel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class MultiTableDeleteExecutor extends AbstractSqlExecutor +{ + /** + * @var string + */ + private $_createTempTableSql; + + /** + * @var string + */ + private $_dropTempTableSql; + + /** + * @var string + */ + private $_insertSql; + + /** + * Initializes a new MultiTableDeleteExecutor. + * + * Internal note: Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + * + * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + + $primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName); + $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable; + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + + // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); + + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT t0.' . implode(', t0.', $idColumnNames); + + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); + $fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array()))); + $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); + + // Append WHERE clause, if there is one. + if ($AST->whereClause) { + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + } + + // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; + + // 3. Create and store DELETE statements + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + foreach (array_reverse($classNames) as $className) { + $tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform); + $this->_sqlStatements[] = 'DELETE FROM ' . $tableName + . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => \Doctrine\DBAL\Types\Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; + $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + $numDeleted = 0; + + // Create temporary id table + $conn->executeUpdate($this->_createTempTableSql); + + try { + // Insert identifiers + $numDeleted = $conn->executeUpdate($this->_insertSql, $params, $types); + + // Execute DELETE statements + foreach ($this->_sqlStatements as $sql) { + $conn->executeUpdate($sql); + } + } catch (\Exception $exception) { + // FAILURE! Drop temporary table to avoid possible collisions + $conn->executeUpdate($this->_dropTempTableSql); + + // Re-throw exception + throw $exception; + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numDeleted; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php new file mode 100644 index 0000000..54653fc --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -0,0 +1,206 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +use Doctrine\ORM\Query\ParameterTypeInferer; +use Doctrine\ORM\Query\AST; + +/** + * Executes the SQL statements for bulk DQL UPDATE statements on classes in + * Class Table Inheritance (JOINED). + * + * @author Roman Borschel + * @since 2.0 + */ +class MultiTableUpdateExecutor extends AbstractSqlExecutor +{ + /** + * @var string + */ + private $_createTempTableSql; + + /** + * @var string + */ + private $_dropTempTableSql; + + /** + * @var string + */ + private $_insertSql; + + /** + * @var array + */ + private $_sqlParameters = array(); + + /** + * @var int + */ + private $_numParametersInUpdateClause = 0; + + /** + * Initializes a new MultiTableUpdateExecutor. + * + * Internal note: Any SQL construction and preparation takes place in the constructor for + * best performance. With a query cache the executor will be cached. + * + * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + $em = $sqlWalker->getEntityManager(); + $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + + $updateClause = $AST->updateClause; + $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName); + $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); + + $updateItems = $updateClause->updateItems; + + $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); + $idColumnNames = $rootClass->getIdentifierColumnNames(); + $idColumnList = implode(', ', $idColumnNames); + + // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() + $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); + + $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' + . ' SELECT t0.' . implode(', t0.', $idColumnNames); + + $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable); + $fromClause = new AST\FromClause(array(new AST\IdentificationVariableDeclaration($rangeDecl, null, array()))); + + $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); + + // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect) + $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; + + // 3. Create and store UPDATE statements + $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); + $i = -1; + + foreach (array_reverse($classNames) as $className) { + $affected = false; + $class = $em->getClassMetadata($className); + $updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET '; + + foreach ($updateItems as $updateItem) { + $field = $updateItem->pathExpression->field; + + if (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited']) || + isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited'])) { + $newValue = $updateItem->newValue; + + if ( ! $affected) { + $affected = true; + ++$i; + } else { + $updateSql .= ', '; + } + + $updateSql .= $sqlWalker->walkUpdateItem($updateItem); + + if ($newValue instanceof AST\InputParameter) { + $this->_sqlParameters[$i][] = $newValue->name; + + ++$this->_numParametersInUpdateClause; + } + } + } + + if ($affected) { + $this->_sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; + } + } + + // Append WHERE clause to insertSql, if there is one. + if ($AST->whereClause) { + $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); + } + + // 4. Store DDL for temporary identifier table. + $columnDefinitions = array(); + + foreach ($idColumnNames as $idColumnName) { + $columnDefinitions[$idColumnName] = array( + 'notnull' => true, + 'type' => Type::getType($rootClass->getTypeOfColumn($idColumnName)) + ); + } + + $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' + . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; + + $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + $numUpdated = 0; + + // Create temporary id table + $conn->executeUpdate($this->_createTempTableSql); + + try { + // Insert identifiers. Parameters from the update clause are cut off. + $numUpdated = $conn->executeUpdate( + $this->_insertSql, + array_slice($params, $this->_numParametersInUpdateClause), + array_slice($types, $this->_numParametersInUpdateClause) + ); + + // Execute UPDATE statements + foreach ($this->_sqlStatements as $key => $statement) { + $paramValues = array(); + $paramTypes = array(); + + if (isset($this->_sqlParameters[$key])) { + foreach ($this->_sqlParameters[$key] as $parameterKey => $parameterName) { + $paramValues[] = $params[$parameterKey]; + $paramTypes[] = isset($types[$parameterKey]) ? $types[$parameterKey] : ParameterTypeInferer::inferType($params[$parameterKey]); + } + } + + $conn->executeUpdate($statement, $paramValues, $paramTypes); + } + } catch (\Exception $exception) { + // FAILURE! Drop temporary table to avoid possible collisions + $conn->executeUpdate($this->_dropTempTableSql); + + // Re-throw exception + throw $exception; + } + + // Drop temporary table + $conn->executeUpdate($this->_dropTempTableSql); + + return $numUpdated; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php new file mode 100644 index 0000000..91827ab --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\SqlWalker; + +/** + * Executor that executes the SQL statement for simple DQL SELECT statements. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + */ +class SingleSelectExecutor extends AbstractSqlExecutor +{ + /** + * @param \Doctrine\ORM\Query\AST\SelectStatement $AST + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + */ + public function __construct(SelectStatement $AST, SqlWalker $sqlWalker) + { + $this->_sqlStatements = $sqlWalker->walkSelectStatement($AST); + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php new file mode 100644 index 0000000..e0183dd --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\Exec; + +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Query\AST; + +/** + * Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes + * that are mapped to a single table. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @author Roman Borschel + * @link www.doctrine-project.org + * @since 2.0 + * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. + */ +class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor +{ + /** + * @param \Doctrine\ORM\Query\AST\Node $AST + * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker + */ + public function __construct(AST\Node $AST, $sqlWalker) + { + if ($AST instanceof AST\UpdateStatement) { + $this->_sqlStatements = $sqlWalker->walkUpdateStatement($AST); + } else if ($AST instanceof AST\DeleteStatement) { + $this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST); + } + } + + /** + * {@inheritDoc} + */ + public function execute(Connection $conn, array $params, array $types) + { + return $conn->executeUpdate($this->_sqlStatements, $params, $types); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php new file mode 100644 index 0000000..b6997d8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr.php @@ -0,0 +1,673 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * This class is used to generate DQL expressions via a set of PHP static functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + * @todo Rename: ExpressionBuilder + */ +class Expr +{ + /** + * Creates a conjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?1) AND (u.role = ?2) + * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2')); + * + * @param \Doctrine\ORM\Query\Expr\Comparison | + * \Doctrine\ORM\Query\Expr\Func | + * \Doctrine\ORM\Query\Expr\Orx + * $x Optional clause. Defaults to null, but requires at least one defined when converting to string. + * + * @return Expr\Andx + */ + public function andX($x = null) + { + return new Expr\Andx(func_get_args()); + } + + /** + * Creates a disjunction of the given boolean expressions. + * + * Example: + * + * [php] + * // (u.type = ?1) OR (u.role = ?2) + * $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2')); + * + * @param mixed $x Optional clause. Defaults to null, but requires + * at least one defined when converting to string. + * + * @return Expr\Orx + */ + public function orX($x = null) + { + return new Expr\Orx(func_get_args()); + } + + /** + * Creates an ASCending order expression. + * + * @param mixed $expr + * + * @return Expr\OrderBy + */ + public function asc($expr) + { + return new Expr\OrderBy($expr, 'ASC'); + } + + /** + * Creates a DESCending order expression. + * + * @param mixed $expr + * + * @return Expr\OrderBy + */ + public function desc($expr) + { + return new Expr\OrderBy($expr, 'DESC'); + } + + /** + * Creates an equality comparison expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a = . Example: + * + * [php] + * // u.id = ?1 + * $expr->eq('u.id', '?1'); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function eq($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::EQ, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <> . Example: + * + * [php] + * // u.id <> ?1 + * $q->where($q->expr()->neq('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function neq($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::NEQ, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a < . Example: + * + * [php] + * // u.id < ?1 + * $q->where($q->expr()->lt('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function lt($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::LT, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a <= . Example: + * + * [php] + * // u.id <= ?1 + * $q->where($q->expr()->lte('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function lte($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::LTE, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a > . Example: + * + * [php] + * // u.id > ?1 + * $q->where($q->expr()->gt('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function gt($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::GT, $y); + } + + /** + * Creates an instance of Expr\Comparison, with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a >= . Example: + * + * [php] + * // u.id >= ?1 + * $q->where($q->expr()->gte('u.id', '?1')); + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Comparison + */ + public function gte($x, $y) + { + return new Expr\Comparison($x, Expr\Comparison::GTE, $y); + } + + /** + * Creates an instance of AVG() function, with the given argument. + * + * @param mixed $x Argument to be used in AVG() function. + * + * @return Expr\Func + */ + public function avg($x) + { + return new Expr\Func('AVG', array($x)); + } + + /** + * Creates an instance of MAX() function, with the given argument. + * + * @param mixed $x Argument to be used in MAX() function. + * + * @return Expr\Func + */ + public function max($x) + { + return new Expr\Func('MAX', array($x)); + } + + /** + * Creates an instance of MIN() function, with the given argument. + * + * @param mixed $x Argument to be used in MIN() function. + * + * @return Expr\Func + */ + public function min($x) + { + return new Expr\Func('MIN', array($x)); + } + + /** + * Creates an instance of COUNT() function, with the given argument. + * + * @param mixed $x Argument to be used in COUNT() function. + * + * @return Expr\Func + */ + public function count($x) + { + return new Expr\Func('COUNT', array($x)); + } + + /** + * Creates an instance of COUNT(DISTINCT) function, with the given argument. + * + * @param mixed $x Argument to be used in COUNT(DISTINCT) function. + * + * @return string + */ + public function countDistinct($x) + { + return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')'; + } + + /** + * Creates an instance of EXISTS() function, with the given DQL Subquery. + * + * @param mixed $subquery DQL Subquery to be used in EXISTS() function. + * + * @return Expr\Func + */ + public function exists($subquery) + { + return new Expr\Func('EXISTS', array($subquery)); + } + + /** + * Creates an instance of ALL() function, with the given DQL Subquery. + * + * @param mixed $subquery DQL Subquery to be used in ALL() function. + * + * @return Expr\Func + */ + public function all($subquery) + { + return new Expr\Func('ALL', array($subquery)); + } + + /** + * Creates a SOME() function expression with the given DQL subquery. + * + * @param mixed $subquery DQL Subquery to be used in SOME() function. + * + * @return Expr\Func + */ + public function some($subquery) + { + return new Expr\Func('SOME', array($subquery)); + } + + /** + * Creates an ANY() function expression with the given DQL subquery. + * + * @param mixed $subquery DQL Subquery to be used in ANY() function. + * + * @return Expr\Func + */ + public function any($subquery) + { + return new Expr\Func('ANY', array($subquery)); + } + + /** + * Creates a negation expression of the given restriction. + * + * @param mixed $restriction Restriction to be used in NOT() function. + * + * @return Expr\Func + */ + public function not($restriction) + { + return new Expr\Func('NOT', array($restriction)); + } + + /** + * Creates an ABS() function expression with the given argument. + * + * @param mixed $x Argument to be used in ABS() function. + * + * @return Expr\Func + */ + public function abs($x) + { + return new Expr\Func('ABS', array($x)); + } + + /** + * Creates a product mathematical expression with the given arguments. + * + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a * . Example: + * + * [php] + * // u.salary * u.percentAnnualSalaryIncrease + * $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function prod($x, $y) + { + return new Expr\Math($x, '*', $y); + } + + /** + * Creates a difference mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a - . Example: + * + * [php] + * // u.monthlySubscriptionCount - 1 + * $q->expr()->diff('u.monthlySubscriptionCount', '1') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function diff($x, $y) + { + return new Expr\Math($x, '-', $y); + } + + /** + * Creates a sum mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a + . Example: + * + * [php] + * // u.numChildren + 1 + * $q->expr()->diff('u.numChildren', '1') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function sum($x, $y) + { + return new Expr\Math($x, '+', $y); + } + + /** + * Creates a quotient mathematical expression with the given arguments. + * First argument is considered the left expression and the second is the right expression. + * When converted to string, it will generated a / . Example: + * + * [php] + * // u.total / u.period + * $expr->quot('u.total', 'u.period') + * + * @param mixed $x Left expression. + * @param mixed $y Right expression. + * + * @return Expr\Math + */ + public function quot($x, $y) + { + return new Expr\Math($x, '/', $y); + } + + /** + * Creates a SQRT() function expression with the given argument. + * + * @param mixed $x Argument to be used in SQRT() function. + * + * @return Expr\Func + */ + public function sqrt($x) + { + return new Expr\Func('SQRT', array($x)); + } + + /** + * Creates an IN() expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IN() function. + * @param mixed $y Argument to be used in IN() function. + * + * @return Expr\Func + */ + public function in($x, $y) + { + if (is_array($y)) { + foreach ($y as &$literal) { + if ( ! ($literal instanceof Expr\Literal)) { + $literal = $this->_quoteLiteral($literal); + } + } + } + return new Expr\Func($x . ' IN', (array) $y); + } + + /** + * Creates a NOT IN() expression with the given arguments. + * + * @param string $x Field in string format to be restricted by NOT IN() function. + * @param mixed $y Argument to be used in NOT IN() function. + * + * @return Expr\Func + */ + public function notIn($x, $y) + { + if (is_array($y)) { + foreach ($y as &$literal) { + if ( ! ($literal instanceof Expr\Literal)) { + $literal = $this->_quoteLiteral($literal); + } + } + } + return new Expr\Func($x . ' NOT IN', (array) $y); + } + + /** + * Creates an IS NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NULL. + * + * @return string + */ + public function isNull($x) + { + return $x . ' IS NULL'; + } + + /** + * Creates an IS NOT NULL expression with the given arguments. + * + * @param string $x Field in string format to be restricted by IS NOT NULL. + * + * @return string + */ + public function isNotNull($x) + { + return $x . ' IS NOT NULL'; + } + + /** + * Creates a LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return Expr\Comparison + */ + public function like($x, $y) + { + return new Expr\Comparison($x, 'LIKE', $y); + } + + /** + * Creates a NOT LIKE() comparison expression with the given arguments. + * + * @param string $x Field in string format to be inspected by LIKE() comparison. + * @param mixed $y Argument to be used in LIKE() comparison. + * + * @return Expr\Comparison + */ + public function notLike($x, $y) + { + return new Expr\Comparison($x, 'NOT LIKE', $y); + } + + /** + * Creates a CONCAT() function expression with the given arguments. + * + * @param mixed $x First argument to be used in CONCAT() function. + * @param mixed $y Second argument to be used in CONCAT() function. + * + * @return Expr\Func + */ + public function concat($x, $y) + { + return new Expr\Func('CONCAT', array($x, $y)); + } + + /** + * Creates a SUBSTRING() function expression with the given arguments. + * + * @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function. + * @param int $from Initial offset to start cropping string. May accept negative values. + * @param int|null $len Length of crop. May accept negative values. + * + * @return Expr\Func + */ + public function substring($x, $from, $len = null) + { + $args = array($x, $from); + if (null !== $len) { + $args[] = $len; + } + return new Expr\Func('SUBSTRING', $args); + } + + /** + * Creates a LOWER() function expression with the given argument. + * + * @param mixed $x Argument to be used in LOWER() function. + * + * @return Expr\Func A LOWER function expression. + */ + public function lower($x) + { + return new Expr\Func('LOWER', array($x)); + } + + /** + * Creates an UPPER() function expression with the given argument. + * + * @param mixed $x Argument to be used in UPPER() function. + * + * @return Expr\Func An UPPER function expression. + */ + public function upper($x) + { + return new Expr\Func('UPPER', array($x)); + } + + /** + * Creates a LENGTH() function expression with the given argument. + * + * @param mixed $x Argument to be used as argument of LENGTH() function. + * + * @return Expr\Func A LENGTH function expression. + */ + public function length($x) + { + return new Expr\Func('LENGTH', array($x)); + } + + /** + * Creates a literal expression of the given argument. + * + * @param mixed $literal Argument to be converted to literal. + * + * @return Expr\Literal + */ + public function literal($literal) + { + return new Expr\Literal($this->_quoteLiteral($literal)); + } + + /** + * Quotes a literal value, if necessary, according to the DQL syntax. + * + * @param mixed $literal The literal value. + * + * @return string + */ + private function _quoteLiteral($literal) + { + if (is_numeric($literal) && !is_string($literal)) { + return (string) $literal; + } else if (is_bool($literal)) { + return $literal ? "true" : "false"; + } else { + return "'" . str_replace("'", "''", $literal) . "'"; + } + } + + /** + * Creates an instance of BETWEEN() function, with the given argument. + * + * @param mixed $val Valued to be inspected by range values. + * @param integer $x Starting range value to be used in BETWEEN() function. + * @param integer $y End point value to be used in BETWEEN() function. + * + * @return Expr\Func A BETWEEN expression. + */ + public function between($val, $x, $y) + { + return $val . ' BETWEEN ' . $x . ' AND ' . $y; + } + + /** + * Creates an instance of TRIM() function, with the given argument. + * + * @param mixed $x Argument to be used as argument of TRIM() function. + * + * @return Expr\Func a TRIM expression. + */ + public function trim($x) + { + return new Expr\Func('TRIM', $x); + } + + /** + * Creates an instance of MEMBER OF function, with the given arguments. + * + * @param string $x Value to be checked + * @param string $y Value to be checked against + * + * @return Expr\Comparison + */ + public function isMemberOf($x, $y) + { + return new Expr\Comparison($x, 'MEMBER OF', $y); + } + + /** + * Creates an instance of INSTANCE OF function, with the given arguments. + * + * @param string $x Value to be checked + * @param string $y Value to be checked against + * + * @return Expr\Comparison + */ + public function isInstanceOf($x, $y) + { + return new Expr\Comparison($x, 'INSTANCE OF', $y); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php new file mode 100644 index 0000000..432f714 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Andx.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Andx extends Composite +{ + /** + * @var string + */ + protected $separator = ' AND '; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Orx', + 'Doctrine\ORM\Query\Expr\Andx', + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php new file mode 100644 index 0000000..6eac70a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Base.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Abstract base Expr class for building DQL parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class Base +{ + /** + * @var string + */ + protected $preSeparator = '('; + + /** + * @var string + */ + protected $separator = ', '; + + /** + * @var string + */ + protected $postSeparator = ')'; + + /** + * @var array + */ + protected $allowedClasses = array(); + + /** + * @var array + */ + protected $parts = array(); + + /** + * @param array $args + */ + public function __construct($args = array()) + { + $this->addMultiple($args); + } + + /** + * @param array $args + * + * @return Base + */ + public function addMultiple($args = array()) + { + foreach ((array) $args as $arg) { + $this->add($arg); + } + + return $this; + } + + /** + * @param mixed $arg + * + * @return Base + * + * @throws \InvalidArgumentException + */ + public function add($arg) + { + if ( $arg !== null && (!$arg instanceof self || $arg->count() > 0) ) { + // If we decide to keep Expr\Base instances, we can use this check + if ( ! is_string($arg)) { + $class = get_class($arg); + + if ( ! in_array($class, $this->allowedClasses)) { + throw new \InvalidArgumentException("Expression of type '$class' not allowed in this context."); + } + } + + $this->parts[] = $arg; + } + + return $this; + } + + /** + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * @return string + */ + public function __toString() + { + if ($this->count() == 1) { + return (string) $this->parts[0]; + } + + return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php new file mode 100644 index 0000000..4103dce --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Comparison.php @@ -0,0 +1,100 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL comparison expressions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Comparison +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + + /** + * @var mixed + */ + protected $leftExpr; + + /** + * @var string + */ + protected $operator; + + /** + * @var mixed + */ + protected $rightExpr; + + /** + * Creates a comparison expression with the given arguments. + * + * @param mixed $leftExpr + * @param string $operator + * @param mixed $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpr = $leftExpr; + $this->operator = $operator; + $this->rightExpr = $rightExpr; + } + + /** + * @return mixed + */ + public function getLeftExpr() + { + return $this->leftExpr; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return mixed + */ + public function getRightExpr() + { + return $this->rightExpr; + } + + /** + * @return string + */ + public function __toString() + { + return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php new file mode 100644 index 0000000..4d9a251 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Composite.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL and parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Composite extends Base +{ + /** + * @return string + */ + public function __toString() + { + if ($this->count() === 1) { + return (string) $this->parts[0]; + } + + $components = array(); + + foreach ($this->parts as $part) { + $components[] = $this->processQueryPart($part); + } + + return implode($this->separator, $components); + } + + /** + * @param string $part + * + * @return string + */ + private function processQueryPart($part) + { + $queryPart = (string) $part; + + if (is_object($part) && $part instanceof self && $part->count() > 1) { + return $this->preSeparator . $queryPart . $this->postSeparator; + } + + // Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND") + if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) { + return $this->preSeparator . $queryPart . $this->postSeparator; + } + + return $queryPart; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php new file mode 100644 index 0000000..9dcce9b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/From.php @@ -0,0 +1,92 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL from. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class From +{ + /** + * @var string + */ + protected $from; + + /** + * @var string + */ + protected $alias; + + /** + * @var string + */ + protected $indexBy; + + /** + * @param string $from The class name. + * @param string $alias The alias of the class. + * @param string $indexBy The index for the from. + */ + public function __construct($from, $alias, $indexBy = null) + { + $this->from = $from; + $this->alias = $alias; + $this->indexBy = $indexBy; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * @return string + */ + public function getIndexBy() + { + return $this->indexBy; + } + + /** + * @return string + */ + public function __toString() + { + return $this->from . ' ' . $this->alias . + ($this->indexBy ? ' INDEX BY ' . $this->indexBy : ''); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php new file mode 100644 index 0000000..b4ed07c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Func.php @@ -0,0 +1,78 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Func +{ + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $arguments; + + /** + * Creates a function, with the given argument. + * + * @param string $name + * @param array $arguments + */ + public function __construct($name, $arguments) + { + $this->name = $name; + $this->arguments = (array) $arguments; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * @return string + */ + public function __toString() + { + return $this->name . '(' . implode(', ', $this->arguments) . ')'; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php new file mode 100644 index 0000000..efa3582 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/GroupBy.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Group By parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GroupBy extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php new file mode 100644 index 0000000..7a59e24 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Join.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL join. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Join +{ + const INNER_JOIN = 'INNER'; + const LEFT_JOIN = 'LEFT'; + + const ON = 'ON'; + const WITH = 'WITH'; + + /** + * @var string + */ + protected $joinType; + + /** + * @var string + */ + protected $join; + + /** + * @var string + */ + protected $alias; + + /** + * @var string + */ + protected $conditionType; + + /** + * @var string + */ + protected $condition; + + /** + * @var string + */ + protected $indexBy; + + /** + * @param string $joinType The condition type constant. Either INNER_JOIN or LEFT_JOIN. + * @param string $join The relationship to join. + * @param string|null $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + */ + public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null) + { + $this->joinType = $joinType; + $this->join = $join; + $this->alias = $alias; + $this->conditionType = $conditionType; + $this->condition = $condition; + $this->indexBy = $indexBy; + } + + /** + * @return string + */ + public function getJoinType() + { + return $this->joinType; + } + + /** + * @return string + */ + public function getJoin() + { + return $this->join; + } + + /** + * @return string + */ + public function getAlias() + { + return $this->alias; + } + + /** + * @return string + */ + public function getConditionType() + { + return $this->conditionType; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return string + */ + public function getIndexBy() + { + return $this->indexBy; + } + + /** + * @return string + */ + public function __toString() + { + return strtoupper($this->joinType) . ' JOIN ' . $this->join + . ($this->alias ? ' ' . $this->alias : '') + . ($this->indexBy ? ' INDEX BY ' . $this->indexBy : '') + . ($this->condition ? ' ' . strtoupper($this->conditionType) . ' ' . $this->condition : ''); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php new file mode 100644 index 0000000..98cee79 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Literal.php @@ -0,0 +1,50 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for generating DQL functions. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Literal extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php new file mode 100644 index 0000000..9bf800d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Math.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for DQL math statements. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Math +{ + /** + * @var mixed + */ + protected $leftExpr; + + /** + * @var string + */ + protected $operator; + + /** + * @var mixed + */ + protected $rightExpr; + + /** + * Creates a mathematical expression with the given arguments. + * + * @param mixed $leftExpr + * @param string $operator + * @param mixed $rightExpr + */ + public function __construct($leftExpr, $operator, $rightExpr) + { + $this->leftExpr = $leftExpr; + $this->operator = $operator; + $this->rightExpr = $rightExpr; + } + + /** + * @return mixed + */ + public function getLeftExpr() + { + return $this->leftExpr; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return mixed + */ + public function getRightExpr() + { + return $this->rightExpr; + } + + /** + * @return string + */ + public function __toString() + { + // Adjusting Left Expression + $leftExpr = (string) $this->leftExpr; + + if ($this->leftExpr instanceof Math) { + $leftExpr = '(' . $leftExpr . ')'; + } + + // Adjusting Right Expression + $rightExpr = (string) $this->rightExpr; + + if ($this->rightExpr instanceof Math) { + $rightExpr = '(' . $rightExpr . ')'; + } + + return $leftExpr . ' ' . $this->operator . ' ' . $rightExpr; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php new file mode 100644 index 0000000..932548b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/OrderBy.php @@ -0,0 +1,104 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL Order By parts. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class OrderBy +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $separator = ', '; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @var array + */ + protected $allowedClasses = array(); + + /** + * @var array + */ + protected $parts = array(); + + /** + * @param string|null $sort + * @param string|null $order + */ + public function __construct($sort = null, $order = null) + { + if ($sort) { + $this->add($sort, $order); + } + } + + /** + * @param string $sort + * @param string|null $order + * + * @return void + */ + public function add($sort, $order = null) + { + $order = ! $order ? 'ASC' : $order; + $this->parts[] = $sort . ' '. $order; + } + + /** + * @return integer + */ + public function count() + { + return count($this->parts); + } + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } + + /** + * @return string + */ + public function __tostring() + { + return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php new file mode 100644 index 0000000..c4ff7ae --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Orx.php @@ -0,0 +1,55 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL OR clauses. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Orx extends Composite +{ + /** + * @var string + */ + protected $separator = ' OR '; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Comparison', + 'Doctrine\ORM\Query\Expr\Func', + 'Doctrine\ORM\Query\Expr\Andx', + 'Doctrine\ORM\Query\Expr\Orx', + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php new file mode 100644 index 0000000..21df6c7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Expr/Select.php @@ -0,0 +1,57 @@ +. + */ + +namespace Doctrine\ORM\Query\Expr; + +/** + * Expression class for building DQL select statements. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Select extends Base +{ + /** + * @var string + */ + protected $preSeparator = ''; + + /** + * @var string + */ + protected $postSeparator = ''; + + /** + * @var array + */ + protected $allowedClasses = array( + 'Doctrine\ORM\Query\Expr\Func' + ); + + /** + * @return array + */ + public function getParts() + { + return $this->parts; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php new file mode 100644 index 0000000..b0248fb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Filter/SQLFilter.php @@ -0,0 +1,155 @@ +. + */ + +namespace Doctrine\ORM\Query\Filter; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\ParameterTypeInferer; + +/** + * The base class that user defined filters should extend. + * + * Handles the setting and escaping of parameters. + * + * @author Alexander + * @author Benjamin Eberlei + * @abstract + */ +abstract class SQLFilter +{ + /** + * The entity manager. + * + * @var EntityManagerInterface + */ + private $em; + + /** + * Parameters for the filter. + * + * @var array + */ + private $parameters = []; + + /** + * Constructs the SQLFilter object. + * + * @param EntityManagerInterface $em The entity manager. + */ + final public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Sets a parameter that can be used by the filter. + * + * @param string $name Name of the parameter. + * @param string $value Value of the parameter. + * @param string|null $type The parameter type. If specified, the given value will be run through + * the type conversion of this type. This is usually not needed for + * strings and numeric types. + * + * @return SQLFilter The current SQL filter. + */ + final public function setParameter($name, $value, $type = null) + { + if (null === $type) { + $type = ParameterTypeInferer::inferType($value); + } + + $this->parameters[$name] = array('value' => $value, 'type' => $type); + + // Keep the parameters sorted for the hash + ksort($this->parameters); + + // The filter collection of the EM is now dirty + $this->em->getFilters()->setFiltersStateDirty(); + + return $this; + } + + /** + * Gets a parameter to use in a query. + * + * The function is responsible for the right output escaping to use the + * value in a query. + * + * @param string $name Name of the parameter. + * + * @return string The SQL escaped parameter to use in a query. + * + * @throws \InvalidArgumentException + */ + final public function getParameter($name) + { + if (!isset($this->parameters[$name])) { + throw new \InvalidArgumentException("Parameter '" . $name . "' does not exist."); + } + + return $this->em->getConnection()->quote($this->parameters[$name]['value'], $this->parameters[$name]['type']); + } + + /** + * Checks if a parameter was set for the filter. + * + * @param string $name Name of the parameter. + * + * @return boolean + */ + final public function hasParameter($name) + { + if (!isset($this->parameters[$name])) { + return false; + } + + return true; + } + + /** + * Returns as string representation of the SQLFilter parameters (the state). + * + * @return string String representation of the SQLFilter. + */ + final public function __toString() + { + return serialize($this->parameters); + } + + /** + * Returns the database connection used by the entity manager + * + * @return \Doctrine\DBAL\Connection + */ + final protected function getConnection() + { + return $this->em->getConnection(); + } + + /** + * Gets the SQL query part to add to a query. + * + * @param ClassMetaData $targetEntity + * @param string $targetTableAlias + * + * @return string The constraint SQL if there is available, empty string otherwise. + */ + abstract public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php new file mode 100644 index 0000000..ffbaeaf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/FilterCollection.php @@ -0,0 +1,225 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Collection class for all the query filters. + * + * @author Alexander + */ +class FilterCollection +{ + /* Filter STATES */ + + /** + * A filter object is in CLEAN state when it has no changed parameters. + */ + const FILTERS_STATE_CLEAN = 1; + + /** + * A filter object is in DIRTY state when it has changed parameters. + */ + const FILTERS_STATE_DIRTY = 2; + + /** + * The used Configuration. + * + * @var \Doctrine\ORM\Configuration + */ + private $config; + + /** + * The EntityManager that "owns" this FilterCollection instance. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * Instances of enabled filters. + * + * @var \Doctrine\ORM\Query\Filter\SQLFilter[] + */ + private $enabledFilters = array(); + + /** + * @var string The filter hash from the last time the query was parsed. + */ + private $filterHash; + + /** + * @var integer The current state of this filter. + */ + private $filtersState = self::FILTERS_STATE_CLEAN; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->config = $em->getConfiguration(); + } + + /** + * Gets all the enabled filters. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter[] The enabled filters. + */ + public function getEnabledFilters() + { + return $this->enabledFilters; + } + + /** + * Enables a filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The enabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function enable($name) + { + if ( ! $this->has($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' does not exist."); + } + + if ( ! $this->isEnabled($name)) { + $filterClass = $this->config->getFilterClassName($name); + + $this->enabledFilters[$name] = new $filterClass($this->em); + + // Keep the enabled filters sorted for the hash + ksort($this->enabledFilters); + + // Now the filter collection is dirty + $this->filtersState = self::FILTERS_STATE_DIRTY; + } + + return $this->enabledFilters[$name]; + } + + /** + * Disables a filter. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The disabled filter. + * + * @throws \InvalidArgumentException If the filter does not exist. + */ + public function disable($name) + { + // Get the filter to return it + $filter = $this->getFilter($name); + + unset($this->enabledFilters[$name]); + + // Now the filter collection is dirty + $this->filtersState = self::FILTERS_STATE_DIRTY; + + return $filter; + } + + /** + * Gets an enabled filter from the collection. + * + * @param string $name Name of the filter. + * + * @return \Doctrine\ORM\Query\Filter\SQLFilter The filter. + * + * @throws \InvalidArgumentException If the filter is not enabled. + */ + public function getFilter($name) + { + if ( ! $this->isEnabled($name)) { + throw new \InvalidArgumentException("Filter '" . $name . "' is not enabled."); + } + + return $this->enabledFilters[$name]; + } + + /** + * Checks whether filter with given name is defined. + * + * @param string $name Name of the filter. + * + * @return bool true if the filter exists, false if not. + */ + public function has($name) + { + return null !== $this->config->getFilterClassName($name); + } + + /** + * Checks if a filter is enabled. + * + * @param string $name Name of the filter. + * + * @return boolean True if the filter is enabled, false otherwise. + */ + public function isEnabled($name) + { + return isset($this->enabledFilters[$name]); + } + + /** + * @return boolean True, if the filter collection is clean. + */ + public function isClean() + { + return self::FILTERS_STATE_CLEAN === $this->filtersState; + } + + /** + * Generates a string of currently enabled filters to use for the cache id. + * + * @return string + */ + public function getHash() + { + // If there are only clean filters, the previous hash can be returned + if (self::FILTERS_STATE_CLEAN === $this->filtersState) { + return $this->filterHash; + } + + $filterHash = ''; + + foreach ($this->enabledFilters as $name => $filter) { + $filterHash .= $name . $filter; + } + + return $filterHash; + } + + /** + * Sets the filter state to dirty. + */ + public function setFiltersStateDirty() + { + $this->filtersState = self::FILTERS_STATE_DIRTY; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php new file mode 100644 index 0000000..d5721a7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Lexer.php @@ -0,0 +1,207 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Scans a DQL query for tokens. + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @since 2.0 + */ +class Lexer extends \Doctrine\Common\Lexer +{ + // All tokens that are not valid identifiers must be < 100 + const T_NONE = 1; + const T_INTEGER = 2; + const T_STRING = 3; + const T_INPUT_PARAMETER = 4; + const T_FLOAT = 5; + const T_CLOSE_PARENTHESIS = 6; + const T_OPEN_PARENTHESIS = 7; + const T_COMMA = 8; + const T_DIVIDE = 9; + const T_DOT = 10; + const T_EQUALS = 11; + const T_GREATER_THAN = 12; + const T_LOWER_THAN = 13; + const T_MINUS = 14; + const T_MULTIPLY = 15; + const T_NEGATE = 16; + const T_PLUS = 17; + const T_OPEN_CURLY_BRACE = 18; + const T_CLOSE_CURLY_BRACE = 19; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; + const T_ALL = 101; + const T_AND = 102; + const T_ANY = 103; + const T_AS = 104; + const T_ASC = 105; + const T_AVG = 106; + const T_BETWEEN = 107; + const T_BOTH = 108; + const T_BY = 109; + const T_CASE = 110; + const T_COALESCE = 111; + const T_COUNT = 112; + const T_DELETE = 113; + const T_DESC = 114; + const T_DISTINCT = 115; + const T_ELSE = 116; + const T_EMPTY = 117; + const T_END = 118; + const T_ESCAPE = 119; + const T_EXISTS = 120; + const T_FALSE = 121; + const T_FROM = 122; + const T_GROUP = 123; + const T_HAVING = 124; + const T_HIDDEN = 125; + const T_IN = 126; + const T_INDEX = 127; + const T_INNER = 128; + const T_INSTANCE = 129; + const T_IS = 130; + const T_JOIN = 131; + const T_LEADING = 132; + const T_LEFT = 133; + const T_LIKE = 134; + const T_MAX = 135; + const T_MEMBER = 136; + const T_MIN = 137; + const T_NOT = 138; + const T_NULL = 139; + const T_NULLIF = 140; + const T_OF = 141; + const T_OR = 142; + const T_ORDER = 143; + const T_OUTER = 144; + const T_SELECT = 145; + const T_SET = 146; + const T_SOME = 147; + const T_SUM = 148; + const T_THEN = 149; + const T_TRAILING = 150; + const T_TRUE = 151; + const T_UPDATE = 152; + const T_WHEN = 153; + const T_WHERE = 154; + const T_WITH = 155; + const T_PARTIAL = 156; + const T_NEW = 157; + + /** + * Creates a new query scanner object. + * + * @param string $input A query string. + */ + public function __construct($input) + { + $this->setInput($input); + } + + /** + * @inheritdoc + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_\\\][a-z0-9_\:\\\]*[a-z0-9_]{1}', + '(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?', + "'(?:[^']|'')*'", + '\?[0-9]*|:[a-z_][a-z0-9_]*' + ); + } + + /** + * @inheritdoc + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '(.)'); + } + + /** + * @inheritdoc + */ + protected function getType(&$value) + { + $type = self::T_NONE; + + switch (true) { + // Recognize numeric values + case (is_numeric($value)): + if (strpos($value, '.') !== false || stripos($value, 'e') !== false) { + return self::T_FLOAT; + } + + return self::T_INTEGER; + + // Recognize quoted strings + case ($value[0] === "'"): + $value = str_replace("''", "'", substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + + // Recognize identifiers + case (ctype_alpha($value[0]) || $value[0] === '_'): + $name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value); + + if (defined($name)) { + $type = constant($name); + + if ($type > 100) { + return $type; + } + } + + return self::T_IDENTIFIER; + + // Recognize input parameters + case ($value[0] === '?' || $value[0] === ':'): + return self::T_INPUT_PARAMETER; + + // Recognize symbols + case ($value === '.'): return self::T_DOT; + case ($value === ','): return self::T_COMMA; + case ($value === '('): return self::T_OPEN_PARENTHESIS; + case ($value === ')'): return self::T_CLOSE_PARENTHESIS; + case ($value === '='): return self::T_EQUALS; + case ($value === '>'): return self::T_GREATER_THAN; + case ($value === '<'): return self::T_LOWER_THAN; + case ($value === '+'): return self::T_PLUS; + case ($value === '-'): return self::T_MINUS; + case ($value === '*'): return self::T_MULTIPLY; + case ($value === '/'): return self::T_DIVIDE; + case ($value === '!'): return self::T_NEGATE; + case ($value === '{'): return self::T_OPEN_CURLY_BRACE; + case ($value === '}'): return self::T_CLOSE_CURLY_BRACE; + + // Default + default: + // Do nothing + } + + return $type; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php new file mode 100644 index 0000000..39e2a7a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parameter.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Defines a Query Parameter. + * + * @link www.doctrine-project.org + * @since 2.3 + * @author Guilherme Blanco + */ +class Parameter +{ + /** + * The parameter name. + * + * @var string + */ + private $name; + + /** + * The parameter value. + * + * @var mixed + */ + private $value; + + /** + * The parameter type. + * + * @var mixed + */ + private $type; + + /** + * Constructor. + * + * @param string $name Parameter name + * @param mixed $value Parameter value + * @param mixed $type Parameter type + */ + public function __construct($name, $value, $type = null) + { + $this->name = trim($name, ':'); + + $this->setValue($value, $type); + } + + /** + * Retrieves the Parameter name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Retrieves the Parameter value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Retrieves the Parameter type. + * + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * Defines the Parameter value. + * + * @param mixed $value Parameter value. + * @param mixed $type Parameter type. + */ + public function setValue($value, $type = null) + { + $this->value = $value; + $this->type = $type ?: ParameterTypeInferer::inferType($value); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php new file mode 100644 index 0000000..a12a559 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParameterTypeInferer.php @@ -0,0 +1,68 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type; + +/** + * Provides an enclosed support for parameter inferring. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ParameterTypeInferer +{ + /** + * Infers type of a given value, returning a compatible constant: + * - Type (\Doctrine\DBAL\Types\Type::*) + * - Connection (\Doctrine\DBAL\Connection::PARAM_*) + * + * @param mixed $value Parameter value. + * + * @return mixed Parameter type constant. + */ + public static function inferType($value) + { + if (is_integer($value)) { + return Type::INTEGER; + } + + if (is_bool($value)) { + return Type::BOOLEAN; + } + + if ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + return Type::DATETIME; + } + + if (is_array($value)) { + return is_integer(current($value)) + ? Connection::PARAM_INT_ARRAY + : Connection::PARAM_STR_ARRAY; + } + + return \PDO::PARAM_STR; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php new file mode 100644 index 0000000..695e7ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Parser.php @@ -0,0 +1,3504 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\Query; +use Doctrine\ORM\Mapping\ClassMetadata; + +/** + * An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language. + * Parses a DQL query, reports any errors in it, and generates an AST. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Janne Vanhala + * @author Fabio B. Silva + */ +class Parser +{ + /** + * READ-ONLY: Maps BUILT-IN string function names to AST class names. + * + * @var array + */ + private static $_STRING_FUNCTIONS = array( + 'concat' => 'Doctrine\ORM\Query\AST\Functions\ConcatFunction', + 'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction', + 'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction', + 'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction', + 'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction', + 'identity' => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction', + ); + + /** + * READ-ONLY: Maps BUILT-IN numeric function names to AST class names. + * + * @var array + */ + private static $_NUMERIC_FUNCTIONS = array( + 'length' => 'Doctrine\ORM\Query\AST\Functions\LengthFunction', + 'locate' => 'Doctrine\ORM\Query\AST\Functions\LocateFunction', + 'abs' => 'Doctrine\ORM\Query\AST\Functions\AbsFunction', + 'sqrt' => 'Doctrine\ORM\Query\AST\Functions\SqrtFunction', + 'mod' => 'Doctrine\ORM\Query\AST\Functions\ModFunction', + 'size' => 'Doctrine\ORM\Query\AST\Functions\SizeFunction', + 'date_diff' => 'Doctrine\ORM\Query\AST\Functions\DateDiffFunction', + 'bit_and' => 'Doctrine\ORM\Query\AST\Functions\BitAndFunction', + 'bit_or' => 'Doctrine\ORM\Query\AST\Functions\BitOrFunction', + ); + + /** + * READ-ONLY: Maps BUILT-IN datetime function names to AST class names. + * + * @var array + */ + private static $_DATETIME_FUNCTIONS = array( + 'current_date' => 'Doctrine\ORM\Query\AST\Functions\CurrentDateFunction', + 'current_time' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimeFunction', + 'current_timestamp' => 'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction', + 'date_add' => 'Doctrine\ORM\Query\AST\Functions\DateAddFunction', + 'date_sub' => 'Doctrine\ORM\Query\AST\Functions\DateSubFunction', + ); + + /* + * Expressions that were encountered during parsing of identifiers and expressions + * and still need to be validated. + */ + + /** + * @var array + */ + private $deferredIdentificationVariables = array(); + + /** + * @var array + */ + private $deferredPartialObjectExpressions = array(); + + /** + * @var array + */ + private $deferredPathExpressions = array(); + + /** + * @var array + */ + private $deferredResultVariables = array(); + + /** + * @var array + */ + private $deferredNewObjectExpressions = array(); + + /** + * The lexer. + * + * @var \Doctrine\ORM\Query\Lexer + */ + private $lexer; + + /** + * The parser result. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $parserResult; + + /** + * The EntityManager. + * + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The Query to parse. + * + * @var Query + */ + private $query; + + /** + * Map of declared query components in the parsed query. + * + * @var array + */ + private $queryComponents = array(); + + /** + * Keeps the nesting level of defined ResultVariables. + * + * @var integer + */ + private $nestingLevel = 0; + + /** + * Any additional custom tree walkers that modify the AST. + * + * @var array + */ + private $customTreeWalkers = array(); + + /** + * The custom last tree walker, if any, that is responsible for producing the output. + * + * @var TreeWalker + */ + private $customOutputWalker; + + /** + * @var array + */ + private $identVariableExpressions = array(); + + /** + * Checks if a function is internally defined. Used to prevent overwriting + * of built-in functions through user-defined functions. + * + * @param string $functionName + * + * @return bool + */ + static public function isInternalFunction($functionName) + { + $functionName = strtolower($functionName); + + return isset(self::$_STRING_FUNCTIONS[$functionName]) + || isset(self::$_DATETIME_FUNCTIONS[$functionName]) + || isset(self::$_NUMERIC_FUNCTIONS[$functionName]); + } + + /** + * Creates a new query parser object. + * + * @param Query $query The Query to parse. + */ + public function __construct(Query $query) + { + $this->query = $query; + $this->em = $query->getEntityManager(); + $this->lexer = new Lexer($query->getDql()); + $this->parserResult = new ParserResult(); + } + + /** + * Sets a custom tree walker that produces output. + * This tree walker will be run last over the AST, after any other walkers. + * + * @param string $className + * + * @return void + */ + public function setCustomOutputTreeWalker($className) + { + $this->customOutputWalker = $className; + } + + /** + * Adds a custom tree walker for modifying the AST. + * + * @param string $className + * + * @return void + */ + public function addCustomTreeWalker($className) + { + $this->customTreeWalkers[] = $className; + } + + /** + * Gets the lexer used by the parser. + * + * @return \Doctrine\ORM\Query\Lexer + */ + public function getLexer() + { + return $this->lexer; + } + + /** + * Gets the ParserResult that is being filled with information during parsing. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + public function getParserResult() + { + return $this->parserResult; + } + + /** + * Gets the EntityManager used by the parser. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Parses and builds AST for the given Query. + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function getAST() + { + // Parse & build AST + $AST = $this->QueryLanguage(); + + // Process any deferred validations of some nodes in the AST. + // This also allows post-processing of the AST for modification purposes. + $this->processDeferredIdentificationVariables(); + + if ($this->deferredPartialObjectExpressions) { + $this->processDeferredPartialObjectExpressions(); + } + + if ($this->deferredPathExpressions) { + $this->processDeferredPathExpressions($AST); + } + + if ($this->deferredResultVariables) { + $this->processDeferredResultVariables(); + } + + if ($this->deferredNewObjectExpressions) { + $this->processDeferredNewObjectExpressions($AST); + } + + $this->processRootEntityAliasSelected(); + + // TODO: Is there a way to remove this? It may impact the mixed hydration resultset a lot! + $this->fixIdentificationVariableOrder($AST); + + return $AST; + } + + /** + * Attempts to match the given token with the current lookahead token. + * + * If they match, updates the lookahead token; otherwise raises a syntax + * error. + * + * @param int $token The token type. + * + * @return void + * + * @throws QueryException If the tokens don't match. + */ + public function match($token) + { + $lookaheadType = $this->lexer->lookahead['type']; + + // short-circuit on first condition, usually types match + if ($lookaheadType !== $token && $token !== Lexer::T_IDENTIFIER && $lookaheadType <= Lexer::T_IDENTIFIER) { + $this->syntaxError($this->lexer->getLiteral($token)); + } + + $this->lexer->moveNext(); + } + + /** + * Frees this parser, enabling it to be reused. + * + * @param boolean $deep Whether to clean peek and reset errors. + * @param integer $position Position to reset. + * + * @return void + */ + public function free($deep = false, $position = 0) + { + // WARNING! Use this method with care. It resets the scanner! + $this->lexer->resetPosition($position); + + // Deep = true cleans peek and also any previously defined errors + if ($deep) { + $this->lexer->resetPeek(); + } + + $this->lexer->token = null; + $this->lexer->lookahead = null; + } + + /** + * Parses a query string. + * + * @return ParserResult + */ + public function parse() + { + $AST = $this->getAST(); + + if (($customWalkers = $this->query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) { + $this->customTreeWalkers = $customWalkers; + } + + if (($customOutputWalker = $this->query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) { + $this->customOutputWalker = $customOutputWalker; + } + + // Run any custom tree walkers over the AST + if ($this->customTreeWalkers) { + $treeWalkerChain = new TreeWalkerChain($this->query, $this->parserResult, $this->queryComponents); + + foreach ($this->customTreeWalkers as $walker) { + $treeWalkerChain->addTreeWalker($walker); + } + + switch (true) { + case ($AST instanceof AST\UpdateStatement): + $treeWalkerChain->walkUpdateStatement($AST); + break; + + case ($AST instanceof AST\DeleteStatement): + $treeWalkerChain->walkDeleteStatement($AST); + break; + + case ($AST instanceof AST\SelectStatement): + default: + $treeWalkerChain->walkSelectStatement($AST); + } + + $this->queryComponents = $treeWalkerChain->getQueryComponents(); + } + + $outputWalkerClass = $this->customOutputWalker ?: __NAMESPACE__ . '\SqlWalker'; + $outputWalker = new $outputWalkerClass($this->query, $this->parserResult, $this->queryComponents); + + // Assign an SQL executor to the parser result + $this->parserResult->setSqlExecutor($outputWalker->getExecutor($AST)); + + return $this->parserResult; + } + + /** + * Fixes order of identification variables. + * + * They have to appear in the select clause in the same order as the + * declarations (from ... x join ... y join ... z ...) appear in the query + * as the hydration process relies on that order for proper operation. + * + * @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST + * + * @return void + */ + private function fixIdentificationVariableOrder($AST) + { + if (count($this->identVariableExpressions) <= 1) { + return; + } + + foreach ($this->queryComponents as $dqlAlias => $qComp) { + if ( ! isset($this->identVariableExpressions[$dqlAlias])) { + continue; + } + + $expr = $this->identVariableExpressions[$dqlAlias]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + + unset($AST->selectClause->selectExpressions[$key]); + + $AST->selectClause->selectExpressions[] = $expr; + } + } + + /** + * Generates a new syntax error. + * + * @param string $expected Expected string. + * @param array|null $token Got token. + * + * @return void + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function syntaxError($expected = '', $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $tokenPos = (isset($token['position'])) ? $token['position'] : '-1'; + + $message = "line 0, col {$tokenPos}: Error: "; + $message .= ($expected !== '') ? "Expected {$expected}, got " : 'Unexpected '; + $message .= ($this->lexer->lookahead === null) ? 'end of string.' : "'{$token['value']}'"; + + throw QueryException::syntaxError($message, QueryException::dqlError($this->query->getDQL())); + } + + /** + * Generates a new semantical error. + * + * @param string $message Optional message. + * @param array|null $token Optional token. + * + * @return void + * + * @throws \Doctrine\ORM\Query\QueryException + */ + public function semanticalError($message = '', $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + // Minimum exposed chars ahead of token + $distance = 12; + + // Find a position of a final word to display in error string + $dql = $this->query->getDql(); + $length = strlen($dql); + $pos = $token['position'] + $distance; + $pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length); + $length = ($pos !== false) ? $pos - $token['position'] : $distance; + + $tokenPos = (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1'; + $tokenStr = substr($dql, $token['position'], $length); + + // Building informative message + $message = 'line 0, col ' . $tokenPos . " near '" . $tokenStr . "': Error: " . $message; + + throw QueryException::semanticalError($message, QueryException::dqlError($this->query->getDQL())); + } + + /** + * Peeks beyond the matched closing parenthesis and returns the first token after that one. + * + * @param boolean $resetPeek Reset peek after finding the closing parenthesis. + * + * @return array + */ + private function peekBeyondClosingParenthesis($resetPeek = true) + { + $token = $this->lexer->peek(); + $numUnmatched = 1; + + while ($numUnmatched > 0 && $token !== null) { + switch ($token['type']) { + case Lexer::T_OPEN_PARENTHESIS: + ++$numUnmatched; + break; + + case Lexer::T_CLOSE_PARENTHESIS: + --$numUnmatched; + break; + + default: + // Do nothing + } + + $token = $this->lexer->peek(); + } + + if ($resetPeek) { + $this->lexer->resetPeek(); + } + + return $token; + } + + /** + * Checks if the given token indicates a mathematical operator. + * + * @param array $token + * + * @return boolean TRUE if the token is a mathematical operator, FALSE otherwise. + */ + private function isMathOperator($token) + { + return in_array($token['type'], array(Lexer::T_PLUS, Lexer::T_MINUS, Lexer::T_DIVIDE, Lexer::T_MULTIPLY)); + } + + /** + * Checks if the next-next (after lookahead) token starts a function. + * + * @return boolean TRUE if the next-next tokens start a function, FALSE otherwise. + */ + private function isFunction() + { + $lookaheadType = $this->lexer->lookahead['type']; + $peek = $this->lexer->peek(); + + $this->lexer->resetPeek(); + + return ($lookaheadType >= Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_OPEN_PARENTHESIS); + } + + /** + * Checks whether the given token type indicates an aggregate function. + * + * @param int $tokenType + * + * @return boolean TRUE if the token type is an aggregate function, FALSE otherwise. + */ + private function isAggregateFunction($tokenType) + { + return in_array($tokenType, array(Lexer::T_AVG, Lexer::T_MIN, Lexer::T_MAX, Lexer::T_SUM, Lexer::T_COUNT)); + } + + /** + * Checks whether the current lookahead token of the lexer has the type T_ALL, T_ANY or T_SOME. + * + * @return boolean + */ + private function isNextAllAnySome() + { + return in_array($this->lexer->lookahead['type'], array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME)); + } + + /** + * Validates that the given IdentificationVariable is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function processDeferredIdentificationVariables() + { + foreach ($this->deferredIdentificationVariables as $deferredItem) { + $identVariable = $deferredItem['expression']; + + // Check if IdentificationVariable exists in queryComponents + if ( ! isset($this->queryComponents[$identVariable])) { + $this->semanticalError( + "'$identVariable' is not defined.", $deferredItem['token'] + ); + } + + $qComp = $this->queryComponents[$identVariable]; + + // Check if queryComponent points to an AbstractSchemaName or a ResultVariable + if ( ! isset($qComp['metadata'])) { + $this->semanticalError( + "'$identVariable' does not point to a Class.", $deferredItem['token'] + ); + } + + // Validate if identification variable nesting level is lower or equal than the current one + if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { + $this->semanticalError( + "'$identVariable' is used outside the scope of its declaration.", $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given NewObjectExpression. + * + * @param \Doctrine\ORM\Query\AST\SelectClause $AST + * + * @return void + */ + private function processDeferredNewObjectExpressions($AST) + { + foreach ($this->deferredNewObjectExpressions as $deferredItem) { + $expression = $deferredItem['expression']; + $token = $deferredItem['token']; + $className = $expression->className; + $args = $expression->args; + $fromClassName = isset($AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName) + ? $AST->fromClause->identificationVariableDeclarations[0]->rangeVariableDeclaration->abstractSchemaName + : null; + + // If the namespace is not given then assumes the first FROM entity namespace + if (strpos($className, '\\') === false && ! class_exists($className) && strpos($fromClassName, '\\') !== false) { + $namespace = substr($fromClassName, 0 , strrpos($fromClassName, '\\')); + $fqcn = $namespace . '\\' . $className; + + if (class_exists($fqcn)) { + $expression->className = $fqcn; + $className = $fqcn; + } + } + + if ( ! class_exists($className)) { + $this->semanticalError(sprintf('Class "%s" is not defined.', $className), $token); + } + + $class = new \ReflectionClass($className); + + if ( ! $class->isInstantiable()) { + $this->semanticalError(sprintf('Class "%s" can not be instantiated.', $className), $token); + } + + if ($class->getConstructor() === null) { + $this->semanticalError(sprintf('Class "%s" has not a valid constructor.', $className), $token); + } + + if ($class->getConstructor()->getNumberOfRequiredParameters() > count($args)) { + $this->semanticalError(sprintf('Number of arguments does not match with "%s" constructor declaration.', $className), $token); + } + } + } + + /** + * Validates that the given PartialObjectExpression is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function processDeferredPartialObjectExpressions() + { + foreach ($this->deferredPartialObjectExpressions as $deferredItem) { + $expr = $deferredItem['expression']; + $class = $this->queryComponents[$expr->identificationVariable]['metadata']; + + foreach ($expr->partialFieldSet as $field) { + if (isset($class->fieldMappings[$field])) { + continue; + } + + if (isset($class->associationMappings[$field]) && + $class->associationMappings[$field]['isOwningSide'] && + $class->associationMappings[$field]['type'] & ClassMetadata::TO_ONE) { + continue; + } + + $this->semanticalError( + "There is no mapped field named '$field' on class " . $class->name . ".", $deferredItem['token'] + ); + } + + if (array_intersect($class->identifier, $expr->partialFieldSet) != $class->identifier) { + $this->semanticalError( + "The partial field selection of class " . $class->name . " must contain the identifier.", + $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given ResultVariable is semantically correct. + * It must exist in query components list. + * + * @return void + */ + private function processDeferredResultVariables() + { + foreach ($this->deferredResultVariables as $deferredItem) { + $resultVariable = $deferredItem['expression']; + + // Check if ResultVariable exists in queryComponents + if ( ! isset($this->queryComponents[$resultVariable])) { + $this->semanticalError( + "'$resultVariable' is not defined.", $deferredItem['token'] + ); + } + + $qComp = $this->queryComponents[$resultVariable]; + + // Check if queryComponent points to an AbstractSchemaName or a ResultVariable + if ( ! isset($qComp['resultVariable'])) { + $this->semanticalError( + "'$resultVariable' does not point to a ResultVariable.", $deferredItem['token'] + ); + } + + // Validate if identification variable nesting level is lower or equal than the current one + if ($qComp['nestingLevel'] > $deferredItem['nestingLevel']) { + $this->semanticalError( + "'$resultVariable' is used outside the scope of its declaration.", $deferredItem['token'] + ); + } + } + } + + /** + * Validates that the given PathExpression is semantically correct for grammar rules: + * + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * StateFieldPathExpression ::= IdentificationVariable "." StateField + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * + * @param mixed $AST + * + * @return void + */ + private function processDeferredPathExpressions($AST) + { + foreach ($this->deferredPathExpressions as $deferredItem) { + $pathExpression = $deferredItem['expression']; + + $qComp = $this->queryComponents[$pathExpression->identificationVariable]; + $class = $qComp['metadata']; + + if (($field = $pathExpression->field) === null) { + $field = $pathExpression->field = $class->identifier[0]; + } + + // Check if field or association exists + if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) { + $this->semanticalError( + 'Class ' . $class->name . ' has no field or association named ' . $field, + $deferredItem['token'] + ); + } + + $fieldType = AST\PathExpression::TYPE_STATE_FIELD; + + if (isset($class->associationMappings[$field])) { + $assoc = $class->associationMappings[$field]; + + $fieldType = ($assoc['type'] & ClassMetadata::TO_ONE) + ? AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + : AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION; + } + + // Validate if PathExpression is one of the expected types + $expectedType = $pathExpression->expectedType; + + if ( ! ($expectedType & $fieldType)) { + // We need to recognize which was expected type(s) + $expectedStringTypes = array(); + + // Validate state field type + if ($expectedType & AST\PathExpression::TYPE_STATE_FIELD) { + $expectedStringTypes[] = 'StateFieldPathExpression'; + } + + // Validate single valued association (*-to-one) + if ($expectedType & AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) { + $expectedStringTypes[] = 'SingleValuedAssociationField'; + } + + // Validate single valued association (*-to-many) + if ($expectedType & AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) { + $expectedStringTypes[] = 'CollectionValuedAssociationField'; + } + + // Build the error message + $semanticalError = 'Invalid PathExpression. '; + $semanticalError .= (count($expectedStringTypes) == 1) + ? 'Must be a ' . $expectedStringTypes[0] . '.' + : implode(' or ', $expectedStringTypes) . ' expected.'; + + $this->semanticalError($semanticalError, $deferredItem['token']); + } + + // We need to force the type in PathExpression + $pathExpression->type = $fieldType; + } + } + + /** + * @return void + */ + private function processRootEntityAliasSelected() + { + if ( ! count($this->identVariableExpressions)) { + return; + } + + $foundRootEntity = false; + + foreach ($this->identVariableExpressions as $dqlAlias => $expr) { + if (isset($this->queryComponents[$dqlAlias]) && $this->queryComponents[$dqlAlias]['parent'] === null) { + $foundRootEntity = true; + } + } + + if ( ! $foundRootEntity) { + $this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.'); + } + } + + /** + * QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement + * + * @return \Doctrine\ORM\Query\AST\SelectStatement | + * \Doctrine\ORM\Query\AST\UpdateStatement | + * \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function QueryLanguage() + { + $this->lexer->moveNext(); + + switch ($this->lexer->lookahead['type']) { + case Lexer::T_SELECT: + $statement = $this->SelectStatement(); + break; + + case Lexer::T_UPDATE: + $statement = $this->UpdateStatement(); + break; + + case Lexer::T_DELETE: + $statement = $this->DeleteStatement(); + break; + + default: + $this->syntaxError('SELECT, UPDATE or DELETE'); + break; + } + + // Check for end of string + if ($this->lexer->lookahead !== null) { + $this->syntaxError('end of string'); + } + + return $statement; + } + + /** + * SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @return \Doctrine\ORM\Query\AST\SelectStatement + */ + public function SelectStatement() + { + $selectStatement = new AST\SelectStatement($this->SelectClause(), $this->FromClause()); + + $selectStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $selectStatement->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $selectStatement->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $selectStatement->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + + return $selectStatement; + } + + /** + * UpdateStatement ::= UpdateClause [WhereClause] + * + * @return \Doctrine\ORM\Query\AST\UpdateStatement + */ + public function UpdateStatement() + { + $updateStatement = new AST\UpdateStatement($this->UpdateClause()); + + $updateStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + + return $updateStatement; + } + + /** + * DeleteStatement ::= DeleteClause [WhereClause] + * + * @return \Doctrine\ORM\Query\AST\DeleteStatement + */ + public function DeleteStatement() + { + $deleteStatement = new AST\DeleteStatement($this->DeleteClause()); + + $deleteStatement->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + + return $deleteStatement; + } + + /** + * IdentificationVariable ::= identifier + * + * @return string + */ + public function IdentificationVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $identVariable = $this->lexer->token['value']; + + $this->deferredIdentificationVariables[] = array( + 'expression' => $identVariable, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $identVariable; + } + + /** + * AliasIdentificationVariable = identifier + * + * @return string + */ + public function AliasIdentificationVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $aliasIdentVariable = $this->lexer->token['value']; + $exists = isset($this->queryComponents[$aliasIdentVariable]); + + if ($exists) { + $this->semanticalError("'$aliasIdentVariable' is already defined.", $this->lexer->token); + } + + return $aliasIdentVariable; + } + + /** + * AbstractSchemaName ::= identifier + * + * @return string + */ + public function AbstractSchemaName() + { + $this->match(Lexer::T_IDENTIFIER); + + $schemaName = ltrim($this->lexer->token['value'], '\\'); + + if (strrpos($schemaName, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $schemaName); + + $schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $exists = class_exists($schemaName, true) || interface_exists($schemaName, true); + + if ( ! $exists) { + $this->semanticalError("Class '$schemaName' is not defined.", $this->lexer->token); + } + + return $schemaName; + } + + /** + * AliasResultVariable ::= identifier + * + * @return string + */ + public function AliasResultVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $resultVariable = $this->lexer->token['value']; + $exists = isset($this->queryComponents[$resultVariable]); + + if ($exists) { + $this->semanticalError("'$resultVariable' is already defined.", $this->lexer->token); + } + + return $resultVariable; + } + + /** + * ResultVariable ::= identifier + * + * @return string + */ + public function ResultVariable() + { + $this->match(Lexer::T_IDENTIFIER); + + $resultVariable = $this->lexer->token['value']; + + // Defer ResultVariable validation + $this->deferredResultVariables[] = array( + 'expression' => $resultVariable, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $resultVariable; + } + + /** + * JoinAssociationPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + * + * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression + */ + public function JoinAssociationPathExpression() + { + $identVariable = $this->IdentificationVariable(); + + if ( ! isset($this->queryComponents[$identVariable])) { + $this->semanticalError( + 'Identification Variable ' . $identVariable .' used in join path expression but was not defined before.' + ); + } + + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->lexer->token['value']; + + // Validate association field + $qComp = $this->queryComponents[$identVariable]; + $class = $qComp['metadata']; + + if ( ! $class->hasAssociation($field)) { + $this->semanticalError('Class ' . $class->name . ' has no association named ' . $field); + } + + return new AST\JoinAssociationPathExpression($identVariable, $field); + } + + /** + * Parses an arbitrary path expression and defers semantical validation + * based on expected types. + * + * PathExpression ::= IdentificationVariable {"." identifier}* + * + * @param integer $expectedTypes + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function PathExpression($expectedTypes) + { + $identVariable = $this->IdentificationVariable(); + $field = null; + + if ($this->lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->lexer->token['value']; + + while ($this->lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + $field .= '.'.$this->lexer->token['value']; + } + } + + // Creating AST node + $pathExpr = new AST\PathExpression($expectedTypes, $identVariable, $field); + + // Defer PathExpression validation if requested to be deferred + $this->deferredPathExpressions[] = array( + 'expression' => $pathExpr, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $pathExpr; + } + + /** + * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function AssociationPathExpression() + { + return $this->PathExpression( + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION | + AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION + ); + } + + /** + * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function SingleValuedPathExpression() + { + return $this->PathExpression( + AST\PathExpression::TYPE_STATE_FIELD | + AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION + ); + } + + /** + * StateFieldPathExpression ::= IdentificationVariable "." StateField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function StateFieldPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_STATE_FIELD); + } + + /** + * SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function SingleValuedAssociationPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION); + } + + /** + * CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField + * + * @return \Doctrine\ORM\Query\AST\PathExpression + */ + public function CollectionValuedPathExpression() + { + return $this->PathExpression(AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION); + } + + /** + * SelectClause ::= "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression} + * + * @return \Doctrine\ORM\Query\AST\SelectClause + */ + public function SelectClause() + { + $isDistinct = false; + $this->match(Lexer::T_SELECT); + + // Check for DISTINCT + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + + $isDistinct = true; + } + + // Process SelectExpressions (1..N) + $selectExpressions = array(); + $selectExpressions[] = $this->SelectExpression(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $selectExpressions[] = $this->SelectExpression(); + } + + return new AST\SelectClause($selectExpressions, $isDistinct); + } + + /** + * SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectClause + */ + public function SimpleSelectClause() + { + $isDistinct = false; + $this->match(Lexer::T_SELECT); + + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + + $isDistinct = true; + } + + return new AST\SimpleSelectClause($this->SimpleSelectExpression(), $isDistinct); + } + + /** + * UpdateClause ::= "UPDATE" AbstractSchemaName ["AS"] AliasIdentificationVariable "SET" UpdateItem {"," UpdateItem}* + * + * @return \Doctrine\ORM\Query\AST\UpdateClause + */ + public function UpdateClause() + { + $this->match(Lexer::T_UPDATE); + $token = $this->lexer->lookahead; + $abstractSchemaName = $this->AbstractSchemaName(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + $class = $this->em->getClassMetadata($abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $class, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; + + $this->match(Lexer::T_SET); + + $updateItems = array(); + $updateItems[] = $this->UpdateItem(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $updateItems[] = $this->UpdateItem(); + } + + $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems); + $updateClause->aliasIdentificationVariable = $aliasIdentificationVariable; + + return $updateClause; + } + + /** + * DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @return \Doctrine\ORM\Query\AST\DeleteClause + */ + public function DeleteClause() + { + $this->match(Lexer::T_DELETE); + + if ($this->lexer->isNextToken(Lexer::T_FROM)) { + $this->match(Lexer::T_FROM); + } + + $token = $this->lexer->lookahead; + $deleteClause = new AST\DeleteClause($this->AbstractSchemaName()); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + + $deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable; + $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $class, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; + + return $deleteClause; + } + + /** + * FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\FromClause + */ + public function FromClause() + { + $this->match(Lexer::T_FROM); + + $identificationVariableDeclarations = array(); + $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $identificationVariableDeclarations[] = $this->IdentificationVariableDeclaration(); + } + + return new AST\FromClause($identificationVariableDeclarations); + } + + /** + * SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}* + * + * @return \Doctrine\ORM\Query\AST\SubselectFromClause + */ + public function SubselectFromClause() + { + $this->match(Lexer::T_FROM); + + $identificationVariables = array(); + $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $identificationVariables[] = $this->SubselectIdentificationVariableDeclaration(); + } + + return new AST\SubselectFromClause($identificationVariables); + } + + /** + * WhereClause ::= "WHERE" ConditionalExpression + * + * @return \Doctrine\ORM\Query\AST\WhereClause + */ + public function WhereClause() + { + $this->match(Lexer::T_WHERE); + + return new AST\WhereClause($this->ConditionalExpression()); + } + + /** + * HavingClause ::= "HAVING" ConditionalExpression + * + * @return \Doctrine\ORM\Query\AST\HavingClause + */ + public function HavingClause() + { + $this->match(Lexer::T_HAVING); + + return new AST\HavingClause($this->ConditionalExpression()); + } + + /** + * GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}* + * + * @return \Doctrine\ORM\Query\AST\GroupByClause + */ + public function GroupByClause() + { + $this->match(Lexer::T_GROUP); + $this->match(Lexer::T_BY); + + $groupByItems = array($this->GroupByItem()); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $groupByItems[] = $this->GroupByItem(); + } + + return new AST\GroupByClause($groupByItems); + } + + /** + * OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + * + * @return \Doctrine\ORM\Query\AST\OrderByClause + */ + public function OrderByClause() + { + $this->match(Lexer::T_ORDER); + $this->match(Lexer::T_BY); + + $orderByItems = array(); + $orderByItems[] = $this->OrderByItem(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $orderByItems[] = $this->OrderByItem(); + } + + return new AST\OrderByClause($orderByItems); + } + + /** + * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] + * + * @return \Doctrine\ORM\Query\AST\Subselect + */ + public function Subselect() + { + // Increase query nesting level + $this->nestingLevel++; + + $subselect = new AST\Subselect($this->SimpleSelectClause(), $this->SubselectFromClause()); + + $subselect->whereClause = $this->lexer->isNextToken(Lexer::T_WHERE) ? $this->WhereClause() : null; + $subselect->groupByClause = $this->lexer->isNextToken(Lexer::T_GROUP) ? $this->GroupByClause() : null; + $subselect->havingClause = $this->lexer->isNextToken(Lexer::T_HAVING) ? $this->HavingClause() : null; + $subselect->orderByClause = $this->lexer->isNextToken(Lexer::T_ORDER) ? $this->OrderByClause() : null; + + // Decrease query nesting level + $this->nestingLevel--; + + return $subselect; + } + + /** + * UpdateItem ::= SingleValuedPathExpression "=" NewValue + * + * @return \Doctrine\ORM\Query\AST\UpdateItem + */ + public function UpdateItem() + { + $pathExpr = $this->SingleValuedPathExpression(); + + $this->match(Lexer::T_EQUALS); + + $updateItem = new AST\UpdateItem($pathExpr, $this->NewValue()); + + return $updateItem; + } + + /** + * GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression + * + * @return string | \Doctrine\ORM\Query\AST\PathExpression + */ + public function GroupByItem() + { + // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] === Lexer::T_DOT) { + return $this->SingleValuedPathExpression(); + } + + // Still need to decide between IdentificationVariable or ResultVariable + $lookaheadValue = $this->lexer->lookahead['value']; + + if ( ! isset($this->queryComponents[$lookaheadValue])) { + $this->semanticalError('Cannot group by undefined identification or result variable.'); + } + + return (isset($this->queryComponents[$lookaheadValue]['metadata'])) + ? $this->IdentificationVariable() + : $this->ResultVariable(); + } + + /** + * OrderByItem ::= ( + * SimpleArithmeticExpression | SingleValuedPathExpression | + * ScalarExpression | ResultVariable | FunctionDeclaration + * ) ["ASC" | "DESC"] + * + * @return \Doctrine\ORM\Query\AST\OrderByItem + */ + public function OrderByItem() + { + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + + $this->lexer->resetPeek(); + + $glimpse = $this->lexer->glimpse(); + + switch (true) { + case ($this->isFunction($peek)): + $expr = $this->FunctionDeclaration(); + break; + + case ($this->isMathOperator($peek)): + $expr = $this->SimpleArithmeticExpression(); + break; + + case ($glimpse['type'] === Lexer::T_DOT): + $expr = $this->SingleValuedPathExpression(); + break; + + case ($this->lexer->peek() && $this->isMathOperator($this->peekBeyondClosingParenthesis())): + $expr = $this->ScalarExpression(); + break; + + default: + $expr = $this->ResultVariable(); + break; + } + + $type = 'ASC'; + $item = new AST\OrderByItem($expr); + + switch (true) { + case ($this->lexer->isNextToken(Lexer::T_DESC)): + $this->match(Lexer::T_DESC); + $type = 'DESC'; + break; + + case ($this->lexer->isNextToken(Lexer::T_ASC)): + $this->match(Lexer::T_ASC); + break; + + default: + // Do nothing + } + + $item->type = $type; + + return $item; + } + + /** + * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | + * EnumPrimary | SimpleEntityExpression | "NULL" + * + * NOTE: Since it is not possible to correctly recognize individual types, here is the full + * grammar that needs to be supported: + * + * NewValue ::= SimpleArithmeticExpression | "NULL" + * + * SimpleArithmeticExpression covers all *Primary grammar rules and also SimpleEntityExpression + * + * @return AST\ArithmeticExpression + */ + public function NewValue() + { + if ($this->lexer->isNextToken(Lexer::T_NULL)) { + $this->match(Lexer::T_NULL); + + return null; + } + + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->lexer->token['value']); + } + + return $this->ArithmeticExpression(); + } + + /** + * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}* + * + * @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function IdentificationVariableDeclaration() + { + $joins = array(); + $rangeVariableDeclaration = $this->RangeVariableDeclaration(); + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) + ? $this->IndexBy() + : null; + + $rangeVariableDeclaration->isRoot = true; + + while ( + $this->lexer->isNextToken(Lexer::T_LEFT) || + $this->lexer->isNextToken(Lexer::T_INNER) || + $this->lexer->isNextToken(Lexer::T_JOIN) + ) { + $joins[] = $this->Join(); + } + + return new AST\IdentificationVariableDeclaration( + $rangeVariableDeclaration, $indexBy, $joins + ); + } + + /** + * + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration + * + * {Internal note: WARNING: Solution is harder than a bare implementation. + * Desired EBNF support: + * + * SubselectIdentificationVariableDeclaration ::= IdentificationVariableDeclaration | (AssociationPathExpression ["AS"] AliasIdentificationVariable) + * + * It demands that entire SQL generation to become programmatical. This is + * needed because association based subselect requires "WHERE" conditional + * expressions to be injected, but there is no scope to do that. Only scope + * accessible is "FROM", prohibiting an easy implementation without larger + * changes.} + * + * @return \Doctrine\ORM\Query\AST\SubselectIdentificationVariableDeclaration | + * \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration + */ + public function SubselectIdentificationVariableDeclaration() + { + /* + NOT YET IMPLEMENTED! + + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] == Lexer::T_DOT) { + $associationPathExpression = $this->AssociationPathExpression(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $identificationVariable = $associationPathExpression->identificationVariable; + $field = $associationPathExpression->associationField; + + $class = $this->queryComponents[$identificationVariable]['metadata']; + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + + // Building queryComponent + $joinQueryComponent = array( + 'metadata' => $targetClass, + 'parent' => $identificationVariable, + 'relation' => $class->getAssociationMapping($field), + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->lookahead + ); + + $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + + return new AST\SubselectIdentificationVariableDeclaration( + $associationPathExpression, $aliasIdentificationVariable + ); + } + */ + + return $this->IdentificationVariableDeclaration(); + } + + /** + * Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" + * (JoinAssociationDeclaration | RangeVariableDeclaration) + * ["WITH" ConditionalExpression] + * + * @return \Doctrine\ORM\Query\AST\Join + */ + public function Join() + { + // Check Join type + $joinType = AST\Join::JOIN_TYPE_INNER; + + switch (true) { + case ($this->lexer->isNextToken(Lexer::T_LEFT)): + $this->match(Lexer::T_LEFT); + + $joinType = AST\Join::JOIN_TYPE_LEFT; + + // Possible LEFT OUTER join + if ($this->lexer->isNextToken(Lexer::T_OUTER)) { + $this->match(Lexer::T_OUTER); + + $joinType = AST\Join::JOIN_TYPE_LEFTOUTER; + } + break; + + case ($this->lexer->isNextToken(Lexer::T_INNER)): + $this->match(Lexer::T_INNER); + break; + + default: + // Do nothing + } + + $this->match(Lexer::T_JOIN); + + $next = $this->lexer->glimpse(); + $joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration(); + $adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH); + $join = new AST\Join($joinType, $joinDeclaration); + + // Describe non-root join declaration + if ($joinDeclaration instanceof AST\RangeVariableDeclaration) { + $joinDeclaration->isRoot = false; + + $adhocConditions = true; + } + + // Check for ad-hoc Join conditions + if ($adhocConditions) { + $this->match(Lexer::T_WITH); + + $join->conditionalExpression = $this->ConditionalExpression(); + } + + return $join; + } + + /** + * RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable + * + * @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration + */ + public function RangeVariableDeclaration() + { + $abstractSchemaName = $this->AbstractSchemaName(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $token = $this->lexer->lookahead; + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $classMetadata = $this->em->getClassMetadata($abstractSchemaName); + + // Building queryComponent + $queryComponent = array( + 'metadata' => $classMetadata, + 'parent' => null, + 'relation' => null, + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token + ); + + $this->queryComponents[$aliasIdentificationVariable] = $queryComponent; + + return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable); + } + + /** + * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy] + * + * @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression + */ + public function JoinAssociationDeclaration() + { + $joinAssociationPathExpression = $this->JoinAssociationPathExpression(); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $aliasIdentificationVariable = $this->AliasIdentificationVariable(); + $indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null; + + $identificationVariable = $joinAssociationPathExpression->identificationVariable; + $field = $joinAssociationPathExpression->associationField; + + $class = $this->queryComponents[$identificationVariable]['metadata']; + $targetClass = $this->em->getClassMetadata($class->associationMappings[$field]['targetEntity']); + + // Building queryComponent + $joinQueryComponent = array( + 'metadata' => $targetClass, + 'parent' => $joinAssociationPathExpression->identificationVariable, + 'relation' => $class->getAssociationMapping($field), + 'map' => null, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->lookahead + ); + + $this->queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; + + return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy); + } + + /** + * PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet + * PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}" + * + * @return array + */ + public function PartialObjectExpression() + { + $this->match(Lexer::T_PARTIAL); + + $partialFieldSet = array(); + + $identificationVariable = $this->IdentificationVariable(); + + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_OPEN_CURLY_BRACE); + $this->match(Lexer::T_IDENTIFIER); + + $partialFieldSet[] = $this->lexer->token['value']; + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + $this->match(Lexer::T_IDENTIFIER); + + $field = $this->lexer->token['value']; + + while ($this->lexer->isNextToken(Lexer::T_DOT)) { + $this->match(Lexer::T_DOT); + $this->match(Lexer::T_IDENTIFIER); + $field .= '.'.$this->lexer->token['value']; + } + + $partialFieldSet[] = $field; + } + + $this->match(Lexer::T_CLOSE_CURLY_BRACE); + + $partialObjectExpression = new AST\PartialObjectExpression($identificationVariable, $partialFieldSet); + + // Defer PartialObjectExpression validation + $this->deferredPartialObjectExpressions[] = array( + 'expression' => $partialObjectExpression, + 'nestingLevel' => $this->nestingLevel, + 'token' => $this->lexer->token, + ); + + return $partialObjectExpression; + } + + /** + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" + * + * @return \Doctrine\ORM\Query\AST\NewObjectExpression + */ + public function NewObjectExpression() + { + $this->match(Lexer::T_NEW); + $this->match(Lexer::T_IDENTIFIER); + + $token = $this->lexer->token; + $className = $token['value']; + + if (strrpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className); + + $className = $this->em->getConfiguration() + ->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $args[] = $this->NewObjectArg(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $args[] = $this->NewObjectArg(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $expression = new AST\NewObjectExpression($className, $args); + + // Defer NewObjectExpression validation + $this->deferredNewObjectExpressions[] = array( + 'token' => $token, + 'expression' => $expression, + 'nestingLevel' => $this->nestingLevel, + ); + + return $expression; + } + + /** + * NewObjectArg ::= ScalarExpression | "(" Subselect ")" + * + * @return mixed + */ + public function NewObjectArg() + { + $token = $this->lexer->lookahead; + $peek = $this->lexer->glimpse(); + + if ($token['type'] === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expression; + } + + return $this->ScalarExpression(); + } + + /** + * IndexBy ::= "INDEX" "BY" StateFieldPathExpression + * + * @return \Doctrine\ORM\Query\AST\IndexBy + */ + public function IndexBy() + { + $this->match(Lexer::T_INDEX); + $this->match(Lexer::T_BY); + $pathExpr = $this->StateFieldPathExpression(); + + // Add the INDEX BY info to the query component + $this->queryComponents[$pathExpr->identificationVariable]['map'] = $pathExpr->field; + + return new AST\IndexBy($pathExpr); + } + + /** + * ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | + * StateFieldPathExpression | BooleanPrimary | CaseExpression | + * InstanceOfExpression + * + * @return mixed One of the possible expressions or subexpressions. + */ + public function ScalarExpression() + { + $lookahead = $this->lexer->lookahead['type']; + $peek = $this->lexer->glimpse(); + + switch (true) { + case ($lookahead === Lexer::T_INTEGER): + case ($lookahead === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) or ( - 1 ) or ( + 1 ) + case ($lookahead === Lexer::T_MINUS): + case ($lookahead === Lexer::T_PLUS): + return $this->SimpleArithmeticExpression(); + + case ($lookahead === Lexer::T_STRING): + return $this->StringPrimary(); + + case ($lookahead === Lexer::T_TRUE): + case ($lookahead === Lexer::T_FALSE): + $this->match($lookahead); + + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); + + case ($lookahead === Lexer::T_INPUT_PARAMETER): + switch (true) { + case $this->isMathOperator($peek): + // :param + u.value + return $this->SimpleArithmeticExpression(); + + default: + return $this->InputParameter(); + } + + case ($lookahead === Lexer::T_CASE): + case ($lookahead === Lexer::T_COALESCE): + case ($lookahead === Lexer::T_NULLIF): + // Since NULLIF and COALESCE can be identified as a function, + // we need to check these before checking for FunctionDeclaration + return $this->CaseExpression(); + + case ($lookahead === Lexer::T_OPEN_PARENTHESIS): + return $this->SimpleArithmeticExpression(); + + // this check must be done before checking for a filed path expression + case ($this->isFunction()): + $this->lexer->peek(); // "(" + + switch (true) { + case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + return $this->SimpleArithmeticExpression(); + + case ($this->isAggregateFunction($this->lexer->lookahead['type'])): + return $this->AggregateExpression(); + + default: + // IDENTITY(u) + return $this->FunctionDeclaration(); + } + + break; + // it is no function, so it must be a field path + case ($lookahead === Lexer::T_IDENTIFIER): + $this->lexer->peek(); // lookahead => '.' + $this->lexer->peek(); // lookahead => token after '.' + $peek = $this->lexer->peek(); // lookahead => token after the token after the '.' + $this->lexer->resetPeek(); + + if ($this->isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + return $this->StateFieldPathExpression(); + + default: + $this->syntaxError(); + } + } + + /** + * CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @return mixed One of the possible expressions or subexpressions. + */ + public function CaseExpression() + { + $lookahead = $this->lexer->lookahead['type']; + + switch ($lookahead) { + case Lexer::T_NULLIF: + return $this->NullIfExpression(); + + case Lexer::T_COALESCE: + return $this->CoalesceExpression(); + + case Lexer::T_CASE: + $this->lexer->resetPeek(); + $peek = $this->lexer->peek(); + + if ($peek['type'] === Lexer::T_WHEN) { + return $this->GeneralCaseExpression(); + } + + return $this->SimpleCaseExpression(); + + default: + // Do nothing + break; + } + + $this->syntaxError(); + } + + /** + * CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")" + * + * @return \Doctrine\ORM\Query\AST\CoalesceExpression + */ + public function CoalesceExpression() + { + $this->match(Lexer::T_COALESCE); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + // Process ScalarExpressions (1..N) + $scalarExpressions = array(); + $scalarExpressions[] = $this->ScalarExpression(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $scalarExpressions[] = $this->ScalarExpression(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\CoalesceExpression($scalarExpressions); + } + + /** + * NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")" + * + * @return \Doctrine\ORM\Query\AST\NullIfExpression + */ + public function NullIfExpression() + { + $this->match(Lexer::T_NULLIF); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $firstExpression = $this->ScalarExpression(); + $this->match(Lexer::T_COMMA); + $secondExpression = $this->ScalarExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\NullIfExpression($firstExpression, $secondExpression); + } + + /** + * GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END" + * + * @return \Doctrine\ORM\Query\AST\GeneralCaseExpression + */ + public function GeneralCaseExpression() + { + $this->match(Lexer::T_CASE); + + // Process WhenClause (1..N) + $whenClauses = array(); + + do { + $whenClauses[] = $this->WhenClause(); + } while ($this->lexer->isNextToken(Lexer::T_WHEN)); + + $this->match(Lexer::T_ELSE); + $scalarExpression = $this->ScalarExpression(); + $this->match(Lexer::T_END); + + return new AST\GeneralCaseExpression($whenClauses, $scalarExpression); + } + + /** + * SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END" + * CaseOperand ::= StateFieldPathExpression | TypeDiscriminator + * + * @return AST\SimpleCaseExpression + */ + public function SimpleCaseExpression() + { + $this->match(Lexer::T_CASE); + $caseOperand = $this->StateFieldPathExpression(); + + // Process SimpleWhenClause (1..N) + $simpleWhenClauses = array(); + + do { + $simpleWhenClauses[] = $this->SimpleWhenClause(); + } while ($this->lexer->isNextToken(Lexer::T_WHEN)); + + $this->match(Lexer::T_ELSE); + $scalarExpression = $this->ScalarExpression(); + $this->match(Lexer::T_END); + + return new AST\SimpleCaseExpression($caseOperand, $simpleWhenClauses, $scalarExpression); + } + + /** + * WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression + * + * @return \Doctrine\ORM\Query\AST\WhenClause + */ + public function WhenClause() + { + $this->match(Lexer::T_WHEN); + $conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_THEN); + + return new AST\WhenClause($conditionalExpression, $this->ScalarExpression()); + } + + /** + * SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression + * + * @return \Doctrine\ORM\Query\AST\SimpleWhenClause + */ + public function SimpleWhenClause() + { + $this->match(Lexer::T_WHEN); + $conditionalExpression = $this->ScalarExpression(); + $this->match(Lexer::T_THEN); + + return new AST\SimpleWhenClause($conditionalExpression, $this->ScalarExpression()); + } + + /** + * SelectExpression ::= ( + * IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | + * PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression + * ) [["AS"] ["HIDDEN"] AliasResultVariable] + * + * @return \Doctrine\ORM\Query\AST\SelectExpression + */ + public function SelectExpression() + { + $expression = null; + $identVariable = null; + $peek = $this->lexer->glimpse(); + $lookaheadType = $this->lexer->lookahead['type']; + + switch (true) { + // ScalarExpression (u.name) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT): + $expression = $this->ScalarExpression(); + break; + + // IdentificationVariable (u) + case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $identVariable = $this->IdentificationVariable(); + break; + + // CaseExpression (CASE ... or NULLIF(...) or COALESCE(...)) + case ($lookaheadType === Lexer::T_CASE): + case ($lookaheadType === Lexer::T_COALESCE): + case ($lookaheadType === Lexer::T_NULLIF): + $expression = $this->CaseExpression(); + break; + + // DQL Function (SUM(u.value) or SUM(u.value) + 1) + case ($this->isFunction()): + $this->lexer->peek(); // "(" + + switch (true) { + case ($this->isMathOperator($this->peekBeyondClosingParenthesis())): + // SUM(u.id) + COUNT(u.id) + $expression = $this->ScalarExpression(); + break; + + case ($this->isAggregateFunction($lookaheadType)): + // COUNT(u.id) + $expression = $this->AggregateExpression(); + break; + + default: + // IDENTITY(u) + $expression = $this->FunctionDeclaration(); + break; + } + + break; + + // PartialObjectExpression (PARTIAL u.{id, name}) + case ($lookaheadType === Lexer::T_PARTIAL): + $expression = $this->PartialObjectExpression(); + $identVariable = $expression->identificationVariable; + break; + + // Subselect + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT): + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + break; + + // Shortcut: ScalarExpression => SimpleArithmeticExpression + case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS): + case ($lookaheadType === Lexer::T_INTEGER): + case ($lookaheadType === Lexer::T_STRING): + case ($lookaheadType === Lexer::T_FLOAT): + // SimpleArithmeticExpression : (- u.value ) or ( + u.value ) + case ($lookaheadType === Lexer::T_MINUS): + case ($lookaheadType === Lexer::T_PLUS): + $expression = $this->SimpleArithmeticExpression(); + break; + + // NewObjectExpression (New ClassName(id, name)) + case ($lookaheadType === Lexer::T_NEW): + $expression = $this->NewObjectExpression(); + break; + + default: + $this->syntaxError( + 'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression', + $this->lexer->lookahead + ); + } + + // [["AS"] ["HIDDEN"] AliasResultVariable] + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + $hiddenAliasResultVariable = false; + + if ($this->lexer->isNextToken(Lexer::T_HIDDEN)) { + $this->match(Lexer::T_HIDDEN); + + $hiddenAliasResultVariable = true; + } + + $aliasResultVariable = null; + + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->lexer->lookahead; + $aliasResultVariable = $this->AliasResultVariable(); + + // Include AliasResultVariable in query components. + $this->queryComponents[$aliasResultVariable] = array( + 'resultVariable' => $expression, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + } + + // AST + + $expr = new AST\SelectExpression($expression, $aliasResultVariable, $hiddenAliasResultVariable); + + if ($identVariable) { + $this->identVariableExpressions[$identVariable] = $expr; + } + + return $expr; + } + + /** + * SimpleSelectExpression ::= ( + * StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | + * AggregateExpression | "(" Subselect ")" | ScalarExpression + * ) [["AS"] AliasResultVariable] + * + * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression + */ + public function SimpleSelectExpression() + { + $peek = $this->lexer->glimpse(); + + switch ($this->lexer->lookahead['type']) { + case Lexer::T_IDENTIFIER: + switch (true) { + case ($peek['type'] === Lexer::T_DOT): + $expression = $this->StateFieldPathExpression(); + + return new AST\SimpleSelectExpression($expression); + + case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $this->IdentificationVariable(); + + return new AST\SimpleSelectExpression($expression); + + case ($this->isFunction()): + // SUM(u.id) + COUNT(u.id) + if ($this->isMathOperator($this->peekBeyondClosingParenthesis())) { + return new AST\SimpleSelectExpression($this->ScalarExpression()); + } + // COUNT(u.id) + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { + return new AST\SimpleSelectExpression($this->AggregateExpression()); + } + // IDENTITY(u) + return new AST\SimpleSelectExpression($this->FunctionDeclaration()); + + default: + // Do nothing + } + break; + + case Lexer::T_OPEN_PARENTHESIS: + if ($peek['type'] !== Lexer::T_SELECT) { + // Shortcut: ScalarExpression => SimpleArithmeticExpression + $expression = $this->SimpleArithmeticExpression(); + + return new AST\SimpleSelectExpression($expression); + } + + // Subselect + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\SimpleSelectExpression($expression); + + default: + // Do nothing + } + + $this->lexer->peek(); + + $expression = $this->ScalarExpression(); + $expr = new AST\SimpleSelectExpression($expression); + + if ($this->lexer->isNextToken(Lexer::T_AS)) { + $this->match(Lexer::T_AS); + } + + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) { + $token = $this->lexer->lookahead; + $resultVariable = $this->AliasResultVariable(); + $expr->fieldIdentificationVariable = $resultVariable; + + // Include AliasResultVariable in query components. + $this->queryComponents[$resultVariable] = array( + 'resultvariable' => $expr, + 'nestingLevel' => $this->nestingLevel, + 'token' => $token, + ); + } + + return $expr; + } + + /** + * ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}* + * + * @return \Doctrine\ORM\Query\AST\ConditionalExpression + */ + public function ConditionalExpression() + { + $conditionalTerms = array(); + $conditionalTerms[] = $this->ConditionalTerm(); + + while ($this->lexer->isNextToken(Lexer::T_OR)) { + $this->match(Lexer::T_OR); + + $conditionalTerms[] = $this->ConditionalTerm(); + } + + // Phase 1 AST optimization: Prevent AST\ConditionalExpression + // if only one AST\ConditionalTerm is defined + if (count($conditionalTerms) == 1) { + return $conditionalTerms[0]; + } + + return new AST\ConditionalExpression($conditionalTerms); + } + + /** + * ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}* + * + * @return \Doctrine\ORM\Query\AST\ConditionalTerm + */ + public function ConditionalTerm() + { + $conditionalFactors = array(); + $conditionalFactors[] = $this->ConditionalFactor(); + + while ($this->lexer->isNextToken(Lexer::T_AND)) { + $this->match(Lexer::T_AND); + + $conditionalFactors[] = $this->ConditionalFactor(); + } + + // Phase 1 AST optimization: Prevent AST\ConditionalTerm + // if only one AST\ConditionalFactor is defined + if (count($conditionalFactors) == 1) { + return $conditionalFactors[0]; + } + + return new AST\ConditionalTerm($conditionalFactors); + } + + /** + * ConditionalFactor ::= ["NOT"] ConditionalPrimary + * + * @return \Doctrine\ORM\Query\AST\ConditionalFactor + */ + public function ConditionalFactor() + { + $not = false; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $not = true; + } + + $conditionalPrimary = $this->ConditionalPrimary(); + + // Phase 1 AST optimization: Prevent AST\ConditionalFactor + // if only one AST\ConditionalPrimary is defined + if ( ! $not) { + return $conditionalPrimary; + } + + $conditionalFactor = new AST\ConditionalFactor($conditionalPrimary); + $conditionalFactor->not = $not; + + return $conditionalFactor; + } + + /** + * ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" + * + * @return \Doctrine\ORM\Query\AST\ConditionalPrimary + */ + public function ConditionalPrimary() + { + $condPrimary = new AST\ConditionalPrimary; + + if ( ! $this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + // Peek beyond the matching closing parenthesis ')' + $peek = $this->peekBeyondClosingParenthesis(); + + if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || + in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS)) || + $this->isMathOperator($peek)) { + $condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression(); + + return $condPrimary; + } + + $this->match(Lexer::T_OPEN_PARENTHESIS); + $condPrimary->conditionalExpression = $this->ConditionalExpression(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $condPrimary; + } + + /** + * SimpleConditionalExpression ::= + * ComparisonExpression | BetweenExpression | LikeExpression | + * InExpression | NullComparisonExpression | ExistsExpression | + * EmptyCollectionComparisonExpression | CollectionMemberExpression | + * InstanceOfExpression + */ + public function SimpleConditionalExpression() + { + if ($this->lexer->isNextToken(Lexer::T_EXISTS)) { + return $this->ExistsExpression(); + } + + $token = $this->lexer->lookahead; + $peek = $this->lexer->glimpse(); + $lookahead = $token; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $token = $this->lexer->glimpse(); + } + + if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER || $this->isFunction()) { + // Peek beyond the matching closing parenthesis. + $beyond = $this->lexer->peek(); + + switch ($peek['value']) { + case '(': + // Peeks beyond the matched closing parenthesis. + $token = $this->peekBeyondClosingParenthesis(false); + + if ($token['type'] === Lexer::T_NOT) { + $token = $this->lexer->peek(); + } + + if ($token['type'] === Lexer::T_IS) { + $lookahead = $this->lexer->peek(); + } + break; + + default: + // Peek beyond the PathExpression or InputParameter. + $token = $beyond; + + while ($token['value'] === '.') { + $this->lexer->peek(); + + $token = $this->lexer->peek(); + } + + // Also peek beyond a NOT if there is one. + if ($token['type'] === Lexer::T_NOT) { + $token = $this->lexer->peek(); + } + + // We need to go even further in case of IS (differentiate between NULL and EMPTY) + $lookahead = $this->lexer->peek(); + } + + // Also peek beyond a NOT if there is one. + if ($lookahead['type'] === Lexer::T_NOT) { + $lookahead = $this->lexer->peek(); + } + + $this->lexer->resetPeek(); + } + + if ($token['type'] === Lexer::T_BETWEEN) { + return $this->BetweenExpression(); + } + + if ($token['type'] === Lexer::T_LIKE) { + return $this->LikeExpression(); + } + + if ($token['type'] === Lexer::T_IN) { + return $this->InExpression(); + } + + if ($token['type'] === Lexer::T_INSTANCE) { + return $this->InstanceOfExpression(); + } + + if ($token['type'] === Lexer::T_MEMBER) { + return $this->CollectionMemberExpression(); + } + + if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_NULL) { + return $this->NullComparisonExpression(); + } + + if ($token['type'] === Lexer::T_IS && $lookahead['type'] === Lexer::T_EMPTY) { + return $this->EmptyCollectionComparisonExpression(); + } + + return $this->ComparisonExpression(); + } + + /** + * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" + * + * @return \Doctrine\ORM\Query\AST\EmptyCollectionComparisonExpression + */ + public function EmptyCollectionComparisonExpression() + { + $emptyCollectionCompExpr = new AST\EmptyCollectionComparisonExpression( + $this->CollectionValuedPathExpression() + ); + $this->match(Lexer::T_IS); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $emptyCollectionCompExpr->not = true; + } + + $this->match(Lexer::T_EMPTY); + + return $emptyCollectionCompExpr; + } + + /** + * CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression + * + * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + * SimpleEntityExpression ::= IdentificationVariable | InputParameter + * + * @return \Doctrine\ORM\Query\AST\CollectionMemberExpression + */ + public function CollectionMemberExpression() + { + $not = false; + $entityExpr = $this->EntityExpression(); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $not = true; + } + + $this->match(Lexer::T_MEMBER); + + if ($this->lexer->isNextToken(Lexer::T_OF)) { + $this->match(Lexer::T_OF); + } + + $collMemberExpr = new AST\CollectionMemberExpression( + $entityExpr, $this->CollectionValuedPathExpression() + ); + $collMemberExpr->not = $not; + + return $collMemberExpr; + } + + /** + * Literal ::= string | char | integer | float | boolean + * + * @return \Doctrine\ORM\Query\AST\Literal + */ + public function Literal() + { + switch ($this->lexer->lookahead['type']) { + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + + case Lexer::T_INTEGER: + case Lexer::T_FLOAT: + $this->match( + $this->lexer->isNextToken(Lexer::T_INTEGER) ? Lexer::T_INTEGER : Lexer::T_FLOAT + ); + return new AST\Literal(AST\Literal::NUMERIC, $this->lexer->token['value']); + + case Lexer::T_TRUE: + case Lexer::T_FALSE: + $this->match( + $this->lexer->isNextToken(Lexer::T_TRUE) ? Lexer::T_TRUE : Lexer::T_FALSE + ); + return new AST\Literal(AST\Literal::BOOLEAN, $this->lexer->token['value']); + + default: + $this->syntaxError('Literal'); + } + } + + /** + * InParameter ::= Literal | InputParameter + * + * @return string | \Doctrine\ORM\Query\AST\InputParameter + */ + public function InParameter() + { + if ($this->lexer->lookahead['type'] == Lexer::T_INPUT_PARAMETER) { + return $this->InputParameter(); + } + + return $this->Literal(); + } + + /** + * InputParameter ::= PositionalParameter | NamedParameter + * + * @return \Doctrine\ORM\Query\AST\InputParameter + */ + public function InputParameter() + { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->lexer->token['value']); + } + + /** + * ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\ArithmeticExpression + */ + public function ArithmeticExpression() + { + $expr = new AST\ArithmeticExpression; + + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $peek = $this->lexer->glimpse(); + + if ($peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr->subselect = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + } + + $expr->simpleArithmeticExpression = $this->SimpleArithmeticExpression(); + + return $expr; + } + + /** + * SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}* + * + * @return \Doctrine\ORM\Query\AST\SimpleArithmeticExpression + */ + public function SimpleArithmeticExpression() + { + $terms = array(); + $terms[] = $this->ArithmeticTerm(); + + while (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + + $terms[] = $this->lexer->token['value']; + $terms[] = $this->ArithmeticTerm(); + } + + // Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression + // if only one AST\ArithmeticTerm is defined + if (count($terms) == 1) { + return $terms[0]; + } + + return new AST\SimpleArithmeticExpression($terms); + } + + /** + * ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}* + * + * @return \Doctrine\ORM\Query\AST\ArithmeticTerm + */ + public function ArithmeticTerm() + { + $factors = array(); + $factors[] = $this->ArithmeticFactor(); + + while (($isMult = $this->lexer->isNextToken(Lexer::T_MULTIPLY)) || $this->lexer->isNextToken(Lexer::T_DIVIDE)) { + $this->match(($isMult) ? Lexer::T_MULTIPLY : Lexer::T_DIVIDE); + + $factors[] = $this->lexer->token['value']; + $factors[] = $this->ArithmeticFactor(); + } + + // Phase 1 AST optimization: Prevent AST\ArithmeticTerm + // if only one AST\ArithmeticFactor is defined + if (count($factors) == 1) { + return $factors[0]; + } + + return new AST\ArithmeticTerm($factors); + } + + /** + * ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary + * + * @return \Doctrine\ORM\Query\AST\ArithmeticFactor + */ + public function ArithmeticFactor() + { + $sign = null; + + if (($isPlus = $this->lexer->isNextToken(Lexer::T_PLUS)) || $this->lexer->isNextToken(Lexer::T_MINUS)) { + $this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS); + $sign = $isPlus; + } + + $primary = $this->ArithmeticPrimary(); + + // Phase 1 AST optimization: Prevent AST\ArithmeticFactor + // if only one AST\ArithmeticPrimary is defined + if ($sign === null) { + return $primary; + } + + return new AST\ArithmeticFactor($primary, $sign); + } + + /** + * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | ParenthesisExpression + * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings + * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable + * | InputParameter | CaseExpression + */ + public function ArithmeticPrimary() + { + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $expr = $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\ParenthesisExpression($expr); + } + + switch ($this->lexer->lookahead['type']) { + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + case Lexer::T_CASE: + return $this->CaseExpression(); + + case Lexer::T_IDENTIFIER: + $peek = $this->lexer->glimpse(); + + if ($peek['value'] == '(') { + return $this->FunctionDeclaration(); + } + + if ($peek['value'] == '.') { + return $this->SingleValuedPathExpression(); + } + + if (isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) { + return $this->ResultVariable(); + } + + return $this->StateFieldPathExpression(); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + default: + $peek = $this->lexer->glimpse(); + + if ($peek['value'] == '(') { + if ($this->isAggregateFunction($this->lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } + + return $this->Literal(); + } + } + + /** + * StringExpression ::= StringPrimary | ResultVariable | "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\StringPrimary | + * \Doctrine\ORM\Query\AST\Subselect | + * string + */ + public function StringExpression() + { + $peek = $this->lexer->glimpse(); + + // Subselect + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $peek['type'] === Lexer::T_SELECT) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expr = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $expr; + } + + // ResultVariable (string) + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && + isset($this->queryComponents[$this->lexer->lookahead['value']]['resultVariable'])) { + return $this->ResultVariable(); + } + + return $this->StringPrimary(); + } + + /** + * StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression + */ + public function StringPrimary() + { + $lookaheadType = $this->lexer->lookahead['type']; + + switch ($lookaheadType) { + case Lexer::T_IDENTIFIER: + $peek = $this->lexer->glimpse(); + + if ($peek['value'] == '.') { + return $this->StateFieldPathExpression(); + } + + if ($peek['value'] == '(') { + // do NOT directly go to FunctionsReturningString() because it doesn't check for custom functions. + return $this->FunctionDeclaration(); + } + + $this->syntaxError("'.' or '('"); + break; + + case Lexer::T_STRING: + $this->match(Lexer::T_STRING); + + return new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + + case Lexer::T_INPUT_PARAMETER: + return $this->InputParameter(); + + case Lexer::T_CASE: + case Lexer::T_COALESCE: + case Lexer::T_NULLIF: + return $this->CaseExpression(); + + default: + if ($this->isAggregateFunction($lookaheadType)) { + return $this->AggregateExpression(); + } + } + + $this->syntaxError( + 'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression' + ); + } + + /** + * EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression + * + * @return \Doctrine\ORM\Query\AST\SingleValuedAssociationPathExpression | + * \Doctrine\ORM\Query\AST\SimpleEntityExpression + */ + public function EntityExpression() + { + $glimpse = $this->lexer->glimpse(); + + if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER) && $glimpse['value'] === '.') { + return $this->SingleValuedAssociationPathExpression(); + } + + return $this->SimpleEntityExpression(); + } + + /** + * SimpleEntityExpression ::= IdentificationVariable | InputParameter + * + * @return string | \Doctrine\ORM\Query\AST\InputParameter + */ + public function SimpleEntityExpression() + { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + return $this->InputParameter(); + } + + return $this->StateFieldPathExpression(); + } + + /** + * AggregateExpression ::= + * ("AVG" | "MAX" | "MIN" | "SUM" | "COUNT") "(" ["DISTINCT"] SimpleArithmeticExpression ")" + * + * @return \Doctrine\ORM\Query\AST\AggregateExpression + */ + public function AggregateExpression() + { + $lookaheadType = $this->lexer->lookahead['type']; + $isDistinct = false; + + if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) { + $this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT'); + } + + $this->match($lookaheadType); + $functionName = $this->lexer->token['value']; + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->lexer->isNextToken(Lexer::T_DISTINCT)) { + $this->match(Lexer::T_DISTINCT); + $isDistinct = true; + } + + $pathExp = $this->SimpleArithmeticExpression(); + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); + } + + /** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\QuantifiedExpression + */ + public function QuantifiedExpression() + { + $lookaheadType = $this->lexer->lookahead['type']; + $value = $this->lexer->lookahead['value']; + + if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) { + $this->syntaxError('ALL, ANY or SOME'); + } + + $this->match($lookaheadType); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $qExpr = new AST\QuantifiedExpression($this->Subselect()); + $qExpr->type = $value; + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $qExpr; + } + + /** + * BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression + * + * @return \Doctrine\ORM\Query\AST\BetweenExpression + */ + public function BetweenExpression() + { + $not = false; + $arithExpr1 = $this->ArithmeticExpression(); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_BETWEEN); + $arithExpr2 = $this->ArithmeticExpression(); + $this->match(Lexer::T_AND); + $arithExpr3 = $this->ArithmeticExpression(); + + $betweenExpr = new AST\BetweenExpression($arithExpr1, $arithExpr2, $arithExpr3); + $betweenExpr->not = $not; + + return $betweenExpr; + } + + /** + * ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) + * + * @return \Doctrine\ORM\Query\AST\ComparisonExpression + */ + public function ComparisonExpression() + { + $this->lexer->glimpse(); + + $leftExpr = $this->ArithmeticExpression(); + $operator = $this->ComparisonOperator(); + $rightExpr = ($this->isNextAllAnySome()) + ? $this->QuantifiedExpression() + : $this->ArithmeticExpression(); + + return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr); + } + + /** + * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + * + * @return \Doctrine\ORM\Query\AST\InExpression + */ + public function InExpression() + { + $inExpression = new AST\InExpression($this->ArithmeticExpression()); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $inExpression->not = true; + } + + $this->match(Lexer::T_IN); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + if ($this->lexer->isNextToken(Lexer::T_SELECT)) { + $inExpression->subselect = $this->Subselect(); + } else { + $literals = array(); + $literals[] = $this->InParameter(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + $literals[] = $this->InParameter(); + } + + $inExpression->literals = $literals; + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $inExpression; + } + + /** + * InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")") + * + * @return \Doctrine\ORM\Query\AST\InstanceOfExpression + */ + public function InstanceOfExpression() + { + $instanceOfExpression = new AST\InstanceOfExpression($this->IdentificationVariable()); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $instanceOfExpression->not = true; + } + + $this->match(Lexer::T_INSTANCE); + $this->match(Lexer::T_OF); + + $exprValues = array(); + + if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $exprValues[] = $this->InstanceOfParameter(); + + while ($this->lexer->isNextToken(Lexer::T_COMMA)) { + $this->match(Lexer::T_COMMA); + + $exprValues[] = $this->InstanceOfParameter(); + } + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + $instanceOfExpression->value = $exprValues; + + return $instanceOfExpression; + } + + $exprValues[] = $this->InstanceOfParameter(); + + $instanceOfExpression->value = $exprValues; + + return $instanceOfExpression; + } + + /** + * InstanceOfParameter ::= AbstractSchemaName | InputParameter + * + * @return mixed + */ + public function InstanceOfParameter() + { + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + + return new AST\InputParameter($this->lexer->token['value']); + } + + return $this->AliasIdentificationVariable(); + } + + /** + * LikeExpression ::= StringExpression ["NOT"] "LIKE" StringPrimary ["ESCAPE" char] + * + * @return \Doctrine\ORM\Query\AST\LikeExpression + */ + public function LikeExpression() + { + $stringExpr = $this->StringExpression(); + $not = false; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_LIKE); + + if ($this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) { + $this->match(Lexer::T_INPUT_PARAMETER); + $stringPattern = new AST\InputParameter($this->lexer->token['value']); + } else { + $stringPattern = $this->StringPrimary(); + } + + $escapeChar = null; + + if ($this->lexer->lookahead['type'] === Lexer::T_ESCAPE) { + $this->match(Lexer::T_ESCAPE); + $this->match(Lexer::T_STRING); + + $escapeChar = new AST\Literal(AST\Literal::STRING, $this->lexer->token['value']); + } + + $likeExpr = new AST\LikeExpression($stringExpr, $stringPattern, $escapeChar); + $likeExpr->not = $not; + + return $likeExpr; + } + + /** + * NullComparisonExpression ::= (InputParameter | NullIfExpression | CoalesceExpression | AggregateExpression | FunctionDeclaration | IdentificationVariable | SingleValuedPathExpression | ResultVariable) "IS" ["NOT"] "NULL" + * + * @return \Doctrine\ORM\Query\AST\NullComparisonExpression + */ + public function NullComparisonExpression() + { + switch (true) { + case $this->lexer->isNextToken(Lexer::T_INPUT_PARAMETER): + $this->match(Lexer::T_INPUT_PARAMETER); + + $expr = new AST\InputParameter($this->lexer->token['value']); + break; + + case $this->lexer->isNextToken(Lexer::T_NULLIF): + $expr = $this->NullIfExpression(); + break; + + case $this->lexer->isNextToken(Lexer::T_COALESCE): + $expr = $this->CoalesceExpression(); + break; + + case $this->isAggregateFunction($this->lexer->lookahead['type']): + $expr = $this->AggregateExpression(); + break; + + case $this->isFunction(): + $expr = $this->FunctionDeclaration(); + break; + + default: + // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression + $glimpse = $this->lexer->glimpse(); + + if ($glimpse['type'] === Lexer::T_DOT) { + $expr = $this->SingleValuedPathExpression(); + + // Leave switch statement + break; + } + + $lookaheadValue = $this->lexer->lookahead['value']; + + // Validate existing component + if ( ! isset($this->queryComponents[$lookaheadValue])) { + $this->semanticalError('Cannot add having condition on undefined result variable.'); + } + + // Validating ResultVariable + if ( ! isset($this->queryComponents[$lookaheadValue]['resultVariable'])) { + $this->semanticalError('Cannot add having condition on a non result variable.'); + } + + $expr = $this->ResultVariable(); + break; + } + + $nullCompExpr = new AST\NullComparisonExpression($expr); + + $this->match(Lexer::T_IS); + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + + $nullCompExpr->not = true; + } + + $this->match(Lexer::T_NULL); + + return $nullCompExpr; + } + + /** + * ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")" + * + * @return \Doctrine\ORM\Query\AST\ExistsExpression + */ + public function ExistsExpression() + { + $not = false; + + if ($this->lexer->isNextToken(Lexer::T_NOT)) { + $this->match(Lexer::T_NOT); + $not = true; + } + + $this->match(Lexer::T_EXISTS); + $this->match(Lexer::T_OPEN_PARENTHESIS); + + $existsExpression = new AST\ExistsExpression($this->Subselect()); + $existsExpression->not = $not; + + $this->match(Lexer::T_CLOSE_PARENTHESIS); + + return $existsExpression; + } + + /** + * ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!=" + * + * @return string + */ + public function ComparisonOperator() + { + switch ($this->lexer->lookahead['value']) { + case '=': + $this->match(Lexer::T_EQUALS); + + return '='; + + case '<': + $this->match(Lexer::T_LOWER_THAN); + $operator = '<'; + + if ($this->lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); + $operator .= '='; + } else if ($this->lexer->isNextToken(Lexer::T_GREATER_THAN)) { + $this->match(Lexer::T_GREATER_THAN); + $operator .= '>'; + } + + return $operator; + + case '>': + $this->match(Lexer::T_GREATER_THAN); + $operator = '>'; + + if ($this->lexer->isNextToken(Lexer::T_EQUALS)) { + $this->match(Lexer::T_EQUALS); + $operator .= '='; + } + + return $operator; + + case '!': + $this->match(Lexer::T_NEGATE); + $this->match(Lexer::T_EQUALS); + + return '<>'; + + default: + $this->syntaxError('=, <, <=, <>, >, >=, !='); + } + } + + /** + * FunctionDeclaration ::= FunctionsReturningStrings | FunctionsReturningNumerics | FunctionsReturningDatetime + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionDeclaration() + { + $token = $this->lexer->lookahead; + $funcName = strtolower($token['value']); + + // Check for built-in functions first! + switch (true) { + case (isset(self::$_STRING_FUNCTIONS[$funcName])): + return $this->FunctionsReturningStrings(); + + case (isset(self::$_NUMERIC_FUNCTIONS[$funcName])): + return $this->FunctionsReturningNumerics(); + + case (isset(self::$_DATETIME_FUNCTIONS[$funcName])): + return $this->FunctionsReturningDatetime(); + + default: + return $this->CustomFunctionDeclaration(); + } + } + + /** + * Helper function for FunctionDeclaration grammar rule. + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + private function CustomFunctionDeclaration() + { + $token = $this->lexer->lookahead; + $funcName = strtolower($token['value']); + + // Check for custom functions afterwards + $config = $this->em->getConfiguration(); + + switch (true) { + case ($config->getCustomStringFunction($funcName) !== null): + return $this->CustomFunctionsReturningStrings(); + + case ($config->getCustomNumericFunction($funcName) !== null): + return $this->CustomFunctionsReturningNumerics(); + + case ($config->getCustomDatetimeFunction($funcName) !== null): + return $this->CustomFunctionsReturningDatetime(); + + default: + $this->syntaxError('known function', $token); + } + } + + /** + * FunctionsReturningNumerics ::= + * "LENGTH" "(" StringPrimary ")" | + * "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")" | + * "ABS" "(" SimpleArithmeticExpression ")" | + * "SQRT" "(" SimpleArithmeticExpression ")" | + * "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "SIZE" "(" CollectionValuedPathExpression ")" | + * "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + * "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")" | + * "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningNumerics() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_NUMERIC_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningNumerics() + { + // getCustomNumericFunction is case-insensitive + $functionName = strtolower($this->lexer->lookahead['value']); + $functionClass = $this->em->getConfiguration()->getCustomNumericFunction($functionName); + + $function = is_string($functionClass) + ? new $functionClass($functionName) + : call_user_func($functionClass, $functionName); + + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningDateTime ::= + * "CURRENT_DATE" | + * "CURRENT_TIME" | + * "CURRENT_TIMESTAMP" | + * "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" | + * "DATE_SUB" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningDatetime() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_DATETIME_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningDatetime() + { + // getCustomDatetimeFunction is case-insensitive + $functionName = $this->lexer->lookahead['value']; + $functionClass = $this->em->getConfiguration()->getCustomDatetimeFunction($functionName); + + $function = is_string($functionClass) + ? new $functionClass($functionName) + : call_user_func($functionClass, $functionName); + + $function->parse($this); + + return $function; + } + + /** + * FunctionsReturningStrings ::= + * "CONCAT" "(" StringPrimary "," StringPrimary ")" | + * "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")" | + * "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")" | + * "LOWER" "(" StringPrimary ")" | + * "UPPER" "(" StringPrimary ")" | + * "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")" + * + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function FunctionsReturningStrings() + { + $funcNameLower = strtolower($this->lexer->lookahead['value']); + $funcClass = self::$_STRING_FUNCTIONS[$funcNameLower]; + + $function = new $funcClass($funcNameLower); + $function->parse($this); + + return $function; + } + + /** + * @return \Doctrine\ORM\Query\AST\Functions\FunctionNode + */ + public function CustomFunctionsReturningStrings() + { + // getCustomStringFunction is case-insensitive + $functionName = $this->lexer->lookahead['value']; + $functionClass = $this->em->getConfiguration()->getCustomStringFunction($functionName); + + $function = is_string($functionClass) + ? new $functionClass($functionName) + : call_user_func($functionClass, $functionName); + + $function->parse($this); + + return $function; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php new file mode 100644 index 0000000..dfc7361 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ParserResult.php @@ -0,0 +1,145 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Encapsulates the resulting components from a DQL query parsing process that + * can be serialized. + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @author Roman Borschel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://www.doctrine-project.org + * @since 2.0 + */ +class ParserResult +{ + /** + * The SQL executor used for executing the SQL. + * + * @var \Doctrine\ORM\Query\Exec\AbstractSqlExecutor + */ + private $_sqlExecutor; + + /** + * The ResultSetMapping that describes how to map the SQL result set. + * + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $_resultSetMapping; + + /** + * The mappings of DQL parameter names/positions to SQL parameter positions. + * + * @var array + */ + private $_parameterMappings = array(); + + /** + * Initializes a new instance of the ParserResult class. + * The new instance is initialized with an empty ResultSetMapping. + */ + public function __construct() + { + $this->_resultSetMapping = new ResultSetMapping; + } + + /** + * Gets the ResultSetMapping for the parsed query. + * + * @return ResultSetMapping|null The result set mapping of the parsed query or NULL + * if the query is not a SELECT query. + */ + public function getResultSetMapping() + { + return $this->_resultSetMapping; + } + + /** + * Sets the ResultSetMapping of the parsed query. + * + * @param ResultSetMapping $rsm + * + * @return void + */ + public function setResultSetMapping(ResultSetMapping $rsm) + { + $this->_resultSetMapping = $rsm; + } + + /** + * Sets the SQL executor that should be used for this ParserResult. + * + * @param \Doctrine\ORM\Query\Exec\AbstractSqlExecutor $executor + * + * @return void + */ + public function setSqlExecutor($executor) + { + $this->_sqlExecutor = $executor; + } + + /** + * Gets the SQL executor used by this ParserResult. + * + * @return \Doctrine\ORM\Query\Exec\AbstractSqlExecutor + */ + public function getSqlExecutor() + { + return $this->_sqlExecutor; + } + + /** + * Adds a DQL to SQL parameter mapping. One DQL parameter name/position can map to + * several SQL parameter positions. + * + * @param string|integer $dqlPosition + * @param integer $sqlPosition + * + * @return void + */ + public function addParameterMapping($dqlPosition, $sqlPosition) + { + $this->_parameterMappings[$dqlPosition][] = $sqlPosition; + } + + /** + * Gets all DQL to SQL parameter mappings. + * + * @return array The parameter mappings. + */ + public function getParameterMappings() + { + return $this->_parameterMappings; + } + + /** + * Gets the SQL parameter positions for a DQL parameter name/position. + * + * @param string|integer $dqlPosition The name or position of the DQL parameter. + * + * @return array The positions of the corresponding SQL parameters. + */ + public function getSqlParameterPositions($dqlPosition) + { + return $this->_parameterMappings[$dqlPosition]; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php new file mode 100644 index 0000000..d92ad85 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Printer.php @@ -0,0 +1,98 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A parse tree printer for Doctrine Query Language parser. + * + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://www.phpdoctrine.org + * @since 2.0 + */ +class Printer +{ + /** + * Current indentation level + * + * @var int + */ + protected $_indent = 0; + + /** + * Defines whether parse tree is printed (default, false) or not (true). + * + * @var bool + */ + protected $_silent; + + /** + * Constructs a new parse tree printer. + * + * @param bool $silent Parse tree will not be printed if true. + */ + public function __construct($silent = false) + { + $this->_silent = $silent; + } + + /** + * Prints an opening parenthesis followed by production name and increases + * indentation level by one. + * + * This method is called before executing a production. + * + * @param string $name Production name. + * + * @return void + */ + public function startProduction($name) + { + $this->println('(' . $name); + $this->_indent++; + } + + /** + * Decreases indentation level by one and prints a closing parenthesis. + * + * This method is called after executing a production. + * + * @return void + */ + public function endProduction() + { + $this->_indent--; + $this->println(')'); + } + + /** + * Prints text indented with spaces depending on current indentation level. + * + * @param string $str The text. + * + * @return void + */ + public function println($str) + { + if ( ! $this->_silent) { + echo str_repeat(' ', $this->_indent), $str, "\n"; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php new file mode 100644 index 0000000..16a1085 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryException.php @@ -0,0 +1,266 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Description of QueryException. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class QueryException extends \Doctrine\ORM\ORMException +{ + /** + * @param string $dql + * + * @return QueryException + */ + public static function dqlError($dql) + { + return new self($dql); + } + + /** + * @param string $message + * @param \Exception|null $previous + * + * @return QueryException + */ + public static function syntaxError($message, $previous = null) + { + return new self('[Syntax Error] ' . $message, 0, $previous); + } + + /** + * @param string $message + * @param \Exception|null $previous + * + * @return QueryException + */ + public static function semanticalError($message, $previous = null) + { + return new self('[Semantical Error] ' . $message, 0, $previous); + } + + /** + * @return QueryException + */ + public static function invalidLockMode() + { + return new self('Invalid lock mode hint provided.'); + } + + /** + * @param string $expected + * @param string $received + * + * @return QueryException + */ + public static function invalidParameterType($expected, $received) + { + return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.'); + } + + /** + * @param string $pos + * + * @return QueryException + */ + public static function invalidParameterPosition($pos) + { + return new self('Invalid parameter position: ' . $pos); + } + + /** + * @param integer $expected + * @param integer $received + * + * @return QueryException + */ + public static function tooManyParameters($expected, $received) + { + return new self('Too many parameters: the query defines ' . $expected . ' parameters and you bound ' . $received); + } + + /** + * @param integer $expected + * @param integer $received + * + * @return QueryException + */ + public static function tooFewParameters($expected, $received) + { + return new self('Too few parameters: the query defines ' . $expected . ' parameters but you only bound ' . $received); + } + + /** + * @param string $value + * + * @return QueryException + */ + public static function invalidParameterFormat($value) + { + return new self('Invalid parameter format, '.$value.' given, but : or ? expected.'); + } + + /** + * @param string $key + * + * @return QueryException + */ + public static function unknownParameter($key) + { + return new self("Invalid parameter: token ".$key." is not defined in the query."); + } + + /** + * @return QueryException + */ + public static function parameterTypeMismatch() + { + return new self("DQL Query parameter and type numbers mismatch, but have to be exactly equal."); + } + + /** + * @param object $pathExpr + * + * @return QueryException + */ + public static function invalidPathExpression($pathExpr) + { + return new self( + "Invalid PathExpression '" . $pathExpr->identificationVariable . "." . $pathExpr->field . "'." + ); + } + + /** + * @param string $literal + * + * @return QueryException + */ + public static function invalidLiteral($literal) + { + return new self("Invalid literal '$literal'"); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function iterateWithFetchJoinCollectionNotAllowed($assoc) + { + return new self( + "Invalid query operation: Not allowed to iterate over fetch join collections ". + "in class ".$assoc['sourceEntity']." association ".$assoc['fieldName'] + ); + } + + /** + * @return QueryException + */ + public static function partialObjectsAreDangerous() + { + return new self( + "Loading partial objects is dangerous. Fetch full objects or consider " . + "using a different fetch mode. If you really want partial objects, " . + "set the doctrine.forcePartialLoad query hint to TRUE." + ); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function overwritingJoinConditionsNotYetSupported($assoc) + { + return new self( + "Unsupported query operation: It is not yet possible to overwrite the join ". + "conditions in class ".$assoc['sourceEntityName']." association ".$assoc['fieldName'].". ". + "Use WITH to append additional join conditions to the association." + ); + } + + /** + * @return QueryException + */ + public static function associationPathInverseSideNotSupported() + { + return new self( + "A single-valued association path expression to an inverse side is not supported". + " in DQL queries. Use an explicit join instead." + ); + } + + /** + * @param array $assoc + * + * @return QueryException + */ + public static function iterateWithFetchJoinNotAllowed($assoc) + { + return new self( + "Iterate with fetch join in class " . $assoc['sourceEntity'] . + " using association " . $assoc['fieldName'] . " not allowed." + ); + } + + /** + * @return QueryException + */ + public static function associationPathCompositeKeyNotSupported() + { + return new self( + "A single-valued association path expression to an entity with a composite primary ". + "key is not supported. Explicitly name the components of the composite primary key ". + "in the query." + ); + } + + /** + * @param string $className + * @param string $rootClass + * + * @return QueryException + */ + public static function instanceOfUnrelatedClass($className, $rootClass) + { + return new self("Cannot check if a child of '" . $rootClass . "' is instanceof '" . $className . "', " . + "inheritance hierarchy does not exists between these two classes."); + } + + /** + * @param string $dqlAlias + * + * @return QueryException + */ + public static function invalidQueryComponent($dqlAlias) + { + return new self( + "Invalid query component given for DQL alias '" . $dqlAlias . "', ". + "requires 'metadata', 'parent', 'relation', 'map', 'nestingLevel' and 'token' keys." + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php new file mode 100644 index 0000000..fe54708 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -0,0 +1,211 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\Common\Collections\ArrayCollection; + +use Doctrine\Common\Collections\Expr\ExpressionVisitor; +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +/** + * Converts Collection expressions to Query expressions. + * + * @author Kirill chEbba Chebunin + * @since 2.4 + */ +class QueryExpressionVisitor extends ExpressionVisitor +{ + /** + * @var array + */ + private static $operatorMap = array( + Comparison::GT => Expr\Comparison::GT, + Comparison::GTE => Expr\Comparison::GTE, + Comparison::LT => Expr\Comparison::LT, + Comparison::LTE => Expr\Comparison::LTE + ); + + /** + * @var array + */ + private $queryAliases; + + /** + * @var Expr + */ + private $expr; + + /** + * @var array + */ + private $parameters = array(); + + /** + * Constructor + * + * @param array $queryAliases + */ + public function __construct($queryAliases) + { + $this->queryAliases = $queryAliases; + $this->expr = new Expr(); + } + + /** + * Gets bound parameters. + * Filled after {@link dispach()}. + * + * @return \Doctrine\Common\Collections\Collection + */ + public function getParameters() + { + return new ArrayCollection($this->parameters); + } + + /** + * Clears parameters. + * + * @return void + */ + public function clearParameters() + { + $this->parameters = array(); + } + + /** + * Converts Criteria expression to Query one based on static map. + * + * @param string $criteriaOperator + * + * @return string|null + */ + private static function convertComparisonOperator($criteriaOperator) + { + return isset(self::$operatorMap[$criteriaOperator]) ? self::$operatorMap[$criteriaOperator] : null; + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return new Expr\Andx($expressionList); + + case CompositeExpression::TYPE_OR: + return new Expr\Orx($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + + if ( ! isset($this->queryAliases[0])) { + throw new QueryException('No aliases are set before invoking walkComparison().'); + } + + $field = $this->queryAliases[0] . '.' . $comparison->getField(); + + foreach($this->queryAliases as $alias) { + if(strpos($comparison->getField() . '.', $alias . '.') === 0) { + $field = $comparison->getField(); + break; + } + } + + $parameterName = str_replace('.', '_', $comparison->getField()); + + foreach($this->parameters as $parameter) { + if($parameter->getName() === $parameterName) { + $parameterName .= '_' . count($this->parameters); + break; + } + } + + $parameter = new Parameter($parameterName, $this->walkValue($comparison->getValue())); + $placeholder = ':' . $parameterName; + + switch ($comparison->getOperator()) { + case Comparison::IN: + $this->parameters[] = $parameter; + return $this->expr->in($field, $placeholder); + + case Comparison::NIN: + $this->parameters[] = $parameter; + return $this->expr->notIn($field, $placeholder); + + case Comparison::EQ: + case Comparison::IS: + if ($this->walkValue($comparison->getValue()) === null) { + return $this->expr->isNull($field); + } + $this->parameters[] = $parameter; + return $this->expr->eq($field, $placeholder); + + case Comparison::NEQ: + if ($this->walkValue($comparison->getValue()) === null) { + return $this->expr->isNotNull($field); + } + $this->parameters[] = $parameter; + return $this->expr->neq($field, $placeholder); + + case Comparison::CONTAINS: + $parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType()); + $this->parameters[] = $parameter; + return $this->expr->like($field, $placeholder); + + default: + $operator = self::convertComparisonOperator($comparison->getOperator()); + if ($operator) { + $this->parameters[] = $parameter; + return new Expr\Comparison( + $field, + $operator, + $placeholder + ); + } + + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php new file mode 100644 index 0000000..bdd5de7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -0,0 +1,578 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result. + * + * IMPORTANT NOTE: + * The properties of this class are only public for fast internal READ access and to (drastically) + * reduce the size of serialized instances for more effective caching due to better (un-)serialization + * performance. + * + * Users should use the public methods. + * + * @author Roman Borschel + * @since 2.0 + * @todo Think about whether the number of lookup maps can be reduced. + */ +class ResultSetMapping +{ + /** + * Whether the result is mixed (contains scalar values together with field values). + * + * @ignore + * @var boolean + */ + public $isMixed = false; + + /** + * Whether the result is a select statement. + * + * @ignore + * @var boolean + */ + public $isSelect = true; + + /** + * Maps alias names to class names. + * + * @ignore + * @var array + */ + public $aliasMap = array(); + + /** + * Maps alias names to related association field names. + * + * @ignore + * @var array + */ + public $relationMap = array(); + + /** + * Maps alias names to parent alias names. + * + * @ignore + * @var array + */ + public $parentAliasMap = array(); + + /** + * Maps column names in the result set to field names for each class. + * + * @ignore + * @var array + */ + public $fieldMappings = array(); + + /** + * Maps column names in the result set to the alias/field name to use in the mapped result. + * + * @ignore + * @var array + */ + public $scalarMappings = array(); + + /** + * Maps column names in the result set to the alias/field type to use in the mapped result. + * + * @ignore + * @var array + */ + public $typeMappings = array(); + + /** + * Maps entities in the result set to the alias name to use in the mapped result. + * + * @ignore + * @var array + */ + public $entityMappings = array(); + + /** + * Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names. + * + * @ignore + * @var array + */ + public $metaMappings = array(); + + /** + * Maps column names in the result set to the alias they belong to. + * + * @ignore + * @var array + */ + public $columnOwnerMap = array(); + + /** + * List of columns in the result set that are used as discriminator columns. + * + * @ignore + * @var array + */ + public $discriminatorColumns = array(); + + /** + * Maps alias names to field names that should be used for indexing. + * + * @ignore + * @var array + */ + public $indexByMap = array(); + + /** + * Map from column names to class names that declare the field the column is mapped to. + * + * @ignore + * @var array + */ + public $declaringClasses = array(); + + /** + * This is necessary to hydrate derivate foreign keys correctly. + * + * @var array + */ + public $isIdentifierColumn = array(); + + /** + * Maps column names in the result set to field names for each new object expression. + * + * @var array + */ + public $newObjectMappings = array(); + + /** + * Maps metadata parameter names to the metadata attribute. + * + * @var array + */ + public $metadataParameterMapping = array(); + + /** + * Adds an entity result to this ResultSetMapping. + * + * @param string $class The class name of the entity. + * @param string $alias The alias for the class. The alias must be unique among all entity + * results or joined entity results within this ResultSetMapping. + * @param string|null $resultAlias The result alias with which the entity result should be + * placed in the result structure. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addRootEntity + */ + public function addEntityResult($class, $alias, $resultAlias = null) + { + $this->aliasMap[$alias] = $class; + $this->entityMappings[$alias] = $resultAlias; + + if ($resultAlias !== null) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Sets a discriminator column for an entity result or joined entity result. + * The discriminator column will be used to determine the concrete class name to + * instantiate. + * + * @param string $alias The alias of the entity result or joined entity result the discriminator + * column should be used for. + * @param string $discrColumn The name of the discriminator column in the SQL result set. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addDiscriminatorColumn + */ + public function setDiscriminatorColumn($alias, $discrColumn) + { + $this->discriminatorColumns[$alias] = $discrColumn; + $this->columnOwnerMap[$discrColumn] = $alias; + + return $this; + } + + /** + * Sets a field to use for indexing an entity result or joined entity result. + * + * @param string $alias The alias of an entity result or joined entity result. + * @param string $fieldName The name of the field to use for indexing. + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexBy($alias, $fieldName) + { + $found = false; + + foreach (array_merge($this->metaMappings, $this->fieldMappings) as $columnName => $columnFieldName) { + if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue; + + $this->addIndexByColumn($alias, $columnName); + $found = true; + + break; + } + + /* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals + if ( ! $found) { + $message = sprintf( + 'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.', + $alias, + $fieldName + ); + + throw new \LogicException($message); + } + */ + + return $this; + } + + /** + * Sets to index by a scalar result column name. + * + * @param string $resultColumnName + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexByScalar($resultColumnName) + { + $this->indexByMap['scalars'] = $resultColumnName; + + return $this; + } + + /** + * Sets a column to use for indexing an entity or joined entity result by the given alias name. + * + * @param string $alias + * @param string $resultColumnName + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addIndexByColumn($alias, $resultColumnName) + { + $this->indexByMap[$alias] = $resultColumnName; + + return $this; + } + + /** + * Checks whether an entity result or joined entity result with a given alias has + * a field set for indexing. + * + * @param string $alias + * + * @return boolean + * + * @todo Rename: isIndexed($alias) + */ + public function hasIndexBy($alias) + { + return isset($this->indexByMap[$alias]); + } + + /** + * Checks whether the column with the given name is mapped as a field result + * as part of an entity result or joined entity result. + * + * @param string $columnName The name of the column in the SQL result set. + * + * @return boolean + * + * @todo Rename: isField + */ + public function isFieldResult($columnName) + { + return isset($this->fieldMappings[$columnName]); + } + + /** + * Adds a field to the result that belongs to an entity or joined entity. + * + * @param string $alias The alias of the root entity or joined entity to which the field belongs. + * @param string $columnName The name of the column in the SQL result set. + * @param string $fieldName The name of the field on the declaring class. + * @param string|null $declaringClass The name of the class that declares/owns the specified field. + * When $alias refers to a superclass in a mapped hierarchy but + * the field $fieldName is defined on a subclass, specify that here. + * If not specified, the field is assumed to belong to the class + * designated by $alias. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addField + */ + public function addFieldResult($alias, $columnName, $fieldName, $declaringClass = null) + { + // column name (in result set) => field name + $this->fieldMappings[$columnName] = $fieldName; + // column name => alias of owner + $this->columnOwnerMap[$columnName] = $alias; + // field name => class name of declaring class + $this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias]; + + if ( ! $this->isMixed && $this->scalarMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Adds a joined entity result. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param string $relation The association field that connects the parent entity result + * with the joined entity result. + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addJoinedEntity + */ + public function addJoinedEntityResult($class, $alias, $parentAlias, $relation) + { + $this->aliasMap[$alias] = $class; + $this->parentAliasMap[$alias] = $parentAlias; + $this->relationMap[$alias] = $relation; + + return $this; + } + + /** + * Adds a scalar result mapping. + * + * @param string $columnName The name of the column in the SQL result set. + * @param string $alias The result alias with which the scalar result should be placed in the result structure. + * @param string $type The column type + * + * @return ResultSetMapping This ResultSetMapping instance. + * + * @todo Rename: addScalar + */ + public function addScalarResult($columnName, $alias, $type = 'string') + { + $this->scalarMappings[$columnName] = $alias; + $this->typeMappings[$columnName] = $type; + + if ( ! $this->isMixed && $this->fieldMappings) { + $this->isMixed = true; + } + + return $this; + } + + /** + * Adds a metadata parameter mappings. + * + * @param mixed $parameter The parameter name in the SQL result set. + * @param string $attribute The metadata attribute. + */ + public function addMetadataParameterMapping($parameter, $attribute) + { + $this->metadataParameterMapping[$parameter] = $attribute; + } + + /** + * Checks whether a column with a given name is mapped as a scalar result. + * + * @param string $columnName The name of the column in the SQL result set. + * + * @return boolean + * + * @todo Rename: isScalar + */ + public function isScalarResult($columnName) + { + return isset($this->scalarMappings[$columnName]); + } + + /** + * Gets the name of the class of an entity result or joined entity result, + * identified by the given unique alias. + * + * @param string $alias + * + * @return string + */ + public function getClassName($alias) + { + return $this->aliasMap[$alias]; + } + + /** + * Gets the field alias for a column that is mapped as a scalar value. + * + * @param string $columnName The name of the column in the SQL result set. + * + * @return string + */ + public function getScalarAlias($columnName) + { + return $this->scalarMappings[$columnName]; + } + + /** + * Gets the name of the class that owns a field mapping for the specified column. + * + * @param string $columnName + * + * @return string + */ + public function getDeclaringClass($columnName) + { + return $this->declaringClasses[$columnName]; + } + + /** + * @param string $alias + * + * @return AssociationMapping + */ + public function getRelation($alias) + { + return $this->relationMap[$alias]; + } + + /** + * @param string $alias + * + * @return boolean + */ + public function isRelation($alias) + { + return isset($this->relationMap[$alias]); + } + + /** + * Gets the alias of the class that owns a field mapping for the specified column. + * + * @param string $columnName + * + * @return string + */ + public function getEntityAlias($columnName) + { + return $this->columnOwnerMap[$columnName]; + } + + /** + * Gets the parent alias of the given alias. + * + * @param string $alias + * + * @return string + */ + public function getParentAlias($alias) + { + return $this->parentAliasMap[$alias]; + } + + /** + * Checks whether the given alias has a parent alias. + * + * @param string $alias + * + * @return boolean + */ + public function hasParentAlias($alias) + { + return isset($this->parentAliasMap[$alias]); + } + + /** + * Gets the field name for a column name. + * + * @param string $columnName + * + * @return string + */ + public function getFieldName($columnName) + { + return $this->fieldMappings[$columnName]; + } + + /** + * @return array + */ + public function getAliasMap() + { + return $this->aliasMap; + } + + /** + * Gets the number of different entities that appear in the mapped result. + * + * @return integer + */ + public function getEntityResultCount() + { + return count($this->aliasMap); + } + + /** + * Checks whether this ResultSetMapping defines a mixed result. + * + * Mixed results can only occur in object and array (graph) hydration. In such a + * case a mixed result means that scalar values are mixed with objects/array in + * the result. + * + * @return boolean + */ + public function isMixedResult() + { + return $this->isMixed; + } + + /** + * Adds a meta column (foreign key or discriminator column) to the result set. + * + * @param string $alias The result alias with which the meta result should be placed in the result structure. + * @param string $columnName The name of the column in the SQL result set. + * @param string $fieldName The name of the field on the declaring class. + * @param bool $isIdentifierColumn + * @param string $type The column type + * + * @return ResultSetMapping This ResultSetMapping instance. + */ + public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null) + { + $this->metaMappings[$columnName] = $fieldName; + $this->columnOwnerMap[$columnName] = $alias; + + if ($isIdentifierColumn) { + $this->isIdentifierColumn[$alias][$columnName] = true; + } + + if ($type) { + $this->typeMappings[$columnName] = $type; + } + + return $this; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php new file mode 100644 index 0000000..804a208 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -0,0 +1,458 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields. + * + * @author Michael Ridgway + * @since 2.1 + */ +class ResultSetMappingBuilder extends ResultSetMapping +{ + /** + * Picking this rename mode will register entity columns as is, + * as they are in the database. This can cause clashes when multiple + * entities are fetched that have columns with the same name. + * + * @var int + */ + const COLUMN_RENAMING_NONE = 1; + + /** + * Picking custom renaming allows the user to define the renaming + * of specific columns with a rename array that contains column names as + * keys and result alias as values. + * + * @var int + */ + const COLUMN_RENAMING_CUSTOM = 2; + + /** + * Incremental renaming uses a result set mapping internal counter to add a + * number to each column result, leading to uniqueness. This only works if + * you use {@see generateSelectClause()} to generate the SELECT clause for + * you. + * + * @var int + */ + const COLUMN_RENAMING_INCREMENT = 3; + + /** + * @var int + */ + private $sqlCounter = 0; + + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * Default column renaming mode. + * + * @var int + */ + private $defaultRenameMode; + + /** + * @param EntityManagerInterface $em + * @param integer $defaultRenameMode + */ + public function __construct(EntityManagerInterface $em, $defaultRenameMode = self::COLUMN_RENAMING_NONE) + { + $this->em = $em; + $this->defaultRenameMode = $defaultRenameMode; + } + + /** + * Adds a root entity and all of its fields to the result set. + * + * @param string $class The class name of the root entity. + * @param string $alias The unique alias to use for the root entity. + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName). + * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). + * + * @return void + */ + public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array(), $renameMode = null) + { + $renameMode = $renameMode ?: $this->defaultRenameMode; + $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); + + $this->addEntityResult($class, $alias); + $this->addAllClassFields($class, $alias, $columnAliasMap); + } + + /** + * Adds a joined entity and all of its fields to the result set. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result + * with the joined entity result. + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName). + * @param int|null $renameMode One of the COLUMN_RENAMING_* constants or array for BC reasons (CUSTOM). + * + * @return void + */ + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array(), $renameMode = null) + { + $renameMode = $renameMode ?: $this->defaultRenameMode; + $columnAliasMap = $this->getColumnAliasMap($class, $renameMode, $renamedColumns); + + $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); + $this->addAllClassFields($class, $alias, $columnAliasMap); + } + + /** + * Adds all fields of the given class to the result set mapping (columns and meta fields). + * + * @param string $class + * @param string $alias + * @param array $columnAliasMap + * + * @return void + * + * @throws \InvalidArgumentException + */ + protected function addAllClassFields($class, $alias, $columnAliasMap = array()) + { + $classMetadata = $this->em->getClassMetadata($class); + $platform = $this->em->getConnection()->getDatabasePlatform(); + + if ( ! $this->isInheritanceSupported($classMetadata)) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support your inheritance scheme.'); + } + + + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]); + + if (isset($this->fieldMappings[$columnAlias])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + + $this->addFieldResult($alias, $columnAlias, $propertyName); + } + + foreach ($classMetadata->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $columnAlias = $platform->getSQLResultCasing($columnAliasMap[$columnName]); + + if (isset($this->metaMappings[$columnAlias])) { + throw new \InvalidArgumentException("The column '$columnAlias' conflicts with another column in the mapper."); + } + + $this->addMetaResult( + $alias, + $columnAlias, + $columnName, + (isset($associationMapping['id']) && $associationMapping['id'] === true) + ); + } + } + } + } + + private function isInheritanceSupported(ClassMetadataInfo $classMetadata) + { + if ($classMetadata->isInheritanceTypeSingleTable() + && in_array($classMetadata->name, $classMetadata->discriminatorMap, true)) { + return true; + } + + return ! ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()); + } + + /** + * Gets column alias for a given column. + * + * @param string $columnName + * @param int $mode + * @param array $customRenameColumns + * + * @return string + */ + private function getColumnAlias($columnName, $mode, array $customRenameColumns) + { + switch ($mode) { + case self::COLUMN_RENAMING_INCREMENT: + return $columnName . $this->sqlCounter++; + + case self::COLUMN_RENAMING_CUSTOM: + return isset($customRenameColumns[$columnName]) + ? $customRenameColumns[$columnName] : $columnName; + + case self::COLUMN_RENAMING_NONE: + return $columnName; + + } + } + + /** + * Retrieves a class columns and join columns aliases that are used in the SELECT clause. + * + * This depends on the renaming mode selected by the user. + * + * @param string $className + * @param int $mode + * @param array $customRenameColumns + * + * @return array + */ + private function getColumnAliasMap($className, $mode, array $customRenameColumns) + { + if ($customRenameColumns) { // for BC with 2.2-2.3 API + $mode = self::COLUMN_RENAMING_CUSTOM; + } + + $columnAlias = array(); + $class = $this->em->getClassMetadata($className); + + foreach ($class->getColumnNames() as $columnName) { + $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); + } + + foreach ($class->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $columnAlias[$columnName] = $this->getColumnAlias($columnName, $mode, $customRenameColumns); + } + } + } + + return $columnAlias; + } + + /** + * Adds the mappings of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param array $queryMapping + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryMapping(ClassMetadataInfo $class, array $queryMapping) + { + if (isset($queryMapping['resultClass'])) { + return $this->addNamedNativeQueryResultClassMapping($class, $queryMapping['resultClass']); + } + + return $this->addNamedNativeQueryResultSetMapping($class, $queryMapping['resultSetMapping']); + } + + /** + * Adds the class mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultClassName + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultClassMapping(ClassMetadataInfo $class, $resultClassName) + { + $classMetadata = $this->em->getClassMetadata($resultClassName); + $shortName = $classMetadata->reflClass->getShortName(); + $alias = strtolower($shortName[0]).'0'; + + $this->addEntityResult($class->name, $alias); + + if ($classMetadata->discriminatorColumn) { + $discriminatorColumn = $classMetadata->discriminatorColumn; + $this->setDiscriminatorColumn($alias, $discriminatorColumn['name']); + $this->addMetaResult($alias, $discriminatorColumn['name'], $discriminatorColumn['fieldName']); + } + + foreach ($classMetadata->getColumnNames() as $key => $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + + foreach ($classMetadata->associationMappings as $associationMapping) { + if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $columnName = $joinColumn['name']; + $this->addMetaResult($alias, $columnName, $columnName, $classMetadata->isIdentifier($columnName)); + } + } + } + + return $this; + } + + /** + * Adds the result set mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $class + * @param string $resultSetMappingName + * + * @return ResultSetMappingBuilder + */ + public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName) + { + $counter = 0; + $resultMapping = $class->getSqlResultSetMapping($resultSetMappingName); + $rooShortName = $class->reflClass->getShortName(); + $rootAlias = strtolower($rooShortName[0]) . $counter; + + + if (isset($resultMapping['entities'])) { + foreach ($resultMapping['entities'] as $key => $entityMapping) { + $classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']); + + if ($class->reflClass->name == $classMetadata->reflClass->name) { + $this->addEntityResult($classMetadata->name, $rootAlias); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias); + } else { + $shortName = $classMetadata->reflClass->getShortName(); + $joinAlias = strtolower($shortName[0]) . ++ $counter; + $associations = $class->getAssociationsByTargetClass($classMetadata->name); + + foreach ($associations as $relation => $mapping) { + $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation); + $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias); + } + } + + } + } + + if (isset($resultMapping['columns'])) { + foreach ($resultMapping['columns'] as $entityMapping) { + $this->addScalarResult($entityMapping['name'], $entityMapping['name']); + } + } + + return $this; + } + + /** + * Adds the entity result mapping of the results of native SQL queries to the result set. + * + * @param ClassMetadataInfo $classMetadata + * @param array $entityMapping + * @param string $alias + * + * @return ResultSetMappingBuilder + * + * @throws \InvalidArgumentException + */ + public function addNamedNativeQueryEntityResultMapping(ClassMetadataInfo $classMetadata, array $entityMapping, $alias) + { + if (isset($entityMapping['discriminatorColumn']) && $entityMapping['discriminatorColumn']) { + $discriminatorColumn = $entityMapping['discriminatorColumn']; + $this->setDiscriminatorColumn($alias, $discriminatorColumn); + $this->addMetaResult($alias, $discriminatorColumn, $discriminatorColumn); + } + + if (isset($entityMapping['fields']) && !empty($entityMapping['fields'])) { + foreach ($entityMapping['fields'] as $field) { + $fieldName = $field['name']; + $relation = null; + + if(strpos($fieldName, '.')){ + list($relation, $fieldName) = explode('.', $fieldName); + } + + if (isset($classMetadata->associationMappings[$relation])) { + if($relation) { + $associationMapping = $classMetadata->associationMappings[$relation]; + $joinAlias = $alias.$relation; + $parentAlias = $alias; + + $this->addJoinedEntityResult($associationMapping['targetEntity'], $joinAlias, $parentAlias, $relation); + $this->addFieldResult($joinAlias, $field['column'], $fieldName); + }else { + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } else { + if(!isset($classMetadata->fieldMappings[$fieldName])) { + throw new \InvalidArgumentException("Entity '".$classMetadata->name."' has no field '".$fieldName."'. "); + } + $this->addFieldResult($alias, $field['column'], $fieldName, $classMetadata->name); + } + } + + } else { + foreach ($classMetadata->getColumnNames() as $columnName) { + $propertyName = $classMetadata->getFieldName($columnName); + $this->addFieldResult($alias, $columnName, $propertyName); + } + } + + return $this; + } + + /** + * Generates the Select clause from this ResultSetMappingBuilder. + * + * Works only for all the entity results. The select parts for scalar + * expressions have to be written manually. + * + * @param array $tableAliases + * + * @return string + */ + public function generateSelectClause($tableAliases = array()) + { + $sql = ""; + + foreach ($this->columnOwnerMap as $columnName => $dqlAlias) { + $tableAlias = isset($tableAliases[$dqlAlias]) + ? $tableAliases[$dqlAlias] : $dqlAlias; + + if ($sql) { + $sql .= ", "; + } + + $sql .= $tableAlias . "."; + + if (isset($this->fieldMappings[$columnName])) { + $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]); + $sql .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName']; + } else if (isset($this->metaMappings[$columnName])) { + $sql .= $this->metaMappings[$columnName]; + } else if (isset($this->discriminatorColumns[$dqlAlias])) { + $sql .= $this->discriminatorColumns[$dqlAlias]; + } + + $sql .= " AS " . $columnName; + } + + return $sql; + } + + /** + * @return string + */ + public function __toString() + { + return $this->generateSelectClause(array()); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php new file mode 100644 index 0000000..3dea8de --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/SqlWalker.php @@ -0,0 +1,2324 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\DBAL\LockMode; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query; +use Doctrine\ORM\OptimisticLockException; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * The SqlWalker is a TreeWalker that walks over a DQL AST and constructs + * the corresponding SQL. + * + * @author Guilherme Blanco + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Alexander + * @author Fabio B. Silva + * @since 2.0 + * @todo Rename: SQLWalker + */ +class SqlWalker implements TreeWalker +{ + /** + * @var string + */ + const HINT_DISTINCT = 'doctrine.distinct'; + + /** + * @var ResultSetMapping + */ + private $rsm; + + /** + * Counter for generating unique column aliases. + * + * @var integer + */ + private $aliasCounter = 0; + + /** + * Counter for generating unique table aliases. + * + * @var integer + */ + private $tableAliasCounter = 0; + + /** + * Counter for generating unique scalar result. + * + * @var integer + */ + private $scalarResultCounter = 1; + + /** + * Counter for generating unique parameter indexes. + * + * @var integer + */ + private $sqlParamIndex = 0; + + /** + * Counter for generating indexes. + * + * @var integer + */ + private $newObjectCounter = 0; + + /** + * @var ParserResult + */ + private $parserResult; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * @var \Doctrine\ORM\AbstractQuery + */ + private $query; + + /** + * @var array + */ + private $tableAliasMap = array(); + + /** + * Map from result variable names to their SQL column alias names. + * + * @var array + */ + private $scalarResultAliasMap = array(); + + /** + * Map from Table-Alias + Column-Name to OrderBy-Direction. + * + * @var array + */ + private $orderedColumnsMap = array(); + + /** + * Map from DQL-Alias + Field-Name to SQL Column Alias. + * + * @var array + */ + private $scalarFields = array(); + + /** + * Map of all components/classes that appear in the DQL query. + * + * @var array + */ + private $queryComponents; + + /** + * A list of classes that appear in non-scalar SelectExpressions. + * + * @var array + */ + private $selectedClasses = array(); + + /** + * The DQL alias of the root class of the currently traversed query. + * + * @var array + */ + private $rootAliases = array(); + + /** + * Flag that indicates whether to generate SQL table aliases in the SQL. + * These should only be generated for SELECT queries, not for UPDATE/DELETE. + * + * @var boolean + */ + private $useSqlTableAliases = true; + + /** + * The database platform abstraction. + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * {@inheritDoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->query = $query; + $this->parserResult = $parserResult; + $this->queryComponents = $queryComponents; + $this->rsm = $parserResult->getResultSetMapping(); + $this->em = $query->getEntityManager(); + $this->conn = $this->em->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Gets the Query instance used by the walker. + * + * @return Query. + */ + public function getQuery() + { + return $this->query; + } + + /** + * Gets the Connection used by the walker. + * + * @return \Doctrine\DBAL\Connection + */ + public function getConnection() + { + return $this->conn; + } + + /** + * Gets the EntityManager used by the walker. + * + * @return \Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Gets the information about a single query component. + * + * @param string $dqlAlias The DQL alias. + * + * @return array + */ + public function getQueryComponent($dqlAlias) + { + return $this->queryComponents[$dqlAlias]; + } + + /** + * {@inheritdoc} + */ + public function getQueryComponents() + { + return $this->queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + switch (true) { + case ($AST instanceof AST\DeleteStatement): + $primaryClass = $this->em->getClassMetadata($AST->deleteClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new Exec\MultiTableDeleteExecutor($AST, $this) + : new Exec\SingleTableDeleteUpdateExecutor($AST, $this); + + case ($AST instanceof AST\UpdateStatement): + $primaryClass = $this->em->getClassMetadata($AST->updateClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new Exec\MultiTableUpdateExecutor($AST, $this) + : new Exec\SingleTableDeleteUpdateExecutor($AST, $this); + + default: + return new Exec\SingleSelectExecutor($AST, $this); + } + } + + /** + * Generates a unique, short SQL table alias. + * + * @param string $tableName Table name + * @param string $dqlAlias The DQL alias. + * + * @return string Generated table alias. + */ + public function getSQLTableAlias($tableName, $dqlAlias = '') + { + $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : ''; + + if ( ! isset($this->tableAliasMap[$tableName])) { + $this->tableAliasMap[$tableName] = (preg_match('/[a-z]/i', $tableName[0]) ? strtolower($tableName[0]) : 't') + . $this->tableAliasCounter++ . '_'; + } + + return $this->tableAliasMap[$tableName]; + } + + /** + * Forces the SqlWalker to use a specific alias for a table name, rather than + * generating an alias on its own. + * + * @param string $tableName + * @param string $alias + * @param string $dqlAlias + * + * @return string + */ + public function setSQLTableAlias($tableName, $alias, $dqlAlias = '') + { + $tableName .= ($dqlAlias) ? '@[' . $dqlAlias . ']' : ''; + + $this->tableAliasMap[$tableName] = $alias; + + return $alias; + } + + /** + * Gets an SQL column alias for a column name. + * + * @param string $columnName + * + * @return string + */ + public function getSQLColumnAlias($columnName) + { + return $this->quoteStrategy->getColumnAlias($columnName, $this->aliasCounter++, $this->platform); + } + + /** + * Generates the SQL JOINs that are necessary for Class Table Inheritance + * for the given class. + * + * @param ClassMetadata $class The class for which to generate the joins. + * @param string $dqlAlias The DQL alias of the class. + * + * @return string The SQL. + */ + private function _generateClassTableInheritanceJoins($class, $dqlAlias) + { + $sql = ''; + + $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // INNER JOIN parent class tables + foreach ($class->parentClasses as $parentClassName) { + $parentClass = $this->em->getClassMetadata($parentClassName); + $tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias); + + // If this is a joined association we must use left joins to preserve the correct result. + $sql .= isset($this->queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER '; + $sql .= 'JOIN ' . $this->quoteStrategy->getTableName($parentClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + // Add filters on the root class + if ($filterSql = $this->generateFilterConditionSQL($parentClass, $tableAlias)) { + $sqlParts[] = $filterSql; + } + + $sql .= implode(' AND ', $sqlParts); + } + + // Ignore subclassing inclusion if partial objects is disallowed + if ($this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + return $sql; + } + + // LEFT JOIN child class tables + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + $sql .= ' LEFT JOIN ' . $this->quoteStrategy->getTableName($subClass, $this->platform) . ' ' . $tableAlias . ' ON '; + + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($subClass, $this->platform) as $columnName) { + $sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql; + } + + /** + * @return string + */ + private function _generateOrderedCollectionOrderByItems() + { + $orderedColumns = array(); + + foreach ($this->selectedClasses as $selectedClass) { + $dqlAlias = $selectedClass['dqlAlias']; + $qComp = $this->queryComponents[$dqlAlias]; + + if ( ! isset($qComp['relation']['orderBy'])) { + continue; + } + + $persister = $this->em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name); + + foreach ($qComp['relation']['orderBy'] as $fieldName => $orientation) { + $columnName = $this->quoteStrategy->getColumnName($fieldName, $qComp['metadata'], $this->platform); + $tableName = ($qComp['metadata']->isInheritanceTypeJoined()) + ? $persister->getOwningTable($fieldName) + : $qComp['metadata']->getTableName(); + + $orderedColumn = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName; + + // OrderByClause should replace an ordered relation. see - DDC-2475 + if (isset($this->orderedColumnsMap[$orderedColumn])) { + continue; + } + + $this->orderedColumnsMap[$orderedColumn] = $orientation; + $orderedColumns[] = $orderedColumn . ' ' . $orientation; + } + } + + return implode(', ', $orderedColumns); + } + + /** + * Generates a discriminator column SQL condition for the class with the given DQL alias. + * + * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions. + * + * @return string + */ + private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases) + { + $sqlParts = array(); + + foreach ($dqlAliases as $dqlAlias) { + $class = $this->queryComponents[$dqlAlias]['metadata']; + + if ( ! $class->isInheritanceTypeSingleTable()) continue; + + $conn = $this->em->getConnection(); + $values = array(); + + if ($class->discriminatorValue !== null) { // discriminators can be 0 + $values[] = $conn->quote($class->discriminatorValue); + } + + foreach ($class->subClasses as $subclassName) { + $values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue); + } + + $sqlParts[] = (($this->useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '') + . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; + } + + $sql = implode(' AND ', $sqlParts); + + return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql; + } + + /** + * Generates the filter SQL for a given entity and table alias. + * + * @param ClassMetadata $targetEntity Metadata of the target entity. + * @param string $targetTableAlias The table alias of the joined/selected table. + * + * @return string The SQL query part to add to a query. + */ + private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + if (!$this->em->hasFilters()) { + return ''; + } + + switch($targetEntity->inheritanceType) { + case ClassMetadata::INHERITANCE_TYPE_NONE: + break; + case ClassMetadata::INHERITANCE_TYPE_JOINED: + // The classes in the inheritance will be added to the query one by one, + // but only the root node is getting filtered + if ($targetEntity->name !== $targetEntity->rootEntityName) { + return ''; + } + break; + case ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE: + // With STI the table will only be queried once, make sure that the filters + // are added to the root entity + $targetEntity = $this->em->getClassMetadata($targetEntity->rootEntityName); + break; + default: + //@todo: throw exception? + return ''; + break; + } + + $filterClauses = array(); + foreach ($this->em->getFilters()->getEnabledFilters() as $filter) { + if ('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + $filterClauses[] = '(' . $filterExpr . ')'; + } + } + + return implode(' AND ', $filterClauses); + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + $limit = $this->query->getMaxResults(); + $offset = $this->query->getFirstResult(); + $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE); + $sql = $this->walkSelectClause($AST->selectClause) + . $this->walkFromClause($AST->fromClause) + . $this->walkWhereClause($AST->whereClause); + + if ($AST->groupByClause) { + $sql .= $this->walkGroupByClause($AST->groupByClause); + } + + if ($AST->havingClause) { + $sql .= $this->walkHavingClause($AST->havingClause); + } + + if ($AST->orderByClause) { + $sql .= $this->walkOrderByClause($AST->orderByClause); + } + + if ( ! $AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) { + $sql .= ' ORDER BY ' . $orderBySql; + } + + if ($limit !== null || $offset !== null) { + $sql = $this->platform->modifyLimitQuery($sql, $limit, $offset); + } + + if ($lockMode === null || $lockMode === false || $lockMode === LockMode::NONE) { + return $sql; + } + + if ($lockMode === LockMode::PESSIMISTIC_READ) { + return $sql . ' ' . $this->platform->getReadLockSQL(); + } + + if ($lockMode === LockMode::PESSIMISTIC_WRITE) { + return $sql . ' ' . $this->platform->getWriteLockSQL(); + } + + if ($lockMode !== LockMode::OPTIMISTIC) { + throw QueryException::invalidLockMode(); + } + + foreach ($this->selectedClasses as $selectedClass) { + if ( ! $selectedClass['class']->isVersioned) { + throw OptimisticLockException::lockFailed($selectedClass['class']->name); + } + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + $this->useSqlTableAliases = false; + $this->rsm->isSelect = false; + + return $this->walkUpdateClause($AST->updateClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + $this->useSqlTableAliases = false; + $this->rsm->isSelect = false; + + return $this->walkDeleteClause($AST->deleteClause) + . $this->walkWhereClause($AST->whereClause); + } + + /** + * Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL. + * This one differs of ->walkIdentificationVariable() because it generates the entity identifiers. + * + * @param string $identVariable + * + * @return string + */ + public function walkEntityIdentificationVariable($identVariable) + { + $class = $this->queryComponents[$identVariable]['metadata']; + $tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable); + $sqlParts = array(); + + foreach ($this->quoteStrategy->getIdentifierColumnNames($class, $this->platform) as $columnName) { + $sqlParts[] = $tableAlias . '.' . $columnName; + } + + return implode(', ', $sqlParts); + } + + /** + * Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL. + * + * @param string $identificationVariable + * @param string $fieldName + * + * @return string The SQL. + */ + public function walkIdentificationVariable($identificationVariable, $fieldName = null) + { + $class = $this->queryComponents[$identificationVariable]['metadata']; + + if ( + $fieldName !== null && $class->isInheritanceTypeJoined() && + isset($class->fieldMappings[$fieldName]['inherited']) + ) { + $class = $this->em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']); + } + + return $this->getSQLTableAlias($class->getTableName(), $identificationVariable); + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + $sql = ''; + + switch ($pathExpr->type) { + case AST\PathExpression::TYPE_STATE_FIELD: + $fieldName = $pathExpr->field; + $dqlAlias = $pathExpr->identificationVariable; + $class = $this->queryComponents[$dqlAlias]['metadata']; + + if ($this->useSqlTableAliases) { + $sql .= $this->walkIdentificationVariable($dqlAlias, $fieldName) . '.'; + } + + $sql .= $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); + break; + + case AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION: + // 1- the owning side: + // Just use the foreign key, i.e. u.group_id + $fieldName = $pathExpr->field; + $dqlAlias = $pathExpr->identificationVariable; + $class = $this->queryComponents[$dqlAlias]['metadata']; + + if (isset($class->associationMappings[$fieldName]['inherited'])) { + $class = $this->em->getClassMetadata($class->associationMappings[$fieldName]['inherited']); + } + + $assoc = $class->associationMappings[$fieldName]; + + if ( ! $assoc['isOwningSide']) { + throw QueryException::associationPathInverseSideNotSupported(); + } + + // COMPOSITE KEYS NOT (YET?) SUPPORTED + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw QueryException::associationPathCompositeKeyNotSupported(); + } + + if ($this->useSqlTableAliases) { + $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'; + } + + $sql .= reset($assoc['targetToSourceKeyColumns']); + break; + + default: + throw QueryException::invalidPathExpression($pathExpr); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : ''); + $sqlSelectExpressions = array_filter(array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions)); + + if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) { + $this->query->setHint(self::HINT_DISTINCT, true); + } + + $addMetaColumns = ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && + $this->query->getHydrationMode() == Query::HYDRATE_OBJECT + || + $this->query->getHydrationMode() != Query::HYDRATE_OBJECT && + $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS); + + foreach ($this->selectedClasses as $selectedClass) { + $class = $selectedClass['class']; + $dqlAlias = $selectedClass['dqlAlias']; + $resultAlias = $selectedClass['resultAlias']; + + // Register as entity or joined entity result + if ($this->queryComponents[$dqlAlias]['relation'] === null) { + $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias); + } else { + $this->rsm->addJoinedEntityResult( + $class->name, + $dqlAlias, + $this->queryComponents[$dqlAlias]['parent'], + $this->queryComponents[$dqlAlias]['relation']['fieldName'] + ); + } + + if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { + // Add discriminator columns to SQL + $rootClass = $this->em->getClassMetadata($class->rootEntityName); + $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias); + $discrColumn = $rootClass->discriminatorColumn; + $columnAlias = $this->getSQLColumnAlias($discrColumn['name']); + + $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias; + + $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']); + } + + // Add foreign key columns to SQL, if necessary + if ( ! $addMetaColumns && ! $class->containsForeignIdentifier) { + continue; + } + + // Add foreign key columns of class and also parent classes + foreach ($class->associationMappings as $assoc) { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } else if ( !$addMetaColumns && !isset($assoc['id'])) { + continue; + } + + $owningClass = (isset($assoc['inherited'])) ? $this->em->getClassMetadata($assoc['inherited']) : $class; + $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias); + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $type = null; + $isIdentifier = (isset($assoc['id']) && $assoc['id'] === true); + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type']; + } + + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, $isIdentifier, $type); + } + } + + // Add foreign key columns to SQL, if necessary + if ( ! $addMetaColumns) { + continue; + } + + // Add foreign key columns of subclasses + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->associationMappings as $assoc) { + // Skip if association is inherited + if (isset($assoc['inherited'])) continue; + + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue; + + foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { + $columnAlias = $this->getSQLColumnAlias($srcColumn); + + $sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias; + + $this->rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn); + } + } + } + } + + $sql .= implode(', ', $sqlSelectExpressions); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + $identificationVarDecls = $fromClause->identificationVariableDeclarations; + $sqlParts = array(); + + foreach ($identificationVarDecls as $identificationVariableDecl) { + $sqlParts[] = $this->walkIdentificationVariableDeclaration($identificationVariableDecl); + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * Walks down a IdentificationVariableDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\IdentificationVariableDeclaration $identificationVariableDecl + * + * @return string + */ + public function walkIdentificationVariableDeclaration($identificationVariableDecl) + { + $sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration); + + if ($identificationVariableDecl->indexBy) { + $this->walkIndexBy($identificationVariableDecl->indexBy); + } + + foreach ($identificationVariableDecl->joins as $join) { + $sql .= $this->walkJoin($join); + } + + return $sql; + } + + /** + * Walks down a IndexBy AST node. + * + * @param AST\IndexBy $indexBy + * + * @return void + */ + public function walkIndexBy($indexBy) + { + $pathExpression = $indexBy->simpleStateFieldPathExpression; + $alias = $pathExpression->identificationVariable; + $field = $pathExpression->field; + + if (isset($this->scalarFields[$alias][$field])) { + $this->rsm->addIndexByScalar($this->scalarFields[$alias][$field]); + + return; + } + + $this->rsm->addIndexBy($alias, $field); + } + + /** + * Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\RangeVariableDeclaration $rangeVariableDeclaration + * + * @return string + */ + public function walkRangeVariableDeclaration($rangeVariableDeclaration) + { + $class = $this->em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName); + $dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable; + + if ($rangeVariableDeclaration->isRoot) { + $this->rootAliases[] = $dqlAlias; + } + + $sql = $this->platform->appendLockHint( + $this->quoteStrategy->getTableName($class, $this->platform) . ' ' . + $this->getSQLTableAlias($class->getTableName(), $dqlAlias), + $this->query->getHint(Query::HINT_LOCK_MODE) + ); + + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + return $sql; + } + + /** + * Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL. + * + * @param AST\JoinAssociationDeclaration $joinAssociationDeclaration + * @param int $joinType + * @param AST\ConditionalExpression $condExpr + * + * @return string + * + * @throws QueryException + */ + public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER, $condExpr = null) + { + $sql = ''; + + $associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression; + $joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable; + $indexBy = $joinAssociationDeclaration->indexBy; + + $relation = $this->queryComponents[$joinedDqlAlias]['relation']; + $targetClass = $this->em->getClassMetadata($relation['targetEntity']); + $sourceClass = $this->em->getClassMetadata($relation['sourceEntity']); + $targetTableName = $this->quoteStrategy->getTableName($targetClass,$this->platform); + + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias); + $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable); + + // Ensure we got the owning side, since it has all mapping info + $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; + + if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias]))) { + if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) { + throw QueryException::iterateWithFetchJoinNotAllowed($assoc); + } + } + + $targetTableJoin = null; + + // This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot + // be the owning side and previously we ensured that $assoc is always the owning side of the associations. + // The owning side is necessary at this point because only it contains the JoinColumn information. + switch (true) { + case ($assoc['type'] & ClassMetadata::TO_ONE): + $conditions = array(); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + if ($relation['isOwningSide']) { + $conditions[] = $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn; + + continue; + } + + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $quotedSourceColumn; + } + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $targetTableJoin = array( + 'table' => $targetTableName . ' ' . $targetTableAlias, + 'condition' => implode(' AND ', $conditions), + ); + break; + + case ($assoc['type'] == ClassMetadata::MANY_TO_MANY): + // Join relation table + $joinTable = $assoc['joinTable']; + $joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias); + $joinTableName = $this->quoteStrategy->getJoinTableName($assoc, $sourceClass, $this->platform); + + $conditions = array(); + $relationColumns = ($relation['isOwningSide']) + ? $assoc['joinTable']['joinColumns'] + : $assoc['joinTable']['inverseJoinColumns']; + + foreach ($relationColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn; + } + + $sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions); + + // Join target table + $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN '; + + $conditions = array(); + $relationColumns = ($relation['isOwningSide']) + ? $assoc['joinTable']['inverseJoinColumns'] + : $assoc['joinTable']['joinColumns']; + + foreach ($relationColumns as $joinColumn) { + $quotedSourceColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); + $quotedTargetColumn = $this->quoteStrategy->getReferencedJoinColumnName($joinColumn, $targetClass, $this->platform); + + $conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $quotedSourceColumn; + } + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $targetTableJoin = array( + 'table' => $targetTableName . ' ' . $targetTableAlias, + 'condition' => implode(' AND ', $conditions), + ); + break; + + default: + throw new \BadMethodCallException('Type of association must be one of *_TO_ONE or MANY_TO_MANY'); + } + + // Handle WITH clause + $withCondition = (null === $condExpr) ? '' : ('(' . $this->walkConditionalExpression($condExpr) . ')'); + + if ($targetClass->isInheritanceTypeJoined()) { + $ctiJoins = $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); + // If we have WITH condition, we need to build nested joins for target class table and cti joins + if ($withCondition) { + $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition']; + } else { + $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins; + } + } else { + $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition']; + } + + if ($withCondition) { + $sql .= ' AND ' . $withCondition; + } + + // Apply the indexes + if ($indexBy) { + // For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently. + $this->walkIndexBy($indexBy); + } else if (isset($relation['indexBy'])) { + $this->rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + return $function->getSql($this); + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + $orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems); + + if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') { + $orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems); + } + + return ' ORDER BY ' . implode(', ', $orderByItems); + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + $type = strtoupper($orderByItem->type); + $expr = $orderByItem->expression; + $sql = ($expr instanceof AST\Node) + ? $expr->dispatch($this) + : $this->walkResultVariable($this->queryComponents[$expr]['token']['value']); + + $this->orderedColumnsMap[$sql] = $type; + + if ($expr instanceof AST\Subselect) { + return '(' . $sql . ') ' . $type; + } + + return $sql . ' ' . $type; + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression); + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + $joinType = $join->joinType; + $joinDeclaration = $join->joinAssociationDeclaration; + + $sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) + ? ' LEFT JOIN ' + : ' INNER JOIN '; + + switch (true) { + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration): + $class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName); + $dqlAlias = $joinDeclaration->aliasIdentificationVariable; + $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + $condition = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')'; + $condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER) + ? ' AND ' + : ' ON '; + + $sql .= $this->walkRangeVariableDeclaration($joinDeclaration); + + $conditions = array($condition); + + // Apply remaining inheritance restrictions + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias)); + + if ($discrSql) { + $conditions[] = $discrSql; + } + + // Apply the filters + $filterExpr = $this->generateFilterConditionSQL($class, $tableAlias); + + if ($filterExpr) { + $conditions[] = $filterExpr; + } + + $sql .= $condExprConjunction . implode(' AND ', $conditions); + break; + + case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration): + $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression); + break; + } + + return $sql; + } + + /** + * Walks down a CaseExpression AST node and generates the corresponding SQL. + * + * @param AST\CoalesceExpression|AST\NullIfExpression|AST\GeneralCaseExpression|AST\SimpleCaseExpression $expression + * + * @return string The SQL. + */ + public function walkCaseExpression($expression) + { + switch (true) { + case ($expression instanceof AST\CoalesceExpression): + return $this->walkCoalesceExpression($expression); + + case ($expression instanceof AST\NullIfExpression): + return $this->walkNullIfExpression($expression); + + case ($expression instanceof AST\GeneralCaseExpression): + return $this->walkGeneralCaseExpression($expression); + + case ($expression instanceof AST\SimpleCaseExpression): + return $this->walkSimpleCaseExpression($expression); + + default: + return ''; + } + } + + /** + * Walks down a CoalesceExpression AST node and generates the corresponding SQL. + * + * @param AST\CoalesceExpression $coalesceExpression + * + * @return string The SQL. + */ + public function walkCoalesceExpression($coalesceExpression) + { + $sql = 'COALESCE('; + + $scalarExpressions = array(); + + foreach ($coalesceExpression->scalarExpressions as $scalarExpression) { + $scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression); + } + + $sql .= implode(', ', $scalarExpressions) . ')'; + + return $sql; + } + + /** + * Walks down a NullIfExpression AST node and generates the corresponding SQL. + * + * @param AST\NullIfExpression $nullIfExpression + * + * @return string The SQL. + */ + public function walkNullIfExpression($nullIfExpression) + { + $firstExpression = is_string($nullIfExpression->firstExpression) + ? $this->conn->quote($nullIfExpression->firstExpression) + : $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression); + + $secondExpression = is_string($nullIfExpression->secondExpression) + ? $this->conn->quote($nullIfExpression->secondExpression) + : $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression); + + return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')'; + } + + /** + * Walks down a GeneralCaseExpression AST node and generates the corresponding SQL. + * + * @param AST\GeneralCaseExpression $generalCaseExpression + * + * @return string The SQL. + */ + public function walkGeneralCaseExpression(AST\GeneralCaseExpression $generalCaseExpression) + { + $sql = 'CASE'; + + foreach ($generalCaseExpression->whenClauses as $whenClause) { + $sql .= ' WHEN ' . $this->walkConditionalExpression($whenClause->caseConditionExpression); + $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($whenClause->thenScalarExpression); + } + + $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($generalCaseExpression->elseScalarExpression) . ' END'; + + return $sql; + } + + /** + * Walks down a SimpleCaseExpression AST node and generates the corresponding SQL. + * + * @param AST\SimpleCaseExpression $simpleCaseExpression + * + * @return string The SQL. + */ + public function walkSimpleCaseExpression($simpleCaseExpression) + { + $sql = 'CASE ' . $this->walkStateFieldPathExpression($simpleCaseExpression->caseOperand); + + foreach ($simpleCaseExpression->simpleWhenClauses as $simpleWhenClause) { + $sql .= ' WHEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->caseScalarExpression); + $sql .= ' THEN ' . $this->walkSimpleArithmeticExpression($simpleWhenClause->thenScalarExpression); + } + + $sql .= ' ELSE ' . $this->walkSimpleArithmeticExpression($simpleCaseExpression->elseScalarExpression) . ' END'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + $sql = ''; + $expr = $selectExpression->expression; + $hidden = $selectExpression->hiddenAliasResultVariable; + + switch (true) { + case ($expr instanceof AST\PathExpression): + if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) { + throw QueryException::invalidPathExpression($expr); + } + + $fieldName = $expr->field; + $dqlAlias = $expr->identificationVariable; + $qComp = $this->queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName; + $tableName = ($class->isInheritanceTypeJoined()) + ? $this->em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName) + : $class->getTableName(); + + $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); + $columnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); + $columnAlias = $this->getSQLColumnAlias($class->fieldMappings[$fieldName]['columnName']); + + $col = $sqlTableAlias . '.' . $columnName; + + $fieldType = $class->getTypeOfField($fieldName); + + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($fieldType); + $col = $type->convertToPHPValueSQL($col, $this->conn->getDatabasePlatform()); + } + + $sql .= $col . ' AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); + $this->scalarFields[$dqlAlias][$fieldName] = $columnAlias; + } + break; + + case ($expr instanceof AST\AggregateExpression): + case ($expr instanceof AST\Functions\FunctionNode): + case ($expr instanceof AST\SimpleArithmeticExpression): + case ($expr instanceof AST\ArithmeticTerm): + case ($expr instanceof AST\ArithmeticFactor): + case ($expr instanceof AST\ParenthesisExpression): + case ($expr instanceof AST\Literal): + case ($expr instanceof AST\NullIfExpression): + case ($expr instanceof AST\CoalesceExpression): + case ($expr instanceof AST\GeneralCaseExpression): + case ($expr instanceof AST\SimpleCaseExpression): + $columnAlias = $this->getSQLColumnAlias('sclr'); + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + // We cannot resolve field type here; assume 'string'. + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + } + break; + + case ($expr instanceof AST\Subselect): + $columnAlias = $this->getSQLColumnAlias('sclr'); + $resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + + if ( ! $hidden) { + // We cannot resolve field type here; assume 'string'. + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + } + break; + + case ($expr instanceof AST\NewObjectExpression): + $sql .= $this->walkNewObject($expr,$selectExpression->fieldIdentificationVariable); + break; + + default: + // IdentificationVariable or PartialObjectExpression + if ($expr instanceof AST\PartialObjectExpression) { + $dqlAlias = $expr->identificationVariable; + $partialFieldSet = $expr->partialFieldSet; + } else { + $dqlAlias = $expr; + $partialFieldSet = array(); + } + + $queryComp = $this->queryComponents[$dqlAlias]; + $class = $queryComp['metadata']; + $resultAlias = $selectExpression->fieldIdentificationVariable ?: null; + + if ( ! isset($this->selectedClasses[$dqlAlias])) { + $this->selectedClasses[$dqlAlias] = array( + 'class' => $class, + 'dqlAlias' => $dqlAlias, + 'resultAlias' => $resultAlias + ); + } + + $sqlParts = array(); + + // Select all fields from the queried class + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) { + continue; + } + + $tableName = (isset($mapping['inherited'])) + ? $this->em->getClassMetadata($mapping['inherited'])->getTableName() + : $class->getTableName(); + + $sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias); + $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $class, $this->platform); + + $col = $sqlTableAlias . '.' . $quotedColumnName; + + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($class->getTypeOfField($fieldName)); + $col = $type->convertToPHPValueSQL($col, $this->platform); + } + + $sqlParts[] = $col . ' AS '. $columnAlias; + + $this->scalarResultAliasMap[$resultAlias][] = $columnAlias; + + $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name); + } + + // Add any additional fields of subclasses (excluding inherited fields) + // 1) on Single Table Inheritance: always, since its marginal overhead + // 2) on Class Table Inheritance only if partial objects are disallowed, + // since it requires outer joining subtables. + if ($class->isInheritanceTypeSingleTable() || ! $this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); + + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) { + continue; + } + + $columnAlias = $this->getSQLColumnAlias($mapping['columnName']); + $quotedColumnName = $this->quoteStrategy->getColumnName($fieldName, $subClass, $this->platform); + + $col = $sqlTableAlias . '.' . $quotedColumnName; + + if (isset($subClass->fieldMappings[$fieldName]['requireSQLConversion'])) { + $type = Type::getType($subClass->getTypeOfField($fieldName)); + $col = $type->convertToPHPValueSQL($col, $this->platform); + } + + $sqlParts[] = $col . ' AS ' . $columnAlias; + + $this->scalarResultAliasMap[$resultAlias][] = $columnAlias; + + $this->rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName); + } + } + } + + $sql .= implode(', ', $sqlParts); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + $useAliasesBefore = $this->useSqlTableAliases; + $rootAliasesBefore = $this->rootAliases; + + $this->rootAliases = array(); // reset the rootAliases for the subselect + $this->useSqlTableAliases = true; + + $sql = $this->walkSimpleSelectClause($subselect->simpleSelectClause); + $sql .= $this->walkSubselectFromClause($subselect->subselectFromClause); + $sql .= $this->walkWhereClause($subselect->whereClause); + + $sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : ''; + $sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : ''; + $sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : ''; + + $this->rootAliases = $rootAliasesBefore; // put the main aliases back + $this->useSqlTableAliases = $useAliasesBefore; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; + $sqlParts = array (); + + foreach ($identificationVarDecls as $subselectIdVarDecl) { + $sqlParts[] = $this->walkIdentificationVariableDeclaration($subselectIdVarDecl); + } + + return ' FROM ' . implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + return 'SELECT' . ($simpleSelectClause->isDistinct ? ' DISTINCT' : '') + . $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression); + } + + /** + * @param \Doctrine\ORM\Query\AST\ParenthesisExpression $parenthesisExpression + * + * @return string. + */ + public function walkParenthesisExpression(AST\ParenthesisExpression $parenthesisExpression) + { + return sprintf('(%s)', $parenthesisExpression->expression->dispatch($this)); + } + + /** + * @param AST\NewObjectExpression $newObjectExpression + * + * @return string The SQL. + */ + public function walkNewObject($newObjectExpression, $newObjectResultAlias=null) + { + $sqlSelectExpressions = array(); + $objIndex = $newObjectResultAlias?:$this->newObjectCounter++; + + foreach ($newObjectExpression->args as $argIndex => $e) { + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr'); + $fieldType = 'string'; + + switch (true) { + case ($e instanceof AST\NewObjectExpression): + $sqlSelectExpressions[] = $e->dispatch($this); + break; + + case ($e instanceof AST\Subselect): + $sqlSelectExpressions[] = '(' . $e->dispatch($this) . ') AS ' . $columnAlias; + break; + + case ($e instanceof AST\PathExpression): + $fieldName = $e->field; + $dqlAlias = $e->identificationVariable; + $qComp = $this->queryComponents[$dqlAlias]; + $class = $qComp['metadata']; + $fieldType = $class->getTypeOfField($fieldName); + + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + + case ($e instanceof AST\Literal): + switch ($e->type) { + case AST\Literal::BOOLEAN: + $fieldType = 'boolean'; + break; + + case AST\Literal::NUMERIC: + $fieldType = is_float($e->value) ? 'float' : 'integer'; + break; + } + + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + + default: + $sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias; + break; + } + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + $this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); + + $this->rsm->newObjectMappings[$columnAlias] = array( + 'className' => $newObjectExpression->className, + 'objIndex' => $objIndex, + 'argIndex' => $argIndex + ); + } + + return implode(', ', $sqlSelectExpressions); + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + $expr = $simpleSelectExpression->expression; + $sql = ' '; + + switch (true) { + case ($expr instanceof AST\PathExpression): + $sql .= $this->walkPathExpression($expr); + break; + + case ($expr instanceof AST\AggregateExpression): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; + break; + + case ($expr instanceof AST\Subselect): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $columnAlias = 'sclr' . $this->aliasCounter++; + $this->scalarResultAliasMap[$alias] = $columnAlias; + + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + break; + + case ($expr instanceof AST\Functions\FunctionNode): + case ($expr instanceof AST\SimpleArithmeticExpression): + case ($expr instanceof AST\ArithmeticTerm): + case ($expr instanceof AST\ArithmeticFactor): + case ($expr instanceof AST\Literal): + case ($expr instanceof AST\NullIfExpression): + case ($expr instanceof AST\CoalesceExpression): + case ($expr instanceof AST\GeneralCaseExpression): + case ($expr instanceof AST\SimpleCaseExpression): + $alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++; + + $columnAlias = $this->getSQLColumnAlias('sclr'); + $this->scalarResultAliasMap[$alias] = $columnAlias; + + $sql .= $expr->dispatch($this) . ' AS ' . $columnAlias; + break; + + case ($expr instanceof AST\ParenthesisExpression): + $sql .= $this->walkParenthesisExpression($expr); + break; + + default: // IdentificationVariable + $sql .= $this->walkEntityIdentificationVariable($expr); + break; + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') + . $this->walkSimpleArithmeticExpression($aggExpression->pathExpression) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + $sqlParts = array(); + + foreach ($groupByClause->groupByItems as $groupByItem) { + $sqlParts[] = $this->walkGroupByItem($groupByItem); + } + + return ' GROUP BY ' . implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + // StateFieldPathExpression + if ( ! is_string($groupByItem)) { + return $this->walkPathExpression($groupByItem); + } + + // ResultVariable + if (isset($this->queryComponents[$groupByItem]['resultVariable'])) { + $resultVariable = $this->queryComponents[$groupByItem]['resultVariable']; + + if ($resultVariable instanceof AST\PathExpression) { + return $this->walkPathExpression($resultVariable); + } + + if (isset($resultVariable->pathExpression)) { + return $this->walkPathExpression($resultVariable->pathExpression); + } + + return $this->walkResultVariable($groupByItem); + } + + // IdentificationVariable + $sqlParts = array(); + + foreach ($this->queryComponents[$groupByItem]['metadata']->fieldNames as $field) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field); + $item->type = AST\PathExpression::TYPE_STATE_FIELD; + + $sqlParts[] = $this->walkPathExpression($item); + } + + foreach ($this->queryComponents[$groupByItem]['metadata']->associationMappings as $mapping) { + if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) { + $item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']); + $item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + + $sqlParts[] = $this->walkPathExpression($item); + } + } + + return implode(', ', $sqlParts); + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + $class = $this->em->getClassMetadata($deleteClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform); + + $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable); + $this->rootAliases[] = $deleteClause->aliasIdentificationVariable; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + $class = $this->em->getClassMetadata($updateClause->abstractSchemaName); + $tableName = $class->getTableName(); + $sql = 'UPDATE ' . $this->quoteStrategy->getTableName($class, $this->platform); + + $this->setSQLTableAlias($tableName, $tableName, $updateClause->aliasIdentificationVariable); + $this->rootAliases[] = $updateClause->aliasIdentificationVariable; + + $sql .= ' SET ' . implode(', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + $useTableAliasesBefore = $this->useSqlTableAliases; + $this->useSqlTableAliases = false; + + $sql = $this->walkPathExpression($updateItem->pathExpression) . ' = '; + $newValue = $updateItem->newValue; + + switch (true) { + case ($newValue instanceof AST\Node): + $sql .= $newValue->dispatch($this); + break; + + case ($newValue === null): + $sql .= 'NULL'; + break; + + default: + $sql .= $this->conn->quote($newValue); + break; + } + + $this->useSqlTableAliases = $useTableAliasesBefore; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + $condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : ''; + $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->rootAliases); + + if ($this->em->hasFilters()) { + $filterClauses = array(); + foreach ($this->rootAliases as $dqlAlias) { + $class = $this->queryComponents[$dqlAlias]['metadata']; + $tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); + + if ($filterExpr = $this->generateFilterConditionSQL($class, $tableAlias)) { + $filterClauses[] = $filterExpr; + } + } + + if (count($filterClauses)) { + if ($condSql) { + $condSql = '(' . $condSql . ') AND '; + } + + $condSql .= implode(' AND ', $filterClauses); + } + } + + if ($condSql) { + return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql); + } + + if ($discrSql) { + return ' WHERE ' . $discrSql; + } + + return ''; + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + // Phase 2 AST optimization: Skip processing of ConditionalExpression + // if only one ConditionalTerm is defined + if ( ! ($condExpr instanceof AST\ConditionalExpression)) { + return $this->walkConditionalTerm($condExpr); + } + + return implode(' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + // Phase 2 AST optimization: Skip processing of ConditionalTerm + // if only one ConditionalFactor is defined + if ( ! ($condTerm instanceof AST\ConditionalTerm)) { + return $this->walkConditionalFactor($condTerm); + } + + return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + // Phase 2 AST optimization: Skip processing of ConditionalFactor + // if only one ConditionalPrimary is defined + return ( ! ($factor instanceof AST\ConditionalFactor)) + ? $this->walkConditionalPrimary($factor) + : ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary); + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($primary) + { + if ($primary->isSimpleConditionalExpression()) { + return $primary->simpleConditionalExpression->dispatch($this); + } + + if ($primary->isConditionalExpression()) { + $condExpr = $primary->conditionalExpression; + + return '(' . $this->walkConditionalExpression($condExpr) . ')'; + } + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + $sql = ($existsExpr->not) ? 'NOT ' : ''; + + $sql .= 'EXISTS (' . $this->walkSubselect($existsExpr->subselect) . ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + $sql = $collMemberExpr->not ? 'NOT ' : ''; + $sql .= 'EXISTS (SELECT 1 FROM '; + + $entityExpr = $collMemberExpr->entityExpression; + $collPathExpr = $collMemberExpr->collectionValuedPathExpression; + + $fieldName = $collPathExpr->field; + $dqlAlias = $collPathExpr->identificationVariable; + + $class = $this->queryComponents[$dqlAlias]['metadata']; + + switch (true) { + // InputParameter + case ($entityExpr instanceof AST\InputParameter): + $dqlParamKey = $entityExpr->name; + $entitySql = '?'; + break; + + // SingleValuedAssociationPathExpression | IdentificationVariable + case ($entityExpr instanceof AST\PathExpression): + $entitySql = $this->walkPathExpression($entityExpr); + break; + + default: + throw new \BadMethodCallException("Not implemented"); + } + + $assoc = $class->associationMappings[$fieldName]; + + if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + $sql .= $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; + $sqlParts = array(); + + foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $this->platform); + + $sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn; + } + + foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql; + } + + $sql .= implode(' AND ', $sqlParts); + } else { // many-to-many + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']]; + $joinTable = $owningAssoc['joinTable']; + + // SQL table aliases + $joinTableAlias = $this->getSQLTableAlias($joinTable['name']); + $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName()); + $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + + // join to target table + $sql .= $this->quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $this->platform) . ' ' . $joinTableAlias + . ' INNER JOIN ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' ' . $targetTableAlias . ' ON '; + + // join conditions + $joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; + $joinSqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $targetClass, $this->platform); + + $joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn; + } + + $sql .= implode(' AND ', $joinSqlParts); + $sql .= ' WHERE '; + + $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; + $sqlParts = array(); + + foreach ($joinColumns as $joinColumn) { + $targetColumn = $this->quoteStrategy->getColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $class, $this->platform); + + $sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn; + } + + foreach ($this->quoteStrategy->getIdentifierColumnNames($targetClass, $this->platform) as $targetColumnName) { + if (isset($dqlParamKey)) { + $this->parserResult->addParameterMapping($dqlParamKey, $this->sqlParamIndex++); + } + + $sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' IN (' . $entitySql . ')'; + } + + $sql .= implode(' AND ', $sqlParts); + } + + return $sql . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + $sizeFunc = new AST\Functions\SizeFunction('size'); + $sizeFunc->collectionPathExpression = $emptyCollCompExpr->expression; + + return $sizeFunc->getSql($this) . ($emptyCollCompExpr->not ? ' > 0' : ' = 0'); + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + $expression = $nullCompExpr->expression; + $comparison = ' IS' . ($nullCompExpr->not ? ' NOT' : '') . ' NULL'; + + // Handle ResultVariable + if (is_string($expression) && isset($this->queryComponents[$expression]['resultVariable'])) { + return $this->walkResultVariable($expression) . $comparison; + } + + // Handle InputParameter mapping inclusion to ParserResult + if ($expression instanceof AST\InputParameter) { + return $this->walkInputParameter($expression) . $comparison; + } + + return $expression->dispatch($this) . $comparison; + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + $sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN ('; + + $sql .= ($inExpr->subselect) + ? $this->walkSubselect($inExpr->subselect) + : implode(', ', array_map(array($this, 'walkInParameter'), $inExpr->literals)); + + $sql .= ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInstanceOfExpression($instanceOfExpr) + { + $sql = ''; + + $dqlAlias = $instanceOfExpr->identificationVariable; + $discrClass = $class = $this->queryComponents[$dqlAlias]['metadata']; + + if ($class->discriminatorColumn) { + $discrClass = $this->em->getClassMetadata($class->rootEntityName); + } + + if ($this->useSqlTableAliases) { + $sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.'; + } + + $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN '); + + $sqlParameterList = array(); + + foreach ($instanceOfExpr->value as $parameter) { + if ($parameter instanceof AST\InputParameter) { + $this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue'); + + $sqlParameterList[] = $this->walkInputParameter($parameter); + + continue; + } + + // Get name from ClassMetadata to resolve aliases. + $entityClassName = $this->em->getClassMetadata($parameter)->name; + $discriminatorValue = $class->discriminatorValue; + + if ($entityClassName !== $class->name) { + $discrMap = array_flip($class->discriminatorMap); + + if ( ! isset($discrMap[$entityClassName])) { + throw QueryException::instanceOfUnrelatedClass($entityClassName, $class->rootEntityName); + } + + $discriminatorValue = $discrMap[$entityClassName]; + } + + $sqlParameterList[] = $this->conn->quote($discriminatorValue); + } + + $sql .= '(' . implode(', ', $sqlParameterList) . ')'; + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInParameter($inParam) + { + return $inParam instanceof AST\InputParameter + ? $this->walkInputParameter($inParam) + : $this->walkLiteral($inParam); + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + switch ($literal->type) { + case AST\Literal::STRING: + return $this->conn->quote($literal->value); + + case AST\Literal::BOOLEAN: + $bool = strtolower($literal->value) == 'true' ? true : false; + $boolVal = $this->conn->getDatabasePlatform()->convertBooleans($bool); + + return $boolVal; + + case AST\Literal::NUMERIC: + return $literal->value; + + default: + throw QueryException::invalidLiteral($literal); + } + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + $sql = $this->walkArithmeticExpression($betweenExpr->expression); + + if ($betweenExpr->not) { + $sql .= ' NOT'; + } + + $sql .= ' BETWEEN ' . $this->walkArithmeticExpression($betweenExpr->leftBetweenExpression) + . ' AND ' . $this->walkArithmeticExpression($betweenExpr->rightBetweenExpression); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + $stringExpr = $likeExpr->stringExpression; + $leftExpr = (is_string($stringExpr) && isset($this->queryComponents[$stringExpr]['resultVariable'])) + ? $this->walkResultVariable($stringExpr) + : $stringExpr->dispatch($this); + + $sql = $leftExpr . ($likeExpr->not ? ' NOT' : '') . ' LIKE '; + + if ($likeExpr->stringPattern instanceof AST\InputParameter) { + $sql .= $this->walkInputParameter($likeExpr->stringPattern); + } elseif ($likeExpr->stringPattern instanceof AST\Functions\FunctionNode) { + $sql .= $this->walkFunction($likeExpr->stringPattern); + } elseif ($likeExpr->stringPattern instanceof AST\PathExpression) { + $sql .= $this->walkPathExpression($likeExpr->stringPattern); + } else { + $sql .= $this->walkLiteral($likeExpr->stringPattern); + } + + if ($likeExpr->escapeChar) { + $sql .= ' ESCAPE ' . $this->walkLiteral($likeExpr->escapeChar); + } + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + return $this->walkPathExpression($stateFieldPathExpression); + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + $leftExpr = $compExpr->leftExpression; + $rightExpr = $compExpr->rightExpression; + $sql = ''; + + $sql .= ($leftExpr instanceof AST\Node) + ? $leftExpr->dispatch($this) + : (is_numeric($leftExpr) ? $leftExpr : $this->conn->quote($leftExpr)); + + $sql .= ' ' . $compExpr->operator . ' '; + + $sql .= ($rightExpr instanceof AST\Node) + ? $rightExpr->dispatch($this) + : (is_numeric($rightExpr) ? $rightExpr : $this->conn->quote($rightExpr)); + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + $this->parserResult->addParameterMapping($inputParam->name, $this->sqlParamIndex++); + + $parameter = $this->query->getParameter($inputParam->name); + + if ($parameter && Type::hasType($type = $parameter->getType())) { + return Type::getType($type)->convertToDatabaseValueSQL('?', $this->platform); + } + + return '?'; + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + return ($arithmeticExpr->isSimpleArithmeticExpression()) + ? $this->walkSimpleArithmeticExpression($arithmeticExpr->simpleArithmeticExpression) + : '(' . $this->walkSubselect($arithmeticExpr->subselect) . ')'; + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + if ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression)) { + return $this->walkArithmeticTerm($simpleArithmeticExpr); + } + + return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)); + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + if (is_string($term)) { + return (isset($this->queryComponents[$term])) + ? $this->walkResultVariable($this->queryComponents[$term]['token']['value']) + : $term; + } + + // Phase 2 AST optimization: Skip processing of ArithmeticTerm + // if only one ArithmeticFactor is defined + if ( ! ($term instanceof AST\ArithmeticTerm)) { + return $this->walkArithmeticFactor($term); + } + + return implode(' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)); + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + if (is_string($factor)) { + return $factor; + } + + // Phase 2 AST optimization: Skip processing of ArithmeticFactor + // if only one ArithmeticPrimary is defined + if ( ! ($factor instanceof AST\ArithmeticFactor)) { + return $this->walkArithmeticPrimary($factor); + } + + $sign = $factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''); + + return $sign . $this->walkArithmeticPrimary($factor->arithmeticPrimary); + } + + /** + * Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $primary + * + * @return string The SQL. + */ + public function walkArithmeticPrimary($primary) + { + if ($primary instanceof AST\SimpleArithmeticExpression) { + return '(' . $this->walkSimpleArithmeticExpression($primary) . ')'; + } + + if ($primary instanceof AST\Node) { + return $primary->dispatch($this); + } + + return $this->walkEntityIdentificationVariable($primary); + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + return (is_string($stringPrimary)) + ? $this->conn->quote($stringPrimary) + : $stringPrimary->dispatch($this); + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + $resultAlias = $this->scalarResultAliasMap[$resultVariable]; + + if (is_array($resultAlias)) { + return implode(', ', $resultAlias); + } + + return $resultAlias; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php new file mode 100644 index 0000000..9ddd86b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalker.php @@ -0,0 +1,479 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Interface for walkers of DQL ASTs (abstract syntax trees). + * + * @author Roman Borschel + * @since 2.0 + */ +interface TreeWalker +{ + /** + * Initializes TreeWalker with important information about the ASTs to be walked. + * + * @param \Doctrine\ORM\AbstractQuery $query The parsed Query. + * @param \Doctrine\ORM\Query\ParserResult $parserResult The result of the parsing process. + * @param array $queryComponents The query components (symbol table). + */ + public function __construct($query, $parserResult, array $queryComponents); + + /** + * Returns internal queryComponents array. + * + * @return array + */ + public function getQueryComponents(); + + /** + * Sets or overrides a query component for a given dql alias. + * + * @param string $dqlAlias The DQL alias. + * @param array $queryComponent + * + * @return void + */ + public function setQueryComponent($dqlAlias, array $queryComponent); + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\SelectStatement $AST + * + * @return string The SQL. + */ + function walkSelectStatement(AST\SelectStatement $AST); + + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SelectClause $selectClause + * + * @return string The SQL. + */ + function walkSelectClause($selectClause); + + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @param AST\FromClause $fromClause + * + * @return string The SQL. + */ + function walkFromClause($fromClause); + + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @param AST\Functions\FunctionNode $function + * + * @return string The SQL. + */ + function walkFunction($function); + + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param AST\OrderByClause $orderByClause + * + * @return string The SQL. + */ + function walkOrderByClause($orderByClause); + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param AST\OrderByItem $orderByItem + * + * @return string The SQL. + */ + function walkOrderByItem($orderByItem); + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param AST\HavingClause $havingClause + * + * @return string The SQL. + */ + function walkHavingClause($havingClause); + + /** + * Walks down a Join AST node and creates the corresponding SQL. + * + * @param AST\Join $join + * + * @return string The SQL. + */ + function walkJoin($join); + + /** + * Walks down a SelectExpression AST node and generates the corresponding SQL. + * + * @param AST\SelectExpression $selectExpression + * + * @return string The SQL. + */ + function walkSelectExpression($selectExpression); + + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\QuantifiedExpression $qExpr + * + * @return string The SQL. + */ + function walkQuantifiedExpression($qExpr); + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param AST\Subselect $subselect + * + * @return string The SQL. + */ + function walkSubselect($subselect); + + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SubselectFromClause $subselectFromClause + * + * @return string The SQL. + */ + function walkSubselectFromClause($subselectFromClause); + + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleSelectClause $simpleSelectClause + * + * @return string The SQL. + */ + function walkSimpleSelectClause($simpleSelectClause); + + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleSelectExpression $simpleSelectExpression + * + * @return string The SQL. + */ + function walkSimpleSelectExpression($simpleSelectExpression); + + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\AggregateExpression $aggExpression + * + * @return string The SQL. + */ + function walkAggregateExpression($aggExpression); + + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param AST\GroupByClause $groupByClause + * + * @return string The SQL. + */ + function walkGroupByClause($groupByClause); + + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param AST\PathExpression|string $groupByItem + * + * @return string The SQL. + */ + function walkGroupByItem($groupByItem); + + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateStatement $AST + * + * @return string The SQL. + */ + function walkUpdateStatement(AST\UpdateStatement $AST); + + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param AST\DeleteStatement $AST + * + * @return string The SQL. + */ + function walkDeleteStatement(AST\DeleteStatement $AST); + + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param AST\DeleteClause $deleteClause + * + * @return string The SQL. + */ + function walkDeleteClause(AST\DeleteClause $deleteClause); + + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateClause $updateClause + * + * @return string The SQL. + */ + function walkUpdateClause($updateClause); + + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param AST\UpdateItem $updateItem + * + * @return string The SQL. + */ + function walkUpdateItem($updateItem); + + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * WhereClause or not, the appropriate discriminator sql is added. + * + * @param AST\WhereClause $whereClause + * + * @return string The SQL. + */ + function walkWhereClause($whereClause); + + /** + * Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalExpression $condExpr + * + * @return string The SQL. + */ + function walkConditionalExpression($condExpr); + + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalTerm $condTerm + * + * @return string The SQL. + */ + function walkConditionalTerm($condTerm); + + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalFactor $factor + * + * @return string The SQL. + */ + function walkConditionalFactor($factor); + + /** + * Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL. + * + * @param AST\ConditionalPrimary $primary + * + * @return string The SQL. + */ + function walkConditionalPrimary($primary); + + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ExistsExpression $existsExpr + * + * @return string The SQL. + */ + function walkExistsExpression($existsExpr); + + /** + * Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\CollectionMemberExpression $collMemberExpr + * + * @return string The SQL. + */ + function walkCollectionMemberExpression($collMemberExpr); + + /** + * Walks down an EmptyCollectionComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\EmptyCollectionComparisonExpression $emptyCollCompExpr + * + * @return string The SQL. + */ + function walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\NullComparisonExpression $nullCompExpr + * + * @return string The SQL. + */ + function walkNullComparisonExpression($nullCompExpr); + + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\InExpression $inExpr + * + * @return string The SQL. + */ + function walkInExpression($inExpr); + + /** + * Walks down an InstanceOfExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\InstanceOfExpression $instanceOfExpr + * + * @return string The SQL. + */ + function walkInstanceOfExpression($instanceOfExpr); + + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $literal + * + * @return string The SQL. + */ + function walkLiteral($literal); + + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\BetweenExpression $betweenExpr + * + * @return string The SQL. + */ + function walkBetweenExpression($betweenExpr); + + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\LikeExpression $likeExpr + * + * @return string The SQL. + */ + function walkLikeExpression($likeExpr); + + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\PathExpression $stateFieldPathExpression + * + * @return string The SQL. + */ + function walkStateFieldPathExpression($stateFieldPathExpression); + + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ComparisonExpression $compExpr + * + * @return string The SQL. + */ + function walkComparisonExpression($compExpr); + + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param AST\InputParameter $inputParam + * + * @return string The SQL. + */ + function walkInputParameter($inputParam); + + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\ArithmeticExpression $arithmeticExpr + * + * @return string The SQL. + */ + function walkArithmeticExpression($arithmeticExpr); + + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed $term + * + * @return string The SQL. + */ + function walkArithmeticTerm($term); + + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $stringPrimary + * + * @return string The SQL. + */ + function walkStringPrimary($stringPrimary); + + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed $factor + * + * @return string The SQL. + */ + function walkArithmeticFactor($factor); + + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param AST\SimpleArithmeticExpression $simpleArithmeticExpr + * + * @return string The SQL. + */ + function walkSimpleArithmeticExpression($simpleArithmeticExpr); + + /** + * Walks down a PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed $pathExpr + * + * @return string The SQL. + */ + function walkPathExpression($pathExpr); + + /** + * Walks down a ResultVariable that represents an AST node, thereby generating the appropriate SQL. + * + * @param string $resultVariable + * + * @return string The SQL. + */ + function walkResultVariable($resultVariable); + + /** + * Gets an executor that can be used to execute the result of this walker. + * + * @param AST\DeleteStatement|AST\UpdateStatement|AST\SelectStatement $AST + * + * @return Exec\AbstractSqlExecutor + */ + function getExecutor($AST); +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php new file mode 100644 index 0000000..e95155c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerAdapter.php @@ -0,0 +1,440 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * An adapter implementation of the TreeWalker interface. The methods in this class + * are empty. This class exists as convenience for creating tree walkers. + * + * @author Roman Borschel + * @since 2.0 + */ +abstract class TreeWalkerAdapter implements TreeWalker +{ + /** + * The original Query. + * + * @var \Doctrine\ORM\AbstractQuery + */ + private $_query; + + /** + * The ParserResult of the original query that was produced by the Parser. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The query components of the original query (the "symbol table") that was produced by the Parser. + * + * @var array + */ + private $_queryComponents; + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + } + + /** + * {@inheritdoc} + */ + public function getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->_queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * @return array + */ + protected function _getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * Retrieves the Query Instance responsible for the current walkers execution. + * + * @return \Doctrine\ORM\AbstractQuery + */ + protected function _getQuery() + { + return $this->_query; + } + + /** + * Retrieves the ParserResult. + * + * @return \Doctrine\ORM\Query\ParserResult + */ + protected function _getParserResult() + { + return $this->_parserResult; + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($primary) + { + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + } + + /** + * {@inheritdoc} + */ + function walkInstanceOfExpression($instanceOfExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php new file mode 100644 index 0000000..19597ed --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChain.php @@ -0,0 +1,575 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * Represents a chain of tree walkers that modify an AST and finally emit output. + * Only the last walker in the chain can emit output. Any previous walkers can modify + * the AST to influence the final output produced by the last walker. + * + * @author Roman Borschel + * @since 2.0 + */ +class TreeWalkerChain implements TreeWalker +{ + /** + * The tree walkers. + * + * @var TreeWalker[] + */ + private $_walkers; + + /** + * The original Query. + * + * @var \Doctrine\ORM\AbstractQuery + */ + private $_query; + + /** + * The ParserResult of the original query that was produced by the Parser. + * + * @var \Doctrine\ORM\Query\ParserResult + */ + private $_parserResult; + + /** + * The query components of the original query (the "symbol table") that was produced by the Parser. + * + * @var array + */ + private $_queryComponents; + + /** + * Returns the internal queryComponents array. + * + * @return array + */ + public function getQueryComponents() + { + return $this->_queryComponents; + } + + /** + * {@inheritdoc} + */ + public function setQueryComponent($dqlAlias, array $queryComponent) + { + $requiredKeys = array('metadata', 'parent', 'relation', 'map', 'nestingLevel', 'token'); + + if (array_diff($requiredKeys, array_keys($queryComponent))) { + throw QueryException::invalidQueryComponent($dqlAlias); + } + + $this->_queryComponents[$dqlAlias] = $queryComponent; + } + + /** + * {@inheritdoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->_query = $query; + $this->_parserResult = $parserResult; + $this->_queryComponents = $queryComponents; + $this->_walkers = new TreeWalkerChainIterator($this, $query, $parserResult); + } + + /** + * Adds a tree walker to the chain. + * + * @param string $walkerClass The class of the walker to instantiate. + * + * @return void + */ + public function addTreeWalker($walkerClass) + { + $this->_walkers[] = $walkerClass; + } + + /** + * {@inheritdoc} + */ + public function walkSelectStatement(AST\SelectStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectStatement($AST); + + $this->_queryComponents = $walker->getQueryComponents(); + } + } + + /** + * {@inheritdoc} + */ + public function walkSelectClause($selectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectClause($selectClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkFromClause($fromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkFromClause($fromClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkFunction($function) + { + foreach ($this->_walkers as $walker) { + $walker->walkFunction($function); + } + } + + /** + * {@inheritdoc} + */ + public function walkOrderByClause($orderByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByClause($orderByClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkOrderByItem($orderByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkOrderByItem($orderByItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkHavingClause($havingClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkHavingClause($havingClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkJoin($join) + { + foreach ($this->_walkers as $walker) { + $walker->walkJoin($join); + } + } + + /** + * {@inheritdoc} + */ + public function walkSelectExpression($selectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSelectExpression($selectExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkQuantifiedExpression($qExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkQuantifiedExpression($qExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkSubselect($subselect) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselect($subselect); + } + } + + /** + * {@inheritdoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSubselectFromClause($subselectFromClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectClause($simpleSelectClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleSelectExpression($simpleSelectExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleSelectExpression($simpleSelectExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkAggregateExpression($aggExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkAggregateExpression($aggExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkGroupByClause($groupByClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByClause($groupByClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkGroupByItem($groupByItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkGroupByItem($groupByItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateStatement(AST\UpdateStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateStatement($AST); + } + } + + /** + * {@inheritdoc} + */ + public function walkDeleteStatement(AST\DeleteStatement $AST) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteStatement($AST); + } + } + + /** + * {@inheritdoc} + */ + public function walkDeleteClause(AST\DeleteClause $deleteClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkDeleteClause($deleteClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateClause($updateClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateClause($updateClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkUpdateItem($updateItem) + { + foreach ($this->_walkers as $walker) { + $walker->walkUpdateItem($updateItem); + } + } + + /** + * {@inheritdoc} + */ + public function walkWhereClause($whereClause) + { + foreach ($this->_walkers as $walker) { + $walker->walkWhereClause($whereClause); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalExpression($condExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalExpression($condExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalTerm($condTerm) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalTerm($condTerm); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalFactor($factor); + } + } + + /** + * {@inheritdoc} + */ + public function walkConditionalPrimary($condPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkConditionalPrimary($condPrimary); + } + } + + /** + * {@inheritdoc} + */ + public function walkExistsExpression($existsExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkExistsExpression($existsExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkCollectionMemberExpression($collMemberExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkCollectionMemberExpression($collMemberExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkEmptyCollectionComparisonExpression($emptyCollCompExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkNullComparisonExpression($nullCompExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkNullComparisonExpression($nullCompExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkInExpression($inExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInExpression($inExpr); + } + } + + /** + * {@inheritdoc} + */ + function walkInstanceOfExpression($instanceOfExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkInstanceOfExpression($instanceOfExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkLiteral($literal) + { + foreach ($this->_walkers as $walker) { + $walker->walkLiteral($literal); + } + } + + /** + * {@inheritdoc} + */ + public function walkBetweenExpression($betweenExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkBetweenExpression($betweenExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkLikeExpression($likeExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkLikeExpression($likeExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkStateFieldPathExpression($stateFieldPathExpression) + { + foreach ($this->_walkers as $walker) { + $walker->walkStateFieldPathExpression($stateFieldPathExpression); + } + } + + /** + * {@inheritdoc} + */ + public function walkComparisonExpression($compExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkComparisonExpression($compExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkInputParameter($inputParam) + { + foreach ($this->_walkers as $walker) { + $walker->walkInputParameter($inputParam); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticExpression($arithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticExpression($arithmeticExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticTerm($term) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticTerm($term); + } + } + + /** + * {@inheritdoc} + */ + public function walkStringPrimary($stringPrimary) + { + foreach ($this->_walkers as $walker) { + $walker->walkStringPrimary($stringPrimary); + } + } + + /** + * {@inheritdoc} + */ + public function walkArithmeticFactor($factor) + { + foreach ($this->_walkers as $walker) { + $walker->walkArithmeticFactor($factor); + } + } + + /** + * {@inheritdoc} + */ + public function walkSimpleArithmeticExpression($simpleArithmeticExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkSimpleArithmeticExpression($simpleArithmeticExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + foreach ($this->_walkers as $walker) { + $walker->walkPathExpression($pathExpr); + } + } + + /** + * {@inheritdoc} + */ + public function walkResultVariable($resultVariable) + { + foreach ($this->_walkers as $walker) { + $walker->walkResultVariable($resultVariable); + } + } + + /** + * {@inheritdoc} + */ + public function getExecutor($AST) + { + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChainIterator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChainIterator.php new file mode 100644 index 0000000..1b0fb8a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Query/TreeWalkerChainIterator.php @@ -0,0 +1,139 @@ +. + */ + +namespace Doctrine\ORM\Query; + +/** + * + */ +class TreeWalkerChainIterator implements \Iterator, \ArrayAccess +{ + /** + * @var TreeWalker[] + */ + private $walkers = array(); + /** + * @var TreeWalkerChain + */ + private $treeWalkerChain; + /** + * @var + */ + private $query; + /** + * @var + */ + private $parserResult; + + public function __construct(TreeWalkerChain $treeWalkerChain, $query, $parserResult) + { + $this->treeWalkerChain = $treeWalkerChain; + $this->query = $query; + $this->parserResult = $parserResult; + } + + /** + * {@inheritdoc} + */ + function rewind() + { + return reset($this->walkers); + } + + /** + * {@inheritdoc} + */ + function current() + { + return $this->offsetGet(key($this->walkers)); + } + + /** + * {@inheritdoc} + */ + function key() + { + return key($this->walkers); + } + + /** + * {@inheritdoc} + */ + function next() + { + next($this->walkers); + + return $this->offsetGet(key($this->walkers)); + } + + /** + * {@inheritdoc} + */ + function valid() + { + return key($this->walkers) !== null; + } + + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return isset($this->walkers[$offset]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + if ($this->offsetExists($offset)) { + return new $this->walkers[$offset]( + $this->query, + $this->parserResult, + $this->treeWalkerChain->getQueryComponents() + ); + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->walkers[] = $value; + } else { + $this->walkers[$offset] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + if ($this->offsetExists($offset)) { + unset($this->walkers[$offset]); + } + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php new file mode 100644 index 0000000..e7c603d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/QueryBuilder.php @@ -0,0 +1,1516 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Criteria; + +use Doctrine\ORM\Query\Expr; +use Doctrine\ORM\Query\QueryExpressionVisitor; + +/** + * This class is responsible for building DQL query strings via an object oriented + * PHP interface. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryBuilder +{ + /* The query types. */ + const SELECT = 0; + const DELETE = 1; + const UPDATE = 2; + + /* The builder states. */ + const STATE_DIRTY = 0; + const STATE_CLEAN = 1; + + /** + * The EntityManager used by this QueryBuilder. + * + * @var EntityManagerInterface + */ + private $_em; + + /** + * The array of DQL parts collected. + * + * @var array + */ + private $_dqlParts = array( + 'distinct' => false, + 'select' => array(), + 'from' => array(), + 'join' => array(), + 'set' => array(), + 'where' => null, + 'groupBy' => array(), + 'having' => null, + 'orderBy' => array() + ); + + /** + * The type of query this is. Can be select, update or delete. + * + * @var integer + */ + private $_type = self::SELECT; + + /** + * The state of the query object. Can be dirty or clean. + * + * @var integer + */ + private $_state = self::STATE_CLEAN; + + /** + * The complete DQL string for this query. + * + * @var string + */ + private $_dql; + + /** + * The query parameters. + * + * @var \Doctrine\Common\Collections\ArrayCollection + */ + private $parameters; + + /** + * The index of the first result to retrieve. + * + * @var integer + */ + private $_firstResult = null; + + /** + * The maximum number of results to retrieve. + * + * @var integer + */ + private $_maxResults = null; + + /** + * Keeps root entity alias names for join entities. + * + * @var array + */ + private $joinRootAliases = array(); + + /** + * Whether to use second level cache, if available. + * + * @var boolean + */ + protected $cacheable = false; + + /** + * Second level cache region name. + * + * @var string|null + */ + protected $cacheRegion; + + /** + * Second level query cache mode. + * + * @var integer|null + */ + protected $cacheMode; + + /** + * @var integer + */ + protected $lifetime = 0; + + /** + * Initializes a new QueryBuilder that uses the given EntityManager. + * + * @param EntityManagerInterface $em The EntityManager to use. + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + $this->parameters = new ArrayCollection(); + } + + /** + * Gets an ExpressionBuilder used for object-oriented construction of query expressions. + * This producer method is intended for convenient inline usage. Example: + * + * + * $qb = $em->createQueryBuilder(); + * $qb + * ->select('u') + * ->from('User', 'u') + * ->where($qb->expr()->eq('u.id', 1)); + * + * + * For more complex expression construction, consider storing the expression + * builder object in a local variable. + * + * @return Query\Expr + */ + public function expr() + { + return $this->_em->getExpressionBuilder(); + } + + /** + * + * Enable/disable second level query (result) caching for this query. + * + * @param boolean $cacheable + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setCacheable($cacheable) + { + $this->cacheable = (boolean) $cacheable; + + return $this; + } + + /** + * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise. + */ + public function isCacheable() + { + return $this->cacheable; + } + + /** + * @param string $cacheRegion + * + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setCacheRegion($cacheRegion) + { + $this->cacheRegion = (string) $cacheRegion; + + return $this; + } + + /** + * Obtain the name of the second level query cache region in which query results will be stored + * + * @return The cache region name; NULL indicates the default region. + */ + public function getCacheRegion() + { + return $this->cacheRegion; + } + + /** + * @return integer + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * Sets the life-time for this query into second level cache. + * + * @param integer $lifetime + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setLifetime($lifetime) + { + $this->lifetime = (integer) $lifetime; + + return $this; + } + + /** + * @return integer + */ + public function getCacheMode() + { + return $this->cacheMode; + } + + /** + * @param integer $cacheMode + * @return \Doctrine\ORM\AbstractQuery This query instance. + */ + public function setCacheMode($cacheMode) + { + $this->cacheMode = (integer) $cacheMode; + + return $this; + } + + /** + * Gets the type of the currently built query. + * + * @return integer + */ + public function getType() + { + return $this->_type; + } + + /** + * Gets the associated EntityManager for this query builder. + * + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * Gets the state of this query builder instance. + * + * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. + */ + public function getState() + { + return $this->_state; + } + + /** + * Gets the complete DQL string formed by the current specifications of this QueryBuilder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * echo $qb->getDql(); // SELECT u FROM User u + * + * + * @return string The DQL query string. + */ + public function getDQL() + { + if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) { + return $this->_dql; + } + + switch ($this->_type) { + case self::DELETE: + $dql = $this->_getDQLForDelete(); + break; + + case self::UPDATE: + $dql = $this->_getDQLForUpdate(); + break; + + case self::SELECT: + default: + $dql = $this->_getDQLForSelect(); + break; + } + + $this->_state = self::STATE_CLEAN; + $this->_dql = $dql; + + return $dql; + } + + /** + * Constructs a Query instance from the current specifications of the builder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * $q = $qb->getQuery(); + * $results = $q->execute(); + * + * + * @return Query + */ + public function getQuery() + { + $parameters = clone $this->parameters; + $query = $this->_em->createQuery($this->getDQL()) + ->setParameters($parameters) + ->setFirstResult($this->_firstResult) + ->setMaxResults($this->_maxResults); + + if ($this->lifetime) { + $query->setLifetime($this->lifetime); + } + + if ($this->cacheMode) { + $query->setCacheMode($this->cacheMode); + } + + if ($this->cacheable) { + $query->setCacheable($this->cacheable); + } + + if ($this->cacheRegion) { + $query->setCacheRegion($this->cacheRegion); + } + + return $query; + } + + /** + * Finds the root entity alias of the joined entity. + * + * @param string $alias The alias of the new join entity + * @param string $parentAlias The parent entity alias of the join relationship + * + * @return string + */ + private function findRootAlias($alias, $parentAlias) + { + $rootAlias = null; + + if (in_array($parentAlias, $this->getRootAliases())) { + $rootAlias = $parentAlias; + } elseif (isset($this->joinRootAliases[$parentAlias])) { + $rootAlias = $this->joinRootAliases[$parentAlias]; + } else { + // Should never happen with correct joining order. Might be + // thoughtful to throw exception instead. + $rootAlias = $this->getRootAlias(); + } + + $this->joinRootAliases[$alias] = $rootAlias; + + return $rootAlias; + } + + /** + * Gets the FIRST root alias of the query. This is the first entity alias involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * echo $qb->getRootAlias(); // u + * + * + * @deprecated Please use $qb->getRootAliases() instead. + * @throws RuntimeException + * + * @return string + */ + public function getRootAlias() + { + $aliases = $this->getRootAliases(); + + if ( ! isset($aliases[0])) { + throw new \RuntimeException('No alias was set before invoking getRootAlias().'); + } + + return $aliases[0]; + } + + /** + * Gets the root aliases of the query. This is the entity aliases involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * $qb->getRootAliases(); // array('u') + * + * + * @return array + */ + public function getRootAliases() + { + $aliases = array(); + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if (is_string($fromClause)) { + $spacePos = strrpos($fromClause, ' '); + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); + + $fromClause = new Query\Expr\From($from, $alias); + } + + $aliases[] = $fromClause->getAlias(); + } + + return $aliases; + } + + /** + * Gets all the aliases that have been used in the query. + * Including all select root aliases and join aliases + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->join('u.articles','a'; + * + * $qb->getAllAliases(); // array('u','a') + * + * @return array + */ + public function getAllAliases() { + return array_merge($this->getRootAliases(),array_keys($this->joinRootAliases)); + } + + /** + * Gets the root entities of the query. This is the entity aliases involved + * in the construction of the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * $qb->getRootEntities(); // array('User') + * + * + * @return array + */ + public function getRootEntities() + { + $entities = array(); + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if (is_string($fromClause)) { + $spacePos = strrpos($fromClause, ' '); + $from = substr($fromClause, 0, $spacePos); + $alias = substr($fromClause, $spacePos + 1); + + $fromClause = new Query\Expr\From($from, $alias); + } + + $entities[] = $fromClause->getFrom(); + } + + return $entities; + } + + /** + * Sets a query parameter for the query being constructed. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = :user_id') + * ->setParameter('user_id', 1); + * + * + * @param string|integer $key The parameter position or name. + * @param mixed $value The parameter value. + * @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameter($key, $value, $type = null) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + if (count($filteredParameters)) { + $parameter = $filteredParameters->first(); + $parameter->setValue($value, $type); + + return $this; + } + + $parameter = new Query\Parameter($key, $value, $type); + + $this->parameters->add($parameter); + + return $this; + } + + /** + * Sets a collection of query parameters for the query being constructed. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = :user_id1 OR u.id = :user_id2') + * ->setParameters(new ArrayCollection(array( + * new Parameter('user_id1', 1), + * new Parameter('user_id2', 2) + * ))); + * + * + * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters to set. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setParameters($parameters) + { + // BC compatibility with 2.3- + if (is_array($parameters)) { + $parameterCollection = new ArrayCollection(); + + foreach ($parameters as $key => $value) { + $parameter = new Query\Parameter($key, $value); + + $parameterCollection->add($parameter); + } + + $parameters = $parameterCollection; + } + + $this->parameters = $parameters; + + return $this; + } + + /** + * Gets all defined query parameters for the query being constructed. + * + * @return \Doctrine\Common\Collections\ArrayCollection The currently defined query parameters. + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets a (previously set) query parameter of the query being constructed. + * + * @param mixed $key The key (index or name) of the bound parameter. + * + * @return Query\Parameter|null The value of the bound parameter. + */ + public function getParameter($key) + { + $filteredParameters = $this->parameters->filter( + function ($parameter) use ($key) + { + // Must not be identical because of string to integer conversion + return ($key == $parameter->getName()); + } + ); + + return count($filteredParameters) ? $filteredParameters->first() : null; + } + + /** + * Sets the position of the first result to retrieve (the "offset"). + * + * @param integer $firstResult The first result to return. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setFirstResult($firstResult) + { + $this->_firstResult = $firstResult; + + return $this; + } + + /** + * Gets the position of the first result the query object was set to retrieve (the "offset"). + * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. + * + * @return integer The position of the first result. + */ + public function getFirstResult() + { + return $this->_firstResult; + } + + /** + * Sets the maximum number of results to retrieve (the "limit"). + * + * @param integer $maxResults The maximum number of results to retrieve. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function setMaxResults($maxResults) + { + $this->_maxResults = $maxResults; + + return $this; + } + + /** + * Gets the maximum number of results the query object was set to retrieve (the "limit"). + * Returns NULL if {@link setMaxResults} was not applied to this query builder. + * + * @return integer Maximum number of results. + */ + public function getMaxResults() + { + return $this->_maxResults; + } + + /** + * Either appends to or replaces a single, generic query part. + * + * The available parts are: 'select', 'from', 'join', 'set', 'where', + * 'groupBy', 'having' and 'orderBy'. + * + * @param string $dqlPartName + * @param Expr\Base $dqlPart + * @param bool $append + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function add($dqlPartName, $dqlPart, $append = false) + { + if ($append && ($dqlPartName === "where" || $dqlPartName === "having")) { + throw new \InvalidArgumentException( + "Using \$append = true does not have an effect with 'where' or 'having' ". + "parts. See QueryBuilder#andWhere() for an example for correct usage." + ); + } + + $isMultiple = is_array($this->_dqlParts[$dqlPartName]) + && !($dqlPartName == 'join' && !$append); + + // Allow adding any part retrieved from self::getDQLParts(). + if (is_array($dqlPart) && $dqlPartName != 'join') { + $dqlPart = reset($dqlPart); + } + + // This is introduced for backwards compatibility reasons. + // TODO: Remove for 3.0 + if ($dqlPartName == 'join') { + $newDqlPart = array(); + + foreach ($dqlPart as $k => $v) { + $k = is_numeric($k) ? $this->getRootAlias() : $k; + + $newDqlPart[$k] = $v; + } + + $dqlPart = $newDqlPart; + } + + if ($append && $isMultiple) { + if (is_array($dqlPart)) { + $key = key($dqlPart); + + $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key]; + } else { + $this->_dqlParts[$dqlPartName][] = $dqlPart; + } + } else { + $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart; + } + + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Specifies an item that is to be returned in the query result. + * Replaces any previously specified selections, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u', 'p') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p'); + * + * + * @param mixed $select The selection expressions. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function select($select = null) + { + $this->_type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', new Expr\Select($selects), false); + } + + /** + * Adds a DISTINCT flag to this query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->distinct() + * ->from('User', 'u'); + * + * + * @param bool $flag + * + * @return QueryBuilder + */ + public function distinct($flag = true) + { + $this->_dqlParts['distinct'] = (bool) $flag; + + return $this; + } + + /** + * Adds an item that is to be returned in the query result. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->addSelect('p') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p'); + * + * + * @param mixed $select The selection expression. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addSelect($select = null) + { + $this->_type = self::SELECT; + + if (empty($select)) { + return $this; + } + + $selects = is_array($select) ? $select : func_get_args(); + + return $this->add('select', new Expr\Select($selects), true); + } + + /** + * Turns the query being built into a bulk delete query that ranges over + * a certain entity type. + * + * + * $qb = $em->createQueryBuilder() + * ->delete('User', 'u') + * ->where('u.id = :user_id') + * ->setParameter('user_id', 1); + * + * + * @param string $delete The class/type whose instances are subject to the deletion. + * @param string $alias The class/type alias used in the constructed query. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function delete($delete = null, $alias = null) + { + $this->_type = self::DELETE; + + if ( ! $delete) { + return $this; + } + + return $this->add('from', new Expr\From($delete, $alias)); + } + + /** + * Turns the query being built into a bulk update query that ranges over + * a certain entity type. + * + * + * $qb = $em->createQueryBuilder() + * ->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $update The class/type whose instances are subject to the update. + * @param string $alias The class/type alias used in the constructed query. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function update($update = null, $alias = null) + { + $this->_type = self::UPDATE; + + if ( ! $update) { + return $this; + } + + return $this->add('from', new Expr\From($update, $alias)); + } + + /** + * Creates and adds a query root corresponding to the entity identified by the given alias, + * forming a cartesian product with any existing query roots. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * + * + * @param string $from The class name. + * @param string $alias The alias of the class. + * @param string $indexBy The index for the from. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function from($from, $alias, $indexBy = null) + { + return $this->add('from', new Expr\From($from, $alias, $indexBy), true); + } + + /** + * Updates a query root corresponding to an entity setting its index by. This method is intended to be used with + * EntityRepository->createQueryBuilder(), which creates the initial FROM clause and do not allow you to update it + * setting an index by. + * + * + * $qb = $userRepository->createQueryBuilder('u') + * ->indexBy('u', 'u.id'); + * + * // Is equivalent to... + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u', 'u.id'); + * + * + * @param string $alias The root alias of the class. + * @param string $indexBy The index for the from. + * + * @return QueryBuilder This QueryBuilder instance. + * + * @throws Query\QueryException + */ + public function indexBy($alias, $indexBy) + { + $rootAliases = $this->getRootAliases(); + + if (!in_array($alias, $rootAliases)) { + throw new Query\QueryException( + sprintf('Specified root alias %s must be set before invoking indexBy().', $alias) + ); + } + + foreach ($this->_dqlParts['from'] as &$fromClause) { + if ($fromClause->getAlias() !== $alias) { + continue; + } + + $fromClause = new Expr\From($fromClause->getFrom(), $fromClause->getAlias(), $indexBy); + } + + return $this; + } + + /** + * Creates and adds a join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * + * @param string $join The relationship to join. + * @param string $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); + } + + /** + * Creates and adds a join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * [php] + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * @param string $join The relationship to join. + * @param string $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $parentAlias = substr($join, 0, strpos($join, '.')); + + $rootAlias = $this->findRootAlias($alias, $parentAlias); + + $join = new Expr\Join( + Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); + } + + /** + * Creates and adds a left join over an entity association to the query. + * + * The entities in the joined association will be fetched as part of the query + * result if the alias used for the joined association is placed in the select + * expressions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); + * + * + * @param string $join The relationship to join. + * @param string $alias The alias of the join. + * @param string|null $conditionType The condition type constant. Either ON or WITH. + * @param string|null $condition The condition for the join. + * @param string|null $indexBy The index for the join. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) + { + $parentAlias = substr($join, 0, strpos($join, '.')); + + $rootAlias = $this->findRootAlias($alias, $parentAlias); + + $join = new Expr\Join( + Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); + } + + /** + * Sets a new value for a field in a bulk update query. + * + * + * $qb = $em->createQueryBuilder() + * ->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where('u.id = ?'); + * + * + * @param string $key The key/field to set. + * @param string $value The value, expression, placeholder, etc. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function set($key, $value) + { + return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true); + } + + /** + * Specifies one or more restrictions to the query result. + * Replaces any previously specified restrictions, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = ?'); + * + * // You can optionally programatically build and/or expressions + * $qb = $em->createQueryBuilder(); + * + * $or = $qb->expr()->orx(); + * $or->add($qb->expr()->eq('u.id', 1)); + * $or->add($qb->expr()->eq('u.id', 2)); + * + * $qb->update('User', 'u') + * ->set('u.password', md5('password')) + * ->where($or); + * + * + * @param mixed $predicates The restriction predicates. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function where($predicates) + { + if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) { + $predicates = new Expr\Andx(func_get_args()); + } + + return $this->add('where', $predicates); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * conjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.username LIKE ?') + * ->andWhere('u.is_active = 1'); + * + * + * @param mixed $where The query restrictions. + * + * @return QueryBuilder This QueryBuilder instance. + * + * @see where() + */ + public function andWhere() + { + $args = func_get_args(); + $where = $this->getDQLPart('where'); + + if ($where instanceof Expr\Andx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Andx($args); + } + + return $this->add('where', $where); + } + + /** + * Adds one or more restrictions to the query results, forming a logical + * disjunction with any previously specified restrictions. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->where('u.id = 1') + * ->orWhere('u.id = 2'); + * + * + * @param mixed $where The WHERE statement. + * + * @return QueryBuilder + * + * @see where() + */ + public function orWhere() + { + $args = func_get_args(); + $where = $this->getDqlPart('where'); + + if ($where instanceof Expr\Orx) { + $where->addMultiple($args); + } else { + array_unshift($args, $where); + $where = new Expr\Orx($args); + } + + return $this->add('where', $where); + } + + /** + * Specifies a grouping over the results of the query. + * Replaces any previously specified groupings, if any. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->groupBy('u.id'); + * + * + * @param string $groupBy The grouping expression. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function groupBy($groupBy) + { + return $this->add('groupBy', new Expr\GroupBy(func_get_args())); + } + + /** + * Adds a grouping expression to the query. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->groupBy('u.lastLogin') + * ->addGroupBy('u.createdAt'); + * + * + * @param string $groupBy The grouping expression. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addGroupBy($groupBy) + { + return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true); + } + + /** + * Specifies a restriction over the groups of the query. + * Replaces any previous having restrictions, if any. + * + * @param mixed $having The restriction over the groups. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function having($having) + { + if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) { + $having = new Expr\Andx(func_get_args()); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * conjunction with any existing having restrictions. + * + * @param mixed $having The restriction to append. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function andHaving($having) + { + $args = func_get_args(); + $having = $this->getDqlPart('having'); + + if ($having instanceof Expr\Andx) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new Expr\Andx($args); + } + + return $this->add('having', $having); + } + + /** + * Adds a restriction over the groups of the query, forming a logical + * disjunction with any existing having restrictions. + * + * @param mixed $having The restriction to add. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function orHaving($having) + { + $args = func_get_args(); + $having = $this->getDqlPart('having'); + + if ($having instanceof Expr\Orx) { + $having->addMultiple($args); + } else { + array_unshift($args, $having); + $having = new Expr\Orx($args); + } + + return $this->add('having', $having); + } + + /** + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. + * + * @param string|Expr\OrderBy $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function orderBy($sort, $order = null) + { + $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); + + return $this->add('orderBy', $orderBy); + } + + /** + * Adds an ordering to the query results. + * + * @param string|Expr\OrderBy $sort The ordering expression. + * @param string $order The ordering direction. + * + * @return QueryBuilder This QueryBuilder instance. + */ + public function addOrderBy($sort, $order = null) + { + $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); + + return $this->add('orderBy', $orderBy, true); + } + + /** + * Adds criteria to the query. + * + * Adds where expressions with AND operator. + * Adds orderings. + * Overrides firstResult and maxResults if they're set. + * + * @param Criteria $criteria + * @return QueryBuilder + * @throws Query\QueryException + */ + public function addCriteria(Criteria $criteria) + { + $allAliases = $this->getAllAliases(); + if ( ! isset($allAliases[0])) { + throw new Query\QueryException('No aliases are set before invoking addCriteria().'); + } + + $visitor = new QueryExpressionVisitor($this->getAllAliases()); + + if ($whereExpression = $criteria->getWhereExpression()) { + $this->andWhere($visitor->dispatch($whereExpression)); + foreach ($visitor->getParameters() as $parameter) { + $this->parameters->add($parameter); + } + } + + if ($criteria->getOrderings()) { + foreach ($criteria->getOrderings() as $sort => $order) { + + $hasValidAlias = false; + foreach($allAliases as $alias) { + if(strpos($sort . '.', $alias . '.') === 0) { + $hasValidAlias = true; + break; + } + } + + if(!$hasValidAlias) { + $sort = $allAliases[0] . '.' . $sort; + } + + $this->addOrderBy($sort, $order); + } + } + + // Overwrite limits only if they was set in criteria + if (($firstResult = $criteria->getFirstResult()) !== null) { + $this->setFirstResult($firstResult); + } + if (($maxResults = $criteria->getMaxResults()) !== null) { + $this->setMaxResults($maxResults); + } + + return $this; + } + + /** + * Gets a query part by its name. + * + * @param string $queryPartName + * + * @return mixed $queryPart + * + * @todo Rename: getQueryPart (or remove?) + */ + public function getDQLPart($queryPartName) + { + return $this->_dqlParts[$queryPartName]; + } + + /** + * Gets all query parts. + * + * @return array $dqlParts + * + * @todo Rename: getQueryParts (or remove?) + */ + public function getDQLParts() + { + return $this->_dqlParts; + } + + /** + * @return string + */ + private function _getDQLForDelete() + { + return 'DELETE' + . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + } + + /** + * @return string + */ + private function _getDQLForUpdate() + { + return 'UPDATE' + . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + } + + /** + * @return string + */ + private function _getDQLForSelect() + { + $dql = 'SELECT' + . ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '') + . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', ')); + + $fromParts = $this->getDQLPart('from'); + $joinParts = $this->getDQLPart('join'); + $fromClauses = array(); + + // Loop through all FROM clauses + if ( ! empty($fromParts)) { + $dql .= ' FROM '; + + foreach ($fromParts as $from) { + $fromClause = (string) $from; + + if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) { + foreach ($joinParts[$from->getAlias()] as $join) { + $fromClause .= ' ' . ((string) $join); + } + } + + $fromClauses[] = $fromClause; + } + } + + $dql .= implode(', ', $fromClauses) + . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) + . $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', ')) + . $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING ')) + . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + + return $dql; + } + + /** + * @param string $queryPartName + * @param array $options + * + * @return string + */ + private function _getReducedDQLQueryPart($queryPartName, $options = array()) + { + $queryPart = $this->getDQLPart($queryPartName); + + if (empty($queryPart)) { + return (isset($options['empty']) ? $options['empty'] : ''); + } + + return (isset($options['pre']) ? $options['pre'] : '') + . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart) + . (isset($options['post']) ? $options['post'] : ''); + } + + /** + * Resets DQL parts. + * + * @param array|null $parts + * + * @return QueryBuilder + */ + public function resetDQLParts($parts = null) + { + if (is_null($parts)) { + $parts = array_keys($this->_dqlParts); + } + + foreach ($parts as $part) { + $this->resetDQLPart($part); + } + + return $this; + } + + /** + * Resets single DQL part. + * + * @param string $part + * + * @return QueryBuilder + */ + public function resetDQLPart($part) + { + $this->_dqlParts[$part] = is_array($this->_dqlParts[$part]) ? array() : null; + $this->_state = self::STATE_DIRTY; + + return $this; + } + + /** + * Gets a string representation of this QueryBuilder which corresponds to + * the final DQL query being constructed. + * + * @return string The string representation of this QueryBuilder. + */ + public function __toString() + { + return $this->getDQL(); + } + + /** + * Deep clones all expression objects in the DQL parts. + * + * @return void + */ + public function __clone() + { + foreach ($this->_dqlParts as $part => $elements) { + if (is_array($this->_dqlParts[$part])) { + foreach ($this->_dqlParts[$part] as $idx => $element) { + if (is_object($element)) { + $this->_dqlParts[$part][$idx] = clone $element; + } + } + } else if (is_object($elements)) { + $this->_dqlParts[$part] = clone $elements; + } + } + + $parameters = array(); + + foreach ($this->parameters as $parameter) { + $parameters[] = clone $parameter; + } + + $this->parameters = new ArrayCollection($parameters); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown b/vendor/doctrine/orm/lib/Doctrine/ORM/README.markdown new file mode 100644 index 0000000..e69de29 diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php new file mode 100644 index 0000000..12163ef --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php @@ -0,0 +1,70 @@ +. + */ + +namespace Doctrine\ORM\Repository; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * This factory is used to create default repository objects for entities at runtime. + * + * @author Guilherme Blanco + * @since 2.4 + */ +final class DefaultRepositoryFactory implements RepositoryFactory +{ + /** + * The list of EntityRepository instances. + * + * @var \Doctrine\Common\Persistence\ObjectRepository[] + */ + private $repositoryList = array(); + + /** + * {@inheritdoc} + */ + public function getRepository(EntityManagerInterface $entityManager, $entityName) + { + $repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_hash($entityManager); + + if (isset($this->repositoryList[$repositoryHash])) { + return $this->repositoryList[$repositoryHash]; + } + + return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName); + } + + /** + * Create a new repository instance for an entity class. + * + * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. + * @param string $entityName The name of the entity. + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + private function createRepository(EntityManagerInterface $entityManager, $entityName) + { + /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */ + $metadata = $entityManager->getClassMetadata($entityName); + $repositoryClassName = $metadata->customRepositoryClassName + ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName(); + + return new $repositoryClassName($entityManager, $metadata); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/RepositoryFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/RepositoryFactory.php new file mode 100644 index 0000000..f3af43e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Repository/RepositoryFactory.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\ORM\Repository; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Interface for entity repository factory. + * + * @author Guilherme Blanco + * @since 2.4 + */ +interface RepositoryFactory +{ + /** + * Gets the repository for an entity class. + * + * @param \Doctrine\ORM\EntityManagerInterface $entityManager The EntityManager instance. + * @param string $entityName The name of the entity. + * + * @return \Doctrine\Common\Persistence\ObjectRepository + */ + public function getRepository(EntityManagerInterface $entityManager, $entityName); +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/AttachEntityListenersListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/AttachEntityListenersListener.php new file mode 100644 index 0000000..2b6c91a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/AttachEntityListenersListener.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; + +/** + * Mechanism to programmatically attach entity listeners. + * + * @author Fabio B. SIlva + * + * @since 2.5 + */ +class AttachEntityListenersListener +{ + /** + * @var array[] + */ + private $entityListeners = array(); + + /** + * Adds a entity listener for a specific entity. + * + * @param string $entityClass The entity to attach the listener. + * @param string $listenerClass The listener class. + * @param string $eventName The entity lifecycle event. + * @param string $listenerCallback|null The listener callback method or NULL to use $eventName. + * + * @return void + */ + public function addEntityListener($entityClass, $listenerClass, $eventName, $listenerCallback = null) + { + $this->entityListeners[ltrim($entityClass, '\\')][] = array( + 'event' => $eventName, + 'class' => $listenerClass, + 'method' => $listenerCallback ?: $eventName + ); + } + + /** + * Processes event and attach the entity listener. + * + * @param \Doctrine\ORM\Event\LoadClassMetadataEventArgs $event + * + * @return void + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $event) + { + /** @var $metadata \Doctrine\ORM\Mapping\ClassMetadata */ + $metadata = $event->getClassMetadata(); + + if ( ! isset($this->entityListeners[$metadata->name])) { + return; + } + + foreach ($this->entityListeners[$metadata->name] as $listener) { + $metadata->addEntityListener($listener['event'], $listener['class'], $listener['method']); + } + + unset($this->entityListeners[$metadata->name]); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php new file mode 100644 index 0000000..80df37a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache; + +/** + * Command to clear a collection cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class CollectionRegionCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:region:collection') + ->setDescription('Clear a second-level cache collection region.') + ->addArgument('owner-class', InputArgument::OPTIONAL, 'The owner entity name.') + ->addArgument('association', InputArgument::OPTIONAL, 'The association collection name.') + ->addArgument('owner-id', InputArgument::OPTIONAL, 'The owner identifier.') + ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all entity regions will be deleted/invalidated.') + ->addOption('flush', null, InputOption::VALUE_NONE,'If defined, all cache entries will be flushed.'); + + + $this->setHelp(<<%command.name% command is meant to clear a second-level cache collection regions for an associated Entity Manager. +It is possible to delete/invalidate all collection region, a specific collection region or flushes the cache provider. + +The execution type differ on how you execute the command. +If you want to invalidate all entries for an collection region this command would do the work: + +%command.name% 'Entities\MyEntity' 'collectionName' + +To invalidate a specific entry you should use : + +%command.name% 'Entities\MyEntity' 'collectionName' 1 + +If you want to invalidate all entries for the all collection regions: + +%command.name% --all + +Alternatively, if you want to flush the configured cache provider for an collection region use this command: + +%command.name% 'Entities\MyEntity' 'collectionName' --flush + +Finally, be aware that if --flush option is passed, +not all cache providers are able to flush entries, because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $ownerClass = $input->getArgument('owner-class'); + $assoc = $input->getArgument('association'); + $ownerId = $input->getArgument('owner-id'); + $cache = $em->getCache(); + + if ( ! $cache instanceof Cache) { + throw new \InvalidArgumentException('No second-level cache is configured on the given EntityManager.'); + } + + if ( (! $ownerClass || ! $assoc) && ! $input->getOption('all')) { + throw new \InvalidArgumentException('Missing arguments "--owner-class" "--association"'); + } + + if ($input->getOption('flush')) { + $collectionRegion = $cache->getCollectionCacheRegion($ownerClass, $assoc); + + if ( ! $collectionRegion instanceof DefaultRegion) { + throw new \InvalidArgumentException(sprintf( + 'The option "--flush" expects a "Doctrine\ORM\Cache\Region\DefaultRegion", but got "%s".', + is_object($collectionRegion) ? get_class($collectionRegion) : gettype($collectionRegion) + )); + } + + $collectionRegion->getCache()->flushAll(); + + $output->writeln(sprintf('Flushing cache provider configured for "%s#%s"', $ownerClass, $assoc)); + + return; + } + + if ($input->getOption('all')) { + $output->writeln('Clearing all second-level cache collection regions'); + + $cache->evictEntityRegions(); + + return; + } + + if ($ownerId) { + $output->writeln(sprintf('Clearing second-level cache entry for collection "%s#%s" owner entity identified by "%s"', $ownerClass, $assoc, $ownerId)); + $cache->evictCollection($ownerClass, $assoc, $ownerId); + + return; + } + + $output->writeln(sprintf('Clearing second-level cache for collection "%s#%s"', $ownerClass, $assoc)); + $cache->evictCollectionRegion($ownerClass, $assoc); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php new file mode 100644 index 0000000..21f6e9d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/EntityRegionCommand.php @@ -0,0 +1,132 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache; + +/** + * Command to clear a entity cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class EntityRegionCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:region:entity') + ->setDescription('Clear a second-level cache entity region.') + ->addArgument('entity-class', InputArgument::OPTIONAL, 'The entity name.') + ->addArgument('entity-id', InputArgument::OPTIONAL, 'The entity identifier.') + ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all entity regions will be deleted/invalidated.') + ->addOption('flush', null, InputOption::VALUE_NONE,'If defined, all cache entries will be flushed.'); + + + $this->setHelp(<<%command.name% command is meant to clear a second-level cache entity region for an associated Entity Manager. +It is possible to delete/invalidate all entity region, a specific entity region or flushes the cache provider. + +The execution type differ on how you execute the command. +If you want to invalidate all entries for an entity region this command would do the work: + +%command.name% 'Entities\MyEntity' + +To invalidate a specific entry you should use : + +%command.name% 'Entities\MyEntity' 1 + +If you want to invalidate all entries for the all entity regions: + +%command.name% --all + +Alternatively, if you want to flush the configured cache provider for an entity region use this command: + +%command.name% 'Entities\MyEntity' --flush + +Finally, be aware that if --flush option is passed, +not all cache providers are able to flush entries, because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $entityClass = $input->getArgument('entity-class'); + $entityId = $input->getArgument('entity-id'); + $cache = $em->getCache(); + + if ( ! $cache instanceof Cache) { + throw new \InvalidArgumentException('No second-level cache is configured on the given EntityManager.'); + } + + if ( ! $entityClass && ! $input->getOption('all')) { + throw new \InvalidArgumentException('Invalid argument "--entity-class"'); + } + + if ($input->getOption('flush')) { + $entityRegion = $cache->getEntityCacheRegion($entityClass); + + if ( ! $entityRegion instanceof DefaultRegion) { + throw new \InvalidArgumentException(sprintf( + 'The option "--flush" expects a "Doctrine\ORM\Cache\Region\DefaultRegion", but got "%s".', + is_object($entityRegion) ? get_class($entityRegion) : gettype($entityRegion) + )); + } + + $entityRegion->getCache()->flushAll(); + + $output->writeln(sprintf('Flushing cache provider configured for entity named "%s"', $entityClass)); + + return; + } + + if ($input->getOption('all')) { + $output->writeln('Clearing all second-level cache entity regions'); + + $cache->evictEntityRegions(); + + return; + } + + if ($entityId) { + $output->writeln(sprintf('Clearing second-level cache entry for entity "%s" identified by "%s"', $entityClass, $entityId)); + $cache->evictEntity($entityClass, $entityId); + + return; + } + + $output->writeln(sprintf('Clearing second-level cache for entity "%s"', $entityClass)); + $cache->evictEntityRegion($entityClass); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php new file mode 100644 index 0000000..e23d36b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/MetadataCommand.php @@ -0,0 +1,109 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; +use Doctrine\Common\Cache\XcacheCache; + +/** + * Command to clear the metadata cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:metadata') + ->setDescription('Clear all metadata cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $this->setHelp(<<%command.name% command is meant to clear the metadata cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getMetadataCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + if ($cacheDriver instanceof XcacheCache) { + throw new \LogicException("Cannot clear XCache Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + + $output->writeln('Clearing ALL Metadata cache entries'); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->writeln($message); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php new file mode 100644 index 0000000..f1be98d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryCommand.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; +use Doctrine\Common\Cache\XcacheCache; + +/** + * Command to clear the query cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class QueryCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:query') + ->setDescription('Clear all query cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $this->setHelp(<<%command.name% command is meant to clear the query cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getQueryCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + if ($cacheDriver instanceof XcacheCache) { + throw new \LogicException("Cannot clear XCache Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->write('Clearing ALL Query cache entries' . PHP_EOL); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->write($message . PHP_EOL); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php new file mode 100644 index 0000000..b5d75d3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/QueryRegionCommand.php @@ -0,0 +1,124 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Cache\Region\DefaultRegion; +use Doctrine\ORM\Cache; + +/** + * Command to clear a query cache region. + * + * @since 2.5 + * @author Fabio B. Silva + */ +class QueryRegionCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:region:query') + ->setDescription('Clear a second-level cache query region.') + ->addArgument('region-name', InputArgument::OPTIONAL, 'The query region to clear.') + ->addOption('all', null, InputOption::VALUE_NONE, 'If defined, all query regions will be deleted/invalidated.') + ->addOption('flush', null, InputOption::VALUE_NONE,'If defined, all cache entries will be flushed.'); + + + $this->setHelp(<<%command.name% command is meant to clear a second-level cache query region for an associated Entity Manager. +It is possible to delete/invalidate all query region, a specific query region or flushes the cache provider. + +The execution type differ on how you execute the command. +If you want to invalidate all entries for the default query region this command would do the work: + +%command.name% + +To invalidate entries for a specific query region you should use : + +%command.name% my_region_name + +If you want to invalidate all entries for the all query region: + +%command.name% --all + +Alternatively, if you want to flush the configured cache provider use this command: + +%command.name% my_region_name --flush + +Finally, be aware that if --flush option is passed, +not all cache providers are able to flush entries, because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $name = $input->getArgument('region-name'); + $cache = $em->getCache(); + + if ($name === null) { + $name = Cache::DEFAULT_QUERY_REGION_NAME; + } + + if ( ! $cache instanceof Cache) { + throw new \InvalidArgumentException('No second-level cache is configured on the given EntityManager.'); + } + + if ($input->getOption('flush')) { + $queryCache = $cache->getQueryCache($name); + $queryRegion = $queryCache->getRegion(); + + if ( ! $queryRegion instanceof DefaultRegion) { + throw new \InvalidArgumentException(sprintf( + 'The option "--flush" expects a "Doctrine\ORM\Cache\Region\DefaultRegion", but got "%s".', + is_object($queryRegion) ? get_class($queryRegion) : gettype($queryRegion) + )); + } + + $queryRegion->getCache()->flushAll(); + + $output->writeln(sprintf('Flushing cache provider configured for second-level cache query region named "%s"', $name)); + + return; + } + + if ($input->getOption('all')) { + $output->writeln('Clearing all second-level cache query regions'); + + $cache->evictQueryRegions(); + + return; + } + + $output->writeln(sprintf('Clearing second-level cache query region named "%s"', $name)); + $cache->evictQueryRegion($name); + } +} \ No newline at end of file diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php new file mode 100644 index 0000000..c21f554 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -0,0 +1,108 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\ClearCache; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Cache\ApcCache; +use Doctrine\Common\Cache\XcacheCache; + +/** + * Command to clear the result cache of the various cache drivers. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ResultCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:clear-cache:result') + ->setDescription('Clear all result cache of the various cache drivers.') + ->setDefinition(array( + new InputOption( + 'flush', null, InputOption::VALUE_NONE, + 'If defined, cache entries will be flushed instead of deleted/invalidated.' + ) + )); + + $this->setHelp(<<%command.name% command is meant to clear the result cache of associated Entity Manager. +It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider +instance completely. + +The execution type differ on how you execute the command. +If you want to invalidate the entries (and not delete from cache instance), this command would do the work: + +%command.name% + +Alternatively, if you want to flush the cache provider using this command: + +%command.name% --flush + +Finally, be aware that if --flush option is passed, not all cache providers are able to flush entries, +because of a limitation of its execution nature. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + $cacheDriver = $em->getConfiguration()->getResultCacheImpl(); + + if ( ! $cacheDriver) { + throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.'); + } + + if ($cacheDriver instanceof ApcCache) { + throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + if ($cacheDriver instanceof XcacheCache) { + throw new \LogicException("Cannot clear XCache Cache from Console, its shared in the Webserver memory and not accessible from the CLI."); + } + + $output->writeln('Clearing ALL Result cache entries'); + + $result = $cacheDriver->deleteAll(); + $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.'; + + if (true === $input->getOption('flush')) { + $result = $cacheDriver->flushAll(); + $message = ($result) ? 'Successfully flushed cache entries.' : $message; + } + + $output->writeln($message); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php new file mode 100644 index 0000000..71d72de --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertDoctrine1SchemaCommand.php @@ -0,0 +1,230 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\ConvertDoctrine1Schema; +use Doctrine\ORM\Tools\EntityGenerator; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to convert a Doctrine 1 schema to a Doctrine 2 mapping file. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1SchemaCommand extends Command +{ + /** + * @var EntityGenerator|null + */ + private $entityGenerator = null; + + /** + * @var ClassMetadataExporter|null + */ + private $metadataExporter = null; + + /** + * @return EntityGenerator + */ + public function getEntityGenerator() + { + if ($this->entityGenerator == null) { + $this->entityGenerator = new EntityGenerator(); + } + + return $this->entityGenerator; + } + + /** + * @param EntityGenerator $entityGenerator + * + * @return void + */ + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->entityGenerator = $entityGenerator; + } + + /** + * @return ClassMetadataExporter + */ + public function getMetadataExporter() + { + if ($this->metadataExporter == null) { + $this->metadataExporter = new ClassMetadataExporter(); + } + + return $this->metadataExporter; + } + + /** + * @param ClassMetadataExporter $metadataExporter + * + * @return void + */ + public function setMetadataExporter(ClassMetadataExporter $metadataExporter) + { + $this->metadataExporter = $metadataExporter; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:convert-d1-schema') + ->setAliases(array('orm:convert:d1-schema')) + ->setDescription('Converts Doctrine 1.X schema into a Doctrine 2.X schema.') + ->setDefinition(array( + new InputArgument( + 'from-path', InputArgument::REQUIRED, 'The path of Doctrine 1.X schema information.' + ), + new InputArgument( + 'to-type', InputArgument::REQUIRED, 'The destination Doctrine 2.X mapping type.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, + 'The path to generate your Doctrine 2.X mapping information.' + ), + new InputOption( + 'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'Optional paths of Doctrine 1.X schema information.', + array() + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ) + )) + ->setHelp(<<getArgument('from-path')), $input->getOption('from')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + $toType = $input->getArgument('to-type'); + $extend = $input->getOption('extend'); + $numSpaces = $input->getOption('num-spaces'); + + $this->convertDoctrine1Schema($fromPaths, $destPath, $toType, $numSpaces, $extend, $output); + } + + /** + * @param array $fromPaths + * @param string $destPath + * @param string $toType + * @param int $numSpaces + * @param string|null $extend + * @param OutputInterface $output + * + * @throws \InvalidArgumentException + */ + public function convertDoctrine1Schema(array $fromPaths, $destPath, $toType, $numSpaces, $extend, OutputInterface $output) + { + foreach ($fromPaths as &$dirName) { + $dirName = realpath($dirName); + + if ( ! file_exists($dirName)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 1.X schema directory '%s' does not exist.", $dirName) + ); + } + + if ( ! is_readable($dirName)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 1.X schema directory '%s' does not have read permissions.", $dirName) + ); + } + } + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 2.X mapping destination directory '%s' does not exist.", $destPath) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Doctrine 2.X mapping destination directory '%s' does not have write permissions.", $destPath) + ); + } + + $cme = $this->getMetadataExporter(); + $exporter = $cme->getExporter($toType, $destPath); + + if (strtolower($toType) === 'annotation') { + $entityGenerator = $this->getEntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + + $entityGenerator->setNumSpaces($numSpaces); + + if ($extend !== null) { + $entityGenerator->setClassToExtend($extend); + } + } + + $converter = new ConvertDoctrine1Schema($fromPaths); + $metadata = $converter->getMetadata(); + + if ($metadata) { + $output->writeln(''); + + foreach ($metadata as $class) { + $output->writeln(sprintf('Processing entity "%s"', $class->name)); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->writeln(PHP_EOL . sprintf( + 'Converting Doctrine 1.X schema to "%s" mapping type in "%s"', $toType, $destPath + )); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php new file mode 100644 index 0000000..b229f4a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -0,0 +1,200 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Doctrine\ORM\Mapping\Driver\DatabaseDriver; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to convert your mapping information between the various formats. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertMappingCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:convert-mapping') + ->setAliases(array('orm:convert:mapping')) + ->setDescription('Convert mapping information between supported formats.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'to-type', InputArgument::REQUIRED, 'The mapping type to be converted.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, + 'The path to generate your entities classes.' + ), + new InputOption( + 'force', 'f', InputOption::VALUE_NONE, + 'Force to overwrite existing mapping files.' + ), + new InputOption( + 'from-database', null, null, 'Whether or not to convert mapping information from existing database.' + ), + new InputOption( + 'extend', null, InputOption::VALUE_OPTIONAL, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_OPTIONAL, + 'Defines the number of indentation spaces', 4 + ), + new InputOption( + 'namespace', null, InputOption::VALUE_OPTIONAL, + 'Defines a namespace for the generated entity classes, if converted from database.' + ), + )) + ->setHelp(<<one-time command. It should not be necessary for +you to call this method multiple times, especially when using the --from-database +flag. + +Converting an existing database schema into mapping files only solves about 70-80% +of the necessary mapping information. Additionally the detection from an existing +database cannot detect inverse associations, inheritance types, +entities with foreign keys as primary keys and many of the +semantical operations on associations such as cascade. + +Hint: There is no need to convert YAML or XML mapping files to annotations +every time you make changes. All mapping drivers are first class citizens +in Doctrine 2 and can be used as runtime mapping for the ORM. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + if ($input->getOption('from-database') === true) { + $databaseDriver = new DatabaseDriver( + $em->getConnection()->getSchemaManager() + ); + + $em->getConfiguration()->setMetadataDriverImpl( + $databaseDriver + ); + + if (($namespace = $input->getOption('namespace')) !== null) { + $databaseDriver->setNamespace($namespace); + } + } + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadata = $cmf->getAllMetadata(); + $metadata = MetadataFilter::filter($metadata, $input->getOption('filter')); + + // Process destination directory + if ( ! is_dir($destPath = $input->getArgument('dest-path'))) { + mkdir($destPath, 0775, true); + } + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Mapping destination directory '%s' does not have write permissions.", $destPath) + ); + } + + $toType = strtolower($input->getArgument('to-type')); + + $exporter = $this->getExporter($toType, $destPath); + $exporter->setOverwriteExistingFiles($input->getOption('force')); + + if ($toType == 'annotation') { + $entityGenerator = new EntityGenerator(); + $exporter->setEntityGenerator($entityGenerator); + + $entityGenerator->setNumSpaces($input->getOption('num-spaces')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + } + + if (count($metadata)) { + foreach ($metadata as $class) { + $output->writeln(sprintf('Processing entity "%s"', $class->name)); + } + + $exporter->setMetadata($metadata); + $exporter->export(); + + $output->writeln(PHP_EOL . sprintf( + 'Exporting "%s" mapping information to "%s"', $toType, $destPath + )); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } + + /** + * @param string $toType + * @param string $destPath + * + * @return \Doctrine\ORM\Tools\Export\Driver\AbstractExporter + */ + protected function getExporter($toType, $destPath) + { + $cme = new ClassMetadataExporter(); + + return $cme->getExporter($toType, $destPath); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php new file mode 100644 index 0000000..3601570 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -0,0 +1,81 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Command to ensure that Doctrine is properly configured for a production environment. + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EnsureProductionSettingsCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:ensure-production-settings') + ->setDescription('Verify that Doctrine is properly configured for a production environment.') + ->setDefinition(array( + new InputOption( + 'complete', null, InputOption::VALUE_NONE, + 'Flag to also inspect database connection existence.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + try { + $em->getConfiguration()->ensureProductionSettings(); + + if ($input->getOption('complete') !== null) { + $em->getConnection()->connect(); + } + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + + return 1; + } + + $output->writeln('Environment is correctly configured for production.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php new file mode 100644 index 0000000..bca32b7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateEntitiesCommand.php @@ -0,0 +1,169 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to generate entity classes and method stubs from your mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateEntitiesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-entities') + ->setAliases(array('orm:generate:entities')) + ->setDescription('Generate entity classes and method stubs from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.' + ), + new InputOption( + 'generate-annotations', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate annotation metadata on entities.', false + ), + new InputOption( + 'generate-methods', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should generate stub methods on entities.', true + ), + new InputOption( + 'regenerate-entities', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should regenerate entity if it exists.', false + ), + new InputOption( + 'update-entities', null, InputOption::VALUE_OPTIONAL, + 'Flag to define if generator should only update entity if it exists.', true + ), + new InputOption( + 'extend', null, InputOption::VALUE_REQUIRED, + 'Defines a base class to be extended by generated entity classes.' + ), + new InputOption( + 'num-spaces', null, InputOption::VALUE_REQUIRED, + 'Defines the number of indentation spaces', 4 + ), + new InputOption( + 'no-backup', null, InputOption::VALUE_NONE, + 'Flag to define if generator should avoid backuping existing entity file if it exists.' + ) + )) + ->setHelp(<<--update-entities or --regenerate-entities flags your existing +code gets overwritten. The EntityGenerator will only append new code to your +file and will not delete the old code. However this approach may still be prone +to error and we suggest you use code repositories such as GIT or SVN to make +backups of your code. + +It makes sense to generate the entity code if you are using entities as Data +Access Objects only and don't put much additional logic on them. If you are +however putting much more logic on the entities you should refrain from using +the entity-generator and code your entities manually. + +Important: Even if you specified Inheritance options in your +XML or YAML Mapping files the generator cannot generate the base and +child classes for you correctly, because it doesn't know which +class is supposed to extend which. You have to adjust the entity +code manually for inheritance to work! +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $em = $this->getHelper('em')->getEntityManager(); + + $cmf = new DisconnectedClassMetadataFactory(); + $cmf->setEntityManager($em); + $metadatas = $cmf->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + // Create EntityGenerator + $entityGenerator = new EntityGenerator(); + + $entityGenerator->setGenerateAnnotations($input->getOption('generate-annotations')); + $entityGenerator->setGenerateStubMethods($input->getOption('generate-methods')); + $entityGenerator->setRegenerateEntityIfExists($input->getOption('regenerate-entities')); + $entityGenerator->setUpdateEntityIfExists($input->getOption('update-entities')); + $entityGenerator->setNumSpaces($input->getOption('num-spaces')); + $entityGenerator->setBackupExisting(!$input->getOption('no-backup')); + + if (($extend = $input->getOption('extend')) !== null) { + $entityGenerator->setClassToExtend($extend); + } + + foreach ($metadatas as $metadata) { + $output->writeln( + sprintf('Processing entity "%s"', $metadata->name) + ); + } + + // Generating Entities + $entityGenerator->generate($metadatas, $destPath); + + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Entity classes generated to "%s"', $destPath)); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php new file mode 100644 index 0000000..21edb9d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -0,0 +1,115 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to (re)generate the proxy classes used by doctrine. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateProxiesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-proxies') + ->setAliases(array('orm:generate:proxies')) + ->setDescription('Generates proxy classes for entity classes.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::OPTIONAL, + 'The path to generate your proxy classes. If none is provided, it will attempt to grab from configuration.' + ), + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + // Process destination directory + if (($destPath = $input->getArgument('dest-path')) === null) { + $destPath = $em->getConfiguration()->getProxyDir(); + } + + if ( ! is_dir($destPath)) { + mkdir($destPath, 0775, true); + } + + $destPath = realpath($destPath); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not exist.", $em->getConfiguration()->getProxyDir()) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Proxies destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if ( count($metadatas)) { + foreach ($metadatas as $metadata) { + $output->writeln( + sprintf('Processing entity "%s"', $metadata->name) + ); + } + + // Generating Proxies + $em->getProxyFactory()->generateProxyClasses($metadatas, $destPath); + + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Proxy classes generated to "%s"', $destPath)); + } else { + $output->writeln('No Metadata Classes to process.'); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php new file mode 100644 index 0000000..c6c1399 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/GenerateRepositoriesCommand.php @@ -0,0 +1,121 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Doctrine\ORM\Tools\Console\MetadataFilter; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Command to generate repository classes for mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class GenerateRepositoriesCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:generate-repositories') + ->setAliases(array('orm:generate:repositories')) + ->setDescription('Generate repository classes from your mapping information.') + ->setDefinition(array( + new InputOption( + 'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'A string pattern used to match entities that should be processed.' + ), + new InputArgument( + 'dest-path', InputArgument::REQUIRED, 'The path to generate your repository classes.' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + + $repositoryName = $em->getConfiguration()->getDefaultRepositoryClassName(); + + // Process destination directory + $destPath = realpath($input->getArgument('dest-path')); + + if ( ! file_exists($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not exist.", $input->getArgument('dest-path')) + ); + } + + if ( ! is_writable($destPath)) { + throw new \InvalidArgumentException( + sprintf("Entities destination directory '%s' does not have write permissions.", $destPath) + ); + } + + if (count($metadatas)) { + $numRepositories = 0; + $generator = new EntityRepositoryGenerator(); + + $generator->setDefaultRepositoryName($repositoryName); + + foreach ($metadatas as $metadata) { + if ($metadata->customRepositoryClassName) { + $output->writeln( + sprintf('Processing repository "%s"', $metadata->customRepositoryClassName) + ); + + $generator->writeEntityRepositoryClass($metadata->customRepositoryClassName, $destPath); + + $numRepositories++; + } + } + + if ($numRepositories) { + // Outputting information message + $output->writeln(PHP_EOL . sprintf('Repository classes generated to "%s"', $destPath) ); + } else { + $output->writeln('No Repository classes were found to be processed.' ); + } + } else { + $output->writeln('No Metadata Classes to process.' ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php new file mode 100644 index 0000000..f23fe22 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/InfoCommand.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Doctrine\ORM\Mapping\MappingException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; + +/** + * Show information about mapped entities. + * + * @link www.doctrine-project.org + * @since 2.1 + * @author Benjamin Eberlei + */ +class InfoCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:info') + ->setDescription('Show basic information about all mapped entities') + ->setHelp(<<%command.name% shows basic information about which +entities exist and possibly if their mapping information contains errors or +not. +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $entityManager \Doctrine\ORM\EntityManager */ + $entityManager = $this->getHelper('em')->getEntityManager(); + + $entityClassNames = $entityManager->getConfiguration() + ->getMetadataDriverImpl() + ->getAllClassNames(); + + if (!$entityClassNames) { + throw new \Exception( + 'You do not have any mapped Doctrine ORM entities according to the current configuration. '. + 'If you have entities or mapping files you should check your mapping configuration for errors.' + ); + } + + $output->writeln(sprintf("Found %d mapped entities:", count($entityClassNames))); + + $failure = false; + + foreach ($entityClassNames as $entityClassName) { + try { + $entityManager->getClassMetadata($entityClassName); + $output->writeln(sprintf("[OK] %s", $entityClassName)); + } catch (MappingException $e) { + $output->writeln("[FAIL] ".$entityClassName); + $output->writeln(sprintf("%s", $e->getMessage())); + $output->writeln(''); + + $failure = true; + } + } + + return $failure ? 1 : 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php new file mode 100644 index 0000000..ab7ea55 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/MappingDescribeCommand.php @@ -0,0 +1,298 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Doctrine\Common\Persistence\Mapping\MappingException; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Show information about mapped entities. + * + * @link www.doctrine-project.org + * @since 2.4 + * @author Daniel Leech + */ +final class MappingDescribeCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:mapping:describe') + ->addArgument('entityName', InputArgument::REQUIRED, 'Full or partial name of entity') + ->setDescription('Display information about mapped objects') + ->setHelp(<<%command.full_name% My\Namespace\Entity\MyEntity + +Or: + + %command.full_name% MyEntity +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /* @var $entityManager \Doctrine\ORM\EntityManagerInterface */ + $entityManager = $this->getHelper('em')->getEntityManager(); + + $this->displayEntity($input->getArgument('entityName'), $entityManager, $output); + + return 0; + } + + /** + * Display all the mapping information for a single Entity. + * + * @param string $entityName Full or partial entity class name + * @param EntityManagerInterface $entityManager + * @param OutputInterface $output + */ + private function displayEntity($entityName, EntityManagerInterface $entityManager, OutputInterface $output) + { + $table = new Table($output); + + $table->setHeaders(array('Field', 'Value')); + + $metadata = $this->getClassMetadata($entityName, $entityManager); + + array_map( + array($table, 'addRow'), + array_merge( + array( + $this->formatField('Name', $metadata->name), + $this->formatField('Root entity name', $metadata->rootEntityName), + $this->formatField('Custom generator definition', $metadata->customGeneratorDefinition), + $this->formatField('Custom repository class', $metadata->customRepositoryClassName), + $this->formatField('Mapped super class?', $metadata->isMappedSuperclass), + $this->formatField('Embedded class?', $metadata->isEmbeddedClass), + $this->formatField('Parent classes', $metadata->parentClasses), + $this->formatField('Sub classes', $metadata->subClasses), + $this->formatField('Embedded classes', $metadata->subClasses), + $this->formatField('Named queries', $metadata->namedQueries), + $this->formatField('Named native queries', $metadata->namedNativeQueries), + $this->formatField('SQL result set mappings', $metadata->sqlResultSetMappings), + $this->formatField('Identifier', $metadata->identifier), + $this->formatField('Inheritance type', $metadata->inheritanceType), + $this->formatField('Discriminator column', $metadata->discriminatorColumn), + $this->formatField('Discriminator value', $metadata->discriminatorValue), + $this->formatField('Discriminator map', $metadata->discriminatorMap), + $this->formatField('Generator type', $metadata->generatorType), + $this->formatField('Table', $metadata->table), + $this->formatField('Composite identifier?', $metadata->isIdentifierComposite), + $this->formatField('Foreign identifier?', $metadata->containsForeignIdentifier), + $this->formatField('Sequence generator definition', $metadata->sequenceGeneratorDefinition), + $this->formatField('Table generator definition', $metadata->tableGeneratorDefinition), + $this->formatField('Change tracking policy', $metadata->changeTrackingPolicy), + $this->formatField('Versioned?', $metadata->isVersioned), + $this->formatField('Version field', $metadata->versionField), + $this->formatField('Read only?', $metadata->isReadOnly), + + $this->formatEntityListeners($metadata->entityListeners), + ), + array($this->formatField('Association mappings:', '')), + $this->formatMappings($metadata->associationMappings), + array($this->formatField('Field mappings:', '')), + $this->formatMappings($metadata->fieldMappings) + ) + ); + + $table->render(); + } + + /** + * Return all mapped entity class names + * + * @param EntityManagerInterface $entityManager + * + * @return string[] + */ + private function getMappedEntities(EntityManagerInterface $entityManager) + { + $entityClassNames = $entityManager + ->getConfiguration() + ->getMetadataDriverImpl() + ->getAllClassNames(); + + if ( ! $entityClassNames) { + throw new \InvalidArgumentException( + 'You do not have any mapped Doctrine ORM entities according to the current configuration. '. + 'If you have entities or mapping files you should check your mapping configuration for errors.' + ); + } + + return $entityClassNames; + } + + /** + * Return the class metadata for the given entity + * name + * + * @param string $entityName Full or partial entity name + * @param EntityManagerInterface $entityManager + * + * @return \Doctrine\ORM\Mapping\ClassMetadata + */ + private function getClassMetadata($entityName, EntityManagerInterface $entityManager) + { + try { + return $entityManager->getClassMetadata($entityName); + } catch (MappingException $e) { + } + + $matches = array_filter( + $this->getMappedEntities($entityManager), + function ($mappedEntity) use ($entityName) { + return preg_match('{' . preg_quote($entityName) . '}', $mappedEntity); + } + ); + + if ( ! $matches) { + throw new \InvalidArgumentException(sprintf( + 'Could not find any mapped Entity classes matching "%s"', + $entityName + )); + } + + if (count($matches) > 1) { + throw new \InvalidArgumentException(sprintf( + 'Entity name "%s" is ambigous, possible matches: "%s"', + $entityName, implode(', ', $matches) + )); + } + + return $entityManager->getClassMetadata(current($matches)); + } + + /** + * Format the given value for console output + * + * @param mixed $value + * + * @return string + */ + private function formatValue($value) + { + if ('' === $value) { + return ''; + } + + if (null === $value) { + return 'Null'; + } + + if (is_bool($value)) { + return '' . ($value ? 'True' : 'False') . ''; + } + + if (empty($value)) { + return 'Empty'; + } + + if (is_array($value)) { + if (defined('JSON_UNESCAPED_UNICODE') && defined('JSON_UNESCAPED_SLASHES')) { + return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + + return json_encode($value); + } + + if (is_object($value)) { + return sprintf('<%s>', get_class($value)); + } + + if (is_scalar($value)) { + return $value; + } + + throw new \InvalidArgumentException(sprintf('Do not know how to format value "%s"', print_r($value, true))); + } + + /** + * Add the given label and value to the two column table output + * + * @param string $label Label for the value + * @param mixed $value A Value to show + * + * @return array + */ + private function formatField($label, $value) + { + if (null === $value) { + $value = 'None'; + } + + return array(sprintf('%s', $label), $this->formatValue($value)); + } + + /** + * Format the association mappings + * + * @param array + * + * @return array + */ + private function formatMappings(array $propertyMappings) + { + $output = array(); + + foreach ($propertyMappings as $propertyName => $mapping) { + $output[] = $this->formatField(sprintf(' %s', $propertyName), ''); + + foreach ($mapping as $field => $value) { + $output[] = $this->formatField(sprintf(' %s', $field), $this->formatValue($value)); + } + } + + return $output; + } + + /** + * Format the entity listeners + * + * @param array $entityListeners + * + * @return array + */ + private function formatEntityListeners(array $entityListeners) + { + return $this->formatField( + 'Entity listeners', + array_map( + function ($entityListener) { + return get_class($entityListener); + }, + $entityListeners + ) + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php new file mode 100644 index 0000000..abd999a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/RunDqlCommand.php @@ -0,0 +1,133 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\Common\Util\Debug; + +/** + * Command to execute DQL queries in a given EntityManager. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class RunDqlCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:run-dql') + ->setDescription('Executes arbitrary DQL directly from the command line.') + ->setDefinition(array( + new InputArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.'), + new InputOption( + 'hydrate', null, InputOption::VALUE_REQUIRED, + 'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.', + 'object' + ), + new InputOption( + 'first-result', null, InputOption::VALUE_REQUIRED, + 'The first result in the result set.' + ), + new InputOption( + 'max-result', null, InputOption::VALUE_REQUIRED, + 'The maximum number of results in the result set.' + ), + new InputOption( + 'depth', null, InputOption::VALUE_REQUIRED, + 'Dumping depth of Entity graph.', 7 + ), + new InputOption( + 'show-sql', null, InputOption::VALUE_NONE, + 'Dump generated SQL instead of executing query' + ) + )) + ->setHelp(<<getHelper('em')->getEntityManager(); + + if (($dql = $input->getArgument('dql')) === null) { + throw new \RuntimeException("Argument 'DQL' is required in order to execute this command correctly."); + } + + $depth = $input->getOption('depth'); + + if ( ! is_numeric($depth)) { + throw new \LogicException("Option 'depth' must contains an integer value"); + } + + $hydrationModeName = $input->getOption('hydrate'); + $hydrationMode = 'Doctrine\ORM\Query::HYDRATE_' . strtoupper(str_replace('-', '_', $hydrationModeName)); + + if ( ! defined($hydrationMode)) { + throw new \RuntimeException( + "Hydration mode '$hydrationModeName' does not exist. It should be either: object. array, scalar or single-scalar." + ); + } + + $query = $em->createQuery($dql); + + if (($firstResult = $input->getOption('first-result')) !== null) { + if ( ! is_numeric($firstResult)) { + throw new \LogicException("Option 'first-result' must contains an integer value"); + } + + $query->setFirstResult((int) $firstResult); + } + + if (($maxResult = $input->getOption('max-result')) !== null) { + if ( ! is_numeric($maxResult)) { + throw new \LogicException("Option 'max-result' must contains an integer value"); + } + + $query->setMaxResults((int) $maxResult); + } + + if ($input->getOption('show-sql')) { + $output->writeln(Debug::dump($query->getSQL(), 2, true, false)); + return; + } + + $resultSet = $query->execute(array(), constant($hydrationMode)); + + $output->writeln(Debug::dump($resultSet, $input->getOption('depth'), true, false)); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php new file mode 100644 index 0000000..0aabd63 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/AbstractCommand.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Base class for CreateCommand, DropCommand and UpdateCommand. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractCommand extends Command +{ + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param SchemaTool $schemaTool + * @param array $metadatas + * + * @return null|int Null or 0 if everything went fine, or an error code. + */ + abstract protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas); + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $emHelper = $this->getHelper('em'); + + /* @var $em \Doctrine\ORM\EntityManager */ + $em = $emHelper->getEntityManager(); + + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + + if ( ! empty($metadatas)) { + // Create SchemaTool + $tool = new SchemaTool($em); + + return $this->executeSchemaCommand($input, $output, $tool, $metadatas); + } else { + $output->writeln('No Metadata Classes to process.'); + return 0; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php new file mode 100644 index 0000000..b2af0ca --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -0,0 +1,85 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to create the database schema for a set of classes based on their mappings. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class CreateCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:schema-tool:create') + ->setDescription( + 'Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.' + ) + ->setDefinition(array( + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Instead of trying to apply generated SQLs into EntityManager Storage Connection, output them.' + ) + )) + ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + if ($input->getOption('dump-sql')) { + $sqls = $schemaTool->getCreateSchemaSql($metadatas); + $output->writeln(implode(';' . PHP_EOL, $sqls) . ';'); + } else { + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + + $output->writeln('Creating database schema...'); + $schemaTool->createSchema($metadatas); + $output->writeln('Database schema created successfully!'); + } + + return 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php new file mode 100644 index 0000000..b222378 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -0,0 +1,130 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to drop the database schema for a set of classes based on their mappings. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DropCommand extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:schema-tool:drop') + ->setDescription( + 'Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.' + ) + ->setDefinition(array( + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Instead of trying to apply generated SQLs into EntityManager Storage Connection, output them.' + ), + new InputOption( + 'force', 'f', InputOption::VALUE_NONE, + "Don't ask for the deletion of the database, but force the operation to run." + ), + new InputOption( + 'full-database', null, InputOption::VALUE_NONE, + 'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.' + ), + )) + ->setHelp(<<Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + $isFullDatabaseDrop = $input->getOption('full-database'); + + if ($input->getOption('dump-sql')) { + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + $output->writeln(implode(';' . PHP_EOL, $sqls)); + + return 0; + } + + if ($input->getOption('force')) { + $output->writeln('Dropping database schema...'); + + if ($isFullDatabaseDrop) { + $schemaTool->dropDatabase(); + } else { + $schemaTool->dropSchema($metadatas); + } + + $output->writeln('Database schema dropped successfully!'); + + return 0; + } + + $output->writeln('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL); + + if ($isFullDatabaseDrop) { + $sqls = $schemaTool->getDropDatabaseSQL(); + } else { + $sqls = $schemaTool->getDropSchemaSQL($metadatas); + } + + if (count($sqls)) { + $output->writeln(sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls))); + $output->writeln('Please run the operation by passing one - or both - of the following options:'); + + $output->writeln(sprintf(' %s --force to execute the command', $this->getName())); + $output->writeln(sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName())); + + return 1; + } + + $output->writeln('Nothing to drop. The database is empty!'); + + return 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php new file mode 100644 index 0000000..a0e942a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -0,0 +1,157 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command\SchemaTool; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * Command to generate the SQL needed to update the database schema to match + * the current mapping information. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Ryan Weaver + */ +class UpdateCommand extends AbstractCommand +{ + /** + * @var string + */ + protected $name = 'orm:schema-tool:update'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName($this->name) + ->setDescription( + 'Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.' + ) + ->setDefinition(array( + new InputOption( + 'complete', null, InputOption::VALUE_NONE, + 'If defined, all assets of the database which are not relevant to the current metadata will be dropped.' + ), + + new InputOption( + 'dump-sql', null, InputOption::VALUE_NONE, + 'Dumps the generated SQL statements to the screen (does not execute them).' + ), + new InputOption( + 'force', 'f', InputOption::VALUE_NONE, + 'Causes the generated SQL statements to be physically executed against your database.' + ), + )); + + $this->setHelp(<<%command.name% command generates the SQL needed to +synchronize the database schema with the current mapping metadata of the +default entity manager. + +For example, if you add metadata for a new column to an entity, this command +would generate and output the SQL needed to add the new column to the database: + +%command.name% --dump-sql + +Alternatively, you can execute the generated queries: + +%command.name% --force + +If both options are specified, the queries are output and then executed: + +%command.name% --dump-sql --force + +Finally, be aware that if the --complete option is passed, this +task will drop all database assets (e.g. tables, etc) that are *not* described +by the current metadata. In other words, without this option, this task leaves +untouched any "extra" tables that exist in the database, but which aren't +described by any metadata. + +Hint: If you have a database with tables that should not be managed +by the ORM, you can use a DBAL functionality to filter the tables and sequences down +on a global level: + + \$config->setFilterSchemaAssetsExpression(\$regexp); +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas) + { + // Defining if update is complete or not (--complete not defined means $saveMode = true) + $saveMode = ! $input->getOption('complete'); + + $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode); + + if (0 === count($sqls)) { + $output->writeln('Nothing to update - your database is already in sync with the current entity metadata.'); + + return 0; + } + + $dumpSql = true === $input->getOption('dump-sql'); + $force = true === $input->getOption('force'); + + if ($dumpSql) { + $output->writeln(implode(';' . PHP_EOL, $sqls) . ';'); + } + + if ($force) { + if ($dumpSql) { + $output->writeln(''); + } + $output->writeln('Updating database schema...'); + $schemaTool->updateSchema($metadatas, $saveMode); + + $pluralization = (1 === count($sqls)) ? 'query was' : 'queries were'; + + $output->writeln(sprintf('Database schema updated successfully! "%s" %s executed', count($sqls), $pluralization)); + } + + if ($dumpSql || $force) { + return 0; + } + + $output->writeln('ATTENTION: This operation should not be executed in a production environment.'); + $output->writeln(' Use the incremental update to detect changes during development and use'); + $output->writeln(' the SQL DDL provided to manually update your database in production.'); + $output->writeln(''); + + $output->writeln(sprintf('The Schema-Tool would execute "%s" queries to update the database.', count($sqls))); + $output->writeln('Please run the operation by passing one - or both - of the following options:'); + + $output->writeln(sprintf(' %s --force to execute the command', $this->getName())); + $output->writeln(sprintf(' %s --dump-sql to dump the SQL statements to the screen', $this->getName())); + + return 1; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php new file mode 100644 index 0000000..eb7697c --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/Command/ValidateSchemaCommand.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\ORM\Tools\SchemaValidator; + +/** + * Command to validate that the current mapping is valid. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ValidateSchemaCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('orm:validate-schema') + ->setDescription('Validate the mapping files.') + ->addOption( + 'skip-mapping', + null, + InputOption::VALUE_NONE, + 'Skip the mapping validation check' + ) + ->addOption( + 'skip-sync', + null, + InputOption::VALUE_NONE, + 'Skip checking if the mapping is in sync with the database' + ) + ->setHelp( + <<getHelper('em')->getEntityManager(); + $validator = new SchemaValidator($em); + $exit = 0; + + if ($input->getOption('skip-mapping')) { + $output->writeln('[Mapping] Skipped mapping check.'); + } elseif ($errors = $validator->validateMapping()) { + foreach ($errors as $className => $errorMessages) { + $output->writeln("[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:"); + + foreach ($errorMessages as $errorMessage) { + $output->writeln('* ' . $errorMessage); + } + + $output->writeln(''); + } + + $exit += 1; + } else { + $output->writeln('[Mapping] OK - The mapping files are correct.'); + } + + if ($input->getOption('skip-sync')) { + $output->writeln('[Database] SKIPPED - The database was not checked for synchronicity.'); + } elseif (!$validator->schemaInSyncWithMetadata()) { + $output->writeln('[Database] FAIL - The database schema is not in sync with the current mapping file.'); + $exit += 2; + } else { + $output->writeln('[Database] OK - The database schema is in sync with the mapping files.'); + } + + return $exit; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php new file mode 100644 index 0000000..55ec379 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/ConsoleRunner.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Doctrine\ORM\Version; +use Doctrine\ORM\EntityManagerInterface; + +use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; +use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper; + +/** + * Handles running the Console Tools inside Symfony Console context. + */ +class ConsoleRunner +{ + /** + * Create a Symfony Console HelperSet + * + * @param EntityManagerInterface $entityManager + * @return HelperSet + */ + public static function createHelperSet(EntityManagerInterface $entityManager) + { + return new HelperSet(array( + 'db' => new ConnectionHelper($entityManager->getConnection()), + 'em' => new EntityManagerHelper($entityManager) + )); + } + + /** + * Runs console with the given helperset. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param \Symfony\Component\Console\Command\Command[] $commands + * + * @return void + */ + static public function run(HelperSet $helperSet, $commands = array()) + { + $cli = self::createApplication($helperSet, $commands); + $cli->run(); + } + + /** + * Creates a console application with the given helperset and + * optional commands. + * + * @param \Symfony\Component\Console\Helper\HelperSet $helperSet + * @param array $commands + * + * @return \Symfony\Component\Console\Application + */ + static public function createApplication(HelperSet $helperSet, $commands = array()) + { + $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + $cli->setCatchExceptions(true); + $cli->setHelperSet($helperSet); + self::addCommands($cli); + $cli->addCommands($commands); + + return $cli; + } + + /** + * @param Application $cli + * + * @return void + */ + static public function addCommands(Application $cli) + { + $cli->addCommands(array( + // DBAL Commands + new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + + // ORM Commands + new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(), + new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(), + new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(), + new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(), + new \Doctrine\ORM\Tools\Console\Command\InfoCommand(), + new \Doctrine\ORM\Tools\Console\Command\MappingDescribeCommand(), + )); + } + + static public function printCliConfigTemplate() + { + echo <<<'HELP' +You are missing a "cli-config.php" or "config/cli-config.php" file in your +project, which is required to get the Doctrine Console working. You can use the +following sample as a template: + +. + */ + +namespace Doctrine\ORM\Tools\Console\Helper; + +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Helper\Helper; + + +/** + * Doctrine CLI Connection Helper. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityManagerHelper extends Helper +{ + /** + * Doctrine ORM EntityManagerInterface. + * + * @var EntityManagerInterface + */ + protected $_em; + + /** + * Constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->_em = $em; + } + + /** + * Retrieves Doctrine ORM EntityManager. + * + * @return EntityManagerInterface + */ + public function getEntityManager() + { + return $this->_em; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'entityManager'; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php new file mode 100644 index 0000000..5a72b7d --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Console/MetadataFilter.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\ORM\Tools\Console; + +/** + * Used by CLI Tools to restrict entity-based commands to given patterns. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class MetadataFilter extends \FilterIterator implements \Countable +{ + /** + * @var array + */ + private $filter = array(); + + /** + * Filter Metadatas by one or more filter options. + * + * @param array $metadatas + * @param array|string $filter + * + * @return array + */ + static public function filter(array $metadatas, $filter) + { + $metadatas = new MetadataFilter(new \ArrayIterator($metadatas), $filter); + + return iterator_to_array($metadatas); + } + + /** + * @param \ArrayIterator $metadata + * @param array|string $filter + */ + public function __construct(\ArrayIterator $metadata, $filter) + { + $this->filter = (array) $filter; + + parent::__construct($metadata); + } + + /** + * @return bool + */ + public function accept() + { + if (count($this->filter) == 0) { + return true; + } + + $it = $this->getInnerIterator(); + $metadata = $it->current(); + + foreach ($this->filter as $filter) { + $pregResult = preg_match("/$filter/", $metadata->name); + + if ($pregResult === false) { + throw new \RuntimeException( + sprintf("Error while evaluating regex '/%s/'.", $filter) + ); + } + + if ($pregResult === 0) { + return false; + } + + if ($pregResult) { + return true; + } + } + + return false; + } + + /** + * @return int + */ + public function count() + { + return count($this->getInnerIterator()); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php new file mode 100644 index 0000000..52f3c92 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ConvertDoctrine1Schema.php @@ -0,0 +1,344 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Types\Type; +use Symfony\Component\Yaml\Yaml; + +/** + * Class to help with converting Doctrine 1 schema files to Doctrine 2 mapping files + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ConvertDoctrine1Schema +{ + /** + * @var array + */ + private $from; + + /** + * @var array + */ + private $legacyTypeMap = array( + // TODO: This list may need to be updated + 'clob' => 'text', + 'timestamp' => 'datetime', + 'enum' => 'string' + ); + + /** + * Constructor passes the directory or array of directories + * to convert the Doctrine 1 schema files from. + * + * @param array $from + * + * @author Jonathan Wage + */ + public function __construct($from) + { + $this->from = (array) $from; + } + + /** + * Gets an array of ClassMetadataInfo instances from the passed + * Doctrine 1 schema. + * + * @return array An array of ClassMetadataInfo instances + */ + public function getMetadata() + { + $schema = array(); + foreach ($this->from as $path) { + if (is_dir($path)) { + $files = glob($path . '/*.yml'); + foreach ($files as $file) { + $schema = array_merge($schema, (array) Yaml::parse(file_get_contents($file))); + } + } else { + $schema = array_merge($schema, (array) Yaml::parse(file_get_contents($path))); + } + } + + $metadatas = array(); + foreach ($schema as $className => $mappingInformation) { + $metadatas[] = $this->convertToClassMetadataInfo($className, $mappingInformation); + } + + return $metadatas; + } + + /** + * @param string $className + * @param array $mappingInformation + * + * @return \Doctrine\ORM\Mapping\ClassMetadataInfo + */ + private function convertToClassMetadataInfo($className, $mappingInformation) + { + $metadata = new ClassMetadataInfo($className); + + $this->convertTableName($className, $mappingInformation, $metadata); + $this->convertColumns($className, $mappingInformation, $metadata); + $this->convertIndexes($className, $mappingInformation, $metadata); + $this->convertRelations($className, $mappingInformation, $metadata); + + return $metadata; + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertTableName($className, array $model, ClassMetadataInfo $metadata) + { + if (isset($model['tableName']) && $model['tableName']) { + $e = explode('.', $model['tableName']); + + if (count($e) > 1) { + $metadata->table['schema'] = $e[0]; + $metadata->table['name'] = $e[1]; + } else { + $metadata->table['name'] = $e[0]; + } + } + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertColumns($className, array $model, ClassMetadataInfo $metadata) + { + $id = false; + + if (isset($model['columns']) && $model['columns']) { + foreach ($model['columns'] as $name => $column) { + $fieldMapping = $this->convertColumn($className, $name, $column, $metadata); + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $id = true; + } + } + } + + if ( ! $id) { + $fieldMapping = array( + 'fieldName' => 'id', + 'columnName' => 'id', + 'type' => 'integer', + 'id' => true + ); + $metadata->mapField($fieldMapping); + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } + } + + /** + * @param string $className + * @param string $name + * @param string|array $column + * @param ClassMetadataInfo $metadata + * + * @return array + * + * @throws ToolsException + */ + private function convertColumn($className, $name, $column, ClassMetadataInfo $metadata) + { + if (is_string($column)) { + $string = $column; + $column = array(); + $column['type'] = $string; + } + + if ( ! isset($column['name'])) { + $column['name'] = $name; + } + + // check if a column alias was used (column_name as field_name) + if (preg_match("/(\w+)\sas\s(\w+)/i", $column['name'], $matches)) { + $name = $matches[1]; + $column['name'] = $name; + $column['alias'] = $matches[2]; + } + + if (preg_match("/([a-zA-Z]+)\(([0-9]+)\)/", $column['type'], $matches)) { + $column['type'] = $matches[1]; + $column['length'] = $matches[2]; + } + + $column['type'] = strtolower($column['type']); + // check if legacy column type (1.x) needs to be mapped to a 2.0 one + if (isset($this->legacyTypeMap[$column['type']])) { + $column['type'] = $this->legacyTypeMap[$column['type']]; + } + + if ( ! Type::hasType($column['type'])) { + throw ToolsException::couldNotMapDoctrine1Type($column['type']); + } + + $fieldMapping = array(); + + if (isset($column['primary'])) { + $fieldMapping['id'] = true; + } + + $fieldMapping['fieldName'] = isset($column['alias']) ? $column['alias'] : $name; + $fieldMapping['columnName'] = $column['name']; + $fieldMapping['type'] = $column['type']; + + if (isset($column['length'])) { + $fieldMapping['length'] = $column['length']; + } + + $allowed = array('precision', 'scale', 'unique', 'options', 'notnull', 'version'); + + foreach ($column as $key => $value) { + if (in_array($key, $allowed)) { + $fieldMapping[$key] = $value; + } + } + + $metadata->mapField($fieldMapping); + + if (isset($column['autoincrement'])) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + } elseif (isset($column['sequence'])) { + $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE); + + $definition = array( + 'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence'] + ); + + if (isset($column['sequence']['size'])) { + $definition['allocationSize'] = $column['sequence']['size']; + } + + if (isset($column['sequence']['value'])) { + $definition['initialValue'] = $column['sequence']['value']; + } + + $metadata->setSequenceGeneratorDefinition($definition); + } + + return $fieldMapping; + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertIndexes($className, array $model, ClassMetadataInfo $metadata) + { + if (empty($model['indexes'])) { + return; + } + + foreach ($model['indexes'] as $name => $index) { + $type = (isset($index['type']) && $index['type'] == 'unique') + ? 'uniqueConstraints' : 'indexes'; + + $metadata->table[$type][$name] = array( + 'columns' => $index['fields'] + ); + } + } + + /** + * @param string $className + * @param array $model + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function convertRelations($className, array $model, ClassMetadataInfo $metadata) + { + if (empty($model['relations'])) { + return; + } + + foreach ($model['relations'] as $name => $relation) { + if ( ! isset($relation['alias'])) { + $relation['alias'] = $name; + } + if ( ! isset($relation['class'])) { + $relation['class'] = $name; + } + if ( ! isset($relation['local'])) { + $relation['local'] = Inflector::tableize($relation['class']); + } + if ( ! isset($relation['foreign'])) { + $relation['foreign'] = 'id'; + } + if ( ! isset($relation['foreignAlias'])) { + $relation['foreignAlias'] = $className; + } + + if (isset($relation['refClass'])) { + $type = 'many'; + $foreignType = 'many'; + $joinColumns = array(); + } else { + $type = isset($relation['type']) ? $relation['type'] : 'one'; + $foreignType = isset($relation['foreignType']) ? $relation['foreignType'] : 'many'; + $joinColumns = array( + array( + 'name' => $relation['local'], + 'referencedColumnName' => $relation['foreign'], + 'onDelete' => isset($relation['onDelete']) ? $relation['onDelete'] : null, + ) + ); + } + + if ($type == 'one' && $foreignType == 'one') { + $method = 'mapOneToOne'; + } elseif ($type == 'many' && $foreignType == 'many') { + $method = 'mapManyToMany'; + } else { + $method = 'mapOneToMany'; + } + + $associationMapping = array(); + $associationMapping['fieldName'] = $relation['alias']; + $associationMapping['targetEntity'] = $relation['class']; + $associationMapping['mappedBy'] = $relation['foreignAlias']; + $associationMapping['joinColumns'] = $joinColumns; + + $metadata->$method($associationMapping); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php new file mode 100644 index 0000000..5196dc3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php @@ -0,0 +1,184 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\Persistence\Proxy; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\UnitOfWork; + +/** + * Use this logger to dump the identity map during the onFlush event. This is useful for debugging + * weird UnitOfWork behavior with complex operations. + */ +class DebugUnitOfWorkListener +{ + /** + * @var string + */ + private $file; + + /** + * @var string + */ + private $context; + + /** + * Pass a stream and context information for the debugging session. + * + * The stream can be php://output to print to the screen. + * + * @param string $file + * @param string $context + */ + public function __construct($file = 'php://output', $context = '') + { + $this->file = $file; + $this->context = $context; + } + + /** + * @param \Doctrine\ORM\Event\OnFlushEventArgs $args + * + * @return void + */ + public function onFlush(OnFlushEventArgs $args) + { + $this->dumpIdentityMap($args->getEntityManager()); + } + + /** + * Dumps the contents of the identity map into a stream. + * + * @param EntityManagerInterface $em + * + * @return void + */ + public function dumpIdentityMap(EntityManagerInterface $em) + { + $uow = $em->getUnitOfWork(); + $identityMap = $uow->getIdentityMap(); + + $fh = fopen($this->file, "x+"); + if (count($identityMap) == 0) { + fwrite($fh, "Flush Operation [".$this->context."] - Empty identity map.\n"); + return; + } + + fwrite($fh, "Flush Operation [".$this->context."] - Dumping identity map:\n"); + foreach ($identityMap as $className => $map) { + fwrite($fh, "Class: ". $className . "\n"); + + foreach ($map as $entity) { + fwrite($fh, " Entity: " . $this->getIdString($entity, $uow) . " " . spl_object_hash($entity)."\n"); + fwrite($fh, " Associations:\n"); + + $cm = $em->getClassMetadata($className); + + foreach ($cm->associationMappings as $field => $assoc) { + fwrite($fh, " " . $field . " "); + $value = $cm->getFieldValue($entity, $field); + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($value === null) { + fwrite($fh, " NULL\n"); + } else { + if ($value instanceof Proxy && !$value->__isInitialized()) { + fwrite($fh, "[PROXY] "); + } + + fwrite($fh, $this->getIdString($value, $uow) . " " . spl_object_hash($value) . "\n"); + } + } else { + $initialized = !($value instanceof PersistentCollection) || $value->isInitialized(); + if ($value === null) { + fwrite($fh, " NULL\n"); + } elseif ($initialized) { + fwrite($fh, "[INITIALIZED] " . $this->getType($value). " " . count($value) . " elements\n"); + + foreach ($value as $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } else { + fwrite($fh, "[PROXY] " . $this->getType($value) . " unknown element size\n"); + foreach ($value->unwrap() as $obj) { + fwrite($fh, " " . $this->getIdString($obj, $uow) . " " . spl_object_hash($obj)."\n"); + } + } + } + } + } + } + + fclose($fh); + } + + /** + * @param mixed $var + * + * @return string + */ + private function getType($var) + { + if (is_object($var)) { + $refl = new \ReflectionObject($var); + + return $refl->getShortname(); + } + + return gettype($var); + } + + /** + * @param object $entity + * @param UnitOfWork $uow + * + * @return string + */ + private function getIdString($entity, UnitOfWork $uow) + { + if ($uow->isInIdentityMap($entity)) { + $ids = $uow->getEntityIdentifier($entity); + $idstring = ""; + + foreach ($ids as $k => $v) { + $idstring .= $k."=".$v; + } + } else { + $idstring = "NEWOBJECT "; + } + + $state = $uow->getEntityState($entity); + + if ($state == UnitOfWork::STATE_NEW) { + $idstring .= " [NEW]"; + } elseif ($state == UnitOfWork::STATE_REMOVED) { + $idstring .= " [REMOVED]"; + } elseif ($state == UnitOfWork::STATE_MANAGED) { + $idstring .= " [MANAGED]"; + } elseif ($state == UnitOfwork::STATE_DETACHED) { + $idstring .= " [DETACHED]"; + } + + return $idstring; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php new file mode 100644 index 0000000..7a3ec6f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/DisconnectedClassMetadataFactory.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\Persistence\Mapping\StaticReflectionService; +use Doctrine\ORM\Mapping\ClassMetadataFactory; + +/** + * The DisconnectedClassMetadataFactory is used to create ClassMetadataInfo objects + * that do not require the entity class actually exist. This allows us to + * load some mapping information and use it to do things like generate code + * from the mapping information. + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class DisconnectedClassMetadataFactory extends ClassMetadataFactory +{ + /** + * @return \Doctrine\Common\Persistence\Mapping\StaticReflectionService + */ + public function getReflectionService() + { + return new StaticReflectionService(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php new file mode 100644 index 0000000..9027d9a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -0,0 +1,1819 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Util\Inflector; +use Doctrine\DBAL\Types\Type; + +/** + * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances. + * + * [php] + * $classes = $em->getClassMetadataFactory()->getAllMetadata(); + * + * $generator = new \Doctrine\ORM\Tools\EntityGenerator(); + * $generator->setGenerateAnnotations(true); + * $generator->setGenerateStubMethods(true); + * $generator->setRegenerateEntityIfExists(false); + * $generator->setUpdateEntityIfExists(true); + * $generator->generate($classes, '/path/to/generate/entities'); + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityGenerator +{ + /** + * Specifies class fields should be protected. + */ + const FIELD_VISIBLE_PROTECTED = 'protected'; + + /** + * Specifies class fields should be private. + */ + const FIELD_VISIBLE_PRIVATE = 'private'; + + /** + * @var bool + */ + protected $backupExisting = true; + + /** + * The extension to use for written php files. + * + * @var string + */ + protected $extension = '.php'; + + /** + * Whether or not the current ClassMetadataInfo instance is new or old. + * + * @var boolean + */ + protected $isNew = true; + + /** + * @var array + */ + protected $staticReflection = array(); + + /** + * Number of spaces to use for indention in generated code. + */ + protected $numSpaces = 4; + + /** + * The actual spaces to use for indention. + * + * @var string + */ + protected $spaces = ' '; + + /** + * The class all generated entities should extend. + * + * @var string + */ + protected $classToExtend; + + /** + * Whether or not to generation annotations. + * + * @var boolean + */ + protected $generateAnnotations = false; + + /** + * @var string + */ + protected $annotationsPrefix = ''; + + /** + * Whether or not to generate sub methods. + * + * @var boolean + */ + protected $generateEntityStubMethods = false; + + /** + * Whether or not to update the entity class if it exists already. + * + * @var boolean + */ + protected $updateEntityIfExists = false; + + /** + * Whether or not to re-generate entity class if it exists already. + * + * @var boolean + */ + protected $regenerateEntityIfExists = false; + + /** + * Visibility of the field + * + * @var string + */ + protected $fieldVisibility = 'private'; + + /** + * Whether or not to make generated embeddables immutable. + * + * @var boolean. + */ + protected $embeddablesImmutable = false; + + /** + * Hash-map for handle types. + * + * @var array + */ + protected $typeAlias = array( + Type::DATETIMETZ => '\DateTime', + Type::DATETIME => '\DateTime', + Type::DATE => '\DateTime', + Type::TIME => '\DateTime', + Type::OBJECT => '\stdClass', + Type::BIGINT => 'integer', + Type::SMALLINT => 'integer', + Type::TEXT => 'string', + Type::BLOB => 'string', + Type::DECIMAL => 'string', + Type::JSON_ARRAY => 'array', + Type::SIMPLE_ARRAY => 'array', + ); + + /** + * Hash-map to handle generator types string. + * + * @var array + */ + protected static $generatorStrategyMap = array( + ClassMetadataInfo::GENERATOR_TYPE_AUTO => 'AUTO', + ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE => 'SEQUENCE', + ClassMetadataInfo::GENERATOR_TYPE_TABLE => 'TABLE', + ClassMetadataInfo::GENERATOR_TYPE_IDENTITY => 'IDENTITY', + ClassMetadataInfo::GENERATOR_TYPE_NONE => 'NONE', + ClassMetadataInfo::GENERATOR_TYPE_UUID => 'UUID', + ClassMetadataInfo::GENERATOR_TYPE_CUSTOM => 'CUSTOM' + ); + + /** + * Hash-map to handle the change tracking policy string. + * + * @var array + */ + protected static $changeTrackingPolicyMap = array( + ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT => 'DEFERRED_IMPLICIT', + ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT => 'DEFERRED_EXPLICIT', + ClassMetadataInfo::CHANGETRACKING_NOTIFY => 'NOTIFY', + ); + + /** + * Hash-map to handle the inheritance type string. + * + * @var array + */ + protected static $inheritanceTypeMap = array( + ClassMetadataInfo::INHERITANCE_TYPE_NONE => 'NONE', + ClassMetadataInfo::INHERITANCE_TYPE_JOINED => 'JOINED', + ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE => 'SINGLE_TABLE', + ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS => 'TABLE_PER_CLASS', + ); + + /** + * @var string + */ + protected static $classTemplate = +' + + + +{ + +} +'; + + /** + * @var string + */ + protected static $getMethodTemplate = +'/** + * + * + * @return + */ +public function () +{ +return $this->; +}'; + + /** + * @var string + */ + protected static $setMethodTemplate = +'/** + * + * + * @param $ + * + * @return + */ +public function ($) +{ +$this-> = $; + +return $this; +}'; + + /** + * @var string + */ + protected static $addMethodTemplate = +'/** + * + * + * @param $ + * + * @return + */ +public function ($) +{ +$this->[] = $; + +return $this; +}'; + + /** + * @var string + */ + protected static $removeMethodTemplate = +'/** + * + * + * @param $ + */ +public function ($) +{ +$this->->removeElement($); +}'; + + /** + * @var string + */ + protected static $lifecycleCallbackMethodTemplate = +'/** + * @ + */ +public function () +{ +// Add your code here +}'; + + /** + * @var string + */ + protected static $constructorMethodTemplate = +'/** + * Constructor + */ +public function __construct() +{ + +} +'; + + /** + * @var string + */ + protected static $embeddableConstructorMethodTemplate = +'/** + * Constructor + * + * + */ +public function __construct() +{ + +} +'; + + /** + * Constructor. + */ + public function __construct() + { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $this->annotationsPrefix = 'ORM\\'; + } + } + + /** + * Generates and writes entity classes for the given array of ClassMetadataInfo instances. + * + * @param array $metadatas + * @param string $outputDirectory + * + * @return void + */ + public function generate(array $metadatas, $outputDirectory) + { + foreach ($metadatas as $metadata) { + $this->writeEntityClass($metadata, $outputDirectory); + } + } + + /** + * Generates and writes entity class to disk for the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * @param string $outputDirectory + * + * @return void + * + * @throws \RuntimeException + */ + public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory) + { + $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0775, true); + } + + $this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateEntityIfExists); + + if ( ! $this->isNew) { + $this->parseTokensInEntityFile(file_get_contents($path)); + } else { + $this->staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array()); + } + + if ($this->backupExisting && file_exists($path)) { + $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~"; + if (!copy($path, $backupPath)) { + throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed."); + } + } + + // If entity doesn't exist or we're re-generating the entities entirely + if ($this->isNew) { + file_put_contents($path, $this->generateEntityClass($metadata)); + // If entity exists and we're allowed to update the entity class + } elseif ( ! $this->isNew && $this->updateEntityIfExists) { + file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path)); + } + chmod($path, 0664); + } + + /** + * Generates a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + public function generateEntityClass(ClassMetadataInfo $metadata) + { + $placeHolders = array( + '', + '', + '', + '', + '' + ); + + $replacements = array( + $this->generateEntityNamespace($metadata), + $this->generateEntityUse(), + $this->generateEntityDocBlock($metadata), + $this->generateEntityClassName($metadata), + $this->generateEntityBody($metadata) + ); + + $code = str_replace($placeHolders, $replacements, static::$classTemplate) . "\n"; + + return str_replace('', $this->spaces, $code); + } + + /** + * Generates the updated code for the given ClassMetadataInfo and entity at path. + * + * @param ClassMetadataInfo $metadata + * @param string $path + * + * @return string + */ + public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path) + { + $currentCode = file_get_contents($path); + + $body = $this->generateEntityBody($metadata); + $body = str_replace('', $this->spaces, $body); + $last = strrpos($currentCode, '}'); + + return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : '') . "}\n"; + } + + /** + * Sets the number of spaces the exported class should have. + * + * @param integer $numSpaces + * + * @return void + */ + public function setNumSpaces($numSpaces) + { + $this->spaces = str_repeat(' ', $numSpaces); + $this->numSpaces = $numSpaces; + } + + /** + * Sets the extension to use when writing php files to disk. + * + * @param string $extension + * + * @return void + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Sets the name of the class the generated classes should extend from. + * + * @param string $classToExtend + * + * @return void + */ + public function setClassToExtend($classToExtend) + { + $this->classToExtend = $classToExtend; + } + + /** + * Sets whether or not to generate annotations for the entity. + * + * @param bool $bool + * + * @return void + */ + public function setGenerateAnnotations($bool) + { + $this->generateAnnotations = $bool; + } + + /** + * Sets the class fields visibility for the entity (can either be private or protected). + * + * @param bool $visibility + * + * @return void + * + * @throws \InvalidArgumentException + */ + public function setFieldVisibility($visibility) + { + if ($visibility !== static::FIELD_VISIBLE_PRIVATE && $visibility !== static::FIELD_VISIBLE_PROTECTED) { + throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility); + } + + $this->fieldVisibility = $visibility; + } + + /** + * Sets whether or not to generate immutable embeddables. + * + * @param boolean $embeddablesImmutable + */ + public function setEmbeddablesImmutable($embeddablesImmutable) + { + $this->embeddablesImmutable = (boolean) $embeddablesImmutable; + } + + /** + * Sets an annotation prefix. + * + * @param string $prefix + * + * @return void + */ + public function setAnnotationPrefix($prefix) + { + $this->annotationsPrefix = $prefix; + } + + /** + * Sets whether or not to try and update the entity if it already exists. + * + * @param bool $bool + * + * @return void + */ + public function setUpdateEntityIfExists($bool) + { + $this->updateEntityIfExists = $bool; + } + + /** + * Sets whether or not to regenerate the entity if it exists. + * + * @param bool $bool + * + * @return void + */ + public function setRegenerateEntityIfExists($bool) + { + $this->regenerateEntityIfExists = $bool; + } + + /** + * Sets whether or not to generate stub methods for the entity. + * + * @param bool $bool + * + * @return void + */ + public function setGenerateStubMethods($bool) + { + $this->generateEntityStubMethods = $bool; + } + + /** + * Should an existing entity be backed up if it already exists? + * + * @param bool $bool + * + * @return void + */ + public function setBackupExisting($bool) + { + $this->backupExisting = $bool; + } + + /** + * @param string $type + * + * @return string + */ + protected function getType($type) + { + if (isset($this->typeAlias[$type])) { + return $this->typeAlias[$type]; + } + + return $type; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityNamespace(ClassMetadataInfo $metadata) + { + if ($this->hasNamespace($metadata)) { + return 'namespace ' . $this->getNamespace($metadata) .';'; + } + } + + protected function generateEntityUse() + { + if ($this->generateAnnotations) { + return "\n".'use Doctrine\ORM\Mapping as ORM;'."\n"; + } else { + return ""; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityClassName(ClassMetadataInfo $metadata) + { + return 'class ' . $this->getClassName($metadata) . + ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityBody(ClassMetadataInfo $metadata) + { + $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata); + $embeddedProperties = $this->generateEntityEmbeddedProperties($metadata); + $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata); + $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null; + $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata); + + $code = array(); + + if ($fieldMappingProperties) { + $code[] = $fieldMappingProperties; + } + + if ($embeddedProperties) { + $code[] = $embeddedProperties; + } + + if ($associationMappingProperties) { + $code[] = $associationMappingProperties; + } + + $code[] = $this->generateEntityConstructor($metadata); + + if ($stubMethods) { + $code[] = $stubMethods; + } + + if ($lifecycleCallbackMethods) { + $code[] = $lifecycleCallbackMethods; + } + + return implode("\n", $code); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityConstructor(ClassMetadataInfo $metadata) + { + if ($this->hasMethod('__construct', $metadata)) { + return ''; + } + + if ($metadata->isEmbeddedClass && $this->embeddablesImmutable) { + return $this->generateEmbeddableConstructor($metadata); + } + + $collections = array(); + + foreach ($metadata->associationMappings as $mapping) { + if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { + $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; + } + } + + if ($collections) { + return $this->prefixCodeWithSpaces(str_replace("", implode("\n".$this->spaces, $collections), static::$constructorMethodTemplate)); + } + + return ''; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + private function generateEmbeddableConstructor(ClassMetadataInfo $metadata) + { + $paramTypes = array(); + $paramVariables = array(); + $params = array(); + $fields = array(); + + // Resort fields to put optional fields at the end of the method signature. + $requiredFields = array(); + $optionalFields = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if (empty($fieldMapping['nullable'])) { + $requiredFields[] = $fieldMapping; + + continue; + } + + $optionalFields[] = $fieldMapping; + } + + $fieldMappings = array_merge($requiredFields, $optionalFields); + + foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { + $paramType = '\\' . ltrim($embeddedClass['class'], '\\'); + $paramVariable = '$' . $fieldName; + + $paramTypes[] = $paramType; + $paramVariables[] = $paramVariable; + $params[] = $paramType . ' ' . $paramVariable; + $fields[] = '$this->' . $fieldName . ' = ' . $paramVariable . ';'; + } + + foreach ($fieldMappings as $fieldMapping) { + if (isset($fieldMapping['declaredField']) && + isset($metadata->embeddedClasses[$fieldMapping['declaredField']]) + ) { + continue; + } + + $paramTypes[] = $this->getType($fieldMapping['type']) . (!empty($fieldMapping['nullable']) ? '|null' : ''); + $param = '$' . $fieldMapping['fieldName']; + $paramVariables[] = $param; + + if ($fieldMapping['type'] === 'datetime') { + $param = $this->getType($fieldMapping['type']) . ' ' . $param; + } + + if (!empty($fieldMapping['nullable'])) { + $param .= ' = null'; + } + + $params[] = $param; + + $fields[] = '$this->' . $fieldMapping['fieldName'] . ' = $' . $fieldMapping['fieldName'] . ';'; + } + + $maxParamTypeLength = max(array_map('strlen', $paramTypes)); + $paramTags = array_map( + function ($type, $variable) use ($maxParamTypeLength) { + return '@param ' . $type . str_repeat(' ', $maxParamTypeLength - strlen($type) + 1) . $variable; + }, + $paramTypes, + $paramVariables + ); + + // Generate multi line constructor if the signature exceeds 120 characters. + if (array_sum(array_map('strlen', $params)) + count($params) * 2 + 29 > 120) { + $delimiter = "\n" . $this->spaces; + $params = $delimiter . implode(',' . $delimiter, $params) . "\n"; + } else { + $params = implode(', ', $params); + } + + $replacements = array( + '' => implode("\n * ", $paramTags), + '' => $params, + '' => implode("\n" . $this->spaces, $fields), + ); + + $constructor = str_replace( + array_keys($replacements), + array_values($replacements), + static::$embeddableConstructorMethodTemplate + ); + + return $this->prefixCodeWithSpaces($constructor); + } + + /** + * @todo this won't work if there is a namespace in brackets and a class outside of it. + * + * @param string $src + * + * @return void + */ + protected function parseTokensInEntityFile($src) + { + $tokens = token_get_all($src); + $lastSeenNamespace = ""; + $lastSeenClass = false; + + $inNamespace = false; + $inClass = false; + + for ($i = 0; $i < count($tokens); $i++) { + $token = $tokens[$i]; + if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + if ($inNamespace) { + if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) { + $lastSeenNamespace .= $token[1]; + } elseif (is_string($token) && in_array($token, array(';', '{'))) { + $inNamespace = false; + } + } + + if ($inClass) { + $inClass = false; + $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1]; + $this->staticReflection[$lastSeenClass]['properties'] = array(); + $this->staticReflection[$lastSeenClass]['methods'] = array(); + } + + if ($token[0] == T_NAMESPACE) { + $lastSeenNamespace = ""; + $inNamespace = true; + } elseif ($token[0] == T_CLASS && $tokens[$i-1][0] != T_DOUBLE_COLON) { + $inClass = true; + } elseif ($token[0] == T_FUNCTION) { + if ($tokens[$i+2][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][1]); + } elseif ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) { + $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+3][1]); + } + } elseif (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) { + $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1); + } + } + } + + /** + * @param string $property + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasProperty($property, ClassMetadataInfo $metadata) + { + if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) { + // don't generate property if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name); + if ($reflClass->hasProperty($property)) { + return true; + } + } + + // check traits for existing property + foreach ($this->getTraits($metadata) as $trait) { + if ($trait->hasProperty($property)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array($property, $this->staticReflection[$metadata->name]['properties']) + ); + } + + /** + * @param string $method + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasMethod($method, ClassMetadataInfo $metadata) + { + if ($this->extendsClass() || (!$this->isNew && class_exists($metadata->name))) { + // don't generate method if its already on the base class. + $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $metadata->name); + + if ($reflClass->hasMethod($method)) { + return true; + } + } + + // check traits for existing method + foreach ($this->getTraits($metadata) as $trait) { + if ($trait->hasMethod($method)) { + return true; + } + } + + return ( + isset($this->staticReflection[$metadata->name]) && + in_array(strtolower($method), $this->staticReflection[$metadata->name]['methods']) + ); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return array + */ + protected function getTraits(ClassMetadataInfo $metadata) + { + if (! ($metadata->reflClass !== null || class_exists($metadata->name))) { + return []; + } + + $reflClass = $metadata->reflClass === null + ? new \ReflectionClass($metadata->name) + : $metadata->reflClass; + + $traits = array(); + + while ($reflClass !== false) { + $traits = array_merge($traits, $reflClass->getTraits()); + + $reflClass = $reflClass->getParentClass(); + } + + return $traits; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return bool + */ + protected function hasNamespace(ClassMetadataInfo $metadata) + { + return strpos($metadata->name, '\\') ? true : false; + } + + /** + * @return bool + */ + protected function extendsClass() + { + return $this->classToExtend ? true : false; + } + + /** + * @return string + */ + protected function getClassToExtend() + { + return $this->classToExtend; + } + + /** + * @return string + */ + protected function getClassToExtendName() + { + $refl = new \ReflectionClass($this->getClassToExtend()); + + return '\\' . $refl->getName(); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function getClassName(ClassMetadataInfo $metadata) + { + return ($pos = strrpos($metadata->name, '\\')) + ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function getNamespace(ClassMetadataInfo $metadata) + { + return substr($metadata->name, 0, strrpos($metadata->name, '\\')); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityDocBlock(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = '/**'; + $lines[] = ' * ' . $this->getClassName($metadata); + + if ($this->generateAnnotations) { + $lines[] = ' *'; + + $methods = array( + 'generateTableAnnotation', + 'generateInheritanceAnnotation', + 'generateDiscriminatorColumnAnnotation', + 'generateDiscriminatorMapAnnotation', + 'generateEntityAnnotation', + ); + + foreach ($methods as $method) { + if ($code = $this->$method($metadata)) { + $lines[] = ' * ' . $code; + } + } + + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks'; + } + } + + $lines[] = ' */'; + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityAnnotation(ClassMetadataInfo $metadata) + { + $prefix = '@' . $this->annotationsPrefix; + + if ($metadata->isEmbeddedClass) { + return $prefix . 'Embeddable'; + } + + $customRepository = $metadata->customRepositoryClassName + ? '(repositoryClass="' . $metadata->customRepositoryClassName . '")' + : ''; + + return $prefix . ($metadata->isMappedSuperclass ? 'MappedSuperclass' : 'Entity') . $customRepository; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateTableAnnotation($metadata) + { + if ($metadata->isEmbeddedClass) { + return ''; + } + + $table = array(); + + if (isset($metadata->table['schema'])) { + $table[] = 'schema="' . $metadata->table['schema'] . '"'; + } + + if (isset($metadata->table['name'])) { + $table[] = 'name="' . $metadata->table['name'] . '"'; + } + + if (isset($metadata->table['options']) && $metadata->table['options']) { + $table[] = 'options={' . $this->exportTableOptions((array) $metadata->table['options']) . '}'; + } + + if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) { + $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']); + $table[] = 'uniqueConstraints={' . $constraints . '}'; + } + + if (isset($metadata->table['indexes']) && $metadata->table['indexes']) { + $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']); + $table[] = 'indexes={' . $constraints . '}'; + } + + return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')'; + } + + /** + * @param string $constraintName + * @param array $constraints + * + * @return string + */ + protected function generateTableConstraints($constraintName, $constraints) + { + $annotations = array(); + foreach ($constraints as $name => $constraint) { + $columns = array(); + foreach ($constraint['columns'] as $column) { + $columns[] = '"' . $column . '"'; + } + $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})'; + } + return implode(', ', $annotations); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateInheritanceAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateDiscriminatorColumnAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $discrColumn = $metadata->discriminatorColumn; + $columnDefinition = 'name="' . $discrColumn['name'] + . '", type="' . $discrColumn['type'] + . '", length=' . $discrColumn['length']; + + return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateDiscriminatorMapAnnotation($metadata) + { + if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $inheritanceClassMap = array(); + + foreach ($metadata->discriminatorMap as $type => $class) { + $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"'; + } + + return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})'; + } + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityStubMethods(ClassMetadataInfo $metadata) + { + $methods = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if (isset($fieldMapping['declaredField']) && + isset($metadata->embeddedClasses[$fieldMapping['declaredField']]) + ) { + continue; + } + + if (( ! isset($fieldMapping['id']) || + ! $fieldMapping['id'] || + $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE + ) && (! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) + ) { + if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) { + $methods[] = $code; + } + } + + foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { + if (isset($embeddedClass['declaredField'])) { + continue; + } + + if ( ! $metadata->isEmbeddedClass || ! $this->embeddablesImmutable) { + if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldName, $embeddedClass['class'])) { + $methods[] = $code; + } + } + + if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldName, $embeddedClass['class'])) { + $methods[] = $code; + } + } + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null; + if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + } elseif ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { + $methods[] = $code; + } + if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + /** + * @param array $associationMapping + * + * @return bool + */ + protected function isAssociationIsNullable($associationMapping) + { + if (isset($associationMapping['id']) && $associationMapping['id']) { + return false; + } + + if (isset($associationMapping['joinColumns'])) { + $joinColumns = $associationMapping['joinColumns']; + } else { + //@todo there is no way to retrieve targetEntity metadata + $joinColumns = array(); + } + + foreach ($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + return false; + } + } + + return true; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata) + { + if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) { + $methods = array(); + + foreach ($metadata->lifecycleCallbacks as $name => $callbacks) { + foreach ($callbacks as $callback) { + if ($code = $this->generateLifecycleCallbackMethod($name, $callback, $metadata)) { + $methods[] = $code; + } + } + } + + return implode("\n\n", $methods); + } + + return ""; + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($this->hasProperty($associationMapping['fieldName'], $metadata)) { + continue; + } + + $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName'] + . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->fieldMappings as $fieldMapping) { + if ($this->hasProperty($fieldMapping['fieldName'], $metadata) || + $metadata->isInheritedField($fieldMapping['fieldName']) || + ( + isset($fieldMapping['declaredField']) && + isset($metadata->embeddedClasses[$fieldMapping['declaredField']]) + ) + ) { + continue; + } + + $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName'] + . (isset($fieldMapping['options']['default']) ? ' = ' . var_export($fieldMapping['options']['default'], true) : null) . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateEntityEmbeddedProperties(ClassMetadataInfo $metadata) + { + $lines = array(); + + foreach ($metadata->embeddedClasses as $fieldName => $embeddedClass) { + if (isset($embeddedClass['declaredField']) || $this->hasProperty($fieldName, $metadata)) { + continue; + } + + $lines[] = $this->generateEmbeddedPropertyDocBlock($embeddedClass); + $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldName . ";\n"; + } + + return implode("\n", $lines); + } + + /** + * @param ClassMetadataInfo $metadata + * @param string $type + * @param string $fieldName + * @param string|null $typeHint + * @param string|null $defaultValue + * + * @return string + */ + protected function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null) + { + $methodName = $type . Inflector::classify($fieldName); + $variableName = Inflector::camelize($fieldName); + if (in_array($type, array("add", "remove"))) { + $methodName = Inflector::singularize($methodName); + $variableName = Inflector::singularize($variableName); + } + + if ($this->hasMethod($methodName, $metadata)) { + return ''; + } + $this->staticReflection[$metadata->name]['methods'][] = strtolower($methodName); + + $var = sprintf('%sMethodTemplate', $type); + $template = static::$$var; + + $methodTypeHint = null; + $types = Type::getTypesMap(); + $variableType = $typeHint ? $this->getType($typeHint) : null; + + if ($typeHint && ! isset($types[$typeHint])) { + $variableType = '\\' . ltrim($variableType, '\\'); + $methodTypeHint = '\\' . $typeHint . ' '; + } + + $replacements = array( + '' => ucfirst($type) . ' ' . $variableName, + '' => $methodTypeHint, + '' => $variableType, + '' => $variableName, + '' => $methodName, + '' => $fieldName, + '' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '', + '' => $this->getClassName($metadata) + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + $template + ); + + return $this->prefixCodeWithSpaces($method); + } + + /** + * @param string $name + * @param string $methodName + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateLifecycleCallbackMethod($name, $methodName, $metadata) + { + if ($this->hasMethod($methodName, $metadata)) { + return ''; + } + $this->staticReflection[$metadata->name]['methods'][] = $methodName; + + $replacements = array( + '' => $this->annotationsPrefix . ucfirst($name), + '' => $methodName, + ); + + $method = str_replace( + array_keys($replacements), + array_values($replacements), + static::$lifecycleCallbackMethodTemplate + ); + + return $this->prefixCodeWithSpaces($method); + } + + /** + * @param array $joinColumn + * + * @return string + */ + protected function generateJoinColumnAnnotation(array $joinColumn) + { + $joinColumnAnnot = array(); + + if (isset($joinColumn['name'])) { + $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"'; + } + + if (isset($joinColumn['referencedColumnName'])) { + $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"'; + } + + if (isset($joinColumn['unique']) && $joinColumn['unique']) { + $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false'); + } + + if (isset($joinColumn['nullable'])) { + $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false'); + } + + if (isset($joinColumn['onDelete'])) { + $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"'); + } + + if (isset($joinColumn['columnDefinition'])) { + $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"'; + } + + return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')'; + } + + /** + * @param array $associationMapping + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + + if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) { + $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\Collection'; + } else { + $lines[] = $this->spaces . ' * @var \\' . ltrim($associationMapping['targetEntity'], '\\'); + } + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + if (isset($associationMapping['id']) && $associationMapping['id']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id'; + + if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + } + + $type = null; + switch ($associationMapping['type']) { + case ClassMetadataInfo::ONE_TO_ONE: + $type = 'OneToOne'; + break; + case ClassMetadataInfo::MANY_TO_ONE: + $type = 'ManyToOne'; + break; + case ClassMetadataInfo::ONE_TO_MANY: + $type = 'OneToMany'; + break; + case ClassMetadataInfo::MANY_TO_MANY: + $type = 'ManyToMany'; + break; + } + $typeOptions = array(); + + if (isset($associationMapping['targetEntity'])) { + $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"'; + } + + if (isset($associationMapping['inversedBy'])) { + $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"'; + } + + if (isset($associationMapping['mappedBy'])) { + $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"'; + } + + if ($associationMapping['cascade']) { + $cascades = array(); + + if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"'; + if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"'; + if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"'; + if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"'; + if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"'; + + if (count($cascades) === 5) { + $cascades = array('"all"'); + } + + $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}'; + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) { + $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false'); + } + + if (isset($associationMapping['fetch']) && $associationMapping['fetch'] !== ClassMetadataInfo::FETCH_LAZY) { + $fetchMap = array( + ClassMetadataInfo::FETCH_EXTRA_LAZY => 'EXTRA_LAZY', + ClassMetadataInfo::FETCH_EAGER => 'EAGER', + ); + + $typeOptions[] = 'fetch="' . $fetchMap[$associationMapping['fetch']] . '"'; + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')'; + + if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinColumns'] as $joinColumn) { + if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) { + $joinColumnsLines[] = $this->spaces . ' * ' . $joinColumnAnnot; + } + } + + $lines[] = implode(",\n", $joinColumnsLines); + $lines[] = $this->spaces . ' * })'; + } + + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTable = array(); + $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"'; + + if (isset($associationMapping['joinTable']['schema'])) { + $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"'; + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ','; + $lines[] = $this->spaces . ' * joinColumns={'; + + $joinColumnsLines = array(); + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $joinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = implode(",". PHP_EOL, $joinColumnsLines); + $lines[] = $this->spaces . ' * },'; + $lines[] = $this->spaces . ' * inverseJoinColumns={'; + + $inverseJoinColumnsLines = array(); + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) { + $inverseJoinColumnsLines[] = $this->spaces . ' * ' . $this->generateJoinColumnAnnotation($joinColumn); + } + + $lines[] = implode(",". PHP_EOL, $inverseJoinColumnsLines); + $lines[] = $this->spaces . ' * }'; + $lines[] = $this->spaces . ' * )'; + } + + if (isset($associationMapping['orderBy'])) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({'; + + foreach ($associationMapping['orderBy'] as $name => $direction) { + $lines[] = $this->spaces . ' * "' . $name . '"="' . $direction . '",'; + } + + $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1); + $lines[] = $this->spaces . ' * })'; + } + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + /** + * @param array $fieldMapping + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + $lines[] = $this->spaces . ' * @var ' . $this->getType($fieldMapping['type']); + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + $column = array(); + if (isset($fieldMapping['columnName'])) { + $column[] = 'name="' . $fieldMapping['columnName'] . '"'; + } + + if (isset($fieldMapping['type'])) { + $column[] = 'type="' . $fieldMapping['type'] . '"'; + } + + if (isset($fieldMapping['length'])) { + $column[] = 'length=' . $fieldMapping['length']; + } + + if (isset($fieldMapping['precision'])) { + $column[] = 'precision=' . $fieldMapping['precision']; + } + + if (isset($fieldMapping['scale'])) { + $column[] = 'scale=' . $fieldMapping['scale']; + } + + if (isset($fieldMapping['nullable'])) { + $column[] = 'nullable=' . var_export($fieldMapping['nullable'], true); + } + + if (isset($fieldMapping['unsigned']) && $fieldMapping['unsigned']) { + $column[] = 'options={"unsigned"=true}'; + } + + if (isset($fieldMapping['columnDefinition'])) { + $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"'; + } + + if (isset($fieldMapping['unique'])) { + $column[] = 'unique=' . var_export($fieldMapping['unique'], true); + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')'; + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id'; + + if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")'; + } + + if ($metadata->sequenceGeneratorDefinition) { + $sequenceGenerator = array(); + + if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) { + $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"'; + } + + if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) { + $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize']; + } + + if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) { + $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue']; + } + + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')'; + } + } + + if (isset($fieldMapping['version']) && $fieldMapping['version']) { + $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version'; + } + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + /** + * @param array $embeddedClass + * + * @return string + */ + protected function generateEmbeddedPropertyDocBlock(array $embeddedClass) + { + $lines = array(); + $lines[] = $this->spaces . '/**'; + $lines[] = $this->spaces . ' * @var \\' . ltrim($embeddedClass['class'], '\\'); + + if ($this->generateAnnotations) { + $lines[] = $this->spaces . ' *'; + + $embedded = array('class="' . $embeddedClass['class'] . '"'); + + if (isset($fieldMapping['columnPrefix'])) { + $embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true); + } + + $lines[] = $this->spaces . ' * @' . + $this->annotationsPrefix . 'Embedded(' . implode(', ', $embedded) . ')'; + } + + $lines[] = $this->spaces . ' */'; + + return implode("\n", $lines); + } + + /** + * @param string $code + * @param int $num + * + * @return string + */ + protected function prefixCodeWithSpaces($code, $num = 1) + { + $lines = explode("\n", $code); + + foreach ($lines as $key => $value) { + if ( ! empty($value)) { + $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key]; + } + } + + return implode("\n", $lines); + } + + /** + * @param integer $type The inheritance type used by the class and its subclasses. + * + * @return string The literal string for the inheritance type. + * + * @throws \InvalidArgumentException When the inheritance type does not exist. + */ + protected function getInheritanceTypeString($type) + { + if ( ! isset(static::$inheritanceTypeMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided InheritanceType: %s', $type)); + } + + return static::$inheritanceTypeMap[$type]; + } + + /** + * @param integer $type The policy used for change-tracking for the mapped class. + * + * @return string The literal string for the change-tracking type. + * + * @throws \InvalidArgumentException When the change-tracking type does not exist. + */ + protected function getChangeTrackingPolicyString($type) + { + if ( ! isset(static::$changeTrackingPolicyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided ChangeTrackingPolicy: %s', $type)); + } + + return static::$changeTrackingPolicyMap[$type]; + } + + /** + * @param integer $type The generator to use for the mapped class. + * + * @return string The literal string for the generator type. + * + * @throws \InvalidArgumentException When the generator type does not exist. + */ + protected function getIdGeneratorTypeString($type) + { + if ( ! isset(static::$generatorStrategyMap[$type])) { + throw new \InvalidArgumentException(sprintf('Invalid provided IdGeneratorType: %s', $type)); + } + + return static::$generatorStrategyMap[$type]; + } + + /** + * Exports (nested) option elements. + * + * @param array $options + */ + private function exportTableOptions(array $options) + { + $optionsStr = array(); + + foreach($options as $name => $option) { + if (is_array($option)) { + $optionsStr[] = '"' . $name . '"={' . $this->exportTableOptions($option) . '}'; + } else { + $optionsStr[] = '"' . $name . '"="' . (string) $option . '"'; + } + } + + return implode(',', $optionsStr); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php new file mode 100644 index 0000000..f431588 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityRepositoryGenerator.php @@ -0,0 +1,171 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +/** + * Class to generate entity repository classes + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class EntityRepositoryGenerator +{ + private $repositoryName; + + protected static $_template = +' + +/** + * + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class extends +{ +} +'; + + /** + * @param string $fullClassName + * + * @return string + */ + public function generateEntityRepositoryClass($fullClassName) + { + $variables = array( + '' => $this->generateEntityRepositoryNamespace($fullClassName), + '' => $this->generateEntityRepositoryName($fullClassName), + '' => $this->generateClassName($fullClassName) + ); + + return str_replace(array_keys($variables), array_values($variables), self::$_template); + } + + /** + * Generates the namespace, if class do not have namespace, return empty string instead. + * + * @param string $fullClassName + * + * @return string $namespace + */ + private function getClassNamespace($fullClassName) + { + $namespace = substr($fullClassName, 0, strrpos($fullClassName, '\\')); + + return $namespace; + } + + /** + * Generates the class name + * + * @param string $fullClassName + * + * @return string + */ + private function generateClassName($fullClassName) + { + $namespace = $this->getClassNamespace($fullClassName); + + $className = $fullClassName; + + if ($namespace) { + $className = substr($fullClassName, strrpos($fullClassName, '\\') + 1, strlen($fullClassName)); + } + + return $className; + } + + /** + * Generates the namespace statement, if class do not have namespace, return empty string instead. + * + * @param string $fullClassName The full repository class name. + * + * @return string $namespace + */ + private function generateEntityRepositoryNamespace($fullClassName) + { + $namespace = $this->getClassNamespace($fullClassName); + + return $namespace ? 'namespace ' . $namespace . ';' : ''; + } + + /** + * @param string $fullClassName + * + * @return string $repositoryName + */ + private function generateEntityRepositoryName($fullClassName) + { + $namespace = $this->getClassNamespace($fullClassName); + + $repositoryName = $this->repositoryName ?: 'Doctrine\ORM\EntityRepository'; + + if ($namespace && $repositoryName[0] !== '\\') { + $repositoryName = '\\' . $repositoryName; + } + + return $repositoryName; + } + + /** + * @param string $fullClassName + * @param string $outputDirectory + * + * @return void + */ + public function writeEntityRepositoryClass($fullClassName, $outputDirectory) + { + $code = $this->generateEntityRepositoryClass($fullClassName); + + $path = $outputDirectory . DIRECTORY_SEPARATOR + . str_replace('\\', \DIRECTORY_SEPARATOR, $fullClassName) . '.php'; + $dir = dirname($path); + + if ( ! is_dir($dir)) { + mkdir($dir, 0775, true); + } + + if ( ! file_exists($path)) { + file_put_contents($path, $code); + chmod($path, 0664); + } + } + + /** + * @param string $repositoryName + * + * @return \Doctrine\ORM\Tools\EntityRepositoryGenerator + */ + public function setDefaultRepositoryName($repositoryName) + { + $this->repositoryName = $repositoryName; + + return $this; + } + +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php new file mode 100644 index 0000000..ed03e32 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaEventArgs.php @@ -0,0 +1,71 @@ +. + */ + +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\ORM\EntityManagerInterface; + +/** + * Event Args used for the Events::postGenerateSchema event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * @param EntityManagerInterface $em + * @param Schema $schema + */ + public function __construct(EntityManagerInterface $em, Schema $schema) + { + $this->em = $em; + $this->schema = $schema; + } + + /** + * @return EntityManagerInterface + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * @return Schema + */ + public function getSchema() + { + return $this->schema; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php new file mode 100644 index 0000000..e2c38f9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Event/GenerateSchemaTableEventArgs.php @@ -0,0 +1,86 @@ +. + */ +namespace Doctrine\ORM\Tools\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; + +/** + * Event Args used for the Events::postGenerateSchemaTable event. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + */ +class GenerateSchemaTableEventArgs extends EventArgs +{ + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata + */ + private $classMetadata; + + /** + * @var \Doctrine\DBAL\Schema\Schema + */ + private $schema; + + /** + * @var \Doctrine\DBAL\Schema\Table + */ + private $classTable; + + /** + * @param ClassMetadata $classMetadata + * @param Schema $schema + * @param Table $classTable + */ + public function __construct(ClassMetadata $classMetadata, Schema $schema, Table $classTable) + { + $this->classMetadata = $classMetadata; + $this->schema = $schema; + $this->classTable = $classTable; + } + + /** + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * @return Schema + */ + public function getSchema() + { + return $this->schema; + } + + /** + * @return Table + */ + public function getClassTable() + { + return $this->classTable; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php new file mode 100644 index 0000000..1ea2a37 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ClassMetadataExporter.php @@ -0,0 +1,76 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export; + +/** + * Class used for converting your mapping information between the + * supported formats: yaml, xml, and php/annotation. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class ClassMetadataExporter +{ + /** + * @var array + */ + private static $_exporterDrivers = array( + 'xml' => 'Doctrine\ORM\Tools\Export\Driver\XmlExporter', + 'yaml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', + 'yml' => 'Doctrine\ORM\Tools\Export\Driver\YamlExporter', + 'php' => 'Doctrine\ORM\Tools\Export\Driver\PhpExporter', + 'annotation' => 'Doctrine\ORM\Tools\Export\Driver\AnnotationExporter' + ); + + /** + * Registers a new exporter driver class under a specified name. + * + * @param string $name + * @param string $class + * + * @return void + */ + public static function registerExportDriver($name, $class) + { + self::$_exporterDrivers[$name] = $class; + } + + /** + * Gets an exporter driver instance. + * + * @param string $type The type to get (yml, xml, etc.). + * @param string|null $dest The directory where the exporter will export to. + * + * @return Driver\AbstractExporter + * + * @throws ExportException + */ + public function getExporter($type, $dest = null) + { + if ( ! isset(self::$_exporterDrivers[$type])) { + throw ExportException::invalidExporterDriverType($type); + } + + $class = self::$_exporterDrivers[$type]; + + return new $class($dest); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php new file mode 100644 index 0000000..b2ed435 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -0,0 +1,269 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\Export\ExportException; + +/** + * Abstract base class which is to be used for the Exporter drivers + * which can be found in \Doctrine\ORM\Tools\Export\Driver. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +abstract class AbstractExporter +{ + /** + * @var array + */ + protected $_metadata = array(); + + /** + * @var string|null + */ + protected $_outputDir; + + /** + * @var string|null + */ + protected $_extension; + + /** + * @var bool + */ + protected $_overwriteExistingFiles = false; + + /** + * @param string|null $dir + */ + public function __construct($dir = null) + { + $this->_outputDir = $dir; + } + + /** + * @param bool $overwrite + * + * @return void + */ + public function setOverwriteExistingFiles($overwrite) + { + $this->_overwriteExistingFiles = $overwrite; + } + + /** + * Converts a single ClassMetadata instance to the exported format + * and returns it. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + abstract public function exportClassMetadata(ClassMetadataInfo $metadata); + + /** + * Sets the array of ClassMetadataInfo instances to export. + * + * @param array $metadata + * + * @return void + */ + public function setMetadata(array $metadata) + { + $this->_metadata = $metadata; + } + + /** + * Gets the extension used to generated the path to a class. + * + * @return string|null + */ + public function getExtension() + { + return $this->_extension; + } + + /** + * Sets the directory to output the mapping files to. + * + * [php] + * $exporter = new YamlExporter($metadata); + * $exporter->setOutputDir(__DIR__ . '/yaml'); + * $exporter->export(); + * + * @param string $dir + * + * @return void + */ + public function setOutputDir($dir) + { + $this->_outputDir = $dir; + } + + /** + * Exports each ClassMetadata instance to a single Doctrine Mapping file + * named after the entity. + * + * @return void + * + * @throws \Doctrine\ORM\Tools\Export\ExportException + */ + public function export() + { + if ( ! is_dir($this->_outputDir)) { + mkdir($this->_outputDir, 0775, true); + } + + foreach ($this->_metadata as $metadata) { + // In case output is returned, write it to a file, skip otherwise + if($output = $this->exportClassMetadata($metadata)){ + $path = $this->_generateOutputPath($metadata); + $dir = dirname($path); + if ( ! is_dir($dir)) { + mkdir($dir, 0775, true); + } + if (file_exists($path) && !$this->_overwriteExistingFiles) { + throw ExportException::attemptOverwriteExistingFile($path); + } + file_put_contents($path, $output); + chmod($path, 0664); + } + } + } + + /** + * Generates the path to write the class for the given ClassMetadataInfo instance. + * + * @param ClassMetadataInfo $metadata + * + * @return string + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '.', $metadata->name) . $this->_extension; + } + + /** + * Sets the directory to output the mapping files to. + * + * [php] + * $exporter = new YamlExporter($metadata, __DIR__ . '/yaml'); + * $exporter->setExtension('.yml'); + * $exporter->export(); + * + * @param string $extension + * + * @return void + */ + public function setExtension($extension) + { + $this->_extension = $extension; + } + + /** + * @param int $type + * + * @return string + */ + protected function _getInheritanceTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::INHERITANCE_TYPE_NONE: + return 'NONE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_JOINED: + return 'JOINED'; + + case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE: + return 'SINGLE_TABLE'; + + case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS: + return 'PER_CLASS'; + } + } + + /** + * @param int $mode + * + * @return string + */ + protected function _getFetchModeString($mode) + { + switch ($mode) { + case ClassMetadataInfo::FETCH_EAGER: + return 'EAGER'; + + case ClassMetadataInfo::FETCH_EXTRA_LAZY: + return 'EXTRA_LAZY'; + + case ClassMetadataInfo::FETCH_LAZY: + return 'LAZY'; + } + } + + /** + * @param int $policy + * + * @return string + */ + protected function _getChangeTrackingPolicyString($policy) + { + switch ($policy) { + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT: + return 'DEFERRED_IMPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT: + return 'DEFERRED_EXPLICIT'; + + case ClassMetadataInfo::CHANGETRACKING_NOTIFY: + return 'NOTIFY'; + } + } + + /** + * @param int $type + * + * @return string + */ + protected function _getIdGeneratorTypeString($type) + { + switch ($type) { + case ClassMetadataInfo::GENERATOR_TYPE_AUTO: + return 'AUTO'; + + case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE: + return 'SEQUENCE'; + + case ClassMetadataInfo::GENERATOR_TYPE_TABLE: + return 'TABLE'; + + case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: + return 'IDENTITY'; + + case ClassMetadataInfo::GENERATOR_TYPE_UUID: + return 'UUID'; + + case ClassMetadataInfo::GENERATOR_TYPE_CUSTOM: + return 'CUSTOM'; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php new file mode 100644 index 0000000..044a1da --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/AnnotationExporter.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\EntityGenerator; + +/** + * ClassMetadata exporter for PHP classes with annotations. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class AnnotationExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.php'; + + /** + * @var EntityGenerator|null + */ + private $_entityGenerator; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + if ( ! $this->_entityGenerator) { + throw new \RuntimeException('For the AnnotationExporter you must set an EntityGenerator instance with the setEntityGenerator() method.'); + } + + $this->_entityGenerator->setGenerateAnnotations(true); + $this->_entityGenerator->setGenerateStubMethods(false); + $this->_entityGenerator->setRegenerateEntityIfExists(false); + $this->_entityGenerator->setUpdateEntityIfExists(false); + + return $this->_entityGenerator->generateEntityClass($metadata); + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata + * + * @return string + */ + protected function _generateOutputPath(ClassMetadataInfo $metadata) + { + return $this->_outputDir . '/' . str_replace('\\', '/', $metadata->name) . $this->_extension; + } + + /** + * @param \Doctrine\ORM\Tools\EntityGenerator $entityGenerator + * + * @return void + */ + public function setEntityGenerator(EntityGenerator $entityGenerator) + { + $this->_entityGenerator = $entityGenerator; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php new file mode 100644 index 0000000..29eb37a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/PhpExporter.php @@ -0,0 +1,177 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for PHP code. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class PhpExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.php'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $lines = array(); + $lines[] = 'isMappedSuperclass) { + $lines[] = '$metadata->isMappedSuperclass = true;'; + } + + if ($metadata->inheritanceType) { + $lines[] = '$metadata->setInheritanceType(ClassMetadataInfo::INHERITANCE_TYPE_' . $this->_getInheritanceTypeString($metadata->inheritanceType) . ');'; + } + + if ($metadata->customRepositoryClassName) { + $lines[] = "\$metadata->customRepositoryClassName = '" . $metadata->customRepositoryClassName . "';"; + } + + if ($metadata->table) { + $lines[] = '$metadata->setPrimaryTable(' . $this->_varExport($metadata->table) . ');'; + } + + if ($metadata->discriminatorColumn) { + $lines[] = '$metadata->setDiscriminatorColumn(' . $this->_varExport($metadata->discriminatorColumn) . ');'; + } + + if ($metadata->discriminatorMap) { + $lines[] = '$metadata->setDiscriminatorMap(' . $this->_varExport($metadata->discriminatorMap) . ');'; + } + + if ($metadata->changeTrackingPolicy) { + $lines[] = '$metadata->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_' . $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy) . ');'; + } + + if ($metadata->lifecycleCallbacks) { + foreach ($metadata->lifecycleCallbacks as $event => $callbacks) { + foreach ($callbacks as $callback) { + $lines[] = "\$metadata->addLifecycleCallback('$callback', '$event');"; + } + } + } + + foreach ($metadata->fieldMappings as $fieldMapping) { + $lines[] = '$metadata->mapField(' . $this->_varExport($fieldMapping) . ');'; + } + + if ( ! $metadata->isIdentifierComposite && $generatorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $lines[] = '$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_' . $generatorType . ');'; + } + + foreach ($metadata->associationMappings as $associationMapping) { + $cascade = array('remove', 'persist', 'refresh', 'merge', 'detach'); + foreach ($cascade as $key => $value) { + if ( ! $associationMapping['isCascade'.ucfirst($value)]) { + unset($cascade[$key]); + } + } + + if (count($cascade) === 5) { + $cascade = array('all'); + } + + $associationMappingArray = array( + 'fieldName' => $associationMapping['fieldName'], + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if (isset($associationMapping['fetch'])) { + $associationMappingArray['fetch'] = $associationMapping['fetch']; + } + + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $method = 'mapOneToOne'; + $oneToOneMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $associationMapping['joinColumns'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $method = 'mapOneToMany'; + $potentialAssociationMappingIndexes = array( + 'mappedBy', + 'orphanRemoval', + 'orderBy', + ); + foreach ($potentialAssociationMappingIndexes as $index) { + if (isset($associationMapping[$index])) { + $oneToManyMappingArray[$index] = $associationMapping[$index]; + } + } + $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $method = 'mapManyToMany'; + $potentialAssociationMappingIndexes = array( + 'mappedBy', + 'joinTable', + 'orderBy', + ); + foreach ($potentialAssociationMappingIndexes as $index) { + if (isset($associationMapping[$index])) { + $manyToManyMappingArray[$index] = $associationMapping[$index]; + } + } + $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); + } + + $lines[] = '$metadata->' . $method . '(' . $this->_varExport($associationMappingArray) . ');'; + } + + return implode("\n", $lines); + } + + /** + * @param mixed $var + * + * @return string + */ + protected function _varExport($var) + { + $export = var_export($var, true); + $export = str_replace("\n", PHP_EOL . str_repeat(' ', 8), $export); + $export = str_replace(' ', ' ', $export); + $export = str_replace('array (', 'array(', $export); + $export = str_replace('array( ', 'array(', $export); + $export = str_replace(',)', ')', $export); + $export = str_replace(', )', ')', $export); + $export = str_replace(' ', ' ', $export); + + return $export; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php new file mode 100644 index 0000000..6b53cbb --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -0,0 +1,453 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine XML mapping files. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class XmlExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.dcm.xml'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $xml = new \SimpleXmlElement(""); + + if ($metadata->isMappedSuperclass) { + $root = $xml->addChild('mapped-superclass'); + } else { + $root = $xml->addChild('entity'); + } + + if ($metadata->customRepositoryClassName) { + $root->addAttribute('repository-class', $metadata->customRepositoryClassName); + } + + $root->addAttribute('name', $metadata->name); + + if (isset($metadata->table['name'])) { + $root->addAttribute('table', $metadata->table['name']); + } + + if (isset($metadata->table['schema'])) { + $root->addAttribute('schema', $metadata->table['schema']); + } + + if ($metadata->inheritanceType && $metadata->inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $root->addAttribute('inheritance-type', $this->_getInheritanceTypeString($metadata->inheritanceType)); + } + + if (isset($metadata->table['options'])) { + $optionsXml = $root->addChild('options'); + + $this->exportTableOptions($optionsXml, $metadata->table['options']); + } + + if ($metadata->discriminatorColumn) { + $discriminatorColumnXml = $root->addChild('discriminator-column'); + $discriminatorColumnXml->addAttribute('name', $metadata->discriminatorColumn['name']); + $discriminatorColumnXml->addAttribute('type', $metadata->discriminatorColumn['type']); + + if (isset($metadata->discriminatorColumn['length'])) { + $discriminatorColumnXml->addAttribute('length', $metadata->discriminatorColumn['length']); + } + } + + if ($metadata->discriminatorMap) { + $discriminatorMapXml = $root->addChild('discriminator-map'); + + foreach ($metadata->discriminatorMap as $value => $className) { + $discriminatorMappingXml = $discriminatorMapXml->addChild('discriminator-mapping'); + $discriminatorMappingXml->addAttribute('value', $value); + $discriminatorMappingXml->addAttribute('class', $className); + } + } + + $trackingPolicy = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); + + if ( $trackingPolicy != 'DEFERRED_IMPLICIT') { + $root->addChild('change-tracking-policy', $trackingPolicy); + } + + if (isset($metadata->table['indexes'])) { + $indexesXml = $root->addChild('indexes'); + + foreach ($metadata->table['indexes'] as $name => $index) { + $indexXml = $indexesXml->addChild('index'); + $indexXml->addAttribute('name', $name); + $indexXml->addAttribute('columns', implode(',', $index['columns'])); + if(isset($index['flags'])) { + $indexXml->addAttribute('flags', implode(',', $index['flags'])); + } + } + } + + if (isset($metadata->table['uniqueConstraints'])) { + $uniqueConstraintsXml = $root->addChild('unique-constraints'); + + foreach ($metadata->table['uniqueConstraints'] as $name => $unique) { + $uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint'); + $uniqueConstraintXml->addAttribute('name', $name); + $uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns'])); + } + } + + $fields = $metadata->fieldMappings; + + $id = array(); + foreach ($fields as $name => $field) { + if (isset($field['id']) && $field['id']) { + $id[$name] = $field; + unset($fields[$name]); + } + } + + foreach ($metadata->associationMappings as $name => $assoc) { + if (isset($assoc['id']) && $assoc['id']) { + $id[$name] = array( + 'fieldName' => $name, + 'associationKey' => true + ); + } + } + + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; + } + + if ($id) { + foreach ($id as $field) { + $idXml = $root->addChild('id'); + $idXml->addAttribute('name', $field['fieldName']); + + if (isset($field['type'])) { + $idXml->addAttribute('type', $field['type']); + } + + if (isset($field['columnName'])) { + $idXml->addAttribute('column', $field['columnName']); + } + + if (isset($field['length'])) { + $idXml->addAttribute('length', $field['length']); + } + + if (isset($field['associationKey']) && $field['associationKey']) { + $idXml->addAttribute('association-key', 'true'); + } + + if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $generatorXml = $idXml->addChild('generator'); + $generatorXml->addAttribute('strategy', $idGeneratorType); + + $this->exportSequenceInformation($idXml, $metadata); + } + } + } + + if ($fields) { + foreach ($fields as $field) { + $fieldXml = $root->addChild('field'); + $fieldXml->addAttribute('name', $field['fieldName']); + $fieldXml->addAttribute('type', $field['type']); + + if (isset($field['columnName'])) { + $fieldXml->addAttribute('column', $field['columnName']); + } + + if (isset($field['length'])) { + $fieldXml->addAttribute('length', $field['length']); + } + + if (isset($field['precision'])) { + $fieldXml->addAttribute('precision', $field['precision']); + } + + if (isset($field['scale'])) { + $fieldXml->addAttribute('scale', $field['scale']); + } + + if (isset($field['unique']) && $field['unique']) { + $fieldXml->addAttribute('unique', $field['unique'] ? 'true' : 'false'); + } + + if (isset($field['options'])) { + $optionsXml = $fieldXml->addChild('options'); + foreach ($field['options'] as $key => $value) { + $optionXml = $optionsXml->addChild('option', $value); + $optionXml->addAttribute('name', $key); + } + } + + if (isset($field['version'])) { + $fieldXml->addAttribute('version', $field['version']); + } + + if (isset($field['columnDefinition'])) { + $fieldXml->addAttribute('column-definition', $field['columnDefinition']); + } + + if (isset($field['nullable'])) { + $fieldXml->addAttribute('nullable', $field['nullable'] ? 'true' : 'false'); + } + } + } + + $orderMap = array( + ClassMetadataInfo::ONE_TO_ONE, + ClassMetadataInfo::ONE_TO_MANY, + ClassMetadataInfo::MANY_TO_ONE, + ClassMetadataInfo::MANY_TO_MANY, + ); + + uasort($metadata->associationMappings, function($m1, $m2) use (&$orderMap){ + $a1 = array_search($m1['type'], $orderMap); + $a2 = array_search($m2['type'], $orderMap); + + return strcmp($a1, $a2); + }); + + foreach ($metadata->associationMappings as $associationMapping) { + if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) { + $associationMappingXml = $root->addChild('one-to-one'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_ONE) { + $associationMappingXml = $root->addChild('many-to-one'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $associationMappingXml = $root->addChild('one-to-many'); + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $associationMappingXml = $root->addChild('many-to-many'); + } + + $associationMappingXml->addAttribute('field', $associationMapping['fieldName']); + $associationMappingXml->addAttribute('target-entity', $associationMapping['targetEntity']); + + if (isset($associationMapping['mappedBy'])) { + $associationMappingXml->addAttribute('mapped-by', $associationMapping['mappedBy']); + } + + if (isset($associationMapping['inversedBy'])) { + $associationMappingXml->addAttribute('inversed-by', $associationMapping['inversedBy']); + } + + if (isset($associationMapping['indexBy'])) { + $associationMappingXml->addAttribute('index-by', $associationMapping['indexBy']); + } + + if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval'] !== false) { + $associationMappingXml->addAttribute('orphan-removal', 'true'); + } + + if (isset($associationMapping['fetch'])) { + $associationMappingXml->addAttribute('fetch', $this->_getFetchModeString($associationMapping['fetch'])); + } + + $cascade = array(); + if ($associationMapping['isCascadeRemove']) { + $cascade[] = 'cascade-remove'; + } + + if ($associationMapping['isCascadePersist']) { + $cascade[] = 'cascade-persist'; + } + + if ($associationMapping['isCascadeRefresh']) { + $cascade[] = 'cascade-refresh'; + } + + if ($associationMapping['isCascadeMerge']) { + $cascade[] = 'cascade-merge'; + } + + if ($associationMapping['isCascadeDetach']) { + $cascade[] = 'cascade-detach'; + } + + if (count($cascade) === 5) { + $cascade = array('cascade-all'); + } + + if ($cascade) { + $cascadeXml = $associationMappingXml->addChild('cascade'); + + foreach ($cascade as $type) { + $cascadeXml->addChild($type); + } + } + + if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) { + $joinTableXml = $associationMappingXml->addChild('join-table'); + $joinTableXml->addAttribute('name', $associationMapping['joinTable']['name']); + $joinColumnsXml = $joinTableXml->addChild('join-columns'); + + foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) { + $joinColumnXml = $joinColumnsXml->addChild('join-column'); + $joinColumnXml->addAttribute('name', $joinColumn['name']); + $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); + + if (isset($joinColumn['onDelete'])) { + $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); + } + } + + $inverseJoinColumnsXml = $joinTableXml->addChild('inverse-join-columns'); + + foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + $inverseJoinColumnXml = $inverseJoinColumnsXml->addChild('join-column'); + $inverseJoinColumnXml->addAttribute('name', $inverseJoinColumn['name']); + $inverseJoinColumnXml->addAttribute('referenced-column-name', $inverseJoinColumn['referencedColumnName']); + + if (isset($inverseJoinColumn['onDelete'])) { + $inverseJoinColumnXml->addAttribute('on-delete', $inverseJoinColumn['onDelete']); + } + + if (isset($inverseJoinColumn['columnDefinition'])) { + $inverseJoinColumnXml->addAttribute('column-definition', $inverseJoinColumn['columnDefinition']); + } + + if (isset($inverseJoinColumn['nullable'])) { + $inverseJoinColumnXml->addAttribute('nullable', $inverseJoinColumn['nullable']); + } + + if (isset($inverseJoinColumn['orderBy'])) { + $inverseJoinColumnXml->addAttribute('order-by', $inverseJoinColumn['orderBy']); + } + } + } + if (isset($associationMapping['joinColumns'])) { + $joinColumnsXml = $associationMappingXml->addChild('join-columns'); + + foreach ($associationMapping['joinColumns'] as $joinColumn) { + $joinColumnXml = $joinColumnsXml->addChild('join-column'); + $joinColumnXml->addAttribute('name', $joinColumn['name']); + $joinColumnXml->addAttribute('referenced-column-name', $joinColumn['referencedColumnName']); + + if (isset($joinColumn['onDelete'])) { + $joinColumnXml->addAttribute('on-delete', $joinColumn['onDelete']); + } + + if (isset($joinColumn['columnDefinition'])) { + $joinColumnXml->addAttribute('column-definition', $joinColumn['columnDefinition']); + } + + if (isset($joinColumn['nullable'])) { + $joinColumnXml->addAttribute('nullable', $joinColumn['nullable']); + } + } + } + if (isset($associationMapping['orderBy'])) { + $orderByXml = $associationMappingXml->addChild('order-by'); + + foreach ($associationMapping['orderBy'] as $name => $direction) { + $orderByFieldXml = $orderByXml->addChild('order-by-field'); + $orderByFieldXml->addAttribute('name', $name); + $orderByFieldXml->addAttribute('direction', $direction); + } + } + } + + if (isset($metadata->lifecycleCallbacks) && count($metadata->lifecycleCallbacks)>0) { + $lifecycleCallbacksXml = $root->addChild('lifecycle-callbacks'); + + foreach ($metadata->lifecycleCallbacks as $name => $methods) { + foreach ($methods as $method) { + $lifecycleCallbackXml = $lifecycleCallbacksXml->addChild('lifecycle-callback'); + $lifecycleCallbackXml->addAttribute('type', $name); + $lifecycleCallbackXml->addAttribute('method', $method); + } + } + } + + return $this->_asXml($xml); + } + + /** + * Exports (nested) option elements. + * + * @param \SimpleXMLElement $parentXml + * @param array $options + */ + private function exportTableOptions(\SimpleXMLElement $parentXml, array $options) + { + foreach ($options as $name => $option) { + $isArray = is_array($option); + $optionXml = $isArray + ? $parentXml->addChild('option') + : $parentXml->addChild('option', (string) $option); + + $optionXml->addAttribute('name', (string) $name); + + if ($isArray) { + $this->exportTableOptions($optionXml, $option); + } + } + } + + /** + * Export sequence information (if available/configured) into the current identifier XML node + * + * @param \SimpleXMLElement $identifierXmlNode + * @param ClassMetadataInfo $metadata + * + * @return void + */ + private function exportSequenceInformation(\SimpleXMLElement $identifierXmlNode, ClassMetadataInfo $metadata) + { + $sequenceDefinition = $metadata->sequenceGeneratorDefinition; + + if (! ($metadata->generatorType === ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE && $sequenceDefinition)) { + return; + } + + $sequenceGeneratorXml = $identifierXmlNode->addChild('sequence-generator'); + + $sequenceGeneratorXml->addAttribute('sequence-name', $sequenceDefinition['sequenceName']); + $sequenceGeneratorXml->addAttribute('allocation-size', $sequenceDefinition['allocationSize']); + $sequenceGeneratorXml->addAttribute('initial-value', $sequenceDefinition['initialValue']); + } + + /** + * @param \SimpleXMLElement $simpleXml + * + * @return string $xml + */ + private function _asXml($simpleXml) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->loadXML($simpleXml->asXML()); + $dom->formatOutput = true; + + return $dom->saveXML(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php new file mode 100644 index 0000000..177cebf --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/Driver/YamlExporter.php @@ -0,0 +1,235 @@ +. + */ + +namespace Doctrine\ORM\Tools\Export\Driver; + +use Symfony\Component\Yaml\Yaml; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * ClassMetadata exporter for Doctrine YAML mapping files. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Jonathan Wage + */ +class YamlExporter extends AbstractExporter +{ + /** + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritdoc} + */ + public function exportClassMetadata(ClassMetadataInfo $metadata) + { + $array = array(); + + if ($metadata->isMappedSuperclass) { + $array['type'] = 'mappedSuperclass'; + } else { + $array['type'] = 'entity'; + } + + $array['table'] = $metadata->table['name']; + + if (isset($metadata->table['schema'])) { + $array['schema'] = $metadata->table['schema']; + } + + $inheritanceType = $metadata->inheritanceType; + + if ($inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) { + $array['inheritanceType'] = $this->_getInheritanceTypeString($inheritanceType); + } + + if ($column = $metadata->discriminatorColumn) { + $array['discriminatorColumn'] = $column; + } + + if ($map = $metadata->discriminatorMap) { + $array['discriminatorMap'] = $map; + } + + if ($metadata->changeTrackingPolicy !== ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT) { + $array['changeTrackingPolicy'] = $this->_getChangeTrackingPolicyString($metadata->changeTrackingPolicy); + } + + if (isset($metadata->table['indexes'])) { + $array['indexes'] = $metadata->table['indexes']; + } + + if ($metadata->customRepositoryClassName) { + $array['repositoryClass'] = $metadata->customRepositoryClassName; + } + + if (isset($metadata->table['uniqueConstraints'])) { + $array['uniqueConstraints'] = $metadata->table['uniqueConstraints']; + } + + if (isset($metadata->table['options'])) { + $array['options'] = $metadata->table['options']; + } + + $fieldMappings = $metadata->fieldMappings; + + $ids = array(); + foreach ($fieldMappings as $name => $fieldMapping) { + $fieldMapping['column'] = $fieldMapping['columnName']; + + unset($fieldMapping['columnName'], $fieldMapping['fieldName']); + + if ($fieldMapping['column'] == $name) { + unset($fieldMapping['column']); + } + + if (isset($fieldMapping['id']) && $fieldMapping['id']) { + $ids[$name] = $fieldMapping; + unset($fieldMappings[$name]); + continue; + } + + $fieldMappings[$name] = $fieldMapping; + } + + if ( ! $metadata->isIdentifierComposite && $idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { + $ids[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType; + } + + $array['id'] = $ids; + + if ($fieldMappings) { + if ( ! isset($array['fields'])) { + $array['fields'] = array(); + } + $array['fields'] = array_merge($array['fields'], $fieldMappings); + } + + foreach ($metadata->associationMappings as $name => $associationMapping) { + $cascade = array(); + + if ($associationMapping['isCascadeRemove']) { + $cascade[] = 'remove'; + } + + if ($associationMapping['isCascadePersist']) { + $cascade[] = 'persist'; + } + + if ($associationMapping['isCascadeRefresh']) { + $cascade[] = 'refresh'; + } + + if ($associationMapping['isCascadeMerge']) { + $cascade[] = 'merge'; + } + + if ($associationMapping['isCascadeDetach']) { + $cascade[] = 'detach'; + } + if (count($cascade) === 5) { + $cascade = array('all'); + } + + $associationMappingArray = array( + 'targetEntity' => $associationMapping['targetEntity'], + 'cascade' => $cascade, + ); + + if (isset($associationMapping['fetch'])) { + $associationMappingArray['fetch'] = $this->_getFetchModeString($associationMapping['fetch']); + } + + if (isset($mapping['id']) && $mapping['id'] === true) { + $array['id'][$name]['associationKey'] = true; + } + + if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) { + $joinColumns = $associationMapping['joinColumns']; + $newJoinColumns = array(); + + foreach ($joinColumns as $joinColumn) { + $newJoinColumns[$joinColumn['name']]['referencedColumnName'] = $joinColumn['referencedColumnName']; + + if (isset($joinColumn['onDelete'])) { + $newJoinColumns[$joinColumn['name']]['onDelete'] = $joinColumn['onDelete']; + } + } + + $oneToOneMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinColumns' => $newJoinColumns, + 'orphanRemoval' => $associationMapping['orphanRemoval'], + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToOneMappingArray); + + if ($associationMapping['type'] & ClassMetadataInfo::ONE_TO_ONE) { + $array['oneToOne'][$name] = $associationMappingArray; + } else { + $array['manyToOne'][$name] = $associationMappingArray; + } + } elseif ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { + $oneToManyMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'orphanRemoval' => $associationMapping['orphanRemoval'], + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null + ); + + $associationMappingArray = array_merge($associationMappingArray, $oneToManyMappingArray); + $array['oneToMany'][$name] = $associationMappingArray; + } elseif ($associationMapping['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $manyToManyMappingArray = array( + 'mappedBy' => $associationMapping['mappedBy'], + 'inversedBy' => $associationMapping['inversedBy'], + 'joinTable' => isset($associationMapping['joinTable']) ? $associationMapping['joinTable'] : null, + 'orderBy' => isset($associationMapping['orderBy']) ? $associationMapping['orderBy'] : null + ); + + $associationMappingArray = array_merge($associationMappingArray, $manyToManyMappingArray); + $array['manyToMany'][$name] = $associationMappingArray; + } + } + if (isset($metadata->lifecycleCallbacks)) { + $array['lifecycleCallbacks'] = $metadata->lifecycleCallbacks; + } + + return $this->yamlDump(array($metadata->name => $array), 10); + } + + /** + * Dumps a PHP array to a YAML string. + * + * The yamlDump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param integer $inline [optional] The level where you switch to inline YAML + * + * @return string A YAML string representing the original PHP array + */ + protected function yamlDump($array, $inline = 2) + { + return Yaml::dump($array, $inline); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php new file mode 100644 index 0000000..89ddfe2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Export/ExportException.php @@ -0,0 +1,38 @@ + FROM ()) + * + * Works with composite keys but cannot deal with queries that have multiple + * root entities (e.g. `SELECT f, b from Foo, Bar`) + * + * @author Sander Marechal + */ +class CountOutputWalker extends SqlWalker +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * Constructor. + * + * Stores various parameters that are otherwise unavailable + * because Doctrine\ORM\Query\SqlWalker keeps everything private without + * accessors. + * + * @param \Doctrine\ORM\Query $query + * @param \Doctrine\ORM\Query\ParserResult $parserResult + * @param array $queryComponents + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); + $this->rsm = $parserResult->getResultSetMapping(); + $this->queryComponents = $queryComponents; + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT). + * + * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL) + * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery + * that will most likely be executed next can be read from the native SQL cache. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($this->platform->getName() === "mssql") { + $AST->orderByClause = null; + } + + $sql = parent::walkSelectStatement($AST); + + // Find out the SQL alias of the identifier column of the root entity + // It may be possible to make this work with multiple root entities but that + // would probably require issuing multiple queries or doing a UNION SELECT + // so for now, It's not supported. + + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->queryComponents[$rootAlias]['metadata']; + $rootIdentifier = $rootClass->identifier; + + // For every identifier, find out the SQL alias by combing through the ResultSetMapping + $sqlIdentifier = array(); + foreach ($rootIdentifier as $property) { + if (isset($rootClass->fieldMappings[$property])) { + foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + + if (isset($rootClass->associationMappings[$property])) { + $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + + foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { + throw new \RuntimeException(sprintf( + 'Not all identifier properties can be found in the ResultSetMapping: %s', + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + )); + } + + // Build the counter query + return sprintf('SELECT %s AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table', + $this->platform->getCountExpression('*'), + implode(', ', $sqlIdentifier), + $sql + ); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php new file mode 100644 index 0000000..ff0c6b3 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php @@ -0,0 +1,88 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class CountWalker extends TreeWalkerAdapter +{ + /** + * Distinct mode hint name. + */ + const HINT_DISTINCT = 'doctrine_paginator.distinct'; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve a COUNT. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($AST->havingClause) { + throw new \RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination'); + } + + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($rootClass->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, + $identifierFieldName + ); + $pathExpression->type = $pathType; + + $distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT); + $AST->selectClause->selectExpressions = array( + new SelectExpression( + new AggregateExpression('count', $pathExpression, $distinct), null + ) + ); + + // ORDER BY is not needed, only increases query execution through unnecessary sorting. + $AST->orderByClause = null; + } +} + diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php new file mode 100644 index 0000000..b1d7bd9 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -0,0 +1,589 @@ + FROM () LIMIT x OFFSET y + * + * Works with composite keys but cannot deal with queries that have multiple + * root entities (e.g. `SELECT f, b from Foo, Bar`) + * + * @author Sander Marechal + */ +class LimitSubqueryOutputWalker extends SqlWalker +{ + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var \Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * @var int + */ + private $firstResult; + + /** + * @var int + */ + private $maxResults; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * @var array + */ + private $orderByPathExpressions = []; + + /** + * @var bool We don't want to add path expressions from sub-selects into the select clause of the containing query. + * This state flag simply keeps track on whether we are walking on a subquery or not + */ + private $inSubSelect = false; + + /** + * Constructor. + * + * Stores various parameters that are otherwise unavailable + * because Doctrine\ORM\Query\SqlWalker keeps everything private without + * accessors. + * + * @param \Doctrine\ORM\Query $query + * @param \Doctrine\ORM\Query\ParserResult $parserResult + * @param array $queryComponents + */ + public function __construct($query, $parserResult, array $queryComponents) + { + $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform(); + $this->rsm = $parserResult->getResultSetMapping(); + $this->queryComponents = $queryComponents; + + // Reset limit and offset + $this->firstResult = $query->getFirstResult(); + $this->maxResults = $query->getMaxResults(); + $query->setFirstResult(null)->setMaxResults(null); + + $this->em = $query->getEntityManager(); + $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy(); + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Check if the platform supports the ROW_NUMBER window function. + * + * @return bool + */ + private function platformSupportsRowNumber() + { + return $this->platform instanceof PostgreSqlPlatform + || $this->platform instanceof SQLServerPlatform + || $this->platform instanceof OraclePlatform + || $this->platform instanceof SQLAnywherePlatform + || $this->platform instanceof DB2Platform + || (method_exists($this->platform, 'supportsRowNumberFunction') + && $this->platform->supportsRowNumberFunction()); + } + + /** + * Rebuilds a select statement's order by clause for use in a + * ROW_NUMBER() OVER() expression. + * + * @param SelectStatement $AST + */ + private function rebuildOrderByForRowNumber(SelectStatement $AST) + { + $orderByClause = $AST->orderByClause; + $selectAliasToExpressionMap = []; + // Get any aliases that are available for select expressions. + foreach ($AST->selectClause->selectExpressions as $selectExpression) { + $selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression; + } + + // Rebuild string orderby expressions to use the select expression they're referencing + foreach ($orderByClause->orderByItems as $orderByItem) { + if (is_string($orderByItem->expression) && isset($selectAliasToExpressionMap[$orderByItem->expression])) { + $orderByItem->expression = $selectAliasToExpressionMap[$orderByItem->expression]; + } + } + $func = new RowNumberOverFunction('dctrn_rownum'); + $func->orderByClause = $AST->orderByClause; + $AST->selectClause->selectExpressions[] = new SelectExpression($func, 'dctrn_rownum', true); + + // No need for an order by clause, we'll order by rownum in the outer query. + $AST->orderByClause = null; + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + if ($this->platformSupportsRowNumber()) { + return $this->walkSelectStatementWithRowNumber($AST); + } + return $this->walkSelectStatementWithoutRowNumber($AST); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * This method is for use with platforms which support ROW_NUMBER. + * + * @param SelectStatement $AST + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatementWithRowNumber(SelectStatement $AST) + { + $hasOrderBy = false; + $outerOrderBy = ' ORDER BY dctrn_minrownum ASC'; + $orderGroupBy = ''; + if ($AST->orderByClause instanceof OrderByClause) { + $hasOrderBy = true; + $this->rebuildOrderByForRowNumber($AST); + } + + $innerSql = $this->getInnerSQL($AST); + + $sqlIdentifier = $this->getSQLIdentifier($AST); + + if ($hasOrderBy) { + $orderGroupBy = ' GROUP BY ' . implode(', ', $sqlIdentifier); + $sqlIdentifier[] = 'MIN(' . $this->walkResultVariable('dctrn_rownum') . ') AS dctrn_minrownum'; + } + + // Build the counter query + $sql = sprintf( + 'SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), + $innerSql + ); + + if ($hasOrderBy) { + $sql .= $orderGroupBy . $outerOrderBy; + } + + // Apply the limit and offset. + $sql = $this->platform->modifyLimitQuery( + $sql, + $this->maxResults, + $this->firstResult + ); + + // Add the columns to the ResultSetMapping. It's not really nice but + // it works. Preferably I'd clear the RSM or simply create a new one + // but that is not possible from inside the output walker, so we dirty + // up the one we have. + foreach ($sqlIdentifier as $property => $alias) { + $this->rsm->addScalarResult($alias, $property); + } + + return $sql; + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT. + * This method is for platforms which DO NOT support ROW_NUMBER. + * + * @param SelectStatement $AST + * @param bool $addMissingItemsFromOrderByToSelect + * + * @return string + * + * @throws \RuntimeException + */ + public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, $addMissingItemsFromOrderByToSelect = true) + { + // We don't want to call this recursively! + if ($AST->orderByClause instanceof OrderByClause && $addMissingItemsFromOrderByToSelect) { + // In the case of ordering a query by columns from joined tables, we + // must add those columns to the select clause of the query BEFORE + // the SQL is generated. + $this->addMissingItemsFromOrderByToSelect($AST); + } + + // Remove order by clause from the inner query + // It will be re-appended in the outer select generated by this method + $orderByClause = $AST->orderByClause; + $AST->orderByClause = null; + + $innerSql = $this->getInnerSQL($AST); + + $sqlIdentifier = $this->getSQLIdentifier($AST); + + // Build the counter query + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $innerSql); + + // http://www.doctrine-project.org/jira/browse/DDC-1958 + $sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause); + + // Apply the limit and offset. + $sql = $this->platform->modifyLimitQuery( + $sql, $this->maxResults, $this->firstResult + ); + + // Add the columns to the ResultSetMapping. It's not really nice but + // it works. Preferably I'd clear the RSM or simply create a new one + // but that is not possible from inside the output walker, so we dirty + // up the one we have. + foreach ($sqlIdentifier as $property => $alias) { + $this->rsm->addScalarResult($alias, $property); + } + + // Restore orderByClause + $AST->orderByClause = $orderByClause; + + return $sql; + } + + /** + * Finds all PathExpressions in an AST's OrderByClause, and ensures that + * the referenced fields are present in the SelectClause of the passed AST. + * + * @param SelectStatement $AST + */ + private function addMissingItemsFromOrderByToSelect(SelectStatement $AST) + { + $this->orderByPathExpressions = []; + + // We need to do this in another walker because otherwise we'll end up + // polluting the state of this one. + $walker = clone $this; + + // This will populate $orderByPathExpressions via + // LimitSubqueryOutputWalker::walkPathExpression, which will be called + // as the select statement is walked. We'll end up with an array of all + // path expressions referenced in the query. + $walker->walkSelectStatementWithoutRowNumber($AST, false); + $orderByPathExpressions = $walker->getOrderByPathExpressions(); + + // Get a map of referenced identifiers to field names. + $selects = []; + foreach ($orderByPathExpressions as $pathExpression) { + $idVar = $pathExpression->identificationVariable; + $field = $pathExpression->field; + if (!isset($selects[$idVar])) { + $selects[$idVar] = []; + } + $selects[$idVar][$field] = true; + } + + // Loop the select clause of the AST and exclude items from $select + // that are already being selected in the query. + foreach ($AST->selectClause->selectExpressions as $selectExpression) { + if ($selectExpression instanceof SelectExpression) { + $idVar = $selectExpression->expression; + if (!is_string($idVar)) { + continue; + } + $field = $selectExpression->fieldIdentificationVariable; + if ($field === null) { + // No need to add this select, as we're already fetching the whole object. + unset($selects[$idVar]); + } else { + unset($selects[$idVar][$field]); + } + } + } + + // Add select items which were not excluded to the AST's select clause. + foreach ($selects as $idVar => $fields) { + $AST->selectClause->selectExpressions[] = new SelectExpression(new PartialObjectExpression($idVar, array_keys($fields)), null, true); + } + } + + /** + * Generates new SQL for statements with an order by clause + * + * @param array $sqlIdentifier + * @param string $innerSql + * @param string $sql + * @param OrderByClause $orderByClause + * + * @return string + */ + private function preserveSqlOrdering(array $sqlIdentifier, $innerSql, $sql, $orderByClause) + { + // If the sql statement has an order by clause, we need to wrap it in a new select distinct + // statement + if (! $orderByClause instanceof OrderByClause) { + return $sql; + } + + // Rebuild the order by clause to work in the scope of the new select statement + /* @var array $orderBy an array of rebuilt order by items */ + $orderBy = $this->rebuildOrderByClauseForOuterScope($orderByClause); + + // Build the select distinct statement + $sql = sprintf( + 'SELECT DISTINCT %s FROM (%s) dctrn_result ORDER BY %s', + implode(', ', $sqlIdentifier), + $innerSql, + implode(', ', $orderBy) + ); + + return $sql; + } + + /** + * Generates a new order by clause that works in the scope of a select query wrapping the original + * + * @param OrderByClause $orderByClause + * @return array + */ + private function rebuildOrderByClauseForOuterScope(OrderByClause $orderByClause) + { + $dqlAliasToSqlTableAliasMap + = $searchPatterns + = $replacements + = $dqlAliasToClassMap + = $selectListAdditions + = $orderByItems + = []; + + // Generate DQL alias -> SQL table alias mapping + foreach(array_keys($this->rsm->aliasMap) as $dqlAlias) { + $dqlAliasToClassMap[$dqlAlias] = $class = $this->queryComponents[$dqlAlias]['metadata']; + $dqlAliasToSqlTableAliasMap[$dqlAlias] = $this->getSQLTableAlias($class->getTableName(), $dqlAlias); + } + + // Pattern to find table path expressions in the order by clause + $fieldSearchPattern = '/(?rsm->fieldMappings as $fieldAlias => $fieldName) { + $dqlAliasForFieldAlias = $this->rsm->columnOwnerMap[$fieldAlias]; + $class = $dqlAliasToClassMap[$dqlAliasForFieldAlias]; + + // If the field is from a joined child table, we won't be ordering + // on it. + if (!isset($class->fieldMappings[$fieldName])) { + continue; + } + + $fieldMapping = $class->fieldMappings[$fieldName]; + + // Get the proper column name as will appear in the select list + $columnName = $this->quoteStrategy->getColumnName( + $fieldName, + $dqlAliasToClassMap[$dqlAliasForFieldAlias], + $this->em->getConnection()->getDatabasePlatform() + ); + + // Get the SQL table alias for the entity and field + $sqlTableAliasForFieldAlias = $dqlAliasToSqlTableAliasMap[$dqlAliasForFieldAlias]; + if (isset($fieldMapping['declared']) && $fieldMapping['declared'] !== $class->name) { + // Field was declared in a parent class, so we need to get the proper SQL table alias + // for the joined parent table. + $otherClassMetadata = $this->em->getClassMetadata($fieldMapping['declared']); + if (!$otherClassMetadata->isMappedSuperclass) { + $sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias); + + } + } + + // Compose search/replace patterns + $searchPatterns[] = sprintf($fieldSearchPattern, $sqlTableAliasForFieldAlias, $columnName); + $replacements[] = $fieldAlias; + } + + foreach($orderByClause->orderByItems as $orderByItem) { + // Walk order by item to get string representation of it + $orderByItemString = $this->walkOrderByItem($orderByItem); + + // Replace path expressions in the order by clause with their column alias + $orderByItemString = preg_replace($searchPatterns, $replacements, $orderByItemString); + + $orderByItems[] = $orderByItemString; + } + + return $orderByItems; + } + + /** + * getter for $orderByPathExpressions + * + * @return array + */ + public function getOrderByPathExpressions() + { + return $this->orderByPathExpressions; + } + + /** + * @param SelectStatement $AST + * + * @return string + * + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\Query\QueryException + */ + private function getInnerSQL(SelectStatement $AST) + { + // Set every select expression as visible(hidden = false) to + // make $AST have scalar mappings properly - this is relevant for referencing selected + // fields from outside the subquery, for example in the ORDER BY segment + $hiddens = []; + + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $hiddens[$idx] = $expr->hiddenAliasResultVariable; + $expr->hiddenAliasResultVariable = false; + } + + $innerSql = parent::walkSelectStatement($AST); + + // Restore hiddens + foreach ($AST->selectClause->selectExpressions as $idx => $expr) { + $expr->hiddenAliasResultVariable = $hiddens[$idx]; + } + + return $innerSql; + } + + /** + * @param SelectStatement $AST + * + * @return array + */ + private function getSQLIdentifier(SelectStatement $AST) + { + // Find out the SQL alias of the identifier column of the root entity. + // It may be possible to make this work with multiple root entities but that + // would probably require issuing multiple queries or doing a UNION SELECT. + // So for now, it's not supported. + + // Get the root entity and alias from the AST fromClause. + $from = $AST->fromClause->identificationVariableDeclarations; + if (count($from) !== 1) { + throw new \RuntimeException('Cannot count query which selects two FROM components, cannot make distinction'); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $this->queryComponents[$rootAlias]['metadata']; + $rootIdentifier = $rootClass->identifier; + + // For every identifier, find out the SQL alias by combing through the ResultSetMapping + $sqlIdentifier = []; + foreach ($rootIdentifier as $property) { + if (isset($rootClass->fieldMappings[$property])) { + foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + + if (isset($rootClass->associationMappings[$property])) { + $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name']; + + foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) { + if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) { + $sqlIdentifier[$property] = $alias; + } + } + } + } + + if (count($sqlIdentifier) === 0) { + throw new \RuntimeException('The Paginator does not support Queries which only yield ScalarResults.'); + } + + if (count($rootIdentifier) != count($sqlIdentifier)) { + throw new \RuntimeException(sprintf( + 'Not all identifier properties can be found in the ResultSetMapping: %s', + implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier))) + )); + } + + return $sqlIdentifier; + } + + /** + * {@inheritdoc} + */ + public function walkPathExpression($pathExpr) + { + if (!$this->inSubSelect && !$this->platformSupportsRowNumber() && !in_array($pathExpr, $this->orderByPathExpressions)) { + $this->orderByPathExpressions[] = $pathExpr; + } + + return parent::walkPathExpression($pathExpr); + } + + /** + * {@inheritdoc} + */ + public function walkSubSelect($subselect) + { + $this->inSubSelect = true; + + $sql = parent::walkSubselect($subselect); + + $this->inSubSelect = false; + + return $sql; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php new file mode 100644 index 0000000..e65cfca --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -0,0 +1,175 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\ORMException; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\TreeWalkerAdapter; +use Doctrine\ORM\Query\AST\Functions\IdentityFunction; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\SelectExpression; +use Doctrine\ORM\Query\AST\SelectStatement; + +/** + * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent. + * + * @category DoctrineExtensions + * @package DoctrineExtensions\Paginate + * @author David Abdemoulaie + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class LimitSubqueryWalker extends TreeWalkerAdapter +{ + /** + * ID type hint. + */ + const IDENTIFIER_TYPE = 'doctrine_paginator.id.type'; + + /** + * Counter for generating unique order column aliases. + * + * @var int + */ + private $_aliasCounter = 0; + + /** + * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids + * of the root Entity. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $selectExpressions = array(); + + $this->validate($AST); + + foreach ($queryComponents as $dqlAlias => $qComp) { + // Preserve mixed data in query for ordering. + if (isset($qComp['resultVariable'])) { + $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias); + continue; + } + } + + $identifier = $rootClass->getSingleIdentifierFieldName(); + + if (isset($rootClass->associationMappings[$identifier])) { + throw new \RuntimeException("Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator."); + } + + $this->_getQuery()->setHint( + self::IDENTIFIER_TYPE, + Type::getType($rootClass->getTypeOfField($identifier)) + ); + + $pathExpression = new PathExpression( + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, + $rootAlias, + $identifier + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + + array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id')); + $AST->selectClause->selectExpressions = $selectExpressions; + + if (isset($AST->orderByClause)) { + foreach ($AST->orderByClause->orderByItems as $item) { + if ( ! $item->expression instanceof PathExpression) { + continue; + } + + $AST->selectClause->selectExpressions[] = new SelectExpression( + $this->createSelectExpressionItem($item->expression), + '_dctrn_ord' . $this->_aliasCounter++ + ); + } + } + + $AST->selectClause->isDistinct = true; + } + + /** + * Validate the AST to ensure that this walker is able to properly manipulate it. + * + * @param SelectStatement $AST + */ + private function validate(SelectStatement $AST) + { + // Prevent LimitSubqueryWalker from being used with queries that include + // a limit, a fetched to-many join, and an order by condition that + // references a column from the fetch joined table. + $queryComponents = $this->getQueryComponents(); + $query = $this->_getQuery(); + $from = $AST->fromClause->identificationVariableDeclarations; + $fromRoot = reset($from); + + if ($query instanceof Query + && $query->getMaxResults() + && $AST->orderByClause + && count($fromRoot->joins)) { + // Check each orderby item. + // TODO: check complex orderby items too... + foreach ($AST->orderByClause->orderByItems as $orderByItem) { + $expression = $orderByItem->expression; + if ($orderByItem->expression instanceof PathExpression + && isset($queryComponents[$expression->identificationVariable])) { + $queryComponent = $queryComponents[$expression->identificationVariable]; + if (isset($queryComponent['parent']) + && $queryComponent['relation']['type'] & ClassMetadataInfo::TO_MANY) { + throw new \RuntimeException("Cannot select distinct identifiers from query with LIMIT and ORDER BY on a column from a fetch joined to-many association. Use output walkers."); + } + } + } + } + } + + /** + * Retrieve either an IdentityFunction (IDENTITY(u.assoc)) or a state field (u.name). + * + * @param \Doctrine\ORM\Query\AST\PathExpression $pathExpression + * + * @return \Doctrine\ORM\Query\AST\Functions\IdentityFunction + */ + private function createSelectExpressionItem(PathExpression $pathExpression) + { + if ($pathExpression->type === PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION) { + $identity = new IdentityFunction('identity'); + + $identity->pathExpression = clone $pathExpression; + + return $identity; + } + + return clone $pathExpression; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php new file mode 100644 index 0000000..c5b0b36 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php @@ -0,0 +1,279 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\Query\Parser; +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Query; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\NoResultException; + +/** + * The paginator can handle various complex scenarios with DQL. + * + * @author Pablo Díez + * @author Benjamin Eberlei + * @license New BSD + */ +class Paginator implements \Countable, \IteratorAggregate +{ + /** + * @var Query + */ + private $query; + + /** + * @var bool + */ + private $fetchJoinCollection; + + /** + * @var bool|null + */ + private $useOutputWalkers; + + /** + * @var int + */ + private $count; + + /** + * Constructor. + * + * @param Query|QueryBuilder $query A Doctrine ORM query or query builder. + * @param boolean $fetchJoinCollection Whether the query joins a collection (true by default). + */ + public function __construct($query, $fetchJoinCollection = true) + { + if ($query instanceof QueryBuilder) { + $query = $query->getQuery(); + } + + $this->query = $query; + $this->fetchJoinCollection = (Boolean) $fetchJoinCollection; + } + + /** + * Returns the query. + * + * @return Query + */ + public function getQuery() + { + return $this->query; + } + + /** + * Returns whether the query joins a collection. + * + * @return boolean Whether the query joins a collection. + */ + public function getFetchJoinCollection() + { + return $this->fetchJoinCollection; + } + + /** + * Returns whether the paginator will use an output walker. + * + * @return bool|null + */ + public function getUseOutputWalkers() + { + return $this->useOutputWalkers; + } + + /** + * Sets whether the paginator will use an output walker. + * + * @param bool|null $useOutputWalkers + * + * @return $this + */ + public function setUseOutputWalkers($useOutputWalkers) + { + $this->useOutputWalkers = $useOutputWalkers; + return $this; + } + + /** + * {@inheritdoc} + */ + public function count() + { + if ($this->count === null) { + try { + $this->count = array_sum(array_map('current', $this->getCountQuery()->getScalarResult())); + } catch(NoResultException $e) { + $this->count = 0; + } + } + + return $this->count; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $offset = $this->query->getFirstResult(); + $length = $this->query->getMaxResults(); + + if ($this->fetchJoinCollection) { + $subQuery = $this->cloneQuery($this->query); + + if ($this->useOutputWalker($subQuery)) { + $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryOutputWalker'); + } else { + $this->appendTreeWalker($subQuery, 'Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'); + } + + $subQuery->setFirstResult($offset)->setMaxResults($length); + + $ids = array_map('current', $subQuery->getScalarResult()); + + $whereInQuery = $this->cloneQuery($this->query); + // don't do this for an empty id array + if (count($ids) == 0) { + return new \ArrayIterator(array()); + } + + $this->appendTreeWalker($whereInQuery, 'Doctrine\ORM\Tools\Pagination\WhereInWalker'); + $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids)); + $whereInQuery->setFirstResult(null)->setMaxResults(null); + $whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $ids); + $whereInQuery->setCacheable($this->query->isCacheable()); + + $result = $whereInQuery->getResult($this->query->getHydrationMode()); + } else { + $result = $this->cloneQuery($this->query) + ->setMaxResults($length) + ->setFirstResult($offset) + ->setCacheable($this->query->isCacheable()) + ->getResult($this->query->getHydrationMode()) + ; + } + + return new \ArrayIterator($result); + } + + /** + * Clones a query. + * + * @param Query $query The query. + * + * @return Query The cloned query. + */ + private function cloneQuery(Query $query) + { + /* @var $cloneQuery Query */ + $cloneQuery = clone $query; + + $cloneQuery->setParameters(clone $query->getParameters()); + $cloneQuery->setCacheable(false); + + foreach ($query->getHints() as $name => $value) { + $cloneQuery->setHint($name, $value); + } + + return $cloneQuery; + } + + /** + * Determines whether to use an output walker for the query. + * + * @param Query $query The query. + * + * @return bool + */ + private function useOutputWalker(Query $query) + { + if ($this->useOutputWalkers === null) { + return (Boolean) $query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER) == false; + } + + return $this->useOutputWalkers; + } + + /** + * Appends a custom tree walker to the tree walkers hint. + * + * @param Query $query + * @param string $walkerClass + */ + private function appendTreeWalker(Query $query, $walkerClass) + { + $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); + + if ($hints === false) { + $hints = array(); + } + + $hints[] = $walkerClass; + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints); + } + + /** + * Returns Query prepared to count. + * + * @return Query + */ + private function getCountQuery() + { + /* @var $countQuery Query */ + $countQuery = $this->cloneQuery($this->query); + + if ( ! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) { + $countQuery->setHint(CountWalker::HINT_DISTINCT, true); + } + + if ($this->useOutputWalker($countQuery)) { + $platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win + + $rsm = new ResultSetMapping(); + $rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count'); + + $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker'); + $countQuery->setResultSetMapping($rsm); + } else { + $this->appendTreeWalker($countQuery, 'Doctrine\ORM\Tools\Pagination\CountWalker'); + } + + $countQuery->setFirstResult(null)->setMaxResults(null); + + $parser = new Parser($countQuery); + $parameterMappings = $parser->parse()->getParameterMappings(); + /* @var $parameters \Doctrine\Common\Collections\Collection|\Doctrine\ORM\Query\Parameter[] */ + $parameters = $countQuery->getParameters(); + + foreach ($parameters as $key => $parameter) { + $parameterName = $parameter->getName(); + + if( ! (isset($parameterMappings[$parameterName]) || array_key_exists($parameterName, $parameterMappings))) { + unset($parameters[$key]); + } + } + + $countQuery->setParameters($parameters); + + return $countQuery; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/RowNumberOverFunction.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/RowNumberOverFunction.php new file mode 100644 index 0000000..ca3e57b --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/RowNumberOverFunction.php @@ -0,0 +1,61 @@ +. + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\ORMException; +use Doctrine\ORM\Query\AST\Functions\FunctionNode; + + +/** + * RowNumberOverFunction + * + * Provides ROW_NUMBER() OVER(ORDER BY...) construct for use in LimitSubqueryOutputWalker + * + * @since 2.5 + * @author Bill Schaller + */ +class RowNumberOverFunction extends FunctionNode +{ + /** + * @var \Doctrine\ORM\Query\AST\OrderByClause + */ + public $orderByClause; + + /** + * @override + */ + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'ROW_NUMBER() OVER(' . trim($sqlWalker->walkOrderByClause( + $this->orderByClause + )) . ')'; + } + + /** + * @override + * + * @throws ORMException + */ + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + throw new ORMException("The RowNumberOverFunction is not intended for, nor is it enabled for use in DQL."); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php new file mode 100644 index 0000000..9f42a1e --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php @@ -0,0 +1,142 @@ + + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ + +namespace Doctrine\ORM\Tools\Pagination; + +use Doctrine\ORM\Query\AST\ArithmeticExpression; +use Doctrine\ORM\Query\AST\SimpleArithmeticExpression; +use Doctrine\ORM\Query\TreeWalkerAdapter; +use Doctrine\ORM\Query\AST\SelectStatement; +use Doctrine\ORM\Query\AST\PathExpression; +use Doctrine\ORM\Query\AST\InExpression; +use Doctrine\ORM\Query\AST\NullComparisonExpression; +use Doctrine\ORM\Query\AST\InputParameter; +use Doctrine\ORM\Query\AST\ConditionalPrimary; +use Doctrine\ORM\Query\AST\ConditionalTerm; +use Doctrine\ORM\Query\AST\ConditionalExpression; +use Doctrine\ORM\Query\AST\ConditionalFactor; +use Doctrine\ORM\Query\AST\WhereClause; + +/** + * Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent. + * + * @category DoctrineExtensions + * @package DoctrineExtensions\Paginate + * @author David Abdemoulaie + * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/) + * @license http://hobodave.com/license.txt New BSD License + */ +class WhereInWalker extends TreeWalkerAdapter +{ + /** + * ID Count hint name. + */ + const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count'; + + /** + * Primary key alias for query. + */ + const PAGINATOR_ID_ALIAS = 'dpid'; + + /** + * Replaces the whereClause in the AST. + * + * Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...) + * + * The parameter namespace (dpid) is defined by + * the PAGINATOR_ID_ALIAS + * + * The total number of parameters is retrieved from + * the HINT_PAGINATOR_ID_COUNT query hint. + * + * @param SelectStatement $AST + * + * @return void + * + * @throws \RuntimeException + */ + public function walkSelectStatement(SelectStatement $AST) + { + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + + if (count($from) > 1) { + throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); + } + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); + + $pathType = PathExpression::TYPE_STATE_FIELD; + if (isset($rootClass->associationMappings[$identifierFieldName])) { + $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + } + + $pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName); + $pathExpression->type = $pathType; + + $count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT); + + if ($count > 0) { + $arithmeticExpression = new ArithmeticExpression(); + $arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression( + array($pathExpression) + ); + $expression = new InExpression($arithmeticExpression); + $expression->literals[] = new InputParameter(":" . self::PAGINATOR_ID_ALIAS); + + } else { + $expression = new NullComparisonExpression($pathExpression); + $expression->not = false; + } + + $conditionalPrimary = new ConditionalPrimary; + $conditionalPrimary->simpleConditionalExpression = $expression; + if ($AST->whereClause) { + if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) { + $AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary; + } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) { + $AST->whereClause->conditionalExpression = new ConditionalExpression(array( + new ConditionalTerm(array( + $AST->whereClause->conditionalExpression, + $conditionalPrimary + )) + )); + } elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression + || $AST->whereClause->conditionalExpression instanceof ConditionalFactor + ) { + $tmpPrimary = new ConditionalPrimary; + $tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression; + $AST->whereClause->conditionalExpression = new ConditionalTerm(array( + $tmpPrimary, + $conditionalPrimary + )); + } + } else { + $AST->whereClause = new WhereClause( + new ConditionalExpression(array( + new ConditionalTerm(array( + $conditionalPrimary + )) + )) + ); + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php new file mode 100644 index 0000000..574c9c2 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ResolveTargetEntityListener.php @@ -0,0 +1,144 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\EventSubscriber; +use Doctrine\ORM\Events; + +/** + * ResolveTargetEntityListener + * + * Mechanism to overwrite interfaces or classes specified as association + * targets. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class ResolveTargetEntityListener implements EventSubscriber +{ + /** + * @var array[] indexed by original entity name + */ + private $resolveTargetEntities = array(); + + /** + * {@inheritDoc} + */ + public function getSubscribedEvents() + { + return array( + Events::loadClassMetadata, + Events::onClassMetadataNotFound + ); + } + + /** + * Adds a target-entity class name to resolve to a new class name. + * + * @param string $originalEntity + * @param string $newEntity + * @param array $mapping + * + * @return void + */ + public function addResolveTargetEntity($originalEntity, $newEntity, array $mapping) + { + $mapping['targetEntity'] = ltrim($newEntity, "\\"); + $this->resolveTargetEntities[ltrim($originalEntity, "\\")] = $mapping; + } + + /** + * @param OnClassMetadataNotFoundEventArgs $args + * + * @internal this is an event callback, and should not be called directly + * + * @return void + */ + public function onClassMetadataNotFound(OnClassMetadataNotFoundEventArgs $args) + { + if (array_key_exists($args->getClassName(), $this->resolveTargetEntities)) { + $args->setFoundMetadata( + $args + ->getObjectManager() + ->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity']) + ); + } + } + + /** + * Processes event and resolves new target entity names. + * + * @param LoadClassMetadataEventArgs $args + * + * @return void + * + * @internal this is an event callback, and should not be called directly + */ + public function loadClassMetadata(LoadClassMetadataEventArgs $args) + { + /* @var $cm \Doctrine\ORM\Mapping\ClassMetadata */ + $cm = $args->getClassMetadata(); + + foreach ($cm->associationMappings as $mapping) { + if (isset($this->resolveTargetEntities[$mapping['targetEntity']])) { + $this->remapAssociation($cm, $mapping); + } + } + + foreach ($this->resolveTargetEntities as $interface => $data) { + if ($data['targetEntity'] == $cm->getName()) { + $args->getEntityManager()->getMetadataFactory()->setMetadataFor($interface, $cm); + } + } + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata + * @param array $mapping + * + * @return void + */ + private function remapAssociation($classMetadata, $mapping) + { + $newMapping = $this->resolveTargetEntities[$mapping['targetEntity']]; + $newMapping = array_replace_recursive($mapping, $newMapping); + $newMapping['fieldName'] = $mapping['fieldName']; + + unset($classMetadata->associationMappings[$mapping['fieldName']]); + + switch ($mapping['type']) { + case ClassMetadata::MANY_TO_MANY: + $classMetadata->mapManyToMany($newMapping); + break; + case ClassMetadata::MANY_TO_ONE: + $classMetadata->mapManyToOne($newMapping); + break; + case ClassMetadata::ONE_TO_MANY: + $classMetadata->mapOneToMany($newMapping); + break; + case ClassMetadata::ONE_TO_ONE: + $classMetadata->mapOneToOne($newMapping); + break; + } + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php new file mode 100644 index 0000000..b8e6273 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -0,0 +1,874 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector; +use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * The SchemaTool is a tool to create/drop/update database schemas based on + * ClassMetadata class descriptors. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Benjamin Eberlei + * @author Stefano Rodriguez + */ +class SchemaTool +{ + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * The quote strategy. + * + * @var \Doctrine\ORM\Mapping\QuoteStrategy + */ + private $quoteStrategy; + + /** + * Initializes a new SchemaTool instance that uses the connection of the + * provided EntityManager. + * + * @param \Doctrine\ORM\EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->platform = $em->getConnection()->getDatabasePlatform(); + $this->quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + } + + /** + * Creates the database schema for the given array of ClassMetadata instances. + * + * @param array $classes + * + * @return void + * + * @throws ToolsException + */ + public function createSchema(array $classes) + { + $createSchemaSql = $this->getCreateSchemaSql($classes); + $conn = $this->em->getConnection(); + + foreach ($createSchemaSql as $sql) { + try { + $conn->executeQuery($sql); + } catch (\Exception $e) { + throw ToolsException::schemaToolFailure($sql, $e); + } + } + } + + /** + * Gets the list of DDL statements that are required to create the database schema for + * the given list of ClassMetadata instances. + * + * @param array $classes + * + * @return array The SQL statements needed to create the schema for the classes. + */ + public function getCreateSchemaSql(array $classes) + { + $schema = $this->getSchemaFromMetadata($classes); + return $schema->toSql($this->platform); + } + + /** + * Detects instances of ClassMetadata that don't need to be processed in the SchemaTool context. + * + * @param ClassMetadata $class + * @param array $processedClasses + * + * @return bool + */ + private function processingNotRequired($class, array $processedClasses) + { + return ( + isset($processedClasses[$class->name]) || + $class->isMappedSuperclass || + $class->isEmbeddedClass || + ($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName) + ); + } + + /** + * Creates a Schema instance from a given set of metadata classes. + * + * @param array $classes + * + * @return Schema + * + * @throws \Doctrine\ORM\ORMException + */ + public function getSchemaFromMetadata(array $classes) + { + // Reminder for processed classes, used for hierarchies + $processedClasses = array(); + $eventManager = $this->em->getEventManager(); + $schemaManager = $this->em->getConnection()->getSchemaManager(); + $metadataSchemaConfig = $schemaManager->createSchemaConfig(); + + $metadataSchemaConfig->setExplicitForeignKeyIndexes(false); + $schema = new Schema(array(), array(), $metadataSchemaConfig); + + $addedFks = array(); + $blacklistedFks = array(); + + foreach ($classes as $class) { + /** @var \Doctrine\ORM\Mapping\ClassMetadata $class */ + if ($this->processingNotRequired($class, $processedClasses)) { + continue; + } + + $table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform)); + + if ($class->isInheritanceTypeSingleTable()) { + $this->gatherColumns($class, $table); + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + + // Add the discriminator column + $this->addDiscriminatorColumnDefinition($class, $table); + + // Aggregate all the information from all classes in the hierarchy + foreach ($class->parentClasses as $parentClassName) { + // Parent class information is already contained in this class + $processedClasses[$parentClassName] = true; + } + + foreach ($class->subClasses as $subClassName) { + $subClass = $this->em->getClassMetadata($subClassName); + $this->gatherColumns($subClass, $table); + $this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks); + $processedClasses[$subClassName] = true; + } + } elseif ($class->isInheritanceTypeJoined()) { + // Add all non-inherited fields as columns + $pkColumns = array(); + foreach ($class->fieldMappings as $fieldName => $mapping) { + if ( ! isset($mapping['inherited'])) { + $columnName = $this->quoteStrategy->getColumnName( + $mapping['fieldName'], + $class, + $this->platform + ); + $this->gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($fieldName)) { + $pkColumns[] = $columnName; + } + } + } + + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + + // Add the discriminator column only to the root table + if ($class->name == $class->rootEntityName) { + $this->addDiscriminatorColumnDefinition($class, $table); + } else { + // Add an ID FK column to child tables + $inheritedKeyColumns = array(); + foreach ($class->identifier as $identifierField) { + $idMapping = $class->fieldMappings[$identifierField]; + if (isset($idMapping['inherited'])) { + $this->gatherColumn($class, $idMapping, $table); + $columnName = $this->quoteStrategy->getColumnName( + $identifierField, + $class, + $this->platform + ); + // TODO: This seems rather hackish, can we optimize it? + $table->getColumn($columnName)->setAutoincrement(false); + + $pkColumns[] = $columnName; + $inheritedKeyColumns[] = $columnName; + } + } + if (!empty($inheritedKeyColumns)) { + // Add a FK constraint on the ID column + $table->addForeignKeyConstraint( + $this->quoteStrategy->getTableName( + $this->em->getClassMetadata($class->rootEntityName), + $this->platform + ), + $inheritedKeyColumns, + $inheritedKeyColumns, + array('onDelete' => 'CASCADE') + ); + } + + } + + $table->setPrimaryKey($pkColumns); + + } elseif ($class->isInheritanceTypeTablePerClass()) { + throw ORMException::notSupported(); + } else { + $this->gatherColumns($class, $table); + $this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks); + } + + $pkColumns = array(); + foreach ($class->identifier as $identifierField) { + if (isset($class->fieldMappings[$identifierField])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($identifierField, $class, $this->platform); + } elseif (isset($class->associationMappings[$identifierField])) { + /* @var $assoc \Doctrine\ORM\Mapping\OneToOne */ + $assoc = $class->associationMappings[$identifierField]; + foreach ($assoc['joinColumns'] as $joinColumn) { + $pkColumns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + } + } + } + + if ( ! $table->hasIndex('primary')) { + $table->setPrimaryKey($pkColumns); + } + + if (isset($class->table['indexes'])) { + foreach ($class->table['indexes'] as $indexName => $indexData) { + if( ! isset($indexData['flags'])) { + $indexData['flags'] = array(); + } + + $table->addIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName, (array)$indexData['flags'], isset($indexData['options']) ? $indexData['options'] : array()); + } + } + + if (isset($class->table['uniqueConstraints'])) { + foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) { + $uniqIndex = new Index($indexName, $indexData['columns'], true, false, [], isset($indexData['options']) ? $indexData['options'] : []); + + foreach ($table->getIndexes() as $tableIndexName => $tableIndex) { + if ($tableIndex->isFullfilledBy($uniqIndex)) { + $table->dropIndex($tableIndexName); + break; + } + } + + $table->addUniqueIndex($indexData['columns'], is_numeric($indexName) ? null : $indexName, isset($indexData['options']) ? $indexData['options'] : array()); + } + } + + if (isset($class->table['options'])) { + foreach ($class->table['options'] as $key => $val) { + $table->addOption($key, $val); + } + } + + $processedClasses[$class->name] = true; + + if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) { + $seqDef = $class->sequenceGeneratorDefinition; + $quotedName = $this->quoteStrategy->getSequenceName($seqDef, $class, $this->platform); + if ( ! $schema->hasSequence($quotedName)) { + $schema->createSequence( + $quotedName, + $seqDef['allocationSize'], + $seqDef['initialValue'] + ); + } + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchemaTable)) { + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchemaTable, + new GenerateSchemaTableEventArgs($class, $schema, $table) + ); + } + } + + if ( ! $this->platform->supportsSchemas() && ! $this->platform->canEmulateSchemas() ) { + $schema->visit(new RemoveNamespacedAssets()); + } + + if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) { + $eventManager->dispatchEvent( + ToolEvents::postGenerateSchema, + new GenerateSchemaEventArgs($this->em, $schema) + ); + } + + return $schema; + } + + /** + * Gets a portable column definition as required by the DBAL for the discriminator + * column of a class. + * + * @param ClassMetadata $class + * @param Table $table + * + * @return array The portable column definition of the discriminator column as required by + * the DBAL. + */ + private function addDiscriminatorColumnDefinition($class, Table $table) + { + $discrColumn = $class->discriminatorColumn; + + if ( ! isset($discrColumn['type']) || + (strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null) + ) { + $discrColumn['type'] = 'string'; + $discrColumn['length'] = 255; + } + + $options = array( + 'length' => isset($discrColumn['length']) ? $discrColumn['length'] : null, + 'notnull' => true + ); + + if (isset($discrColumn['columnDefinition'])) { + $options['columnDefinition'] = $discrColumn['columnDefinition']; + } + + $table->addColumn($discrColumn['name'], $discrColumn['type'], $options); + } + + /** + * Gathers the column definitions as required by the DBAL of all field mappings + * found in the given class. + * + * @param ClassMetadata $class + * @param Table $table + * + * @return array The list of portable column definitions as required by the DBAL. + */ + private function gatherColumns($class, Table $table) + { + $pkColumns = array(); + + foreach ($class->fieldMappings as $mapping) { + if ($class->isInheritanceTypeSingleTable() && isset($mapping['inherited'])) { + continue; + } + + $this->gatherColumn($class, $mapping, $table); + + if ($class->isIdentifier($mapping['fieldName'])) { + $pkColumns[] = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); + } + } + + // For now, this is a hack required for single table inheritence, since this method is called + // twice by single table inheritence relations + if (!$table->hasIndex('primary')) { + //$table->setPrimaryKey($pkColumns); + } + } + + /** + * Creates a column definition as required by the DBAL from an ORM field mapping definition. + * + * @param ClassMetadata $class The class that owns the field mapping. + * @param array $mapping The field mapping. + * @param Table $table + * + * @return array The portable column definition as required by the DBAL. + */ + private function gatherColumn($class, array $mapping, Table $table) + { + $columnName = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform); + $columnType = $mapping['type']; + + $options = array(); + $options['length'] = isset($mapping['length']) ? $mapping['length'] : null; + $options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true; + if ($class->isInheritanceTypeSingleTable() && count($class->parentClasses) > 0) { + $options['notnull'] = false; + } + + $options['platformOptions'] = array(); + $options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false; + + if (strtolower($columnType) == 'string' && $options['length'] === null) { + $options['length'] = 255; + } + + if (isset($mapping['precision'])) { + $options['precision'] = $mapping['precision']; + } + + if (isset($mapping['scale'])) { + $options['scale'] = $mapping['scale']; + } + + if (isset($mapping['default'])) { + $options['default'] = $mapping['default']; + } + + if (isset($mapping['columnDefinition'])) { + $options['columnDefinition'] = $mapping['columnDefinition']; + } + + if (isset($mapping['options'])) { + $knownOptions = array('comment', 'unsigned', 'fixed', 'default'); + + foreach ($knownOptions as $knownOption) { + if (array_key_exists($knownOption, $mapping['options'])) { + $options[$knownOption] = $mapping['options'][$knownOption]; + + unset($mapping['options'][$knownOption]); + } + } + + $options['customSchemaOptions'] = $mapping['options']; + } + + if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) { + $options['autoincrement'] = true; + } + if ($class->isInheritanceTypeJoined() && $class->name != $class->rootEntityName) { + $options['autoincrement'] = false; + } + + if ($table->hasColumn($columnName)) { + // required in some inheritance scenarios + $table->changeColumn($columnName, $options); + } else { + $table->addColumn($columnName, $columnType, $options); + } + + $isUnique = isset($mapping['unique']) ? $mapping['unique'] : false; + if ($isUnique) { + $table->addUniqueIndex(array($columnName)); + } + } + + /** + * Gathers the SQL for properly setting up the relations of the given class. + * This includes the SQL for foreign key constraints and join tables. + * + * @param ClassMetadata $class + * @param Table $table + * @param Schema $schema + * @param array $addedFks + * @param array $blacklistedFks + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + */ + private function gatherRelationsSql($class, $table, $schema, &$addedFks, &$blacklistedFks) + { + foreach ($class->associationMappings as $mapping) { + if (isset($mapping['inherited'])) { + continue; + } + + $foreignClass = $this->em->getClassMetadata($mapping['targetEntity']); + + if ($mapping['type'] & ClassMetadata::TO_ONE && $mapping['isOwningSide']) { + $primaryKeyColumns = array(); // PK is unnecessary for this relation-type + + $this->gatherRelationJoinColumns( + $mapping['joinColumns'], + $table, + $foreignClass, + $mapping, + $primaryKeyColumns, + $addedFks, + $blacklistedFks + ); + } elseif ($mapping['type'] == ClassMetadata::ONE_TO_MANY && $mapping['isOwningSide']) { + //... create join table, one-many through join table supported later + throw ORMException::notSupported(); + } elseif ($mapping['type'] == ClassMetadata::MANY_TO_MANY && $mapping['isOwningSide']) { + // create join table + $joinTable = $mapping['joinTable']; + + $theJoinTable = $schema->createTable( + $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform) + ); + + $primaryKeyColumns = array(); + + // Build first FK constraint (relation table => source table) + $this->gatherRelationJoinColumns( + $joinTable['joinColumns'], + $theJoinTable, + $class, + $mapping, + $primaryKeyColumns, + $addedFks, + $blacklistedFks + ); + + // Build second FK constraint (relation table => target table) + $this->gatherRelationJoinColumns( + $joinTable['inverseJoinColumns'], + $theJoinTable, + $foreignClass, + $mapping, + $primaryKeyColumns, + $addedFks, + $blacklistedFks + ); + + $theJoinTable->setPrimaryKey($primaryKeyColumns); + } + } + } + + /** + * Gets the class metadata that is responsible for the definition of the referenced column name. + * + * Previously this was a simple task, but with DDC-117 this problem is actually recursive. If its + * not a simple field, go through all identifier field names that are associations recursively and + * find that referenced column name. + * + * TODO: Is there any way to make this code more pleasing? + * + * @param ClassMetadata $class + * @param string $referencedColumnName + * + * @return array (ClassMetadata, referencedFieldName) + */ + private function getDefiningClass($class, $referencedColumnName) + { + $referencedFieldName = $class->getFieldName($referencedColumnName); + + if ($class->hasField($referencedFieldName)) { + return array($class, $referencedFieldName); + } + + if (in_array($referencedColumnName, $class->getIdentifierColumnNames())) { + // it seems to be an entity as foreign key + foreach ($class->getIdentifierFieldNames() as $fieldName) { + if ($class->hasAssociation($fieldName) + && $class->getSingleAssociationJoinColumnName($fieldName) == $referencedColumnName) { + return $this->getDefiningClass( + $this->em->getClassMetadata($class->associationMappings[$fieldName]['targetEntity']), + $class->getSingleAssociationReferencedJoinColumnName($fieldName) + ); + } + } + } + + return null; + } + + /** + * Gathers columns and fk constraints that are required for one part of relationship. + * + * @param array $joinColumns + * @param Table $theJoinTable + * @param ClassMetadata $class + * @param array $mapping + * @param array $primaryKeyColumns + * @param array $addedFks + * @param array $blacklistedFks + * + * @return void + * + * @throws \Doctrine\ORM\ORMException + */ + private function gatherRelationJoinColumns( + $joinColumns, + $theJoinTable, + $class, + $mapping, + &$primaryKeyColumns, + &$addedFks, + &$blacklistedFks + ) { + $localColumns = array(); + $foreignColumns = array(); + $fkOptions = array(); + $foreignTableName = $this->quoteStrategy->getTableName($class, $this->platform); + $uniqueConstraints = array(); + + foreach ($joinColumns as $joinColumn) { + + list($definingClass, $referencedFieldName) = $this->getDefiningClass( + $class, + $joinColumn['referencedColumnName'] + ); + + if ( ! $definingClass) { + throw new \Doctrine\ORM\ORMException( + "Column name `".$joinColumn['referencedColumnName']."` referenced for relation from ". + $mapping['sourceEntity'] . " towards ". $mapping['targetEntity'] . " does not exist." + ); + } + + $quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); + $quotedRefColumnName = $this->quoteStrategy->getReferencedJoinColumnName( + $joinColumn, + $class, + $this->platform + ); + + $primaryKeyColumns[] = $quotedColumnName; + $localColumns[] = $quotedColumnName; + $foreignColumns[] = $quotedRefColumnName; + + if ( ! $theJoinTable->hasColumn($quotedColumnName)) { + // Only add the column to the table if it does not exist already. + // It might exist already if the foreign key is mapped into a regular + // property as well. + + $fieldMapping = $definingClass->getFieldMapping($referencedFieldName); + + $columnDef = null; + if (isset($joinColumn['columnDefinition'])) { + $columnDef = $joinColumn['columnDefinition']; + } elseif (isset($fieldMapping['columnDefinition'])) { + $columnDef = $fieldMapping['columnDefinition']; + } + + $columnOptions = array('notnull' => false, 'columnDefinition' => $columnDef); + + if (isset($joinColumn['nullable'])) { + $columnOptions['notnull'] = !$joinColumn['nullable']; + } + + if (isset($fieldMapping['options'])) { + $columnOptions['options'] = $fieldMapping['options']; + } + + if ($fieldMapping['type'] == "string" && isset($fieldMapping['length'])) { + $columnOptions['length'] = $fieldMapping['length']; + } elseif ($fieldMapping['type'] == "decimal") { + $columnOptions['scale'] = $fieldMapping['scale']; + $columnOptions['precision'] = $fieldMapping['precision']; + } + + $theJoinTable->addColumn($quotedColumnName, $fieldMapping['type'], $columnOptions); + } + + if (isset($joinColumn['unique']) && $joinColumn['unique'] == true) { + $uniqueConstraints[] = array('columns' => array($quotedColumnName)); + } + + if (isset($joinColumn['onDelete'])) { + $fkOptions['onDelete'] = $joinColumn['onDelete']; + } + } + + // Prefer unique constraints over implicit simple indexes created for foreign keys. + // Also avoids index duplication. + foreach ($uniqueConstraints as $indexName => $unique) { + $theJoinTable->addUniqueIndex($unique['columns'], is_numeric($indexName) ? null : $indexName); + } + + $compositeName = $theJoinTable->getName().'.'.implode('', $localColumns); + if (isset($addedFks[$compositeName]) + && ($foreignTableName != $addedFks[$compositeName]['foreignTableName'] + || 0 < count(array_diff($foreignColumns, $addedFks[$compositeName]['foreignColumns']))) + ) { + foreach ($theJoinTable->getForeignKeys() as $fkName => $key) { + if (0 === count(array_diff($key->getLocalColumns(), $localColumns)) + && (($key->getForeignTableName() != $foreignTableName) + || 0 < count(array_diff($key->getForeignColumns(), $foreignColumns))) + ) { + $theJoinTable->removeForeignKey($fkName); + break; + } + } + $blacklistedFks[$compositeName] = true; + } elseif (!isset($blacklistedFks[$compositeName])) { + $addedFks[$compositeName] = array('foreignTableName' => $foreignTableName, 'foreignColumns' => $foreignColumns); + $theJoinTable->addUnnamedForeignKeyConstraint( + $foreignTableName, + $localColumns, + $foreignColumns, + $fkOptions + ); + } + } + + /** + * Drops the database schema for the given classes. + * + * In any way when an exception is thrown it is suppressed since drop was + * issued for all classes of the schema and some probably just don't exist. + * + * @param array $classes + * + * @return void + */ + public function dropSchema(array $classes) + { + $dropSchemaSql = $this->getDropSchemaSQL($classes); + $conn = $this->em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + try { + $conn->executeQuery($sql); + } catch (\Exception $e) { + + } + } + } + + /** + * Drops all elements in the database of the current connection. + * + * @return void + */ + public function dropDatabase() + { + $dropSchemaSql = $this->getDropDatabaseSQL(); + $conn = $this->em->getConnection(); + + foreach ($dropSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the SQL needed to drop the database schema for the connections database. + * + * @return array + */ + public function getDropDatabaseSQL() + { + $sm = $this->em->getConnection()->getSchemaManager(); + $schema = $sm->createSchema(); + + $visitor = new DropSchemaSqlCollector($this->platform); + $schema->visit($visitor); + + return $visitor->getQueries(); + } + + /** + * Gets SQL to drop the tables defined by the passed classes. + * + * @param array $classes + * + * @return array + */ + public function getDropSchemaSQL(array $classes) + { + $visitor = new DropSchemaSqlCollector($this->platform); + $schema = $this->getSchemaFromMetadata($classes); + + $sm = $this->em->getConnection()->getSchemaManager(); + $fullSchema = $sm->createSchema(); + + foreach ($fullSchema->getTables() as $table) { + if ( ! $schema->hasTable($table->getName())) { + foreach ($table->getForeignKeys() as $foreignKey) { + /* @var $foreignKey \Doctrine\DBAL\Schema\ForeignKeyConstraint */ + if ($schema->hasTable($foreignKey->getForeignTableName())) { + $visitor->acceptForeignKey($table, $foreignKey); + } + } + } else { + $visitor->acceptTable($table); + foreach ($table->getForeignKeys() as $foreignKey) { + $visitor->acceptForeignKey($table, $foreignKey); + } + } + } + + if ($this->platform->supportsSequences()) { + foreach ($schema->getSequences() as $sequence) { + $visitor->acceptSequence($sequence); + } + + foreach ($schema->getTables() as $table) { + /* @var $sequence Table */ + if ($table->hasPrimaryKey()) { + $columns = $table->getPrimaryKey()->getColumns(); + if (count($columns) == 1) { + $checkSequence = $table->getName() . "_" . $columns[0] . "_seq"; + if ($fullSchema->hasSequence($checkSequence)) { + $visitor->acceptSequence($fullSchema->getSequence($checkSequence)); + } + } + } + } + } + + return $visitor->getQueries(); + } + + /** + * Updates the database schema of the given classes by comparing the ClassMetadata + * instances to the current database schema that is inspected. If $saveMode is set + * to true the command is executed in the Database, else SQL is returned. + * + * @param array $classes + * @param boolean $saveMode + * + * @return void + */ + public function updateSchema(array $classes, $saveMode = false) + { + $updateSchemaSql = $this->getUpdateSchemaSql($classes, $saveMode); + $conn = $this->em->getConnection(); + + foreach ($updateSchemaSql as $sql) { + $conn->executeQuery($sql); + } + } + + /** + * Gets the sequence of SQL statements that need to be performed in order + * to bring the given class mappings in-synch with the relational schema. + * If $saveMode is set to true the command is executed in the Database, + * else SQL is returned. + * + * @param array $classes The classes to consider. + * @param boolean $saveMode True for writing to DB, false for SQL string. + * + * @return array The sequence of SQL statements. + */ + public function getUpdateSchemaSql(array $classes, $saveMode = false) + { + $sm = $this->em->getConnection()->getSchemaManager(); + + $fromSchema = $sm->createSchema(); + $toSchema = $this->getSchemaFromMetadata($classes); + + $comparator = new Comparator(); + $schemaDiff = $comparator->compare($fromSchema, $toSchema); + + if ($saveMode) { + return $schemaDiff->toSaveSql($this->platform); + } + + return $schemaDiff->toSql($this->platform); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php new file mode 100644 index 0000000..8f59ea7 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -0,0 +1,272 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\DBAL\Types\Type; + +/** + * Performs strict validation of the mapping schema + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class SchemaValidator +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Checks the internal consistency of all mapping files. + * + * There are several checks that can't be done at runtime or are too expensive, which can be verified + * with this command. For example: + * + * 1. Check if a relation with "mappedBy" is actually connected to that specified field. + * 2. Check if "mappedBy" and "inversedBy" are consistent to each other. + * 3. Check if "referencedColumnName" attributes are really pointing to primary key columns. + * + * @return array + */ + public function validateMapping() + { + $errors = array(); + $cmf = $this->em->getMetadataFactory(); + $classes = $cmf->getAllMetadata(); + + foreach ($classes as $class) { + if ($ce = $this->validateClass($class)) { + $errors[$class->name] = $ce; + } + } + + return $errors; + } + + /** + * Validates a single class of the current. + * + * @param ClassMetadataInfo $class + * + * @return array + */ + public function validateClass(ClassMetadataInfo $class) + { + $ce = array(); + $cmf = $this->em->getMetadataFactory(); + + foreach ($class->fieldMappings as $fieldName => $mapping) { + if (!Type::hasType($mapping['type'])) { + $ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existant type '" . $mapping['type'] . "'."; + } + } + + foreach ($class->associationMappings as $fieldName => $assoc) { + if (!class_exists($assoc['targetEntity']) || $cmf->isTransient($assoc['targetEntity'])) { + $ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.'; + return $ce; + } + + if ($assoc['mappedBy'] && $assoc['inversedBy']) { + $ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning."; + } + + $targetMetadata = $cmf->getMetadataFor($assoc['targetEntity']); + + if (isset($assoc['id']) && $targetMetadata->containsForeignIdentifier) { + $ce[] = "Cannot map association '" . $class->name. "#". $fieldName ." as identifier, because " . + "the target entity '". $targetMetadata->name . "' also maps an association as identifier."; + } + + if ($assoc['mappedBy']) { + if ($targetMetadata->hasField($assoc['mappedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which is not defined as association, but as field."; + } + if (!$targetMetadata->hasAssociation($assoc['mappedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " which does not exist."; + } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] == null) { + $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ". + "bi-directional relationship, but the specified mappedBy association on the target-entity ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". + "'inversedBy=\"" . $fieldName . "\"' attribute."; + } elseif ($targetMetadata->associationMappings[$assoc['mappedBy']]['inversedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " are ". + "inconsistent with each other."; + } + } + + if ($assoc['inversedBy']) { + if ($targetMetadata->hasField($assoc['inversedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which is not defined as association."; + } + + if (!$targetMetadata->hasAssociation($assoc['inversedBy'])) { + $ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ". + "field " . $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " which does not exist."; + } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] == null) { + $ce[] = "The field " . $class->name . "#" . $fieldName . " is on the owning side of a ". + "bi-directional relationship, but the specified mappedBy association on the target-entity ". + $assoc['targetEntity'] . "#" . $assoc['mappedBy'] . " does not contain the required ". + "'inversedBy' attribute."; + } elseif ($targetMetadata->associationMappings[$assoc['inversedBy']]['mappedBy'] != $fieldName) { + $ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " . + $assoc['targetEntity'] . "#" . $assoc['inversedBy'] . " are ". + "inconsistent with each other."; + } + + // Verify inverse side/owning side match each other + if (array_key_exists($assoc['inversedBy'], $targetMetadata->associationMappings)) { + $targetAssoc = $targetMetadata->associationMappings[$assoc['inversedBy']]; + if ($assoc['type'] == ClassMetadataInfo::ONE_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_ONE){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is one-to-one, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-one as well."; + } elseif ($assoc['type'] == ClassMetadataInfo::MANY_TO_ONE && $targetAssoc['type'] !== ClassMetadataInfo::ONE_TO_MANY){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-one, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be one-to-many."; + } elseif ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY && $targetAssoc['type'] !== ClassMetadataInfo::MANY_TO_MANY){ + $ce[] = "If association " . $class->name . "#" . $fieldName . " is many-to-many, then the inversed " . + "side " . $targetMetadata->name . "#" . $assoc['inversedBy'] . " has to be many-to-many as well."; + } + } + } + + if ($assoc['isOwningSide']) { + if ($assoc['type'] == ClassMetadataInfo::MANY_TO_MANY) { + $identifierColumns = $class->getIdentifierColumnNames(); + foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { + if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$class->name."'."; + break; + } + } + + $identifierColumns = $targetMetadata->getIdentifierColumnNames(); + foreach ($assoc['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$targetMetadata->name."'."; + break; + } + } + + if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) { + $ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " . + "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) . + "' are missing."; + } + + if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) { + $ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to contain to ALL identifier columns of the source entity '". $class->name . "', " . + "however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) . + "' are missing."; + } + + } elseif ($assoc['type'] & ClassMetadataInfo::TO_ONE) { + $identifierColumns = $targetMetadata->getIdentifierColumnNames(); + foreach ($assoc['joinColumns'] as $joinColumn) { + if (!in_array($joinColumn['referencedColumnName'], $identifierColumns)) { + $ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " . + "has to be a primary key column on the target entity class '".$targetMetadata->name."'."; + } + } + + if (count($identifierColumns) != count($assoc['joinColumns'])) { + $ids = array(); + + foreach ($assoc['joinColumns'] as $joinColumn) { + $ids[] = $joinColumn['name']; + } + + $ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " . + "have to match to ALL identifier columns of the target entity '". $targetMetadata->name . "', " . + "however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) . + "' are missing."; + } + } + } + + if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) { + foreach ($assoc['orderBy'] as $orderField => $orientation) { + if (!$targetMetadata->hasField($orderField) && !$targetMetadata->hasAssociation($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " . + $orderField . " that is not a field on the target entity " . $targetMetadata->name . "."; + continue; + } + if ($targetMetadata->isCollectionValuedAssociation($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a field " . + $orderField . " on " . $targetMetadata->name . " that is a collection-valued association."; + continue; + } + if ($targetMetadata->isAssociationInverseSide($orderField)) { + $ce[] = "The association " . $class->name."#".$fieldName." is ordered by a field " . + $orderField . " on " . $targetMetadata->name . " that is the inverse side of an association."; + continue; + } + } + } + } + + foreach ($class->subClasses as $subClass) { + if (!in_array($class->name, class_parents($subClass))) { + $ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child ". + "of '" . $class->name . "' but these entities are not related through inheritance."; + } + } + + return $ce; + } + + /** + * Checks if the Database Schema is in sync with the current metadata state. + * + * @return bool + */ + public function schemaInSyncWithMetadata() + { + $schemaTool = new SchemaTool($this->em); + + $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); + + return count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php new file mode 100644 index 0000000..79d4670 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Setup.php @@ -0,0 +1,162 @@ +. +*/ + +namespace Doctrine\ORM\Tools; + +use Doctrine\Common\ClassLoader; +use Doctrine\Common\Cache\Cache; +use Doctrine\Common\Cache\CacheProvider; +use Doctrine\Common\Cache\ArrayCache; +use Doctrine\ORM\Configuration; +use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\Mapping\Driver\YamlDriver; + +/** + * Convenience class for setting up Doctrine from different installations and configurations. + * + * @author Benjamin Eberlei + */ +class Setup +{ + /** + * Use this method to register all autoloads for a downloaded Doctrine library. + * Pick the directory the library was uncompressed into. + * + * @param string $directory + * + * @return void + */ + public static function registerAutoloadDirectory($directory) + { + if (!class_exists('Doctrine\Common\ClassLoader', false)) { + require_once $directory . "/Doctrine/Common/ClassLoader.php"; + } + + $loader = new ClassLoader("Doctrine", $directory); + $loader->register(); + + $loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine"); + $loader->register(); + } + + /** + * Creates a configuration with an annotation metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * @param bool $useSimpleAnnotationReader + * + * @return Configuration + */ + public static function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null, $useSimpleAnnotationReader = true) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths, $useSimpleAnnotationReader)); + + return $config; + } + + /** + * Creates a configuration with a xml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new XmlDriver($paths)); + + return $config; + } + + /** + * Creates a configuration with a yaml metadata driver. + * + * @param array $paths + * @param boolean $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $config = self::createConfiguration($isDevMode, $proxyDir, $cache); + $config->setMetadataDriverImpl(new YamlDriver($paths)); + + return $config; + } + + /** + * Creates a configuration without a metadata driver. + * + * @param bool $isDevMode + * @param string $proxyDir + * @param Cache $cache + * + * @return Configuration + */ + public static function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null) + { + $proxyDir = $proxyDir ?: sys_get_temp_dir(); + + if ($isDevMode === false && $cache === null) { + if (extension_loaded('apc')) { + $cache = new \Doctrine\Common\Cache\ApcCache(); + } elseif (extension_loaded('xcache')) { + $cache = new \Doctrine\Common\Cache\XcacheCache(); + } elseif (extension_loaded('memcache')) { + $memcache = new \Memcache(); + $memcache->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\MemcacheCache(); + $cache->setMemcache($memcache); + } elseif (extension_loaded('redis')) { + $redis = new \Redis(); + $redis->connect('127.0.0.1'); + $cache = new \Doctrine\Common\Cache\RedisCache(); + $cache->setRedis($redis); + } else { + $cache = new ArrayCache(); + } + } elseif ($cache === null) { + $cache = new ArrayCache(); + } + + if ($cache instanceof CacheProvider) { + $cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions + } + + $config = new Configuration(); + $config->setMetadataCacheImpl($cache); + $config->setQueryCacheImpl($cache); + $config->setResultCacheImpl($cache); + $config->setProxyDir($proxyDir); + $config->setProxyNamespace('DoctrineProxies'); + $config->setAutoGenerateProxyClasses($isDevMode); + + return $config; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php new file mode 100644 index 0000000..aebb5d8 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolEvents.php @@ -0,0 +1,42 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +class ToolEvents +{ + /** + * The postGenerateSchemaTable event occurs in SchemaTool#getSchemaFromMetadata() + * whenever an entity class is transformed into its table representation. It receives + * the current non-complete Schema instance, the Entity Metadata Class instance and + * the Schema Table instance of this entity. + * + * @var string + */ + const postGenerateSchemaTable = 'postGenerateSchemaTable'; + + /** + * The postGenerateSchema event is triggered in SchemaTool#getSchemaFromMetadata() + * after all entity classes have been transformed into the related Schema structure. + * The EventArgs contain the EntityManager and the created Schema instance. + * + * @var string + */ + const postGenerateSchema = 'postGenerateSchema'; +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php new file mode 100644 index 0000000..0a46164 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/ToolsException.php @@ -0,0 +1,51 @@ +. + */ + +namespace Doctrine\ORM\Tools; + +use Doctrine\ORM\ORMException; + +/** + * Tools related Exceptions. + * + * @author Benjamin Eberlei + */ +class ToolsException extends ORMException +{ + /** + * @param string $sql + * @param \Exception $e + * + * @return ToolsException + */ + public static function schemaToolFailure($sql, \Exception $e) + { + return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e); + } + + /** + * @param string $type + * + * @return ToolsException + */ + public static function couldNotMapDoctrine1Type($type) + { + return new self("Could not map doctrine 1 type '$type'!"); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php new file mode 100644 index 0000000..c3417b6 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/TransactionRequiredException.php @@ -0,0 +1,40 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Is thrown when a transaction is required for the current operation, but there is none open. + * + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link www.doctrine-project.com + * @since 1.0 + * @author Benjamin Eberlei + * @author Roman Borschel + */ +class TransactionRequiredException extends ORMException +{ + /** + * @return TransactionRequiredException + */ + static public function transactionRequired() + { + return new self('An open transaction is required for this operation.'); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php new file mode 100644 index 0000000..3cd561f --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnexpectedResultException.php @@ -0,0 +1,30 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Exception for a unexpected query result. + * + * @author Fabio B. Silva + * @since 2.3 + */ +class UnexpectedResultException extends ORMException +{ +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php new file mode 100644 index 0000000..d27748a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php @@ -0,0 +1,3474 @@ +. + */ + +namespace Doctrine\ORM; + +use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService; +use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Internal\HydrationCompleteHandler; +use Doctrine\ORM\Mapping\Reflection\ReflectionPropertiesGetter; +use Exception; +use InvalidArgumentException; +use UnexpectedValueException; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\NotifyPropertyChanged; +use Doctrine\Common\PropertyChangedListener; +use Doctrine\Common\Persistence\ObjectManagerAware; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Proxy\Proxy; + +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; +use Doctrine\ORM\Event\PreFlushEventArgs; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Event\PostFlushEventArgs; +use Doctrine\ORM\Event\ListenersInvoker; + +use Doctrine\ORM\Cache\Persister\CachedPersister; +use Doctrine\ORM\Persisters\Entity\BasicEntityPersister; +use Doctrine\ORM\Persisters\Entity\SingleTablePersister; +use Doctrine\ORM\Persisters\Entity\JoinedSubclassPersister; +use Doctrine\ORM\Persisters\Collection\OneToManyPersister; +use Doctrine\ORM\Persisters\Collection\ManyToManyPersister; +use Doctrine\ORM\Utility\IdentifierFlattener; +use Doctrine\ORM\Cache\AssociationCacheEntry; + +/** + * The UnitOfWork is responsible for tracking changes to objects during an + * "object-level" transaction and for writing out changes to the database + * in the correct order. + * + * Internal note: This class contains highly performance-sensitive code. + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Rob Caiger + */ +class UnitOfWork implements PropertyChangedListener +{ + /** + * An entity is in MANAGED state when its persistence is managed by an EntityManager. + */ + const STATE_MANAGED = 1; + + /** + * An entity is new if it has just been instantiated (i.e. using the "new" operator) + * and is not (yet) managed by an EntityManager. + */ + const STATE_NEW = 2; + + /** + * A detached entity is an instance with persistent state and identity that is not + * (or no longer) associated with an EntityManager (and a UnitOfWork). + */ + const STATE_DETACHED = 3; + + /** + * A removed entity instance is an instance with a persistent identity, + * associated with an EntityManager, whose persistent state will be deleted + * on commit. + */ + const STATE_REMOVED = 4; + + /** + * Hint used to collect all primary keys of associated entities during hydration + * and execute it in a dedicated query afterwards + * @see https://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html?highlight=eager#temporarily-change-fetch-mode-in-dql + */ + const HINT_DEFEREAGERLOAD = 'deferEagerLoad'; + + /** + * The identity map that holds references to all managed entities that have + * an identity. The entities are grouped by their class name. + * Since all classes in a hierarchy must share the same identifier set, + * we always take the root class name of the hierarchy. + * + * @var array + */ + private $identityMap = array(); + + /** + * Map of all identifiers of managed entities. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $entityIdentifiers = array(); + + /** + * Map of the original entity data of managed entities. + * Keys are object ids (spl_object_hash). This is used for calculating changesets + * at commit time. + * + * Internal note: Note that PHPs "copy-on-write" behavior helps a lot with memory usage. + * A value will only really be copied if the value in the entity is modified + * by the user. + * + * @var array + */ + private $originalEntityData = array(); + + /** + * Map of entity changes. Keys are object ids (spl_object_hash). + * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end. + * + * @var array + */ + private $entityChangeSets = array(); + + /** + * The (cached) states of any known entities. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $entityStates = array(); + + /** + * Map of entities that are scheduled for dirty checking at commit time. + * This is only used for entities with a change tracking policy of DEFERRED_EXPLICIT. + * Keys are object ids (spl_object_hash). + * + * @var array + */ + private $scheduledForSynchronization = array(); + + /** + * A list of all pending entity insertions. + * + * @var array + */ + private $entityInsertions = array(); + + /** + * A list of all pending entity updates. + * + * @var array + */ + private $entityUpdates = array(); + + /** + * Any pending extra updates that have been scheduled by persisters. + * + * @var array + */ + private $extraUpdates = array(); + + /** + * A list of all pending entity deletions. + * + * @var array + */ + private $entityDeletions = array(); + + /** + * All pending collection deletions. + * + * @var array + */ + private $collectionDeletions = array(); + + /** + * All pending collection updates. + * + * @var array + */ + private $collectionUpdates = array(); + + /** + * List of collections visited during changeset calculation on a commit-phase of a UnitOfWork. + * At the end of the UnitOfWork all these collections will make new snapshots + * of their data. + * + * @var array + */ + private $visitedCollections = array(); + + /** + * The EntityManager that "owns" this UnitOfWork instance. + * + * @var EntityManagerInterface + */ + private $em; + + /** + * The calculator used to calculate the order in which changes to + * entities need to be written to the database. + * + * @var \Doctrine\ORM\Internal\CommitOrderCalculator + */ + private $commitOrderCalculator; + + /** + * The entity persister instances used to persist entity instances. + * + * @var array + */ + private $persisters = array(); + + /** + * The collection persister instances used to persist collections. + * + * @var array + */ + private $collectionPersisters = array(); + + /** + * The EventManager used for dispatching events. + * + * @var \Doctrine\Common\EventManager + */ + private $evm; + + /** + * The ListenersInvoker used for dispatching events. + * + * @var \Doctrine\ORM\Event\ListenersInvoker + */ + private $listenersInvoker; + + /** + * The IdentifierFlattener used for manipulating identifiers + * + * @var \Doctrine\ORM\Utility\IdentifierFlattener + */ + private $identifierFlattener; + + /** + * Orphaned entities that are scheduled for removal. + * + * @var array + */ + private $orphanRemovals = array(); + + /** + * Read-Only objects are never evaluated + * + * @var array + */ + private $readOnlyObjects = array(); + + /** + * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. + * + * @var array + */ + private $eagerLoadingEntities = array(); + + /** + * @var boolean + */ + protected $hasCache = false; + + /** + * Helper for handling completion of hydration + * + * @var HydrationCompleteHandler + */ + private $hydrationCompleteHandler; + + /** + * @var ReflectionPropertiesGetter + */ + private $reflectionPropertiesGetter; + + /** + * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + $this->evm = $em->getEventManager(); + $this->listenersInvoker = new ListenersInvoker($em); + $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); + $this->identifierFlattener = new IdentifierFlattener($this, $em->getMetadataFactory()); + $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); + $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService()); + } + + /** + * Commits the UnitOfWork, executing all operations that have been postponed + * up to this point. The state of all managed entities will be synchronized with + * the database. + * + * The operations are executed in the following order: + * + * 1) All entity insertions + * 2) All entity updates + * 3) All collection deletions + * 4) All collection updates + * 5) All entity deletions + * + * @param null|object|array $entity + * + * @return void + * + * @throws \Exception + */ + public function commit($entity = null) + { + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em)); + } + + // Compute changes done since last commit. + if ($entity === null) { + $this->computeChangeSets(); + } elseif (is_object($entity)) { + $this->computeSingleEntityChangeSet($entity); + } elseif (is_array($entity)) { + foreach ($entity as $object) { + $this->computeSingleEntityChangeSet($object); + } + } + + if ( ! ($this->entityInsertions || + $this->entityDeletions || + $this->entityUpdates || + $this->collectionUpdates || + $this->collectionDeletions || + $this->orphanRemovals)) { + $this->dispatchOnFlushEvent(); + $this->dispatchPostFlushEvent(); + + return; // Nothing to do. + } + + if ($this->orphanRemovals) { + foreach ($this->orphanRemovals as $orphan) { + $this->remove($orphan); + } + } + + $this->dispatchOnFlushEvent(); + + // Now we need a commit order to maintain referential integrity + $commitOrder = $this->getCommitOrder(); + + $conn = $this->em->getConnection(); + $conn->beginTransaction(); + + try { + if ($this->entityInsertions) { + foreach ($commitOrder as $class) { + $this->executeInserts($class); + } + } + + if ($this->entityUpdates) { + foreach ($commitOrder as $class) { + $this->executeUpdates($class); + } + } + + // Extra updates that were requested by persisters. + if ($this->extraUpdates) { + $this->executeExtraUpdates(); + } + + // Collection deletions (deletions of complete collections) + foreach ($this->collectionDeletions as $collectionToDelete) { + $this->getCollectionPersister($collectionToDelete->getMapping())->delete($collectionToDelete); + } + + // Collection updates (deleteRows, updateRows, insertRows) + foreach ($this->collectionUpdates as $collectionToUpdate) { + $this->getCollectionPersister($collectionToUpdate->getMapping())->update($collectionToUpdate); + } + + // Entity deletions come last and need to be in reverse commit order + if ($this->entityDeletions) { + for ($count = count($commitOrder), $i = $count - 1; $i >= 0 && $this->entityDeletions; --$i) { + $this->executeDeletions($commitOrder[$i]); + } + } + + $conn->commit(); + } catch (Exception $e) { + $this->em->close(); + $conn->rollback(); + + $this->afterTransactionRolledBack(); + + throw $e; + } + + $this->afterTransactionComplete(); + + // Take new snapshots from visited collections + foreach ($this->visitedCollections as $coll) { + $coll->takeSnapshot(); + } + + $this->dispatchPostFlushEvent(); + + // Clear up + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->extraUpdates = + $this->entityChangeSets = + $this->collectionUpdates = + $this->collectionDeletions = + $this->visitedCollections = + $this->scheduledForSynchronization = + $this->orphanRemovals = array(); + } + + /** + * Computes the changesets of all entities scheduled for insertion. + * + * @return void + */ + private function computeScheduleInsertsChangeSets() + { + foreach ($this->entityInsertions as $entity) { + $class = $this->em->getClassMetadata(get_class($entity)); + + $this->computeChangeSet($class, $entity); + } + } + + /** + * Only flushes the given entity according to a ruleset that keeps the UoW consistent. + * + * 1. All entities scheduled for insertion, (orphan) removals and changes in collections are processed as well! + * 2. Read Only entities are skipped. + * 3. Proxies are skipped. + * 4. Only if entity is properly managed. + * + * @param object $entity + * + * @return void + * + * @throws \InvalidArgumentException + */ + private function computeSingleEntityChangeSet($entity) + { + $state = $this->getEntityState($entity); + + if ($state !== self::STATE_MANAGED && $state !== self::STATE_REMOVED) { + throw new \InvalidArgumentException("Entity has to be managed or scheduled for removal for single computation " . self::objToStr($entity)); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($state === self::STATE_MANAGED && $class->isChangeTrackingDeferredImplicit()) { + $this->persist($entity); + } + + // Compute changes for INSERTed entities first. This must always happen even in this case. + $this->computeScheduleInsertsChangeSets(); + + if ($class->isReadOnly) { + return; + } + + // Ignore uninitialized proxy objects + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + return; + } + + // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + + /** + * Executes any extra updates that have been scheduled. + */ + private function executeExtraUpdates() + { + foreach ($this->extraUpdates as $oid => $update) { + list ($entity, $changeset) = $update; + + $this->entityChangeSets[$oid] = $changeset; + $this->getEntityPersister(get_class($entity))->update($entity); + } + + $this->extraUpdates = array(); + } + + /** + * Gets the changeset for an entity. + * + * @param object $entity + * + * @return array + */ + public function getEntityChangeSet($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityChangeSets[$oid])) { + return $this->entityChangeSets[$oid]; + } + + return array(); + } + + /** + * Computes the changes that happened to a single entity. + * + * Modifies/populates the following properties: + * + * {@link _originalEntityData} + * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) + * then it was not fetched from the database and therefore we have no original + * entity data yet. All of the current entity data is stored as the original entity data. + * + * {@link _entityChangeSets} + * The changes detected on all properties of the entity are stored there. + * A change is a tuple array where the first entry is the old value and the second + * entry is the new value of the property. Changesets are used by persisters + * to INSERT/UPDATE the persistent entity state. + * + * {@link _entityUpdates} + * If the entity is already fully MANAGED (has been fetched from the database before) + * and any changes to its properties are detected, then a reference to the entity is stored + * there to mark it for an update. + * + * {@link _collectionDeletions} + * If a PersistentCollection has been de-referenced in a fully MANAGED entity, + * then this collection is marked for deletion. + * + * @ignore + * + * @internal Don't call from the outside. + * + * @param ClassMetadata $class The class descriptor of the entity. + * @param object $entity The entity for which to compute the changes. + * + * @return void + */ + public function computeChangeSet(ClassMetadata $class, $entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->readOnlyObjects[$oid])) { + return; + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->em->getClassMetadata(get_class($entity)); + } + + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER; + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + $value = $refProp->getValue($entity); + + if ($class->isCollectionValuedAssociation($name) && $value !== null) { + if ($value instanceof PersistentCollection) { + if ($value->getOwner() === $entity) { + continue; + } + + $value = new ArrayCollection($value->getValues()); + } + + // If $value is not a Collection then use an ArrayCollection. + if ( ! $value instanceof Collection) { + $value = new ArrayCollection($value); + } + + $assoc = $class->associationMappings[$name]; + + // Inject PersistentCollection + $value = new PersistentCollection( + $this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value + ); + $value->setOwner($entity, $assoc); + $value->setDirty( ! $value->isEmpty()); + + $class->reflFields[$name]->setValue($entity, $value); + + $actualData[$name] = $value; + + continue; + } + + if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) && ($name !== $class->versionField)) { + $actualData[$name] = $value; + } + } + + if ( ! isset($this->originalEntityData[$oid])) { + // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). + // These result in an INSERT. + $this->originalEntityData[$oid] = $actualData; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + if ( ! isset($class->associationMappings[$propName])) { + $changeSet[$propName] = array(null, $actualValue); + + continue; + } + + $assoc = $class->associationMappings[$propName]; + + if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { + $changeSet[$propName] = array(null, $actualValue); + } + } + + $this->entityChangeSets[$oid] = $changeSet; + } else { + // Entity is "fully" MANAGED: it was already fully persisted before + // and we have a copy of the original data + $originalData = $this->originalEntityData[$oid]; + $isChangeTrackingNotify = $class->isChangeTrackingNotify(); + $changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) + ? $this->entityChangeSets[$oid] + : array(); + + foreach ($actualData as $propName => $actualValue) { + // skip field, its a partially omitted one! + if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) { + continue; + } + + $orgValue = $originalData[$propName]; + + // skip if value haven't changed + if ($orgValue === $actualValue) { + continue; + } + + // if regular field + if ( ! isset($class->associationMappings[$propName])) { + if ($isChangeTrackingNotify) { + continue; + } + + $changeSet[$propName] = array($orgValue, $actualValue); + + continue; + } + + $assoc = $class->associationMappings[$propName]; + + // Persistent collection was exchanged with the "originally" + // created one. This can only mean it was cloned and replaced + // on another entity. + if ($actualValue instanceof PersistentCollection) { + $owner = $actualValue->getOwner(); + if ($owner === null) { // cloned + $actualValue->setOwner($entity, $assoc); + } else if ($owner !== $entity) { // no clone, we have to fix + if (!$actualValue->isInitialized()) { + $actualValue->initialize(); // we have to do this otherwise the cols share state + } + $newValue = clone $actualValue; + $newValue->setOwner($entity, $assoc); + $class->reflFields[$propName]->setValue($entity, $newValue); + } + } + + if ($orgValue instanceof PersistentCollection) { + // A PersistentCollection was de-referenced, so delete it. + $coid = spl_object_hash($orgValue); + + if (isset($this->collectionDeletions[$coid])) { + continue; + } + + $this->collectionDeletions[$coid] = $orgValue; + $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. + + continue; + } + + if ($assoc['type'] & ClassMetadata::TO_ONE) { + if ($assoc['isOwningSide']) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + + if ($orgValue !== null && $assoc['orphanRemoval']) { + $this->scheduleOrphanRemoval($orgValue); + } + } + } + + if ($changeSet) { + $this->entityChangeSets[$oid] = $changeSet; + $this->originalEntityData[$oid] = $actualData; + $this->entityUpdates[$oid] = $entity; + } + } + + // Look for changes in associations of the entity + foreach ($class->associationMappings as $field => $assoc) { + + if (($val = $class->reflFields[$field]->getValue($entity)) === null) { + continue; + } + + $this->computeAssociationChanges($assoc, $val); + + if ( ! isset($this->entityChangeSets[$oid]) && + $assoc['isOwningSide'] && + $assoc['type'] == ClassMetadata::MANY_TO_MANY && + $val instanceof PersistentCollection && + $val->isDirty()) { + + $this->entityChangeSets[$oid] = array(); + $this->originalEntityData[$oid] = $actualData; + $this->entityUpdates[$oid] = $entity; + } + } + } + + /** + * Computes all the changes that have been done to entities and collections + * since the last commit and stores these changes in the _entityChangeSet map + * temporarily for access by the persisters, until the UoW commit is finished. + * + * @return void + */ + public function computeChangeSets() + { + // Compute changes for INSERTed entities first. This must always happen. + $this->computeScheduleInsertsChangeSets(); + + // Compute changes for other MANAGED entities. Change tracking policies take effect here. + foreach ($this->identityMap as $className => $entities) { + $class = $this->em->getClassMetadata($className); + + // Skip class if instances are read-only + if ($class->isReadOnly) { + continue; + } + + // If change tracking is explicit or happens through notification, then only compute + // changes on entities of that type that are explicitly marked for synchronization. + switch (true) { + case ($class->isChangeTrackingDeferredImplicit()): + $entitiesToProcess = $entities; + break; + + case (isset($this->scheduledForSynchronization[$className])): + $entitiesToProcess = $this->scheduledForSynchronization[$className]; + break; + + default: + $entitiesToProcess = array(); + + } + + foreach ($entitiesToProcess as $entity) { + // Ignore uninitialized proxy objects + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + continue; + } + + // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION OR DELETION are processed here. + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityInsertions[$oid]) && ! isset($this->entityDeletions[$oid]) && isset($this->entityStates[$oid])) { + $this->computeChangeSet($class, $entity); + } + } + } + } + + /** + * Computes the changes of an association. + * + * @param array $assoc The association mapping. + * @param mixed $value The value of the association. + * + * @throws ORMInvalidArgumentException + * @throws ORMException + * + * @return void + */ + private function computeAssociationChanges($assoc, $value) + { + if ($value instanceof Proxy && ! $value->__isInitialized__) { + return; + } + + if ($value instanceof PersistentCollection && $value->isDirty()) { + $coid = spl_object_hash($value); + + if ($assoc['isOwningSide']) { + $this->collectionUpdates[$coid] = $value; + } + + $this->visitedCollections[$coid] = $value; + } + + // Look through the entities, and in any of their associations, + // for transient (new) entities, recursively. ("Persistence by reachability") + // Unwrap. Uninitialized collections will simply be empty. + $unwrappedValue = ($assoc['type'] & ClassMetadata::TO_ONE) ? array($value) : $value->unwrap(); + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + foreach ($unwrappedValue as $key => $entry) { + if (! ($entry instanceof $targetClass->name)) { + throw ORMInvalidArgumentException::invalidAssociation($targetClass, $assoc, $entry); + } + + $state = $this->getEntityState($entry, self::STATE_NEW); + + if ( ! ($entry instanceof $assoc['targetEntity'])) { + throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']); + } + + switch ($state) { + case self::STATE_NEW: + if ( ! $assoc['isCascadePersist']) { + throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry); + } + + $this->persistNew($targetClass, $entry); + $this->computeChangeSet($targetClass, $entry); + break; + + case self::STATE_REMOVED: + // Consume the $value as array (it's either an array or an ArrayAccess) + // and remove the element from Collection. + if ($assoc['type'] & ClassMetadata::TO_MANY) { + unset($value[$key]); + } + break; + + case self::STATE_DETACHED: + // Can actually not happen right now as we assume STATE_NEW, + // so the exception will be raised from the DBAL layer (constraint violation). + throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry); + break; + + default: + // MANAGED associated entities are already taken into account + // during changeset calculation anyway, since they are in the identity map. + } + } + } + + /** + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * @param object $entity + * + * @return void + */ + private function persistNew($class, $entity) + { + $oid = spl_object_hash($entity); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::prePersist); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::prePersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + + $idGen = $class->idGenerator; + + if ( ! $idGen->isPostInsertGenerator()) { + $idValue = $idGen->generate($this->em, $entity); + + if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { + $idValue = array($class->identifier[0] => $idValue); + + $class->setIdentifierValues($entity, $idValue); + } + + $this->entityIdentifiers[$oid] = $idValue; + } + + $this->entityStates[$oid] = self::STATE_MANAGED; + + $this->scheduleForInsert($entity); + } + + /** + * INTERNAL: + * Computes the changeset of an individual entity, independently of the + * computeChangeSets() routine that is used at the beginning of a UnitOfWork#commit(). + * + * The passed entity must be a managed entity. If the entity already has a change set + * because this method is invoked during a commit cycle then the change sets are added. + * whereby changes detected in this method prevail. + * + * @ignore + * + * @param ClassMetadata $class The class descriptor of the entity. + * @param object $entity The entity for which to (re)calculate the change set. + * + * @return void + * + * @throws ORMInvalidArgumentException If the passed entity is not MANAGED. + */ + public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityStates[$oid]) || $this->entityStates[$oid] != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + // skip if change tracking is "NOTIFY" + if ($class->isChangeTrackingNotify()) { + return; + } + + if ( ! $class->isInheritanceTypeNone()) { + $class = $this->em->getClassMetadata(get_class($entity)); + } + + $actualData = array(); + + foreach ($class->reflFields as $name => $refProp) { + if (( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) + && ($name !== $class->versionField) + && ! $class->isCollectionValuedAssociation($name)) { + $actualData[$name] = $refProp->getValue($entity); + } + } + + if ( ! isset($this->originalEntityData[$oid])) { + throw new \RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.'); + } + + $originalData = $this->originalEntityData[$oid]; + $changeSet = array(); + + foreach ($actualData as $propName => $actualValue) { + $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + + if ($orgValue !== $actualValue) { + $changeSet[$propName] = array($orgValue, $actualValue); + } + } + + if ($changeSet) { + if (isset($this->entityChangeSets[$oid])) { + $this->entityChangeSets[$oid] = array_merge($this->entityChangeSets[$oid], $changeSet); + } else if ( ! isset($this->entityInsertions[$oid])) { + $this->entityChangeSets[$oid] = $changeSet; + $this->entityUpdates[$oid] = $entity; + } + $this->originalEntityData[$oid] = $actualData; + } + } + + /** + * Executes all entity insertions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeInserts($class) + { + $entities = array(); + $className = $class->name; + $persister = $this->getEntityPersister($className); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist); + + foreach ($this->entityInsertions as $oid => $entity) { + + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + $persister->addInsert($entity); + + unset($this->entityInsertions[$oid]); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $entities[] = $entity; + } + } + + $postInsertIds = $persister->executeInserts(); + + if ($postInsertIds) { + // Persister returned post-insert IDs + foreach ($postInsertIds as $postInsertId) { + $id = $postInsertId['generatedId']; + $entity = $postInsertId['entity']; + $oid = spl_object_hash($entity); + $idField = $class->identifier[0]; + + $class->reflFields[$idField]->setValue($entity, $id); + + $this->entityIdentifiers[$oid] = array($idField => $id); + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid][$idField] = $id; + + $this->addToIdentityMap($entity); + } + } + + foreach ($entities as $entity) { + $this->listenersInvoker->invoke($class, Events::postPersist, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + + /** + * Executes all entity updates for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeUpdates($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $preUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preUpdate); + $postUpdateInvoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postUpdate); + + foreach ($this->entityUpdates as $oid => $entity) { + + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + if ($preUpdateInvoke != ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preUpdate, $entity, new PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]), $preUpdateInvoke); + $this->recomputeSingleEntityChangeSet($class, $entity); + } + + if ( ! empty($this->entityChangeSets[$oid])) { + $persister->update($entity); + } + + unset($this->entityUpdates[$oid]); + + if ($postUpdateInvoke != ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postUpdate, $entity, new LifecycleEventArgs($entity, $this->em), $postUpdateInvoke); + } + } + } + + /** + * Executes all entity deletions for entities of the specified type. + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * + * @return void + */ + private function executeDeletions($class) + { + $className = $class->name; + $persister = $this->getEntityPersister($className); + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postRemove); + + foreach ($this->entityDeletions as $oid => $entity) { + if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { + continue; + } + + $persister->delete($entity); + + unset( + $this->entityDeletions[$oid], + $this->entityIdentifiers[$oid], + $this->originalEntityData[$oid], + $this->entityStates[$oid] + ); + + // Entity with this $oid after deletion treated as NEW, even if the $oid + // is obtained by a new entity because the old one went out of scope. + //$this->entityStates[$oid] = self::STATE_NEW; + if ( ! $class->isIdentifierNatural()) { + $class->reflFields[$class->identifier[0]]->setValue($entity, null); + } + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::postRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + } + } + + /** + * Gets the commit order. + * + * @param array|null $entityChangeSet + * + * @return array + */ + private function getCommitOrder(array $entityChangeSet = null) + { + if ($entityChangeSet === null) { + $entityChangeSet = array_merge($this->entityInsertions, $this->entityUpdates, $this->entityDeletions); + } + + $calc = $this->getCommitOrderCalculator(); + + // See if there are any new classes in the changeset, that are not in the + // commit order graph yet (don't have a node). + // We have to inspect changeSet to be able to correctly build dependencies. + // It is not possible to use IdentityMap here because post inserted ids + // are not yet available. + $newNodes = array(); + + foreach ($entityChangeSet as $entity) { + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($calc->hasClass($class->name)) { + continue; + } + + $calc->addClass($class); + + $newNodes[] = $class; + } + + // Calculate dependencies for new nodes + while ($class = array_pop($newNodes)) { + foreach ($class->associationMappings as $assoc) { + if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ( ! $calc->hasClass($targetClass->name)) { + $calc->addClass($targetClass); + + $newNodes[] = $targetClass; + } + + $calc->addDependency($targetClass, $class); + + // If the target class has mapped subclasses, these share the same dependency. + if ( ! $targetClass->subClasses) { + continue; + } + + foreach ($targetClass->subClasses as $subClassName) { + $targetSubClass = $this->em->getClassMetadata($subClassName); + + if ( ! $calc->hasClass($subClassName)) { + $calc->addClass($targetSubClass); + + $newNodes[] = $targetSubClass; + } + + $calc->addDependency($targetSubClass, $class); + } + } + } + + return $calc->getCommitOrder(); + } + + /** + * Schedules an entity for insertion into the database. + * If the entity already has an identifier, it will be added to the identity map. + * + * @param object $entity The entity to schedule for insertion. + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws \InvalidArgumentException + */ + public function scheduleForInsert($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityUpdates[$oid])) { + throw new InvalidArgumentException("Dirty entity can not be scheduled for insertion."); + } + + if (isset($this->entityDeletions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertForRemovedEntity($entity); + } + if (isset($this->originalEntityData[$oid]) && ! isset($this->entityInsertions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertForManagedEntity($entity); + } + + if (isset($this->entityInsertions[$oid])) { + throw ORMInvalidArgumentException::scheduleInsertTwice($entity); + } + + $this->entityInsertions[$oid] = $entity; + + if (isset($this->entityIdentifiers[$oid])) { + $this->addToIdentityMap($entity); + } + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + } + + /** + * Checks whether an entity is scheduled for insertion. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForInsert($entity) + { + return isset($this->entityInsertions[spl_object_hash($entity)]); + } + + /** + * Schedules an entity for being updated. + * + * @param object $entity The entity to schedule for being updated. + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function scheduleForUpdate($entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityIdentifiers[$oid])) { + throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "scheduling for update"); + } + + if (isset($this->entityDeletions[$oid])) { + throw ORMInvalidArgumentException::entityIsRemoved($entity, "schedule for update"); + } + + if ( ! isset($this->entityUpdates[$oid]) && ! isset($this->entityInsertions[$oid])) { + $this->entityUpdates[$oid] = $entity; + } + } + + /** + * INTERNAL: + * Schedules an extra update that will be executed immediately after the + * regular entity updates within the currently running commit cycle. + * + * Extra updates for entities are stored as (entity, changeset) tuples. + * + * @ignore + * + * @param object $entity The entity for which to schedule an extra update. + * @param array $changeset The changeset of the entity (what to update). + * + * @return void + */ + public function scheduleExtraUpdate($entity, array $changeset) + { + $oid = spl_object_hash($entity); + $extraUpdate = array($entity, $changeset); + + if (isset($this->extraUpdates[$oid])) { + list($ignored, $changeset2) = $this->extraUpdates[$oid]; + + $extraUpdate = array($entity, $changeset + $changeset2); + } + + $this->extraUpdates[$oid] = $extraUpdate; + } + + /** + * Checks whether an entity is registered as dirty in the unit of work. + * Note: Is not very useful currently as dirty entities are only registered + * at commit time. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForUpdate($entity) + { + return isset($this->entityUpdates[spl_object_hash($entity)]); + } + + /** + * Checks whether an entity is registered to be checked in the unit of work. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForDirtyCheck($entity) + { + $rootEntityName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + return isset($this->scheduledForSynchronization[$rootEntityName][spl_object_hash($entity)]); + } + + /** + * INTERNAL: + * Schedules an entity for deletion. + * + * @param object $entity + * + * @return void + */ + public function scheduleForDelete($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityInsertions[$oid])) { + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } + + unset($this->entityInsertions[$oid], $this->entityStates[$oid]); + + return; // entity has not been persisted yet, so nothing more to do. + } + + if ( ! $this->isInIdentityMap($entity)) { + return; + } + + $this->removeFromIdentityMap($entity); + + if (isset($this->entityUpdates[$oid])) { + unset($this->entityUpdates[$oid]); + } + + if ( ! isset($this->entityDeletions[$oid])) { + $this->entityDeletions[$oid] = $entity; + $this->entityStates[$oid] = self::STATE_REMOVED; + } + } + + /** + * Checks whether an entity is registered as removed/deleted with the unit + * of work. + * + * @param object $entity + * + * @return boolean + */ + public function isScheduledForDelete($entity) + { + return isset($this->entityDeletions[spl_object_hash($entity)]); + } + + /** + * Checks whether an entity is scheduled for insertion, update or deletion. + * + * @param object $entity + * + * @return boolean + */ + public function isEntityScheduled($entity) + { + $oid = spl_object_hash($entity); + + return isset($this->entityInsertions[$oid]) + || isset($this->entityUpdates[$oid]) + || isset($this->entityDeletions[$oid]); + } + + /** + * INTERNAL: + * Registers an entity in the identity map. + * Note that entities in a hierarchy are registered with the class name of + * the root entity. + * + * @ignore + * + * @param object $entity The entity to register. + * + * @return boolean TRUE if the registration was successful, FALSE if the identity of + * the entity in question is already managed. + * + * @throws ORMInvalidArgumentException + */ + public function addToIdentityMap($entity) + { + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[spl_object_hash($entity)]); + + if ($idHash === '') { + throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity); + } + + $className = $classMetadata->rootEntityName; + + if (isset($this->identityMap[$className][$idHash])) { + return false; + } + + $this->identityMap[$className][$idHash] = $entity; + + return true; + } + + /** + * Gets the state of an entity with regard to the current unit of work. + * + * @param object $entity + * @param int|null $assume The state to assume if the state is not yet known (not MANAGED or REMOVED). + * This parameter can be set to improve performance of entity state detection + * by potentially avoiding a database lookup if the distinction between NEW and DETACHED + * is either known or does not matter for the caller of the method. + * + * @return int The entity state. + */ + public function getEntityState($entity, $assume = null) + { + $oid = spl_object_hash($entity); + + if (isset($this->entityStates[$oid])) { + return $this->entityStates[$oid]; + } + + if ($assume !== null) { + return $assume; + } + + // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known. + // Note that you can not remember the NEW or DETACHED state in _entityStates since + // the UoW does not hold references to such objects and the object hash can be reused. + // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it. + $class = $this->em->getClassMetadata(get_class($entity)); + $id = $class->getIdentifierValues($entity); + + if ( ! $id) { + return self::STATE_NEW; + } + + if ($class->containsForeignIdentifier) { + $id = $this->identifierFlattener->flattenIdentifier($class, $id); + } + + switch (true) { + case ($class->isIdentifierNatural()): + // Check for a version field, if available, to avoid a db lookup. + if ($class->isVersioned) { + return ($class->getFieldValue($entity, $class->versionField)) + ? self::STATE_DETACHED + : self::STATE_NEW; + } + + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootEntityName)) { + return self::STATE_DETACHED; + } + + // db lookup + if ($this->getEntityPersister($class->name)->exists($entity)) { + return self::STATE_DETACHED; + } + + return self::STATE_NEW; + + case ( ! $class->idGenerator->isPostInsertGenerator()): + // if we have a pre insert generator we can't be sure that having an id + // really means that the entity exists. We have to verify this through + // the last resort: a db lookup + + // Last try before db lookup: check the identity map. + if ($this->tryGetById($id, $class->rootEntityName)) { + return self::STATE_DETACHED; + } + + // db lookup + if ($this->getEntityPersister($class->name)->exists($entity)) { + return self::STATE_DETACHED; + } + + return self::STATE_NEW; + + default: + return self::STATE_DETACHED; + } + } + + /** + * INTERNAL: + * Removes an entity from the identity map. This effectively detaches the + * entity from the persistence management of Doctrine. + * + * @ignore + * + * @param object $entity + * + * @return boolean + * + * @throws ORMInvalidArgumentException + */ + public function removeFromIdentityMap($entity) + { + $oid = spl_object_hash($entity); + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[$oid]); + + if ($idHash === '') { + throw ORMInvalidArgumentException::entityHasNoIdentity($entity, "remove from identity map"); + } + + $className = $classMetadata->rootEntityName; + + if (isset($this->identityMap[$className][$idHash])) { + unset($this->identityMap[$className][$idHash]); + unset($this->readOnlyObjects[$oid]); + + //$this->entityStates[$oid] = self::STATE_DETACHED; + + return true; + } + + return false; + } + + /** + * INTERNAL: + * Gets an entity in the identity map by its identifier hash. + * + * @ignore + * + * @param string $idHash + * @param string $rootClassName + * + * @return object + */ + public function getByIdHash($idHash, $rootClassName) + { + return $this->identityMap[$rootClassName][$idHash]; + } + + /** + * INTERNAL: + * Tries to get an entity by its identifier hash. If no entity is found for + * the given hash, FALSE is returned. + * + * @ignore + * + * @param mixed $idHash (must be possible to cast it to string) + * @param string $rootClassName + * + * @return object|bool The found entity or FALSE. + */ + public function tryGetByIdHash($idHash, $rootClassName) + { + $stringIdHash = (string) $idHash; + + if (isset($this->identityMap[$rootClassName][$stringIdHash])) { + return $this->identityMap[$rootClassName][$stringIdHash]; + } + + return false; + } + + /** + * Checks whether an entity is registered in the identity map of this UnitOfWork. + * + * @param object $entity + * + * @return boolean + */ + public function isInIdentityMap($entity) + { + $oid = spl_object_hash($entity); + + if ( ! isset($this->entityIdentifiers[$oid])) { + return false; + } + + $classMetadata = $this->em->getClassMetadata(get_class($entity)); + $idHash = implode(' ', $this->entityIdentifiers[$oid]); + + if ($idHash === '') { + return false; + } + + return isset($this->identityMap[$classMetadata->rootEntityName][$idHash]); + } + + /** + * INTERNAL: + * Checks whether an identifier hash exists in the identity map. + * + * @ignore + * + * @param string $idHash + * @param string $rootClassName + * + * @return boolean + */ + public function containsIdHash($idHash, $rootClassName) + { + return isset($this->identityMap[$rootClassName][$idHash]); + } + + /** + * Persists an entity as part of the current unit of work. + * + * @param object $entity The entity to persist. + * + * @return void + */ + public function persist($entity) + { + $visited = array(); + + $this->doPersist($entity, $visited); + } + + /** + * Persists an entity as part of the current unit of work. + * + * This method is internally called during persist() cascades as it tracks + * the already visited entities to prevent infinite recursions. + * + * @param object $entity The entity to persist. + * @param array $visited The already visited entities. + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws UnexpectedValueException + */ + private function doPersist($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // Mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + // We assume NEW, so DETACHED entities result in an exception on flush (constraint violation). + // If we would detect DETACHED here we would throw an exception anyway with the same + // consequences (not recoverable/programming error), so just assuming NEW here + // lets us avoid some database lookups for entities with natural identifiers. + $entityState = $this->getEntityState($entity, self::STATE_NEW); + + switch ($entityState) { + case self::STATE_MANAGED: + // Nothing to do, except if policy is "deferred explicit" + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + break; + + case self::STATE_NEW: + $this->persistNew($class, $entity); + break; + + case self::STATE_REMOVED: + // Entity becomes managed again + unset($this->entityDeletions[$oid]); + $this->addToIdentityMap($entity); + + $this->entityStates[$oid] = self::STATE_MANAGED; + break; + + case self::STATE_DETACHED: + // Can actually not happen right now since we assume STATE_NEW. + throw ORMInvalidArgumentException::detachedEntityCannot($entity, "persisted"); + + default: + throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity)); + } + + $this->cascadePersist($entity, $visited); + } + + /** + * Deletes an entity as part of the current unit of work. + * + * @param object $entity The entity to remove. + * + * @return void + */ + public function remove($entity) + { + $visited = array(); + + $this->doRemove($entity, $visited); + } + + /** + * Deletes an entity as part of the current unit of work. + * + * This method is internally called during delete() cascades as it tracks + * the already visited entities to prevent infinite recursions. + * + * @param object $entity The entity to delete. + * @param array $visited The map of the already visited entities. + * + * @return void + * + * @throws ORMInvalidArgumentException If the instance is a detached entity. + * @throws UnexpectedValueException + */ + private function doRemove($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + // Cascade first, because scheduleForDelete() removes the entity from the identity map, which + // can cause problems when a lazy proxy has to be initialized for the cascade operation. + $this->cascadeRemove($entity, $visited); + + $class = $this->em->getClassMetadata(get_class($entity)); + $entityState = $this->getEntityState($entity); + + switch ($entityState) { + case self::STATE_NEW: + case self::STATE_REMOVED: + // nothing to do + break; + + case self::STATE_MANAGED: + $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preRemove); + + if ($invoke !== ListenersInvoker::INVOKE_NONE) { + $this->listenersInvoker->invoke($class, Events::preRemove, $entity, new LifecycleEventArgs($entity, $this->em), $invoke); + } + + $this->scheduleForDelete($entity); + break; + + case self::STATE_DETACHED: + throw ORMInvalidArgumentException::detachedEntityCannot($entity, "removed"); + default: + throw new UnexpectedValueException("Unexpected entity state: $entityState." . self::objToStr($entity)); + } + + } + + /** + * Merges the state of the given detached entity into this UnitOfWork. + * + * @param object $entity + * + * @return object The managed copy of the entity. + * + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * + * @todo Require active transaction!? OptimisticLockException may result in undefined state!? + */ + public function merge($entity) + { + $visited = array(); + + return $this->doMerge($entity, $visited); + } + + /** + * Executes a merge operation on an entity. + * + * @param object $entity + * @param array $visited + * @param object|null $prevManagedCopy + * @param array|null $assoc + * + * @return object The managed copy of the entity. + * + * @throws OptimisticLockException If the entity uses optimistic locking through a version + * attribute and the version check against the managed copy fails. + * @throws ORMInvalidArgumentException If the entity instance is NEW. + * @throws EntityNotFoundException + */ + private function doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + $managedCopy = $visited[$oid]; + + if ($prevManagedCopy !== null) { + $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); + } + + return $managedCopy; + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + // First we assume DETACHED, although it can still be NEW but we can avoid + // an extra db-roundtrip this way. If it is not MANAGED but has an identity, + // we need to fetch it from the db anyway in order to merge. + // MANAGED entities are ignored by the merge operation. + $managedCopy = $entity; + + if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { + // Try to look the entity up in the identity map. + $id = $class->getIdentifierValues($entity); + + // If there is no ID, it is actually NEW. + if ( ! $id) { + $managedCopy = $this->newInstance($class); + + $this->persistNew($class, $managedCopy); + } else { + $flatId = ($class->containsForeignIdentifier) + ? $this->identifierFlattener->flattenIdentifier($class, $id) + : $id; + + $managedCopy = $this->tryGetById($flatId, $class->rootEntityName); + + if ($managedCopy) { + // We have the entity in-memory already, just make sure its not removed. + if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) { + throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, "merge"); + } + } else { + // We need to fetch the managed copy in order to merge. + $managedCopy = $this->em->find($class->name, $flatId); + } + + if ($managedCopy === null) { + // If the identifier is ASSIGNED, it is NEW, otherwise an error + // since the managed entity was not found. + if ( ! $class->isIdentifierNatural()) { + throw EntityNotFoundException::fromClassNameAndIdentifier( + $class->getName(), + $this->identifierFlattener->flattenIdentifier($class, $id) + ); + } + + $managedCopy = $this->newInstance($class); + $class->setIdentifierValues($managedCopy, $id); + + $this->persistNew($class, $managedCopy); + } + } + + if ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity)) { + $reflField = $class->reflFields[$class->versionField]; + $managedCopyVersion = $reflField->getValue($managedCopy); + $entityVersion = $reflField->getValue($entity); + + // Throw exception if versions don't match. + if ($managedCopyVersion != $entityVersion) { + throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); + } + } + + $visited[$oid] = $managedCopy; // mark visited + + if ($this->isLoaded($entity)) { + if ($managedCopy instanceof Proxy && ! $managedCopy->__isInitialized()) { + $managedCopy->__load(); + } + + $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); + } + + if ($class->isChangeTrackingDeferredExplicit()) { + $this->scheduleForDirtyCheck($entity); + } + } + + if ($prevManagedCopy !== null) { + $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); + } + + // Mark the managed copy visited as well + $visited[spl_object_hash($managedCopy)] = $managedCopy; + + $this->cascadeMerge($entity, $managedCopy, $visited); + + return $managedCopy; + } + + /** + * Tests if an entity is loaded - must either be a loaded proxy or not a proxy + * + * @param object $entity + * + * @return bool + */ + private function isLoaded($entity) + { + return !($entity instanceof Proxy) || $entity->__isInitialized(); + } + + /** + * Sets/adds associated managed copies into the previous entity's association field + * + * @param object $entity + * @param array $association + * @param object $previousManagedCopy + * @param object $managedCopy + * + * @return void + */ + private function updateAssociationWithMergedEntity($entity, array $association, $previousManagedCopy, $managedCopy) + { + $assocField = $association['fieldName']; + $prevClass = $this->em->getClassMetadata(get_class($previousManagedCopy)); + + if ($association['type'] & ClassMetadata::TO_ONE) { + $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy); + + return; + } + + $value = $prevClass->reflFields[$assocField]->getValue($previousManagedCopy); + $value[] = $managedCopy; + + if ($association['type'] == ClassMetadata::ONE_TO_MANY) { + $class = $this->em->getClassMetadata(get_class($entity)); + + $class->reflFields[$association['mappedBy']]->setValue($managedCopy, $previousManagedCopy); + } + } + + /** + * Detaches an entity from the persistence management. It's persistence will + * no longer be managed by Doctrine. + * + * @param object $entity The entity to detach. + * + * @return void + */ + public function detach($entity) + { + $visited = array(); + + $this->doDetach($entity, $visited); + } + + /** + * Executes a detach operation on the given entity. + * + * @param object $entity + * @param array $visited + * @param boolean $noCascade if true, don't cascade detach operation. + * + * @return void + */ + private function doDetach($entity, array &$visited, $noCascade = false) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + switch ($this->getEntityState($entity, self::STATE_DETACHED)) { + case self::STATE_MANAGED: + if ($this->isInIdentityMap($entity)) { + $this->removeFromIdentityMap($entity); + } + + unset( + $this->entityInsertions[$oid], + $this->entityUpdates[$oid], + $this->entityDeletions[$oid], + $this->entityIdentifiers[$oid], + $this->entityStates[$oid], + $this->originalEntityData[$oid] + ); + break; + case self::STATE_NEW: + case self::STATE_DETACHED: + return; + } + + if ( ! $noCascade) { + $this->cascadeDetach($entity, $visited); + } + } + + /** + * Refreshes the state of the given entity from the database, overwriting + * any local, unpersisted changes. + * + * @param object $entity The entity to refresh. + * + * @return void + * + * @throws InvalidArgumentException If the entity is not MANAGED. + */ + public function refresh($entity) + { + $visited = array(); + + $this->doRefresh($entity, $visited); + } + + /** + * Executes a refresh operation on an entity. + * + * @param object $entity The entity to refresh. + * @param array $visited The already visited entities during cascades. + * + * @return void + * + * @throws ORMInvalidArgumentException If the entity is not MANAGED. + */ + private function doRefresh($entity, array &$visited) + { + $oid = spl_object_hash($entity); + + if (isset($visited[$oid])) { + return; // Prevent infinite recursion + } + + $visited[$oid] = $entity; // mark visited + + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($this->getEntityState($entity) !== self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $this->getEntityPersister($class->name)->refresh( + array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), + $entity + ); + + $this->cascadeRefresh($entity, $visited); + } + + /** + * Cascades a refresh operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeRefresh($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRefresh']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doRefresh($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doRefresh($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades a detach operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeDetach($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeDetach']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + foreach ($relatedEntities as $relatedEntity) { + $this->doDetach($relatedEntity, $visited); + } + break; + + case ($relatedEntities !== null): + $this->doDetach($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades a merge operation to associated entities. + * + * @param object $entity + * @param object $managedCopy + * @param array $visited + * + * @return void + */ + private function cascadeMerge($entity, $managedCopy, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeMerge']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + if ($relatedEntities instanceof Collection) { + if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { + continue; + } + + if ($relatedEntities instanceof PersistentCollection) { + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + } + + foreach ($relatedEntities as $relatedEntity) { + $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); + } + } else if ($relatedEntities !== null) { + $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc); + } + } + } + + /** + * Cascades the save operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadePersist($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadePersist']; } + ); + + foreach ($associationMappings as $assoc) { + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof PersistentCollection): + // Unwrap so that foreach() does not initialize + $relatedEntities = $relatedEntities->unwrap(); + // break; is commented intentionally! + + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + if (($assoc['type'] & ClassMetadata::TO_MANY) <= 0) { + throw ORMInvalidArgumentException::invalidAssociation( + $this->em->getClassMetadata($assoc['targetEntity']), + $assoc, + $relatedEntities + ); + } + + foreach ($relatedEntities as $relatedEntity) { + $this->doPersist($relatedEntity, $visited); + } + + break; + + case ($relatedEntities !== null): + if (! $relatedEntities instanceof $assoc['targetEntity']) { + throw ORMInvalidArgumentException::invalidAssociation( + $this->em->getClassMetadata($assoc['targetEntity']), + $assoc, + $relatedEntities + ); + } + + $this->doPersist($relatedEntities, $visited); + break; + + default: + // Do nothing + } + } + } + + /** + * Cascades the delete operation to associated entities. + * + * @param object $entity + * @param array $visited + * + * @return void + */ + private function cascadeRemove($entity, array &$visited) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRemove']; } + ); + + $entitiesToCascade = array(); + + foreach ($associationMappings as $assoc) { + if ($entity instanceof Proxy && !$entity->__isInitialized__) { + $entity->__load(); + } + + $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + + switch (true) { + case ($relatedEntities instanceof Collection): + case (is_array($relatedEntities)): + // If its a PersistentCollection initialization is intended! No unwrap! + foreach ($relatedEntities as $relatedEntity) { + $entitiesToCascade[] = $relatedEntity; + } + break; + + case ($relatedEntities !== null): + $entitiesToCascade[] = $relatedEntities; + break; + + default: + // Do nothing + } + } + + foreach ($entitiesToCascade as $relatedEntity) { + $this->doRemove($relatedEntity, $visited); + } + } + + /** + * Acquire a lock on the given entity. + * + * @param object $entity + * @param int $lockMode + * @param int $lockVersion + * + * @return void + * + * @throws ORMInvalidArgumentException + * @throws TransactionRequiredException + * @throws OptimisticLockException + */ + public function lock($entity, $lockMode, $lockVersion = null) + { + if ($entity === null) { + throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock()."); + } + + if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) { + throw ORMInvalidArgumentException::entityNotManaged($entity); + } + + $class = $this->em->getClassMetadata(get_class($entity)); + + switch (true) { + case LockMode::OPTIMISTIC === $lockMode: + if ( ! $class->isVersioned) { + throw OptimisticLockException::notVersioned($class->name); + } + + if ($lockVersion === null) { + return; + } + + if ($entity instanceof Proxy && !$entity->__isInitialized__) { + $entity->__load(); + } + + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); + + if ($entityVersion != $lockVersion) { + throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion); + } + + break; + + case LockMode::NONE === $lockMode: + case LockMode::PESSIMISTIC_READ === $lockMode: + case LockMode::PESSIMISTIC_WRITE === $lockMode: + if (!$this->em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + $oid = spl_object_hash($entity); + + $this->getEntityPersister($class->name)->lock( + array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), + $lockMode + ); + break; + + default: + // Do nothing + } + } + + /** + * Gets the CommitOrderCalculator used by the UnitOfWork to order commits. + * + * @return \Doctrine\ORM\Internal\CommitOrderCalculator + */ + public function getCommitOrderCalculator() + { + if ($this->commitOrderCalculator === null) { + $this->commitOrderCalculator = new Internal\CommitOrderCalculator; + } + + return $this->commitOrderCalculator; + } + + /** + * Clears the UnitOfWork. + * + * @param string|null $entityName if given, only entities of this type will get detached. + * + * @return void + */ + public function clear($entityName = null) + { + if ($entityName === null) { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForSynchronization = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->visitedCollections = + $this->orphanRemovals = array(); + + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } + } else { + $visited = array(); + + foreach ($this->identityMap as $className => $entities) { + if ($className !== $entityName) { + continue; + } + + foreach ($entities as $entity) { + $this->doDetach($entity, $visited, false); + } + } + } + + if ($this->evm->hasListeners(Events::onClear)) { + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName)); + } + } + + /** + * INTERNAL: + * Schedules an orphaned entity for removal. The remove() operation will be + * invoked on that entity at the beginning of the next commit of this + * UnitOfWork. + * + * @ignore + * + * @param object $entity + * + * @return void + */ + public function scheduleOrphanRemoval($entity) + { + $this->orphanRemovals[spl_object_hash($entity)] = $entity; + } + + /** + * INTERNAL: + * Schedules a complete collection for removal when this UnitOfWork commits. + * + * @param PersistentCollection $coll + * + * @return void + */ + public function scheduleCollectionDeletion(PersistentCollection $coll) + { + $coid = spl_object_hash($coll); + + // TODO: if $coll is already scheduled for recreation ... what to do? + // Just remove $coll from the scheduled recreations? + if (isset($this->collectionUpdates[$coid])) { + unset($this->collectionUpdates[$coid]); + } + + $this->collectionDeletions[$coid] = $coll; + } + + /** + * @param PersistentCollection $coll + * + * @return bool + */ + public function isCollectionScheduledForDeletion(PersistentCollection $coll) + { + return isset($this->collectionDeletions[spl_object_hash($coll)]); + } + + /** + * @param ClassMetadata $class + * + * @return \Doctrine\Common\Persistence\ObjectManagerAware|object + */ + private function newInstance($class) + { + $entity = $class->newInstance(); + + if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + return $entity; + } + + /** + * INTERNAL: + * Creates an entity. Used for reconstitution of persistent entities. + * + * Internal note: Highly performance-sensitive method. + * + * @ignore + * + * @param string $className The name of the entity class. + * @param array $data The data for the entity. + * @param array $hints Any hints to account for during reconstitution/lookup of the entity. + * + * @return object The managed entity instance. + * + * @todo Rename: getOrCreateEntity + */ + public function createEntity($className, array $data, &$hints = array()) + { + $class = $this->em->getClassMetadata($className); + //$isReadOnly = isset($hints[Query::HINT_READ_ONLY]); + + $id = $this->identifierFlattener->flattenIdentifier($class, $data); + $idHash = implode(' ', $id); + + if (isset($this->identityMap[$class->rootEntityName][$idHash])) { + $entity = $this->identityMap[$class->rootEntityName][$idHash]; + $oid = spl_object_hash($entity); + + if ( + isset($hints[Query::HINT_REFRESH]) + && isset($hints[Query::HINT_REFRESH_ENTITY]) + && ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity + && $unmanagedProxy instanceof Proxy + && $this->isIdentifierEquals($unmanagedProxy, $entity) + ) { + // DDC-1238 - we have a managed instance, but it isn't the provided one. + // Therefore we clear its identifier. Also, we must re-fetch metadata since the + // refreshed object may be anything + + foreach ($class->identifier as $fieldName) { + $class->reflFields[$fieldName]->setValue($unmanagedProxy, null); + } + + return $unmanagedProxy; + } + + if ($entity instanceof Proxy && ! $entity->__isInitialized()) { + $entity->__setInitialized(true); + + $overrideLocalValues = true; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + } else { + $overrideLocalValues = isset($hints[Query::HINT_REFRESH]); + + // If only a specific entity is set to refresh, check that it's the one + if (isset($hints[Query::HINT_REFRESH_ENTITY])) { + $overrideLocalValues = $hints[Query::HINT_REFRESH_ENTITY] === $entity; + } + } + + if ($overrideLocalValues) { + // inject ObjectManager upon refresh. + if ($entity instanceof ObjectManagerAware) { + $entity->injectObjectManager($this->em, $class); + } + + $this->originalEntityData[$oid] = $data; + } + } else { + $entity = $this->newInstance($class); + $oid = spl_object_hash($entity); + + $this->entityIdentifiers[$oid] = $id; + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid] = $data; + + $this->identityMap[$class->rootEntityName][$idHash] = $entity; + + if ($entity instanceof NotifyPropertyChanged) { + $entity->addPropertyChangedListener($this); + } + + $overrideLocalValues = true; + } + + if ( ! $overrideLocalValues) { + return $entity; + } + + foreach ($data as $field => $value) { + if (isset($class->fieldMappings[$field])) { + $class->reflFields[$field]->setValue($entity, $value); + } + } + + // Loading the entity right here, if its in the eager loading map get rid of it there. + unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]); + + if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) { + unset($this->eagerLoadingEntities[$class->rootEntityName]); + } + + // Properly initialize any unfetched associations, if partial objects are not allowed. + if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { + return $entity; + } + + foreach ($class->associationMappings as $field => $assoc) { + // Check if the association is not among the fetch-joined associations already. + if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) { + continue; + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + switch (true) { + case ($assoc['type'] & ClassMetadata::TO_ONE): + if ( ! $assoc['isOwningSide']) { + + // use the given entity association + if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) { + + $this->originalEntityData[$oid][$field] = $data[$field]; + + $class->reflFields[$field]->setValue($entity, $data[$field]); + $targetClass->reflFields[$assoc['mappedBy']]->setValue($data[$field], $entity); + + continue 2; + } + + // Inverse side of x-to-one can never be lazy + $class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity)); + + continue 2; + } + + // use the entity association + if (isset($data[$field]) && is_object($data[$field]) && isset($this->entityStates[spl_object_hash($data[$field])])) { + $class->reflFields[$field]->setValue($entity, $data[$field]); + $this->originalEntityData[$oid][$field] = $data[$field]; + + continue; + } + + $associatedId = array(); + + // TODO: Is this even computed right in all cases of composite keys? + foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) { + $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null; + + if ($joinColumnValue !== null) { + if ($targetClass->containsForeignIdentifier) { + $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue; + } else { + $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue; + } + } elseif ($targetClass->containsForeignIdentifier + && in_array($targetClass->getFieldForColumn($targetColumn), $targetClass->identifier, true) + ) { + // the missing key is part of target's entity primary key + $associatedId = array(); + break; + } + } + + if ( ! $associatedId) { + // Foreign key is NULL + $class->reflFields[$field]->setValue($entity, null); + $this->originalEntityData[$oid][$field] = null; + + continue; + } + + if ( ! isset($hints['fetchMode'][$class->name][$field])) { + $hints['fetchMode'][$class->name][$field] = $assoc['fetch']; + } + + // Foreign key is set + // Check identity map first + // FIXME: Can break easily with composite keys if join column values are in + // wrong order. The correct order is the one in ClassMetadata#identifier. + $relatedIdHash = implode(' ', $associatedId); + + switch (true) { + case (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])): + $newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash]; + + // If this is an uninitialized proxy, we are deferring eager loads, + // this association is marked as eager fetch, and its an uninitialized proxy (wtf!) + // then we can append this entity for eager loading! + if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER && + isset($hints[self::HINT_DEFEREAGERLOAD]) && + !$targetClass->isIdentifierComposite && + $newValue instanceof Proxy && + $newValue->__isInitialized__ === false) { + + $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); + } + + break; + + case ($targetClass->subClasses): + // If it might be a subtype, it can not be lazy. There isn't even + // a way to solve this with deferred eager loading, which means putting + // an entity with subclasses at a *-to-one location is really bad! (performance-wise) + $newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId); + break; + + default: + switch (true) { + // We are negating the condition here. Other cases will assume it is valid! + case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER): + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); + break; + + // Deferred eager load only works for single identifier classes + case (isset($hints[self::HINT_DEFEREAGERLOAD]) && ! $targetClass->isIdentifierComposite): + // TODO: Is there a faster approach? + $this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId); + + $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId); + break; + + default: + // TODO: This is very imperformant, ignore it? + $newValue = $this->em->find($assoc['targetEntity'], $associatedId); + break; + } + + // PERF: Inlined & optimized code from UnitOfWork#registerManaged() + $newValueOid = spl_object_hash($newValue); + $this->entityIdentifiers[$newValueOid] = $associatedId; + $this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue; + + if ( + $newValue instanceof NotifyPropertyChanged && + ( ! $newValue instanceof Proxy || $newValue->__isInitialized()) + ) { + $newValue->addPropertyChangedListener($this); + } + $this->entityStates[$newValueOid] = self::STATE_MANAGED; + // make sure that when an proxy is then finally loaded, $this->originalEntityData is set also! + break; + } + + $this->originalEntityData[$oid][$field] = $newValue; + $class->reflFields[$field]->setValue($entity, $newValue); + + if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) { + $inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']]; + $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity); + } + + break; + + default: + // Ignore if its a cached collection + if (isset($hints[Query::HINT_CACHE_ENABLED]) && $class->getFieldValue($entity, $field) instanceof PersistentCollection) { + break; + } + + // use the given collection + if (isset($data[$field]) && $data[$field] instanceof PersistentCollection) { + + $data[$field]->setOwner($entity, $assoc); + + $class->reflFields[$field]->setValue($entity, $data[$field]); + $this->originalEntityData[$oid][$field] = $data[$field]; + + break; + } + + // Inject collection + $pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection); + $pColl->setOwner($entity, $assoc); + $pColl->setInitialized(false); + + $reflField = $class->reflFields[$field]; + $reflField->setValue($entity, $pColl); + + if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) { + $this->loadCollection($pColl); + $pColl->takeSnapshot(); + } + + $this->originalEntityData[$oid][$field] = $pColl; + break; + } + } + + if ($overrideLocalValues) { + // defer invoking of postLoad event to hydration complete step + $this->hydrationCompleteHandler->deferPostLoadInvoking($class, $entity); + } + + return $entity; + } + + /** + * @return void + */ + public function triggerEagerLoads() + { + if ( ! $this->eagerLoadingEntities) { + return; + } + + // avoid infinite recursion + $eagerLoadingEntities = $this->eagerLoadingEntities; + $this->eagerLoadingEntities = array(); + + foreach ($eagerLoadingEntities as $entityName => $ids) { + if ( ! $ids) { + continue; + } + + $class = $this->em->getClassMetadata($entityName); + + $this->getEntityPersister($entityName)->loadAll( + array_combine($class->identifier, array(array_values($ids))) + ); + } + } + + /** + * Initializes (loads) an uninitialized persistent collection of an entity. + * + * @param \Doctrine\ORM\PersistentCollection $collection The collection to initialize. + * + * @return void + * + * @todo Maybe later move to EntityManager#initialize($proxyOrCollection). See DDC-733. + */ + public function loadCollection(PersistentCollection $collection) + { + $assoc = $collection->getMapping(); + $persister = $this->getEntityPersister($assoc['targetEntity']); + + switch ($assoc['type']) { + case ClassMetadata::ONE_TO_MANY: + $persister->loadOneToManyCollection($assoc, $collection->getOwner(), $collection); + break; + + case ClassMetadata::MANY_TO_MANY: + $persister->loadManyToManyCollection($assoc, $collection->getOwner(), $collection); + break; + } + + $collection->setInitialized(true); + } + + /** + * Gets the identity map of the UnitOfWork. + * + * @return array + */ + public function getIdentityMap() + { + return $this->identityMap; + } + + /** + * Gets the original data of an entity. The original data is the data that was + * present at the time the entity was reconstituted from the database. + * + * @param object $entity + * + * @return array + */ + public function getOriginalEntityData($entity) + { + $oid = spl_object_hash($entity); + + if (isset($this->originalEntityData[$oid])) { + return $this->originalEntityData[$oid]; + } + + return array(); + } + + /** + * @ignore + * + * @param object $entity + * @param array $data + * + * @return void + */ + public function setOriginalEntityData($entity, array $data) + { + $this->originalEntityData[spl_object_hash($entity)] = $data; + } + + /** + * INTERNAL: + * Sets a property value of the original data array of an entity. + * + * @ignore + * + * @param string $oid + * @param string $property + * @param mixed $value + * + * @return void + */ + public function setOriginalEntityProperty($oid, $property, $value) + { + $this->originalEntityData[$oid][$property] = $value; + } + + /** + * Gets the identifier of an entity. + * The returned value is always an array of identifier values. If the entity + * has a composite identifier then the identifier values are in the same + * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames(). + * + * @param object $entity + * + * @return array The identifier values. + */ + public function getEntityIdentifier($entity) + { + return $this->entityIdentifiers[spl_object_hash($entity)]; + } + + /** + * Processes an entity instance to extract their identifier values. + * + * @param object $entity The entity instance. + * + * @return mixed A scalar value. + * + * @throws \Doctrine\ORM\ORMInvalidArgumentException + */ + public function getSingleIdentifierValue($entity) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + if ($class->isIdentifierComposite) { + throw ORMInvalidArgumentException::invalidCompositeIdentifier(); + } + + $values = $this->isInIdentityMap($entity) + ? $this->getEntityIdentifier($entity) + : $class->getIdentifierValues($entity); + + return isset($values[$class->identifier[0]]) ? $values[$class->identifier[0]] : null; + } + + /** + * Tries to find an entity with the given identifier in the identity map of + * this UnitOfWork. + * + * @param mixed $id The entity identifier to look for. + * @param string $rootClassName The name of the root class of the mapped entity hierarchy. + * + * @return object|bool Returns the entity with the specified identifier if it exists in + * this UnitOfWork, FALSE otherwise. + */ + public function tryGetById($id, $rootClassName) + { + $idHash = implode(' ', (array) $id); + + if (isset($this->identityMap[$rootClassName][$idHash])) { + return $this->identityMap[$rootClassName][$idHash]; + } + + return false; + } + + /** + * Schedules an entity for dirty-checking at commit-time. + * + * @param object $entity The entity to schedule for dirty-checking. + * + * @return void + * + * @todo Rename: scheduleForSynchronization + */ + public function scheduleForDirtyCheck($entity) + { + $rootClassName = $this->em->getClassMetadata(get_class($entity))->rootEntityName; + + $this->scheduledForSynchronization[$rootClassName][spl_object_hash($entity)] = $entity; + } + + /** + * Checks whether the UnitOfWork has any pending insertions. + * + * @return boolean TRUE if this UnitOfWork has pending insertions, FALSE otherwise. + */ + public function hasPendingInsertions() + { + return ! empty($this->entityInsertions); + } + + /** + * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the + * number of entities in the identity map. + * + * @return integer + */ + public function size() + { + $countArray = array_map(function ($item) { return count($item); }, $this->identityMap); + + return array_sum($countArray); + } + + /** + * Gets the EntityPersister for an Entity. + * + * @param string $entityName The name of the Entity. + * + * @return \Doctrine\ORM\Persisters\Entity\EntityPersister + */ + public function getEntityPersister($entityName) + { + if (isset($this->persisters[$entityName])) { + return $this->persisters[$entityName]; + } + + $class = $this->em->getClassMetadata($entityName); + + switch (true) { + case ($class->isInheritanceTypeNone()): + $persister = new BasicEntityPersister($this->em, $class); + break; + + case ($class->isInheritanceTypeSingleTable()): + $persister = new SingleTablePersister($this->em, $class); + break; + + case ($class->isInheritanceTypeJoined()): + $persister = new JoinedSubclassPersister($this->em, $class); + break; + + default: + throw new \RuntimeException('No persister found for entity.'); + } + + if ($this->hasCache && $class->cache !== null) { + $persister = $this->em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheFactory() + ->buildCachedEntityPersister($this->em, $persister, $class); + } + + $this->persisters[$entityName] = $persister; + + return $this->persisters[$entityName]; + } + + /** + * Gets a collection persister for a collection-valued association. + * + * @param array $association + * + * @return \Doctrine\ORM\Persisters\Collection\CollectionPersister + */ + public function getCollectionPersister(array $association) + { + $role = isset($association['cache']) + ? $association['sourceEntity'] . '::' . $association['fieldName'] + : $association['type']; + + if (isset($this->collectionPersisters[$role])) { + return $this->collectionPersisters[$role]; + } + + $persister = ClassMetadata::ONE_TO_MANY === $association['type'] + ? new OneToManyPersister($this->em) + : new ManyToManyPersister($this->em); + + if ($this->hasCache && isset($association['cache'])) { + $persister = $this->em->getConfiguration() + ->getSecondLevelCacheConfiguration() + ->getCacheFactory() + ->buildCachedCollectionPersister($this->em, $persister, $association); + } + + $this->collectionPersisters[$role] = $persister; + + return $this->collectionPersisters[$role]; + } + + /** + * INTERNAL: + * Registers an entity as managed. + * + * @param object $entity The entity. + * @param array $id The identifier values. + * @param array $data The original entity data. + * + * @return void + */ + public function registerManaged($entity, array $id, array $data) + { + $oid = spl_object_hash($entity); + + $this->entityIdentifiers[$oid] = $id; + $this->entityStates[$oid] = self::STATE_MANAGED; + $this->originalEntityData[$oid] = $data; + + $this->addToIdentityMap($entity); + + if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) { + $entity->addPropertyChangedListener($this); + } + } + + /** + * INTERNAL: + * Clears the property changeset of the entity with the given OID. + * + * @param string $oid The entity's OID. + * + * @return void + */ + public function clearEntityChangeSet($oid) + { + $this->entityChangeSets[$oid] = array(); + } + + /* PropertyChangedListener implementation */ + + /** + * Notifies this UnitOfWork of a property change in an entity. + * + * @param object $entity The entity that owns the property. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property. + * @param mixed $newValue The new value of the property. + * + * @return void + */ + public function propertyChanged($entity, $propertyName, $oldValue, $newValue) + { + $oid = spl_object_hash($entity); + $class = $this->em->getClassMetadata(get_class($entity)); + + $isAssocField = isset($class->associationMappings[$propertyName]); + + if ( ! $isAssocField && ! isset($class->fieldMappings[$propertyName])) { + return; // ignore non-persistent fields + } + + // Update changeset and mark entity for synchronization + $this->entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue); + + if ( ! isset($this->scheduledForSynchronization[$class->rootEntityName][$oid])) { + $this->scheduleForDirtyCheck($entity); + } + } + + /** + * Gets the currently scheduled entity insertions in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityInsertions() + { + return $this->entityInsertions; + } + + /** + * Gets the currently scheduled entity updates in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityUpdates() + { + return $this->entityUpdates; + } + + /** + * Gets the currently scheduled entity deletions in this UnitOfWork. + * + * @return array + */ + public function getScheduledEntityDeletions() + { + return $this->entityDeletions; + } + + /** + * Gets the currently scheduled complete collection deletions + * + * @return array + */ + public function getScheduledCollectionDeletions() + { + return $this->collectionDeletions; + } + + /** + * Gets the currently scheduled collection inserts, updates and deletes. + * + * @return array + */ + public function getScheduledCollectionUpdates() + { + return $this->collectionUpdates; + } + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * @param object $obj + * + * @return void + */ + public function initializeObject($obj) + { + if ($obj instanceof Proxy) { + $obj->__load(); + + return; + } + + if ($obj instanceof PersistentCollection) { + $obj->initialize(); + } + } + + /** + * Helper method to show an object as string. + * + * @param object $obj + * + * @return string + */ + private static function objToStr($obj) + { + return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); + } + + /** + * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit(). + * + * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information + * on this object that might be necessary to perform a correct update. + * + * @param object $object + * + * @return void + * + * @throws ORMInvalidArgumentException + */ + public function markReadOnly($object) + { + if ( ! is_object($object) || ! $this->isInIdentityMap($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + $this->readOnlyObjects[spl_object_hash($object)] = true; + } + + /** + * Is this entity read only? + * + * @param object $object + * + * @return bool + * + * @throws ORMInvalidArgumentException + */ + public function isReadOnly($object) + { + if ( ! is_object($object)) { + throw ORMInvalidArgumentException::readOnlyRequiresManagedEntity($object); + } + + return isset($this->readOnlyObjects[spl_object_hash($object)]); + } + + /** + * Perform whatever processing is encapsulated here after completion of the transaction. + */ + private function afterTransactionComplete() + { + if ( ! $this->hasCache) { + return; + } + + foreach ($this->persisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionComplete(); + } + } + + foreach ($this->collectionPersisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionComplete(); + } + } + } + + /** + * Perform whatever processing is encapsulated here after completion of the rolled-back. + */ + private function afterTransactionRolledBack() + { + if ( ! $this->hasCache) { + return; + } + + foreach ($this->persisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionRolledBack(); + } + } + + foreach ($this->collectionPersisters as $persister) { + if ($persister instanceof CachedPersister) { + $persister->afterTransactionRolledBack(); + } + } + } + + private function dispatchOnFlushEvent() + { + if ($this->evm->hasListeners(Events::onFlush)) { + $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em)); + } + } + + private function dispatchPostFlushEvent() + { + if ($this->evm->hasListeners(Events::postFlush)) { + $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em)); + } + } + + /** + * Verifies if two given entities actually are the same based on identifier comparison + * + * @param object $entity1 + * @param object $entity2 + * + * @return bool + */ + private function isIdentifierEquals($entity1, $entity2) + { + if ($entity1 === $entity2) { + return true; + } + + $class = $this->em->getClassMetadata(get_class($entity1)); + + if ($class !== $this->em->getClassMetadata(get_class($entity2))) { + return false; + } + + $oid1 = spl_object_hash($entity1); + $oid2 = spl_object_hash($entity2); + + $id1 = isset($this->entityIdentifiers[$oid1]) + ? $this->entityIdentifiers[$oid1] + : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity1)); + $id2 = isset($this->entityIdentifiers[$oid2]) + ? $this->entityIdentifiers[$oid2] + : $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($entity2)); + + return $id1 === $id2 || implode(' ', $id1) === implode(' ', $id2); + } + + /** + * @param object $entity + * @param object $managedCopy + * + * @throws ORMException + * @throws OptimisticLockException + * @throws TransactionRequiredException + */ + private function mergeEntityStateIntoManagedCopy($entity, $managedCopy) + { + $class = $this->em->getClassMetadata(get_class($entity)); + + foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) { + $name = $prop->name; + + $prop->setAccessible(true); + + if ( ! isset($class->associationMappings[$name])) { + if ( ! $class->isIdentifier($name)) { + $prop->setValue($managedCopy, $prop->getValue($entity)); + } + } else { + $assoc2 = $class->associationMappings[$name]; + + if ($assoc2['type'] & ClassMetadata::TO_ONE) { + $other = $prop->getValue($entity); + if ($other === null) { + $prop->setValue($managedCopy, null); + } else { + if ($other instanceof Proxy && !$other->__isInitialized()) { + // do not merge fields marked lazy that have not been fetched. + continue; + } + + if ( ! $assoc2['isCascadeMerge']) { + if ($this->getEntityState($other) === self::STATE_DETACHED) { + $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); + $relatedId = $targetClass->getIdentifierValues($other); + + if ($targetClass->subClasses) { + $other = $this->em->find($targetClass->name, $relatedId); + } else { + $other = $this->em->getProxyFactory()->getProxy( + $assoc2['targetEntity'], + $relatedId + ); + $this->registerManaged($other, $relatedId, array()); + } + } + + $prop->setValue($managedCopy, $other); + } + } + } else { + $mergeCol = $prop->getValue($entity); + + if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) { + // do not merge fields marked lazy that have not been fetched. + // keep the lazy persistent collection of the managed copy. + continue; + } + + $managedCol = $prop->getValue($managedCopy); + + if ( ! $managedCol) { + $managedCol = new PersistentCollection( + $this->em, + $this->em->getClassMetadata($assoc2['targetEntity']), + new ArrayCollection + ); + $managedCol->setOwner($managedCopy, $assoc2); + $prop->setValue($managedCopy, $managedCol); + + $this->originalEntityData[spl_object_hash($entity)][$name] = $managedCol; + } + + if ($assoc2['isCascadeMerge']) { + $managedCol->initialize(); + + // clear and set dirty a managed collection if its not also the same collection to merge from. + if ( ! $managedCol->isEmpty() && $managedCol !== $mergeCol) { + $managedCol->unwrap()->clear(); + $managedCol->setDirty(true); + + if ($assoc2['isOwningSide'] + && $assoc2['type'] == ClassMetadata::MANY_TO_MANY + && $class->isChangeTrackingNotify() + ) { + $this->scheduleForDirtyCheck($managedCopy); + } + } + } + } + } + + if ($class->isChangeTrackingNotify()) { + // Just treat all properties as changed, there is no other choice. + $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); + } + } + } + + /** + * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle. + * Unit of work able to fire deferred events, related to loading events here. + * + * @internal should be called internally from object hydrators + */ + public function hydrationComplete() + { + $this->hydrationCompleteHandler->hydrationComplete(); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/IdentifierFlattener.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/IdentifierFlattener.php new file mode 100644 index 0000000..4a58dfe --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/IdentifierFlattener.php @@ -0,0 +1,103 @@ +. + */ + + +namespace Doctrine\ORM\Utility; + +use Doctrine\ORM\UnitOfWork; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; + +/** + * The IdentifierFlattener utility now houses some of the identifier manipulation logic from unit of work, so that it + * can be re-used elsewhere. + * + * @since 2.5 + * @author Rob Caiger + */ +final class IdentifierFlattener +{ + /** + * The UnitOfWork used to coordinate object-level transactions. + * + * @var UnitOfWork + */ + private $unitOfWork; + + /** + * The metadata factory, used to retrieve the ORM metadata of entity classes. + * + * @var ClassMetadataFactory + */ + private $metadataFactory; + + /** + * Initializes a new IdentifierFlattener instance, bound to the given EntityManager. + * + * @param UnitOfWork $unitOfWork + * @param ClassMetadataFactory $metadataFactory + */ + public function __construct(UnitOfWork $unitOfWork, ClassMetadataFactory $metadataFactory) + { + $this->unitOfWork = $unitOfWork; + $this->metadataFactory = $metadataFactory; + } + + /** + * convert foreign identifiers into scalar foreign key values to avoid object to string conversion failures. + * + * @param ClassMetadata $class + * @param array $id + * + * @return array + */ + public function flattenIdentifier(ClassMetadata $class, array $id) + { + $flatId = array(); + + foreach ($class->identifier as $field) { + if (isset($class->associationMappings[$field]) && isset($id[$field]) && is_object($id[$field])) { + /* @var $targetClassMetadata ClassMetadata */ + $targetClassMetadata = $this->metadataFactory->getMetadataFor( + $class->associationMappings[$field]['targetEntity'] + ); + + if ($this->unitOfWork->isInIdentityMap($id[$field])) { + $associatedId = $this->flattenIdentifier($targetClassMetadata, $this->unitOfWork->getEntityIdentifier($id[$field])); + } else { + $associatedId = $this->flattenIdentifier($targetClassMetadata, $targetClassMetadata->getIdentifierValues($id[$field])); + } + + $flatId[$field] = implode(' ', $associatedId); + } elseif (isset($class->associationMappings[$field])) { + $associatedId = array(); + + foreach ($class->associationMappings[$field]['joinColumns'] as $joinColumn) { + $associatedId[] = $id[$joinColumn['name']]; + } + + $flatId[$field] = implode(' ', $associatedId); + } else { + $flatId[$field] = $id[$field]; + } + } + + return $flatId; + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/PersisterHelper.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/PersisterHelper.php new file mode 100644 index 0000000..6fe7545 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Utility/PersisterHelper.php @@ -0,0 +1,136 @@ +. + */ + +namespace Doctrine\ORM\Utility; + +use Doctrine\Common\Util\ClassUtils; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Query\QueryException; + +/** + * The PersisterHelper contains logic to infer binding types which is used in + * several persisters. + * + * @link www.doctrine-project.org + * @since 2.5 + * @author Jasper N. Brouwer + */ +class PersisterHelper +{ + /** + * @param string $fieldName + * @param ClassMetadata $class + * @param EntityManagerInterface $em + * + * @return array + * + * @throws QueryException + */ + public static function getTypeOfField($fieldName, ClassMetadata $class, EntityManagerInterface $em) + { + if (isset($class->fieldMappings[$fieldName])) { + return array($class->fieldMappings[$fieldName]['type']); + } + + if ( ! isset($class->associationMappings[$fieldName])) { + return array(); + } + + $assoc = $class->associationMappings[$fieldName]; + + if (! $assoc['isOwningSide']) { + return self::getTypeOfField($assoc['mappedBy'], $em->getClassMetadata($assoc['targetEntity']), $em); + } + + if ($assoc['type'] & ClassMetadata::MANY_TO_MANY) { + $joinData = $assoc['joinTable']; + } else { + $joinData = $assoc; + } + + $types = array(); + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + + foreach ($joinData['joinColumns'] as $joinColumn) { + $types[] = self::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $em); + } + + return $types; + } + + /** + * @param string $columnName + * @param ClassMetadata $class + * @param EntityManagerInterface $em + * + * @return string + * + * @throws \RuntimeException + */ + public static function getTypeOfColumn($columnName, ClassMetadata $class, EntityManagerInterface $em) + { + if (isset($class->fieldNames[$columnName])) { + $fieldName = $class->fieldNames[$columnName]; + + if (isset($class->fieldMappings[$fieldName])) { + return $class->fieldMappings[$fieldName]['type']; + } + } + + // iterate over to-one association mappings + foreach ($class->associationMappings as $assoc) { + if ( ! isset($assoc['joinColumns'])) { + continue; + } + + foreach ($assoc['joinColumns'] as $joinColumn) { + if ($joinColumn['name'] == $columnName) { + $targetColumnName = $joinColumn['referencedColumnName']; + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + + return self::getTypeOfColumn($targetColumnName, $targetClass, $em); + } + } + } + + // iterate over to-many association mappings + foreach ($class->associationMappings as $assoc) { + if ( ! (isset($assoc['joinTable']) && isset($assoc['joinTable']['joinColumns']))) { + continue; + } + + foreach ($assoc['joinTable']['joinColumns'] as $joinColumn) { + if ($joinColumn['name'] == $columnName) { + $targetColumnName = $joinColumn['referencedColumnName']; + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + + return self::getTypeOfColumn($targetColumnName, $targetClass, $em); + } + } + } + + throw new \RuntimeException(sprintf( + 'Could not resolve type of column "%s" of class "%s"', + $columnName, + $class->getName() + )); + } +} diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php new file mode 100644 index 0000000..8589933 --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Version.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\ORM; + +/** + * Class to store and retrieve the version of Doctrine + * + * + * @link www.doctrine-project.org + * @since 2.0 + * @version $Revision$ + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Version +{ + /** + * Current Doctrine Version + */ + const VERSION = '2.5.3'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * + * @return int Returns -1 if older, 0 if it is the same, 1 if version + * passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/incenteev/composer-parameter-handler/.travis.yml b/vendor/incenteev/composer-parameter-handler/.travis.yml new file mode 100644 index 0000000..92d9e52 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/.travis.yml @@ -0,0 +1,27 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache/files + +matrix: + include: + - php: 5.3 + env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable' + - php: 5.4 + - php: 5.5 + - php: 5.6 + - php: 7.0 + env: DEPENDENCIES=dev + - php: hhvm + +before_script: + - if [ "$DEPENDENCIES" = "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi; + - composer update $COMPOSER_FLAGS + +script: phpunit --coverage-text --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/vendor/incenteev/composer-parameter-handler/CHANGELOG.md b/vendor/incenteev/composer-parameter-handler/CHANGELOG.md new file mode 100644 index 0000000..1f1ca25 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/CHANGELOG.md @@ -0,0 +1,27 @@ +## 2.1.2 (2015-11-10) + +* Mark symfony/yaml 3 as supported to be compatible with Symfony 3 +* Dropped support for symfony/yaml 2.2 and older (which are long unmaintained) +* Added testing on PHP 7 + +## 2.1.1 (2015-06-03) + +* Removed usage of a deprecated way to use the Yaml parser +* Added a more detailed exception message when the top-level key is missing + +## 2.1.0 (2013-12-07) + +* Move most of the logic to a ``Processor`` class which does not depend on the composer event and package. Ref #30 +* Add the support of existing empty file for Capifony compatibility +* Add the support of multiple managed files +* Preserve other top-level keys than the configured one in the file +* Add a rename map used to rename parameters when updating the parameters file +* Add the possibility to use another top-level key than ``parameters`` + +## 2.0.0 (2013-04-06) + +* BC BREAK the env map has been changed, inverting the keys and the values. Refs #14 + +## 1.0.0 (2013-04-06) + +Initial release of the library. diff --git a/vendor/incenteev/composer-parameter-handler/LICENSE b/vendor/incenteev/composer-parameter-handler/LICENSE new file mode 100644 index 0000000..72adcbf --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Christophe Coevoet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/incenteev/composer-parameter-handler/Processor.php b/vendor/incenteev/composer-parameter-handler/Processor.php new file mode 100644 index 0000000..6dc208f --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Processor.php @@ -0,0 +1,167 @@ +io = $io; + } + + public function processFile(array $config) + { + $config = $this->processConfig($config); + + $realFile = $config['file']; + $parameterKey = $config['parameter-key']; + + $exists = is_file($realFile); + + $yamlParser = new Parser(); + + $action = $exists ? 'Updating' : 'Creating'; + $this->io->write(sprintf('%s the "%s" file', $action, $realFile)); + + // Find the expected params + $expectedValues = $yamlParser->parse(file_get_contents($config['dist-file'])); + if (!isset($expectedValues[$parameterKey])) { + throw new \InvalidArgumentException(sprintf('The top-level key %s is missing.', $parameterKey)); + } + $expectedParams = (array) $expectedValues[$parameterKey]; + + // find the actual params + $actualValues = array_merge( + // Preserve other top-level keys than `$parameterKey` in the file + $expectedValues, + array($parameterKey => array()) + ); + if ($exists) { + $existingValues = $yamlParser->parse(file_get_contents($realFile)); + if ($existingValues === null) { + $existingValues = array(); + } + if (!is_array($existingValues)) { + throw new \InvalidArgumentException(sprintf('The existing "%s" file does not contain an array', $realFile)); + } + $actualValues = array_merge($actualValues, $existingValues); + } + + $actualValues[$parameterKey] = $this->processParams($config, $expectedParams, (array) $actualValues[$parameterKey]); + + if (!is_dir($dir = dirname($realFile))) { + mkdir($dir, 0755, true); + } + + file_put_contents($realFile, "# This file is auto-generated during the composer install\n" . Yaml::dump($actualValues, 99)); + } + + private function processConfig(array $config) + { + if (empty($config['file'])) { + throw new \InvalidArgumentException('The extra.incenteev-parameters.file setting is required to use this script handler.'); + } + + if (empty($config['dist-file'])) { + $config['dist-file'] = $config['file'].'.dist'; + } + + if (!is_file($config['dist-file'])) { + throw new \InvalidArgumentException(sprintf('The dist file "%s" does not exist. Check your dist-file config or create it.', $config['dist-file'])); + } + + if (empty($config['parameter-key'])) { + $config['parameter-key'] = 'parameters'; + } + + return $config; + } + + private function processParams(array $config, array $expectedParams, array $actualParams) + { + // Grab values for parameters that were renamed + $renameMap = empty($config['rename-map']) ? array() : (array) $config['rename-map']; + $actualParams = array_replace($actualParams, $this->processRenamedValues($renameMap, $actualParams)); + + $keepOutdatedParams = false; + if (isset($config['keep-outdated'])) { + $keepOutdatedParams = (boolean) $config['keep-outdated']; + } + + if (!$keepOutdatedParams) { + $actualParams = array_intersect_key($actualParams, $expectedParams); + } + + $envMap = empty($config['env-map']) ? array() : (array) $config['env-map']; + + // Add the params coming from the environment values + $actualParams = array_replace($actualParams, $this->getEnvValues($envMap)); + + return $this->getParams($expectedParams, $actualParams); + } + + private function getEnvValues(array $envMap) + { + $params = array(); + foreach ($envMap as $param => $env) { + $value = getenv($env); + if ($value) { + $params[$param] = Inline::parse($value); + } + } + + return $params; + } + + private function processRenamedValues(array $renameMap, array $actualParams) + { + foreach ($renameMap as $param => $oldParam) { + if (array_key_exists($param, $actualParams)) { + continue; + } + + if (!array_key_exists($oldParam, $actualParams)) { + continue; + } + + $actualParams[$param] = $actualParams[$oldParam]; + } + + return $actualParams; + } + + private function getParams(array $expectedParams, array $actualParams) + { + // Simply use the expectedParams value as default for the missing params. + if (!$this->io->isInteractive()) { + return array_replace($expectedParams, $actualParams); + } + + $isStarted = false; + + foreach ($expectedParams as $key => $message) { + if (array_key_exists($key, $actualParams)) { + continue; + } + + if (!$isStarted) { + $isStarted = true; + $this->io->write('Some parameters are missing. Please provide them.'); + } + + $default = Inline::dump($message); + $value = $this->io->ask(sprintf('%s (%s): ', $key, $default), $default); + + $actualParams[$key] = Inline::parse($value); + } + + return $actualParams; + } +} diff --git a/vendor/incenteev/composer-parameter-handler/README.md b/vendor/incenteev/composer-parameter-handler/README.md new file mode 100644 index 0000000..0f42600 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/README.md @@ -0,0 +1,174 @@ +# Managing your ignored parameters with Composer + +This tool allows you to manage your ignored parameters when running a composer +install or update. It works when storing the parameters in a Yaml file under +a single top-level key (named ``parameters`` by default). Other keys are +copied without change. + +[![Build Status](https://travis-ci.org/Incenteev/ParameterHandler.png)](https://travis-ci.org/Incenteev/ParameterHandler) +[![Code Coverage](https://scrutinizer-ci.com/g/Incenteev/ParameterHandler/badges/coverage.png?s=ea5de28d9764fdcb6a576a41e244c0ac537b3c81)](https://scrutinizer-ci.com/g/Incenteev/ParameterHandler/) +[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/Incenteev/ParameterHandler/badges/quality-score.png?s=6143d945bbdfac5c1114d4fe5d0f4ee737db18bf)](https://scrutinizer-ci.com/g/Incenteev/ParameterHandler/) +[![SensioLabsInsight](https://insight.sensiolabs.com/projects/3a432e49-6018-41a5-a37b-b7fb151706c1/mini.png)](https://insight.sensiolabs.com/projects/3a432e49-6018-41a5-a37b-b7fb151706c1) +[![Latest Stable Version](https://poser.pugx.org/incenteev/composer-parameter-handler/v/stable.png)](https://packagist.org/packages/incenteev/composer-parameter-handler) +[![Latest Unstable Version](https://poser.pugx.org/incenteev/composer-parameter-handler/v/unstable.png)](https://packagist.org/packages/incenteev/composer-parameter-handler) + +## Usage + +Add the following in your root composer.json file: + +```json +{ + "require": { + "incenteev/composer-parameter-handler": "~2.0" + }, + "scripts": { + "post-install-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters" + ], + "post-update-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters" + ] + }, + "extra": { + "incenteev-parameters": { + "file": "app/config/parameters.yml" + } + } +} +``` + +The ``app/config/parameters.yml`` will then be created or updated by the +composer script, to match the structure of the dist file ``app/config/parameters.yml.dist`` +by asking you the missing parameters. + +By default, the dist file is assumed to be in the same place than the parameters +file, suffixed by ``.dist``. This can be changed in the configuration: + +```json +{ + "extra": { + "incenteev-parameters": { + "file": "app/config/parameters.yml", + "dist-file": "some/other/folder/to/other/parameters/file/parameters.yml.dist" + } + } +} +``` + +The script handler will ask you interactively for parameters which are missing +in the parameters file, using the value of the dist file as default value. +All prompted values are parsed as inline Yaml, to allow you to define ``true``, +``false``, ``null`` or numbers easily. +If composer is run in a non-interactive mode, the values of the dist file +will be used for missing parameters. + +**Warning:** This parameters handler will overwrite any comments or spaces into +your parameters.yml file so handle with care. If you want to give format +and comments to your parameter's file you should do it on your dist version. + +### Keeping outdated parameters + +Warning: This script removes outdated params from ``parameters.yml`` which are not in ``parameters.yml.dist`` +If you need to keep outdated params you can use `keep-outdated` param in the configuration: + +```json +{ + "extra": { + "incenteev-parameters": { + "keep-outdated": true + } + } +} +``` + +### Using a different top-level key + +The script handler looks for a ``parameters`` key in your dist file. You can change this by using the +`parameter-key` param in the configuration: +```json +{ + "extra": { + "incenteev-parameters": { + "parameter-key": "config" + } + } +} +``` + +### Using environment variables to set the parameters + +For your prod environment, using an interactive prompt may not be possible +when deploying. In this case, you can rely on environment variables to provide +the parameters. This is achieved by providing a map between environment variables +and the parameters they should fill: + +```json +{ + "extra": { + "incenteev-parameters": { + "env-map": { + "my_first_param": "MY_FIRST_PARAM", + "my_second_param": "MY_SECOND_PARAM" + } + } + } +} +``` + +If an environment variable is set, its value will always replace the value +set in the existing parameters file. + +As environment variables can only be strings, they are also parsed as inline +Yaml values to allows specifying ``null``, ``false``, ``true`` or numbers +easily. + +### Renaming parameters + +If you are renaming a parameter, the new key will be set according to the usual +routine (prompt if possible, use environment variables, use default). +To have the parameters handler use the value of an (obsolete) parameter, specify +a rename-map: +```json +{ + "extra": { + "incenteev-parameters": { + "rename-map": { + "new_param_1": "old_param_1", + "new_param_2": "old_param_2" + } + } + } +} +``` + +This will create the new parameters new_param_1 and new_param_2 while using the +values from old_param_1 and old_param_2, respectively. It will not remove the +old parameters unless you've also removed them from the dist version. + +If the old parameter is no longer present (maybe because it has been renamed and +removed already), no parameters are overwritten. You don't need to remove obsolete +parameters from the rename map once they have been renamed. + +### Managing multiple ignored files + +The parameter handler can manage multiple ignored files. To use this feature, +the ``incenteev-parameters`` extra should contain a JSON array with multiple +configurations inside it instead of a configuration object: + +```json +{ + "extra": { + "incenteev-parameters": [ + { + "file": "app/config/parameters.yml", + "env-map": {} + }, + { + "file": "app/config/databases.yml", + "dist-file": "app/config/databases.dist.yml", + "parameter-key": "config" + } + ] + } +} +``` diff --git a/vendor/incenteev/composer-parameter-handler/ScriptHandler.php b/vendor/incenteev/composer-parameter-handler/ScriptHandler.php new file mode 100644 index 0000000..165064a --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/ScriptHandler.php @@ -0,0 +1,37 @@ +getComposer()->getPackage()->getExtra(); + + if (!isset($extras['incenteev-parameters'])) { + throw new \InvalidArgumentException('The parameter handler needs to be configured through the extra.incenteev-parameters setting.'); + } + + $configs = $extras['incenteev-parameters']; + + if (!is_array($configs)) { + throw new \InvalidArgumentException('The extra.incenteev-parameters setting must be an array or a configuration object.'); + } + + if (array_keys($configs) !== range(0, count($configs) - 1)) { + $configs = array($configs); + } + + $processor = new Processor($event->getIO()); + + foreach ($configs as $config) { + if (!is_array($config)) { + throw new \InvalidArgumentException('The extra.incenteev-parameters setting must be an array of configuration objects.'); + } + + $processor->processFile($config); + } + } +} diff --git a/vendor/incenteev/composer-parameter-handler/Tests/ProcessorTest.php b/vendor/incenteev/composer-parameter-handler/Tests/ProcessorTest.php new file mode 100644 index 0000000..babf44a --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/ProcessorTest.php @@ -0,0 +1,174 @@ +io = $this->prophesize('Composer\IO\IOInterface'); + $this->processor = new Processor($this->io->reveal()); + } + + protected function tearDown() + { + parent::tearDown(); + + foreach ($this->environmentBackup as $var => $value) { + if (false === $value) { + putenv($var); + } else { + putenv($var.'='.$value); + } + } + } + + /** + * @dataProvider provideInvalidConfiguration + */ + public function testInvalidConfiguration(array $config, $exceptionMessage) + { + chdir(__DIR__); + + $this->setExpectedException('InvalidArgumentException', $exceptionMessage); + + $this->processor->processFile($config); + } + + public function provideInvalidConfiguration() + { + return array( + 'no file' => array( + array(), + 'The extra.incenteev-parameters.file setting is required to use this script handler.', + ), + 'missing default dist file' => array( + array( + 'file' => 'fixtures/invalid/missing.yml', + ), + 'The dist file "fixtures/invalid/missing.yml.dist" does not exist. Check your dist-file config or create it.', + ), + 'missing custom dist file' => array( + array( + 'file' => 'fixtures/invalid/missing.yml', + 'dist-file' => 'fixtures/invalid/non-existent.dist.yml', + ), + 'The dist file "fixtures/invalid/non-existent.dist.yml" does not exist. Check your dist-file config or create it.', + ), + 'missing top level key in dist file' => array( + array( + 'file' => 'fixtures/invalid/missing_top_level.yml', + ), + 'The top-level key parameters is missing.', + ), + 'invalid values in the existing file' => array( + array( + 'file' => 'fixtures/invalid/invalid_existing_values.yml', + ), + 'The existing "fixtures/invalid/invalid_existing_values.yml" file does not contain an array', + ), + ); + } + + /** + * @dataProvider provideParameterHandlingTestCases + */ + public function testParameterHandling($testCaseName) + { + $dataDir = __DIR__.'/fixtures/testcases/'.$testCaseName; + + $testCase = array_replace_recursive( + array( + 'title' => 'unknown test', + 'config' => array( + 'file' => 'parameters.yml', + ), + 'dist-file' => 'parameters.yml.dist', + 'environment' => array(), + 'interactive' => false, + ), + (array) Yaml::parse(file_get_contents($dataDir.'/setup.yml')) + ); + + $workingDir = sys_get_temp_dir() . '/incenteev_parameter_handler'; + $exists = $this->initializeTestCase($testCase, $dataDir, $workingDir); + + $message = sprintf('%s the "%s" file', $exists ? 'Updating' : 'Creating', $testCase['config']['file']); + $this->io->write($message)->shouldBeCalled(); + + $this->setInteractionExpectations($testCase); + + $this->processor->processFile($testCase['config']); + + $this->assertFileEquals($dataDir.'/expected.yml', $workingDir.'/'.$testCase['config']['file'], $testCase['title']); + } + + private function initializeTestCase(array $testCase, $dataDir, $workingDir) + { + $fs = new Filesystem(); + + if (is_dir($workingDir)) { + $fs->remove($workingDir); + } + + $fs->copy($dataDir.'/dist.yml', $workingDir.'/'. $testCase['dist-file']); + + if ($exists = file_exists($dataDir.'/existing.yml')) { + $fs->copy($dataDir.'/existing.yml', $workingDir.'/'.$testCase['config']['file']); + } + + foreach ($testCase['environment'] as $var => $value) { + $this->environmentBackup[$var] = getenv($var); + putenv($var.'='.$value); + }; + + chdir($workingDir); + + return $exists; + } + + private function setInteractionExpectations(array $testCase) + { + $this->io->isInteractive()->willReturn($testCase['interactive']); + + if (!$testCase['interactive']) { + return; + } + + if (!empty($testCase['requested_params'])) { + $this->io->write('Some parameters are missing. Please provide them.')->shouldBeCalledTimes(1); + } + + foreach ($testCase['requested_params'] as $param => $settings) { + $this->io->ask(sprintf('%s (%s): ', $param, $settings['default']), $settings['default']) + ->willReturn($settings['input']) + ->shouldBeCalled(); + } + } + + public function provideParameterHandlingTestCases() + { + $tests = array(); + + foreach (glob(__DIR__.'/fixtures/testcases/*/') as $folder) { + $tests[] = array(basename($folder)); + } + + return $tests; + } +} diff --git a/vendor/incenteev/composer-parameter-handler/Tests/ScriptHandlerTest.php b/vendor/incenteev/composer-parameter-handler/Tests/ScriptHandlerTest.php new file mode 100644 index 0000000..b77e76a --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/ScriptHandlerTest.php @@ -0,0 +1,63 @@ +event = $this->prophesize('Composer\Script\Event'); + $this->io = $this->prophesize('Composer\IO\IOInterface'); + $this->package = $this->prophesize('Composer\Package\PackageInterface'); + $composer = $this->prophesize('Composer\Composer'); + + $composer->getPackage()->willReturn($this->package); + $this->event->getComposer()->willReturn($composer); + $this->event->getIO()->willReturn($this->io); + } + + /** + * @dataProvider provideInvalidConfiguration + */ + public function testInvalidConfiguration(array $extras, $exceptionMessage) + { + $this->package->getExtra()->willReturn($extras); + + chdir(__DIR__); + + $this->setExpectedException('InvalidArgumentException', $exceptionMessage); + + ScriptHandler::buildParameters($this->event->reveal()); + } + + public function provideInvalidConfiguration() + { + return array( + 'no extra' => array( + array(), + 'The parameter handler needs to be configured through the extra.incenteev-parameters setting.', + ), + 'invalid type' => array( + array('incenteev-parameters' => 'not an array'), + 'The extra.incenteev-parameters setting must be an array or a configuration object.', + ), + 'invalid type for multiple file' => array( + array('incenteev-parameters' => array('not an array')), + 'The extra.incenteev-parameters setting must be an array of configuration objects.', + ), + 'no file' => array( + array('incenteev-parameters' => array()), + 'The extra.incenteev-parameters.file setting is required to use this script handler.', + ), + ); + } +} diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/invalid_existing_values.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/invalid_existing_values.yml new file mode 100644 index 0000000..fbbc579 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/invalid_existing_values.yml @@ -0,0 +1 @@ +not an array diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/invalid_existing_values.yml.dist b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/invalid_existing_values.yml.dist new file mode 100644 index 0000000..d426a85 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/invalid_existing_values.yml.dist @@ -0,0 +1,2 @@ +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/missing_top_level.yml.dist b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/missing_top_level.yml.dist new file mode 100644 index 0000000..a0242f2 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/invalid/missing_top_level.yml.dist @@ -0,0 +1 @@ +another: The parameters key is missing diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/dist.yml new file mode 100644 index 0000000..df4994f --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/dist.yml @@ -0,0 +1,3 @@ +parameters: + foo: bar + boolean: false diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/existing.yml new file mode 100644 index 0000000..f4b75e8 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/existing.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/expected.yml new file mode 100644 index 0000000..f4b75e8 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/expected.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/setup.yml new file mode 100644 index 0000000..a06a62d --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_dist_file/setup.yml @@ -0,0 +1,6 @@ +title: Existing values are kept + +config: + dist-file: parameters.dist.yml + +dist-file: parameters.dist.yml diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/dist.yml new file mode 100644 index 0000000..3ff8093 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/dist.yml @@ -0,0 +1,7 @@ +config: + foo: bar + boolean: false + another: ~ + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/existing.yml new file mode 100644 index 0000000..dd272fe --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/existing.yml @@ -0,0 +1,8 @@ +# This file is auto-generated during the composer install +config: + foo: existing_foo + boolean: false + another: ~ + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/expected.yml new file mode 100644 index 0000000..7f985cb --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/expected.yml @@ -0,0 +1,8 @@ +# This file is auto-generated during the composer install +config: + foo: existing_foo + boolean: false + another: null + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/setup.yml new file mode 100644 index 0000000..6611ac7 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/custom_key/setup.yml @@ -0,0 +1,5 @@ +title: Using a custom parameter key + +config: + parameter-key: config + diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/dist.yml new file mode 100644 index 0000000..45f3fdd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/dist.yml @@ -0,0 +1,7 @@ +parameters: + foo: bar + boolean: false + another: ~ + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/existing.yml new file mode 100644 index 0000000..60e3bbe --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/existing.yml @@ -0,0 +1,8 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false + another: ~ + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/expected.yml new file mode 100644 index 0000000..31ff131 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/expected.yml @@ -0,0 +1,8 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false + another: null + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/setup.yml new file mode 100644 index 0000000..b72f1b5 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent/setup.yml @@ -0,0 +1 @@ +title: Existing values are kept diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/dist.yml new file mode 100644 index 0000000..d426a85 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/dist.yml @@ -0,0 +1,2 @@ +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/existing.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/expected.yml new file mode 100644 index 0000000..367f8eb --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/expected.yml @@ -0,0 +1,3 @@ +# This file is auto-generated during the composer install +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/setup.yml new file mode 100644 index 0000000..dedcd0f --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_empty/setup.yml @@ -0,0 +1 @@ +title: Existing empty files are valid (Capifony compatibility) diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/dist.yml new file mode 100644 index 0000000..d426a85 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/dist.yml @@ -0,0 +1,2 @@ +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/existing.yml new file mode 100644 index 0000000..678ab28 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/existing.yml @@ -0,0 +1,2 @@ +# This file is auto-generated during the composer install +foobar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/expected.yml new file mode 100644 index 0000000..76f04cf --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/expected.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: bar +foobar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/setup.yml new file mode 100644 index 0000000..3797a0b --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/existent_without_key/setup.yml @@ -0,0 +1 @@ +title: Existing files without the parameters key are valid diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/dist.yml new file mode 100644 index 0000000..fbc0b90 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/dist.yml @@ -0,0 +1,5 @@ +parameters: + foo: bar + boolean: false + +extra_key: a new extra key diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/existing.yml new file mode 100644 index 0000000..2e08a16 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/existing.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo +another_key: foo diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/expected.yml new file mode 100644 index 0000000..4ede434 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/expected.yml @@ -0,0 +1,6 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false +extra_key: 'a new extra key' +another_key: foo diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/setup.yml new file mode 100644 index 0000000..50666fd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/extra_keys/setup.yml @@ -0,0 +1 @@ +title: Extra top level keys are preserved diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/dist.yml new file mode 100644 index 0000000..4134d19 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/dist.yml @@ -0,0 +1,4 @@ +parameters: + foo: bar + boolean: false + another: ~ diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/existing.yml new file mode 100644 index 0000000..c31530c --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/existing.yml @@ -0,0 +1,3 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/expected.yml new file mode 100644 index 0000000..73f278e --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/expected.yml @@ -0,0 +1,5 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false + another: 'null' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/setup.yml new file mode 100644 index 0000000..79664cb --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_existent/setup.yml @@ -0,0 +1,11 @@ +title: Existing values are not asked interactively again + +interactive: true + +requested_params: + boolean: + default: 'false' + input: 'false' + another: + default: 'null' + input: '"null"' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/dist.yml new file mode 100644 index 0000000..c6e5b2c --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/dist.yml @@ -0,0 +1,4 @@ +parameters: + boolean: false + another: test + nested: nested diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/expected.yml new file mode 100644 index 0000000..84b8ddd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/expected.yml @@ -0,0 +1,10 @@ +# This file is auto-generated during the composer install +parameters: + boolean: true + another: null + nested: + foo: bar + bar: + - foo + - test + - null diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/setup.yml new file mode 100644 index 0000000..40effce --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_non_existent/setup.yml @@ -0,0 +1,14 @@ +title: Missing keys are asked interactively + +interactive: true + +requested_params: + boolean: + default: 'false' + input: 'true' + nested: + default: nested + input: '{foo: bar, bar: [foo, test, null]}' + another: + default: test + input: 'null' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/dist.yml new file mode 100644 index 0000000..c6e5b2c --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/dist.yml @@ -0,0 +1,4 @@ +parameters: + boolean: false + another: test + nested: nested diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/expected.yml new file mode 100644 index 0000000..e693c31 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/expected.yml @@ -0,0 +1,10 @@ +# This file is auto-generated during the composer install +parameters: + boolean: true + nested: + foo: env_foo + bar: + - env + - test + - null + another: null diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/setup.yml new file mode 100644 index 0000000..80e2585 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/interaction_with_environment/setup.yml @@ -0,0 +1,17 @@ +title: Values provided by the environment are not asked interactively + +config: + env-map: + boolean: IC_TEST_BOOL + nested: IC_TEST_NESTED + +environment: + IC_TEST_BOOL: 'true' + IC_TEST_NESTED: '{foo: env_foo, bar: [env, test, null]}' + +interactive: true + +requested_params: + another: + default: test + input: 'null' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/dist.yml new file mode 100644 index 0000000..d426a85 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/dist.yml @@ -0,0 +1,2 @@ +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/existing.yml new file mode 100644 index 0000000..a5cd519 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/existing.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + outdated: outdated_param diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/expected.yml new file mode 100644 index 0000000..a5cd519 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/expected.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + outdated: outdated_param diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/setup.yml new file mode 100644 index 0000000..d3f2eec --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/keep_outdated/setup.yml @@ -0,0 +1,4 @@ +title: Outdated keys can be kept in the parameters file + +config: + keep-outdated: true diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/dist.yml new file mode 100644 index 0000000..45f3fdd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/dist.yml @@ -0,0 +1,7 @@ +parameters: + foo: bar + boolean: false + another: ~ + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/expected.yml new file mode 100644 index 0000000..9084edc --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/expected.yml @@ -0,0 +1,8 @@ +# This file is auto-generated during the composer install +parameters: + foo: bar + boolean: false + another: null + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/setup.yml new file mode 100644 index 0000000..9c33d2d --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent/setup.yml @@ -0,0 +1 @@ +title: Non existent files are created diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/dist.yml new file mode 100644 index 0000000..45f3fdd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/dist.yml @@ -0,0 +1,7 @@ +parameters: + foo: bar + boolean: false + another: ~ + nested: + foo: bar + bar: baz diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/expected.yml new file mode 100644 index 0000000..45b12e2 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/expected.yml @@ -0,0 +1,11 @@ +# This file is auto-generated during the composer install +parameters: + foo: foobar + boolean: true + another: null + nested: + foo: env_foo + bar: + - env + - test + - null diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/setup.yml new file mode 100644 index 0000000..684e4de --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/non_existent_with_environment/setup.yml @@ -0,0 +1,13 @@ +title: Environment variables are used over dist file defaults + +config: + env-map: + boolean: IC_TEST_BOOL + foo: IC_TEST_FOO + nested: IC_TEST_NESTED + another: IC_TEST_NOT_SET + +environment: + IC_TEST_BOOL: 'true' + IC_TEST_FOO: 'foobar' + IC_TEST_NESTED: '{foo: env_foo, bar: [env, test, null]}' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/dist.yml new file mode 100644 index 0000000..d426a85 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/dist.yml @@ -0,0 +1,2 @@ +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/existing.yml new file mode 100644 index 0000000..a5cd519 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/existing.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + outdated: outdated_param diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/expected.yml new file mode 100644 index 0000000..c31530c --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/expected.yml @@ -0,0 +1,3 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/setup.yml new file mode 100644 index 0000000..183e5cd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/remove_outdated/setup.yml @@ -0,0 +1 @@ +title: Outdated keys are removed from the parameters file diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/dist.yml new file mode 100644 index 0000000..9e4d0aa --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/dist.yml @@ -0,0 +1,6 @@ +parameters: + new: bar + new2: new2 + new3: test + new4: test4 + new5: test5 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/existing.yml new file mode 100644 index 0000000..be294b3 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/existing.yml @@ -0,0 +1,6 @@ +# This file is auto-generated during the composer install +parameters: + old: old_value + new2: foo + old2: bar + old4: old4 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/expected.yml new file mode 100644 index 0000000..b7d7fcd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/expected.yml @@ -0,0 +1,7 @@ +# This file is auto-generated during the composer install +parameters: + new: old_value + new2: foo + new3: test + new4: old4 + new5: old4 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/setup.yml new file mode 100644 index 0000000..153ed8c --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed/setup.yml @@ -0,0 +1,9 @@ +title: Key can be renamed and the value is reused + +config: + rename-map: + new: old + new2: old2 + new3: old3 + new4: old4 + new5: new4 # Cascade renaming diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/dist.yml new file mode 100644 index 0000000..be0c216 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/dist.yml @@ -0,0 +1,3 @@ +parameters: + new: bar + new2: new2 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/existing.yml new file mode 100644 index 0000000..b27e31b --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/existing.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + old: old_value + old2: old_value2 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/expected.yml new file mode 100644 index 0000000..5e24ba6 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/expected.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + new: new_env_value + new2: old_value2 diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/setup.yml new file mode 100644 index 0000000..7c3853f --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/renamed_and_environment/setup.yml @@ -0,0 +1,11 @@ +title: Environment variables win over renamed keys + +config: + rename-map: + new: old + new2: old2 + env-map: + new: IC_TEST_NEW + +environment: + IC_TEST_NEW: 'new_env_value' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/dist.yml new file mode 100644 index 0000000..df4994f --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/dist.yml @@ -0,0 +1,3 @@ +parameters: + foo: bar + boolean: false diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/existing.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/existing.yml new file mode 100644 index 0000000..f4b75e8 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/existing.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/expected.yml new file mode 100644 index 0000000..f4b75e8 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/expected.yml @@ -0,0 +1,4 @@ +# This file is auto-generated during the composer install +parameters: + foo: existing_foo + boolean: false diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/setup.yml new file mode 100644 index 0000000..21a4b00 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder/setup.yml @@ -0,0 +1,6 @@ +title: Files can be located in subfolders + +config: + file: 'app/parameters.yml' + +dist-file: 'app/parameters.yml.dist' diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/dist.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/dist.yml new file mode 100644 index 0000000..d426a85 --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/dist.yml @@ -0,0 +1,2 @@ +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/expected.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/expected.yml new file mode 100644 index 0000000..367f8eb --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/expected.yml @@ -0,0 +1,3 @@ +# This file is auto-generated during the composer install +parameters: + foo: bar diff --git a/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/setup.yml b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/setup.yml new file mode 100644 index 0000000..c30049a --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/Tests/fixtures/testcases/subfolder_created/setup.yml @@ -0,0 +1,7 @@ +title: Files can be located in different folders than the dist and the folder is created + +config: + file: 'app/parameters.yml' + dist-file: 'dist/parameters.yml' + +dist-file: 'dist/parameters.yml' diff --git a/vendor/incenteev/composer-parameter-handler/composer.json b/vendor/incenteev/composer-parameter-handler/composer.json new file mode 100644 index 0000000..784ccfd --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/composer.json @@ -0,0 +1,30 @@ +{ + "name": "incenteev/composer-parameter-handler", + "description": "Composer script handling your ignored parameter file", + "keywords": ["parameters management"], + "homepage": "https://github.com/Incenteev/ParameterHandler", + "license": "MIT", + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpspec/prophecy-phpunit": "~1.0", + "symfony/filesystem": "~2.2" + }, + "autoload": { + "psr-4": { "Incenteev\\ParameterHandler\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + } +} diff --git a/vendor/incenteev/composer-parameter-handler/phpunit.xml.dist b/vendor/incenteev/composer-parameter-handler/phpunit.xml.dist new file mode 100644 index 0000000..a274a5f --- /dev/null +++ b/vendor/incenteev/composer-parameter-handler/phpunit.xml.dist @@ -0,0 +1,19 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/jdorn/sql-formatter/.travis.yml b/vendor/jdorn/sql-formatter/.travis.yml new file mode 100644 index 0000000..310d8e0 --- /dev/null +++ b/vendor/jdorn/sql-formatter/.travis.yml @@ -0,0 +1,5 @@ +language: php +php: + - 5.4 + - 5.3 +script: phpunit --coverage-text diff --git a/vendor/jdorn/sql-formatter/LICENSE.txt b/vendor/jdorn/sql-formatter/LICENSE.txt new file mode 100644 index 0000000..e822279 --- /dev/null +++ b/vendor/jdorn/sql-formatter/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Jeremy Dorn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/jdorn/sql-formatter/README.md b/vendor/jdorn/sql-formatter/README.md new file mode 100644 index 0000000..a144323 --- /dev/null +++ b/vendor/jdorn/sql-formatter/README.md @@ -0,0 +1,185 @@ +SqlFormatter +============= + +A lightweight php class for formatting sql statements. + +It can automatically indent and add line breaks in addition to syntax highlighting. + +History +============ + +I found myself having to debug auto-generated SQL statements all the time and +wanted some way to easily output formatted HTML without having to include a +huge library or copy and paste into online formatters. + +I was originally planning to extract the formatting code from PhpMyAdmin, +but that was 10,000+ lines of code and used global variables. + +I saw that other people had the same problem and used Stack Overflow user +losif's answer as a starting point. http://stackoverflow.com/a/3924147 + +Usage +============ + +The SqlFormatter class has a static method 'format' which takes a SQL string +as input and returns a formatted HTML block inside a pre tag. + +Sample usage: + +```php += NOW()) ) + GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10"; + +echo SqlFormatter::format($query); +``` + +Output: + +![](http://jdorn.github.com/sql-formatter/format-highlight.png) + +Formatting Only +------------------------- +If you don't want syntax highlighting and only want the indentations and +line breaks, pass in false as the second parameter. + +This is useful for outputting to error logs or other non-html formats. + +```php +=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "autoload": { + "classmap": ["lib"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + } +} diff --git a/vendor/jdorn/sql-formatter/composer.lock b/vendor/jdorn/sql-formatter/composer.lock new file mode 100644 index 0000000..379ab13 --- /dev/null +++ b/vendor/jdorn/sql-formatter/composer.lock @@ -0,0 +1,422 @@ +{ + "hash": "a709b40d4a35e7077aa40fbd0f78f6c6", + "packages": [ + + ], + "packages-dev": [ + { + "name": "phpunit/php-code-coverage", + "version": "1.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1.2.9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.9", + "reference": "1.2.9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-text-template": ">=1.1.1@stable", + "phpunit/php-token-stream": ">=1.1.3@stable" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2013-02-26 18:55:56" + }, + { + "name": "phpunit/php-file-iterator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2deb24c65ea78e126daa8d45b2089ddc29ec1d26", + "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-01-07 10:47:05" + }, + { + "name": "phpunit/php-text-template", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "1eeef106193d2f8c539728e566bb4793071a9e18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/1eeef106193d2f8c539728e566bb4793071a9e18", + "reference": "1eeef106193d2f8c539728e566bb4793071a9e18", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2013-01-07 10:56:17" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/ecf7920b27003a9412b07dad79dbb5ad1249e6c3", + "reference": "ecf7920b27003a9412b07dad79dbb5ad1249e6c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-01-30 06:08:51" + }, + { + "name": "phpunit/php-token-stream", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c25dd88e1592e66dee2553c99ef244203d5a1b98", + "reference": "c25dd88e1592e66dee2553c99ef244203d5a1b98", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2013-01-07 10:56:35" + }, + { + "name": "phpunit/phpunit", + "version": "3.7.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "2c67e52445416bb7c14046b432acd7eb79e4e612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2c67e52445416bb7c14046b432acd7eb79e4e612", + "reference": "2c67e52445416bb7c14046b432acd7eb79e4e612", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": ">=1.2.1,<1.3.0", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-timer": ">=1.0.2,<1.1.0", + "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", + "symfony/yaml": ">=2.2.0" + }, + "require-dev": { + "pear-pear/pear": "1.9.4" + }, + "suggest": { + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "phpunit/php-invoker": ">=1.1.0,<1.2.0" + }, + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2013-03-11 07:06:05" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/d49b5683200b5db9b1c64cb06f52f50d147891c4", + "reference": "d49b5683200b5db9b1c64cb06f52f50d147891c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2013-02-05 07:46:41" + }, + { + "name": "symfony/yaml", + "version": "dev-master", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "f198ac28048eeceae852419c076123aaee59cd1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/f198ac28048eeceae852419c076123aaee59cd1c", + "reference": "f198ac28048eeceae852419c076123aaee59cd1c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2013-01-31 21:39:01" + } + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": [ + + ], + "platform": { + "php": ">=5.2.4" + }, + "platform-dev": [ + + ] +} diff --git a/vendor/jdorn/sql-formatter/examples/cli.php b/vendor/jdorn/sql-formatter/examples/cli.php new file mode 100644 index 0000000..17ee0e1 --- /dev/null +++ b/vendor/jdorn/sql-formatter/examples/cli.php @@ -0,0 +1,17 @@ +Run this php script from the command line to see CLI syntax highlighting and formatting. It support Unix pipes or command line argument style.

"; + echo "
php examples/cli.php \"SELECT * FROM MyTable WHERE (id>5 AND \\`name\\` LIKE \\"testing\\");\"
"; + echo "
echo \"SELECT * FROM MyTable WHERE (id>5 AND \\`name\\` LIKE \\"testing\\");\" | php examples/cli.php
"; +} + +if(isset($argv[1])) { + $sql = $argv[1]; +} +else { + $sql = stream_get_contents(fopen("php://stdin", "r")); +} + +require_once(__DIR__.'/../lib/SqlFormatter.php'); + +echo SqlFormatter::format($sql); diff --git a/vendor/jdorn/sql-formatter/examples/examples.php b/vendor/jdorn/sql-formatter/examples/examples.php new file mode 100644 index 0000000..f54cfb0 --- /dev/null +++ b/vendor/jdorn/sql-formatter/examples/examples.php @@ -0,0 +1,270 @@ + + + + SqlFormatter Examples + + + += + DATE_FORMAT((DATE_SUB(NOW(),INTERVAL 1 DAY)),'%Y-%c-%d') AND t_create + < DATE_FORMAT(NOW(), '%Y-%c-%d') ORDER BY d.id LIMIT 2,10) a, + orc_scheme_detail b WHERE a.id = b.id", + + "SELECT * from Table1 LEFT + OUTER JOIN Table2 on Table1.id = Table2.id", + + "SELECT * FROM MyTable WHERE id = 46", + + "SELECT count(*),`Column1` as count,`Testing`, `Testing Three` FROM `Table1` + WHERE Column1 = 'testing' AND ( (`Column2` = `Column3` OR Column4 >= NOW()) ) + GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10", + + "select * from `Table`, (SELECT group_concat(column1) as col FROM Table2 GROUP BY category) + Table2, Table3 where Table2.col = (Table3.col2 - `Table`.id)", + + "insert ignore into Table3 (column1, column2) VALUES ('test1','test2'), ('test3','test4');", + + "UPDATE MyTable SET name='sql', category='databases' WHERE id > '65'", + + "delete from MyTable WHERE name LIKE \"test%\"", + + "SELECT * FROM UnmatchedParens WHERE ( A = B)) AND (((Test=1)", + + "-- This is a comment + SELECT + /* This is another comment + On more than one line */ + Id #This is one final comment + as temp, DateCreated as Created FROM MyTable;", +); + +// Example statements for splitting SQL strings into individual queries +$split_statements = array( + "DROP TABLE IF EXISTS MyTable; + CREATE TABLE MyTable ( id int ); + INSERT INTO MyTable (id) + VALUES + (1),(2),(3),(4); + SELECT * FROM MyTable;", + + "SELECT \";\"; SELECT \";\\\"; a;\"; + SELECT \"; + abc\"; + SELECT a,b #comment; + FROM test;", + + " + -- Drop the table first if it exists + DROP TABLE IF EXISTS MyTable; + + -- Create the table + CREATE TABLE MyTable ( id int ); + + -- Insert values + INSERT INTO MyTable (id) + VALUES + (1),(2),(3),(4); + + -- Done", +); + +// Example statements for removing comments +$comment_statements = array( + "-- This is a comment + SELECT + /* This is another comment + On more than one line */ + Id #This is one final comment + as temp, DateCreated as Created FROM MyTable;", +); +?> + + +

Formatting And Syntax Highlighting

+ +
+ Usage: +
+    '); ?>
+    
+
+ + + + + + + + + + + +
OriginalFormatted And Highlighted
+
+
+ + +

Formatting Only

+ +
+ Usage: +
+    '); ?>
+    
+
+ + + + + + + + + + + +
OriginalFormatted
+
+
+ + +

Syntax Highlighting Only

+ +
+ Usage: +
+    '); ?>
+    
+
+ + + + + + + + + + + +
OriginalHighlighted
+
+
+ + +

Compress Query

+ +
+ Usage: +
+    '); ?>
+    
+
+ + + + + + + + + + + +
OriginalCompressed
+
+
+ + +

Splitting SQL Strings Into Individual Queries

+ +
+ Usage: +
+    '); ?>
+    
+
+ + + + + + + + + + + +
OriginalSplit
+
+
"; + foreach ($queries as $query) { + echo "
  • " . SqlFormatter::highlight($query) . "
  • "; + } + echo ""; + ?>
    + + +

    Removing Comments

    + +
    + Usage: +
    +    '); ?>
    +    
    +
    + + + + + + + + + + + +
    OriginalComments Removed
    +
    +
    +
    +
    + + + diff --git a/vendor/jdorn/sql-formatter/lib/SqlFormatter.php b/vendor/jdorn/sql-formatter/lib/SqlFormatter.php new file mode 100644 index 0000000..e569682 --- /dev/null +++ b/vendor/jdorn/sql-formatter/lib/SqlFormatter.php @@ -0,0 +1,1080 @@ + + * @author Florin Patan + * @copyright 2013 Jeremy Dorn + * @license http://opensource.org/licenses/MIT + * @link http://github.com/jdorn/sql-formatter + * @version 1.2.17 + */ +class SqlFormatter +{ + // Constants for token types + const TOKEN_TYPE_WHITESPACE = 0; + const TOKEN_TYPE_WORD = 1; + const TOKEN_TYPE_QUOTE = 2; + const TOKEN_TYPE_BACKTICK_QUOTE = 3; + const TOKEN_TYPE_RESERVED = 4; + const TOKEN_TYPE_RESERVED_TOPLEVEL = 5; + const TOKEN_TYPE_RESERVED_NEWLINE = 6; + const TOKEN_TYPE_BOUNDARY = 7; + const TOKEN_TYPE_COMMENT = 8; + const TOKEN_TYPE_BLOCK_COMMENT = 9; + const TOKEN_TYPE_NUMBER = 10; + const TOKEN_TYPE_ERROR = 11; + const TOKEN_TYPE_VARIABLE = 12; + + // Constants for different components of a token + const TOKEN_TYPE = 0; + const TOKEN_VALUE = 1; + + // Reserved words (for syntax highlighting) + protected static $reserved = array( + 'ACCESSIBLE', 'ACTION', 'AGAINST', 'AGGREGATE', 'ALGORITHM', 'ALL', 'ALTER', 'ANALYSE', 'ANALYZE', 'AS', 'ASC', + 'AUTOCOMMIT', 'AUTO_INCREMENT', 'BACKUP', 'BEGIN', 'BETWEEN', 'BINLOG', 'BOTH', 'CASCADE', 'CASE', 'CHANGE', 'CHANGED', 'CHARACTER SET', + 'CHARSET', 'CHECK', 'CHECKSUM', 'COLLATE', 'COLLATION', 'COLUMN', 'COLUMNS', 'COMMENT', 'COMMIT', 'COMMITTED', 'COMPRESSED', 'CONCURRENT', + 'CONSTRAINT', 'CONTAINS', 'CONVERT', 'CREATE', 'CROSS', 'CURRENT_TIMESTAMP', 'DATABASE', 'DATABASES', 'DAY', 'DAY_HOUR', 'DAY_MINUTE', + 'DAY_SECOND', 'DEFAULT', 'DEFINER', 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DETERMINISTIC', 'DISTINCT', 'DISTINCTROW', 'DIV', + 'DO', 'DUMPFILE', 'DUPLICATE', 'DYNAMIC', 'ELSE', 'ENCLOSED', 'END', 'ENGINE', 'ENGINE_TYPE', 'ENGINES', 'ESCAPE', 'ESCAPED', 'EVENTS', 'EXECUTE', + 'EXISTS', 'EXPLAIN', 'EXTENDED', 'FAST', 'FIELDS', 'FILE', 'FIRST', 'FIXED', 'FLUSH', 'FOR', 'FORCE', 'FOREIGN', 'FULL', 'FULLTEXT', + 'FUNCTION', 'GLOBAL', 'GRANT', 'GRANTS', 'GROUP_CONCAT', 'HEAP', 'HIGH_PRIORITY', 'HOSTS', 'HOUR', 'HOUR_MINUTE', + 'HOUR_SECOND', 'IDENTIFIED', 'IF', 'IFNULL', 'IGNORE', 'IN', 'INDEX', 'INDEXES', 'INFILE', 'INSERT', 'INSERT_ID', 'INSERT_METHOD', 'INTERVAL', + 'INTO', 'INVOKER', 'IS', 'ISOLATION', 'KEY', 'KEYS', 'KILL', 'LAST_INSERT_ID', 'LEADING', 'LEVEL', 'LIKE', 'LINEAR', + 'LINES', 'LOAD', 'LOCAL', 'LOCK', 'LOCKS', 'LOGS', 'LOW_PRIORITY', 'MARIA', 'MASTER', 'MASTER_CONNECT_RETRY', 'MASTER_HOST', 'MASTER_LOG_FILE', + 'MATCH','MAX_CONNECTIONS_PER_HOUR', 'MAX_QUERIES_PER_HOUR', 'MAX_ROWS', 'MAX_UPDATES_PER_HOUR', 'MAX_USER_CONNECTIONS', + 'MEDIUM', 'MERGE', 'MINUTE', 'MINUTE_SECOND', 'MIN_ROWS', 'MODE', 'MODIFY', + 'MONTH', 'MRG_MYISAM', 'MYISAM', 'NAMES', 'NATURAL', 'NOT', 'NOW()','NULL', 'OFFSET', 'ON', 'OPEN', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', + 'ON UPDATE', 'ON DELETE', 'OUTFILE', 'PACK_KEYS', 'PAGE', 'PARTIAL', 'PARTITION', 'PARTITIONS', 'PASSWORD', 'PRIMARY', 'PRIVILEGES', 'PROCEDURE', + 'PROCESS', 'PROCESSLIST', 'PURGE', 'QUICK', 'RANGE', 'RAID0', 'RAID_CHUNKS', 'RAID_CHUNKSIZE','RAID_TYPE', 'READ', 'READ_ONLY', + 'READ_WRITE', 'REFERENCES', 'REGEXP', 'RELOAD', 'RENAME', 'REPAIR', 'REPEATABLE', 'REPLACE', 'REPLICATION', 'RESET', 'RESTORE', 'RESTRICT', + 'RETURN', 'RETURNS', 'REVOKE', 'RLIKE', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', 'SECOND', 'SECURITY', 'SEPARATOR', + 'SERIALIZABLE', 'SESSION', 'SHARE', 'SHOW', 'SHUTDOWN', 'SLAVE', 'SONAME', 'SOUNDS', 'SQL', 'SQL_AUTO_IS_NULL', 'SQL_BIG_RESULT', + 'SQL_BIG_SELECTS', 'SQL_BIG_TABLES', 'SQL_BUFFER_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_LOG_BIN', 'SQL_LOG_OFF', 'SQL_LOG_UPDATE', + 'SQL_LOW_PRIORITY_UPDATES', 'SQL_MAX_JOIN_SIZE', 'SQL_QUOTE_SHOW_CREATE', 'SQL_SAFE_UPDATES', 'SQL_SELECT_LIMIT', 'SQL_SLAVE_SKIP_COUNTER', + 'SQL_SMALL_RESULT', 'SQL_WARNINGS', 'SQL_CACHE', 'SQL_NO_CACHE', 'START', 'STARTING', 'STATUS', 'STOP', 'STORAGE', + 'STRAIGHT_JOIN', 'STRING', 'STRIPED', 'SUPER', 'TABLE', 'TABLES', 'TEMPORARY', 'TERMINATED', 'THEN', 'TO', 'TRAILING', 'TRANSACTIONAL', 'TRUE', + 'TRUNCATE', 'TYPE', 'TYPES', 'UNCOMMITTED', 'UNIQUE', 'UNLOCK', 'UNSIGNED', 'USAGE', 'USE', 'USING', 'VARIABLES', + 'VIEW', 'WHEN', 'WITH', 'WORK', 'WRITE', 'YEAR_MONTH' + ); + + // For SQL formatting + // These keywords will all be on their own line + protected static $reserved_toplevel = array( + 'SELECT', 'FROM', 'WHERE', 'SET', 'ORDER BY', 'GROUP BY', 'LIMIT', 'DROP', + 'VALUES', 'UPDATE', 'HAVING', 'ADD', 'AFTER', 'ALTER TABLE', 'DELETE FROM', 'UNION ALL', 'UNION', 'EXCEPT', 'INTERSECT' + ); + + protected static $reserved_newline = array( + 'LEFT OUTER JOIN', 'RIGHT OUTER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'OUTER JOIN', 'INNER JOIN', 'JOIN', 'XOR', 'OR', 'AND' + ); + + protected static $functions = array ( + 'ABS', 'ACOS', 'ADDDATE', 'ADDTIME', 'AES_DECRYPT', 'AES_ENCRYPT', 'AREA', 'ASBINARY', 'ASCII', 'ASIN', 'ASTEXT', 'ATAN', 'ATAN2', + 'AVG', 'BDMPOLYFROMTEXT', 'BDMPOLYFROMWKB', 'BDPOLYFROMTEXT', 'BDPOLYFROMWKB', 'BENCHMARK', 'BIN', 'BIT_AND', 'BIT_COUNT', 'BIT_LENGTH', + 'BIT_OR', 'BIT_XOR', 'BOUNDARY', 'BUFFER', 'CAST', 'CEIL', 'CEILING', 'CENTROID', 'CHAR', 'CHARACTER_LENGTH', 'CHARSET', 'CHAR_LENGTH', + 'COALESCE', 'COERCIBILITY', 'COLLATION', 'COMPRESS', 'CONCAT', 'CONCAT_WS', 'CONNECTION_ID', 'CONTAINS', 'CONV', 'CONVERT', 'CONVERT_TZ', + 'CONVEXHULL', 'COS', 'COT', 'COUNT', 'CRC32', 'CROSSES', 'CURDATE', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER', + 'CURTIME', 'DATABASE', 'DATE', 'DATEDIFF', 'DATE_ADD', 'DATE_DIFF', 'DATE_FORMAT', 'DATE_SUB', 'DAY', 'DAYNAME', 'DAYOFMONTH', 'DAYOFWEEK', + 'DAYOFYEAR', 'DECODE', 'DEFAULT', 'DEGREES', 'DES_DECRYPT', 'DES_ENCRYPT', 'DIFFERENCE', 'DIMENSION', 'DISJOINT', 'DISTANCE', 'ELT', 'ENCODE', + 'ENCRYPT', 'ENDPOINT', 'ENVELOPE', 'EQUALS', 'EXP', 'EXPORT_SET', 'EXTERIORRING', 'EXTRACT', 'EXTRACTVALUE', 'FIELD', 'FIND_IN_SET', 'FLOOR', + 'FORMAT', 'FOUND_ROWS', 'FROM_DAYS', 'FROM_UNIXTIME', 'GEOMCOLLFROMTEXT', 'GEOMCOLLFROMWKB', 'GEOMETRYCOLLECTION', 'GEOMETRYCOLLECTIONFROMTEXT', + 'GEOMETRYCOLLECTIONFROMWKB', 'GEOMETRYFROMTEXT', 'GEOMETRYFROMWKB', 'GEOMETRYN', 'GEOMETRYTYPE', 'GEOMFROMTEXT', 'GEOMFROMWKB', 'GET_FORMAT', + 'GET_LOCK', 'GLENGTH', 'GREATEST', 'GROUP_CONCAT', 'GROUP_UNIQUE_USERS', 'HEX', 'HOUR', 'IF', 'IFNULL', 'INET_ATON', 'INET_NTOA', 'INSERT', 'INSTR', + 'INTERIORRINGN', 'INTERSECTION', 'INTERSECTS', 'INTERVAL', 'ISCLOSED', 'ISEMPTY', 'ISNULL', 'ISRING', 'ISSIMPLE', 'IS_FREE_LOCK', 'IS_USED_LOCK', + 'LAST_DAY', 'LAST_INSERT_ID', 'LCASE', 'LEAST', 'LEFT', 'LENGTH', 'LINEFROMTEXT', 'LINEFROMWKB', 'LINESTRING', 'LINESTRINGFROMTEXT', 'LINESTRINGFROMWKB', + 'LN', 'LOAD_FILE', 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCATE', 'LOG', 'LOG10', 'LOG2', 'LOWER', 'LPAD', 'LTRIM', 'MAKEDATE', 'MAKETIME', 'MAKE_SET', + 'MASTER_POS_WAIT', 'MAX', 'MBRCONTAINS', 'MBRDISJOINT', 'MBREQUAL', 'MBRINTERSECTS', 'MBROVERLAPS', 'MBRTOUCHES', 'MBRWITHIN', 'MD5', 'MICROSECOND', + 'MID', 'MIN', 'MINUTE', 'MLINEFROMTEXT', 'MLINEFROMWKB', 'MOD', 'MONTH', 'MONTHNAME', 'MPOINTFROMTEXT', 'MPOINTFROMWKB', 'MPOLYFROMTEXT', 'MPOLYFROMWKB', + 'MULTILINESTRING', 'MULTILINESTRINGFROMTEXT', 'MULTILINESTRINGFROMWKB', 'MULTIPOINT', 'MULTIPOINTFROMTEXT', 'MULTIPOINTFROMWKB', 'MULTIPOLYGON', + 'MULTIPOLYGONFROMTEXT', 'MULTIPOLYGONFROMWKB', 'NAME_CONST', 'NULLIF', 'NUMGEOMETRIES', 'NUMINTERIORRINGS', 'NUMPOINTS', 'OCT', 'OCTET_LENGTH', + 'OLD_PASSWORD', 'ORD', 'OVERLAPS', 'PASSWORD', 'PERIOD_ADD', 'PERIOD_DIFF', 'PI', 'POINT', 'POINTFROMTEXT', 'POINTFROMWKB', 'POINTN', 'POINTONSURFACE', + 'POLYFROMTEXT', 'POLYFROMWKB', 'POLYGON', 'POLYGONFROMTEXT', 'POLYGONFROMWKB', 'POSITION', 'POW', 'POWER', 'QUARTER', 'QUOTE', 'RADIANS', 'RAND', + 'RELATED', 'RELEASE_LOCK', 'REPEAT', 'REPLACE', 'REVERSE', 'RIGHT', 'ROUND', 'ROW_COUNT', 'RPAD', 'RTRIM', 'SCHEMA', 'SECOND', 'SEC_TO_TIME', + 'SESSION_USER', 'SHA', 'SHA1', 'SIGN', 'SIN', 'SLEEP', 'SOUNDEX', 'SPACE', 'SQRT', 'SRID', 'STARTPOINT', 'STD', 'STDDEV', 'STDDEV_POP', 'STDDEV_SAMP', + 'STRCMP', 'STR_TO_DATE', 'SUBDATE', 'SUBSTR', 'SUBSTRING', 'SUBSTRING_INDEX', 'SUBTIME', 'SUM', 'SYMDIFFERENCE', 'SYSDATE', 'SYSTEM_USER', 'TAN', + 'TIME', 'TIMEDIFF', 'TIMESTAMP', 'TIMESTAMPADD', 'TIMESTAMPDIFF', 'TIME_FORMAT', 'TIME_TO_SEC', 'TOUCHES', 'TO_DAYS', 'TRIM', 'TRUNCATE', 'UCASE', + 'UNCOMPRESS', 'UNCOMPRESSED_LENGTH', 'UNHEX', 'UNIQUE_USERS', 'UNIX_TIMESTAMP', 'UPDATEXML', 'UPPER', 'USER', 'UTC_DATE', 'UTC_TIME', 'UTC_TIMESTAMP', + 'UUID', 'VARIANCE', 'VAR_POP', 'VAR_SAMP', 'VERSION', 'WEEK', 'WEEKDAY', 'WEEKOFYEAR', 'WITHIN', 'X', 'Y', 'YEAR', 'YEARWEEK' + ); + + // Punctuation that can be used as a boundary between other tokens + protected static $boundaries = array(',', ';',':', ')', '(', '.', '=', '<', '>', '+', '-', '*', '/', '!', '^', '%', '|', '&', '#'); + + // For HTML syntax highlighting + // Styles applied to different token types + public static $quote_attributes = 'style="color: blue;"'; + public static $backtick_quote_attributes = 'style="color: purple;"'; + public static $reserved_attributes = 'style="font-weight:bold;"'; + public static $boundary_attributes = ''; + public static $number_attributes = 'style="color: green;"'; + public static $word_attributes = 'style="color: #333;"'; + public static $error_attributes = 'style="background-color: red;"'; + public static $comment_attributes = 'style="color: #aaa;"'; + public static $variable_attributes = 'style="color: orange;"'; + public static $pre_attributes = 'style="color: black; background-color: white;"'; + + // Boolean - whether or not the current environment is the CLI + // This affects the type of syntax highlighting + // If not defined, it will be determined automatically + public static $cli; + + // For CLI syntax highlighting + public static $cli_quote = "\x1b[34;1m"; + public static $cli_backtick_quote = "\x1b[35;1m"; + public static $cli_reserved = "\x1b[37m"; + public static $cli_boundary = ""; + public static $cli_number = "\x1b[32;1m"; + public static $cli_word = ""; + public static $cli_error = "\x1b[31;1;7m"; + public static $cli_comment = "\x1b[30;1m"; + public static $cli_functions = "\x1b[37m"; + public static $cli_variable = "\x1b[36;1m"; + + // The tab character to use when formatting SQL + public static $tab = ' '; + + // This flag tells us if queries need to be enclosed in
     tags
    +    public static $use_pre = true;
    +
    +    // This flag tells us if SqlFormatted has been initialized
    +    protected static $init;
    +
    +    // Regular expressions for tokenizing
    +    protected static $regex_boundaries;
    +    protected static $regex_reserved;
    +    protected static $regex_reserved_newline;
    +    protected static $regex_reserved_toplevel;
    +    protected static $regex_function;
    +
    +    // Cache variables
    +    // Only tokens shorter than this size will be cached.  Somewhere between 10 and 20 seems to work well for most cases.
    +    public static $max_cachekey_size = 15;
    +    protected static $token_cache = array();
    +    protected static $cache_hits = 0;
    +    protected static $cache_misses = 0;
    +
    +    /**
    +     * Get stats about the token cache
    +     * @return Array An array containing the keys 'hits', 'misses', 'entries', and 'size' in bytes
    +     */
    +    public static function getCacheStats()
    +    {
    +        return array(
    +            'hits'=>self::$cache_hits,
    +            'misses'=>self::$cache_misses,
    +            'entries'=>count(self::$token_cache),
    +            'size'=>strlen(serialize(self::$token_cache))
    +        );
    +    }
    +
    +    /**
    +     * Stuff that only needs to be done once.  Builds regular expressions and sorts the reserved words.
    +     */
    +    protected static function init()
    +    {
    +        if (self::$init) return;
    +
    +        // Sort reserved word list from longest word to shortest, 3x faster than usort
    +        $reservedMap = array_combine(self::$reserved, array_map('strlen', self::$reserved));
    +        arsort($reservedMap);
    +        self::$reserved = array_keys($reservedMap);
    +
    +        // Set up regular expressions
    +        self::$regex_boundaries = '('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$boundaries)).')';
    +        self::$regex_reserved = '('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$reserved)).')';
    +        self::$regex_reserved_toplevel = str_replace(' ','\\s+','('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$reserved_toplevel)).')');
    +        self::$regex_reserved_newline = str_replace(' ','\\s+','('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$reserved_newline)).')');
    +
    +        self::$regex_function = '('.implode('|',array_map(array(__CLASS__, 'quote_regex'),self::$functions)).')';
    +
    +        self::$init = true;
    +    }
    +
    +    /**
    +     * Return the next token and token type in a SQL string.
    +     * Quoted strings, comments, reserved words, whitespace, and punctuation are all their own tokens.
    +     *
    +     * @param String $string   The SQL string
    +     * @param array  $previous The result of the previous getNextToken() call
    +     *
    +     * @return Array An associative array containing the type and value of the token.
    +     */
    +    protected static function getNextToken($string, $previous = null)
    +    {
    +        // Whitespace
    +        if (preg_match('/^\s+/',$string,$matches)) {
    +            return array(
    +                self::TOKEN_VALUE => $matches[0],
    +                self::TOKEN_TYPE=>self::TOKEN_TYPE_WHITESPACE
    +            );
    +        }
    +
    +        // Comment
    +        if ($string[0] === '#' || (isset($string[1])&&($string[0]==='-'&&$string[1]==='-') || ($string[0]==='/'&&$string[1]==='*'))) {
    +            // Comment until end of line
    +            if ($string[0] === '-' || $string[0] === '#') {
    +                $last = strpos($string, "\n");
    +                $type = self::TOKEN_TYPE_COMMENT;
    +            } else { // Comment until closing comment tag
    +                $last = strpos($string, "*/", 2) + 2;
    +                $type = self::TOKEN_TYPE_BLOCK_COMMENT;
    +            }
    +
    +            if ($last === false) {
    +                $last = strlen($string);
    +            }
    +
    +            return array(
    +                self::TOKEN_VALUE => substr($string, 0, $last),
    +                self::TOKEN_TYPE  => $type
    +            );
    +        }
    +
    +        // Quoted String
    +        if ($string[0]==='"' || $string[0]==='\'' || $string[0]==='`') {
    +            $return = array(
    +                self::TOKEN_TYPE => ($string[0]==='`'? self::TOKEN_TYPE_BACKTICK_QUOTE : self::TOKEN_TYPE_QUOTE),
    +                self::TOKEN_VALUE => self::getQuotedString($string)
    +            );
    +
    +            return $return;
    +        }
    +
    +        // User-defined Variable
    +        if ($string[0] === '@' && isset($string[1])) {
    +            $ret = array(
    +                self::TOKEN_VALUE => null,
    +                self::TOKEN_TYPE => self::TOKEN_TYPE_VARIABLE
    +            );
    +            
    +            // If the variable name is quoted
    +            if ($string[1]==='"' || $string[1]==='\'' || $string[1]==='`') {
    +                $ret[self::TOKEN_VALUE] = '@'.self::getQuotedString(substr($string,1));
    +            }
    +            // Non-quoted variable name
    +            else {
    +                preg_match('/^(@[a-zA-Z0-9\._\$]+)/',$string,$matches);
    +                if ($matches) {
    +                    $ret[self::TOKEN_VALUE] = $matches[1];
    +                }
    +            }
    +            
    +            if($ret[self::TOKEN_VALUE] !== null) return $ret;
    +        }
    +
    +        // Number (decimal, binary, or hex)
    +        if (preg_match('/^([0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)($|\s|"\'`|'.self::$regex_boundaries.')/',$string,$matches)) {
    +            return array(
    +                self::TOKEN_VALUE => $matches[1],
    +                self::TOKEN_TYPE=>self::TOKEN_TYPE_NUMBER
    +            );
    +        }
    +
    +        // Boundary Character (punctuation and symbols)
    +        if (preg_match('/^('.self::$regex_boundaries.')/',$string,$matches)) {
    +            return array(
    +                self::TOKEN_VALUE => $matches[1],
    +                self::TOKEN_TYPE  => self::TOKEN_TYPE_BOUNDARY
    +            );
    +        }
    +
    +        // A reserved word cannot be preceded by a '.'
    +        // this makes it so in "mytable.from", "from" is not considered a reserved word
    +        if (!$previous || !isset($previous[self::TOKEN_VALUE]) || $previous[self::TOKEN_VALUE] !== '.') {
    +            $upper = strtoupper($string);
    +            // Top Level Reserved Word
    +            if (preg_match('/^('.self::$regex_reserved_toplevel.')($|\s|'.self::$regex_boundaries.')/', $upper,$matches)) {
    +                return array(
    +                    self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED_TOPLEVEL,
    +                    self::TOKEN_VALUE=>substr($string,0,strlen($matches[1]))
    +                );
    +            }
    +            // Newline Reserved Word
    +            if (preg_match('/^('.self::$regex_reserved_newline.')($|\s|'.self::$regex_boundaries.')/', $upper,$matches)) {
    +                return array(
    +                    self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED_NEWLINE,
    +                    self::TOKEN_VALUE=>substr($string,0,strlen($matches[1]))
    +                );
    +            }
    +            // Other Reserved Word
    +            if (preg_match('/^('.self::$regex_reserved.')($|\s|'.self::$regex_boundaries.')/', $upper,$matches)) {
    +                return array(
    +                    self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED,
    +                    self::TOKEN_VALUE=>substr($string,0,strlen($matches[1]))
    +                );
    +            }
    +        }
    +
    +        // A function must be suceeded by '('
    +        // this makes it so "count(" is considered a function, but "count" alone is not
    +        $upper = strtoupper($string);
    +        // function
    +        if (preg_match('/^('.self::$regex_function.'[(]|\s|[)])/', $upper,$matches)) {
    +            return array(
    +                self::TOKEN_TYPE=>self::TOKEN_TYPE_RESERVED,
    +                self::TOKEN_VALUE=>substr($string,0,strlen($matches[1])-1)
    +            );
    +        }
    +
    +        // Non reserved word
    +        preg_match('/^(.*?)($|\s|["\'`]|'.self::$regex_boundaries.')/',$string,$matches);
    +
    +        return array(
    +            self::TOKEN_VALUE => $matches[1],
    +            self::TOKEN_TYPE  => self::TOKEN_TYPE_WORD
    +        );
    +    }
    +
    +    protected static function getQuotedString($string)
    +    {
    +        $ret = null;
    +        
    +        // This checks for the following patterns:
    +        // 1. backtick quoted string using `` to escape
    +        // 2. double quoted string using "" or \" to escape
    +        // 3. single quoted string using '' or \' to escape
    +        if ( preg_match('/^(((`[^`]*($|`))+)|(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)|((\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*(\'|$))+))/s', $string, $matches)) {
    +            $ret = $matches[1];
    +        }
    +        
    +        return $ret;
    +    }
    +
    +    /**
    +     * Takes a SQL string and breaks it into tokens.
    +     * Each token is an associative array with type and value.
    +     *
    +     * @param String $string The SQL string
    +     *
    +     * @return Array An array of tokens.
    +     */
    +    protected static function tokenize($string)
    +    {
    +        self::init();
    +
    +        $tokens = array();
    +
    +        // Used for debugging if there is an error while tokenizing the string
    +        $original_length = strlen($string);
    +
    +        // Used to make sure the string keeps shrinking on each iteration
    +        $old_string_len = strlen($string) + 1;
    +
    +        $token = null;
    +
    +        $current_length = strlen($string);
    +
    +        // Keep processing the string until it is empty
    +        while ($current_length) {
    +            // If the string stopped shrinking, there was a problem
    +            if ($old_string_len <= $current_length) {
    +                $tokens[] = array(
    +                    self::TOKEN_VALUE=>$string,
    +                    self::TOKEN_TYPE=>self::TOKEN_TYPE_ERROR
    +                );
    +
    +                return $tokens;
    +            }
    +            $old_string_len =  $current_length;
    +
    +            // Determine if we can use caching
    +            if ($current_length >= self::$max_cachekey_size) {
    +                $cacheKey = substr($string,0,self::$max_cachekey_size);
    +            } else {
    +                $cacheKey = false;
    +            }
    +
    +            // See if the token is already cached
    +            if ($cacheKey && isset(self::$token_cache[$cacheKey])) {
    +                // Retrieve from cache
    +                $token = self::$token_cache[$cacheKey];
    +                $token_length = strlen($token[self::TOKEN_VALUE]);
    +                self::$cache_hits++;
    +            } else {
    +                // Get the next token and the token type
    +                $token = self::getNextToken($string, $token);
    +                $token_length = strlen($token[self::TOKEN_VALUE]);
    +                self::$cache_misses++;
    +
    +                // If the token is shorter than the max length, store it in cache
    +                if ($cacheKey && $token_length < self::$max_cachekey_size) {
    +                    self::$token_cache[$cacheKey] = $token;
    +                }
    +            }
    +
    +            $tokens[] = $token;
    +
    +            // Advance the string
    +            $string = substr($string, $token_length);
    +
    +            $current_length -= $token_length;
    +        }
    +
    +        return $tokens;
    +    }
    +
    +    /**
    +     * Format the whitespace in a SQL string to make it easier to read.
    +     *
    +     * @param String  $string    The SQL string
    +     * @param boolean $highlight If true, syntax highlighting will also be performed
    +     *
    +     * @return String The SQL string with HTML styles and formatting wrapped in a 
     tag
    +     */
    +    public static function format($string, $highlight=true)
    +    {
    +        // This variable will be populated with formatted html
    +        $return = '';
    +
    +        // Use an actual tab while formatting and then switch out with self::$tab at the end
    +        $tab = "\t";
    +
    +        $indent_level = 0;
    +        $newline = false;
    +        $inline_parentheses = false;
    +        $increase_special_indent = false;
    +        $increase_block_indent = false;
    +        $indent_types = array();
    +        $added_newline = false;
    +        $inline_count = 0;
    +        $inline_indented = false;
    +        $clause_limit = false;
    +
    +        // Tokenize String
    +        $original_tokens = self::tokenize($string);
    +
    +        // Remove existing whitespace
    +        $tokens = array();
    +        foreach ($original_tokens as $i=>$token) {
    +            if ($token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
    +                $token['i'] = $i;
    +                $tokens[] = $token;
    +            }
    +        }
    +
    +        // Format token by token
    +        foreach ($tokens as $i=>$token) {
    +            // Get highlighted token if doing syntax highlighting
    +            if ($highlight) {
    +                $highlighted = self::highlightToken($token);
    +            } else { // If returning raw text
    +                $highlighted = $token[self::TOKEN_VALUE];
    +            }
    +
    +            // If we are increasing the special indent level now
    +            if ($increase_special_indent) {
    +                $indent_level++;
    +                $increase_special_indent = false;
    +                array_unshift($indent_types,'special');
    +            }
    +            // If we are increasing the block indent level now
    +            if ($increase_block_indent) {
    +                $indent_level++;
    +                $increase_block_indent = false;
    +                array_unshift($indent_types,'block');
    +            }
    +
    +            // If we need a new line before the token
    +            if ($newline) {
    +                $return .= "\n" . str_repeat($tab, $indent_level);
    +                $newline = false;
    +                $added_newline = true;
    +            } else {
    +                $added_newline = false;
    +            }
    +
    +            // Display comments directly where they appear in the source
    +            if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
    +                if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
    +                    $indent = str_repeat($tab,$indent_level);
    +                    $return .= "\n" . $indent;
    +                    $highlighted = str_replace("\n","\n".$indent,$highlighted);
    +                }
    +
    +                $return .= $highlighted;
    +                $newline = true;
    +                continue;
    +            }
    +
    +            if ($inline_parentheses) {
    +                // End of inline parentheses
    +                if ($token[self::TOKEN_VALUE] === ')') {
    +                    $return = rtrim($return,' ');
    +
    +                    if ($inline_indented) {
    +                        array_shift($indent_types);
    +                        $indent_level --;
    +                        $return .= "\n" . str_repeat($tab, $indent_level);
    +                    }
    +
    +                    $inline_parentheses = false;
    +
    +                    $return .= $highlighted . ' ';
    +                    continue;
    +                }
    +
    +                if ($token[self::TOKEN_VALUE] === ',') {
    +                    if ($inline_count >= 30) {
    +                        $inline_count = 0;
    +                        $newline = true;
    +                    }
    +                }
    +
    +                $inline_count += strlen($token[self::TOKEN_VALUE]);
    +            }
    +
    +            // Opening parentheses increase the block indent level and start a new line
    +            if ($token[self::TOKEN_VALUE] === '(') {
    +                // First check if this should be an inline parentheses block
    +                // Examples are "NOW()", "COUNT(*)", "int(10)", key(`somecolumn`), DECIMAL(7,2)
    +                // Allow up to 3 non-whitespace tokens inside inline parentheses
    +                $length = 0;
    +                for ($j=1;$j<=250;$j++) {
    +                    // Reached end of string
    +                    if (!isset($tokens[$i+$j])) break;
    +
    +                    $next = $tokens[$i+$j];
    +
    +                    // Reached closing parentheses, able to inline it
    +                    if ($next[self::TOKEN_VALUE] === ')') {
    +                        $inline_parentheses = true;
    +                        $inline_count = 0;
    +                        $inline_indented = false;
    +                        break;
    +                    }
    +
    +                    // Reached an invalid token for inline parentheses
    +                    if ($next[self::TOKEN_VALUE]===';' || $next[self::TOKEN_VALUE]==='(') {
    +                        break;
    +                    }
    +
    +                    // Reached an invalid token type for inline parentheses
    +                    if ($next[self::TOKEN_TYPE]===self::TOKEN_TYPE_RESERVED_TOPLEVEL || $next[self::TOKEN_TYPE]===self::TOKEN_TYPE_RESERVED_NEWLINE || $next[self::TOKEN_TYPE]===self::TOKEN_TYPE_COMMENT || $next[self::TOKEN_TYPE]===self::TOKEN_TYPE_BLOCK_COMMENT) {
    +                        break;
    +                    }
    +
    +                    $length += strlen($next[self::TOKEN_VALUE]);
    +                }
    +
    +                if ($inline_parentheses && $length > 30) {
    +                    $increase_block_indent = true;
    +                    $inline_indented = true;
    +                    $newline = true;
    +                }
    +
    +                // Take out the preceding space unless there was whitespace there in the original query
    +                if (isset($original_tokens[$token['i']-1]) && $original_tokens[$token['i']-1][self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
    +                    $return = rtrim($return,' ');
    +                }
    +
    +                if (!$inline_parentheses) {
    +                    $increase_block_indent = true;
    +                    // Add a newline after the parentheses
    +                    $newline = true;
    +                }
    +
    +            }
    +
    +            // Closing parentheses decrease the block indent level
    +            elseif ($token[self::TOKEN_VALUE] === ')') {
    +                // Remove whitespace before the closing parentheses
    +                $return = rtrim($return,' ');
    +
    +                $indent_level--;
    +
    +                // Reset indent level
    +                while ($j=array_shift($indent_types)) {
    +                    if ($j==='special') {
    +                        $indent_level--;
    +                    } else {
    +                        break;
    +                    }
    +                }
    +
    +                if ($indent_level < 0) {
    +                    // This is an error
    +                    $indent_level = 0;
    +
    +                    if ($highlight) {
    +                        $return .= "\n".self::highlightError($token[self::TOKEN_VALUE]);
    +                        continue;
    +                    }
    +                }
    +
    +                // Add a newline before the closing parentheses (if not already added)
    +                if (!$added_newline) {
    +                    $return .= "\n" . str_repeat($tab, $indent_level);
    +                }
    +            }
    +
    +            // Top level reserved words start a new line and increase the special indent level
    +            elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
    +                $increase_special_indent = true;
    +
    +                // If the last indent type was 'special', decrease the special indent for this round
    +                reset($indent_types);
    +                if (current($indent_types)==='special') {
    +                    $indent_level--;
    +                    array_shift($indent_types);
    +                }
    +
    +                // Add a newline after the top level reserved word
    +                $newline = true;
    +                // Add a newline before the top level reserved word (if not already added)
    +                if (!$added_newline) {
    +                    $return .= "\n" . str_repeat($tab, $indent_level);
    +                }
    +                // If we already added a newline, redo the indentation since it may be different now
    +                else {
    +                    $return = rtrim($return,$tab).str_repeat($tab, $indent_level);
    +                }
    +
    +                // If the token may have extra whitespace
    +                if (strpos($token[self::TOKEN_VALUE],' ')!==false || strpos($token[self::TOKEN_VALUE],"\n")!==false || strpos($token[self::TOKEN_VALUE],"\t")!==false) {
    +                    $highlighted = preg_replace('/\s+/',' ',$highlighted);
    +                }
    +                //if SQL 'LIMIT' clause, start variable to reset newline
    +                if ($token[self::TOKEN_VALUE] === 'LIMIT' && !$inline_parentheses) {
    +                    $clause_limit = true;
    +                }
    +            }
    +
    +            // Checks if we are out of the limit clause
    +            elseif ($clause_limit && $token[self::TOKEN_VALUE] !== "," && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_NUMBER && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
    +                $clause_limit = false;
    +            }
    +
    +            // Commas start a new line (unless within inline parentheses or SQL 'LIMIT' clause)
    +            elseif ($token[self::TOKEN_VALUE] === ',' && !$inline_parentheses) {
    +                //If the previous TOKEN_VALUE is 'LIMIT', resets new line
    +                if ($clause_limit === true) {
    +                    $newline = false;
    +                    $clause_limit = false;
    +                }
    +                // All other cases of commas
    +                else {
    +                    $newline = true;
    +                }
    +            }
    +
    +            // Newline reserved words start a new line
    +            elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE) {
    +                // Add a newline before the reserved word (if not already added)
    +                if (!$added_newline) {
    +                    $return .= "\n" . str_repeat($tab, $indent_level);
    +                }
    +
    +                // If the token may have extra whitespace
    +                if (strpos($token[self::TOKEN_VALUE],' ')!==false || strpos($token[self::TOKEN_VALUE],"\n")!==false || strpos($token[self::TOKEN_VALUE],"\t")!==false) {
    +                    $highlighted = preg_replace('/\s+/',' ',$highlighted);
    +                }
    +            }
    +
    +            // Multiple boundary characters in a row should not have spaces between them (not including parentheses)
    +            elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BOUNDARY) {
    +                if (isset($tokens[$i-1]) && $tokens[$i-1][self::TOKEN_TYPE] === self::TOKEN_TYPE_BOUNDARY) {
    +                    if (isset($original_tokens[$token['i']-1]) && $original_tokens[$token['i']-1][self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
    +                        $return = rtrim($return,' ');
    +                    }
    +                }
    +            }
    +
    +            // If the token shouldn't have a space before it
    +            if ($token[self::TOKEN_VALUE] === '.' || $token[self::TOKEN_VALUE] === ',' || $token[self::TOKEN_VALUE] === ';') {
    +                $return = rtrim($return, ' ');
    +            }
    +
    +            $return .= $highlighted.' ';
    +
    +            // If the token shouldn't have a space after it
    +            if ($token[self::TOKEN_VALUE] === '(' || $token[self::TOKEN_VALUE] === '.') {
    +                $return = rtrim($return,' ');
    +            }
    +            
    +            // If this is the "-" of a negative number, it shouldn't have a space after it
    +            if($token[self::TOKEN_VALUE] === '-' && isset($tokens[$i+1]) && $tokens[$i+1][self::TOKEN_TYPE] === self::TOKEN_TYPE_NUMBER && isset($tokens[$i-1])) {
    +                $prev = $tokens[$i-1][self::TOKEN_TYPE];
    +                if($prev !== self::TOKEN_TYPE_QUOTE && $prev !== self::TOKEN_TYPE_BACKTICK_QUOTE && $prev !== self::TOKEN_TYPE_WORD && $prev !== self::TOKEN_TYPE_NUMBER) {
    +                    $return = rtrim($return,' ');
    +                }
    +            } 
    +        }
    +
    +        // If there are unmatched parentheses
    +        if ($highlight && array_search('block',$indent_types) !== false) {
    +            $return .= "\n".self::highlightError("WARNING: unclosed parentheses or section");
    +        }
    +
    +        // Replace tab characters with the configuration tab character
    +        $return = trim(str_replace("\t",self::$tab,$return));
    +
    +        if ($highlight) {
    +            $return = self::output($return);
    +        }
    +
    +        return $return;
    +    }
    +
    +    /**
    +     * Add syntax highlighting to a SQL string
    +     *
    +     * @param String $string The SQL string
    +     *
    +     * @return String The SQL string with HTML styles applied
    +     */
    +    public static function highlight($string)
    +    {
    +        $tokens = self::tokenize($string);
    +
    +        $return = '';
    +
    +        foreach ($tokens as $token) {
    +            $return .= self::highlightToken($token);
    +        }
    +
    +        return self::output($return);
    +    }
    +
    +    /**
    +     * Split a SQL string into multiple queries.
    +     * Uses ";" as a query delimiter.
    +     *
    +     * @param String $string The SQL string
    +     *
    +     * @return Array An array of individual query strings without trailing semicolons
    +     */
    +    public static function splitQuery($string)
    +    {
    +        $queries = array();
    +        $current_query = '';
    +        $empty = true;
    +
    +        $tokens = self::tokenize($string);
    +
    +        foreach ($tokens as $token) {
    +            // If this is a query separator
    +            if ($token[self::TOKEN_VALUE] === ';') {
    +                if (!$empty) {
    +                    $queries[] = $current_query.';';
    +                }
    +                $current_query = '';
    +                $empty = true;
    +                continue;
    +            }
    +
    +            // If this is a non-empty character
    +            if ($token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_COMMENT && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_BLOCK_COMMENT) {
    +                $empty = false;
    +            }
    +
    +            $current_query .= $token[self::TOKEN_VALUE];
    +        }
    +
    +        if (!$empty) {
    +            $queries[] = trim($current_query);
    +        }
    +
    +        return $queries;
    +    }
    +
    +    /**
    +     * Remove all comments from a SQL string
    +     *
    +     * @param String $string The SQL string
    +     *
    +     * @return String The SQL string without comments
    +     */
    +    public static function removeComments($string)
    +    {
    +        $result = '';
    +
    +        $tokens = self::tokenize($string);
    +
    +        foreach ($tokens as $token) {
    +            // Skip comment tokens
    +            if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
    +                continue;
    +            }
    +
    +            $result .= $token[self::TOKEN_VALUE];
    +        }
    +        $result = self::format( $result,false);
    +
    +        return $result;
    +    }
    +
    +    /**
    +     * Compress a query by collapsing white space and removing comments
    +     *
    +     * @param String $string The SQL string
    +     *
    +     * @return String The SQL string without comments
    +     */
    +    public static function compress($string)
    +    {
    +        $result = '';
    +
    +        $tokens = self::tokenize($string);
    +
    +        $whitespace = true;
    +        foreach ($tokens as $token) {
    +            // Skip comment tokens
    +            if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
    +                continue;
    +            }
    +            // Remove extra whitespace in reserved words (e.g "OUTER     JOIN" becomes "OUTER JOIN")
    +            elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
    +                $token[self::TOKEN_VALUE] = preg_replace('/\s+/',' ',$token[self::TOKEN_VALUE]);
    +            }
    +
    +            if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_WHITESPACE) {
    +                // If the last token was whitespace, don't add another one
    +                if ($whitespace) {
    +                    continue;
    +                } else {
    +                    $whitespace = true;
    +                    // Convert all whitespace to a single space
    +                    $token[self::TOKEN_VALUE] = ' ';
    +                }
    +            } else {
    +                $whitespace = false;
    +            }
    +
    +            $result .= $token[self::TOKEN_VALUE];
    +        }
    +
    +        return rtrim($result);
    +    }
    +
    +    /**
    +     * Highlights a token depending on its type.
    +     *
    +     * @param Array $token An associative array containing type and value.
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightToken($token)
    +    {
    +        $type = $token[self::TOKEN_TYPE];
    +
    +        if (self::is_cli()) {
    +            $token = $token[self::TOKEN_VALUE];
    +        } else {
    +            $token = htmlentities($token[self::TOKEN_VALUE],ENT_COMPAT,'UTF-8');
    +        }
    +
    +        if ($type===self::TOKEN_TYPE_BOUNDARY) {
    +            return self::highlightBoundary($token);
    +        } elseif ($type===self::TOKEN_TYPE_WORD) {
    +            return self::highlightWord($token);
    +        } elseif ($type===self::TOKEN_TYPE_BACKTICK_QUOTE) {
    +            return self::highlightBacktickQuote($token);
    +        } elseif ($type===self::TOKEN_TYPE_QUOTE) {
    +            return self::highlightQuote($token);
    +        } elseif ($type===self::TOKEN_TYPE_RESERVED) {
    +            return self::highlightReservedWord($token);
    +        } elseif ($type===self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
    +            return self::highlightReservedWord($token);
    +        } elseif ($type===self::TOKEN_TYPE_RESERVED_NEWLINE) {
    +            return self::highlightReservedWord($token);
    +        } elseif ($type===self::TOKEN_TYPE_NUMBER) {
    +            return self::highlightNumber($token);
    +        } elseif ($type===self::TOKEN_TYPE_VARIABLE) {
    +            return self::highlightVariable($token);
    +        } elseif ($type===self::TOKEN_TYPE_COMMENT || $type===self::TOKEN_TYPE_BLOCK_COMMENT) {
    +            return self::highlightComment($token);
    +        }
    +
    +        return $token;
    +    }
    +
    +    /**
    +     * Highlights a quoted string
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightQuote($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_quote . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a backtick quoted string
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightBacktickQuote($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_backtick_quote . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a reserved word
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightReservedWord($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_reserved . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a boundary token
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightBoundary($value)
    +    {
    +        if ($value==='(' || $value===')') return $value;
    +
    +        if (self::is_cli()) {
    +            return self::$cli_boundary . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a number
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightNumber($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_number . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights an error
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightError($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_error . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a comment
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightComment($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_comment . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a word token
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightWord($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_word . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Highlights a variable token
    +     *
    +     * @param String $value The token's value
    +     *
    +     * @return String HTML code of the highlighted token.
    +     */
    +    protected static function highlightVariable($value)
    +    {
    +        if (self::is_cli()) {
    +            return self::$cli_variable . $value . "\x1b[0m";
    +        } else {
    +            return '' . $value . '';
    +        }
    +    }
    +
    +    /**
    +     * Helper function for building regular expressions for reserved words and boundary characters
    +     *
    +     * @param String $a The string to be quoted
    +     *
    +     * @return String The quoted string
    +     */
    +    private static function quote_regex($a)
    +    {
    +        return preg_quote($a,'/');
    +    }
    +
    +    /**
    +     * Helper function for building string output
    +     *
    +     * @param String $string The string to be quoted
    +     *
    +     * @return String The quoted string
    +     */
    +    private static function output($string)
    +    {
    +        if (self::is_cli()) {
    +            return $string."\n";
    +        } else {
    +            $string=trim($string);
    +            if (!self::$use_pre) {
    +                return $string;
    +            }
    +
    +            return '
    ' . $string . '
    '; + } + } + + private static function is_cli() + { + if (isset(self::$cli)) return self::$cli; + else return php_sapi_name() === 'cli'; + } + +} diff --git a/vendor/jdorn/sql-formatter/phpunit.xml.dist b/vendor/jdorn/sql-formatter/phpunit.xml.dist new file mode 100644 index 0000000..ebfda97 --- /dev/null +++ b/vendor/jdorn/sql-formatter/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./tests + + + + + + ./ + + ./tests + ./vendor + ./examples + + + + diff --git a/vendor/jdorn/sql-formatter/tests/SqlFormatterTest.php b/vendor/jdorn/sql-formatter/tests/SqlFormatterTest.php new file mode 100644 index 0000000..ca535cf --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/SqlFormatterTest.php @@ -0,0 +1,197 @@ +assertEquals(trim($html), trim(SqlFormatter::format($sql))); + } + /** + * @dataProvider formatData + */ + function testFormat($sql, $html) { + $this->assertEquals(trim($html), trim(SqlFormatter::format($sql, false))); + } + /** + * @dataProvider highlightData + */ + function testHighlight($sql, $html) { + $this->assertEquals(trim($html), trim(SqlFormatter::highlight($sql))); + } + /** + * @dataProvider highlightCliData + */ + function testCliHighlight($sql, $html) { + SqlFormatter::$cli = true; + $this->assertEquals(trim($html), trim(SqlFormatter::format($sql))); + SqlFormatter::$cli = false; + } + /** + * @dataProvider compressData + */ + function testCompress($sql, $html) { + $this->assertEquals(trim($html), trim(SqlFormatter::compress($sql))); + } + + function testUsePre() { + SqlFormatter::$use_pre = false; + $actual = SqlFormatter::highlight("test"); + $expected = 'test'; + $this->assertEquals($actual,$expected); + + SqlFormatter::$use_pre = true; + $actual = SqlFormatter::highlight("test"); + $expected = '
    test
    '; + $this->assertEquals($actual,$expected); + } + + function testSplitQuery() { + $expected = array( + "SELECT 'test' FROM MyTable;", + "SELECT Column2 FROM SomeOther Table WHERE (test = true);" + ); + + $actual = SqlFormatter::splitQuery(implode(';',$expected)); + + $this->assertEquals($expected, $actual); + } + + function testSplitQueryEmpty() { + $sql = "SELECT 1;SELECT 2;\n-- This is a comment\n;SELECT 3"; + $expected = array("SELECT 1;","SELECT 2;","SELECT 3"); + $actual = SqlFormatter::splitQuery($sql); + + $this->assertEquals($expected, $actual); + } + + function testRemoveComments() { + $expected = SqlFormatter::format("SELECT\n * FROM\n MyTable",false); + $sql = "/* this is a comment */SELECT#This is another comment\n * FROM-- One final comment\n MyTable"; + $actual = SqlFormatter::removeComments($sql); + + $this->assertEquals($expected, $actual); + } + + function testCacheStats() { + $stats = SqlFormatter::getCacheStats(); + $this->assertGreaterThan(1,$stats['hits']); + } + + function formatHighlightData() { + $formatHighlightData = explode("\n\n",file_get_contents(__DIR__."/format-highlight.html")); + $sqlData = $this->sqlData(); + + $return = array(); + foreach($formatHighlightData as $i=>$data) { + $return[] = array( + $sqlData[$i], + $data + ); + } + + return $return; + } + + function highlightCliData() { + $clidata = explode("\n\n",file_get_contents(__DIR__."/clihighlight.html")); + $sqlData = $this->sqlData(); + + $return = array(); + foreach($clidata as $i=>$data) { + $return[] = array( + $sqlData[$i], + $data + ); + } + + return $return; + } + + function formatData() { + $formatData = explode("\n\n",file_get_contents(__DIR__."/format.html")); + $sqlData = $this->sqlData(); + + $return = array(); + foreach($formatData as $i=>$data) { + $return[] = array( + $sqlData[$i], + $data + ); + } + + return $return; + } + + function compressData() { + $compressData = explode("\n\n",file_get_contents(__DIR__."/compress.html")); + $sqlData = $this->sqlData(); + + $return = array(); + foreach($compressData as $i=>$data) { + $return[] = array( + $sqlData[$i], + $data + ); + } + + return $return; + } + + function highlightData() { + $highlightData = explode("\n\n",file_get_contents(__DIR__."/highlight.html")); + $sqlData = $this->sqlData(); + + $return = array(); + foreach($highlightData as $i=>$data) { + $return[] = array( + $sqlData[$i], + $data + ); + } + + return $return; + } + + + + function sqlData() { + if(!$this->sqlData) { + $this->sqlData = explode("\n\n",file_get_contents(__DIR__."/sql.sql")); + } + + /** + $formatHighlight = array(); + $highlight = array(); + $format = array(); + $compress = array(); + $clihighlight = array(); + + foreach($this->sqlData as $sql) { + $formatHighlight[] = trim(SqlFormatter::format($sql)); + $highlight[] = trim(SqlFormatter::highlight($sql)); + $format[] = trim(SqlFormatter::format($sql, false)); + $compress[] = trim(SqlFormatter::compress($sql)); + + SqlFormatter::$cli = true; + $clihighlight[] = trim(SqlFormatter::format($sql)); + SqlFormatter::$cli = false; + } + + file_put_contents(__DIR__."/format-highlight.html", implode("\n\n",$formatHighlight)); + file_put_contents(__DIR__."/highlight.html", implode("\n\n",$highlight)); + file_put_contents(__DIR__."/format.html", implode("\n\n",$format)); + file_put_contents(__DIR__."/compress.html", implode("\n\n",$compress)); + file_put_contents(__DIR__."/clihighlight.html", implode("\n\n",$clihighlight)); + /**/ + + return $this->sqlData; + } + +} diff --git a/vendor/jdorn/sql-formatter/tests/clihighlight.html b/vendor/jdorn/sql-formatter/tests/clihighlight.html new file mode 100644 index 0000000..6ccfc12 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/clihighlight.html @@ -0,0 +1,809 @@ +SELECT + customer_id, + customer_name, + COUNT(order_id) as total +FROM + customers + INNER JOIN orders ON customers.customer_id = orders.customer_id +GROUP BY + customer_id, + customer_name +HAVING + COUNT(order_id) > 5 +ORDER BY + COUNT(order_id) DESC; + +UPDATE + customers +SET + totalorders = ordersummary.total +FROM + ( + SELECT + customer_id, + count(order_id) As total + FROM + orders + GROUP BY + customer_id + ) As ordersummary +WHERE + customers.customer_id = ordersummary.customer_id + +SELECT + * +FROM + sometable +UNION ALL +SELECT + * +FROM + someothertable; + +SET + NAMES 'utf8'; + +CREATE TABLE `PREFIX_address` ( + `id_address` int(10) unsigned NOT NULL auto_increment, + `id_country` int(10) unsigned NOT NULL, + `id_state` int(10) unsigned default NULL, + `id_customer` int(10) unsigned NOT NULL default '0', + `id_manufacturer` int(10) unsigned NOT NULL default '0', + `id_supplier` int(10) unsigned NOT NULL default '0', + `id_warehouse` int(10) unsigned NOT NULL default '0', + `alias` varchar(32) NOT NULL, + `company` varchar(64) default NULL, + `lastname` varchar(32) NOT NULL, + `firstname` varchar(32) NOT NULL, + `address1` varchar(128) NOT NULL, + `address2` varchar(128) default NULL, + `postcode` varchar(12) default NULL, + `city` varchar(64) NOT NULL, + `other` text, + `phone` varchar(16) default NULL, + `phone_mobile` varchar(16) default NULL, + `vat_number` varchar(32) default NULL, + `dni` varchar(16) DEFAULT NULL, + `date_add` datetime NOT NULL, + `date_upd` datetime NOT NULL, + `active` tinyint(1) unsigned NOT NULL default '1', + `deleted` tinyint(1) unsigned NOT NULL default '0', + PRIMARY KEY (`id_address`), + KEY `address_customer` (`id_customer`), + KEY `id_country` (`id_country`), + KEY `id_state` (`id_state`), + KEY `id_manufacturer` (`id_manufacturer`), + KEY `id_supplier` (`id_supplier`), + KEY `id_warehouse` (`id_warehouse`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +CREATE TABLE `PREFIX_alias` ( + `id_alias` int(10) unsigned NOT NULL auto_increment, + `alias` varchar(255) NOT NULL, + `search` varchar(255) NOT NULL, + `active` tinyint(1) NOT NULL default '1', + PRIMARY KEY (`id_alias`), + UNIQUE KEY `alias` (`alias`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +CREATE TABLE `PREFIX_carrier` ( + `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_reference` int(10) unsigned NOT NULL, + `id_tax_rules_group` int(10) unsigned DEFAULT '0', + `name` varchar(64) NOT NULL, + `url` varchar(255) DEFAULT NULL, + `active` tinyint(1) unsigned NOT NULL DEFAULT '0', + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', + `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', + `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', + `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', + `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', + `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', + `external_module_name` varchar(64) DEFAULT NULL, + `shipping_method` int(2) NOT NULL DEFAULT '0', + `position` int(10) unsigned NOT NULL default '0', + `max_width` int(10) DEFAULT 0, + `max_height` int(10) DEFAULT 0, + `max_depth` int(10) DEFAULT 0, + `max_weight` int(10) DEFAULT 0, + `grade` int(10) DEFAULT 0, + PRIMARY KEY (`id_carrier`), + KEY `deleted` (`deleted`, `active`), + KEY `id_tax_rules_group` (`id_tax_rules_group`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( + `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `id_shop` int(11) unsigned NOT NULL DEFAULT '1', + `id_currency` int(10) unsigned NOT NULL, + `id_country` int(10) unsigned NOT NULL, + `id_group` int(10) unsigned NOT NULL, + `from_quantity` mediumint(8) unsigned NOT NULL, + `price` DECIMAL(20, 6), + `reduction` decimal(20, 6) NOT NULL, + `reduction_type` enum('amount', 'percentage') NOT NULL, + `from` datetime NOT NULL, + `to` datetime NOT NULL, + PRIMARY KEY (`id_specific_price_rule`), + KEY `id_product` ( + `id_shop`, `id_currency`, `id_country`, + `id_group`, `from_quantity`, `from`, + `to` + ) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +UPDATE + `PREFIX_configuration` +SET + value = '6' +WHERE + name = 'PS_SEARCH_WEIGHT_PNAME' + +UPDATE + `PREFIX_hook_module` +SET + position = 1 +WHERE + id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayPayment' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'cheque' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayPaymentReturn' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'cheque' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayHome' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'homeslider' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionAuthentication' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statsdata' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionShopDataDuplication' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'homeslider' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayTop' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blocklanguages' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCustomerAccountAdd' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statsdata' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayCustomerAccount' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'favoriteproducts' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayAdminStatsModules' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statsvisits' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayAdminStatsGraphEngine' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'graphvisifire' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayAdminStatsGridEngine' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'gridhtml' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayLeftColumnProduct' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blocksharefb' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionSearch' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statssearch' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCategoryAdd' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCategoryUpdate' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCategoryDelete' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionAdminMetaSave' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayMyAccountBlock' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'favoriteproducts' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayFooter' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockreinsurance' + ) + +ALTER TABLE + `PREFIX_employee` +ADD + `bo_color` varchar(32) default NULL +AFTER + `stats_date_to` + +INSERT INTO `PREFIX_cms_category_lang` +VALUES + ( + 1, 3, 'Inicio', '', 'home', NULL, NULL, + NULL + ) + +INSERT INTO `PREFIX_cms_category` +VALUES + (1, 0, 0, 1, NOW(), NOW(), 0) + +UPDATE + `PREFIX_cms_category` +SET + `position` = 0 + +ALTER TABLE + `PREFIX_customer` +ADD + `note` text +AFTER + `secure_key` + +ALTER TABLE + `PREFIX_contact` +ADD + `customer_service` tinyint(1) NOT NULL DEFAULT 0 +AFTER + `email` + +INSERT INTO `PREFIX_specific_price` ( + `id_product`, `id_shop`, `id_currency`, + `id_country`, `id_group`, `priority`, + `price`, `from_quantity`, `reduction`, + `reduction_type`, `from`, `to` +) ( + SELECT + dq.`id_product`, + 1, + 1, + 0, + 1, + 0, + 0.00, + dq.`quantity`, + IF( + dq.`id_discount_type` = 2, dq.`value`, + dq.`value` / 100 + ), + IF ( + dq.`id_discount_type` = 2, 'amount', + 'percentage' + ), + '0000-00-00 00:00:00', + '0000-00-00 00:00:00' + FROM + `PREFIX_discount_quantity` dq + INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) +) + +DROP + TABLE `PREFIX_discount_quantity` + +INSERT INTO `PREFIX_specific_price` ( + `id_product`, `id_shop`, `id_currency`, + `id_country`, `id_group`, `priority`, + `price`, `from_quantity`, `reduction`, + `reduction_type`, `from`, `to` +) ( + SELECT + p.`id_product`, + 1, + 0, + 0, + 0, + 0, + 0.00, + 1, + IF( + p.`reduction_price` > 0, p.`reduction_price`, + p.`reduction_percent` / 100 + ), + IF( + p.`reduction_price` > 0, 'amount', + 'percentage' + ), + IF ( + p.`reduction_from` = p.`reduction_to`, + '0000-00-00 00:00:00', p.`reduction_from` + ), + IF ( + p.`reduction_from` = p.`reduction_to`, + '0000-00-00 00:00:00', p.`reduction_to` + ) + FROM + `PREFIX_product` p + WHERE + p.`reduction_price` + OR p.`reduction_percent` +) + +ALTER TABLE + `PREFIX_product` +DROP + `reduction_price`, +DROP + `reduction_percent`, +DROP + `reduction_from`, +DROP + `reduction_to` + +INSERT INTO `PREFIX_configuration` ( + `name`, `value`, `date_add`, `date_upd` +) +VALUES + ( + 'PS_SPECIFIC_PRICE_PRIORITIES', + 'id_shop;id_currency;id_country;id_group', + NOW(), NOW() + ), + ('PS_TAX_DISPLAY', 0, NOW(), NOW()), + ( + 'PS_SMARTY_FORCE_COMPILE', 1, NOW(), + NOW() + ), + ( + 'PS_DISTANCE_UNIT', 'km', NOW(), NOW() + ), + ( + 'PS_STORES_DISPLAY_CMS', 0, NOW(), + NOW() + ), + ( + 'PS_STORES_DISPLAY_FOOTER', 0, NOW(), + NOW() + ), + ( + 'PS_STORES_SIMPLIFIED', 0, NOW(), + NOW() + ), + ( + 'PS_STATSDATA_CUSTOMER_PAGESVIEWS', + 1, NOW(), NOW() + ), + ( + 'PS_STATSDATA_PAGESVIEWS', 1, NOW(), + NOW() + ), + ( + 'PS_STATSDATA_PLUGINS', 1, NOW(), + NOW() + ) + +INSERT INTO `PREFIX_configuration` ( + `name`, `value`, `date_add`, `date_upd` +) +VALUES + ( + 'PS_CONDITIONS_CMS_ID', + IFNULL( + ( + SELECT + `id_cms` + FROM + `PREFIX_cms` + WHERE + `id_cms` = 3 + ), + 0 + ), + NOW(), + NOW() + ) + +CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (`value` text) + +SET + @defaultOOS = ( + SELECT + value + FROM + `PREFIX_configuration` + WHERE + name = 'PS_ORDER_OUT_OF_STOCK' + ) + +UPDATE + `PREFIX_product` p +SET + `cache_default_attribute` = 0 +WHERE + `id_product` NOT IN ( + SELECT + `id_product` + FROM + `PREFIX_product_attribute` + ) + +INSERT INTO `PREFIX_hook` ( + `name`, `title`, `description`, `position` +) +VALUES + ( + 'processCarrier', 'Carrier Process', + NULL, 0 + ) + +INSERT INTO `PREFIX_stock_mvt_reason_lang` ( + `id_stock_mvt_reason`, `id_lang`, + `name` +) +VALUES + (1, 1, 'Order'), + (1, 2, 'Commande'), + (2, 1, 'Missing Stock Movement'), + ( + 2, 2, 'Mouvement de stock manquant' + ), + (3, 1, 'Restocking'), + (3, 2, 'Réassort') + +INSERT INTO `PREFIX_meta_lang` ( + `id_lang`, `id_meta`, `title`, `url_rewrite` +) +VALUES + ( + 1, + ( + SELECT + `id_meta` + FROM + `PREFIX_meta` + WHERE + `page` = 'authentication' + ), + 'Authentication', + 'authentication' + ), + ( + 2, + ( + SELECT + `id_meta` + FROM + `PREFIX_meta` + WHERE + `page` = 'authentication' + ), + 'Authentification', + 'authentification' + ), + ( + 3, + ( + SELECT + `id_meta` + FROM + `PREFIX_meta` + WHERE + `page` = 'authentication' + ), + 'Autenticación', + 'autenticacion' + ) + +LOCK TABLES `admin_assert` WRITE + +UNLOCK TABLES + +DROP + TABLE IF EXISTS `admin_role` + +SELECT + * +FROM + -- This is another comment + MyTable # One final comment + + /* This is a block comment + */ +WHERE + 1 = 2; + +SELECT + -- This is a test + +SELECT + Test +FROM + Test +WHERE + (MyColumn = 1) +) +AND ( + ( + (SomeOtherColumn = 2); +WARNING: unclosed parentheses or section + +SELECT + * +LIMIT + 1; +SELECT + a, + b, + c, + d +FROM + e +LIMIT + 1, 2; +SELECT + 1, + 2, + 3 +WHERE + a in (1, 2, 3, 4, 5) + and b = 5; + +SELECT + count - 50 +WHERE + a - 50 = b +WHERE + 1 + and -50 +WHERE + -50 = a +WHERE + a = -50 +WHERE + 1 + /*test*/ + -50 +WHERE + 1 + and -50; + +SELECT + @ + and b; + +SELECT + @"weird variable name"; + +SELECT + "no closing quote + \ No newline at end of file diff --git a/vendor/jdorn/sql-formatter/tests/compress.html b/vendor/jdorn/sql-formatter/tests/compress.html new file mode 100644 index 0000000..bb2fcf7 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/compress.html @@ -0,0 +1,77 @@ +SELECT customer_id, customer_name, COUNT(order_id) as total FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id GROUP BY customer_id, customer_name HAVING COUNT(order_id) > 5 ORDER BY COUNT(order_id) DESC; + +UPDATE customers SET totalorders = ordersummary.total FROM (SELECT customer_id, count(order_id) As total FROM orders GROUP BY customer_id) As ordersummary WHERE customers.customer_id = ordersummary.customer_id + +SELECT * FROM sometable UNION ALL SELECT * FROM someothertable; + +SET NAMES 'utf8'; + +CREATE TABLE `PREFIX_address` ( `id_address` int(10) unsigned NOT NULL auto_increment, `id_country` int(10) unsigned NOT NULL, `id_state` int(10) unsigned default NULL, `id_customer` int(10) unsigned NOT NULL default '0', `id_manufacturer` int(10) unsigned NOT NULL default '0', `id_supplier` int(10) unsigned NOT NULL default '0', `id_warehouse` int(10) unsigned NOT NULL default '0', `alias` varchar(32) NOT NULL, `company` varchar(64) default NULL, `lastname` varchar(32) NOT NULL, `firstname` varchar(32) NOT NULL, `address1` varchar(128) NOT NULL, `address2` varchar(128) default NULL, `postcode` varchar(12) default NULL, `city` varchar(64) NOT NULL, `other` text, `phone` varchar(16) default NULL, `phone_mobile` varchar(16) default NULL, `vat_number` varchar(32) default NULL, `dni` varchar(16) DEFAULT NULL, `date_add` datetime NOT NULL, `date_upd` datetime NOT NULL, `active` tinyint(1) unsigned NOT NULL default '1', `deleted` tinyint(1) unsigned NOT NULL default '0', PRIMARY KEY (`id_address`), KEY `address_customer` (`id_customer`), KEY `id_country` (`id_country`), KEY `id_state` (`id_state`), KEY `id_manufacturer` (`id_manufacturer`), KEY `id_supplier` (`id_supplier`), KEY `id_warehouse` (`id_warehouse`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +CREATE TABLE `PREFIX_alias` ( `id_alias` int(10) unsigned NOT NULL auto_increment, `alias` varchar(255) NOT NULL, `search` varchar(255) NOT NULL, `active` tinyint(1) NOT NULL default '1', PRIMARY KEY (`id_alias`), UNIQUE KEY `alias` (`alias`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +CREATE TABLE `PREFIX_carrier` ( `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, `id_reference` int(10) unsigned NOT NULL, `id_tax_rules_group` int(10) unsigned DEFAULT '0', `name` varchar(64) NOT NULL, `url` varchar(255) DEFAULT NULL, `active` tinyint(1) unsigned NOT NULL DEFAULT '0', `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', `external_module_name` varchar(64) DEFAULT NULL, `shipping_method` int(2) NOT NULL DEFAULT '0', `position` int(10) unsigned NOT NULL default '0', `max_width` int(10) DEFAULT 0, `max_height` int(10) DEFAULT 0, `max_depth` int(10) DEFAULT 0, `max_weight` int(10) DEFAULT 0, `grade` int(10) DEFAULT 0, PRIMARY KEY (`id_carrier`), KEY `deleted` (`deleted`,`active`), KEY `id_tax_rules_group` (`id_tax_rules_group`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `id_shop` int(11) unsigned NOT NULL DEFAULT '1', `id_currency` int(10) unsigned NOT NULL, `id_country` int(10) unsigned NOT NULL, `id_group` int(10) unsigned NOT NULL, `from_quantity` mediumint(8) unsigned NOT NULL, `price` DECIMAL(20,6), `reduction` decimal(20,6) NOT NULL, `reduction_type` enum('amount','percentage') NOT NULL, `from` datetime NOT NULL, `to` datetime NOT NULL, PRIMARY KEY (`id_specific_price_rule`), KEY `id_product` (`id_shop`,`id_currency`,`id_country`,`id_group`,`from_quantity`,`from`,`to`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +UPDATE `PREFIX_configuration` SET value = '6' WHERE name = 'PS_SEARCH_WEIGHT_PNAME' + +UPDATE `PREFIX_hook_module` SET position = 1 WHERE id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPayment') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPaymentReturn') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayHome') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAuthentication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionShopDataDuplication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayTop') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocklanguages') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCustomerAccountAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayCustomerAccount') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsModules') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsvisits') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGraphEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'graphvisifire') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGridEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'gridhtml') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayLeftColumnProduct') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocksharefb') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionSearch') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statssearch') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryUpdate') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryDelete') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAdminMetaSave') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayMyAccountBlock') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') OR id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayFooter') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockreinsurance') + +ALTER TABLE `PREFIX_employee` ADD `bo_color` varchar(32) default NULL AFTER `stats_date_to` + +INSERT INTO `PREFIX_cms_category_lang` VALUES(1, 3, 'Inicio', '', 'home', NULL, NULL, NULL) + +INSERT INTO `PREFIX_cms_category` VALUES(1, 0, 0, 1, NOW(), NOW(),0) + +UPDATE `PREFIX_cms_category` SET `position` = 0 + +ALTER TABLE `PREFIX_customer` ADD `note` text AFTER `secure_key` + +ALTER TABLE `PREFIX_contact` ADD `customer_service` tinyint(1) NOT NULL DEFAULT 0 AFTER `email` + +INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) ( SELECT dq.`id_product`, 1, 1, 0, 1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100), IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) ) + +DROP TABLE `PREFIX_discount_quantity` + +INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) ( SELECT p.`id_product`, 1, 0, 0, 0, 0, 0.00, 1, IF(p.`reduction_price` > 0, p.`reduction_price`, p.`reduction_percent` / 100), IF(p.`reduction_price` > 0, 'amount', 'percentage'), IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_from`), IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_to`) FROM `PREFIX_product` p WHERE p.`reduction_price` OR p.`reduction_percent` ) + +ALTER TABLE `PREFIX_product` DROP `reduction_price`, DROP `reduction_percent`, DROP `reduction_from`, DROP `reduction_to` + +INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_SPECIFIC_PRICE_PRIORITIES', 'id_shop;id_currency;id_country;id_group', NOW(), NOW()), ('PS_TAX_DISPLAY', 0, NOW(), NOW()), ('PS_SMARTY_FORCE_COMPILE', 1, NOW(), NOW()), ('PS_DISTANCE_UNIT', 'km', NOW(), NOW()), ('PS_STORES_DISPLAY_CMS', 0, NOW(), NOW()), ('PS_STORES_DISPLAY_FOOTER', 0, NOW(), NOW()), ('PS_STORES_SIMPLIFIED', 0, NOW(), NOW()), ('PS_STATSDATA_CUSTOMER_PAGESVIEWS', 1, NOW(), NOW()), ('PS_STATSDATA_PAGESVIEWS', 1, NOW(), NOW()), ('PS_STATSDATA_PLUGINS', 1, NOW(), NOW()) + +INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_CONDITIONS_CMS_ID', IFNULL((SELECT `id_cms` FROM `PREFIX_cms` WHERE `id_cms` = 3), 0), NOW(), NOW()) + +CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` ( `value` text ) + +SET @defaultOOS = (SELECT value FROM `PREFIX_configuration` WHERE name = 'PS_ORDER_OUT_OF_STOCK') + +UPDATE `PREFIX_product` p SET `cache_default_attribute` = 0 WHERE `id_product` NOT IN (SELECT `id_product` FROM `PREFIX_product_attribute`) + +INSERT INTO `PREFIX_hook` (`name`, `title`, `description`, `position`) VALUES ('processCarrier', 'Carrier Process', NULL, 0) + +INSERT INTO `PREFIX_stock_mvt_reason_lang` (`id_stock_mvt_reason`, `id_lang`, `name`) VALUES (1, 1, 'Order'), (1, 2, 'Commande'), (2, 1, 'Missing Stock Movement'), (2, 2, 'Mouvement de stock manquant'), (3, 1, 'Restocking'), (3, 2, 'Réassort') + +INSERT INTO `PREFIX_meta_lang` (`id_lang`, `id_meta`, `title`, `url_rewrite`) VALUES (1, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentication', 'authentication'), (2, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentification', 'authentification'), (3, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Autenticación', 'autenticacion') + +LOCK TABLES `admin_assert` WRITE + +UNLOCK TABLES + +DROP TABLE IF EXISTS `admin_role` + +SELECT * FROM MyTable WHERE 1 = 2; + +SELECT + +SELECT Test FROM Test WHERE ( MyColumn = 1 )) AND ((( SomeOtherColumn = 2); + +SELECT * LIMIT 1; SELECT a,b,c,d FROM e LIMIT 1, 2; SELECT 1,2,3 WHERE a in (1,2,3,4,5) and b=5; + +SELECT count - 50 WHERE a-50 = b WHERE 1 and - 50 WHERE -50 = a WHERE a = -50 WHERE 1 - 50 WHERE 1 and -50; + +SELECT @ and b; + +SELECT @"weird variable name"; + +SELECT "no closing quote \ No newline at end of file diff --git a/vendor/jdorn/sql-formatter/tests/format-highlight.html b/vendor/jdorn/sql-formatter/tests/format-highlight.html new file mode 100644 index 0000000..73af253 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/format-highlight.html @@ -0,0 +1,809 @@ +
    SELECT 
    +  customer_id, 
    +  customer_name, 
    +  COUNT(order_id) as total 
    +FROM 
    +  customers 
    +  INNER JOIN orders ON customers.customer_id = orders.customer_id 
    +GROUP BY 
    +  customer_id, 
    +  customer_name 
    +HAVING 
    +  COUNT(order_id) > 5 
    +ORDER BY 
    +  COUNT(order_id) DESC;
    + +
    UPDATE 
    +  customers 
    +SET 
    +  totalorders = ordersummary.total 
    +FROM 
    +  (
    +    SELECT 
    +      customer_id, 
    +      count(order_id) As total 
    +    FROM 
    +      orders 
    +    GROUP BY 
    +      customer_id
    +  ) As ordersummary 
    +WHERE 
    +  customers.customer_id = ordersummary.customer_id
    + +
    SELECT 
    +  * 
    +FROM 
    +  sometable 
    +UNION ALL 
    +SELECT 
    +  * 
    +FROM 
    +  someothertable;
    + +
    SET 
    +  NAMES 'utf8';
    + +
    CREATE TABLE `PREFIX_address` (
    +  `id_address` int(10) unsigned NOT NULL auto_increment, 
    +  `id_country` int(10) unsigned NOT NULL, 
    +  `id_state` int(10) unsigned default NULL, 
    +  `id_customer` int(10) unsigned NOT NULL default '0', 
    +  `id_manufacturer` int(10) unsigned NOT NULL default '0', 
    +  `id_supplier` int(10) unsigned NOT NULL default '0', 
    +  `id_warehouse` int(10) unsigned NOT NULL default '0', 
    +  `alias` varchar(32) NOT NULL, 
    +  `company` varchar(64) default NULL, 
    +  `lastname` varchar(32) NOT NULL, 
    +  `firstname` varchar(32) NOT NULL, 
    +  `address1` varchar(128) NOT NULL, 
    +  `address2` varchar(128) default NULL, 
    +  `postcode` varchar(12) default NULL, 
    +  `city` varchar(64) NOT NULL, 
    +  `other` text, 
    +  `phone` varchar(16) default NULL, 
    +  `phone_mobile` varchar(16) default NULL, 
    +  `vat_number` varchar(32) default NULL, 
    +  `dni` varchar(16) DEFAULT NULL, 
    +  `date_add` datetime NOT NULL, 
    +  `date_upd` datetime NOT NULL, 
    +  `active` tinyint(1) unsigned NOT NULL default '1', 
    +  `deleted` tinyint(1) unsigned NOT NULL default '0', 
    +  PRIMARY KEY (`id_address`), 
    +  KEY `address_customer` (`id_customer`), 
    +  KEY `id_country` (`id_country`), 
    +  KEY `id_state` (`id_state`), 
    +  KEY `id_manufacturer` (`id_manufacturer`), 
    +  KEY `id_supplier` (`id_supplier`), 
    +  KEY `id_warehouse` (`id_warehouse`)
    +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
    + +
    CREATE TABLE `PREFIX_alias` (
    +  `id_alias` int(10) unsigned NOT NULL auto_increment, 
    +  `alias` varchar(255) NOT NULL, 
    +  `search` varchar(255) NOT NULL, 
    +  `active` tinyint(1) NOT NULL default '1', 
    +  PRIMARY KEY (`id_alias`), 
    +  UNIQUE KEY `alias` (`alias`)
    +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
    + +
    CREATE TABLE `PREFIX_carrier` (
    +  `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    +  `id_reference` int(10) unsigned NOT NULL, 
    +  `id_tax_rules_group` int(10) unsigned DEFAULT '0', 
    +  `name` varchar(64) NOT NULL, 
    +  `url` varchar(255) DEFAULT NULL, 
    +  `active` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', 
    +  `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    +  `external_module_name` varchar(64) DEFAULT NULL, 
    +  `shipping_method` int(2) NOT NULL DEFAULT '0', 
    +  `position` int(10) unsigned NOT NULL default '0', 
    +  `max_width` int(10) DEFAULT 0, 
    +  `max_height` int(10) DEFAULT 0, 
    +  `max_depth` int(10) DEFAULT 0, 
    +  `max_weight` int(10) DEFAULT 0, 
    +  `grade` int(10) DEFAULT 0, 
    +  PRIMARY KEY (`id_carrier`), 
    +  KEY `deleted` (`deleted`, `active`), 
    +  KEY `id_tax_rules_group` (`id_tax_rules_group`)
    +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
    + +
    CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` (
    +  `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    +  `name` VARCHAR(255) NOT NULL, 
    +  `id_shop` int(11) unsigned NOT NULL DEFAULT '1', 
    +  `id_currency` int(10) unsigned NOT NULL, 
    +  `id_country` int(10) unsigned NOT NULL, 
    +  `id_group` int(10) unsigned NOT NULL, 
    +  `from_quantity` mediumint(8) unsigned NOT NULL, 
    +  `price` DECIMAL(20, 6), 
    +  `reduction` decimal(20, 6) NOT NULL, 
    +  `reduction_type` enum('amount', 'percentage') NOT NULL, 
    +  `from` datetime NOT NULL, 
    +  `to` datetime NOT NULL, 
    +  PRIMARY KEY (`id_specific_price_rule`), 
    +  KEY `id_product` (
    +    `id_shop`, `id_currency`, `id_country`, 
    +    `id_group`, `from_quantity`, `from`, 
    +    `to`
    +  )
    +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
    + +
    UPDATE 
    +  `PREFIX_configuration` 
    +SET 
    +  value = '6' 
    +WHERE 
    +  name = 'PS_SEARCH_WEIGHT_PNAME'
    + +
    UPDATE 
    +  `PREFIX_hook_module` 
    +SET 
    +  position = 1 
    +WHERE 
    +  id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayPayment'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'cheque'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayPaymentReturn'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'cheque'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayHome'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'homeslider'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionAuthentication'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'statsdata'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionShopDataDuplication'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'homeslider'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayTop'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blocklanguages'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionCustomerAccountAdd'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'statsdata'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayCustomerAccount'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'favoriteproducts'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayAdminStatsModules'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'statsvisits'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayAdminStatsGraphEngine'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'graphvisifire'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayAdminStatsGridEngine'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'gridhtml'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayLeftColumnProduct'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blocksharefb'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionSearch'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'statssearch'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionCategoryAdd'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blockcategories'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionCategoryUpdate'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blockcategories'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionCategoryDelete'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blockcategories'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'actionAdminMetaSave'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blockcategories'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayMyAccountBlock'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'favoriteproducts'
    +  ) 
    +  OR id_hook = (
    +    SELECT 
    +      id_hook 
    +    FROM 
    +      `PREFIX_hook` 
    +    WHERE 
    +      name = 'displayFooter'
    +  ) 
    +  AND id_module = (
    +    SELECT 
    +      id_module 
    +    FROM 
    +      `PREFIX_module` 
    +    WHERE 
    +      name = 'blockreinsurance'
    +  )
    + +
    ALTER TABLE 
    +  `PREFIX_employee` 
    +ADD 
    +  `bo_color` varchar(32) default NULL 
    +AFTER 
    +  `stats_date_to`
    + +
    INSERT INTO `PREFIX_cms_category_lang` 
    +VALUES 
    +  (
    +    1, 3, 'Inicio', '', 'home', NULL, NULL, 
    +    NULL
    +  )
    + +
    INSERT INTO `PREFIX_cms_category` 
    +VALUES 
    +  (1, 0, 0, 1, NOW(), NOW(), 0)
    + +
    UPDATE 
    +  `PREFIX_cms_category` 
    +SET 
    +  `position` = 0
    + +
    ALTER TABLE 
    +  `PREFIX_customer` 
    +ADD 
    +  `note` text 
    +AFTER 
    +  `secure_key`
    + +
    ALTER TABLE 
    +  `PREFIX_contact` 
    +ADD 
    +  `customer_service` tinyint(1) NOT NULL DEFAULT 0 
    +AFTER 
    +  `email`
    + +
    INSERT INTO `PREFIX_specific_price` (
    +  `id_product`, `id_shop`, `id_currency`, 
    +  `id_country`, `id_group`, `priority`, 
    +  `price`, `from_quantity`, `reduction`, 
    +  `reduction_type`, `from`, `to`
    +) (
    +  SELECT 
    +    dq.`id_product`, 
    +    1, 
    +    1, 
    +    0, 
    +    1, 
    +    0, 
    +    0.00, 
    +    dq.`quantity`, 
    +    IF(
    +      dq.`id_discount_type` = 2, dq.`value`, 
    +      dq.`value` / 100
    +    ), 
    +    IF (
    +      dq.`id_discount_type` = 2, 'amount', 
    +      'percentage'
    +    ), 
    +    '0000-00-00 00:00:00', 
    +    '0000-00-00 00:00:00' 
    +  FROM 
    +    `PREFIX_discount_quantity` dq 
    +    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)
    +)
    + +
    DROP 
    +  TABLE `PREFIX_discount_quantity`
    + +
    INSERT INTO `PREFIX_specific_price` (
    +  `id_product`, `id_shop`, `id_currency`, 
    +  `id_country`, `id_group`, `priority`, 
    +  `price`, `from_quantity`, `reduction`, 
    +  `reduction_type`, `from`, `to`
    +) (
    +  SELECT 
    +    p.`id_product`, 
    +    1, 
    +    0, 
    +    0, 
    +    0, 
    +    0, 
    +    0.00, 
    +    1, 
    +    IF(
    +      p.`reduction_price` > 0, p.`reduction_price`, 
    +      p.`reduction_percent` / 100
    +    ), 
    +    IF(
    +      p.`reduction_price` > 0, 'amount', 
    +      'percentage'
    +    ), 
    +    IF (
    +      p.`reduction_from` = p.`reduction_to`, 
    +      '0000-00-00 00:00:00', p.`reduction_from`
    +    ), 
    +    IF (
    +      p.`reduction_from` = p.`reduction_to`, 
    +      '0000-00-00 00:00:00', p.`reduction_to`
    +    ) 
    +  FROM 
    +    `PREFIX_product` p 
    +  WHERE 
    +    p.`reduction_price` 
    +    OR p.`reduction_percent`
    +)
    + +
    ALTER TABLE 
    +  `PREFIX_product` 
    +DROP 
    +  `reduction_price`, 
    +DROP 
    +  `reduction_percent`, 
    +DROP 
    +  `reduction_from`, 
    +DROP 
    +  `reduction_to`
    + +
    INSERT INTO `PREFIX_configuration` (
    +  `name`, `value`, `date_add`, `date_upd`
    +) 
    +VALUES 
    +  (
    +    'PS_SPECIFIC_PRICE_PRIORITIES', 
    +    'id_shop;id_currency;id_country;id_group', 
    +    NOW(), NOW()
    +  ), 
    +  ('PS_TAX_DISPLAY', 0, NOW(), NOW()), 
    +  (
    +    'PS_SMARTY_FORCE_COMPILE', 1, NOW(), 
    +    NOW()
    +  ), 
    +  (
    +    'PS_DISTANCE_UNIT', 'km', NOW(), NOW()
    +  ), 
    +  (
    +    'PS_STORES_DISPLAY_CMS', 0, NOW(), 
    +    NOW()
    +  ), 
    +  (
    +    'PS_STORES_DISPLAY_FOOTER', 0, NOW(), 
    +    NOW()
    +  ), 
    +  (
    +    'PS_STORES_SIMPLIFIED', 0, NOW(), 
    +    NOW()
    +  ), 
    +  (
    +    'PS_STATSDATA_CUSTOMER_PAGESVIEWS', 
    +    1, NOW(), NOW()
    +  ), 
    +  (
    +    'PS_STATSDATA_PAGESVIEWS', 1, NOW(), 
    +    NOW()
    +  ), 
    +  (
    +    'PS_STATSDATA_PLUGINS', 1, NOW(), 
    +    NOW()
    +  )
    + +
    INSERT INTO `PREFIX_configuration` (
    +  `name`, `value`, `date_add`, `date_upd`
    +) 
    +VALUES 
    +  (
    +    'PS_CONDITIONS_CMS_ID', 
    +    IFNULL(
    +      (
    +        SELECT 
    +          `id_cms` 
    +        FROM 
    +          `PREFIX_cms` 
    +        WHERE 
    +          `id_cms` = 3
    +      ), 
    +      0
    +    ), 
    +    NOW(), 
    +    NOW()
    +  )
    + +
    CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (`value` text)
    + +
    SET 
    +  @defaultOOS = (
    +    SELECT 
    +      value 
    +    FROM 
    +      `PREFIX_configuration` 
    +    WHERE 
    +      name = 'PS_ORDER_OUT_OF_STOCK'
    +  )
    + +
    UPDATE 
    +  `PREFIX_product` p 
    +SET 
    +  `cache_default_attribute` = 0 
    +WHERE 
    +  `id_product` NOT IN (
    +    SELECT 
    +      `id_product` 
    +    FROM 
    +      `PREFIX_product_attribute`
    +  )
    + +
    INSERT INTO `PREFIX_hook` (
    +  `name`, `title`, `description`, `position`
    +) 
    +VALUES 
    +  (
    +    'processCarrier', 'Carrier Process', 
    +    NULL, 0
    +  )
    + +
    INSERT INTO `PREFIX_stock_mvt_reason_lang` (
    +  `id_stock_mvt_reason`, `id_lang`, 
    +  `name`
    +) 
    +VALUES 
    +  (1, 1, 'Order'), 
    +  (1, 2, 'Commande'), 
    +  (2, 1, 'Missing Stock Movement'), 
    +  (
    +    2, 2, 'Mouvement de stock manquant'
    +  ), 
    +  (3, 1, 'Restocking'), 
    +  (3, 2, 'Réassort')
    + +
    INSERT INTO `PREFIX_meta_lang` (
    +  `id_lang`, `id_meta`, `title`, `url_rewrite`
    +) 
    +VALUES 
    +  (
    +    1, 
    +    (
    +      SELECT 
    +        `id_meta` 
    +      FROM 
    +        `PREFIX_meta` 
    +      WHERE 
    +        `page` = 'authentication'
    +    ), 
    +    'Authentication', 
    +    'authentication'
    +  ), 
    +  (
    +    2, 
    +    (
    +      SELECT 
    +        `id_meta` 
    +      FROM 
    +        `PREFIX_meta` 
    +      WHERE 
    +        `page` = 'authentication'
    +    ), 
    +    'Authentification', 
    +    'authentification'
    +  ), 
    +  (
    +    3, 
    +    (
    +      SELECT 
    +        `id_meta` 
    +      FROM 
    +        `PREFIX_meta` 
    +      WHERE 
    +        `page` = 'authentication'
    +    ), 
    +    'Autenticación', 
    +    'autenticacion'
    +  )
    + +
    LOCK TABLES `admin_assert` WRITE
    + +
    UNLOCK TABLES
    + +
    DROP 
    +  TABLE IF EXISTS `admin_role`
    + +
    SELECT 
    +  * 
    +FROM 
    +  -- This is another comment
    +  MyTable # One final comment
    +  
    +  /* This is a block comment 
    +  */
    +WHERE 
    +  1 = 2;
    + +
    SELECT 
    +  -- This is a test
    + +
    SELECT 
    +  Test 
    +FROM 
    +  Test 
    +WHERE 
    +  (MyColumn = 1)
    +)
    +AND (
    +  (
    +    (SomeOtherColumn = 2); 
    +WARNING: unclosed parentheses or section
    + +
    SELECT 
    +  * 
    +LIMIT 
    +  1; 
    +SELECT 
    +  a, 
    +  b, 
    +  c, 
    +  d 
    +FROM 
    +  e 
    +LIMIT 
    +  1, 2; 
    +SELECT 
    +  1, 
    +  2, 
    +  3 
    +WHERE 
    +  a in (1, 2, 3, 4, 5) 
    +  and b = 5;
    + +
    SELECT 
    +  count - 50 
    +WHERE 
    +  a - 50 = b 
    +WHERE 
    +  1 
    +  and -50 
    +WHERE 
    +  -50 = a 
    +WHERE 
    +  a = -50 
    +WHERE 
    +  1 
    +  /*test*/
    +  -50 
    +WHERE 
    +  1 
    +  and -50;
    + +
    SELECT 
    +  @ 
    +  and b;
    + +
    SELECT 
    +  @"weird variable name";
    + +
    SELECT 
    +  "no closing quote
    +
    \ No newline at end of file diff --git a/vendor/jdorn/sql-formatter/tests/format.html b/vendor/jdorn/sql-formatter/tests/format.html new file mode 100644 index 0000000..dcbac03 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/format.html @@ -0,0 +1,807 @@ +SELECT + customer_id, + customer_name, + COUNT(order_id) as total +FROM + customers + INNER JOIN orders ON customers.customer_id = orders.customer_id +GROUP BY + customer_id, + customer_name +HAVING + COUNT(order_id) > 5 +ORDER BY + COUNT(order_id) DESC; + +UPDATE + customers +SET + totalorders = ordersummary.total +FROM + ( + SELECT + customer_id, + count(order_id) As total + FROM + orders + GROUP BY + customer_id + ) As ordersummary +WHERE + customers.customer_id = ordersummary.customer_id + +SELECT + * +FROM + sometable +UNION ALL +SELECT + * +FROM + someothertable; + +SET + NAMES 'utf8'; + +CREATE TABLE `PREFIX_address` ( + `id_address` int(10) unsigned NOT NULL auto_increment, + `id_country` int(10) unsigned NOT NULL, + `id_state` int(10) unsigned default NULL, + `id_customer` int(10) unsigned NOT NULL default '0', + `id_manufacturer` int(10) unsigned NOT NULL default '0', + `id_supplier` int(10) unsigned NOT NULL default '0', + `id_warehouse` int(10) unsigned NOT NULL default '0', + `alias` varchar(32) NOT NULL, + `company` varchar(64) default NULL, + `lastname` varchar(32) NOT NULL, + `firstname` varchar(32) NOT NULL, + `address1` varchar(128) NOT NULL, + `address2` varchar(128) default NULL, + `postcode` varchar(12) default NULL, + `city` varchar(64) NOT NULL, + `other` text, + `phone` varchar(16) default NULL, + `phone_mobile` varchar(16) default NULL, + `vat_number` varchar(32) default NULL, + `dni` varchar(16) DEFAULT NULL, + `date_add` datetime NOT NULL, + `date_upd` datetime NOT NULL, + `active` tinyint(1) unsigned NOT NULL default '1', + `deleted` tinyint(1) unsigned NOT NULL default '0', + PRIMARY KEY (`id_address`), + KEY `address_customer` (`id_customer`), + KEY `id_country` (`id_country`), + KEY `id_state` (`id_state`), + KEY `id_manufacturer` (`id_manufacturer`), + KEY `id_supplier` (`id_supplier`), + KEY `id_warehouse` (`id_warehouse`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +CREATE TABLE `PREFIX_alias` ( + `id_alias` int(10) unsigned NOT NULL auto_increment, + `alias` varchar(255) NOT NULL, + `search` varchar(255) NOT NULL, + `active` tinyint(1) NOT NULL default '1', + PRIMARY KEY (`id_alias`), + UNIQUE KEY `alias` (`alias`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +CREATE TABLE `PREFIX_carrier` ( + `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_reference` int(10) unsigned NOT NULL, + `id_tax_rules_group` int(10) unsigned DEFAULT '0', + `name` varchar(64) NOT NULL, + `url` varchar(255) DEFAULT NULL, + `active` tinyint(1) unsigned NOT NULL DEFAULT '0', + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', + `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', + `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', + `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', + `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', + `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', + `external_module_name` varchar(64) DEFAULT NULL, + `shipping_method` int(2) NOT NULL DEFAULT '0', + `position` int(10) unsigned NOT NULL default '0', + `max_width` int(10) DEFAULT 0, + `max_height` int(10) DEFAULT 0, + `max_depth` int(10) DEFAULT 0, + `max_weight` int(10) DEFAULT 0, + `grade` int(10) DEFAULT 0, + PRIMARY KEY (`id_carrier`), + KEY `deleted` (`deleted`, `active`), + KEY `id_tax_rules_group` (`id_tax_rules_group`) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( + `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `id_shop` int(11) unsigned NOT NULL DEFAULT '1', + `id_currency` int(10) unsigned NOT NULL, + `id_country` int(10) unsigned NOT NULL, + `id_group` int(10) unsigned NOT NULL, + `from_quantity` mediumint(8) unsigned NOT NULL, + `price` DECIMAL(20, 6), + `reduction` decimal(20, 6) NOT NULL, + `reduction_type` enum('amount', 'percentage') NOT NULL, + `from` datetime NOT NULL, + `to` datetime NOT NULL, + PRIMARY KEY (`id_specific_price_rule`), + KEY `id_product` ( + `id_shop`, `id_currency`, `id_country`, + `id_group`, `from_quantity`, `from`, + `to` + ) +) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8 + +UPDATE + `PREFIX_configuration` +SET + value = '6' +WHERE + name = 'PS_SEARCH_WEIGHT_PNAME' + +UPDATE + `PREFIX_hook_module` +SET + position = 1 +WHERE + id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayPayment' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'cheque' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayPaymentReturn' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'cheque' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayHome' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'homeslider' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionAuthentication' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statsdata' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionShopDataDuplication' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'homeslider' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayTop' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blocklanguages' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCustomerAccountAdd' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statsdata' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayCustomerAccount' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'favoriteproducts' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayAdminStatsModules' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statsvisits' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayAdminStatsGraphEngine' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'graphvisifire' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayAdminStatsGridEngine' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'gridhtml' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayLeftColumnProduct' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blocksharefb' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionSearch' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'statssearch' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCategoryAdd' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCategoryUpdate' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionCategoryDelete' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'actionAdminMetaSave' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockcategories' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayMyAccountBlock' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'favoriteproducts' + ) + OR id_hook = ( + SELECT + id_hook + FROM + `PREFIX_hook` + WHERE + name = 'displayFooter' + ) + AND id_module = ( + SELECT + id_module + FROM + `PREFIX_module` + WHERE + name = 'blockreinsurance' + ) + +ALTER TABLE + `PREFIX_employee` +ADD + `bo_color` varchar(32) default NULL +AFTER + `stats_date_to` + +INSERT INTO `PREFIX_cms_category_lang` +VALUES + ( + 1, 3, 'Inicio', '', 'home', NULL, NULL, + NULL + ) + +INSERT INTO `PREFIX_cms_category` +VALUES + (1, 0, 0, 1, NOW(), NOW(), 0) + +UPDATE + `PREFIX_cms_category` +SET + `position` = 0 + +ALTER TABLE + `PREFIX_customer` +ADD + `note` text +AFTER + `secure_key` + +ALTER TABLE + `PREFIX_contact` +ADD + `customer_service` tinyint(1) NOT NULL DEFAULT 0 +AFTER + `email` + +INSERT INTO `PREFIX_specific_price` ( + `id_product`, `id_shop`, `id_currency`, + `id_country`, `id_group`, `priority`, + `price`, `from_quantity`, `reduction`, + `reduction_type`, `from`, `to` +) ( + SELECT + dq.`id_product`, + 1, + 1, + 0, + 1, + 0, + 0.00, + dq.`quantity`, + IF( + dq.`id_discount_type` = 2, dq.`value`, + dq.`value` / 100 + ), + IF ( + dq.`id_discount_type` = 2, 'amount', + 'percentage' + ), + '0000-00-00 00:00:00', + '0000-00-00 00:00:00' + FROM + `PREFIX_discount_quantity` dq + INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) +) + +DROP + TABLE `PREFIX_discount_quantity` + +INSERT INTO `PREFIX_specific_price` ( + `id_product`, `id_shop`, `id_currency`, + `id_country`, `id_group`, `priority`, + `price`, `from_quantity`, `reduction`, + `reduction_type`, `from`, `to` +) ( + SELECT + p.`id_product`, + 1, + 0, + 0, + 0, + 0, + 0.00, + 1, + IF( + p.`reduction_price` > 0, p.`reduction_price`, + p.`reduction_percent` / 100 + ), + IF( + p.`reduction_price` > 0, 'amount', + 'percentage' + ), + IF ( + p.`reduction_from` = p.`reduction_to`, + '0000-00-00 00:00:00', p.`reduction_from` + ), + IF ( + p.`reduction_from` = p.`reduction_to`, + '0000-00-00 00:00:00', p.`reduction_to` + ) + FROM + `PREFIX_product` p + WHERE + p.`reduction_price` + OR p.`reduction_percent` +) + +ALTER TABLE + `PREFIX_product` +DROP + `reduction_price`, +DROP + `reduction_percent`, +DROP + `reduction_from`, +DROP + `reduction_to` + +INSERT INTO `PREFIX_configuration` ( + `name`, `value`, `date_add`, `date_upd` +) +VALUES + ( + 'PS_SPECIFIC_PRICE_PRIORITIES', + 'id_shop;id_currency;id_country;id_group', + NOW(), NOW() + ), + ('PS_TAX_DISPLAY', 0, NOW(), NOW()), + ( + 'PS_SMARTY_FORCE_COMPILE', 1, NOW(), + NOW() + ), + ( + 'PS_DISTANCE_UNIT', 'km', NOW(), NOW() + ), + ( + 'PS_STORES_DISPLAY_CMS', 0, NOW(), + NOW() + ), + ( + 'PS_STORES_DISPLAY_FOOTER', 0, NOW(), + NOW() + ), + ( + 'PS_STORES_SIMPLIFIED', 0, NOW(), + NOW() + ), + ( + 'PS_STATSDATA_CUSTOMER_PAGESVIEWS', + 1, NOW(), NOW() + ), + ( + 'PS_STATSDATA_PAGESVIEWS', 1, NOW(), + NOW() + ), + ( + 'PS_STATSDATA_PLUGINS', 1, NOW(), + NOW() + ) + +INSERT INTO `PREFIX_configuration` ( + `name`, `value`, `date_add`, `date_upd` +) +VALUES + ( + 'PS_CONDITIONS_CMS_ID', + IFNULL( + ( + SELECT + `id_cms` + FROM + `PREFIX_cms` + WHERE + `id_cms` = 3 + ), + 0 + ), + NOW(), + NOW() + ) + +CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (`value` text) + +SET + @defaultOOS = ( + SELECT + value + FROM + `PREFIX_configuration` + WHERE + name = 'PS_ORDER_OUT_OF_STOCK' + ) + +UPDATE + `PREFIX_product` p +SET + `cache_default_attribute` = 0 +WHERE + `id_product` NOT IN ( + SELECT + `id_product` + FROM + `PREFIX_product_attribute` + ) + +INSERT INTO `PREFIX_hook` ( + `name`, `title`, `description`, `position` +) +VALUES + ( + 'processCarrier', 'Carrier Process', + NULL, 0 + ) + +INSERT INTO `PREFIX_stock_mvt_reason_lang` ( + `id_stock_mvt_reason`, `id_lang`, + `name` +) +VALUES + (1, 1, 'Order'), + (1, 2, 'Commande'), + (2, 1, 'Missing Stock Movement'), + ( + 2, 2, 'Mouvement de stock manquant' + ), + (3, 1, 'Restocking'), + (3, 2, 'Réassort') + +INSERT INTO `PREFIX_meta_lang` ( + `id_lang`, `id_meta`, `title`, `url_rewrite` +) +VALUES + ( + 1, + ( + SELECT + `id_meta` + FROM + `PREFIX_meta` + WHERE + `page` = 'authentication' + ), + 'Authentication', + 'authentication' + ), + ( + 2, + ( + SELECT + `id_meta` + FROM + `PREFIX_meta` + WHERE + `page` = 'authentication' + ), + 'Authentification', + 'authentification' + ), + ( + 3, + ( + SELECT + `id_meta` + FROM + `PREFIX_meta` + WHERE + `page` = 'authentication' + ), + 'Autenticación', + 'autenticacion' + ) + +LOCK TABLES `admin_assert` WRITE + +UNLOCK TABLES + +DROP + TABLE IF EXISTS `admin_role` + +SELECT + * +FROM + -- This is another comment + MyTable # One final comment + + /* This is a block comment + */ +WHERE + 1 = 2; + +SELECT + -- This is a test + +SELECT + Test +FROM + Test +WHERE + (MyColumn = 1) +) +AND ( + ( + (SomeOtherColumn = 2); + +SELECT + * +LIMIT + 1; +SELECT + a, + b, + c, + d +FROM + e +LIMIT + 1, 2; +SELECT + 1, + 2, + 3 +WHERE + a in (1, 2, 3, 4, 5) + and b = 5; + +SELECT + count - 50 +WHERE + a - 50 = b +WHERE + 1 + and -50 +WHERE + -50 = a +WHERE + a = -50 +WHERE + 1 + /*test*/ + -50 +WHERE + 1 + and -50; + +SELECT + @ + and b; + +SELECT + @"weird variable name"; + +SELECT + "no closing quote \ No newline at end of file diff --git a/vendor/jdorn/sql-formatter/tests/highlight.html b/vendor/jdorn/sql-formatter/tests/highlight.html new file mode 100644 index 0000000..e26af9a --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/highlight.html @@ -0,0 +1,262 @@ +
    SELECT customer_id, customer_name, COUNT(order_id) as total
    +FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id
    +GROUP BY customer_id, customer_name
    +HAVING COUNT(order_id) > 5
    +ORDER BY COUNT(order_id) DESC;
    + +
    UPDATE customers
    +        SET totalorders = ordersummary.total
    +        FROM (SELECT customer_id, count(order_id) As total 
    +FROM orders GROUP BY customer_id) As ordersummary
    +        WHERE customers.customer_id = ordersummary.customer_id
    + +
    SELECT * FROM sometable
    +UNION ALL
    +SELECT * FROM someothertable;
    + +
    SET NAMES 'utf8';
    + +
    CREATE TABLE `PREFIX_address` (
    +  `id_address` int(10) unsigned NOT NULL auto_increment,
    +  `id_country` int(10) unsigned NOT NULL,
    +  `id_state` int(10) unsigned default NULL,
    +  `id_customer` int(10) unsigned NOT NULL default '0',
    +  `id_manufacturer` int(10) unsigned NOT NULL default '0',
    +  `id_supplier` int(10) unsigned NOT NULL default '0',
    +  `id_warehouse` int(10) unsigned NOT NULL default '0',
    +  `alias` varchar(32) NOT NULL,
    +  `company` varchar(64) default NULL,
    +  `lastname` varchar(32) NOT NULL,
    +  `firstname` varchar(32) NOT NULL,
    +  `address1` varchar(128) NOT NULL,
    +  `address2` varchar(128) default NULL,
    +  `postcode` varchar(12) default NULL,
    +  `city` varchar(64) NOT NULL,
    +  `other` text,
    +  `phone` varchar(16) default NULL,
    +  `phone_mobile` varchar(16) default NULL,
    +  `vat_number` varchar(32) default NULL,
    +  `dni` varchar(16) DEFAULT NULL,
    +  `date_add` datetime NOT NULL,
    +  `date_upd` datetime NOT NULL,
    +  `active` tinyint(1) unsigned NOT NULL default '1',
    +  `deleted` tinyint(1) unsigned NOT NULL default '0',
    +  PRIMARY KEY (`id_address`),
    +  KEY `address_customer` (`id_customer`),
    +  KEY `id_country` (`id_country`),
    +  KEY `id_state` (`id_state`),
    +  KEY `id_manufacturer` (`id_manufacturer`),
    +  KEY `id_supplier` (`id_supplier`),
    +  KEY `id_warehouse` (`id_warehouse`)
    +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8
    + +
    CREATE TABLE `PREFIX_alias` (
    +  `id_alias` int(10) unsigned NOT NULL auto_increment,
    +  `alias` varchar(255) NOT NULL,
    +  `search` varchar(255) NOT NULL,
    +  `active` tinyint(1) NOT NULL default '1',
    +  PRIMARY KEY (`id_alias`),
    +  UNIQUE KEY `alias` (`alias`)
    +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8
    + +
    CREATE TABLE `PREFIX_carrier` (
    +  `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT,
    +  `id_reference` int(10) unsigned NOT NULL,
    +  `id_tax_rules_group` int(10) unsigned DEFAULT '0',
    +  `name` varchar(64) NOT NULL,
    +  `url` varchar(255) DEFAULT NULL,
    +  `active` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1',
    +  `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0',
    +  `external_module_name` varchar(64) DEFAULT NULL,
    +  `shipping_method` int(2) NOT NULL DEFAULT '0',
    +  `position` int(10) unsigned NOT NULL default '0',
    +  `max_width` int(10) DEFAULT 0,
    +  `max_height` int(10)  DEFAULT 0,
    +  `max_depth` int(10)  DEFAULT 0,
    +  `max_weight` int(10)  DEFAULT 0,
    +  `grade` int(10)  DEFAULT 0,
    +  PRIMARY KEY (`id_carrier`),
    +  KEY `deleted` (`deleted`,`active`),
    +  KEY `id_tax_rules_group` (`id_tax_rules_group`)
    +) ENGINE=ENGINE_TYPE  DEFAULT CHARSET=utf8
    + +
    CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` (
    +	`id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT,
    +	`name` VARCHAR(255) NOT NULL,
    +	`id_shop` int(11) unsigned NOT NULL DEFAULT '1',
    +	`id_currency` int(10) unsigned NOT NULL,
    +	`id_country` int(10) unsigned NOT NULL,
    +	`id_group` int(10) unsigned NOT NULL,
    +	`from_quantity` mediumint(8) unsigned NOT NULL,
    +	`price` DECIMAL(20,6),
    +	`reduction` decimal(20,6) NOT NULL,
    +	`reduction_type` enum('amount','percentage') NOT NULL,
    +	`from` datetime NOT NULL,
    +	`to` datetime NOT NULL,
    +	PRIMARY KEY (`id_specific_price_rule`),
    +	KEY `id_product` (`id_shop`,`id_currency`,`id_country`,`id_group`,`from_quantity`,`from`,`to`)
    +) ENGINE=ENGINE_TYPE  DEFAULT CHARSET=utf8
    + +
    UPDATE `PREFIX_configuration` SET value = '6' WHERE name = 'PS_SEARCH_WEIGHT_PNAME'
    + +
    UPDATE `PREFIX_hook_module` SET position = 1
    +WHERE
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPayment') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPaymentReturn') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayHome') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAuthentication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionShopDataDuplication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayTop') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocklanguages')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCustomerAccountAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayCustomerAccount') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsModules') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsvisits')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGraphEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'graphvisifire')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGridEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'gridhtml')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayLeftColumnProduct') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocksharefb')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionSearch') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statssearch')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryUpdate') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryDelete') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAdminMetaSave') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayMyAccountBlock') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts')
    +	OR
    +	id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayFooter') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockreinsurance')
    + +
    ALTER TABLE `PREFIX_employee` ADD `bo_color` varchar(32) default NULL AFTER `stats_date_to`
    + +
    INSERT INTO `PREFIX_cms_category_lang` VALUES(1, 3, 'Inicio', '', 'home', NULL, NULL, NULL)
    + +
    INSERT INTO `PREFIX_cms_category` VALUES(1, 0, 0, 1, NOW(), NOW(),0)
    + +
    UPDATE `PREFIX_cms_category` SET `position` = 0
    + +
    ALTER TABLE `PREFIX_customer` ADD `note` text AFTER `secure_key`
    + +
    ALTER TABLE `PREFIX_contact` ADD `customer_service` tinyint(1) NOT NULL DEFAULT 0 AFTER `email`
    + +
    INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`)
    +	(	SELECT dq.`id_product`, 1, 1, 0, 1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100), IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00'
    +		FROM `PREFIX_discount_quantity` dq
    +		INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)
    +	)
    + +
    DROP TABLE `PREFIX_discount_quantity`
    + +
    INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) (
    +	SELECT
    +		p.`id_product`,
    +		1,
    +		0,
    +		0,
    +		0,
    +		0,
    +		0.00,
    +		1,
    +		IF(p.`reduction_price` > 0, p.`reduction_price`, p.`reduction_percent` / 100),
    +		IF(p.`reduction_price` > 0, 'amount', 'percentage'),
    +		IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_from`),
    +		IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_to`)
    +	FROM `PREFIX_product` p
    +	WHERE p.`reduction_price` OR p.`reduction_percent`
    +)
    + +
    ALTER TABLE `PREFIX_product`
    +	DROP `reduction_price`,
    +	DROP `reduction_percent`,
    +	DROP `reduction_from`,
    +	DROP `reduction_to`
    + +
    INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES
    +('PS_SPECIFIC_PRICE_PRIORITIES', 'id_shop;id_currency;id_country;id_group', NOW(), NOW()),
    +('PS_TAX_DISPLAY', 0, NOW(), NOW()),
    +('PS_SMARTY_FORCE_COMPILE', 1, NOW(), NOW()),
    +('PS_DISTANCE_UNIT', 'km', NOW(), NOW()),
    +('PS_STORES_DISPLAY_CMS', 0, NOW(), NOW()),
    +('PS_STORES_DISPLAY_FOOTER', 0, NOW(), NOW()),
    +('PS_STORES_SIMPLIFIED', 0, NOW(), NOW()),
    +('PS_STATSDATA_CUSTOMER_PAGESVIEWS', 1, NOW(), NOW()),
    +('PS_STATSDATA_PAGESVIEWS', 1, NOW(), NOW()),
    +('PS_STATSDATA_PLUGINS', 1, NOW(), NOW())
    + +
    INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_CONDITIONS_CMS_ID', IFNULL((SELECT `id_cms` FROM `PREFIX_cms` WHERE `id_cms` = 3), 0), NOW(), NOW())
    + +
    CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (
    +	`value` text
    +)
    + +
    SET @defaultOOS = (SELECT value FROM `PREFIX_configuration` WHERE name = 'PS_ORDER_OUT_OF_STOCK')
    + +
    UPDATE `PREFIX_product` p SET `cache_default_attribute` =  0 WHERE `id_product` NOT IN (SELECT `id_product` FROM `PREFIX_product_attribute`)
    + +
    INSERT INTO `PREFIX_hook` (`name`, `title`, `description`, `position`) VALUES ('processCarrier', 'Carrier Process', NULL, 0)
    + +
    INSERT INTO `PREFIX_stock_mvt_reason_lang` (`id_stock_mvt_reason`, `id_lang`, `name`) VALUES
    +(1, 1, 'Order'),
    +(1, 2, 'Commande'),
    +(2, 1, 'Missing Stock Movement'),
    +(2, 2, 'Mouvement de stock manquant'),
    +(3, 1, 'Restocking'),
    +(3, 2, 'Réassort')
    + +
    INSERT INTO `PREFIX_meta_lang` (`id_lang`, `id_meta`, `title`, `url_rewrite`) VALUES
    +(1, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentication', 'authentication'),
    +(2, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentification', 'authentification'),
    +(3, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Autenticación', 'autenticacion')
    + +
    LOCK TABLES `admin_assert` WRITE
    + +
    UNLOCK TABLES
    + +
    DROP TABLE IF EXISTS `admin_role`
    + +
    SELECT * FROM
    +-- This is another comment
    +MyTable # One final comment
    +/* This is a block comment 
    +*/ WHERE 1 = 2;
    + +
    SELECT -- This is a test
    + +
    SELECT Test FROM Test WHERE
    +(
    + MyColumn = 1 )) AND ((( SomeOtherColumn = 2);
    + +
    SELECT * LIMIT 1; SELECT a,b,c,d FROM e LIMIT 1, 2; SELECT 1,2,3 WHERE a in (1,2,3,4,5) and b=5;
    + +
    SELECT count - 50
    +WHERE a-50 = b
    +WHERE 1 and - 50
    +WHERE -50 = a
    +WHERE a = -50
    +WHERE 1 /*test*/ - 50
    +WHERE 1 and -50;
    + +
    SELECT @ and b;
    + +
    SELECT @"weird variable name";
    + +
    SELECT "no closing quote
    +
    \ No newline at end of file diff --git a/vendor/jdorn/sql-formatter/tests/performance.php b/vendor/jdorn/sql-formatter/tests/performance.php new file mode 100644 index 0000000..6c9eb97 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/performance.php @@ -0,0 +1,44 @@ +Formatted ".$num." queries using a max_cachekey_size of ".SqlFormatter::$max_cachekey_size."

    "; + +echo "

    Average query length of ".number_format($chars/$num,5)." characters

    "; + +echo "

    Took ".number_format($end-$start,5)." seconds total, ".number_format(($end-$start)/$num,5)." seconds per query, ".number_format(1000*($end-$start)/$chars,5)." seconds per 1000 characters

    "; + +echo "

    Used ".number_format($uend-$ustart)." bytes of memory

    "; + +echo "

    Cache Stats

    ".print_r(SqlFormatter::getCacheStats(),true)."
    "; + diff --git a/vendor/jdorn/sql-formatter/tests/sql.sql b/vendor/jdorn/sql-formatter/tests/sql.sql new file mode 100644 index 0000000..4e2d988 --- /dev/null +++ b/vendor/jdorn/sql-formatter/tests/sql.sql @@ -0,0 +1,261 @@ +SELECT customer_id, customer_name, COUNT(order_id) as total +FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id +GROUP BY customer_id, customer_name +HAVING COUNT(order_id) > 5 +ORDER BY COUNT(order_id) DESC; + +UPDATE customers + SET totalorders = ordersummary.total + FROM (SELECT customer_id, count(order_id) As total +FROM orders GROUP BY customer_id) As ordersummary + WHERE customers.customer_id = ordersummary.customer_id + +SELECT * FROM sometable +UNION ALL +SELECT * FROM someothertable; + +SET NAMES 'utf8'; + +CREATE TABLE `PREFIX_address` ( + `id_address` int(10) unsigned NOT NULL auto_increment, + `id_country` int(10) unsigned NOT NULL, + `id_state` int(10) unsigned default NULL, + `id_customer` int(10) unsigned NOT NULL default '0', + `id_manufacturer` int(10) unsigned NOT NULL default '0', + `id_supplier` int(10) unsigned NOT NULL default '0', + `id_warehouse` int(10) unsigned NOT NULL default '0', + `alias` varchar(32) NOT NULL, + `company` varchar(64) default NULL, + `lastname` varchar(32) NOT NULL, + `firstname` varchar(32) NOT NULL, + `address1` varchar(128) NOT NULL, + `address2` varchar(128) default NULL, + `postcode` varchar(12) default NULL, + `city` varchar(64) NOT NULL, + `other` text, + `phone` varchar(16) default NULL, + `phone_mobile` varchar(16) default NULL, + `vat_number` varchar(32) default NULL, + `dni` varchar(16) DEFAULT NULL, + `date_add` datetime NOT NULL, + `date_upd` datetime NOT NULL, + `active` tinyint(1) unsigned NOT NULL default '1', + `deleted` tinyint(1) unsigned NOT NULL default '0', + PRIMARY KEY (`id_address`), + KEY `address_customer` (`id_customer`), + KEY `id_country` (`id_country`), + KEY `id_state` (`id_state`), + KEY `id_manufacturer` (`id_manufacturer`), + KEY `id_supplier` (`id_supplier`), + KEY `id_warehouse` (`id_warehouse`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +CREATE TABLE `PREFIX_alias` ( + `id_alias` int(10) unsigned NOT NULL auto_increment, + `alias` varchar(255) NOT NULL, + `search` varchar(255) NOT NULL, + `active` tinyint(1) NOT NULL default '1', + PRIMARY KEY (`id_alias`), + UNIQUE KEY `alias` (`alias`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +CREATE TABLE `PREFIX_carrier` ( + `id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_reference` int(10) unsigned NOT NULL, + `id_tax_rules_group` int(10) unsigned DEFAULT '0', + `name` varchar(64) NOT NULL, + `url` varchar(255) DEFAULT NULL, + `active` tinyint(1) unsigned NOT NULL DEFAULT '0', + `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', + `shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1', + `range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0', + `is_module` tinyint(1) unsigned NOT NULL DEFAULT '0', + `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0', + `shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0', + `need_range` tinyint(1) unsigned NOT NULL DEFAULT '0', + `external_module_name` varchar(64) DEFAULT NULL, + `shipping_method` int(2) NOT NULL DEFAULT '0', + `position` int(10) unsigned NOT NULL default '0', + `max_width` int(10) DEFAULT 0, + `max_height` int(10) DEFAULT 0, + `max_depth` int(10) DEFAULT 0, + `max_weight` int(10) DEFAULT 0, + `grade` int(10) DEFAULT 0, + PRIMARY KEY (`id_carrier`), + KEY `deleted` (`deleted`,`active`), + KEY `id_tax_rules_group` (`id_tax_rules_group`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` ( + `id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `id_shop` int(11) unsigned NOT NULL DEFAULT '1', + `id_currency` int(10) unsigned NOT NULL, + `id_country` int(10) unsigned NOT NULL, + `id_group` int(10) unsigned NOT NULL, + `from_quantity` mediumint(8) unsigned NOT NULL, + `price` DECIMAL(20,6), + `reduction` decimal(20,6) NOT NULL, + `reduction_type` enum('amount','percentage') NOT NULL, + `from` datetime NOT NULL, + `to` datetime NOT NULL, + PRIMARY KEY (`id_specific_price_rule`), + KEY `id_product` (`id_shop`,`id_currency`,`id_country`,`id_group`,`from_quantity`,`from`,`to`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 + +UPDATE `PREFIX_configuration` SET value = '6' WHERE name = 'PS_SEARCH_WEIGHT_PNAME' + +UPDATE `PREFIX_hook_module` SET position = 1 +WHERE + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPayment') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayPaymentReturn') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'cheque') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayHome') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAuthentication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionShopDataDuplication') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'homeslider') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayTop') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocklanguages') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCustomerAccountAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsdata') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayCustomerAccount') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsModules') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statsvisits') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGraphEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'graphvisifire') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayAdminStatsGridEngine') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'gridhtml') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayLeftColumnProduct') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blocksharefb') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionSearch') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'statssearch') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryAdd') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryUpdate') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionCategoryDelete') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'actionAdminMetaSave') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockcategories') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayMyAccountBlock') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'favoriteproducts') + OR + id_hook = (SELECT id_hook FROM `PREFIX_hook` WHERE name = 'displayFooter') AND id_module = (SELECT id_module FROM `PREFIX_module` WHERE name = 'blockreinsurance') + +ALTER TABLE `PREFIX_employee` ADD `bo_color` varchar(32) default NULL AFTER `stats_date_to` + +INSERT INTO `PREFIX_cms_category_lang` VALUES(1, 3, 'Inicio', '', 'home', NULL, NULL, NULL) + +INSERT INTO `PREFIX_cms_category` VALUES(1, 0, 0, 1, NOW(), NOW(),0) + +UPDATE `PREFIX_cms_category` SET `position` = 0 + +ALTER TABLE `PREFIX_customer` ADD `note` text AFTER `secure_key` + +ALTER TABLE `PREFIX_contact` ADD `customer_service` tinyint(1) NOT NULL DEFAULT 0 AFTER `email` + +INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) + ( SELECT dq.`id_product`, 1, 1, 0, 1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100), IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' + FROM `PREFIX_discount_quantity` dq + INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`) + ) + +DROP TABLE `PREFIX_discount_quantity` + +INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, `price`, `from_quantity`, `reduction`, `reduction_type`, `from`, `to`) ( + SELECT + p.`id_product`, + 1, + 0, + 0, + 0, + 0, + 0.00, + 1, + IF(p.`reduction_price` > 0, p.`reduction_price`, p.`reduction_percent` / 100), + IF(p.`reduction_price` > 0, 'amount', 'percentage'), + IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_from`), + IF (p.`reduction_from` = p.`reduction_to`, '0000-00-00 00:00:00', p.`reduction_to`) + FROM `PREFIX_product` p + WHERE p.`reduction_price` OR p.`reduction_percent` +) + +ALTER TABLE `PREFIX_product` + DROP `reduction_price`, + DROP `reduction_percent`, + DROP `reduction_from`, + DROP `reduction_to` + +INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES +('PS_SPECIFIC_PRICE_PRIORITIES', 'id_shop;id_currency;id_country;id_group', NOW(), NOW()), +('PS_TAX_DISPLAY', 0, NOW(), NOW()), +('PS_SMARTY_FORCE_COMPILE', 1, NOW(), NOW()), +('PS_DISTANCE_UNIT', 'km', NOW(), NOW()), +('PS_STORES_DISPLAY_CMS', 0, NOW(), NOW()), +('PS_STORES_DISPLAY_FOOTER', 0, NOW(), NOW()), +('PS_STORES_SIMPLIFIED', 0, NOW(), NOW()), +('PS_STATSDATA_CUSTOMER_PAGESVIEWS', 1, NOW(), NOW()), +('PS_STATSDATA_PAGESVIEWS', 1, NOW(), NOW()), +('PS_STATSDATA_PLUGINS', 1, NOW(), NOW()) + +INSERT INTO `PREFIX_configuration` (`name`, `value`, `date_add`, `date_upd`) VALUES ('PS_CONDITIONS_CMS_ID', IFNULL((SELECT `id_cms` FROM `PREFIX_cms` WHERE `id_cms` = 3), 0), NOW(), NOW()) + +CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` ( + `value` text +) + +SET @defaultOOS = (SELECT value FROM `PREFIX_configuration` WHERE name = 'PS_ORDER_OUT_OF_STOCK') + +UPDATE `PREFIX_product` p SET `cache_default_attribute` = 0 WHERE `id_product` NOT IN (SELECT `id_product` FROM `PREFIX_product_attribute`) + +INSERT INTO `PREFIX_hook` (`name`, `title`, `description`, `position`) VALUES ('processCarrier', 'Carrier Process', NULL, 0) + +INSERT INTO `PREFIX_stock_mvt_reason_lang` (`id_stock_mvt_reason`, `id_lang`, `name`) VALUES +(1, 1, 'Order'), +(1, 2, 'Commande'), +(2, 1, 'Missing Stock Movement'), +(2, 2, 'Mouvement de stock manquant'), +(3, 1, 'Restocking'), +(3, 2, 'Réassort') + +INSERT INTO `PREFIX_meta_lang` (`id_lang`, `id_meta`, `title`, `url_rewrite`) VALUES +(1, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentication', 'authentication'), +(2, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Authentification', 'authentification'), +(3, (SELECT `id_meta` FROM `PREFIX_meta` WHERE `page` = 'authentication'), 'Autenticación', 'autenticacion') + +LOCK TABLES `admin_assert` WRITE + +UNLOCK TABLES + +DROP TABLE IF EXISTS `admin_role` + +SELECT * FROM +-- This is another comment +MyTable # One final comment +/* This is a block comment +*/ WHERE 1 = 2; + +SELECT -- This is a test + +SELECT Test FROM Test WHERE +( + MyColumn = 1 )) AND ((( SomeOtherColumn = 2); + +SELECT * LIMIT 1; SELECT a,b,c,d FROM e LIMIT 1, 2; SELECT 1,2,3 WHERE a in (1,2,3,4,5) and b=5; + +SELECT count - 50 +WHERE a-50 = b +WHERE 1 and - 50 +WHERE -50 = a +WHERE a = -50 +WHERE 1 /*test*/ - 50 +WHERE 1 and -50; + +SELECT @ and b; + +SELECT @"weird variable name"; + +SELECT "no closing quote diff --git a/vendor/monolog/monolog/.php_cs b/vendor/monolog/monolog/.php_cs new file mode 100644 index 0000000..8c11b23 --- /dev/null +++ b/vendor/monolog/monolog/.php_cs @@ -0,0 +1,15 @@ +files() + ->name('*.php') + ->in(__DIR__.'/src') + ->in(__DIR__.'/tests') +; + +return Symfony\CS\Config\Config::create() + ->fixers(array( + 'psr0', 'encoding', 'short_tag', 'braces', 'elseif', 'eof_ending', 'function_declaration', 'indentation', 'line_after_namespace', 'linefeed', 'lowercase_constants', 'lowercase_keywords', 'multiple_use', 'php_closing_tag', 'trailing_spaces', 'visibility', 'duplicate_semicolon', 'extra_empty_lines', 'include', 'namespace_no_leading_whitespace', 'object_operator', 'operators_spaces', 'phpdoc_params', 'return', 'single_array_no_trailing_comma', 'spaces_cast', 'standardize_not_equal', 'ternary_spaces', 'unused_use', 'whitespacy_lines', + )) + ->finder($finder) +; diff --git a/vendor/monolog/monolog/CHANGELOG.mdown b/vendor/monolog/monolog/CHANGELOG.mdown new file mode 100644 index 0000000..2aaeec5 --- /dev/null +++ b/vendor/monolog/monolog/CHANGELOG.mdown @@ -0,0 +1,265 @@ +### 1.17.2 (2015-10-14) + + * Fixed ErrorHandler compatibility with non-Monolog PSR-3 loggers + * Fixed SlackHandler handling to use slack functionalities better + * Fixed SwiftMailerHandler bug when sending multiple emails they all had the same id + * Fixed 5.3 compatibility regression + +### 1.17.1 (2015-08-31) + + * Fixed RollbarHandler triggering PHP notices + +### 1.17.0 (2015-08-30) + + * Added support for `checksum` and `release` context/extra values in RavenHandler + * Added better support for exceptions in RollbarHandler + * Added UidProcessor::getUid + * Added support for showing the resource type in NormalizedFormatter + * Fixed IntrospectionProcessor triggering PHP notices + +### 1.16.0 (2015-08-09) + + * Added IFTTTHandler to notify ifttt.com triggers + * Added Logger::setHandlers() to allow setting/replacing all handlers + * Added $capSize in RedisHandler to cap the log size + * Fixed StreamHandler creation of directory to only trigger when the first log write happens + * Fixed bug in the handling of curl failures + * Fixed duplicate logging of fatal errors when both error and fatal error handlers are registered in monolog's ErrorHandler + * Fixed missing fatal errors records with handlers that need to be closed to flush log records + * Fixed TagProcessor::addTags support for associative arrays + +### 1.15.0 (2015-07-12) + + * Added addTags and setTags methods to change a TagProcessor + * Added automatic creation of directories if they are missing for a StreamHandler to open a log file + * Added retry functionality to Loggly, Cube and Mandrill handlers so they retry up to 5 times in case of network failure + * Fixed process exit code being incorrectly reset to 0 if ErrorHandler::registerExceptionHandler was used + * Fixed HTML/JS escaping in BrowserConsoleHandler + * Fixed JSON encoding errors being silently suppressed (PHP 5.5+ only) + +### 1.14.0 (2015-06-19) + + * Added PHPConsoleHandler to send record to Chrome's PHP Console extension and library + * Added support for objects implementing __toString in the NormalizerFormatter + * Added support for HipChat's v2 API in HipChatHandler + * Added Logger::setTimezone() to initialize the timezone monolog should use in case date.timezone isn't correct for your app + * Added an option to send formatted message instead of the raw record on PushoverHandler via ->useFormattedMessage(true) + * Fixed curl errors being silently suppressed + +### 1.13.1 (2015-03-09) + + * Fixed regression in HipChat requiring a new token to be created + +### 1.13.0 (2015-03-05) + + * Added Registry::hasLogger to check for the presence of a logger instance + * Added context.user support to RavenHandler + * Added HipChat API v2 support in the HipChatHandler + * Added NativeMailerHandler::addParameter to pass params to the mail() process + * Added context data to SlackHandler when $includeContextAndExtra is true + * Added ability to customize the Swift_Message per-email in SwiftMailerHandler + * Fixed SwiftMailerHandler to lazily create message instances if a callback is provided + * Fixed serialization of INF and NaN values in Normalizer and LineFormatter + +### 1.12.0 (2014-12-29) + + * Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers. + * Added PsrHandler to forward records to another PSR-3 logger + * Added SamplingHandler to wrap around a handler and include only every Nth record + * Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now) + * Added exception codes in the output of most formatters + * Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line) + * Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data + * Added $host to HipChatHandler for users of private instances + * Added $transactionName to NewRelicHandler and support for a transaction_name context value + * Fixed MandrillHandler to avoid outputing API call responses + * Fixed some non-standard behaviors in SyslogUdpHandler + +### 1.11.0 (2014-09-30) + + * Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names + * Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails + * Added MandrillHandler to send emails via the Mandrillapp.com API + * Added SlackHandler to log records to a Slack.com account + * Added FleepHookHandler to log records to a Fleep.io account + * Added LogglyHandler::addTag to allow adding tags to an existing handler + * Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end + * Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing + * Added support for PhpAmqpLib in the AmqpHandler + * Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs + * Added support for adding extra fields from $_SERVER in the WebProcessor + * Fixed support for non-string values in PrsLogMessageProcessor + * Fixed SwiftMailer messages being sent with the wrong date in long running scripts + * Fixed minor PHP 5.6 compatibility issues + * Fixed BufferHandler::close being called twice + +### 1.10.0 (2014-06-04) + + * Added Logger::getHandlers() and Logger::getProcessors() methods + * Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached + * Added support for extra data in NewRelicHandler + * Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines + +### 1.9.1 (2014-04-24) + + * Fixed regression in RotatingFileHandler file permissions + * Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records + * Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative + +### 1.9.0 (2014-04-20) + + * Added LogEntriesHandler to send logs to a LogEntries account + * Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler + * Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes + * Added support for table formatting in FirePHPHandler via the table context key + * Added a TagProcessor to add tags to records, and support for tags in RavenHandler + * Added $appendNewline flag to the JsonFormatter to enable using it when logging to files + * Added sound support to the PushoverHandler + * Fixed multi-threading support in StreamHandler + * Fixed empty headers issue when ChromePHPHandler received no records + * Fixed default format of the ErrorLogHandler + +### 1.8.0 (2014-03-23) + + * Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them + * Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output + * Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler + * Added FlowdockHandler to send logs to a Flowdock account + * Added RollbarHandler to send logs to a Rollbar account + * Added HtmlFormatter to send prettier log emails with colors for each log level + * Added GitProcessor to add the current branch/commit to extra record data + * Added a Monolog\Registry class to allow easier global access to pre-configured loggers + * Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement + * Added support for HHVM + * Added support for Loggly batch uploads + * Added support for tweaking the content type and encoding in NativeMailerHandler + * Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor + * Fixed batch request support in GelfHandler + +### 1.7.0 (2013-11-14) + + * Added ElasticSearchHandler to send logs to an Elastic Search server + * Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB + * Added SyslogUdpHandler to send logs to a remote syslogd server + * Added LogglyHandler to send logs to a Loggly account + * Added $level to IntrospectionProcessor so it only adds backtraces when needed + * Added $version to LogstashFormatter to allow using the new v1 Logstash format + * Added $appName to NewRelicHandler + * Added configuration of Pushover notification retries/expiry + * Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default + * Added chainability to most setters for all handlers + * Fixed RavenHandler batch processing so it takes the message from the record with highest priority + * Fixed HipChatHandler batch processing so it sends all messages at once + * Fixed issues with eAccelerator + * Fixed and improved many small things + +### 1.6.0 (2013-07-29) + + * Added HipChatHandler to send logs to a HipChat chat room + * Added ErrorLogHandler to send logs to PHP's error_log function + * Added NewRelicHandler to send logs to NewRelic's service + * Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler + * Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel + * Added stack traces output when normalizing exceptions (json output & co) + * Added Monolog\Logger::API constant (currently 1) + * Added support for ChromePHP's v4.0 extension + * Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel + * Added support for sending messages to multiple users at once with the PushoverHandler + * Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler) + * Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now + * Fixed issue in RotatingFileHandler when an open_basedir restriction is active + * Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0 + * Fixed SyslogHandler issue when many were used concurrently with different facilities + +### 1.5.0 (2013-04-23) + + * Added ProcessIdProcessor to inject the PID in log records + * Added UidProcessor to inject a unique identifier to all log records of one request/run + * Added support for previous exceptions in the LineFormatter exception serialization + * Added Monolog\Logger::getLevels() to get all available levels + * Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle + +### 1.4.1 (2013-04-01) + + * Fixed exception formatting in the LineFormatter to be more minimalistic + * Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0 + * Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days + * Fixed WebProcessor array access so it checks for data presence + * Fixed Buffer, Group and FingersCrossed handlers to make use of their processors + +### 1.4.0 (2013-02-13) + + * Added RedisHandler to log to Redis via the Predis library or the phpredis extension + * Added ZendMonitorHandler to log to the Zend Server monitor + * Added the possibility to pass arrays of handlers and processors directly in the Logger constructor + * Added `$useSSL` option to the PushoverHandler which is enabled by default + * Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously + * Fixed header injection capability in the NativeMailHandler + +### 1.3.1 (2013-01-11) + + * Fixed LogstashFormatter to be usable with stream handlers + * Fixed GelfMessageFormatter levels on Windows + +### 1.3.0 (2013-01-08) + + * Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface` + * Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance + * Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash) + * Added PushoverHandler to send mobile notifications + * Added CouchDBHandler and DoctrineCouchDBHandler + * Added RavenHandler to send data to Sentry servers + * Added support for the new MongoClient class in MongoDBHandler + * Added microsecond precision to log records' timestamps + * Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing + the oldest entries + * Fixed normalization of objects with cyclic references + +### 1.2.1 (2012-08-29) + + * Added new $logopts arg to SyslogHandler to provide custom openlog options + * Fixed fatal error in SyslogHandler + +### 1.2.0 (2012-08-18) + + * Added AmqpHandler (for use with AMQP servers) + * Added CubeHandler + * Added NativeMailerHandler::addHeader() to send custom headers in mails + * Added the possibility to specify more than one recipient in NativeMailerHandler + * Added the possibility to specify float timeouts in SocketHandler + * Added NOTICE and EMERGENCY levels to conform with RFC 5424 + * Fixed the log records to use the php default timezone instead of UTC + * Fixed BufferHandler not being flushed properly on PHP fatal errors + * Fixed normalization of exotic resource types + * Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog + +### 1.1.0 (2012-04-23) + + * Added Monolog\Logger::isHandling() to check if a handler will + handle the given log level + * Added ChromePHPHandler + * Added MongoDBHandler + * Added GelfHandler (for use with Graylog2 servers) + * Added SocketHandler (for use with syslog-ng for example) + * Added NormalizerFormatter + * Added the possibility to change the activation strategy of the FingersCrossedHandler + * Added possibility to show microseconds in logs + * Added `server` and `referer` to WebProcessor output + +### 1.0.2 (2011-10-24) + + * Fixed bug in IE with large response headers and FirePHPHandler + +### 1.0.1 (2011-08-25) + + * Added MemoryPeakUsageProcessor and MemoryUsageProcessor + * Added Monolog\Logger::getName() to get a logger's channel name + +### 1.0.0 (2011-07-06) + + * Added IntrospectionProcessor to get info from where the logger was called + * Fixed WebProcessor in CLI + +### 1.0.0-RC1 (2011-07-01) + + * Initial release diff --git a/vendor/monolog/monolog/LICENSE b/vendor/monolog/monolog/LICENSE new file mode 100644 index 0000000..56e08d5 --- /dev/null +++ b/vendor/monolog/monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2015 Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/monolog/monolog/README.mdown b/vendor/monolog/monolog/README.mdown new file mode 100644 index 0000000..7d8ade5 --- /dev/null +++ b/vendor/monolog/monolog/README.mdown @@ -0,0 +1,95 @@ +# Monolog - Logging for PHP [![Build Status](https://img.shields.io/travis/Seldaek/monolog.svg)](https://travis-ci.org/Seldaek/monolog) + +[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) +[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) +[![Reference Status](https://www.versioneye.com/php/monolog:monolog/reference_badge.svg)](https://www.versioneye.com/php/monolog:monolog/references) + + +Monolog sends your logs to files, sockets, inboxes, databases and various +web services. See the complete list of handlers below. Special handlers +allow you to build advanced logging strategies. + +This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +interface that you can type-hint against in your own libraries to keep +a maximum of interoperability. You can also use it in your applications to +make sure you can always use another compatible logger at a later time. +As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels. +Internally Monolog still uses its own level scheme since it predates PSR-3. + +## Installation + +Install the latest version with + +```bash +$ composer require monolog/monolog +``` + +## Basic Usage + +```php +pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); + +// add records to the log +$log->addWarning('Foo'); +$log->addError('Bar'); +``` + +## Documentation + +- [Usage Instructions](doc/01-usage.md) +- [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md) +- [Utility classes](doc/03-utilities.md) +- [Extending Monolog](doc/04-extending.md) + +## Third Party Packages + +Third party handlers, formatters and processors are +[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You +can also add your own there if you publish one. + +## About + +### Requirements + +- Monolog works with PHP 5.3 or above, and is also tested to work with HHVM. + +### Submitting bugs and feature requests + +Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) + +### Framework Integrations + +- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) + can be used very easily with Monolog since it implements the interface. +- [Symfony2](http://symfony.com) comes out of the box with Monolog. +- [Silex](http://silex.sensiolabs.org/) comes out of the box with Monolog. +- [Laravel 4 & 5](http://laravel.com/) come out of the box with Monolog. +- [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog. +- [PPI](http://www.ppi.io/) comes out of the box with Monolog. +- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin. +- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer. +- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog. +- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog. +- [Nette Framework](http://nette.org/en/) can be used with Monolog via [Kdyby/Monolog](https://github.com/Kdyby/Monolog) extension. +- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog. + +### Author + +Jordi Boggiano - -
    +See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project. + +### License + +Monolog is licensed under the MIT License - see the `LICENSE` file for details + +### Acknowledgements + +This library is heavily inspired by Python's [Logbook](http://packages.python.org/Logbook/) +library, although most concepts have been adjusted to fit to the PHP world. diff --git a/vendor/monolog/monolog/composer.json b/vendor/monolog/monolog/composer.json new file mode 100644 index 0000000..2bb9053 --- /dev/null +++ b/vendor/monolog/monolog/composer.json @@ -0,0 +1,65 @@ +{ + "name": "monolog/monolog", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "keywords": ["log", "logging", "psr-3"], + "homepage": "http://github.com/Seldaek/monolog", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5", + "graylog2/gelf-php": "~1.0", + "raven/raven": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "doctrine/couchdb": "~1.0@dev", + "aws/aws-sdk-php": "^2.4.9", + "videlalvaro/php-amqplib": "~2.4", + "swiftmailer/swiftmailer": "~5.3", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit-mock-objects": "2.3.0", + "jakub-onderka/php-parallel-lint": "0.9" + }, + "_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - needs hhvm 3.8+ on travis", + "suggest": { + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "raven/raven": "Allow sending log messages to a Sentry server", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "php-console/php-console": "Allow sending log messages to Google Chrome" + }, + "autoload": { + "psr-4": {"Monolog\\": "src/Monolog"} + }, + "autoload-dev": { + "psr-4": {"Monolog\\": "tests/Monolog"} + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "extra": { + "branch-alias": { + "dev-master": "1.16.x-dev" + } + }, + "scripts": { + "test": [ + "parallel-lint . --exclude vendor", + "phpunit" + ] + } +} diff --git a/vendor/monolog/monolog/doc/01-usage.md b/vendor/monolog/monolog/doc/01-usage.md new file mode 100644 index 0000000..75bc402 --- /dev/null +++ b/vendor/monolog/monolog/doc/01-usage.md @@ -0,0 +1,228 @@ +# Using Monolog + +- [Installation](#installation) +- [Core Concepts](#core-concepts) +- [Log Levels](#log-levels) +- [Configuring a logger](#configuring-a-logger) +- [Adding extra data in the records](#adding-extra-data-in-the-records) +- [Leveraging channels](#leveraging-channels) +- [Customizing the log format](#customizing-the-log-format) + +## Installation + +Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog)) +and as such installable via [Composer](http://getcomposer.org/). + +```bash +composer require monolog/monolog +``` + +If you do not use Composer, you can grab the code from GitHub, and use any +PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader)) +to load Monolog classes. + +## Core Concepts + +Every `Logger` instance has a channel (name) and a stack of handlers. Whenever +you add a record to the logger, it traverses the handler stack. Each handler +decides whether it fully handled the record, and if so, the propagation of the +record ends there. + +This allows for flexible logging setups, for example having a `StreamHandler` at +the bottom of the stack that will log anything to disk, and on top of that add +a `MailHandler` that will send emails only when an error message is logged. +Handlers also have a `$bubble` property which defines whether they block the +record or not if they handled it. In this example, setting the `MailHandler`'s +`$bubble` argument to false means that records handled by the `MailHandler` will +not propagate to the `StreamHandler` anymore. + +You can create many `Logger`s, each defining a channel (e.g.: db, request, +router, ..) and each of them combining various handlers, which can be shared +or not. The channel is reflected in the logs and allows you to easily see or +filter records. + +Each Handler also has a Formatter, a default one with settings that make sense +will be created if you don't set one. The formatters normalize and format +incoming records so that they can be used by the handlers to output useful +information. + +Custom severity levels are not available. Only the eight +[RFC 5424](http://tools.ietf.org/html/rfc5424) levels (debug, info, notice, +warning, error, critical, alert, emergency) are present for basic filtering +purposes, but for sorting and other use cases that would require +flexibility, you should add Processors to the Logger that can add extra +information (tags, user ip, ..) to the records before they are handled. + +## Log Levels + +Monolog supports the logging levels described by [RFC 5424](http://tools.ietf.org/html/rfc5424). + +- **DEBUG** (100): Detailed debug information. + +- **INFO** (200): Interesting events. Examples: User logs in, SQL logs. + +- **NOTICE** (250): Normal but significant events. + +- **WARNING** (300): Exceptional occurrences that are not errors. Examples: + Use of deprecated APIs, poor use of an API, undesirable things that are not + necessarily wrong. + +- **ERROR** (400): Runtime errors that do not require immediate action but + should typically be logged and monitored. + +- **CRITICAL** (500): Critical conditions. Example: Application component + unavailable, unexpected exception. + +- **ALERT** (550): Action must be taken immediately. Example: Entire website + down, database unavailable, etc. This should trigger the SMS alerts and wake + you up. + +- **EMERGENCY** (600): Emergency: system is unusable. + +## Configuring a logger + +Here is a basic setup to log to a file and to firephp on the DEBUG level: + +```php +pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG)); +$logger->pushHandler(new FirePHPHandler()); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); +``` + +Let's explain it. The first step is to create the logger instance which will +be used in your code. The argument is a channel name, which is useful when +you use several loggers (see below for more details about it). + +The logger itself does not know how to handle a record. It delegates it to +some handlers. The code above registers two handlers in the stack to allow +handling records in two different ways. + +Note that the FirePHPHandler is called first as it is added on top of the +stack. This allows you to temporarily add a logger with bubbling disabled if +you want to override other configured loggers. + +> If you use Monolog standalone and are looking for an easy way to +> configure many handlers, the [theorchard/monolog-cascade](https://github.com/theorchard/monolog-cascade) +> can help you build complex logging configs via PHP arrays, yaml or json configs. + +## Adding extra data in the records + +Monolog provides two different ways to add extra informations along the simple +textual message. + +### Using the logging context + +The first way is the context, allowing to pass an array of data along the +record: + +```php +addInfo('Adding a new user', array('username' => 'Seldaek')); +``` + +Simple handlers (like the StreamHandler for instance) will simply format +the array to a string but richer handlers can take advantage of the context +(FirePHP is able to display arrays in pretty way for instance). + +### Using processors + +The second way is to add extra data for all records by using a processor. +Processors can be any callable. They will get the record as parameter and +must return it after having eventually changed the `extra` part of it. Let's +write a processor adding some dummy data in the record: + +```php +pushProcessor(function ($record) { + $record['extra']['dummy'] = 'Hello world!'; + + return $record; +}); +``` + +Monolog provides some built-in processors that can be used in your project. +Look at the [dedicated chapter](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#processors) for the list. + +> Tip: processors can also be registered on a specific handler instead of + the logger to apply only for this handler. + +## Leveraging channels + +Channels are a great way to identify to which part of the application a record +is related. This is useful in big applications (and is leveraged by +MonologBundle in Symfony2). + +Picture two loggers sharing a handler that writes to a single log file. +Channels would allow you to identify the logger that issued every record. +You can easily grep through the log files filtering this or that channel. + +```php +pushHandler($stream); +$logger->pushHandler($firephp); + +// Create a logger for the security-related stuff with a different channel +$securityLogger = new Logger('security'); +$securityLogger->pushHandler($stream); +$securityLogger->pushHandler($firephp); +``` + +## Customizing the log format + +In Monolog it's easy to customize the format of the logs written into files, +sockets, mails, databases and other handlers. Most of the handlers use the + +```php +$record['formatted'] +``` + +value to be automatically put into the log device. This value depends on the +formatter settings. You can choose between predefined formatter classes or +write your own (e.g. a multiline text file for human-readable output). + +To configure a predefined formatter class, just set it as the handler's field: + +```php +// the default date format is "Y-m-d H:i:s" +$dateFormat = "Y n j, g:i a"; +// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n" +$output = "%datetime% > %level_name% > %message% %context% %extra%\n"; +// finally, create a formatter +$formatter = new LineFormatter($output, $dateFormat); + +// Create a handler +$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG); +$stream->setFormatter($formatter); +// bind it to a logger object +$securityLogger = new Logger('security'); +$securityLogger->pushHandler($stream); +``` + +You may also reuse the same formatter between multiple handlers and share those +handlers between multiple loggers. + +[Handlers, Formatters and Processors](02-handlers-formatters-processors.md) → diff --git a/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md b/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md new file mode 100644 index 0000000..90f8fce --- /dev/null +++ b/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md @@ -0,0 +1,142 @@ +# Handlers, Formatters and Processors + +- [Handlers](#handlers) + - [Log to files and syslog](#log-to-files-and-syslog) + - [Send alerts and emails](#send-alerts-and-emails) + - [Log specific servers and networked logging](#log-specific-servers-and-networked-logging) + - [Logging in development](#logging-in-development) + - [Log to databases](#log-to-databases) + - [Wrappers / Special Handlers](#wrappers--special-handlers) +- [Formatters](#formatters) +- [Processors](#processors) +- [Third Party Packages](#third-party-packages) + +## Handlers + +### Log to files and syslog + +- _StreamHandler_: Logs records into any PHP stream, use this for log files. +- _RotatingFileHandler_: Logs records to a file and creates one logfile per day. + It will also delete files older than `$maxFiles`. You should use + [logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile + setups though, this is just meant as a quick and dirty solution. +- _SyslogHandler_: Logs records to the syslog. +- _ErrorLogHandler_: Logs records to PHP's + [`error_log()`](http://docs.php.net/manual/en/function.error-log.php) function. + +### Send alerts and emails + +- _NativeMailerHandler_: Sends emails using PHP's + [`mail()`](http://php.net/manual/en/function.mail.php) function. +- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance. +- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API. +- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API. +- _FlowdockHandler_: Logs records to a [Flowdock](https://www.flowdock.com/) account. +- _SlackHandler_: Logs records to a [Slack](https://www.slack.com/) account. +- _MandrillHandler_: Sends emails via the Mandrill API using a [`Swift_Message`](http://swiftmailer.org/) instance. +- _FleepHookHandler_: Logs records to a [Fleep](https://fleep.io/) conversation using Webhooks. +- _IFTTTHandler_: Notifies an [IFTTT](https://ifttt.com/maker) trigger with the log channel, level name and message. + +### Log specific servers and networked logging + +- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this + for UNIX and TCP sockets. See an [example](sockets.md). +- _AmqpHandler_: Logs records to an [amqp](http://www.amqp.org/) compatible + server. Requires the [php-amqp](http://pecl.php.net/package/amqp) extension (1.0+). +- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server. +- _CubeHandler_: Logs records to a [Cube](http://square.github.com/cube/) server. +- _RavenHandler_: Logs records to a [Sentry](http://getsentry.com/) server using + [raven](https://packagist.org/packages/raven/raven). +- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server. +- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application. +- _LogglyHandler_: Logs records to a [Loggly](http://www.loggly.com/) account. +- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account. +- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. +- _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account. + +### Logging in development + +- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing + inline `console` messages within [FireBug](http://getfirebug.com/). +- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing + inline `console` messages within Chrome. +- _BrowserConsoleHandler_: Handler to send logs to browser's Javascript `console` with + no browser extension required. Most browsers supporting `console` API are supported. +- _PHPConsoleHandler_: Handler for [PHP Console](https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef), providing + inline `console` and notification popup messages within Chrome. + +### Log to databases + +- _RedisHandler_: Logs records to a [redis](http://redis.io) server. +- _MongoDBHandler_: Handler to write records in MongoDB via a + [Mongo](http://pecl.php.net/package/mongo) extension connection. +- _CouchDBHandler_: Logs records to a CouchDB server. +- _DoctrineCouchDBHandler_: Logs records to a CouchDB server via the Doctrine CouchDB ODM. +- _ElasticSearchHandler_: Logs records to an Elastic Search server. +- _DynamoDbHandler_: Logs records to a DynamoDB table with the [AWS SDK](https://github.com/aws/aws-sdk-php). + +### Wrappers / Special Handlers + +- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as + parameter and will accumulate log records of all levels until a record + exceeds the defined severity level. At which point it delivers all records, + including those of lower severity, to the handler it wraps. This means that + until an error actually happens you will not see anything in your logs, but + when it happens you will have the full information, including debug and info + records. This provides you with all the information you need, but only when + you need it. +- _WhatFailureGroupHandler_: This handler extends the _GroupHandler_ ignoring + exceptions raised by each child handler. This allows you to ignore issues + where a remote tcp connection may have died but you do not want your entire + application to crash and may wish to continue to log to other handlers. +- _BufferHandler_: This handler will buffer all the log records it receives + until `close()` is called at which point it will call `handleBatch()` on the + handler it wraps with all the log messages at once. This is very useful to + send an email with all records at once for example instead of having one mail + for every log record. +- _GroupHandler_: This handler groups other handlers. Every record received is + sent to all the handlers it is configured with. +- _FilterHandler_: This handler only lets records of the given levels through + to the wrapped handler. +- _SamplingHandler_: Wraps around another handler and lets you sample records + if you only want to store some of them. +- _NullHandler_: Any record it can handle will be thrown away. This can be used + to put on top of an existing handler stack to disable it temporarily. +- _PsrHandler_: Can be used to forward log records to an existing PSR-3 logger +- _TestHandler_: Used for testing, it records everything that is sent to it and + has accessors to read out the information. + +## Formatters + +- _LineFormatter_: Formats a log record into a one-line string. +- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails. +- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. +- _ScalarFormatter_: Used to format log records into an associative array of scalar values. +- _JsonFormatter_: Encodes a log record into json. +- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler. +- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler. +- _GelfMessageFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler. +- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/latest). +- _ElasticaFormatter_: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler. +- _LogglyFormatter_: Used to format log records into Loggly messages, only useful for the LogglyHandler. +- _FlowdockFormatter_: Used to format log records into Flowdock messages, only useful for the FlowdockHandler. +- _MongoDBFormatter_: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler. + +## Processors + +- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated. +- _WebProcessor_: Adds the current request URI, request method and client IP to a log record. +- _MemoryUsageProcessor_: Adds the current memory usage to a log record. +- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record. +- _ProcessIdProcessor_: Adds the process id to a log record. +- _UidProcessor_: Adds a unique identifier to a log record. +- _GitProcessor_: Adds the current git branch and commit to a log record. +- _TagProcessor_: Adds an array of predefined tags to a log record. + +## Third Party Packages + +Third party handlers, formatters and processors are +[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You +can also add your own there if you publish one. + +← [Usage](01-usage.md) | [Utility classes](03-utilities.md) → diff --git a/vendor/monolog/monolog/doc/03-utilities.md b/vendor/monolog/monolog/doc/03-utilities.md new file mode 100644 index 0000000..c62aa41 --- /dev/null +++ b/vendor/monolog/monolog/doc/03-utilities.md @@ -0,0 +1,13 @@ +# Utilities + +- _Registry_: The `Monolog\Registry` class lets you configure global loggers that you + can then statically access from anywhere. It is not really a best practice but can + help in some older codebases or for ease of use. +- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register + a Logger instance as an exception handler, error handler or fatal error handler. +- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log + level is reached. +- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain + log level is reached, depending on which channel received the log record. + +← [Handlers, Formatters and Processors](02-handlers-formatters-processors.md) | [Extending Monolog](04-extending.md) → diff --git a/vendor/monolog/monolog/doc/04-extending.md b/vendor/monolog/monolog/doc/04-extending.md new file mode 100644 index 0000000..ebd9104 --- /dev/null +++ b/vendor/monolog/monolog/doc/04-extending.md @@ -0,0 +1,76 @@ +# Extending Monolog + +Monolog is fully extensible, allowing you to adapt your logger to your needs. + +## Writing your own handler + +Monolog provides many built-in handlers. But if the one you need does not +exist, you can write it and use it in your logger. The only requirement is +to implement `Monolog\Handler\HandlerInterface`. + +Let's write a PDOHandler to log records to a database. We will extend the +abstract class provided by Monolog to keep things DRY. + +```php +pdo = $pdo; + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + if (!$this->initialized) { + $this->initialize(); + } + + $this->statement->execute(array( + 'channel' => $record['channel'], + 'level' => $record['level'], + 'message' => $record['formatted'], + 'time' => $record['datetime']->format('U'), + )); + } + + private function initialize() + { + $this->pdo->exec( + 'CREATE TABLE IF NOT EXISTS monolog ' + .'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)' + ); + $this->statement = $this->pdo->prepare( + 'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)' + ); + + $this->initialized = true; + } +} +``` + +You can now use this handler in your logger: + +```php +pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite'))); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); +``` + +The `Monolog\Handler\AbstractProcessingHandler` class provides most of the +logic needed for the handler, including the use of processors and the formatting +of the record (which is why we use ``$record['formatted']`` instead of ``$record['message']``). + +← [Utility classes](03-utilities.md) diff --git a/vendor/monolog/monolog/doc/sockets.md b/vendor/monolog/monolog/doc/sockets.md new file mode 100644 index 0000000..ea9cf0e --- /dev/null +++ b/vendor/monolog/monolog/doc/sockets.md @@ -0,0 +1,39 @@ +Sockets Handler +=============== + +This handler allows you to write your logs to sockets using [fsockopen](http://php.net/fsockopen) +or [pfsockopen](http://php.net/pfsockopen). + +Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening +the connections between requests. + +You can use a `unix://` prefix to access unix sockets and `udp://` to open UDP sockets instead of the default TCP. + +Basic Example +------------- + +```php +setPersistent(true); + +// Now add the handler +$logger->pushHandler($handler, Logger::DEBUG); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); + +``` + +In this example, using syslog-ng, you should see the log on the log server: + + cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] [] + diff --git a/vendor/monolog/monolog/src/Monolog/ErrorHandler.php b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php new file mode 100644 index 0000000..f21eb28 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Monolog\Handler\AbstractHandler; + +/** + * Monolog error handler + * + * A facility to enable logging of runtime errors, exceptions and fatal errors. + * + * Quick setup: ErrorHandler::register($logger); + * + * @author Jordi Boggiano + */ +class ErrorHandler +{ + private $logger; + + private $previousExceptionHandler; + private $uncaughtExceptionLevel; + + private $previousErrorHandler; + private $errorLevelMap; + + private $hasFatalErrorHandler; + private $fatalLevel; + private $reservedMemory; + private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Registers a new ErrorHandler for a given Logger + * + * By default it will handle errors, exceptions and fatal errors + * + * @param LoggerInterface $logger + * @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling + * @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling + * @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling + * @return ErrorHandler + */ + public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null) + { + $handler = new static($logger); + if ($errorLevelMap !== false) { + $handler->registerErrorHandler($errorLevelMap); + } + if ($exceptionLevel !== false) { + $handler->registerExceptionHandler($exceptionLevel); + } + if ($fatalLevel !== false) { + $handler->registerFatalHandler($fatalLevel); + } + + return $handler; + } + + public function registerExceptionHandler($level = null, $callPrevious = true) + { + $prev = set_exception_handler(array($this, 'handleException')); + $this->uncaughtExceptionLevel = $level; + if ($callPrevious && $prev) { + $this->previousExceptionHandler = $prev; + } + } + + public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1) + { + $prev = set_error_handler(array($this, 'handleError'), $errorTypes); + $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); + if ($callPrevious) { + $this->previousErrorHandler = $prev ?: true; + } + } + + public function registerFatalHandler($level = null, $reservedMemorySize = 20) + { + register_shutdown_function(array($this, 'handleFatalError')); + + $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); + $this->fatalLevel = $level; + $this->hasFatalErrorHandler = true; + } + + protected function defaultErrorLevelMap() + { + return array( + E_ERROR => LogLevel::CRITICAL, + E_WARNING => LogLevel::WARNING, + E_PARSE => LogLevel::ALERT, + E_NOTICE => LogLevel::NOTICE, + E_CORE_ERROR => LogLevel::CRITICAL, + E_CORE_WARNING => LogLevel::WARNING, + E_COMPILE_ERROR => LogLevel::ALERT, + E_COMPILE_WARNING => LogLevel::WARNING, + E_USER_ERROR => LogLevel::ERROR, + E_USER_WARNING => LogLevel::WARNING, + E_USER_NOTICE => LogLevel::NOTICE, + E_STRICT => LogLevel::NOTICE, + E_RECOVERABLE_ERROR => LogLevel::ERROR, + E_DEPRECATED => LogLevel::NOTICE, + E_USER_DEPRECATED => LogLevel::NOTICE, + ); + } + + /** + * @private + */ + public function handleException($e) + { + $this->logger->log( + $this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, + sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), + array('exception' => $e) + ); + + if ($this->previousExceptionHandler) { + call_user_func($this->previousExceptionHandler, $e); + } + + exit(255); + } + + /** + * @private + */ + public function handleError($code, $message, $file = '', $line = 0, $context = array()) + { + if (!(error_reporting() & $code)) { + return; + } + + // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries + if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { + $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; + $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); + } + + if ($this->previousErrorHandler === true) { + return false; + } elseif ($this->previousErrorHandler) { + return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context); + } + } + + /** + * @private + */ + public function handleFatalError() + { + $this->reservedMemory = null; + + $lastError = error_get_last(); + if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) { + $this->logger->log( + $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, + 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], + array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) + ); + + if ($this->logger instanceof Logger) { + foreach ($this->logger->getHandlers() as $handler) { + if ($handler instanceof AbstractHandler) { + $handler->close(); + } + } + } + } + } + + private static function codeToString($code) + { + switch ($code) { + case E_ERROR: + return 'E_ERROR'; + case E_WARNING: + return 'E_WARNING'; + case E_PARSE: + return 'E_PARSE'; + case E_NOTICE: + return 'E_NOTICE'; + case E_CORE_ERROR: + return 'E_CORE_ERROR'; + case E_CORE_WARNING: + return 'E_CORE_WARNING'; + case E_COMPILE_ERROR: + return 'E_COMPILE_ERROR'; + case E_COMPILE_WARNING: + return 'E_COMPILE_WARNING'; + case E_USER_ERROR: + return 'E_USER_ERROR'; + case E_USER_WARNING: + return 'E_USER_WARNING'; + case E_USER_NOTICE: + return 'E_USER_NOTICE'; + case E_STRICT: + return 'E_STRICT'; + case E_RECOVERABLE_ERROR: + return 'E_RECOVERABLE_ERROR'; + case E_DEPRECATED: + return 'E_DEPRECATED'; + case E_USER_DEPRECATED: + return 'E_USER_DEPRECATED'; + } + + return 'Unknown PHP error'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php new file mode 100644 index 0000000..56d3e27 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats a log message according to the ChromePHP array format + * + * @author Christophe Coevoet + */ +class ChromePHPFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'log', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + Logger::EMERGENCY => 'error', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $backtrace = 'unknown'; + if (isset($record['extra']['file']) && isset($record['extra']['line'])) { + $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; + unset($record['extra']['file']); + unset($record['extra']['line']); + } + + $message = array('message' => $record['message']); + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + return array( + $record['channel'], + $message, + $backtrace, + $this->logLevels[$record['level']], + ); + } + + public function formatBatch(array $records) + { + $formatted = array(); + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php new file mode 100644 index 0000000..b0b0cf0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Elastica\Document; + +/** + * Format a log message into an Elastica Document + * + * @author Jelle Vink + */ +class ElasticaFormatter extends NormalizerFormatter +{ + /** + * @var string Elastic search index name + */ + protected $index; + + /** + * @var string Elastic search document type + */ + protected $type; + + /** + * @param string $index Elastic Search index name + * @param string $type Elastic Search document type + */ + public function __construct($index, $type) + { + parent::__construct(\DateTime::ISO8601); + $this->index = $index; + $this->type = $type; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + return $this->getDocument($record); + } + + /** + * Getter index + * @return string + */ + public function getIndex() + { + return $this->index; + } + + /** + * Getter type + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Convert a log message into an Elastica Document + * + * @param array $record Log message + * @return Document + */ + protected function getDocument($record) + { + $document = new Document(); + $document->setData($record); + $document->setType($this->type); + $document->setIndex($this->index); + + return $document; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php new file mode 100644 index 0000000..5094af3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * formats the record to be used in the FlowdockHandler + * + * @author Dominik Liebler + */ +class FlowdockFormatter implements FormatterInterface +{ + /** + * @var string + */ + private $source; + + /** + * @var string + */ + private $sourceEmail; + + /** + * @param string $source + * @param string $sourceEmail + */ + public function __construct($source, $sourceEmail) + { + $this->source = $source; + $this->sourceEmail = $sourceEmail; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $tags = array( + '#logs', + '#' . strtolower($record['level_name']), + '#' . $record['channel'], + ); + + foreach ($record['extra'] as $value) { + $tags[] = '#' . $value; + } + + $subject = sprintf( + 'in %s: %s - %s', + $this->source, + $record['level_name'], + $this->getShortMessage($record['message']) + ); + + $record['flowdock'] = array( + 'source' => $this->source, + 'from_address' => $this->sourceEmail, + 'subject' => $subject, + 'content' => $record['message'], + 'tags' => $tags, + 'project' => $this->source, + ); + + return $record; + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + $formatted = array(); + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } + + /** + * @param string $message + * + * @return string + */ + public function getShortMessage($message) + { + static $hasMbString; + + if (null === $hasMbString) { + $hasMbString = function_exists('mb_strlen'); + } + + $maxLength = 45; + + if ($hasMbString) { + if (mb_strlen($message, 'UTF-8') > $maxLength) { + $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...'; + } + } else { + if (strlen($message) > $maxLength) { + $message = substr($message, 0, $maxLength - 4) . ' ...'; + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php new file mode 100644 index 0000000..b5de751 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Interface for formatters + * + * @author Jordi Boggiano + */ +interface FormatterInterface +{ + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + */ + public function format(array $record); + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + */ + public function formatBatch(array $records); +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php new file mode 100644 index 0000000..1e43175 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Gelf\Message; + +/** + * Serializes a log message to GELF + * @see http://www.graylog2.org/about/gelf + * + * @author Matt Lehner + */ +class GelfMessageFormatter extends NormalizerFormatter +{ + /** + * @var string the name of the system for the Gelf log message + */ + protected $systemName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * Translates Monolog log levels to Graylog2 log priorities. + */ + private $logLevels = array( + Logger::DEBUG => 7, + Logger::INFO => 6, + Logger::NOTICE => 5, + Logger::WARNING => 4, + Logger::ERROR => 3, + Logger::CRITICAL => 2, + Logger::ALERT => 1, + Logger::EMERGENCY => 0, + ); + + public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_') + { + parent::__construct('U.u'); + + $this->systemName = $systemName ?: gethostname(); + + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + if (!isset($record['datetime'], $record['message'], $record['level'])) { + throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); + } + + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setHost($this->systemName) + ->setLevel($this->logLevels[$record['level']]); + + if (isset($record['channel'])) { + $message->setFacility($record['channel']); + } + if (isset($record['extra']['line'])) { + $message->setLine($record['extra']['line']); + unset($record['extra']['line']); + } + if (isset($record['extra']['file'])) { + $message->setFile($record['extra']['file']); + unset($record['extra']['file']); + } + + foreach ($record['extra'] as $key => $val) { + $message->setAdditional($this->extraPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); + } + + foreach ($record['context'] as $key => $val) { + $message->setAdditional($this->contextPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); + } + + if (null === $message->getFile() && isset($record['context']['exception']['file'])) { + if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) { + $message->setFile($matches[1]); + $message->setLine($matches[2]); + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php new file mode 100644 index 0000000..255d288 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats incoming records into an HTML table + * + * This is especially useful for html email logging + * + * @author Tiago Brito + */ +class HtmlFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to html color priorities. + */ + private $logLevels = array( + Logger::DEBUG => '#cccccc', + Logger::INFO => '#468847', + Logger::NOTICE => '#3a87ad', + Logger::WARNING => '#c09853', + Logger::ERROR => '#f0ad4e', + Logger::CRITICAL => '#FF7708', + Logger::ALERT => '#C12A19', + Logger::EMERGENCY => '#000000', + ); + + /** + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($dateFormat = null) + { + parent::__construct($dateFormat); + } + + /** + * Creates an HTML table row + * + * @param string $th Row header content + * @param string $td Row standard cell content + * @param bool $escapeTd false if td content must not be html escaped + * @return string + */ + private function addRow($th, $td = ' ', $escapeTd = true) + { + $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); + if ($escapeTd) { + $td = '
    '.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
    '; + } + + return "\n$th:\n".$td."\n"; + } + + /** + * Create a HTML h1 tag + * + * @param string $title Text to be in the h1 + * @param integer $level Error level + * @return string + */ + private function addTitle($title, $level) + { + $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); + + return '

    '.$title.'

    '; + } + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + */ + public function format(array $record) + { + $output = $this->addTitle($record['level_name'], $record['level']); + $output .= ''; + + $output .= $this->addRow('Message', (string) $record['message']); + $output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat)); + $output .= $this->addRow('Channel', $record['channel']); + if ($record['context']) { + $embeddedTable = '
    '; + foreach ($record['context'] as $key => $value) { + $embeddedTable .= $this->addRow($key, $this->convertToString($value)); + } + $embeddedTable .= '
    '; + $output .= $this->addRow('Context', $embeddedTable, false); + } + if ($record['extra']) { + $embeddedTable = ''; + foreach ($record['extra'] as $key => $value) { + $embeddedTable .= $this->addRow($key, $this->convertToString($value)); + } + $embeddedTable .= '
    '; + $output .= $this->addRow('Extra', $embeddedTable, false); + } + + return $output.''; + } + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + */ + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + protected function convertToString($data) + { + if (null === $data || is_scalar($data)) { + return (string) $data; + } + + $data = $this->normalize($data); + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return str_replace('\\/', '/', json_encode($data)); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php new file mode 100644 index 0000000..e5a1d2c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes whatever record data is passed to it as json + * + * This can be useful to log to databases or remote APIs + * + * @author Jordi Boggiano + */ +class JsonFormatter implements FormatterInterface +{ + const BATCH_MODE_JSON = 1; + const BATCH_MODE_NEWLINES = 2; + + protected $batchMode; + protected $appendNewline; + + /** + * @param int $batchMode + */ + public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true) + { + $this->batchMode = $batchMode; + $this->appendNewline = $appendNewline; + } + + /** + * The batch mode option configures the formatting style for + * multiple records. By default, multiple records will be + * formatted as a JSON-encoded array. However, for + * compatibility with some API endpoints, alternative styles + * are available. + * + * @return int + */ + public function getBatchMode() + { + return $this->batchMode; + } + + /** + * True if newlines are appended to every formatted record + * + * @return bool + */ + public function isAppendingNewlines() + { + return $this->appendNewline; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return json_encode($record) . ($this->appendNewline ? "\n" : ''); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + switch ($this->batchMode) { + case static::BATCH_MODE_NEWLINES: + return $this->formatBatchNewlines($records); + + case static::BATCH_MODE_JSON: + default: + return $this->formatBatchJson($records); + } + } + + /** + * Return a JSON-encoded array of records. + * + * @param array $records + * @return string + */ + protected function formatBatchJson(array $records) + { + return json_encode($records); + } + + /** + * Use new lines to separate records instead of a + * JSON-encoded array. + * + * @param array $records + * @return string + */ + protected function formatBatchNewlines(array $records) + { + $instance = $this; + + $oldNewline = $this->appendNewline; + $this->appendNewline = false; + array_walk($records, function (&$value, $key) use ($instance) { + $value = $instance->format($value); + }); + $this->appendNewline = $oldNewline; + + return implode("\n", $records); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php new file mode 100644 index 0000000..388e226 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Exception; + +/** + * Formats incoming records into a one-line string + * + * This is especially useful for logging to files + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class LineFormatter extends NormalizerFormatter +{ + const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; + + protected $format; + protected $allowInlineLineBreaks; + protected $ignoreEmptyContextAndExtra; + protected $includeStacktraces; + + /** + * @param string $format The format of the message + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries + * @param bool $ignoreEmptyContextAndExtra + */ + public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false) + { + $this->format = $format ?: static::SIMPLE_FORMAT; + $this->allowInlineLineBreaks = $allowInlineLineBreaks; + $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; + parent::__construct($dateFormat); + } + + public function includeStacktraces($include = true) + { + $this->includeStacktraces = $include; + if ($this->includeStacktraces) { + $this->allowInlineLineBreaks = true; + } + } + + public function allowInlineLineBreaks($allow = true) + { + $this->allowInlineLineBreaks = $allow; + } + + public function ignoreEmptyContextAndExtra($ignore = true) + { + $this->ignoreEmptyContextAndExtra = $ignore; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $vars = parent::format($record); + + $output = $this->format; + + foreach ($vars['extra'] as $var => $val) { + if (false !== strpos($output, '%extra.'.$var.'%')) { + $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); + unset($vars['extra'][$var]); + } + } + + if ($this->ignoreEmptyContextAndExtra) { + if (empty($vars['context'])) { + unset($vars['context']); + $output = str_replace('%context%', '', $output); + } + + if (empty($vars['extra'])) { + unset($vars['extra']); + $output = str_replace('%extra%', '', $output); + } + } + + foreach ($vars as $var => $val) { + if (false !== strpos($output, '%'.$var.'%')) { + $output = str_replace('%'.$var.'%', $this->stringify($val), $output); + } + } + + return $output; + } + + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + public function stringify($value) + { + return $this->replaceNewlines($this->convertToString($value)); + } + + protected function normalizeException(Exception $e) + { + $previousText = ''; + if ($previous = $e->getPrevious()) { + do { + $previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); + } while ($previous = $previous->getPrevious()); + } + + $str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')'; + if ($this->includeStacktraces) { + $str .= "\n[stacktrace]\n".$e->getTraceAsString(); + } + + return $str; + } + + protected function convertToString($data) + { + if (null === $data || is_bool($data)) { + return var_export($data, true); + } + + if (is_scalar($data)) { + return (string) $data; + } + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return $this->toJson($data, true); + } + + return str_replace('\\/', '/', @json_encode($data)); + } + + protected function replaceNewlines($str) + { + if ($this->allowInlineLineBreaks) { + return $str; + } + + return str_replace(array("\r\n", "\r", "\n"), ' ', $str); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php new file mode 100644 index 0000000..f02bceb --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.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 Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Loggly. + * + * @author Adam Pancutt + */ +class LogglyFormatter extends JsonFormatter +{ + /** + * Overrides the default batch mode to new lines for compatibility with the + * Loggly bulk API. + * + * @param integer $batchMode + */ + public function __construct($batchMode = self::BATCH_MODE_NEWLINES, $appendNewline = false) + { + parent::__construct($batchMode, $appendNewline); + } + + /** + * Appends the 'timestamp' parameter for indexing by Loggly. + * + * @see https://www.loggly.com/docs/automated-parsing/#json + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record) + { + if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTime)) { + $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO"); + // TODO 2.0 unset the 'datetime' parameter, retained for BC + } + + return parent::format($record); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php new file mode 100644 index 0000000..be21b2c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Serializes a log message to Logstash Event Format + * + * @see http://logstash.net/ + * @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb + * + * @author Tim Mower + */ +class LogstashFormatter extends NormalizerFormatter +{ + const V0 = 0; + const V1 = 1; + + /** + * @var string the name of the system for the Logstash log message, used to fill the @source field + */ + protected $systemName; + + /** + * @var string an application name for the Logstash log message, used to fill the @type field + */ + protected $applicationName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * @var integer logstash format version to use + */ + protected $version; + + /** + * @param string $applicationName the application that sends the data, used as the "type" field of logstash + * @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine + * @param string $extraPrefix prefix for extra keys inside logstash "fields" + * @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_ + * @param integer $version the logstash format version to use, defaults to 0 + */ + public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $version = self::V0) + { + // logstash requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->systemName = $systemName ?: gethostname(); + $this->applicationName = $applicationName; + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + $this->version = $version; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + if ($this->version === self::V1) { + $message = $this->formatV1($record); + } else { + $message = $this->formatV0($record); + } + + return $this->toJson($message) . "\n"; + } + + protected function formatV0(array $record) + { + if (empty($record['datetime'])) { + $record['datetime'] = gmdate('c'); + } + $message = array( + '@timestamp' => $record['datetime'], + '@source' => $this->systemName, + '@fields' => array() + ); + if (isset($record['message'])) { + $message['@message'] = $record['message']; + } + if (isset($record['channel'])) { + $message['@tags'] = array($record['channel']); + $message['@fields']['channel'] = $record['channel']; + } + if (isset($record['level'])) { + $message['@fields']['level'] = $record['level']; + } + if ($this->applicationName) { + $message['@type'] = $this->applicationName; + } + if (isset($record['extra']['server'])) { + $message['@source_host'] = $record['extra']['server']; + } + if (isset($record['extra']['url'])) { + $message['@source_path'] = $record['extra']['url']; + } + if (!empty($record['extra'])) { + foreach ($record['extra'] as $key => $val) { + $message['@fields'][$this->extraPrefix . $key] = $val; + } + } + if (!empty($record['context'])) { + foreach ($record['context'] as $key => $val) { + $message['@fields'][$this->contextPrefix . $key] = $val; + } + } + + return $message; + } + + protected function formatV1(array $record) + { + if (empty($record['datetime'])) { + $record['datetime'] = gmdate('c'); + } + $message = array( + '@timestamp' => $record['datetime'], + '@version' => 1, + 'host' => $this->systemName, + ); + if (isset($record['message'])) { + $message['message'] = $record['message']; + } + if (isset($record['channel'])) { + $message['type'] = $record['channel']; + $message['channel'] = $record['channel']; + } + if (isset($record['level_name'])) { + $message['level'] = $record['level_name']; + } + if ($this->applicationName) { + $message['type'] = $this->applicationName; + } + if (!empty($record['extra'])) { + foreach ($record['extra'] as $key => $val) { + $message[$this->extraPrefix . $key] = $val; + } + } + if (!empty($record['context'])) { + foreach ($record['context'] as $key => $val) { + $message[$this->contextPrefix . $key] = $val; + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php new file mode 100644 index 0000000..eb067bb --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Formats a record for use with the MongoDBHandler. + * + * @author Florian Plattner + */ +class MongoDBFormatter implements FormatterInterface +{ + private $exceptionTraceAsString; + private $maxNestingLevel; + + /** + * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2 + * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings + */ + public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true) + { + $this->maxNestingLevel = max($maxNestingLevel, 0); + $this->exceptionTraceAsString = (bool) $exceptionTraceAsString; + } + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + return $this->formatArray($record); + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + protected function formatArray(array $record, $nestingLevel = 0) + { + if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) { + foreach ($record as $name => $value) { + if ($value instanceof \DateTime) { + $record[$name] = $this->formatDate($value, $nestingLevel + 1); + } elseif ($value instanceof \Exception) { + $record[$name] = $this->formatException($value, $nestingLevel + 1); + } elseif (is_array($value)) { + $record[$name] = $this->formatArray($value, $nestingLevel + 1); + } elseif (is_object($value)) { + $record[$name] = $this->formatObject($value, $nestingLevel + 1); + } + } + } else { + $record = '[...]'; + } + + return $record; + } + + protected function formatObject($value, $nestingLevel) + { + $objectVars = get_object_vars($value); + $objectVars['class'] = get_class($value); + + return $this->formatArray($objectVars, $nestingLevel); + } + + protected function formatException(\Exception $exception, $nestingLevel) + { + $formattedException = array( + 'class' => get_class($exception), + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + ); + + if ($this->exceptionTraceAsString === true) { + $formattedException['trace'] = $exception->getTraceAsString(); + } else { + $formattedException['trace'] = $exception->getTrace(); + } + + return $this->formatArray($formattedException, $nestingLevel); + } + + protected function formatDate(\DateTime $value, $nestingLevel) + { + return new \MongoDate($value->getTimestamp()); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php new file mode 100644 index 0000000..0361935 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Exception; + +/** + * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets + * + * @author Jordi Boggiano + */ +class NormalizerFormatter implements FormatterInterface +{ + const SIMPLE_DATE = "Y-m-d H:i:s"; + + protected $dateFormat; + + /** + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($dateFormat = null) + { + $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); + } + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return $this->normalize($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + protected function normalize($data) + { + if (null === $data || is_scalar($data)) { + if (is_float($data)) { + if (is_infinite($data)) { + return ($data > 0 ? '' : '-') . 'INF'; + } + if (is_nan($data)) { + return 'NaN'; + } + } + + return $data; + } + + if (is_array($data) || $data instanceof \Traversable) { + $normalized = array(); + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ >= 1000) { + $normalized['...'] = 'Over 1000 items, aborting normalization'; + break; + } + $normalized[$key] = $this->normalize($value); + } + + return $normalized; + } + + if ($data instanceof \DateTime) { + return $data->format($this->dateFormat); + } + + if (is_object($data)) { + if ($data instanceof Exception) { + return $this->normalizeException($data); + } + + // non-serializable objects that implement __toString stringified + if (method_exists($data, '__toString') && !$data instanceof \JsonSerializable) { + $value = (string) $data; + } else { + // the rest is json-serialized in some way + $value = $this->toJson($data, true); + } + + return sprintf("[object] (%s: %s)", get_class($data), $value); + } + + if (is_resource($data)) { + return sprintf('[resource] (%s)', get_resource_type($data)); + } + + return '[unknown('.gettype($data).')]'; + } + + protected function normalizeException(Exception $e) + { + $data = array( + 'class' => get_class($e), + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile().':'.$e->getLine(), + ); + + $trace = $e->getTrace(); + foreach ($trace as $frame) { + if (isset($frame['file'])) { + $data['trace'][] = $frame['file'].':'.$frame['line']; + } else { + // We should again normalize the frames, because it might contain invalid items + $data['trace'][] = $this->toJson($this->normalize($frame), true); + } + } + + if ($previous = $e->getPrevious()) { + $data['previous'] = $this->normalizeException($previous); + } + + return $data; + } + + protected function toJson($data, $ignoreErrors = false) + { + // suppress json_encode errors since it's twitchy with some inputs + if ($ignoreErrors) { + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return @json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return @json_encode($data); + } + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + $json = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } else { + $json = json_encode($data); + } + + if ($json === false) { + $this->throwEncodeError(json_last_error(), $data); + } + + return $json; + } + + /** + * Throws an exception according to a given code with a customized message + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException + */ + private function throwEncodeError($code, $data) + { + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + + throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php new file mode 100644 index 0000000..5d345d5 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Formats data into an associative array of scalar values. + * Objects and arrays will be JSON encoded. + * + * @author Andrew Lawson + */ +class ScalarFormatter extends NormalizerFormatter +{ + /** + * {@inheritdoc} + */ + public function format(array $record) + { + foreach ($record as $key => $value) { + $record[$key] = $this->normalizeValue($value); + } + + return $record; + } + + /** + * @param mixed $value + * @return mixed + */ + protected function normalizeValue($value) + { + $normalized = $this->normalize($value); + + if (is_array($normalized) || is_object($normalized)) { + return $this->toJson($normalized, true); + } + + return $normalized; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php new file mode 100644 index 0000000..654710a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Eric Clemmons (@ericclemmons) + * @author Christophe Coevoet + * @author Kirill chEbba Chebunin + */ +class WildfireFormatter extends NormalizerFormatter +{ + const TABLE = 'table'; + + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'LOG', + Logger::INFO => 'INFO', + Logger::NOTICE => 'INFO', + Logger::WARNING => 'WARN', + Logger::ERROR => 'ERROR', + Logger::CRITICAL => 'ERROR', + Logger::ALERT => 'ERROR', + Logger::EMERGENCY => 'ERROR', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $file = $line = ''; + if (isset($record['extra']['file'])) { + $file = $record['extra']['file']; + unset($record['extra']['file']); + } + if (isset($record['extra']['line'])) { + $line = $record['extra']['line']; + unset($record['extra']['line']); + } + + $record = $this->normalize($record); + $message = array('message' => $record['message']); + $handleError = false; + if ($record['context']) { + $message['context'] = $record['context']; + $handleError = true; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + $handleError = true; + } + if (count($message) === 1) { + $message = reset($message); + } + + if (isset($record['context'][self::TABLE])) { + $type = 'TABLE'; + $label = $record['channel'] .': '. $record['message']; + $message = $record['context'][self::TABLE]; + } else { + $type = $this->logLevels[$record['level']]; + $label = $record['channel']; + } + + // Create JSON object describing the appearance of the message in the console + $json = $this->toJson(array( + array( + 'Type' => $type, + 'File' => $file, + 'Line' => $line, + 'Label' => $label, + ), + $message, + ), $handleError); + + // The message itself is a serialization of the above JSON object + it's length + return sprintf( + '%s|%s|', + strlen($json), + $json + ); + } + + public function formatBatch(array $records) + { + throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); + } + + protected function normalize($data) + { + if (is_object($data) && !$data instanceof \DateTime) { + return $data; + } + + return parent::normalize($data); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php new file mode 100644 index 0000000..69ede49 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Base Handler class providing the Handler structure + * + * @author Jordi Boggiano + */ +abstract class AbstractHandler implements HandlerInterface +{ + protected $level = Logger::DEBUG; + protected $bubble = true; + + /** + * @var FormatterInterface + */ + protected $formatter; + protected $processors = array(); + + /** + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + $this->setLevel($level); + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * Closes the handler. + * + * This will be called automatically when the object is destroyed + */ + public function close() + { + } + + /** + * {@inheritdoc} + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Sets minimum logging level at which this handler will be triggered. + * + * @param integer $level + * @return self + */ + public function setLevel($level) + { + $this->level = Logger::toMonologLevel($level); + + return $this; + } + + /** + * Gets minimum logging level at which this handler will be triggered. + * + * @return integer + */ + public function getLevel() + { + return $this->level; + } + + /** + * Sets the bubbling behavior. + * + * @param Boolean $bubble true means that this handler allows bubbling. + * false means that bubbling is not permitted. + * @return self + */ + public function setBubble($bubble) + { + $this->bubble = $bubble; + + return $this; + } + + /** + * Gets the bubbling behavior. + * + * @return Boolean true means that this handler allows bubbling. + * false means that bubbling is not permitted. + */ + public function getBubble() + { + return $this->bubble; + } + + public function __destruct() + { + try { + $this->close(); + } catch (\Exception $e) { + // do nothing + } + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php new file mode 100644 index 0000000..6f18f72 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing the Handler structure + * + * Classes extending it should (in most cases) only implement write($record) + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +abstract class AbstractProcessingHandler extends AbstractHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + $record = $this->processRecord($record); + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * @return void + */ + abstract protected function write(array $record); + + /** + * Processes a record. + * + * @param array $record + * @return array + */ + protected function processRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php new file mode 100644 index 0000000..3eb83bd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * Common syslog functionality + */ +abstract class AbstractSyslogHandler extends AbstractProcessingHandler +{ + protected $facility; + + /** + * Translates Monolog log levels to syslog log priorities. + */ + protected $logLevels = array( + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG, + ); + + /** + * List of valid log facility names. + */ + protected $facilities = array( + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP, + ); + + /** + * @param mixed $facility + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->facilities['local0'] = LOG_LOCAL0; + $this->facilities['local1'] = LOG_LOCAL1; + $this->facilities['local2'] = LOG_LOCAL2; + $this->facilities['local3'] = LOG_LOCAL3; + $this->facilities['local4'] = LOG_LOCAL4; + $this->facilities['local5'] = LOG_LOCAL5; + $this->facilities['local6'] = LOG_LOCAL6; + $this->facilities['local7'] = LOG_LOCAL7; + } + + // convert textual description of facility to syslog constant + if (array_key_exists(strtolower($facility), $this->facilities)) { + $facility = $this->facilities[strtolower($facility)]; + } elseif (!in_array($facility, array_values($this->facilities), true)) { + throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); + } + + $this->facility = $facility; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php new file mode 100644 index 0000000..a28ba02 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\JsonFormatter; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Channel\AMQPChannel; +use AMQPExchange; + +class AmqpHandler extends AbstractProcessingHandler +{ + /** + * @var AMQPExchange|AMQPChannel $exchange + */ + protected $exchange; + + /** + * @var string + */ + protected $exchangeName; + + /** + * @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use + * @param string $exchangeName + * @param int $level + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true) + { + if ($exchange instanceof AMQPExchange) { + $exchange->setName($exchangeName); + } elseif ($exchange instanceof AMQPChannel) { + $this->exchangeName = $exchangeName; + } else { + throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required'); + } + $this->exchange = $exchange; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $data = $record["formatted"]; + + $routingKey = sprintf( + '%s.%s', + // TODO 2.0 remove substr call + substr($record['level_name'], 0, 4), + $record['channel'] + ); + + if ($this->exchange instanceof AMQPExchange) { + $this->exchange->publish( + $data, + strtolower($routingKey), + 0, + array( + 'delivery_mode' => 2, + 'Content-type' => 'application/json' + ) + ); + } else { + $this->exchange->basic_publish( + new AMQPMessage( + (string) $data, + array( + 'delivery_mode' => 2, + 'content_type' => 'application/json' + ) + ), + $this->exchangeName, + strtolower($routingKey) + ); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php new file mode 100644 index 0000000..a409952 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; + +/** + * Handler sending logs to browser's javascript console with no browser extension required + * + * @author Olivier Poitrey + */ +class BrowserConsoleHandler extends AbstractProcessingHandler +{ + protected static $initialized = false; + protected static $records = array(); + + /** + * {@inheritDoc} + * + * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. + * + * Example of formatted string: + * + * You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} + * + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + // Accumulate records + self::$records[] = $record; + + // Register shutdown handler if not already done + if (PHP_SAPI !== 'cli' && !self::$initialized) { + self::$initialized = true; + register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send')); + } + } + + /** + * Convert records to javascript console commands and send it to the browser. + * This method is automatically called on PHP shutdown if output is HTML or Javascript. + */ + public static function send() + { + $htmlTags = true; + // Check content type + foreach (headers_list() as $header) { + if (stripos($header, 'content-type:') === 0) { + // This handler only works with HTML and javascript outputs + // text/javascript is obsolete in favour of application/javascript, but still used + if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) { + $htmlTags = false; + } elseif (stripos($header, 'text/html') === false) { + return; + } + break; + } + } + + if (count(self::$records)) { + if ($htmlTags) { + echo ''; + } else { + echo self::generateScript(); + } + self::reset(); + } + } + + /** + * Forget all logged records + */ + public static function reset() + { + self::$records = array(); + } + + private static function generateScript() + { + $script = array(); + foreach (self::$records as $record) { + $context = self::dump('Context', $record['context']); + $extra = self::dump('Extra', $record['extra']); + + if (empty($context) && empty($extra)) { + $script[] = self::call_array('log', self::handleStyles($record['formatted'])); + } else { + $script = array_merge($script, + array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), + $context, + $extra, + array(self::call('groupEnd')) + ); + } + } + + return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; + } + + private static function handleStyles($formatted) + { + $args = array(self::quote('font-weight: normal')); + $format = '%c' . $formatted; + preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + + foreach (array_reverse($matches) as $match) { + $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); + $args[] = '"font-weight: normal"'; + + $pos = $match[0][1]; + $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); + } + + array_unshift($args, self::quote($format)); + + return $args; + } + + private static function handleCustomStyles($style, $string) + { + static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'); + static $labels = array(); + + return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) { + if (trim($m[1]) === 'autolabel') { + // Format the string as a label with consistent auto assigned background color + if (!isset($labels[$string])) { + $labels[$string] = $colors[count($labels) % count($colors)]; + } + $color = $labels[$string]; + + return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; + } + + return $m[1]; + }, $style); + } + + private static function dump($title, array $dict) + { + $script = array(); + $dict = array_filter($dict); + if (empty($dict)) { + return $script; + } + $script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); + foreach ($dict as $key => $value) { + $value = json_encode($value); + if (empty($value)) { + $value = self::quote(''); + } + $script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); + } + + return $script; + } + + private static function quote($arg) + { + return '"' . addcslashes($arg, "\"\n\\") . '"'; + } + + private static function call() + { + $args = func_get_args(); + $method = array_shift($args); + + return self::call_array($method, $args); + } + + private static function call_array($method, array $args) + { + return 'c.' . $method . '(' . implode(', ', $args) . ');'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000..6d8136f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + */ +class BufferHandler extends AbstractHandler +{ + protected $handler; + protected $bufferSize = 0; + protected $bufferLimit; + protected $flushOnOverflow; + protected $buffer = array(); + protected $initialized = false; + + /** + * @param HandlerInterface $handler Handler. + * @param integer $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + */ + public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = (int) $bufferLimit; + $this->flushOnOverflow = $flushOnOverflow; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + $this->initialized = true; + } + + if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { + if ($this->flushOnOverflow) { + $this->flush(); + } else { + array_shift($this->buffer); + $this->bufferSize--; + } + } + + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $this->buffer[] = $record; + $this->bufferSize++; + + return false === $this->bubble; + } + + public function flush() + { + if ($this->bufferSize === 0) { + return; + } + + $this->handler->handleBatch($this->buffer); + $this->clear(); + } + + public function __destruct() + { + // suppress the parent behavior since we already have register_shutdown_function() + // to call close(), and the reference contained there will prevent this from being + // GC'd until the end of the request + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->flush(); + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + */ + public function clear() + { + $this->bufferSize = 0; + $this->buffer = array(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000..b3f2963 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ChromePHPFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * @author Christophe Coevoet + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + /** + * Version of the extension + */ + const VERSION = '4.0'; + + /** + * Header name + */ + const HEADER_NAME = 'X-ChromeLogger-Data'; + + protected static $initialized = false; + + /** + * Tracks whether we sent too much data + * + * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending + * + * @var Boolean + */ + protected static $overflowed = false; + + protected static $json = array( + 'version' => self::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array(), + ); + + protected static $sendHeaders = true; + + /** + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); + } + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + * @param array $record + */ + protected function write(array $record) + { + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send() + { + if (self::$overflowed || !self::$sendHeaders) { + return; + } + + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + } + + $json = @json_encode(self::$json); + $data = base64_encode(utf8_encode($json)); + if (strlen($data) > 240 * 1024) { + self::$overflowed = true; + + $record = array( + 'message' => 'Incomplete logs, chrome header size limit reached', + 'context' => array(), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'monolog', + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); + $json = @json_encode(self::$json); + $data = base64_encode(utf8_encode($json)); + } + + if (trim($data) !== '') { + $this->sendHeader(self::HEADER_NAME, $data); + } + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + if (empty($_SERVER['HTTP_USER_AGENT'])) { + return false; + } + + return preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']); + } + + /** + * BC getter for the sendHeaders property that has been made static + */ + public function __get($property) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + return static::$sendHeaders; + } + + /** + * BC setter for the sendHeaders property that has been made static + */ + public function __set($property, $value) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + static::$sendHeaders = $value; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php new file mode 100644 index 0000000..b3687c3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\JsonFormatter; +use Monolog\Logger; + +/** + * CouchDB handler + * + * @author Markus Bachmann + */ +class CouchDBHandler extends AbstractProcessingHandler +{ + private $options; + + public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true) + { + $this->options = array_merge(array( + 'host' => 'localhost', + 'port' => 5984, + 'dbname' => 'logger', + 'username' => null, + 'password' => null, + ), $options); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $basicAuth = null; + if ($this->options['username']) { + $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); + } + + $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; + $context = stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'content' => $record['formatted'], + 'ignore_errors' => true, + 'max_redirects' => 0, + 'header' => 'Content-type: application/json', + ) + )); + + if (false === @file_get_contents($url, null, $context)) { + throw new \RuntimeException(sprintf('Could not connect to %s', $url)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php new file mode 100644 index 0000000..e7dd854 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Logs to Cube. + * + * @link http://square.github.com/cube/ + * @author Wan Chen + */ +class CubeHandler extends AbstractProcessingHandler +{ + private $udpConnection = null; + private $httpConnection = null; + private $scheme = null; + private $host = null; + private $port = null; + private $acceptedSchemes = array('http', 'udp'); + + /** + * Create a Cube handler + * + * @throws \UnexpectedValueException when given url is not a valid url. + * A valid url must consists of three parts : protocol://host:port + * Only valid protocol used by Cube are http and udp + */ + public function __construct($url, $level = Logger::DEBUG, $bubble = true) + { + $urlInfos = parse_url($url); + + if (!isset($urlInfos['scheme']) || !isset($urlInfos['host']) || !isset($urlInfos['port'])) { + throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); + } + + if (!in_array($urlInfos['scheme'], $this->acceptedSchemes)) { + throw new \UnexpectedValueException( + 'Invalid protocol (' . $urlInfos['scheme'] . ').' + . ' Valid options are ' . implode(', ', $this->acceptedSchemes)); + } + + $this->scheme = $urlInfos['scheme']; + $this->host = $urlInfos['host']; + $this->port = $urlInfos['port']; + + parent::__construct($level, $bubble); + } + + /** + * Establish a connection to an UDP socket + * + * @throws \LogicException when unable to connect to the socket + */ + protected function connectUdp() + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); + } + + $this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); + if (!$this->udpConnection) { + throw new \LogicException('Unable to create a socket'); + } + + if (!socket_connect($this->udpConnection, $this->host, $this->port)) { + throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); + } + } + + /** + * Establish a connection to a http server + */ + protected function connectHttp() + { + if (!extension_loaded('curl')) { + throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler'); + } + + $this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); + + if (!$this->httpConnection) { + throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); + } + + curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $date = $record['datetime']; + + $data = array('time' => $date->format('Y-m-d\TH:i:s.uO')); + unset($record['datetime']); + + if (isset($record['context']['type'])) { + $data['type'] = $record['context']['type']; + unset($record['context']['type']); + } else { + $data['type'] = $record['channel']; + } + + $data['data'] = $record['context']; + $data['data']['level'] = $record['level']; + + if ($this->scheme === 'http') { + $this->writeHttp(json_encode($data)); + } else { + $this->writeUdp(json_encode($data)); + } + } + + private function writeUdp($data) + { + if (!$this->udpConnection) { + $this->connectUdp(); + } + + socket_send($this->udpConnection, $data, strlen($data), 0); + } + + private function writeHttp($data) + { + if (!$this->httpConnection) { + $this->connectHttp(); + } + + curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); + curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json', + 'Content-Length: ' . strlen('['.$data.']')) + ); + + Curl\Util::execute($ch, 5, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php b/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php new file mode 100644 index 0000000..adbe4f4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Curl; + +class Util +{ + private static $retriableErrorCodes = array( + CURLE_COULDNT_RESOLVE_HOST, + CURLE_COULDNT_CONNECT, + CURLE_HTTP_NOT_FOUND, + CURLE_READ_ERROR, + CURLE_OPERATION_TIMEOUTED, + CURLE_HTTP_POST_ERROR, + CURLE_SSL_CONNECT_ERROR, + ); + + /** + * Executes a CURL request with optional retries and exception on failure + * + * @param resource $ch curl handler + * @throws \RuntimeException + */ + public static function execute($ch, $retries = 5, $closeAfterDone = true) + { + while ($retries--) { + if (curl_exec($ch) === false) { + $curlErrno = curl_errno($ch); + + if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { + $curlError = curl_error($ch); + + if ($closeAfterDone) { + curl_close($ch); + } + + throw new \RuntimeException(sprintf('Curl error (code %s): %s', $curlErrno, $curlError)); + } + + continue; + } + + if ($closeAfterDone) { + curl_close($ch); + } + break; + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php new file mode 100644 index 0000000..b91ffec --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; +use Doctrine\CouchDB\CouchDBClient; + +/** + * CouchDB handler for Doctrine CouchDB ODM + * + * @author Markus Bachmann + */ +class DoctrineCouchDBHandler extends AbstractProcessingHandler +{ + private $client; + + public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = true) + { + $this->client = $client; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $this->client->postDocument($record['formatted']); + } + + protected function getDefaultFormatter() + { + return new NormalizerFormatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php new file mode 100644 index 0000000..e7f843c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Aws\Common\Aws; +use Aws\DynamoDb\DynamoDbClient; +use Monolog\Formatter\ScalarFormatter; +use Monolog\Logger; + +/** + * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) + * + * @link https://github.com/aws/aws-sdk-php/ + * @author Andrew Lawson + */ +class DynamoDbHandler extends AbstractProcessingHandler +{ + const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; + + /** + * @var DynamoDbClient + */ + protected $client; + + /** + * @var string + */ + protected $table; + + /** + * @param DynamoDbClient $client + * @param string $table + * @param integer $level + * @param boolean $bubble + */ + public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = true) + { + if (!defined('Aws\Common\Aws::VERSION') || version_compare('3.0', Aws::VERSION, '<=')) { + throw new \RuntimeException('The DynamoDbHandler is only known to work with the AWS SDK 2.x releases'); + } + + $this->client = $client; + $this->table = $table; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $filtered = $this->filterEmptyFields($record['formatted']); + $formatted = $this->client->formatAttributes($filtered); + + $this->client->putItem(array( + 'TableName' => $this->table, + 'Item' => $formatted + )); + } + + /** + * @param array $record + * @return array + */ + protected function filterEmptyFields(array $record) + { + return array_filter($record, function ($value) { + return !empty($value) || false === $value || 0 === $value; + }); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new ScalarFormatter(self::DATE_FORMAT); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php new file mode 100644 index 0000000..96e5d57 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticaFormatter; +use Monolog\Logger; +use Elastica\Client; +use Elastica\Exception\ExceptionInterface; + +/** + * Elastic Search handler + * + * Usage example: + * + * $client = new \Elastica\Client(); + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', + * ); + * $handler = new ElasticSearchHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Jelle Vink + */ +class ElasticSearchHandler extends AbstractProcessingHandler +{ + /** + * @var Client + */ + protected $client; + + /** + * @var array Handler config options + */ + protected $options = array(); + + /** + * @param Client $client Elastica Client object + * @param array $options Handler configuration + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + array( + 'index' => 'monolog', // Elastic index name + 'type' => 'record', // Elastic document type + 'ignore_error' => false, // Suppress Elastica exceptions + ), + $options + ); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $this->bulkSend(array($record['formatted'])); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + if ($formatter instanceof ElasticaFormatter) { + return parent::setFormatter($formatter); + } + throw new \InvalidArgumentException('ElasticSearchHandler is only compatible with ElasticaFormatter'); + } + + /** + * Getter options + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new ElasticaFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * @param array $documents + * @throws \RuntimeException + */ + protected function bulkSend(array $documents) + { + try { + $this->client->addDocuments($documents); + } catch (ExceptionInterface $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php new file mode 100644 index 0000000..d1e1ee6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Stores to PHP error_log() handler. + * + * @author Elan Ruusamäe + */ +class ErrorLogHandler extends AbstractProcessingHandler +{ + const OPERATING_SYSTEM = 0; + const SAPI = 4; + + protected $messageType; + protected $expandNewlines; + + /** + * @param integer $messageType Says where the error should go. + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + */ + public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false) + { + parent::__construct($level, $bubble); + + if (false === in_array($messageType, self::getAvailableTypes())) { + $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); + throw new \InvalidArgumentException($message); + } + + $this->messageType = $messageType; + $this->expandNewlines = $expandNewlines; + } + + /** + * @return array With all available types + */ + public static function getAvailableTypes() + { + return array( + self::OPERATING_SYSTEM, + self::SAPI, + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if ($this->expandNewlines) { + $lines = preg_split('{[\r\n]+}', (string) $record['formatted']); + foreach ($lines as $line) { + error_log($line, $this->messageType); + } + } else { + error_log((string) $record['formatted'], $this->messageType); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php new file mode 100644 index 0000000..dad8227 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Simple handler wrapper that filters records based on a list of levels + * + * It can be configured with an exact list of levels to allow, or a min/max level. + * + * @author Hennadiy Verkh + * @author Jordi Boggiano + */ +class FilterHandler extends AbstractHandler +{ + /** + * Handler or factory callable($record, $this) + * + * @var callable|\Monolog\Handler\HandlerInterface + */ + protected $handler; + + /** + * Minimum level for logs that are passes to handler + * + * @var int[] + */ + protected $acceptedLevels; + + /** + * Whether the messages that are handled can bubble up the stack or not + * + * @var Boolean + */ + protected $bubble; + + /** + * @param callable|HandlerInterface $handler Handler or factory callable($record, $this). + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true) + { + $this->handler = $handler; + $this->bubble = $bubble; + $this->setAcceptedLevels($minLevelOrList, $maxLevel); + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * @return array + */ + public function getAcceptedLevels() + { + return array_flip($this->acceptedLevels); + } + + /** + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + */ + public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY) + { + if (is_array($minLevelOrList)) { + $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); + } else { + $minLevelOrList = Logger::toMonologLevel($minLevelOrList); + $maxLevel = Logger::toMonologLevel($maxLevel); + $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) { + return $level >= $minLevelOrList && $level <= $maxLevel; + })); + } + $this->acceptedLevels = array_flip($acceptedLevels); + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return isset($this->acceptedLevels[$record['level']]); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + // The same logic as in FingersCrossedHandler + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $this->handler->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $filtered = array(); + foreach ($records as $record) { + if ($this->isHandling($record)) { + $filtered[] = $record; + } + } + + $this->handler->handleBatch($filtered); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000..c3e42ef --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @param array $record + * @return Boolean + */ + public function isHandlerActivated(array $record); +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php new file mode 100644 index 0000000..e3b403f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php @@ -0,0 +1,59 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; + +/** + * Channel and Error level based monolog activation strategy. Allows to trigger activation + * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except + * for records of the 'sql' channel; those should trigger activation on level 'WARN'. + * + * Example: + * + * + * $activationStrategy = new ChannelLevelActivationStrategy( + * Logger::CRITICAL, + * array( + * 'request' => Logger::ALERT, + * 'sensitive' => Logger::ERROR, + * ) + * ); + * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); + * + * + * @author Mike Meessen + */ +class ChannelLevelActivationStrategy implements ActivationStrategyInterface +{ + private $defaultActionLevel; + private $channelToActionLevel; + + /** + * @param int $defaultActionLevel The default action level to be used if the record's category doesn't match any + * @param array $channelToActionLevel An array that maps channel names to action levels. + */ + public function __construct($defaultActionLevel, $channelToActionLevel = array()) + { + $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); + $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); + } + + public function isHandlerActivated(array $record) + { + if (isset($this->channelToActionLevel[$record['channel']])) { + return $record['level'] >= $this->channelToActionLevel[$record['channel']]; + } + + return $record['level'] >= $this->defaultActionLevel; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..6e63085 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +use Monolog\Logger; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + private $actionLevel; + + public function __construct($actionLevel) + { + $this->actionLevel = Logger::toMonologLevel($actionLevel); + } + + public function isHandlerActivated(array $record) + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000..30a85dd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * You can find the various activation strategies in the + * Monolog\Handler\FingersCrossed\ namespace. + * + * @author Jordi Boggiano + */ +class FingersCrossedHandler extends AbstractHandler +{ + protected $handler; + protected $activationStrategy; + protected $buffering = true; + protected $bufferSize; + protected $buffer = array(); + protected $stopBuffering; + protected $passthruLevel; + + /** + * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). + * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + */ + public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + + // convert simple int activationStrategy to an object + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + + if ($passthruLevel !== null) { + $this->passthruLevel = Logger::toMonologLevel($passthruLevel); + } + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + if ($this->stopBuffering) { + $this->buffering = false; + } + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + } else { + $this->handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = array_filter($this->buffer, function ($record) use ($level) { + return $record['level'] >= $level; + }); + if (count($this->buffer) > 0) { + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + } + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + public function reset() + { + $this->buffering = true; + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + * + * It also resets the handler to its initial buffering state. + */ + public function clear() + { + $this->buffer = array(); + $this->reset(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..fee4795 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\WildfireFormatter; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + /** + * WildFire JSON header message format + */ + const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + protected static $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * @return array Complete header string ready for the client as key and message as value + */ + protected function createHeader(array $meta, $message) + { + $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); + + return array($header => $message); + } + + /** + * Creates message header from record + * + * @see createHeader() + * @param array $record + * @return string + */ + protected function createRecordHeader(array $record) + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + array(1, 1, 1, self::$messageIndex++), + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * @return array + */ + protected function getInitHeaders() + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), + $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), + $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + * @param array $record + */ + protected function write(array $record) + { + if (!self::$sendHeaders) { + return; + } + + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + } + + $header = $this->createRecordHeader($record); + if (trim(current($header)) !== '') { + $this->sendHeader(key($header), current($header)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { + return true; + } + + return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } + + /** + * BC getter for the sendHeaders property that has been made static + */ + public function __get($property) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + return static::$sendHeaders; + } + + /** + * BC setter for the sendHeaders property that has been made static + */ + public function __set($property, $value) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + static::$sendHeaders = $value; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php new file mode 100644 index 0000000..388692c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Sends logs to Fleep.io using Webhook integrations + * + * You'll need a Fleep.io account to use this handler. + * + * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation + * @author Ando Roots + */ +class FleepHookHandler extends SocketHandler +{ + const FLEEP_HOST = 'fleep.io'; + + const FLEEP_HOOK_URI = '/hook/'; + + /** + * @var string Webhook token (specifies the conversation where logs are sent) + */ + protected $token; + + /** + * Construct a new Fleep.io Handler. + * + * For instructions on how to create a new web hook in your conversations + * see https://fleep.io/integrations/webhooks/ + * + * @param string $token Webhook token + * @param bool|int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @throws MissingExtensionException + */ + public function __construct($token, $level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); + } + + $this->token = $token; + + $connectionString = 'ssl://' . self::FLEEP_HOST . ':443'; + parent::__construct($connectionString, $level, $bubble); + } + + /** + * Returns the default formatter to use with this handler + * + * Overloaded to remove empty context and extra arrays from the end of the log message. + * + * @return LineFormatter + */ + protected function getDefaultFormatter() + { + return new LineFormatter(null, null, true, true); + } + + /** + * Handles a log record + * + * @param array $record + */ + public function write(array $record) + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST " . self::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . self::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = array( + 'message' => $record['formatted'] + ); + + return http_build_query($dataArray); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php new file mode 100644 index 0000000..dd9a361 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FlowdockFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Sends notifications through the Flowdock push API + * + * This must be configured with a FlowdockFormatter instance via setFormatter() + * + * Notes: + * API token - Flowdock API token + * + * @author Dominik Liebler + * @see https://www.flowdock.com/api/push + */ +class FlowdockHandler extends SocketHandler +{ + /** + * @var string + */ + protected $apiToken; + + /** + * @param string $apiToken + * @param bool|int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @throws MissingExtensionException if OpenSSL is missing + */ + public function __construct($apiToken, $level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + + parent::__construct('ssl://api.flowdock.com:443', $level, $bubble); + $this->apiToken = $apiToken; + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + if (!$formatter instanceof FlowdockFormatter) { + throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + return parent::setFormatter($formatter); + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() + { + throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + parent::write($record); + + $this->closeSocket(); + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + return json_encode($record['formatted']['flowdock']); + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..28c7b55 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\IMessagePublisher; +use Gelf\PublisherInterface; +use Gelf\Publisher; +use InvalidArgumentException; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + * @author Benjamin Zikarsky + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var Publisher the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param PublisherInterface|IMessagePublisher|Publisher $publisher a publisher object + * @param integer $level The minimum logging level at which this handler will be triggered + * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($publisher, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$publisher instanceof Publisher && !$publisher instanceof IMessagePublisher && !$publisher instanceof PublisherInterface) { + throw new InvalidArgumentException("Invalid publisher, expected a Gelf\Publisher, Gelf\IMessagePublisher or Gelf\PublisherInterface instance"); + } + + $this->publisher = $publisher; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->publisher = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new GelfMessageFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000..99384d3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + */ +class GroupHandler extends AbstractHandler +{ + protected $handlers; + + /** + * @param array $handlers Array of Handlers. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000..d920c4b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * Handlers should still check the record levels within handle(), returning false in isHandling() + * is no guarantee that handle() will not be called, and isHandling() might not be called + * for a given record. + * + * @param array $record Partial log record containing only a level key + * + * @return Boolean + */ + public function isHandling(array $record); + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * @param array $record The record to handle + * @return Boolean true means that this handler handled the record, and that bubbling is not permitted. + * false means the record was either not processed or that this handler allows bubbling. + */ + public function handle(array $record); + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + */ + public function handleBatch(array $records); + + /** + * Adds a processor in the stack. + * + * @param callable $callback + * @return self + */ + public function pushProcessor($callback); + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor(); + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + * @return self + */ + public function setFormatter(FormatterInterface $formatter); + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php new file mode 100644 index 0000000..34d3437 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php @@ -0,0 +1,337 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Sends notifications through the hipchat api to a hipchat room + * + * Notes: + * API token - HipChat API token + * Room - HipChat Room Id or name, where messages are sent + * Name - Name used to send the message (from) + * notify - Should the message trigger a notification in the clients + * version - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2) + * + * @author Rafael Dohms + * @see https://www.hipchat.com/docs/api + */ +class HipChatHandler extends SocketHandler +{ + /** + * Use API version 1 + */ + const API_V1 = 'v1'; + + /** + * Use API version v2 + */ + const API_V2 = 'v2'; + + /** + * The maximum allowed length for the name used in the "from" field. + */ + const MAXIMUM_NAME_LENGTH = 15; + + /** + * The maximum allowed length for the message. + */ + const MAXIMUM_MESSAGE_LENGTH = 9500; + + /** + * @var string + */ + private $token; + + /** + * @var string + */ + private $room; + + /** + * @var string + */ + private $name; + + /** + * @var bool + */ + private $notify; + + /** + * @var string + */ + private $format; + + /** + * @var string + */ + private $host; + + /** + * @var string + */ + private $version; + + /** + * @param string $token HipChat API Token + * @param string $room The room that should be alerted of the message (Id or Name) + * @param string $name Name used in the "from" field. Not used for v2 + * @param bool $notify Trigger a notification in clients or not + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $useSSL Whether to connect via SSL. + * @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) + * @param string $host The HipChat server hostname. + * @param string $version The HipChat API version (default HipChatHandler::API_V1) + */ + public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) + { + if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { + throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); + } + + $connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80'; + parent::__construct($connectionString, $level, $bubble); + + $this->token = $token; + $this->name = $name; + $this->notify = $notify; + $this->room = $room; + $this->format = $format; + $this->host = $host; + $this->version = $version; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = array( + 'notify' => $this->version == self::API_V1 ? + ($this->notify ? 1 : 0) : + ($this->notify ? 'true' : 'false'), + 'message' => $record['formatted'], + 'message_format' => $this->format, + 'color' => $this->getAlertColor($record['level']), + ); + + // if we are using the legacy API then we need to send some additional information + if ($this->version == self::API_V1) { + $dataArray['room_id'] = $this->room; + $dataArray['from'] = $this->name; + } + + return http_build_query($dataArray); + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + if ($this->version == self::API_V1) { + $header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n"; + } else { + // needed for rooms with special (spaces, etc) characters in the name + $room = rawurlencode($this->room); + $header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n"; + } + + $header .= "Host: {$this->host}\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Assigns a color to each level of log records. + * + * @param integer $level + * @return string + */ + protected function getAlertColor($level) + { + switch (true) { + case $level >= Logger::ERROR: + return 'red'; + case $level >= Logger::WARNING: + return 'yellow'; + case $level >= Logger::INFO: + return 'green'; + case $level == Logger::DEBUG: + return 'gray'; + default: + return 'yellow'; + } + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + if (count($records) == 0) { + return true; + } + + $batchRecords = $this->combineRecords($records); + + $handled = false; + foreach ($batchRecords as $batchRecord) { + if ($this->isHandling($batchRecord)) { + $this->write($batchRecord); + $handled = true; + } + } + + if (!$handled) { + return false; + } + + return false === $this->bubble; + } + + /** + * Combines multiple records into one. Error level of the combined record + * will be the highest level from the given records. Datetime will be taken + * from the first record. + * + * @param $records + * @return array + */ + private function combineRecords($records) + { + $batchRecord = null; + $batchRecords = array(); + $messages = array(); + $formattedMessages = array(); + $level = 0; + $levelName = null; + $datetime = null; + + foreach ($records as $record) { + $record = $this->processRecord($record); + + if ($record['level'] > $level) { + $level = $record['level']; + $levelName = $record['level_name']; + } + + if (null === $datetime) { + $datetime = $record['datetime']; + } + + $messages[] = $record['message']; + $messageStr = implode(PHP_EOL, $messages); + $formattedMessages[] = $this->getFormatter()->format($record); + $formattedMessageStr = implode('', $formattedMessages); + + $batchRecord = array( + 'message' => $messageStr, + 'formatted' => $formattedMessageStr, + 'context' => array(), + 'extra' => array(), + ); + + if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) { + // Pop the last message and implode the remaining messages + $lastMessage = array_pop($messages); + $lastFormattedMessage = array_pop($formattedMessages); + $batchRecord['message'] = implode(PHP_EOL, $messages); + $batchRecord['formatted'] = implode('', $formattedMessages); + + $batchRecords[] = $batchRecord; + $messages = array($lastMessage); + $formattedMessages = array($lastFormattedMessage); + + $batchRecord = null; + } + } + + if (null !== $batchRecord) { + $batchRecords[] = $batchRecord; + } + + // Set the max level and datetime for all records + foreach ($batchRecords as &$batchRecord) { + $batchRecord = array_merge( + $batchRecord, + array( + 'level' => $level, + 'level_name' => $levelName, + 'datetime' => $datetime + ) + ); + } + + return $batchRecords; + } + + /** + * Validates the length of a string. + * + * If the `mb_strlen()` function is available, it will use that, as HipChat + * allows UTF-8 characters. Otherwise, it will fall back to `strlen()`. + * + * Note that this might cause false failures in the specific case of using + * a valid name with less than 16 characters, but 16 or more bytes, on a + * system where `mb_strlen()` is unavailable. + * + * @param string $str + * @param int $length + * + * @return bool + */ + private function validateStringLength($str, $length) + { + if (function_exists('mb_strlen')) { + return (mb_strlen($str) <= $length); + } + + return (strlen($str) <= $length); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php new file mode 100644 index 0000000..bc11705 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * IFTTTHandler uses cURL to trigger IFTTT Maker actions + * + * Register a secret key and trigger/event name at https://ifttt.com/maker + * + * value1 will be the channel from monolog's Logger constructor, + * value2 will be the level name (ERROR, WARNING, ..) + * value3 will be the log record's message + * + * @author Nehal Patel + */ +class IFTTTHandler extends AbstractProcessingHandler +{ + private $eventName; + private $secretKey; + + /** + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true) + { + $this->eventName = $eventName; + $this->secretKey = $secretKey; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + public function write(array $record) + { + $postData = array( + "value1" => $record["channel"], + "value2" => $record["level_name"], + "value3" => $record["message"] + ); + $postString = json_encode($postData); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postString); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + "Content-Type: application/json" + )); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php new file mode 100644 index 0000000..bd56230 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by LogEntries + * @param boolean $useSSL Whether or not SSL encryption should be used. + * @param int $level The minimum logging level to trigger this handler + * @param boolean $bubble Whether or not messages that are handled should bubble up the stack. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true) + { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL ? 'ssl://data.logentries.com:443' : 'data.logentries.com:80'; + parent::__construct($endpoint, $level, $bubble); + $this->logToken = $token; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php new file mode 100644 index 0000000..bcd62e1 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LogglyFormatter; + +/** + * Sends errors to Loggly. + * + * @author Przemek Sobstel + * @author Adam Pancutt + * @author Gregory Barchard + */ +class LogglyHandler extends AbstractProcessingHandler +{ + const HOST = 'logs-01.loggly.com'; + const ENDPOINT_SINGLE = 'inputs'; + const ENDPOINT_BATCH = 'bulk'; + + protected $token; + + protected $tag = array(); + + public function __construct($token, $level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('curl')) { + throw new \LogicException('The curl extension is needed to use the LogglyHandler'); + } + + $this->token = $token; + + parent::__construct($level, $bubble); + } + + public function setTag($tag) + { + $tag = !empty($tag) ? $tag : array(); + $this->tag = is_array($tag) ? $tag : array($tag); + } + + public function addTag($tag) + { + if (!empty($tag)) { + $tag = is_array($tag) ? $tag : array($tag); + $this->tag = array_unique(array_merge($this->tag, $tag)); + } + } + + protected function write(array $record) + { + $this->send($record["formatted"], self::ENDPOINT_SINGLE); + } + + public function handleBatch(array $records) + { + $level = $this->level; + + $records = array_filter($records, function ($record) use ($level) { + return ($record['level'] >= $level); + }); + + if ($records) { + $this->send($this->getFormatter()->formatBatch($records), self::ENDPOINT_BATCH); + } + } + + protected function send($data, $endpoint) + { + $url = sprintf("https://%s/%s/%s/", self::HOST, $endpoint, $this->token); + + $headers = array('Content-Type: application/json'); + + if (!empty($this->tag)) { + $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); + } + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + Curl\Util::execute($ch); + } + + protected function getDefaultFormatter() + { + return new LogglyFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000..50ed638 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content formatted email body to be sent + * @param array $records the array of log records that formed this content + */ + abstract protected function send($content, array $records); + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->send((string) $record['formatted'], array($record)); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php new file mode 100644 index 0000000..0ed098a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * MandrillHandler uses cURL to send the emails to the Mandrill API + * + * @author Adam Nicholson + */ +class MandrillHandler extends MailHandler +{ + protected $message; + protected $apiKey; + + /** + * @param string $apiKey A valid Mandrill API key + * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$message instanceof \Swift_Message && is_callable($message)) { + $message = call_user_func($message); + } + if (!$message instanceof \Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); + } + $this->message = $message; + $this->apiKey = $apiKey; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $message = clone $this->message; + $message->setBody($content); + $message->setDate(time()); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( + 'key' => $this->apiKey, + 'raw_message' => (string) $message, + 'async' => false, + ))); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000..4724a7e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Exception can be thrown if an extension for an handler is missing + * + * @author Christian Bergau + */ +class MissingExtensionException extends \Exception +{ +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000..6c431f2 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; + +/** + * Logs to a MongoDB database. + * + * usage example: + * + * $log = new Logger('application'); + * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); + * $log->pushHandler($mongodb); + * + * @author Thomas Tourlourat + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + protected $mongoCollection; + + public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true) + { + if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { + throw new \InvalidArgumentException('MongoClient or Mongo instance required'); + } + + $this->mongoCollection = $mongo->selectCollection($database, $collection); + + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + $this->mongoCollection->save($record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new NormalizerFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000..5118a0e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + * @author Mark Garrett + */ +class NativeMailerHandler extends MailHandler +{ + /** + * The email addresses to which the message will be sent + * @var array + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * Optional headers for the message + * @var array + */ + protected $headers = array(); + + /** + * Optional parameters for the message + * @var array + */ + protected $parameters = array(); + + /** + * The wordwrap length for the message + * @var integer + */ + protected $maxColumnWidth; + + /** + * The Content-type for the message + * @var string + */ + protected $contentType = 'text/plain'; + + /** + * The encoding for the message + * @var string + */ + protected $encoding = 'utf-8'; + + /** + * @param string|array $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param integer $level The minimum logging level at which this handler will be triggered + * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $maxColumnWidth The maximum column width that the message lines will have + */ + public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true, $maxColumnWidth = 70) + { + parent::__construct($level, $bubble); + $this->to = is_array($to) ? $to : array($to); + $this->subject = $subject; + $this->addHeader(sprintf('From: %s', $from)); + $this->maxColumnWidth = $maxColumnWidth; + } + + /** + * Add headers to the message + * + * @param string|array $headers Custom added headers + * @return self + */ + public function addHeader($headers) + { + foreach ((array) $headers as $header) { + if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { + throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); + } + $this->headers[] = $header; + } + + return $this; + } + + /** + * Add parameters to the message + * + * @param string|array $parameters Custom added parameters + * @return self + */ + public function addParameter($parameters) + { + $this->parameters = array_merge($this->parameters, (array) $parameters); + + return $this; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $content = wordwrap($content, $this->maxColumnWidth); + $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); + $headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n"; + if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) { + $headers .= 'MIME-Version: 1.0' . "\r\n"; + } + foreach ($this->to as $to) { + mail($to, $this->subject, $content, $headers, implode(' ', $this->parameters)); + } + } + + /** + * @return string $contentType + */ + public function getContentType() + { + return $this->contentType; + } + + /** + * @return string $encoding + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML + * messages. + * @return self + */ + public function setContentType($contentType) + { + if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { + throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); + } + + $this->contentType = $contentType; + + return $this; + } + + /** + * @param string $encoding + * @return self + */ + public function setEncoding($encoding) + { + if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { + throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); + } + + $this->encoding = $encoding; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php new file mode 100644 index 0000000..8cb4ab3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -0,0 +1,198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; + +/** + * Class to record a log on a NewRelic application. + * Enabling New Relic High Security mode may prevent capture of useful information. + * + * @see https://docs.newrelic.com/docs/agents/php-agent + * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security + */ +class NewRelicHandler extends AbstractProcessingHandler +{ + /** + * Name of the New Relic application that will receive logs from this handler. + * + * @var string + */ + protected $appName; + + /** + * Name of the current transaction + * + * @var string + */ + protected $transactionName; + + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + * + * @var boolean + */ + protected $explodeArrays; + + /** + * {@inheritDoc} + * + * @param string $appName + * @param boolean $explodeArrays + * @param string $transactionName + */ + public function __construct( + $level = Logger::ERROR, + $bubble = true, + $appName = null, + $explodeArrays = false, + $transactionName = null + ) { + parent::__construct($level, $bubble); + + $this->appName = $appName; + $this->explodeArrays = $explodeArrays; + $this->transactionName = $transactionName; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + if (!$this->isNewRelicEnabled()) { + throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); + } + + if ($appName = $this->getAppName($record['context'])) { + $this->setNewRelicAppName($appName); + } + + if ($transactionName = $this->getTransactionName($record['context'])) { + $this->setNewRelicTransactionName($transactionName); + unset($record['formatted']['context']['transaction_name']); + } + + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { + newrelic_notice_error($record['message'], $record['context']['exception']); + unset($record['formatted']['context']['exception']); + } else { + newrelic_notice_error($record['message']); + } + + foreach ($record['formatted']['context'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('context_' . $key, $parameter); + } + } + + foreach ($record['formatted']['extra'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('extra_' . $key, $parameter); + } + } + } + + /** + * Checks whether the NewRelic extension is enabled in the system. + * + * @return bool + */ + protected function isNewRelicEnabled() + { + return extension_loaded('newrelic'); + } + + /** + * Returns the appname where this log should be sent. Each log can override the default appname, set in this + * handler's constructor, by providing the appname in it's context. + * + * @param array $context + * @return null|string + */ + protected function getAppName(array $context) + { + if (isset($context['appname'])) { + return $context['appname']; + } + + return $this->appName; + } + + /** + * Returns the name of the current transaction. Each log can override the default transaction name, set in this + * handler's constructor, by providing the transaction_name in it's context + * + * @param array $context + * + * @return null|string + */ + protected function getTransactionName(array $context) + { + if (isset($context['transaction_name'])) { + return $context['transaction_name']; + } + + return $this->transactionName; + } + + /** + * Sets the NewRelic application that should receive this log. + * + * @param string $appName + */ + protected function setNewRelicAppName($appName) + { + newrelic_set_appname($appName); + } + + /** + * Overwrites the name of the current transaction + * + * @param string $transactionName + */ + protected function setNewRelicTransactionName($transactionName) + { + newrelic_name_transaction($transactionName); + } + + /** + * @param string $key + * @param mixed $value + */ + protected function setNewRelicParameter($key, $value) + { + if (null === $value || is_scalar($value)) { + newrelic_add_custom_parameter($key, $value); + } else { + newrelic_add_custom_parameter($key, @json_encode($value)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new NormalizerFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000..3754e45 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + */ +class NullHandler extends AbstractHandler +{ + /** + * @param integer $level The minimum logging level at which this handler will be triggered + */ + public function __construct($level = Logger::DEBUG) + { + parent::__construct($level, false); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + return true; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php new file mode 100644 index 0000000..2b7e353 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php @@ -0,0 +1,242 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Exception; +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; +use PhpConsole\Connector; +use PhpConsole\Handler; +use PhpConsole\Helper; + +/** + * Monolog handler for Google Chrome extension "PHP Console" + * + * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely + * + * Usage: + * 1. Install Google Chrome extension https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef + * 2. See overview https://github.com/barbushin/php-console#overview + * 3. Install PHP Console library https://github.com/barbushin/php-console#installation + * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) + * + * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); + * \Monolog\ErrorHandler::register($logger); + * echo $undefinedVar; + * $logger->addDebug('SELECT * FROM users', array('db', 'time' => 0.012)); + * PC::debug($_SERVER); // PHP Console debugger for any type of vars + * + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + */ +class PHPConsoleHandler extends AbstractProcessingHandler +{ + private $options = array( + 'enabled' => true, // bool Is PHP Console server enabled + 'classesPartialsTraceIgnore' => array('Monolog\\'), // array Hide calls of classes started with... + 'debugTagsKeysInContext' => array(0, 'tag'), // bool Is PHP Console server enabled + 'useOwnErrorsHandler' => false, // bool Enable errors handling + 'useOwnExceptionsHandler' => false, // bool Enable exceptions handling + 'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths + 'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') + 'serverEncoding' => null, // string|null Server internal encoding + 'headersLimit' => null, // int|null Set headers size limit for your web-server + 'password' => null, // string|null Protect PHP Console connection by password + 'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed + 'ipMasks' => array(), // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') + 'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) + 'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings + 'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level + 'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number + 'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item + 'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON + 'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug + 'dataStorage' => null, // PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) + ); + + /** @var Connector */ + private $connector; + + /** + * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details + * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) + * @param int $level + * @param bool $bubble + * @throws Exception + */ + public function __construct(array $options = array(), Connector $connector = null, $level = Logger::DEBUG, $bubble = true) + { + if (!class_exists('PhpConsole\Connector')) { + throw new Exception('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + parent::__construct($level, $bubble); + $this->options = $this->initOptions($options); + $this->connector = $this->initConnector($connector); + } + + private function initOptions(array $options) + { + $wrongOptions = array_diff(array_keys($options), array_keys($this->options)); + if ($wrongOptions) { + throw new Exception('Unknown options: ' . implode(', ', $wrongOptions)); + } + + return array_replace($this->options, $options); + } + + private function initConnector(Connector $connector = null) + { + if (!$connector) { + if ($this->options['dataStorage']) { + Connector::setPostponeStorage($this->options['dataStorage']); + } + $connector = Connector::getInstance(); + } + + if ($this->options['registerHelper'] && !Helper::isRegistered()) { + Helper::register(); + } + + if ($this->options['enabled'] && $connector->isActiveClient()) { + if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { + $handler = Handler::getInstance(); + $handler->setHandleErrors($this->options['useOwnErrorsHandler']); + $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); + $handler->start(); + } + if ($this->options['sourcesBasePath']) { + $connector->setSourcesBasePath($this->options['sourcesBasePath']); + } + if ($this->options['serverEncoding']) { + $connector->setServerEncoding($this->options['serverEncoding']); + } + if ($this->options['password']) { + $connector->setPassword($this->options['password']); + } + if ($this->options['enableSslOnlyMode']) { + $connector->enableSslOnlyMode(); + } + if ($this->options['ipMasks']) { + $connector->setAllowedIpMasks($this->options['ipMasks']); + } + if ($this->options['headersLimit']) { + $connector->setHeadersLimit($this->options['headersLimit']); + } + if ($this->options['detectDumpTraceAndSource']) { + $connector->getDebugDispatcher()->detectTraceAndSource = true; + } + $dumper = $connector->getDumper(); + $dumper->levelLimit = $this->options['dumperLevelLimit']; + $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; + $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; + $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; + $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; + if ($this->options['enableEvalListener']) { + $connector->startEvalRequestsListener(); + } + } + + return $connector; + } + + public function getConnector() + { + return $this->connector; + } + + public function getOptions() + { + return $this->options; + } + + public function handle(array $record) + { + if ($this->options['enabled'] && $this->connector->isActiveClient()) { + return parent::handle($record); + } + + return !$this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * @return void + */ + protected function write(array $record) + { + if ($record['level'] < Logger::NOTICE) { + $this->handleDebugRecord($record); + } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) { + $this->handleExceptionRecord($record); + } else { + $this->handleErrorRecord($record); + } + } + + private function handleDebugRecord(array $record) + { + $tags = $this->getRecordTags($record); + $message = $record['message']; + if ($record['context']) { + $message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context']))); + } + $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); + } + + private function handleExceptionRecord(array $record) + { + $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); + } + + private function handleErrorRecord(array $record) + { + $context = $record['context']; + + $this->connector->getErrorsDispatcher()->dispatchError( + isset($context['code']) ? $context['code'] : null, + isset($context['message']) ? $context['message'] : $record['message'], + isset($context['file']) ? $context['file'] : null, + isset($context['line']) ? $context['line'] : null, + $this->options['classesPartialsTraceIgnore'] + ); + } + + private function getRecordTags(array &$record) + { + $tags = null; + if (!empty($record['context'])) { + $context = & $record['context']; + foreach ($this->options['debugTagsKeysInContext'] as $key) { + if (!empty($context[$key])) { + $tags = $context[$key]; + if ($key === 0) { + array_shift($context); + } else { + unset($context[$key]); + } + break; + } + } + } + + return $tags ?: strtolower($record['level_name']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('%message%'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php new file mode 100644 index 0000000..1ae8584 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LoggerInterface; + +/** + * Proxies log messages to an existing PSR-3 compliant logger. + * + * @author Michael Moussa + */ +class PsrHandler extends AbstractHandler +{ + /** + * PSR-3 compliant logger + * + * @var LoggerInterface + */ + protected $logger; + + /** + * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']); + + return false === $this->bubble; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000..9917b64 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Sends notifications through the pushover api to mobile phones + * + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + */ +class PushoverHandler extends SocketHandler +{ + private $token; + private $users; + private $title; + private $user; + private $retry; + private $expire; + + private $highPriorityLevel; + private $emergencyLevel; + private $useFormattedMessage = false; + + /** + * All parameters that can be sent to Pushover + * @see https://pushover.net/api + * @var array + */ + private $parameterNames = array( + 'token' => true, + 'user' => true, + 'message' => true, + 'device' => true, + 'title' => true, + 'url' => true, + 'url_title' => true, + 'priority' => true, + 'timestamp' => true, + 'sound' => true, + 'retry' => true, + 'expire' => true, + 'callback' => true, + ); + + /** + * Sounds the api supports by default + * @see https://pushover.net/api#sounds + * @var array + */ + private $sounds = array( + 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', + 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', + 'persistent', 'echo', 'updown', 'none', + ); + + /** + * @param string $token Pushover api token + * @param string|array $users Pushover user id or array of ids the message will be sent to + * @param string $title Title sent to the Pushover API + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * the pushover.net app owner. OpenSSL is required for this option. + * @param integer $highPriorityLevel The minimum logging level at which this handler will start + * sending "high priority" requests to the Pushover API + * @param integer $emergencyLevel The minimum logging level at which this handler will start + * sending "emergency" requests to the Pushover API + * @param integer $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user. + * @param integer $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds). + */ + public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200) + { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct($connectionString, $level, $bubble); + + $this->token = $token; + $this->users = (array) $users; + $this->title = $title ?: gethostname(); + $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); + $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); + $this->retry = $retry; + $this->expire = $expire; + } + + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + private function buildContent($record) + { + // Pushover has a limit of 512 characters on title and message combined. + $maxMessageLength = 512 - strlen($this->title); + + $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; + $message = substr($message, 0, $maxMessageLength); + + $timestamp = $record['datetime']->getTimestamp(); + + $dataArray = array( + 'token' => $this->token, + 'user' => $this->user, + 'message' => $message, + 'title' => $this->title, + 'timestamp' => $timestamp + ); + + if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { + $dataArray['priority'] = 2; + $dataArray['retry'] = $this->retry; + $dataArray['expire'] = $this->expire; + } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { + $dataArray['priority'] = 1; + } + + // First determine the available parameters + $context = array_intersect_key($record['context'], $this->parameterNames); + $extra = array_intersect_key($record['extra'], $this->parameterNames); + + // Least important info should be merged with subsequent info + $dataArray = array_merge($extra, $context, $dataArray); + + // Only pass sounds that are supported by the API + if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { + unset($dataArray['sound']); + } + + return http_build_query($dataArray); + } + + private function buildHeader($content) + { + $header = "POST /1/messages.json HTTP/1.1\r\n"; + $header .= "Host: api.pushover.net\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + protected function write(array $record) + { + foreach ($this->users as $user) { + $this->user = $user; + + parent::write($record); + $this->closeSocket(); + } + + $this->user = null; + } + + public function setHighPriorityLevel($value) + { + $this->highPriorityLevel = $value; + } + + public function setEmergencyLevel($value) + { + $this->emergencyLevel = $value; + } + + /** + * Use the formatted message? + * @param boolean $value + */ + public function useFormattedMessage($value) + { + $this->useFormattedMessage = (boolean) $value; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php new file mode 100644 index 0000000..1807705 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Raven_Client; + +/** + * Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server + * using raven-php (https://github.com/getsentry/raven-php) + * + * @author Marc Abramowitz + */ +class RavenHandler extends AbstractProcessingHandler +{ + /** + * Translates Monolog log levels to Raven log levels. + */ + private $logLevels = array( + Logger::DEBUG => Raven_Client::DEBUG, + Logger::INFO => Raven_Client::INFO, + Logger::NOTICE => Raven_Client::INFO, + Logger::WARNING => Raven_Client::WARNING, + Logger::ERROR => Raven_Client::ERROR, + Logger::CRITICAL => Raven_Client::FATAL, + Logger::ALERT => Raven_Client::FATAL, + Logger::EMERGENCY => Raven_Client::FATAL, + ); + + /** + * @var Raven_Client the client object that sends the message to the server + */ + protected $ravenClient; + + /** + * @var LineFormatter The formatter to use for the logs generated via handleBatch() + */ + protected $batchFormatter; + + /** + * @param Raven_Client $ravenClient + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->ravenClient = $ravenClient; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $level = $this->level; + + // filter records based on their level + $records = array_filter($records, function ($record) use ($level) { + return $record['level'] >= $level; + }); + + if (!$records) { + return; + } + + // the record with the highest severity is the "main" one + $record = array_reduce($records, function ($highest, $record) { + if ($record['level'] >= $highest['level']) { + return $record; + } + + return $highest; + }); + + // the other ones are added as a context item + $logs = array(); + foreach ($records as $r) { + $logs[] = $this->processRecord($r); + } + + if ($logs) { + $record['context']['logs'] = (string) $this->getBatchFormatter()->formatBatch($logs); + } + + $this->handle($record); + } + + /** + * Sets the formatter for the logs generated by handleBatch(). + * + * @param FormatterInterface $formatter + */ + public function setBatchFormatter(FormatterInterface $formatter) + { + $this->batchFormatter = $formatter; + } + + /** + * Gets the formatter for the logs generated by handleBatch(). + * + * @return FormatterInterface + */ + public function getBatchFormatter() + { + if (!$this->batchFormatter) { + $this->batchFormatter = $this->getDefaultBatchFormatter(); + } + + return $this->batchFormatter; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $previousUserContext = false; + $options = array(); + $options['level'] = $this->logLevels[$record['level']]; + $options['tags'] = array(); + if (!empty($record['extra']['tags'])) { + $options['tags'] = array_merge($options['tags'], $record['extra']['tags']); + unset($record['extra']['tags']); + } + if (!empty($record['context']['tags'])) { + $options['tags'] = array_merge($options['tags'], $record['context']['tags']); + unset($record['context']['tags']); + } + if (!empty($record['context']['logger'])) { + $options['logger'] = $record['context']['logger']; + unset($record['context']['logger']); + } else { + $options['logger'] = $record['channel']; + } + foreach ($this->getExtraParameters() as $key) { + foreach (array('extra', 'context') as $source) { + if (!empty($record[$source][$key])) { + $options[$key] = $record[$source][$key]; + unset($record[$source][$key]); + } + } + } + if (!empty($record['context'])) { + $options['extra']['context'] = $record['context']; + if (!empty($record['context']['user'])) { + $previousUserContext = $this->ravenClient->context->user; + $this->ravenClient->user_context($record['context']['user']); + unset($options['extra']['context']['user']); + } + } + if (!empty($record['extra'])) { + $options['extra']['extra'] = $record['extra']; + } + + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { + $options['extra']['message'] = $record['formatted']; + $this->ravenClient->captureException($record['context']['exception'], $options); + } else { + $this->ravenClient->captureMessage($record['formatted'], array(), $options); + } + + if ($previousUserContext !== false) { + $this->ravenClient->user_context($previousUserContext); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[%channel%] %message%'); + } + + /** + * Gets the default formatter for the logs generated by handleBatch(). + * + * @return FormatterInterface + */ + protected function getDefaultBatchFormatter() + { + return new LineFormatter(); + } + + /** + * Gets extra parameters supported by Raven that can be found in "extra" and "context" + * + * @return array + */ + protected function getExtraParameters() + { + return array('checksum', 'release'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php new file mode 100644 index 0000000..157e2f9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Logs to a Redis key using rpush + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); + * $log->pushHandler($redis); + * + * @author Thomas Tourlourat + */ +class RedisHandler extends AbstractProcessingHandler +{ + private $redisClient; + private $redisKey; + protected $capSize; + + /** + * @param \Predis\Client|\Redis $redis The redis instance + * @param string $key The key name to push records to + * @param integer $level The minimum logging level at which this handler will be triggered + * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param integer $capSize Number of entries to limit list size to + */ + public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = true, $capSize = false) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->redisKey = $key; + $this->capSize = $capSize; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + if ($this->capSize) { + $this->writeCapped($record); + } else { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + } + + /** + * Write and cap the collection + * Writes the record to the redis list and caps its + * + * @param array $record associative record array + * @return void + */ + protected function writeCapped(array $record) + { + if ($this->redisClient instanceof \Redis) { + $this->redisClient->multi() + ->rpush($this->redisKey, $record["formatted"]) + ->ltrim($this->redisKey, -$this->capSize, -1) + ->execute(); + } else { + $redisKey = $this->redisKey; + $capSize = $this->capSize; + $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { + $tx->rpush($redisKey, $record["formatted"]); + $tx->ltrim($redisKey, -$capSize, -1); + }); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php new file mode 100644 index 0000000..0ad618d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use RollbarNotifier; +use Exception; +use Monolog\Logger; + +/** + * Sends errors to Rollbar + * + * @author Paul Statezny + */ +class RollbarHandler extends AbstractProcessingHandler +{ + /** + * Rollbar notifier + * + * @var RollbarNotifier + */ + protected $rollbarNotifier; + + /** + * Records whether any log records have been added since the last flush of the rollbar notifier + * + * @var bool + */ + private $hasRecords = false; + + /** + * @param RollbarNotifier $rollbarNotifier RollbarNotifier object constructed with valid token + * @param integer $level The minimum logging level at which this handler will be triggered + * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(RollbarNotifier $rollbarNotifier, $level = Logger::ERROR, $bubble = true) + { + $this->rollbarNotifier = $rollbarNotifier; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) { + $context = $record['context']; + $exception = $context['exception']; + unset($context['exception']); + + $payload = array(); + if (isset($context['payload'])) { + $payload = $context['payload']; + unset($context['payload']); + } + + $this->rollbarNotifier->report_exception($exception, $context, $payload); + } else { + $extraData = array( + 'level' => $record['level'], + 'channel' => $record['channel'], + 'datetime' => $record['datetime']->format('U'), + ); + + $this->rollbarNotifier->report_message( + $record['message'], + $record['level_name'], + array_merge($record['context'], $record['extra'], $extraData) + ); + } + + $this->hasRecords = true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + if ($this->hasRecords) { + $this->rollbarNotifier->flush(); + $this->hasRecords = false; + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000..0a20377 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class RotatingFileHandler extends StreamHandler +{ + protected $filename; + protected $maxFiles; + protected $mustRotate; + protected $nextRotation; + protected $filenameFormat; + protected $dateFormat; + + /** + * @param string $filename + * @param integer $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param Boolean $useLocking Try to lock log file before doing any writes + */ + public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) + { + $this->filename = $filename; + $this->maxFiles = (int) $maxFiles; + $this->nextRotation = new \DateTime('tomorrow'); + $this->filenameFormat = '{filename}-{date}'; + $this->dateFormat = 'Y-m-d'; + + parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + public function setFilenameFormat($filenameFormat, $dateFormat) + { + $this->filenameFormat = $filenameFormat; + $this->dateFormat = $dateFormat; + $this->url = $this->getTimedFilename(); + $this->close(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = !file_exists($this->url); + } + + if ($this->nextRotation < $record['datetime']) { + $this->mustRotate = true; + $this->close(); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate() + { + // update filename + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTime('tomorrow'); + + // skip GC of old logs if files are unlimited + if (0 === $this->maxFiles) { + return; + } + + $logFiles = glob($this->getGlobPattern()); + if ($this->maxFiles >= count($logFiles)) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + usort($logFiles, function ($a, $b) { + return strcmp($b, $a); + }); + + foreach (array_slice($logFiles, $this->maxFiles) as $file) { + if (is_writable($file)) { + unlink($file); + } + } + + $this->mustRotate = false; + } + + protected function getTimedFilename() + { + $fileInfo = pathinfo($this->filename); + $timedFilename = str_replace( + array('{filename}', '{date}'), + array($fileInfo['filename'], date($this->dateFormat)), + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + + if (!empty($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + return $timedFilename; + } + + protected function getGlobPattern() + { + $fileInfo = pathinfo($this->filename); + $glob = str_replace( + array('{filename}', '{date}'), + array($fileInfo['filename'], '*'), + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + if (!empty($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + + return $glob; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 0000000..9509ae3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Sampling handler + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurrence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application, but based on the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + */ +class SamplingHandler extends AbstractHandler +{ + /** + * @var callable|HandlerInterface $handler + */ + protected $handler; + + /** + * @var int $factor + */ + protected $factor; + + /** + * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). + * @param int $factor Sample factor + */ + public function __construct($handler, $factor) + { + parent::__construct(); + $this->handler = $handler; + $this->factor = $factor; + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + public function isHandling(array $record) + { + return $this->handler->isHandling($record); + } + + public function handle(array $record) + { + if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { + // The same logic as in FingersCrossedHandler + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $this->handler->handle($record); + } + + return false === $this->bubble; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php new file mode 100644 index 0000000..61245ff --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * Sends notifications through Slack API + * + * @author Greg Kedzierski + * @see https://api.slack.com/ + */ +class SlackHandler extends SocketHandler +{ + /** + * Slack API token + * @var string + */ + private $token; + + /** + * Slack channel (encoded ID or name) + * @var string + */ + private $channel; + + /** + * Name of a bot + * @var string + */ + private $username; + + /** + * Emoji icon name + * @var string + */ + private $iconEmoji; + + /** + * Whether the message should be added to Slack as attachment (plain text otherwise) + * @var bool + */ + private $useAttachment; + + /** + * Whether the the context/extra messages added to Slack as attachments are in a short style + * @var bool + */ + private $useShortAttachment; + + /** + * Whether the attachment should include context and extra data + * @var bool + */ + private $includeContextAndExtra; + + /** + * @var LineFormatter + */ + private $lineFormatter; + + /** + * @param string $token Slack API token + * @param string $channel Slack channel (encoded ID or name) + * @param string $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + */ + public function __construct($token, $channel, $username = 'Monolog', $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false) + { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); + } + + parent::__construct('ssl://slack.com:443', $level, $bubble); + + $this->token = $token; + $this->channel = $channel; + $this->username = $username; + $this->iconEmoji = trim($iconEmoji, ':'); + $this->useAttachment = $useAttachment; + $this->useShortAttachment = $useShortAttachment; + $this->includeContextAndExtra = $includeContextAndExtra; + if ($this->includeContextAndExtra) { + $this->lineFormatter = new LineFormatter; + } + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = $this->prepareContentData($record); + + return http_build_query($dataArray); + } + + /** + * Prepares content data + * + * @param array $record + * @return array + */ + protected function prepareContentData($record) + { + $dataArray = array( + 'token' => $this->token, + 'channel' => $this->channel, + 'username' => $this->username, + 'text' => '', + 'attachments' => array() + ); + + if ($this->useAttachment) { + $attachment = array( + 'fallback' => $record['message'], + 'color' => $this->getAttachmentColor($record['level']), + 'fields' => array() + ); + + if ($this->useShortAttachment) { + $attachment['title'] = $record['level_name']; + $attachment['text'] = $record['message']; + } else { + $attachment['title'] = 'Message'; + $attachment['text'] = $record['message']; + $attachment['fields'][] = array( + 'title' => 'Level', + 'value' => $record['level_name'], + 'short' => true + ); + } + + if ($this->includeContextAndExtra) { + if (!empty($record['extra'])) { + if ($this->useShortAttachment) { + $attachment['fields'][] = array( + 'title' => "Extra", + 'value' => $this->stringify($record['extra']), + 'short' => $this->useShortAttachment + ); + } else { + // Add all extra fields as individual fields in attachment + foreach ($record['extra'] as $var => $val) { + $attachment['fields'][] = array( + 'title' => $var, + 'value' => $val, + 'short' => $this->useShortAttachment + ); + } + } + } + + if (!empty($record['context'])) { + if ($this->useShortAttachment) { + $attachment['fields'][] = array( + 'title' => "Context", + 'value' => $this->stringify($record['context']), + 'short' => $this->useShortAttachment + ); + } else { + // Add all context fields as individual fields in attachment + foreach ($record['context'] as $var => $val) { + $attachment['fields'][] = array( + 'title' => $var, + 'value' => $val, + 'short' => $this->useShortAttachment + ); + } + } + } + } + + $dataArray['attachments'] = json_encode(array($attachment)); + } else { + $dataArray['text'] = $record['message']; + } + + if ($this->iconEmoji) { + $dataArray['icon_emoji'] = ":{$this->iconEmoji}:"; + } + + return $dataArray; + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; + $header .= "Host: slack.com\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + parent::write($record); + $this->closeSocket(); + } + + /** + * Returned a Slack message attachment color associated with + * provided level. + * + * @param int $level + * @return string + */ + protected function getAttachmentColor($level) + { + switch (true) { + case $level >= Logger::ERROR: + return 'danger'; + case $level >= Logger::WARNING: + return 'warning'; + case $level >= Logger::INFO: + return 'good'; + default: + return '#e3e4e6'; + } + } + + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param array $fields + * @access protected + * @return string + */ + protected function stringify($fields) + { + $string = ''; + foreach ($fields as $var => $val) { + $string .= $var.': '.$this->lineFormatter->stringify($val)." | "; + } + + $string = rtrim($string, " |"); + + return $string; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000..a3e7252 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + */ +class SocketHandler extends AbstractProcessingHandler +{ + private $connectionString; + private $connectionTimeout; + private $resource; + private $timeout = 0; + private $persistent = false; + private $errno; + private $errstr; + + /** + * @param string $connectionString Socket connection string + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + $this->connectionTimeout = (float) ini_get('default_socket_timeout'); + } + + /** + * Connect (if necessary) and write to the socket + * + * @param array $record + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + protected function write(array $record) + { + $this->connectIfNotConnected(); + $data = $this->generateDataStream($record); + $this->writeToSocket($data); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close() + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket() + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to nbe persistent. It only has effect before the connection is initiated. + * + * @param boolean $persistent + */ + public function setPersistent($persistent) + { + $this->persistent = (boolean) $persistent; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @param float $seconds + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->connectionTimeout = (float) $seconds; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @param float $seconds + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->timeout = (float) $seconds; + } + + /** + * Get current connection string + * + * @return string + */ + public function getConnectionString() + { + return $this->connectionString; + } + + /** + * Get persistent setting + * + * @return boolean + */ + public function isPersistent() + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + * + * @return float + */ + public function getConnectionTimeout() + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + * + * @return float + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + * + * @return boolean + */ + public function isConnected() + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + protected function streamSetTimeout() + { + $seconds = floor($this->timeout); + $microseconds = round(($this->timeout - $seconds) * 1e6); + + return stream_set_timeout($this->resource, $seconds, $microseconds); + } + + /** + * Wrapper to allow mocking + */ + protected function fwrite($data) + { + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + */ + protected function streamGetMetadata() + { + return stream_get_meta_data($this->resource); + } + + private function validateTimeout($value) + { + $ok = filter_var($value, FILTER_VALIDATE_FLOAT); + if ($ok === false || $value < 0) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); + } + } + + private function connectIfNotConnected() + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + protected function generateDataStream($record) + { + return (string) $record['formatted']; + } + + private function connect() + { + $this->createSocketResource(); + $this->setSocketTimeout(); + } + + private function createSocketResource() + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (!$resource) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout() + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function writeToSocket($data) + { + $length = strlen($data); + $sent = 0; + while ($this->isConnected() && $sent < $length) { + if (0 == $sent) { + $chunk = $this->fwrite($data); + } else { + $chunk = $this->fwrite(substr($data, $sent)); + } + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if ($socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000..28b9b11 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + */ +class StreamHandler extends AbstractProcessingHandler +{ + protected $stream; + protected $url; + private $errorMessage; + protected $filePermission; + protected $useLocking; + private $dirCreated; + + /** + * @param resource|string $stream + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param Boolean $useLocking Try to lock log file before doing any writes + * + * @throws \Exception If a missing directory is not buildable + * @throws \InvalidArgumentException If stream is not a resource or string + */ + public function __construct($stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) + { + parent::__construct($level, $bubble); + if (is_resource($stream)) { + $this->stream = $stream; + } elseif (is_string($stream)) { + $this->url = $stream; + } else { + throw new \InvalidArgumentException('A stream must either be a resource or a string.'); + } + + $this->filePermission = $filePermission; + $this->useLocking = $useLocking; + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (!is_resource($this->stream)) { + if (!$this->url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); + } + $this->createDir(); + $this->errorMessage = null; + set_error_handler(array($this, 'customErrorHandler')); + $this->stream = fopen($this->url, 'a'); + if ($this->filePermission !== null) { + @chmod($this->url, $this->filePermission); + } + restore_error_handler(); + if (!is_resource($this->stream)) { + $this->stream = null; + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url)); + } + } + + if ($this->useLocking) { + // ignoring errors here, there's not much we can do about them + flock($this->stream, LOCK_EX); + } + + fwrite($this->stream, (string) $record['formatted']); + + if ($this->useLocking) { + flock($this->stream, LOCK_UN); + } + } + + private function customErrorHandler($code, $msg) + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + } + + /** + * @param string $stream + * + * @return null|string + */ + private function getDirFromStream($stream) + { + $pos = strpos($stream, '://'); + if ($pos === false) { + return dirname($stream); + } + + if ('file://' === substr($stream, 0, 7)) { + return dirname(substr($stream, 7)); + } + + return; + } + + private function createDir() + { + // Do not try to create dir if it has already been tried. + if ($this->dirCreated) { + return; + } + + $dir = $this->getDirFromStream($this->url); + if (null !== $dir && !is_dir($dir)) { + $this->errorMessage = null; + set_error_handler(array($this, 'customErrorHandler')); + $status = mkdir($dir, 0777, true); + restore_error_handler(); + if (false === $status) { + throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); + } + } + $this->dirCreated = true; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..5c2b156 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + */ +class SwiftMailerHandler extends MailHandler +{ + protected $mailer; + private $messageTemplate; + + /** + * @param \Swift_Mailer $mailer The mailer to use + * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->mailer = $mailer; + $this->messageTemplate = $message; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Creates instance of Swift_Message to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return \Swift_Message + */ + protected function buildMessage($content, array $records) + { + $message = null; + if ($this->messageTemplate instanceof \Swift_Message) { + $message = clone $this->messageTemplate; + $message->generateId(); + } elseif (is_callable($this->messageTemplate)) { + $message = call_user_func($this->messageTemplate, $content, $records); + } + + if (!$message instanceof \Swift_Message) { + throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it'); + } + + $message->setBody($content); + $message->setDate(time()); + + return $message; + } + + /** + * BC getter, to be removed in 2.0 + */ + public function __get($name) + { + if ($name === 'message') { + trigger_error('SwiftMailerHandler->message is deprecated, use ->buildMessage() instead to retrieve the message', E_USER_DEPRECATED); + + return $this->buildMessage(null, array()); + } + + throw new \InvalidArgumentException('Invalid property '.$name); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000..47c73e1 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractSyslogHandler +{ + protected $ident; + protected $logopts; + + /** + * @param string $ident + * @param mixed $facility + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + */ + public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID) + { + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->logopts = $logopts; + } + + /** + * {@inheritdoc} + */ + public function close() + { + closelog(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (!openlog($this->ident, $this->logopts, $this->facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"'); + } + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php new file mode 100644 index 0000000..3bff085 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\SyslogUdp; + +class UdpSocket +{ + const DATAGRAM_MAX_LENGTH = 65023; + + protected $ip; + protected $port; + protected $socket; + + public function __construct($ip, $port = 514) + { + $this->ip = $ip; + $this->port = $port; + $this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + } + + public function write($line, $header = "") + { + $this->send($this->assembleMessage($line, $header)); + } + + public function close() + { + if (is_resource($this->socket)) { + socket_close($this->socket); + $this->socket = null; + } + } + + protected function send($chunk) + { + if (!is_resource($this->socket)) { + throw new \LogicException('The UdpSocket to '.$this->ip.':'.$this->port.' has been closed and can not be written to anymore'); + } + socket_sendto($this->socket, $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); + } + + protected function assembleMessage($line, $header) + { + $chunkSize = self::DATAGRAM_MAX_LENGTH - strlen($header); + + return $header . substr($line, 0, $chunkSize); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php new file mode 100644 index 0000000..2e32fa7 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Handler\SyslogUdp\UdpSocket; + +/** + * A Handler for logging to a remote syslogd server. + * + * @author Jesper Skovgaard Nielsen + */ +class SyslogUdpHandler extends AbstractSyslogHandler +{ + protected $socket; + + /** + * @param string $host + * @param int $port + * @param mixed $facility + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($facility, $level, $bubble); + + $this->socket = new UdpSocket($host, $port ?: 514); + } + + protected function write(array $record) + { + $lines = $this->splitMessageIntoLines($record['formatted']); + + $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']]); + + foreach ($lines as $line) { + $this->socket->write($line, $header); + } + } + + public function close() + { + $this->socket->close(); + } + + private function splitMessageIntoLines($message) + { + if (is_array($message)) { + $message = implode("\n", $message); + } + + return preg_split('/$\R?^/m', $message); + } + + /** + * Make common syslog header (see rfc5424) + */ + protected function makeCommonSyslogHeader($severity) + { + $priority = $severity + $this->facility; + + return "<$priority>1 "; + } + + /** + * Inject your own socket, mainly used for testing + */ + public function setSocket($socket) + { + $this->socket = $socket; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php new file mode 100644 index 0000000..e24fa9e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + * + * @method boolean hasEmergency($record) + * @method boolean hasAlert($record) + * @method boolean hasCritical($record) + * @method boolean hasError($record) + * @method boolean hasWarning($record) + * @method boolean hasNotice($record) + * @method boolean hasInfo($record) + * @method boolean hasDebug($record) + * + * @method boolean hasEmergencyRecords() + * @method boolean hasAlertRecords() + * @method boolean hasCriticalRecords() + * @method boolean hasErrorRecords() + * @method boolean hasWarningRecords() + * @method boolean hasNoticeRecords() + * @method boolean hasInfoRecords() + * @method boolean hasDebugRecords() + * + * @method boolean hasEmergencyThatContains($message) + * @method boolean hasAlertThatContains($message) + * @method boolean hasCriticalThatContains($message) + * @method boolean hasErrorThatContains($message) + * @method boolean hasWarningThatContains($message) + * @method boolean hasNoticeThatContains($message) + * @method boolean hasInfoThatContains($message) + * @method boolean hasDebugThatContains($message) + * + * @method boolean hasEmergencyThatMatches($message) + * @method boolean hasAlertThatMatches($message) + * @method boolean hasCriticalThatMatches($message) + * @method boolean hasErrorThatMatches($message) + * @method boolean hasWarningThatMatches($message) + * @method boolean hasNoticeThatMatches($message) + * @method boolean hasInfoThatMatches($message) + * @method boolean hasDebugThatMatches($message) + * + * @method boolean hasEmergencyThatPasses($message) + * @method boolean hasAlertThatPasses($message) + * @method boolean hasCriticalThatPasses($message) + * @method boolean hasErrorThatPasses($message) + * @method boolean hasWarningThatPasses($message) + * @method boolean hasNoticeThatPasses($message) + * @method boolean hasInfoThatPasses($message) + * @method boolean hasDebugThatPasses($message) + */ +class TestHandler extends AbstractProcessingHandler +{ + protected $records = array(); + protected $recordsByLevel = array(); + + public function getRecords() + { + return $this->records; + } + + protected function hasRecordRecords($level) + { + return isset($this->recordsByLevel[$level]); + } + + protected function hasRecord($record, $level) + { + if (is_array($record)) { + $record = $record['message']; + } + + return $this->hasRecordThatPasses(function ($rec) use ($record) { + return $rec['message'] === $record; + }, $level); + } + + public function hasRecordThatContains($message, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== false; + }, $level); + } + + public function hasRecordThatMatches($regex, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($regex) { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + + public function hasRecordThatPasses($predicate, $level) + { + if (!is_callable($predicate)) { + throw new \InvalidArgumentException("Expected a callable for hasRecordThatSucceeds"); + } + + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if (call_user_func($predicate, $rec, $i)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + public function __call($method, $args) + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . 'Record' . $matches[3]; + $level = constant('Monolog\Logger::' . strtoupper($matches[2])); + if (method_exists($this, $genericMethod)) { + $args[] = $level; + + return call_user_func_array(array($this, $genericMethod), $args); + } + } + + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php new file mode 100644 index 0000000..05a8817 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers suppressing failures of each handler + * and continuing through to give every handler a chance to succeed. + * + * @author Craig D'Amelio + */ +class WhatFailureGroupHandler extends GroupHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + } catch (\Exception $e) { + // What failure? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + } catch (\Exception $e) { + // What failure? + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php new file mode 100644 index 0000000..f22cf21 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to Zend Monitor + * + * @author Christian Bergau + */ +class ZendMonitorHandler extends AbstractProcessingHandler +{ + /** + * Monolog level / ZendMonitor Custom Event priority map + * + * @var array + */ + protected $levelMap = array( + Logger::DEBUG => 1, + Logger::INFO => 2, + Logger::NOTICE => 3, + Logger::WARNING => 4, + Logger::ERROR => 5, + Logger::CRITICAL => 6, + Logger::ALERT => 7, + Logger::EMERGENCY => 0, + ); + + /** + * Construct + * + * @param int $level + * @param bool $bubble + * @throws MissingExtensionException + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + if (!function_exists('zend_monitor_custom_event')) { + throw new MissingExtensionException('You must have Zend Server installed in order to use this handler'); + } + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->writeZendMonitorCustomEvent( + $this->levelMap[$record['level']], + $record['message'], + $record['formatted'] + ); + } + + /** + * Write a record to Zend Monitor + * + * @param int $level + * @param string $message + * @param array $formatted + */ + protected function writeZendMonitorCustomEvent($level, $message, $formatted) + { + zend_monitor_custom_event($level, $message, $formatted); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFormatter() + { + return new NormalizerFormatter(); + } + + /** + * Get the level map + * + * @return array + */ + public function getLevelMap() + { + return $this->levelMap; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Logger.php b/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000..d31a098 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,649 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Monolog\Handler\HandlerInterface; +use Monolog\Handler\StreamHandler; +use Psr\Log\LoggerInterface; +use Psr\Log\InvalidArgumentException; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + */ +class Logger implements LoggerInterface +{ + /** + * Detailed debug information + */ + const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + const INFO = 200; + + /** + * Uncommon events + */ + const NOTICE = 250; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + const WARNING = 300; + + /** + * Runtime errors + */ + const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + const ALERT = 550; + + /** + * Urgent alert. + */ + const EMERGENCY = 600; + + /** + * Monolog API version + * + * This is only bumped when API breaks are done and should + * follow the major version of the library + * + * @var int + */ + const API = 1; + + /** + * Logging levels from syslog protocol defined in RFC 5424 + * + * @var array $levels Logging levels + */ + protected static $levels = array( + 100 => 'DEBUG', + 200 => 'INFO', + 250 => 'NOTICE', + 300 => 'WARNING', + 400 => 'ERROR', + 500 => 'CRITICAL', + 550 => 'ALERT', + 600 => 'EMERGENCY', + ); + + /** + * @var \DateTimeZone + */ + protected static $timezone; + + /** + * @var string + */ + protected $name; + + /** + * The handler stack + * + * @var HandlerInterface[] + */ + protected $handlers; + + /** + * Processors that will process all log records + * + * To process records of a single handler instead, add the processor on that specific handler + * + * @var callable[] + */ + protected $processors; + + /** + * @param string $name The logging channel + * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param callable[] $processors Optional array of processors + */ + public function __construct($name, array $handlers = array(), array $processors = array()) + { + $this->name = $name; + $this->handlers = $handlers; + $this->processors = $processors; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Pushes a handler on to the stack. + * + * @param HandlerInterface $handler + * @return $this + */ + public function pushHandler(HandlerInterface $handler) + { + array_unshift($this->handlers, $handler); + + return $this; + } + + /** + * Pops a handler from the stack + * + * @return HandlerInterface + */ + public function popHandler() + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + + return array_shift($this->handlers); + } + + /** + * Set handlers, replacing all existing ones. + * + * If a map is passed, keys will be ignored. + * + * @param HandlerInterface[] $handlers + * @return $this + */ + public function setHandlers(array $handlers) + { + $this->handlers = array(); + foreach (array_reverse($handlers) as $handler) { + $this->pushHandler($handler); + } + + return $this; + } + + /** + * @return HandlerInterface[] + */ + public function getHandlers() + { + return $this->handlers; + } + + /** + * Adds a processor on to the stack. + * + * @param callable $callback + * @return $this + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * @return callable[] + */ + public function getProcessors() + { + return $this->processors; + } + + /** + * Adds a log record. + * + * @param integer $level The logging level + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addRecord($level, $message, array $context = array()) + { + if (!$this->handlers) { + $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG)); + } + + $levelName = static::getLevelName($level); + + // check if any handler will handle this message so we can return early and save cycles + $handlerKey = null; + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling(array('level' => $level))) { + $handlerKey = $key; + break; + } + } + + if (null === $handlerKey) { + return false; + } + + if (!static::$timezone) { + static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + + $record = array( + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => $levelName, + 'channel' => $this->name, + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone), + 'extra' => array(), + ); + + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + while (isset($this->handlers[$handlerKey]) && + false === $this->handlers[$handlerKey]->handle($record)) { + $handlerKey++; + } + + return true; + } + + /** + * Adds a log record at the DEBUG level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addDebug($message, array $context = array()) + { + return $this->addRecord(static::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addInfo($message, array $context = array()) + { + return $this->addRecord(static::INFO, $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addNotice($message, array $context = array()) + { + return $this->addRecord(static::NOTICE, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addWarning($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addError($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addCritical($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addAlert($message, array $context = array()) + { + return $this->addRecord(static::ALERT, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addEmergency($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + + /** + * Gets all supported logging levels. + * + * @return array Assoc array with human-readable level names => level codes. + */ + public static function getLevels() + { + return array_flip(static::$levels); + } + + /** + * Gets the name of the logging level. + * + * @param integer $level + * @return string + */ + public static function getLevelName($level) + { + if (!isset(static::$levels[$level])) { + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels))); + } + + return static::$levels[$level]; + } + + /** + * Converts PSR-3 levels to Monolog ones if necessary + * + * @param string|int Level number (monolog) or name (PSR-3) + * @return int + */ + public static function toMonologLevel($level) + { + if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) { + return constant(__CLASS__.'::'.strtoupper($level)); + } + + return $level; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @param integer $level + * @return Boolean + */ + public function isHandling($level) + { + $record = array( + 'level' => $level, + ); + + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * Adds a log record at an arbitrary level. + * + * This method allows for compatibility with common interfaces. + * + * @param mixed $level The log level + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function log($level, $message, array $context = array()) + { + $level = static::toMonologLevel($level); + + return $this->addRecord($level, $message, $context); + } + + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function debug($message, array $context = array()) + { + return $this->addRecord(static::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function info($message, array $context = array()) + { + return $this->addRecord(static::INFO, $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function notice($message, array $context = array()) + { + return $this->addRecord(static::NOTICE, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function warn($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function warning($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function err($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function error($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function crit($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function critical($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function alert($message, array $context = array()) + { + return $this->addRecord(static::ALERT, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function emerg($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function emergency($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + + /** + * Set the timezone to be used for the timestamp of log records. + * + * This is stored globally for all Logger instances + * + * @param \DateTimeZone $tz Timezone object + */ + public static function setTimezone(\DateTimeZone $tz) + { + self::$timezone = $tz; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 0000000..1899400 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; + +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + */ +class GitProcessor +{ + private $level; + private static $cache; + + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['git'] = self::getGitInfo(); + + return $record; + } + + private static function getGitInfo() + { + if (self::$cache) { + return self::$cache; + } + + $branches = `git branch -v --no-abbrev`; + if (preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) { + return self::$cache = array( + 'branch' => $matches[1], + 'commit' => $matches[2], + ); + } + + return self::$cache = array(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000..8f553fe --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +use Monolog\Logger; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + */ +class IntrospectionProcessor +{ + private $level; + + private $skipClassesPartials; + + private $skipFunctions = array( + 'call_user_func', + 'call_user_func_array', + ); + + public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array()) + { + $this->level = Logger::toMonologLevel($level); + $this->skipClassesPartials = array_merge(array('Monolog\\'), $skipClassesPartials); + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $trace = debug_backtrace(); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + if (isset($trace[$i]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (strpos($trace[$i]['class'], $part) !== false) { + $i++; + continue 2; + } + } + } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) { + $i++; + continue; + } + + break; + } + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + array( + 'file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, + 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ) + ); + + return $record; + } + + private function isTraceClassOrSkippedFunction(array $trace, $index) + { + if (!isset($trace[$index])) { + return false; + } + + return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000..0543e92 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_peak_usage($this->realUsage); + $formatted = $this->formatBytes($bytes); + + $record['extra']['memory_peak_usage'] = $formatted; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000..eb802c0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor +{ + /** + * @var boolean If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. + */ + protected $realUsage; + + /** + * @var boolean If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + protected $useFormatting; + + /** + * @param boolean $realUsage Set this to true to get the real size of memory allocated from system. + * @param boolean $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + public function __construct($realUsage = true, $useFormatting = true) + { + $this->realUsage = (boolean) $realUsage; + $this->useFormatting = (boolean) $useFormatting; + } + + /** + * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is + * + * @param int $bytes + * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as is + */ + protected function formatBytes($bytes) + { + $bytes = (int) $bytes; + + if (!$this->useFormatting) { + return $bytes; + } + + if ($bytes > 1024 * 1024) { + return round($bytes / 1024 / 1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes / 1024, 2).' KB'; + } + + return $bytes . ' B'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000..2783d65 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_usage($this->realUsage); + $formatted = $this->formatBytes($bytes); + + $record['extra']['memory_usage'] = $formatted; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php new file mode 100644 index 0000000..9d3f559 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $record['extra']['process_id'] = getmypid(); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php new file mode 100644 index 0000000..c2686ce --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Processes a record's message according to PSR-3 rules + * + * It replaces {foo} with the value from $context['foo'] + * + * @author Jordi Boggiano + */ +class PsrLogMessageProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + if (false === strpos($record['message'], '{')) { + return $record; + } + + $replacements = array(); + foreach ($record['context'] as $key => $val) { + if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { + $replacements['{'.$key.'}'] = $val; + } elseif (is_object($val)) { + $replacements['{'.$key.'}'] = '[object '.get_class($val).']'; + } else { + $replacements['{'.$key.'}'] = '['.gettype($val).']'; + } + } + + $record['message'] = strtr($record['message'], $replacements); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php new file mode 100644 index 0000000..7e2df2a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds a tags array into record + * + * @author Martijn Riemers + */ +class TagProcessor +{ + private $tags; + + public function __construct(array $tags = array()) + { + $this->setTags($tags); + } + + public function addTags(array $tags = array()) + { + $this->tags = array_merge($this->tags, $tags); + } + + public function setTags(array $tags = array()) + { + $this->tags = $tags; + } + + public function __invoke(array $record) + { + $record['extra']['tags'] = $this->tags; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php new file mode 100644 index 0000000..812707c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Adds a unique identifier into records + * + * @author Simon Mönch + */ +class UidProcessor +{ + private $uid; + + public function __construct($length = 7) + { + if (!is_int($length) || $length > 32 || $length < 1) { + throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); + } + + $this->uid = substr(hash('md5', uniqid('', true)), 0, $length); + } + + public function __invoke(array $record) + { + $record['extra']['uid'] = $this->uid; + + return $record; + } + + /** + * @return string + */ + public function getUid() + { + return $this->uid; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..21f22a6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor +{ + /** + * @var array|\ArrayAccess + */ + protected $serverData; + + /** + * @var array + */ + protected $extraFields = array( + 'url' => 'REQUEST_URI', + 'ip' => 'REMOTE_ADDR', + 'http_method' => 'REQUEST_METHOD', + 'server' => 'SERVER_NAME', + 'referrer' => 'HTTP_REFERER', + ); + + /** + * @param array|\ArrayAccess $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|null $extraFields Extra field names to be added (all available by default) + */ + public function __construct($serverData = null, array $extraFields = null) + { + if (null === $serverData) { + $this->serverData = &$_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + + if (null !== $extraFields) { + foreach (array_keys($this->extraFields) as $fieldName) { + if (!in_array($fieldName, $extraFields)) { + unset($this->extraFields[$fieldName]); + } + } + } + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + $record['extra'] = $this->appendExtraFields($record['extra']); + + return $record; + } + + /** + * @param string $extraName + * @param string $serverName + * @return $this + */ + public function addExtraField($extraName, $serverName) + { + $this->extraFields[$extraName] = $serverName; + + return $this; + } + + /** + * @param array $extra + * @return array + */ + private function appendExtraFields(array $extra) + { + foreach ($this->extraFields as $extraName => $serverName) { + $extra[$extraName] = isset($this->serverData[$serverName]) ? $this->serverData[$serverName] : null; + } + + if (isset($this->serverData['UNIQUE_ID'])) { + $extra['unique_id'] = $this->serverData['UNIQUE_ID']; + } + + return $extra; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Registry.php b/vendor/monolog/monolog/src/Monolog/Registry.php new file mode 100644 index 0000000..a33cb7c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Registry.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use InvalidArgumentException; + +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->addError('Sent to $api Logger instance'); + * Monolog\Registry::application()->addError('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (by named indexes) + * + * @var Logger[] + */ + private static $loggers = array(); + + /** + * Adds new logging channel to the registry + * + * @param Logger $logger Instance of the logging channel + * @param string|null $name Name of the logging channel ($logger->getName() by default) + * @param boolean $overwrite Overwrite instance in the registry if the given name already exists? + * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists + */ + public static function addLogger(Logger $logger, $name = null, $overwrite = false) + { + $name = $name ?: $logger->getName(); + + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new InvalidArgumentException('Logger with the given name already exists'); + } + + self::$loggers[$name] = $logger; + } + + /** + * Checks if such logging channel exists by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function hasLogger($logger) + { + if ($logger instanceof Logger) { + $index = array_search($logger, self::$loggers, true); + + return false !== $index; + } else { + return isset(self::$loggers[$logger]); + } + } + + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger) + { + if ($logger instanceof Logger) { + if (false !== ($idx = array_search($logger, self::$loggers, true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + + /** + * Clears the registry + */ + public static function clear() + { + self::$loggers = array(); + } + + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @return Logger Requested instance of Logger + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function getInstance($name) + { + if (!isset(self::$loggers[$name])) { + throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + + return self::$loggers[$name]; + } + + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param array $arguments Arguments passed to static method call + * @return Logger Requested instance of Logger + * @throws \InvalidArgumentException If named Logger instance is not in the registry + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/vendor/paragonie/random_compat/.scrutinizer.yml b/vendor/paragonie/random_compat/.scrutinizer.yml new file mode 100644 index 0000000..a2f32ea --- /dev/null +++ b/vendor/paragonie/random_compat/.scrutinizer.yml @@ -0,0 +1,4 @@ +checks: + php: + code_rating: true + duplication: false \ No newline at end of file diff --git a/vendor/paragonie/random_compat/.travis.yml b/vendor/paragonie/random_compat/.travis.yml new file mode 100644 index 0000000..43ec127 --- /dev/null +++ b/vendor/paragonie/random_compat/.travis.yml @@ -0,0 +1,26 @@ +language: php +php: + +- "7.0" +- "5.6" +- "5.5" +- "5.4" +- "5.3" +- "hhvm" + + +sudo: false + +matrix: + fast_finish: true + allow_failures: + - php: "hhvm" + +install: + +- composer install +- composer self-update +- composer update +- chmod +x ./phpunit.sh + +script: ./phpunit.sh travis diff --git a/vendor/paragonie/random_compat/ERRATA.md b/vendor/paragonie/random_compat/ERRATA.md new file mode 100644 index 0000000..9c0ef9f --- /dev/null +++ b/vendor/paragonie/random_compat/ERRATA.md @@ -0,0 +1,40 @@ +## Errata (Design Decisions) + +### Reasoning Behind the Order of Preferred Random Data Sources + +The order is: + + 1. `libsodium if available` + 2. `fread() /dev/urandom if available` + 3. `mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)` + 4. `COM('CAPICOM.Utilities.1')->GetRandom()` + 5. `openssl_random_pseudo_bytes()` + +If libsodium is available, we get random data from it. This is the preferred +method on all OSes, but libsodium is not very widely installed, so other +fallbacks are available. + +Next, we read `/dev/urandom` (if it exists). This is the preferred file to read +for random data for cryptographic purposes for BSD and Linux. + +Despite [strongly urging people not to use mcrypt in their projects](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong), +because libmcrypt is abandonware and the API puts too much responsibility on the +implementor, we prioritize `mcrypt_create_iv()` with `MCRYPT_DEV_URANDOM` above +the remaining implementations. + +The reason is simple: `mcrypt_create_iv()` is part of PHP's `ext/mcrypt` code, +and is not part `libmcrypt`. It actually does the right thing: + + * On Unix-based operating systems, it reads from `/dev/urandom`, which is the + sane and correct thing to do. + * On Windows, it reads from `CryptGenRandom`, which is an exclusively Windows + way to get random bytes. + +If we're on Windows and don't have access to `mcrypt`, we use `CAPICOM.Utilities.1`. + +Finally, we use `openssl_random_pseudo_bytes()` **as a last resort**, due to +[PHP bug #70014](https://bugs.php.net/bug.php?id=70014). Internally, this +function calls `RAND_pseudo_bytes()`, which has been [deprecated](https://github.com/paragonie/random_compat/issues/5) +by the OpenSSL team. Furthermore, [it might silently return weak random data](https://github.com/paragonie/random_compat/issues/6#issuecomment-119564973) +if it is called before OpenSSL's **userspace** CSPRNG is seeded. Also, +[you want the OS CSPRNG, not a userspace CSPRNG](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/). diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 0000000..45c7017 --- /dev/null +++ b/vendor/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/paragonie/random_compat/README.md b/vendor/paragonie/random_compat/README.md new file mode 100644 index 0000000..ed5b1d8 --- /dev/null +++ b/vendor/paragonie/random_compat/README.md @@ -0,0 +1,149 @@ +# random_compat + +[![Build Status](https://travis-ci.org/paragonie/random_compat.svg?branch=master)](https://travis-ci.org/paragonie/random_compat) +[![Scrutinizer](https://scrutinizer-ci.com/g/paragonie/random_compat/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/paragonie/random_compat) + +PHP 5.x polyfill for `random_bytes()` and `random_int()` created and maintained +by [Paragon Initiative Enterprises](https://paragonie.com). + +Although this library *should* function in earlier versions of PHP, we will only +consider issues relevant to [supported PHP versions](https://secure.php.net/supported-versions.php). +**If you are using an unsupported version of PHP, please upgrade as soon as possible.** + +## Important + +Although this library has been examined by some security experts in the PHP +community, there will always be a chance that we overlooked something. Please +ask your favorite trusted hackers to hammer it for implementation errors and +bugs before even thinking about deploying it in production. + +**Do not use the master branch, use a [stable release](https://github.com/paragonie/random_compat/releases/latest).** + +For the background of this library, please refer to our blog post on +[Generating Random Integers and Strings in PHP](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php). + +### Usability Notice + +If PHP cannot safely generate random data, this library will throw an `Exception`. +It will never fall back to insecure random data. If this keeps happening, upgrade +to a newer version of PHP immediately. + +## Usage + +This library exposes the [CSPRNG functions added in PHP 7](https://secure.php.net/manual/en/ref.csprng.php) +for use in PHP 5 projects. Their behavior should be identical. + +### Generate a string of random bytes + +```php +try { + $string = random_bytes(32); +} catch (TypeError $e) { + // Well, it's an integer, so this IS unexpected. + die("An unexpected error has occurred"); +} catch (Error $e) { + // This is also unexpected because 32 is a reasonable integer. + die("An unexpected error has occurred"); +} catch (Exception $e) { + // If you get this message, the CSPRNG failed hard. + die("Could not generate a random string. Is our OS secure?"); +} + +var_dump(bin2hex($string)); +// string(64) "5787c41ae124b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2eeac6f" +``` + +### Generate a random integer between two given integers (inclusive) + +```php +try { + $int = random_int(0,255); + +} catch (TypeError $e) { + // Well, it's an integer, so this IS unexpected. + die("An unexpected error has occurred"); +} catch (Error $e) { + // This is also unexpected because 0 and 255 are both reasonable integers. + die("An unexpected error has occurred"); +} catch (Exception $e) { + // If you get this message, the CSPRNG failed hard. + die("Could not generate a random string. Is our OS secure?"); +} + +var_dump($int); +// int(47) +``` + +### Exception handling + +When handling exceptions and errors you must account for differences between +PHP 5 and PHP7. + +The differences: + +* Catching `Error` works, so long as it is caught before `Exception`. +* Catching `Exception` has different behavior, without previously catching `Error`. +* There is *no* portable way to catch all errors/exceptions. + +#### Our recommendation + +**Always** catch `Error` before `Exception`. + +#### Example + +```php +try { + return random_int(1, $userInput); +} catch (TypeError $e) { + // This is okay, so long as `Error` is caught before `Exception`. + throw new Exception('Please enter a number!'); +} catch (Error $e) { + // This is required, if you do not need to do anything just rethrow. + throw $e; +} catch (Exception $e) { + // This is optional and maybe omitted if you do not want to handle errors + // during generation. + throw new InternalServerErrorException( + 'Oops, our server is bust and cannot generate any random data.', + 500, + $e + ); +} +``` + +## Contributors + +This project would not be anywhere near as excellent as it is today if it +weren't for the contributions of the following individuals: + +* [@AndrewCarterUK (Andrew Carter)](https://github.com/AndrewCarterUK) +* [@asgrim (James Titcumb)](https://github.com/asgrim) +* [@CodesInChaos (Christian Winnerlein)](https://github.com/CodesInChaos) +* [@chriscct7 (Chris Christoff)](https://github.com/chriscct7) +* [@cs278 (Chris Smith)](https://github.com/cs278) +* [@cweagans (Cameron Eagans)](https://github.com/cweagans) +* [@dd32 (Dion Hulse)](https://github.com/dd32) +* [@geggleto (Glenn Eggleton)](https://github.com/geggleto) +* [@ircmaxell (Anthony Ferrara)](https://github.com/ircmaxell) +* [@jedisct1 (Frank Denis)](https://github.com/jedisct1) +* [@juliangut (Julián Gutiérrez)](https://github.com/juliangut) +* [@kelunik (Niklas Keller)](https://github.com/kelunik) +* [@lt (Leigh)](https://github.com/lt) +* [@MasonM (Mason Malone)](https://github.com/MasonM) +* [@mmeyer2k (Michael M)](https://github.com/mmeyer2k) +* [@narfbg (Andrey Andreev)](https://github.com/narfbg) +* [@nicolas-grekas (Nicolas Grekas)](https://github.com/nicolas-grekas) +* [@oittaa](https://github.com/oittaa) +* [@redragonx (Stephen Chavez)](https://github.com/redragonx) +* [@rchouinard (Ryan Chouinard)](https://github.com/rchouinard) +* [@SammyK (Sammy Kaye Powers)](https://github.com/SammyK) +* [@scottchiefbaker (Scott Baker)](https://github.com/scottchiefbaker) +* [@skyosev (Stoyan Kyosev)](https://github.com/skyosev) +* [@stof (Christophe Coevoet)](https://github.com/stof) +* [@teohhanhui (Teoh Han Hui)](https://github.com/teohhanhui) +* [@tom-- (Tom Worster)](https://github.com/tom--) +* [@tsyr2ko](https://github.com/tsyr2ko) +* [@trowski (Aaron Piotrowski)](https://github.com/trowski) +* [@twistor (Chris Lepannen)](https://github.com/twistor) +* [@voku (Lars Moelleken)](https://github.com/voku) +* [@xabbuh (Christian Flothmann)](https://github.com/xabbuh) diff --git a/vendor/paragonie/random_compat/SECURITY.md b/vendor/paragonie/random_compat/SECURITY.md new file mode 100644 index 0000000..8f731b3 --- /dev/null +++ b/vendor/paragonie/random_compat/SECURITY.md @@ -0,0 +1,108 @@ +# An Invitation to Security Researchers + +Every company says they take security "very seriously." Rather than bore anyone +with banal boilerplate, here are some quick answers followed by detailed +elaboration. If you have any questions about our policies, please email them to +`scott@paragonie.com`. + +## Quick Answers + +* There is no compulsion to disclose vulnerabilities privately, but we + appreciate a head's up. +* `security@paragonie.com` will get your reports to the right person. Our GPG + fingerprint, should you decide to encrypt your report, is + `7F52 D5C6 1D12 55C7 3136 2E82 6B97 A1C2 8264 04DA`. + +* **YES**, we will reward security researchers who disclose vulnerabilities in + our software. +* In most cases, **No Proof-of-Concept Required.** + +## How to Report a Security Bug to Paragon Initiative Enterprises + +### There is no compulsion to disclose privately. + +We believe vulnerability disclosure style is a personal choice and enjoy working +with a diverse community. We understand and appreciate the importance of Full +Disclosure in the history and practice of security research. + +We would *like* to know about high-severity bugs before they become public +knowledge, so we can fix them in a timely manner, but **we do not believe in +threatening researchers or trying to enforce vulnerability embargoes**. + +Ultimately, if you discover a security-affecting vulnerability, what you do with +it is your choice. We would like to work with people, and to celebrate and +reward their skill, experience, and dedication. We appreciate being informed of +our mistakes so we can learn from them and build a better product. Our goal is +to empower the community. + +### Where to Send Security Vulnerabilities + +Our security email address is `security@paragonie.com`. Also feel free to open a +new issue on Github if you want to disclose publicly. + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG + +mQENBFUgwRUBCADcIpqNwyYc5UmY/tpx1sF/rQ3knR1YNXYZThzFV+Gmqhp1fDH5 +qBs9foh1xwI6O7knWmQngnf/nBumI3x6xj7PuOdEZUh2FwCG/VWnglW8rKmoHzHA +ivjiu9SLnPIPAgHSHeh2XD7q3Ndm3nenbjAiRFNl2iXcwA2cTQp9Mmfw9vVcw0G0 +z1o0G3s8cC8ZS6flFySIervvfSRWj7A1acI5eE3+AH/qXJRdEJ+9J8OB65p1JMfk +6+fWgOB1XZxMpz70S0rW6IX38WDSRhEK2fXyZJAJjyt+YGuzjZySNSoQR/V6vNYn +syrNPCJ2i5CgZQxAkyBBcr7koV9RIhPRzct/ABEBAAG0IVNlY3VyaXR5IDxzZWN1 +cml0eUBwYXJhZ29uaWUuY29tPokBOQQTAQIAIwUCVSDBFQIbAwcLCQgHAwIBBhUI +AgkKCwQWAgMBAh4BAheAAAoJEGuXocKCZATat2YIAIoejNFEQ2c1iaOEtSuB7Pn/ +WLbsDsHNLDKOV+UnfaCjv/vL7D+5NMChFCi2frde/NQb2TsjqmIH+V+XbnJtlrXD +Vj7yvMVal+Jqjwj7v4eOEWcKVcFZk+9cfUgh7t92T2BMX58RpgZF0IQZ6Z1R3FfC +9Ub4X6ykW+te1q0/4CoRycniwmlQi6iGSr99LQ5pfJq2Qlmz/luTZ0UX0h575T7d +cp2T1sX/zFRk/fHeANWSksipdDBjAXR7NMnYZgw2HghEdFk/xRDY7K1NRWNZBf05 +WrMHmh6AIVJiWZvI175URxEe268hh+wThBhXQHMhFNJM1qPIuzb4WogxM3UUD7m5 +AQ0EVSDBFQEIALNkpzSuJsHAHh79sc0AYWztdUe2MzyofQbbOnOCpWZebYsC3EXU +335fIg59k0m6f+O7GmEZzzIv5v0i99GS1R8CJm6FvhGqtH8ZqmOGbc71WdJSiNVE +0kpQoJlVzRbig6ZyyjzrggbM1eh5OXOk5pw4+23FFEdw7JWU0HJS2o71r1hwp05Z +vy21kcUEobz/WWQQyGS0Neo7PJn+9KS6wOxXul/UE0jct/5f7KLMdWMJ1VgniQmm +hjvkHLPSICteqCI04RfcmMseW9gueHQXeUu1SNIvsWa2MhxjeBej3pDnrZWszKwy +gF45GO9/v4tkIXNMy5J1AtOyRgQ3IUMqp8EAEQEAAYkBHwQYAQIACQUCVSDBFQIb +DAAKCRBrl6HCgmQE2jnIB/4/xFz8InpM7eybnBOAir3uGcYfs3DOmaKn7qWVtGzv +rKpQPYnVtlU2i6Z5UO4c4jDLT/8Xm1UDz3Lxvqt4xCaDwJvBZexU5BMK8l5DvOzH +6o6P2L1UDu6BvmPXpVZz7/qUhOnyf8VQg/dAtYF4/ax19giNUpI5j5o5mX5w80Rx +qSXV9NdSL4fdjeG1g/xXv2luhoV53T1bsycI3wjk/x5tV+M2KVhZBvvuOm/zhJje +oLWp0saaESkGXIXqurj6gZoujJvSvzl0n9F9VwqMEizDUfrXgtD1siQGhP0sVC6q +ha+F/SAEJ0jEquM4TfKWWU2S5V5vgPPpIQSYRnhQW4b1 +=xJPW +-----END PGP PUBLIC KEY BLOCK----- +``` + +### We Will Reward Security Researchers + +**This process has not been formalized; nor have dollar amounts been +discussed.** + +However, if you report a valid security-affecting bug, we will compensate you +for the time spent finding the vulnerability and reward you for being a good +neighbor. + +#### What does a "valid" bug mean? + +There are two sides to this: + +1. Some have spammed projects with invalid bug reports hoping to collect + bounties for pressing a button and running an automated analysis tool. This + is not cool. +2. There is a potential for the developers of a project to declare all security + bug reports as invalid to save money. + +Our team members have an established history of reporting vulnerabilities to +large open source projects. **We aren't in the business of ripping people off.** +When in doubt, our policy is to err on the side of generosity. + +### No Proof-of-Concept Required + +We might ask for one if we feel we do not understand some of the details +pertaining to a specific vulnerability. We certainly appreciate them if you +include them in your report, but we believe **the burden lies with the developer +to prove their software *is* secure** rather than with the researcher to prove +that it isn't. + +In our experience, most bugs are simpler to fix than they are to exploit. + diff --git a/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json new file mode 100644 index 0000000..d363f4c --- /dev/null +++ b/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,35 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": ["lib/random.php"] + } +} diff --git a/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/vendor/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 0000000..a3cc90b --- /dev/null +++ b/vendor/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,160 @@ + operators might accidentally let a float + * through. + * + * @param numeric $number The number we want to convert to an int + * @param boolean $fail_open Set to true to not throw an exception + * + * @return int (or float if $fail_open) + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_numeric($number)) { + $number += 0; + } + if ( + is_float($number) && + $number > ~PHP_INT_MAX && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + if (is_int($number) || $fail_open) { + return $number; + } + throw new TypeError( + 'Expected an integer.' + ); + } +} diff --git a/vendor/paragonie/random_compat/lib/error_polyfill.php b/vendor/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 0000000..57cfefd --- /dev/null +++ b/vendor/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,42 @@ +GetRandom() + * 5. openssl_random_pseudo_bytes() (absolute last resort) + * + * See ERRATA.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + require_once $RandomCompatDIR.'/random_bytes_libsodium.php'; + } + if ( + !function_exists('random_bytes') && + DIRECTORY_SEPARATOR === '/' && + @is_readable('/dev/urandom') + ) { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + // + // Error suppression on is_readable() in case of an open_basedir or + // safe_mode failure. All we care about is whether or not we can + // read it at this point. If the PHP environment is going to panic + // over trying to see if the file can be read in the first place, + // that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php'; + } + if ( + !function_exists('random_bytes') && + PHP_VERSION_ID >= 50307 && + extension_loaded('mcrypt') + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR.'/random_bytes_mcrypt.php'; + } + if ( + !function_exists('random_bytes') && + extension_loaded('com_dotnet') && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + if (method_exists($RandomCompatCOMtest, 'GetRandom')) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + if ( + !function_exists('random_bytes') && + extension_loaded('openssl') && + ( + // Unix-like with PHP >= 5.3.0 or + ( + DIRECTORY_SEPARATOR === '/' && + PHP_VERSION_ID >= 50300 + ) || + // Windows with PHP >= 5.4.1 + PHP_VERSION_ID >= 50401 + ) + ) { + // See random_bytes_openssl.php + require_once $RandomCompatDIR.'/random_bytes_openssl.php'; + } + if (!function_exists('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + */ + function random_bytes() + { + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + } + } + } + if (!function_exists('random_int')) { + require_once $RandomCompatDIR.'/random_int.php'; + } + $RandomCompatDIR = null; +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 0000000..0a781f4 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,77 @@ +GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 0000000..5d07104 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,142 @@ + 0); + + /** + * Is our result valid? + */ + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 0000000..938cac9 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,84 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + $buf = \Sodium\randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); +} diff --git a/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 0000000..5a1b688 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,72 @@ + operators might accidentally let a float + * through. + */ + + try { + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + try { + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + if ($max === $min) { + return $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + $mask = ~0; + } else { + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + if ($randomByteString === false) { + throw new Exception( + 'Random number generator failure' + ); + } + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val = 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + return (int) $val; +} diff --git a/vendor/paragonie/random_compat/phpunit.sh b/vendor/paragonie/random_compat/phpunit.sh new file mode 100755 index 0000000..c4c6a9f --- /dev/null +++ b/vendor/paragonie/random_compat/phpunit.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +if [ "$1" == 'full' ]; then + fulltest=1 +elif [ "$1" == 'each' ]; then + testeach=1 +else + fulltest=0 +fi + +PHP_VERSION=$(php -r "echo PHP_VERSION_ID;") + +echo +echo -e "\033[33mBegin Unit Testing\033[0m" +# Run the testing suite +echo "Basic test suite:" +php vendor/bin/phpunit tests/unit +if [ $? -ne 0 ]; then + # Test failure + exit 1 +fi +echo "With open_basedir enabled:" +php -d open_basedir=`pwd` vendor/bin/phpunit tests/unit +if [ $? -ne 0 ]; then + # Test failure + exit 1 +fi +echo "With open_basedir enabled, allowing /dev:" +php -d open_basedir=`pwd`:/dev vendor/bin/phpunit tests/unit +if [ $? -ne 0 ]; then + # Test failure + exit 1 +fi +echo "With mbstring.func_overload enabled:" +php -d mbstring.func_overload=7 vendor/bin/phpunit tests/unit +if [ $? -ne 0 ]; then + # Test failure + exit 1 +fi + +if [[ "$testeach" == "1" ]]; then + echo " CAPICOM:" + php vendor/bin/phpunit --bootstrap tests/specific/capicom.php tests/unit + echo " /dev/urandom:" + php vendor/bin/phpunit --bootstrap tests/specific/dev_urandom.php tests/unit + echo " libsodium:" + php vendor/bin/phpunit --bootstrap tests/specific/libsodium.php tests/unit + echo " mcrypt:" + php vendor/bin/phpunit --bootstrap tests/specific/mcrypt.php tests/unit + echo " openssl:" + php vendor/bin/phpunit --bootstrap tests/specific/openssl.php tests/unit +fi + +# Should we perform full statistical analyses? +if [[ "$fulltest" == "1" ]]; then + php vendor/bin/phpunit tests/full + if [ $? -ne 0 ]; then + # Test failure + exit 1 + fi +fi + diff --git a/vendor/paragonie/random_compat/phpunit.xml.dist b/vendor/paragonie/random_compat/phpunit.xml.dist new file mode 100644 index 0000000..b4697d3 --- /dev/null +++ b/vendor/paragonie/random_compat/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + tests/unit + + + + + ./tests/unit + + + + + ./lib + + + \ No newline at end of file diff --git a/vendor/paragonie/random_compat/tests/full/DieHardTest.php b/vendor/paragonie/random_compat/tests/full/DieHardTest.php new file mode 100644 index 0000000..2e6dc70 --- /dev/null +++ b/vendor/paragonie/random_compat/tests/full/DieHardTest.php @@ -0,0 +1,61 @@ + $i, + 'value' => $buckets[$i], + 'min' => $min, + 'nums' => $nums_to_generate, + 'reason' => 'below min' + ]); + } + if ($buckets[$i] >= $max ) { + var_dump([ + 'bucket' => $i, + 'value' => $buckets[$i], + 'maax' => $max, + 'nums' => $nums_to_generate, + 'reason' => 'above max' + ]); + } + + $this->assertTrue($buckets[$i] < $max && $buckets[$i] > $min); + } + } + } +} \ No newline at end of file diff --git a/vendor/paragonie/random_compat/tests/full/StatTest.php b/vendor/paragonie/random_compat/tests/full/StatTest.php new file mode 100644 index 0000000..e78eb14 --- /dev/null +++ b/vendor/paragonie/random_compat/tests/full/StatTest.php @@ -0,0 +1,52 @@ + 30% but less than 170% + * + * This also catches 0 and 1000 + */ + public function testDistribution() + { + $integers = array_fill(0, 100, 0); + for ($i = 0; $i < 10000; ++$i) { + ++$integers[random_int(0,99)]; + } + for ($i = 0; $i < 100; ++$i) { + $this->assertFalse($integers[$i] < 30); + $this->assertFalse($integers[$i] > 170); + } + } + + /** + * This should be between 55% and 75%, always + */ + public function testCoverage() + { + $integers = array_fill(0, 2000, 0); + for ($i = 0; $i < 2000; ++$i) { + ++$integers[random_int(0,1999)]; + } + $coverage = 0; + for ($i = 0; $i < 2000; ++$i) { + if ($integers[$i] > 0) { + ++$coverage; + } + } + $this->assertTrue($coverage >= 1150); + $this->assertTrue($coverage <= 1350); + } + + public function testCompressionRatios() + { + $some_bytes = random_bytes(65536); + $compressed = gzcompress($some_bytes, 9); + if (function_exists('mb_strlen')) { + $length = mb_strlen($compressed, '8bit'); + } else { + $length = strlen($compressed); + } + $this->assertTrue($length >= 65000 && $length <= 67000); + } +} \ No newline at end of file diff --git a/vendor/paragonie/random_compat/tests/specific/capicom.php b/vendor/paragonie/random_compat/tests/specific/capicom.php new file mode 100644 index 0000000..640be1b --- /dev/null +++ b/vendor/paragonie/random_compat/tests/specific/capicom.php @@ -0,0 +1,7 @@ +assertTrue(function_exists('random_bytes')); + } + + public function testOutput() + { + $bytes = array( + random_bytes(12), + random_bytes(64), + random_bytes(64), + random_bytes(1.5) + ); + + $this->assertTrue( + strlen(bin2hex($bytes[0])) === 24 + ); + $this->assertTrue( + strlen(bin2hex($bytes[3])) === 2 + ); + + // This should never generate identical byte strings + $this->assertFalse( + $bytes[1] === $bytes[2] + ); + + try { + $x = random_bytes(~PHP_INT_MAX - 1000000000); + $this->assertTrue(false); + } catch (TypeError $ex) { + $this->assertTrue(true); + } catch (Error $ex) { + $this->assertTrue(true); + } catch (Exception $ex) { + $this->assertTrue(true); + } + + try { + $x = random_bytes(PHP_INT_MAX + 1000000000); + $this->assertTrue(false); + } catch (TypeError $ex) { + $this->assertTrue(true); + } catch (Error $ex) { + $this->assertTrue(true); + } catch (Exception $ex) { + $this->assertTrue(true); + } + } +} diff --git a/vendor/paragonie/random_compat/tests/unit/RandomIntTest.php b/vendor/paragonie/random_compat/tests/unit/RandomIntTest.php new file mode 100644 index 0000000..f9fc3c3 --- /dev/null +++ b/vendor/paragonie/random_compat/tests/unit/RandomIntTest.php @@ -0,0 +1,52 @@ +assertTrue(function_exists('random_int')); + } + + public function testOutput() + { + $half_neg_max = (~PHP_INT_MAX / 2); + $integers = array( + random_int(0, 1000), + random_int(1001,2000), + random_int(-100, -10), + random_int(-1000, 1000), + random_int(~PHP_INT_MAX, PHP_INT_MAX), + random_int("0", "1"), + random_int(0.11111, 0.99999), + random_int($half_neg_max, PHP_INT_MAX), + random_int(0.0, 255.0), + random_int(-4.5, -4.5), + random_int("1337e3","1337e3") + ); + + $this->assertFalse($integers[0] === $integers[1]); + $this->assertTrue($integers[0] >= 0 && $integers[0] <= 1000); + $this->assertTrue($integers[1] >= 1001 && $integers[1] <= 2000); + $this->assertTrue($integers[2] >= -100 && $integers[2] <= -10); + $this->assertTrue($integers[3] >= -1000 && $integers[3] <= 1000); + $this->assertTrue($integers[4] >= ~PHP_INT_MAX && $integers[4] <= PHP_INT_MAX); + $this->assertTrue($integers[5] >= 0 && $integers[5] <= 1); + $this->assertTrue($integers[6] === 0); + $this->assertTrue($integers[7] >= $half_neg_max && $integers[7] <= PHP_INT_MAX); + $this->assertTrue($integers[8] >= 0 && $integers[8] <= 255); + $this->assertTrue($integers[9] === -4); + $this->assertTrue($integers[10] === 1337000); + + try { + $h = random_int("2147483648", "2147483647"); + $i = random_int("9223372036854775808", "9223372036854775807"); + $this->assertFalse(is_int($i)); + $h = random_int("-2147483648", "2147483647"); + $i = random_int("-9223372036854775808", "9223372036854775807"); + $this->assertFalse(true); + } catch (Error $ex) { + $this->assertTrue($ex instanceof Error); + } catch (Exception $ex) { + $this->assertTrue($ex instanceof Exception); + } + } +} diff --git a/vendor/paragonie/random_compat/tests/unit/UtilityTest.php b/vendor/paragonie/random_compat/tests/unit/UtilityTest.php new file mode 100644 index 0000000..294a801 --- /dev/null +++ b/vendor/paragonie/random_compat/tests/unit/UtilityTest.php @@ -0,0 +1,95 @@ +markTestSkipped( + 'We don\' need to test this in PHP 7.' + ); + } + $this->assertEquals(RandomCompat_strlen("\xF0\x9D\x92\xB3"), 4); + } + + public function testIntval() + { + if (!function_exists('RandomCompat_intval')) { + return $this->markTestSkipped( + 'We don\' need to test this in PHP 7.' + ); + } + // Equals + $this->assertEquals( + abs(RandomCompat_intval(-4.5)), + abs(RandomCompat_intval(4.5)) + ); + + // True + $this->assertTrue( + is_int(RandomCompat_intval(PHP_INT_MAX, true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval(~PHP_INT_MAX, true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval(~PHP_INT_MAX + 1, true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval("1337e3", true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval("1.", true)) + ); + + // False + $this->assertFalse( + is_int(RandomCompat_intval((float) PHP_INT_MAX, true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval((float) ~PHP_INT_MAX, true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval(PHP_INT_MAX + 1, true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval(~PHP_INT_MAX - 1, true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval(~PHP_INT_MAX - 0.1, true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval(PHP_INT_MAX + 0.1, true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval("hello", true)) + ); + + if (PHP_INT_SIZE === 8) { + $this->assertFalse( + is_int(RandomCompat_intval("-9223372036854775809", true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval("-9223372036854775808", true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval("9223372036854775808", true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval("9223372036854775807", true)) + ); + } else { + $this->assertFalse( + is_int(RandomCompat_intval("2147483648", true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval("2147483647", true)) + ); + $this->assertFalse( + is_int(RandomCompat_intval("-2147483649", true)) + ); + $this->assertTrue( + is_int(RandomCompat_intval("-2147483648", true)) + ); + } + } +} diff --git a/vendor/psr/log/LICENSE b/vendor/psr/log/LICENSE new file mode 100644 index 0000000..474c952 --- /dev/null +++ b/vendor/psr/log/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/psr/log/Psr/Log/AbstractLogger.php b/vendor/psr/log/Psr/Log/AbstractLogger.php new file mode 100644 index 0000000..00f9034 --- /dev/null +++ b/vendor/psr/log/Psr/Log/AbstractLogger.php @@ -0,0 +1,120 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * @return null + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * @return null + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * @return null + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * @return null + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * @return null + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * @return null + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * @return null + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 0000000..67f852d --- /dev/null +++ b/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 0000000..476bb96 --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,114 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * @return null + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * @return null + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * @return null + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * @return null + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * @return null + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * @return null + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * @return null + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 0000000..553a3c5 --- /dev/null +++ b/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,27 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php new file mode 100644 index 0000000..a932815 --- /dev/null +++ b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php @@ -0,0 +1,116 @@ + " + * + * Example ->error('Foo') would yield "error Foo" + * + * @return string[] + */ + abstract function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + $level.' message of level '.$level.' with context: Bob', + $level.' message of level '.$level.' with context: Bob', + ); + $this->assertEquals($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + /** + * @expectedException Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + $dummy->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->getLogger()->warning($dummy); + } + + public function testContextCanContainAnything() + { + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest), + 'object' => new \DateTime, + 'resource' => fopen('php://memory', 'r'), + ); + + $this->getLogger()->warning('Crazy context data', $context); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $this->getLogger()->warning('Random message', array('exception' => 'oops')); + $this->getLogger()->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + } +} + +class DummyTest +{ +} \ No newline at end of file diff --git a/vendor/psr/log/README.md b/vendor/psr/log/README.md new file mode 100644 index 0000000..574bc1c --- /dev/null +++ b/vendor/psr/log/README.md @@ -0,0 +1,45 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json new file mode 100644 index 0000000..6bdcc21 --- /dev/null +++ b/vendor/psr/log/composer.json @@ -0,0 +1,17 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + } +} diff --git a/vendor/sensio/distribution-bundle/Composer/ScriptHandler.php b/vendor/sensio/distribution-bundle/Composer/ScriptHandler.php new file mode 100644 index 0000000..f7067ae --- /dev/null +++ b/vendor/sensio/distribution-bundle/Composer/ScriptHandler.php @@ -0,0 +1,466 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle\Composer; + +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\PhpExecutableFinder; +use Composer\Script\CommandEvent; + +/** + * @author Jordi Boggiano + */ +class ScriptHandler +{ + /** + * Composer variables are declared static so that an event could update + * a composer.json and set new options, making them immediately available + * to forthcoming listeners. + */ + protected static $options = array( + 'symfony-app-dir' => 'app', + 'symfony-web-dir' => 'web', + 'symfony-assets-install' => 'hard', + 'symfony-cache-warmup' => false, + ); + + /** + * Asks if the new directory structure should be used, installs the structure if needed. + * + * @param CommandEvent $event + */ + public static function defineDirectoryStructure(CommandEvent $event) + { + $options = static::getOptions($event); + + if (!getenv('SENSIOLABS_ENABLE_NEW_DIRECTORY_STRUCTURE') || !$event->getIO()->askConfirmation('Would you like to use Symfony 3 directory structure? [y/N] ', false)) { + return; + } + + $rootDir = getcwd(); + $appDir = $options['symfony-app-dir']; + $webDir = $options['symfony-web-dir']; + $binDir = static::$options['symfony-bin-dir'] = 'bin'; + $varDir = static::$options['symfony-var-dir'] = 'var'; + + static::updateDirectoryStructure($event, $rootDir, $appDir, $binDir, $varDir, $webDir); + } + + /** + * Builds the bootstrap file. + * + * The bootstrap file contains PHP file that are always needed by the application. + * It speeds up the application bootstrapping. + * + * @param $event CommandEvent A instance + */ + public static function buildBootstrap(CommandEvent $event) + { + $options = static::getOptions($event); + $bootstrapDir = $autoloadDir = $options['symfony-app-dir']; + + if (static::useNewDirectoryStructure($options)) { + $bootstrapDir = $options['symfony-var-dir']; + if (!static::hasDirectory($event, 'symfony-var-dir', $bootstrapDir, 'build bootstrap file')) { + return; + } + } + if (!static::hasDirectory($event, 'symfony-app-dir', $autoloadDir, 'build bootstrap file')) { + return; + } + + static::executeBuildBootstrap($event, $bootstrapDir, $autoloadDir, $options['process-timeout']); + } + + protected static function hasDirectory(CommandEvent $event, $configName, $path, $actionName) + { + if (!is_dir($path)) { + $event->getIO()->write(sprintf('The %s (%s) specified in composer.json was not found in %s, can not %s.', $configName, $path, getcwd(), $actionName)); + + return false; + } + + return true; + } + + /** + * Sets up deployment target specific features. + * Could be custom web server configs, boot command files etc. + * + * @param $event CommandEvent An instance + */ + public static function prepareDeploymentTarget(CommandEvent $event) + { + static::prepareDeploymentTargetHeroku($event); + } + + protected static function prepareDeploymentTargetHeroku(CommandEvent $event) + { + $options = static::getOptions($event); + if (($stack = getenv('STACK')) && ($stack == 'cedar' || $stack == 'cedar-14')) { + $fs = new Filesystem(); + if (!$fs->exists('Procfile')) { + $event->getIO()->write('Heroku deploy detected; creating default Procfile for "web" dyno'); + $fs->dumpFile('Procfile', sprintf('web: $(composer config bin-dir)/heroku-php-apache2 %s/', $options['symfony-web-dir'])); + } + } + } + + /** + * Clears the Symfony cache. + * + * @param $event CommandEvent A instance + */ + public static function clearCache(CommandEvent $event) + { + $options = static::getOptions($event); + $consoleDir = static::getConsoleDir($event, 'clear the cache'); + + if (null === $consoleDir) { + return; + } + + $warmup = ''; + if (!$options['symfony-cache-warmup']) { + $warmup = ' --no-warmup'; + } + + static::executeCommand($event, $consoleDir, 'cache:clear'.$warmup, $options['process-timeout']); + } + + /** + * Installs the assets under the web root directory. + * + * For better interoperability, assets are copied instead of symlinked by default. + * + * Even if symlinks work on Windows, this is only true on Windows Vista and later, + * but then, only when running the console with admin rights or when disabling the + * strict user permission checks (which can be done on Windows 7 but not on Windows + * Vista). + * + * @param $event CommandEvent A instance + */ + public static function installAssets(CommandEvent $event) + { + $options = static::getOptions($event); + $consoleDir = static::getConsoleDir($event, 'install assets'); + + if (null === $consoleDir) { + return; + } + + $webDir = $options['symfony-web-dir']; + + $symlink = ''; + if ($options['symfony-assets-install'] == 'symlink') { + $symlink = '--symlink '; + } elseif ($options['symfony-assets-install'] == 'relative') { + $symlink = '--symlink --relative '; + } + + if (!static::hasDirectory($event, 'symfony-web-dir', $webDir, 'install assets')) { + return; + } + + static::executeCommand($event, $consoleDir, 'assets:install '.$symlink.escapeshellarg($webDir), $options['process-timeout']); + } + + /** + * Updated the requirements file. + * + * @param $event CommandEvent A instance + */ + public static function installRequirementsFile(CommandEvent $event) + { + $options = static::getOptions($event); + $appDir = $options['symfony-app-dir']; + $fs = new Filesystem(); + + $newDirectoryStructure = static::useNewDirectoryStructure($options); + + if (!$newDirectoryStructure) { + if (!static::hasDirectory($event, 'symfony-app-dir', $appDir, 'install the requirements files')) { + return; + } + $fs->copy(__DIR__.'/../Resources/skeleton/app/SymfonyRequirements.php', $appDir.'/SymfonyRequirements.php', true); + $fs->copy(__DIR__.'/../Resources/skeleton/app/check.php', $appDir.'/check.php', true); + } else { + $binDir = $options['symfony-bin-dir']; + $varDir = $options['symfony-var-dir']; + if (!static::hasDirectory($event, 'symfony-var-dir', $varDir, 'install the requirements files')) { + return; + } + if (!static::hasDirectory($event, 'symfony-bin-dir', $binDir, 'install the requirements files')) { + return; + } + $fs->copy(__DIR__.'/../Resources/skeleton/app/SymfonyRequirements.php', $varDir.'/SymfonyRequirements.php', true); + $fs->copy(__DIR__.'/../Resources/skeleton/app/check.php', $binDir.'/symfony_requirements', true); + $fs->remove(array($appDir.'/check.php', $appDir.'/SymfonyRequirements.php', true)); + + $fs->dumpFile($binDir.'/symfony_requirements', '#!/usr/bin/env php'."\n".str_replace(".'/SymfonyRequirements.php'", ".'/".$fs->makePathRelative($varDir, $binDir)."SymfonyRequirements.php'", file_get_contents($binDir.'/symfony_requirements'))); + $fs->chmod($binDir.'/symfony_requirements', 0755); + } + + $webDir = $options['symfony-web-dir']; + + // if the user has already removed the config.php file, do nothing + // as the file must be removed for production use + if ($fs->exists($webDir.'/config.php')) { + if (!$newDirectoryStructure) { + $fs->copy(__DIR__.'/../Resources/skeleton/web/config.php', $webDir.'/config.php', true); + } else { + $fs->dumpFile($webDir.'/config.php', str_replace('/../app/SymfonyRequirements.php', '/'.$fs->makePathRelative($varDir, $webDir).'SymfonyRequirements.php', file_get_contents(__DIR__.'/../Resources/skeleton/web/config.php'))); + } + } + } + + public static function removeSymfonyStandardFiles(CommandEvent $event) + { + $options = static::getOptions($event); + $appDir = $options['symfony-app-dir']; + + if (!is_dir($appDir)) { + return; + } + + if (!is_dir($appDir.'/SymfonyStandard')) { + return; + } + + $fs = new Filesystem(); + $fs->remove($appDir.'/SymfonyStandard'); + } + + public static function doBuildBootstrap($bootstrapDir) + { + $file = $bootstrapDir.'/bootstrap.php.cache'; + if (file_exists($file)) { + unlink($file); + } + + $classes = array( + 'Symfony\\Component\\HttpFoundation\\ParameterBag', + 'Symfony\\Component\\HttpFoundation\\HeaderBag', + 'Symfony\\Component\\HttpFoundation\\FileBag', + 'Symfony\\Component\\HttpFoundation\\ServerBag', + 'Symfony\\Component\\HttpFoundation\\Request', + 'Symfony\\Component\\HttpFoundation\\Response', + 'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag', + + 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface', + 'Symfony\\Component\\DependencyInjection\\Container', + 'Symfony\\Component\\HttpKernel\\Kernel', + 'Symfony\\Component\\ClassLoader\\ClassCollectionLoader', + 'Symfony\\Component\\ClassLoader\\ApcClassLoader', + 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle', + 'Symfony\\Component\\Config\\ConfigCache', + // cannot be included as commands are discovered based on the path to this class via Reflection + //'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', + ); + + // introspect the autoloader to get the right file + // we cannot use class_exist() here as it would load the class + // which won't be included into the cache then. + // we know that composer autoloader is first (see bin/build_bootstrap.php) + $autoloaders = spl_autoload_functions(); + if (is_array($autoloaders[0]) && method_exists($autoloaders[0][0], 'findFile') && $autoloaders[0][0]->findFile('Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel')) { + $classes[] = 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel'; + } else { + $classes[] = 'Symfony\\Component\\HttpKernel\\HttpKernel'; + } + + ClassCollectionLoader::load($classes, dirname($file), basename($file, '.php.cache'), false, false, '.php.cache'); + + $bootstrapContent = substr(file_get_contents($file), 5); + + file_put_contents($file, sprintf(<<<'EOF' +getIO()->isDecorated()) { + $console .= ' --ansi'; + } + + $process = new Process($php.($phpArgs ? ' '.$phpArgs : '').' '.$console.' '.$cmd, null, null, null, $timeout); + $process->run(function ($type, $buffer) use ($event) { $event->getIO()->write($buffer, false); }); + if (!$process->isSuccessful()) { + throw new \RuntimeException(sprintf("An error occurred when executing the \"%s\" command:\n\n%s\n\n%s.", escapeshellarg($cmd), $process->getOutput(), $process->getErrorOutput())); + } + } + + protected static function executeBuildBootstrap(CommandEvent $event, $bootstrapDir, $autoloadDir, $timeout = 300) + { + $php = escapeshellarg(static::getPhp(false)); + $phpArgs = implode(' ', array_map('escapeshellarg', static::getPhpArguments())); + $cmd = escapeshellarg(__DIR__.'/../Resources/bin/build_bootstrap.php'); + $bootstrapDir = escapeshellarg($bootstrapDir); + $autoloadDir = escapeshellarg($autoloadDir); + $useNewDirectoryStructure = ''; + if (static::useNewDirectoryStructure(static::getOptions($event))) { + $useNewDirectoryStructure = escapeshellarg('--use-new-directory-structure'); + } + + $process = new Process($php.($phpArgs ? ' '.$phpArgs : '').' '.$cmd.' '.$bootstrapDir.' '.$autoloadDir.' '.$useNewDirectoryStructure, getcwd(), null, null, $timeout); + $process->run(function ($type, $buffer) use ($event) { $event->getIO()->write($buffer, false); }); + if (!$process->isSuccessful()) { + throw new \RuntimeException('An error occurred when generating the bootstrap file.'); + } + } + + protected static function updateDirectoryStructure(CommandEvent $event, $rootDir, $appDir, $binDir, $varDir, $webDir) + { + $event->getIO()->write('Updating Symfony directory structure...'); + + $fs = new Filesystem(); + + $fs->mkdir(array($binDir, $varDir)); + + foreach (array( + $appDir.'/console' => $binDir.'/console', + $appDir.'/phpunit.xml.dist' => $rootDir.'/phpunit.xml.dist', + ) as $source => $target) { + $fs->rename($source, $target, true); + } + + foreach (array('/logs', '/cache') as $dir) { + $fs->rename($appDir.$dir, $varDir.$dir); + } + + $gitignore = << + + + --> +EOF; + $phpunitKernelAfter = << + + +EOF; + $phpunit = str_replace(array('../src', '"bootstrap.php.cache"', $phpunitKernelBefore), array('src', '"'.$varDir.'/bootstrap.php.cache"', $phpunitKernelAfter), file_get_contents($rootDir.'/phpunit.xml.dist')); + $composer = str_replace('"symfony-app-dir": "app",', "\"symfony-app-dir\": \"app\",\n \"symfony-bin-dir\": \"bin\",\n \"symfony-var-dir\": \"var\",", file_get_contents($rootDir.'/composer.json')); + + $fs->dumpFile($webDir.'/app.php', str_replace($appDir.'/bootstrap.php.cache', $varDir.'/bootstrap.php.cache', file_get_contents($webDir.'/app.php'))); + $fs->dumpFile($webDir.'/app_dev.php', str_replace($appDir.'/bootstrap.php.cache', $varDir.'/bootstrap.php.cache', file_get_contents($webDir.'/app_dev.php'))); + $fs->dumpFile($binDir.'/console', str_replace(array(".'/bootstrap.php.cache'", ".'/AppKernel.php'"), array(".'/".$fs->makePathRelative($varDir, $binDir)."bootstrap.php.cache'", ".'/".$fs->makePathRelative($appDir, $binDir)."AppKernel.php'"), file_get_contents($binDir.'/console'))); + $fs->dumpFile($rootDir.'/phpunit.xml.dist', $phpunit); + $fs->dumpFile($rootDir.'/composer.json', $composer); + + $fs->dumpFile($rootDir.'/.gitignore', $gitignore); + + $fs->chmod($binDir.'/console', 0755); + } + + protected static function getOptions(CommandEvent $event) + { + $options = array_merge(static::$options, $event->getComposer()->getPackage()->getExtra()); + + $options['symfony-assets-install'] = getenv('SYMFONY_ASSETS_INSTALL') ?: $options['symfony-assets-install']; + + $options['process-timeout'] = $event->getComposer()->getConfig()->get('process-timeout'); + + return $options; + } + + protected static function getPhp($includeArgs = true) + { + $phpFinder = new PhpExecutableFinder(); + if (!$phpPath = $phpFinder->find($includeArgs)) { + throw new \RuntimeException('The php executable could not be found, add it to your PATH environment variable and try again'); + } + + return $phpPath; + } + + protected static function getPhpArguments() + { + $arguments = array(); + + $phpFinder = new PhpExecutableFinder(); + if (method_exists($phpFinder, 'findArguments')) { + $arguments = $phpFinder->findArguments(); + } + + if (false !== $ini = php_ini_loaded_file()) { + $arguments[] = '--php-ini='.$ini; + } + + return $arguments; + } + + /** + * Returns a relative path to the directory that contains the `console` command. + * + * @param CommandEvent $event The command event. + * @param string $actionName The name of the action + * + * @return string|null The path to the console directory, null if not found. + */ + protected static function getConsoleDir(CommandEvent $event, $actionName) + { + $options = static::getOptions($event); + + if (static::useNewDirectoryStructure($options)) { + if (!static::hasDirectory($event, 'symfony-bin-dir', $options['symfony-bin-dir'], $actionName)) { + return; + } + + return $options['symfony-bin-dir']; + } + + if (!static::hasDirectory($event, 'symfony-app-dir', $options['symfony-app-dir'], 'execute command')) { + return; + } + + return $options['symfony-app-dir']; + } + + /** + * Returns true if the new directory structure is used. + * + * @param array $options Composer options + * + * @return bool + */ + protected static function useNewDirectoryStructure(array $options) + { + return isset($options['symfony-var-dir']) && is_dir($options['symfony-var-dir']); + } +} diff --git a/vendor/sensio/distribution-bundle/DependencyInjection/SensioDistributionExtension.php b/vendor/sensio/distribution-bundle/DependencyInjection/SensioDistributionExtension.php new file mode 100644 index 0000000..bcee5e3 --- /dev/null +++ b/vendor/sensio/distribution-bundle/DependencyInjection/SensioDistributionExtension.php @@ -0,0 +1,33 @@ + + */ +class SensioDistributionExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('security.xml'); + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/symfony/sensiodistribution'; + } + + public function getAlias() + { + return 'sensio_distribution'; + } +} diff --git a/vendor/sensio/distribution-bundle/README.rst b/vendor/sensio/distribution-bundle/README.rst new file mode 100644 index 0000000..5e4db4a --- /dev/null +++ b/vendor/sensio/distribution-bundle/README.rst @@ -0,0 +1,27 @@ +SensioDistributionBundle +======================== + +SensioDistributionBundle provides useful developer features that can be re-used +amongst several Symfony Distributions. + +Composer Hooks +-------------- + +The bundle hooks up into the Composer process to automate the following actions +when running an install or an update: + +* Update the ``bootstrap.php.cache`` file (and clears the cache); + +* Install the assets under the web root directory; + +* Update the requirements file. + +Security +-------- + +The bundle includes the SensioLabs Security Checker. When included in a Symfony +application, the check is available: + +.. code-block:: bash + + $ ./app/console security:check diff --git a/vendor/sensio/distribution-bundle/Resources/bin/build.sh b/vendor/sensio/distribution-bundle/Resources/bin/build.sh new file mode 100755 index 0000000..9e23ca6 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/bin/build.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# This file is part of the Symfony Standard Edition. +# +# (c) Fabien Potencier +# +# For the full copyright and license information, please view the LICENSE +# file that was distributed with this source code. + +if [ ! $1 ]; then + echo "\033[37;41mYou must pass the build dir as an absolute path\033[0m" + exit 1 +fi + +if [ ! $2 ]; then + echo "\033[37;41mYou must pass the version to build\033[0m" + exit 1 +fi + +DIR=$1 +CURRENT=`php -r "echo realpath(dirname(\\$_SERVER['argv'][0]));"` + +if [[ ! "$DIR" = /* ]]; then + DIR="$CURRENT/$DIR" +fi + +if [ ! -d $DIR ]; then + echo "\033[37;41mThe build dir does not exist\033[0m" + exit 1 +fi + +# avoid the creation of ._* files +export COPY_EXTENDED_ATTRIBUTES_DISABLE=true +export COPYFILE_DISABLE=true + +# Temp dir +rm -rf /tmp/Symfony +mkdir /tmp/Symfony + +# Create project +composer create-project --prefer-dist --no-interaction symfony/framework-standard-edition /tmp/Symfony $2 + +if [ 0 -ne $? ]; then + echo "\033[37;41mVersion $2 does not exist\033[0m" + exit 1 +fi + +cd /tmp/Symfony + +# cleanup +rm -rf app/cache/* app/logs/* .git* +chmod 777 app/cache app/logs +find . -name .DS_Store | xargs rm -rf - + +VERSION=`grep ' VERSION ' vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php | sed -E "s/.*'(.+)'.*/\1/g"` + +# With vendors +cd /tmp/Symfony +TARGET=/tmp/Symfony/vendor + +# Doctrine +cd $TARGET/doctrine/orm && rm -rf UPGRADE* build* tests tools lib/vendor +cd $TARGET/doctrine/dbal && rm -rf build* tests lib/vendor +cd $TARGET/doctrine/common && rm -rf build* tests lib/vendor +if [ -d $TARGET/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle ]; then + cd $TARGET/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle && rm -rf Tests Resources/doc +else + cd $TARGET/doctrine/doctrine-bundle && rm -rf Tests Resources/doc +fi + +# kriswallsmith +if [ -d $TARGET/kriswallsmith/assetic ]; then + cd $TARGET/kriswallsmith/assetic && rm -rf CHANGELOG* phpunit.xml* tests docs +fi + +# Monolog +cd $TARGET/monolog/monolog && rm -rf README.markdown phpunit.xml* tests + +# Sensio +if [ -d $TARGET/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle ]; then + cd $TARGET/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +else + cd $TARGET/sensio/distribution-bundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +fi +if [ -d $TARGET/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle ]; then + cd $TARGET/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +else + cd $TARGET/sensio/framework-extra-bundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +fi +if [ -d $TARGET/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle ]; then + cd $TARGET/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +else + cd $TARGET/sensio/generator-bundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +fi + +# Swiftmailer +cd $TARGET/swiftmailer/swiftmailer && rm -rf CHANGES README* build* docs notes test-suite tests create_pear_package.php package* + +# Symfony +cd $TARGET/symfony/symfony && rm -rf README.md phpunit.xml* tests *.sh vendor + +if [ -d $TARGET/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle ]; then + cd $TARGET/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle && rm -rf Tests Resources/doc +elif [ -d $TARGET/symfony/assetic-bundle ]; then + cd $TARGET/symfony/assetic-bundle && rm -rf Tests Resources/doc +fi + +if [ -d $TARGET/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle ]; then + cd $TARGET/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle && rm -rf Tests Resources/doc +else + cd $TARGET/symfony/swiftmailer-bundle && rm -rf Tests Resources/doc +fi + +if [ -d $TARGET/symfony/monolog-bundle/Symfony/Bundle/MonologBundle ]; then + cd $TARGET/symfony/monolog-bundle/Symfony/Bundle/MonologBundle && rm -rf Tests Resources/doc +else + cd $TARGET/symfony/monolog-bundle && rm -rf Tests Resources/doc +fi + +# Twig +cd $TARGET/twig/twig && rm -rf AUTHORS CHANGELOG README.markdown bin doc package.xml.tpl phpunit.xml* test + +# cleanup +find $TARGET -name .git | xargs rm -rf - +find $TARGET -name .gitignore | xargs rm -rf - +find $TARGET -name .gitmodules | xargs rm -rf - +find $TARGET -name .svn | xargs rm -rf - + +# With vendors +cd /tmp +tar zcpf $DIR/Symfony_Standard_Vendors_$VERSION.tgz Symfony +rm -f $DIR/Symfony_Standard_Vendors_$VERSION.zip +zip -rq $DIR/Symfony_Standard_Vendors_$VERSION.zip Symfony + +# Without vendors +cd /tmp +rm -rf Symfony/vendor +tar zcpf $DIR/Symfony_Standard_$VERSION.tgz Symfony +rm -f $DIR/Symfony_Standard_$VERSION.zip +zip -rq $DIR/Symfony_Standard_$VERSION.zip Symfony diff --git a/vendor/sensio/distribution-bundle/Resources/bin/build_bootstrap.php b/vendor/sensio/distribution-bundle/Resources/bin/build_bootstrap.php new file mode 100755 index 0000000..8adf9b4 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/bin/build_bootstrap.php @@ -0,0 +1,59 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Sensio\Bundle\DistributionBundle\Composer\ScriptHandler; + +if (PHP_SAPI !== 'cli') { + echo 'Warning: '.__FILE__.' should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL; +} + +function getRealpath($path, $message = 'Directory %s does not seem to be valid.') +{ + if (!$path = realpath($path)) { + exit(sprintf($message, $path)); + } + + return $path; +} + +$argv = $_SERVER['argv']; +$autoloadDir = $bootstrapDir = null; +$useNewDirectoryStructure = false; + +// allow the base path to be passed as the first argument, or default +if (!empty($argv[1])) { + $bootstrapDir = getRealpath($argv[1]); +} + +if (!empty($argv[2])) { + $autoloadDir = getRealpath($argv[2]); +} + +if (!empty($argv[3])) { + $useNewDirectoryStructure = true; +} + +$rootDir = __DIR__.'/../../../../..'; +if (null === $autoloadDir) { + $autoloadDir = getRealpath($rootDir.'/app', 'Looks like you don\'t have a standard layout.'); +} +if (null === $bootstrapDir) { + $bootstrapDir = $autoloadDir; + if ($useNewDirectoryStructure) { + $bootstrapDir = getRealpath($rootDir.'/var'); + } +} + +require_once $autoloadDir.'/autoload.php'; + +// here we pass realpaths as resolution between absolute and relative path can be wrong +ScriptHandler::doBuildBootstrap($bootstrapDir); diff --git a/vendor/sensio/distribution-bundle/Resources/bin/build_demo.sh b/vendor/sensio/distribution-bundle/Resources/bin/build_demo.sh new file mode 100755 index 0000000..60cfc31 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/bin/build_demo.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# This file is part of the Symfony Standard Edition. +# +# (c) Fabien Potencier +# +# For the full copyright and license information, please view the LICENSE +# file that was distributed with this source code. + +if [ ! $1 ]; then + echo "\033[37;41mYou must pass the build dir as an absolute path\033[0m" + exit 1 +fi + +DIR=$1 +CURRENT=`php -r "echo realpath(dirname(\\$_SERVER['argv'][0]));"` + +if [[ ! "$DIR" = /* ]]; then + DIR="$CURRENT/$DIR" +fi + +if [ ! -d $DIR ]; then + echo "\033[37;41mThe build dir does not exist\033[0m" + exit 1 +fi + +# avoid the creation of ._* files +export COPY_EXTENDED_ATTRIBUTES_DISABLE=true +export COPYFILE_DISABLE=true + +# Prepare temp. dir +rm -rf /tmp/Symfony +mkdir /tmp/Symfony + +# Clone demo application and install its dependencies +git clone https://github.com/symfony/symfony-demo /tmp/Symfony +cd /tmp/Symfony +composer install --prefer-dist --no-interaction --ignore-platform-reqs --no-plugins --optimize-autoloader + +# cleanup +cd /tmp/Symfony +rm -f UPGRADE* +mv .gitignore keep.gitignore +rm -rf app/cache/* app/logs/* .git* +mv keep.gitignore .gitignore +chmod 777 app/cache app/logs +find . -name .DS_Store | xargs rm -rf - + +# remove unneded dependencies files +cd /tmp/Symfony +TARGET=/tmp/Symfony/vendor + +# Doctrine +cd $TARGET/doctrine/orm && rm -rf UPGRADE* build* bin tests tools lib/vendor +cd $TARGET/doctrine/dbal && rm -rf bin build* tests lib/vendor +cd $TARGET/doctrine/common && rm -rf build* tests lib/vendor +if [ -d $TARGET/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle ]; then + cd $TARGET/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle && rm -rf Tests Resources/doc +else + cd $TARGET/doctrine/doctrine-bundle && rm -rf Tests Resources/doc +fi + +# kriswallsmith +cd $TARGET/kriswallsmith/assetic && rm -rf CHANGELOG* phpunit.xml* tests docs + +# Monolog +cd $TARGET/monolog/monolog && rm -rf README.markdown phpunit.xml* tests + +# Sensio +cd $TARGET/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +cd $TARGET/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc +cd $TARGET/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle && rm -rf phpunit.xml* Tests CHANGELOG* Resources/doc + +# Swiftmailer +cd $TARGET/swiftmailer/swiftmailer && rm -rf CHANGES README* build* docs notes test-suite tests create_pear_package.php package* + +# Symfony +cd $TARGET/symfony/symfony && rm -rf README.md phpunit.xml* tests *.sh vendor + +if [ -d $TARGET/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle ]; then + cd $TARGET/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle && rm -rf Tests Resources/doc +else + cd $TARGET/symfony/assetic-bundle && rm -rf Tests Resources/doc +fi + +if [ -d $TARGET/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle ]; then + cd $TARGET/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle && rm -rf Tests Resources/doc +else + cd $TARGET/symfony/swiftmailer-bundle && rm -rf Tests Resources/doc +fi + +if [ -d $TARGET/symfony/monolog-bundle/Symfony/Bundle/MonologBundle ]; then + cd $TARGET/symfony/monolog-bundle/Symfony/Bundle/MonologBundle && rm -rf Tests Resources/doc +else + cd $TARGET/symfony/monolog-bundle && rm -rf Tests Resources/doc +fi + +# Twig +cd $TARGET/twig/twig && rm -rf AUTHORS CHANGELOG README.markdown bin doc package.xml.tpl phpunit.xml* test +cd $TARGET/twig/extensions && rm -rf README doc phpunit.xml* test + +# final cleanup +find $TARGET -name .git | xargs rm -rf - +find $TARGET -name .gitignore | xargs rm -rf - +find $TARGET -name .gitmodules | xargs rm -rf - +find $TARGET -name .svn | xargs rm -rf - + +# build ZIP and TGZ packages +cd /tmp +tar zcpf $DIR/Symfony_Demo.tgz Symfony +rm -f $DIR/Symfony_Demo.zip +zip -rq $DIR/Symfony_Demo.zip Symfony diff --git a/vendor/sensio/distribution-bundle/Resources/config/security.xml b/vendor/sensio/distribution-bundle/Resources/config/security.xml new file mode 100644 index 0000000..933e253 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/config/security.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/vendor/sensio/distribution-bundle/Resources/meta/LICENSE b/vendor/sensio/distribution-bundle/Resources/meta/LICENSE new file mode 100644 index 0000000..ad32bc5 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010,2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/sensio/distribution-bundle/Resources/skeleton/app/SymfonyRequirements.php b/vendor/sensio/distribution-bundle/Resources/skeleton/app/SymfonyRequirements.php new file mode 100644 index 0000000..28b0dcd --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/skeleton/app/SymfonyRequirements.php @@ -0,0 +1,764 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Users of PHP 5.2 should be able to run the requirements checks. + * This is why the file and all classes must be compatible with PHP 5.2+ + * (e.g. not using namespaces and closures). + * + * ************** CAUTION ************** + * + * DO NOT EDIT THIS FILE as it will be overridden by Composer as part of + * the installation/update process. The original file resides in the + * SensioDistributionBundle. + * + * ************** CAUTION ************** + */ + +/** + * Represents a single PHP requirement, e.g. an installed extension. + * It can be a mandatory requirement or an optional recommendation. + * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration. + * + * @author Tobias Schultze + */ +class Requirement +{ + private $fulfilled; + private $testMessage; + private $helpText; + private $helpHtml; + private $optional; + + /** + * Constructor that initializes the requirement. + * + * @param bool $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) + { + $this->fulfilled = (bool) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (bool) $optional; + } + + /** + * Returns whether the requirement is fulfilled. + * + * @return bool true if fulfilled, otherwise false + */ + public function isFulfilled() + { + return $this->fulfilled; + } + + /** + * Returns the message for testing the requirement. + * + * @return string The test message + */ + public function getTestMessage() + { + return $this->testMessage; + } + + /** + * Returns the help text for resolving the problem. + * + * @return string The help text + */ + public function getHelpText() + { + return $this->helpText; + } + + /** + * Returns the help text formatted in HTML. + * + * @return string The HTML help + */ + public function getHelpHtml() + { + return $this->helpHtml; + } + + /** + * Returns whether this is only an optional recommendation and not a mandatory requirement. + * + * @return bool true if optional, false if mandatory + */ + public function isOptional() + { + return $this->optional; + } +} + +/** + * Represents a PHP requirement in form of a php.ini configuration. + * + * @author Tobias Schultze + */ +class PhpIniRequirement extends Requirement +{ + /** + * Constructor that initializes the requirement. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) + { + $cfgValue = ini_get($cfgName); + + if (is_callable($evaluation)) { + if (null === $testMessage || null === $helpHtml) { + throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.'); + } + + $fulfilled = call_user_func($evaluation, $cfgValue); + } else { + if (null === $testMessage) { + $testMessage = sprintf('%s %s be %s in php.ini', + $cfgName, + $optional ? 'should' : 'must', + $evaluation ? 'enabled' : 'disabled' + ); + } + + if (null === $helpHtml) { + $helpHtml = sprintf('Set %s to %s in php.ini*.', + $cfgName, + $evaluation ? 'on' : 'off' + ); + } + + $fulfilled = $evaluation == $cfgValue; + } + + parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional); + } +} + +/** + * A RequirementCollection represents a set of Requirement instances. + * + * @author Tobias Schultze + */ +class RequirementCollection implements IteratorAggregate +{ + private $requirements = array(); + + /** + * Gets the current RequirementCollection as an Iterator. + * + * @return Traversable A Traversable interface + */ + public function getIterator() + { + return new ArrayIterator($this->requirements); + } + + /** + * Adds a Requirement. + * + * @param Requirement $requirement A Requirement instance + */ + public function add(Requirement $requirement) + { + $this->requirements[] = $requirement; + } + + /** + * Adds a mandatory requirement. + * + * @param bool $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation. + * + * @param bool $fulfilled Whether the recommendation is fulfilled + * @param string $testMessage The message for testing the recommendation + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a mandatory requirement in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a requirement collection to the current set of requirements. + * + * @param RequirementCollection $collection A RequirementCollection instance + */ + public function addCollection(RequirementCollection $collection) + { + $this->requirements = array_merge($this->requirements, $collection->all()); + } + + /** + * Returns both requirements and recommendations. + * + * @return array Array of Requirement instances + */ + public function all() + { + return $this->requirements; + } + + /** + * Returns all mandatory requirements. + * + * @return array Array of Requirement instances + */ + public function getRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the mandatory requirements that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && !$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns all optional recommendations. + * + * @return array Array of Requirement instances + */ + public function getRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if ($req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the recommendations that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns whether a php.ini configuration is not correct. + * + * @return bool php.ini configuration problem? + */ + public function hasPhpIniConfigIssue() + { + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) { + return true; + } + } + + return false; + } + + /** + * Returns the PHP configuration file (php.ini) path. + * + * @return string|false php.ini file path + */ + public function getPhpIniConfigPath() + { + return get_cfg_var('cfg_file_path'); + } +} + +/** + * This class specifies all requirements and optional recommendations that + * are necessary to run the Symfony Standard Edition. + * + * @author Tobias Schultze + * @author Fabien Potencier + */ +class SymfonyRequirements extends RequirementCollection +{ + const REQUIRED_PHP_VERSION = '5.3.3'; + + /** + * Constructor that initializes the requirements. + */ + public function __construct() + { + /* mandatory requirements follow */ + + $installedPhpVersion = phpversion(); + + $this->addRequirement( + version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='), + sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion), + sprintf('You are running PHP version "%s", but Symfony needs at least PHP "%s" to run. + Before using Symfony, upgrade your PHP installation, preferably to the latest version.', + $installedPhpVersion, self::REQUIRED_PHP_VERSION), + sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion) + ); + + $this->addRequirement( + version_compare($installedPhpVersion, '5.3.16', '!='), + 'PHP version must not be 5.3.16 as Symfony won\'t work properly with it', + 'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)' + ); + + $this->addRequirement( + is_dir(__DIR__.'/../vendor/composer'), + 'Vendor libraries must be installed', + 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. '. + 'Then run "php composer.phar install" to install them.' + ); + + $cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache'; + + $this->addRequirement( + is_writable($cacheDir), + 'app/cache/ or var/cache/ directory must be writable', + 'Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.' + ); + + $logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs'; + + $this->addRequirement( + is_writable($logsDir), + 'app/logs/ or var/logs/ directory must be writable', + 'Change the permissions of either "app/logs/" or "var/logs/" directory so that the web server can write into it.' + ); + + $this->addPhpIniRequirement( + 'date.timezone', true, false, + 'date.timezone setting must be set', + 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).' + ); + + if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) { + $timezones = array(); + foreach (DateTimeZone::listAbbreviations() as $abbreviations) { + foreach ($abbreviations as $abbreviation) { + $timezones[$abbreviation['timezone_id']] = true; + } + } + + $this->addRequirement( + isset($timezones[@date_default_timezone_get()]), + sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()), + 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.' + ); + } + + $this->addRequirement( + function_exists('iconv'), + 'iconv() must be available', + 'Install and enable the iconv extension.' + ); + + $this->addRequirement( + function_exists('json_encode'), + 'json_encode() must be available', + 'Install and enable the JSON extension.' + ); + + $this->addRequirement( + function_exists('session_start'), + 'session_start() must be available', + 'Install and enable the session extension.' + ); + + $this->addRequirement( + function_exists('ctype_alpha'), + 'ctype_alpha() must be available', + 'Install and enable the ctype extension.' + ); + + $this->addRequirement( + function_exists('token_get_all'), + 'token_get_all() must be available', + 'Install and enable the Tokenizer extension.' + ); + + $this->addRequirement( + function_exists('simplexml_import_dom'), + 'simplexml_import_dom() must be available', + 'Install and enable the SimpleXML extension.' + ); + + if (function_exists('apc_store') && ini_get('apc.enabled')) { + if (version_compare($installedPhpVersion, '5.4.0', '>=')) { + $this->addRequirement( + version_compare(phpversion('apc'), '3.1.13', '>='), + 'APC version must be at least 3.1.13 when using PHP 5.4', + 'Upgrade your APC extension (3.1.13+).' + ); + } else { + $this->addRequirement( + version_compare(phpversion('apc'), '3.0.17', '>='), + 'APC version must be at least 3.0.17', + 'Upgrade your APC extension (3.0.17+).' + ); + } + } + + $this->addPhpIniRequirement('detect_unicode', false); + + if (extension_loaded('suhosin')) { + $this->addPhpIniRequirement( + 'suhosin.executor.include.whitelist', + create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'), + false, + 'suhosin.executor.include.whitelist must be configured correctly in php.ini', + 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.' + ); + } + + if (extension_loaded('xdebug')) { + $this->addPhpIniRequirement( + 'xdebug.show_exception_trace', false, true + ); + + $this->addPhpIniRequirement( + 'xdebug.scream', false, true + ); + + $this->addPhpIniRecommendation( + 'xdebug.max_nesting_level', + create_function('$cfgValue', 'return $cfgValue > 100;'), + true, + 'xdebug.max_nesting_level should be above 100 in php.ini', + 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.' + ); + } + + $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; + + $this->addRequirement( + null !== $pcreVersion, + 'PCRE extension must be available', + 'Install the PCRE extension (version 8.0+).' + ); + + if (extension_loaded('mbstring')) { + $this->addPhpIniRequirement( + 'mbstring.func_overload', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'string functions should not be overloaded', + 'Set "mbstring.func_overload" to 0 in php.ini* to disable function overloading by the mbstring extension.' + ); + } + + /* optional recommendations follow */ + + if (file_exists(__DIR__.'/../vendor/composer')) { + require_once __DIR__.'/../vendor/autoload.php'; + + try { + $r = new ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle'); + + $contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php'); + } catch (ReflectionException $e) { + $contents = ''; + } + $this->addRecommendation( + file_get_contents(__FILE__) === $contents, + 'Requirements file should be up-to-date', + 'Your requirements file is outdated. Run composer install and re-check your configuration.' + ); + } + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.4', '>='), + 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions', + 'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.8', '>='), + 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156', + 'Install PHP 5.3.8 or newer if your project uses annotations.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.4.0', '!='), + 'You should not use PHP 5.4.0 due to the PHP bug #61453', + 'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.4.11', '>='), + 'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)', + 'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.' + ); + + $this->addRecommendation( + (version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<')) + || + version_compare($installedPhpVersion, '5.4.8', '>='), + 'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909', + 'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.' + ); + + if (null !== $pcreVersion) { + $this->addRecommendation( + $pcreVersion >= 8.0, + sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion), + 'PCRE 8.0+ is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.' + ); + } + + $this->addRecommendation( + class_exists('DomDocument'), + 'PHP-DOM and PHP-XML modules should be installed', + 'Install and enable the PHP-DOM and the PHP-XML modules.' + ); + + $this->addRecommendation( + function_exists('mb_strlen'), + 'mb_strlen() should be available', + 'Install and enable the mbstring extension.' + ); + + $this->addRecommendation( + function_exists('iconv'), + 'iconv() should be available', + 'Install and enable the iconv extension.' + ); + + $this->addRecommendation( + function_exists('utf8_decode'), + 'utf8_decode() should be available', + 'Install and enable the XML extension.' + ); + + $this->addRecommendation( + function_exists('filter_var'), + 'filter_var() should be available', + 'Install and enable the filter extension.' + ); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->addRecommendation( + function_exists('posix_isatty'), + 'posix_isatty() should be available', + 'Install and enable the php_posix extension (used to colorize the CLI output).' + ); + } + + $this->addRecommendation( + extension_loaded('intl'), + 'intl extension should be available', + 'Install and enable the intl extension (used for validators).' + ); + + if (extension_loaded('intl')) { + // in some WAMP server installations, new Collator() returns null + $this->addRecommendation( + null !== new Collator('fr_FR'), + 'intl extension should be correctly configured', + 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' + ); + + // check for compatible ICU versions (only done when you have the intl extension) + if (defined('INTL_ICU_VERSION')) { + $version = INTL_ICU_VERSION; + } else { + $reflector = new ReflectionExtension('intl'); + + ob_start(); + $reflector->info(); + $output = strip_tags(ob_get_clean()); + + preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); + $version = $matches[1]; + } + + $this->addRecommendation( + version_compare($version, '4.0', '>='), + 'intl ICU version should be at least 4+', + 'Upgrade your intl extension with a newer ICU version (4+).' + ); + + $this->addPhpIniRecommendation( + 'intl.error_level', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'intl.error_level should be 0 in php.ini', + 'Set "intl.error_level" to "0" in php.ini* to inhibit the messages when an error occurs in ICU functions.' + ); + } + + $accelerator = + (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) + || + (extension_loaded('apc') && ini_get('apc.enabled')) + || + (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) + || + (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) + || + (extension_loaded('xcache') && ini_get('xcache.cacher')) + || + (extension_loaded('wincache') && ini_get('wincache.ocenabled')) + ; + + $this->addRecommendation( + $accelerator, + 'a PHP accelerator should be installed', + 'Install and/or enable a PHP accelerator (highly recommended).' + ); + + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $this->addRecommendation( + $this->getRealpathCacheSize() > 1000, + 'realpath_cache_size should be above 1024 in php.ini', + 'Set "realpath_cache_size" to e.g. "1024" in php.ini* to improve performance on windows.' + ); + } + + $this->addPhpIniRecommendation('short_open_tag', false); + + $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); + + $this->addPhpIniRecommendation('register_globals', false, true); + + $this->addPhpIniRecommendation('session.auto_start', false); + + $this->addRecommendation( + class_exists('PDO'), + 'PDO should be installed', + 'Install PDO (mandatory for Doctrine).' + ); + + if (class_exists('PDO')) { + $drivers = PDO::getAvailableDrivers(); + $this->addRecommendation( + count($drivers) > 0, + sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), + 'Install PDO drivers (mandatory for Doctrine).' + ); + } + } + + /** + * Loads realpath_cache_size from php.ini and converts it to int. + * + * (e.g. 16k is converted to 16384 int) + * + * @return int + */ + protected function getRealpathCacheSize() + { + $size = ini_get('realpath_cache_size'); + $size = trim($size); + $unit = strtolower(substr($size, -1, 1)); + switch ($unit) { + case 'g': + return $size * 1024 * 1024 * 1024; + case 'm': + return $size * 1024 * 1024; + case 'k': + return $size * 1024; + default: + return (int) $size; + } + } +} diff --git a/vendor/sensio/distribution-bundle/Resources/skeleton/app/check.php b/vendor/sensio/distribution-bundle/Resources/skeleton/app/check.php new file mode 100644 index 0000000..282507f --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/skeleton/app/check.php @@ -0,0 +1,142 @@ +getPhpIniConfigPath(); + +echo_title('Symfony2 Requirements Checker'); + +echo '> PHP is using the following php.ini file:'.PHP_EOL; +if ($iniPath) { + echo_style('green', ' '.$iniPath); +} else { + echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!'); +} + +echo PHP_EOL.PHP_EOL; + +echo '> Checking Symfony requirements:'.PHP_EOL.' '; + +$messages = array(); +foreach ($symfonyRequirements->getRequirements() as $req) { + /** @var $req Requirement */ + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('red', 'E'); + $messages['error'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +$checkPassed = empty($messages['error']); + +foreach ($symfonyRequirements->getRecommendations() as $req) { + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('yellow', 'W'); + $messages['warning'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +if ($checkPassed) { + echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects'); +} else { + echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects'); + + echo_title('Fix the following mandatory requirements', 'red'); + + foreach ($messages['error'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +if (!empty($messages['warning'])) { + echo_title('Optional recommendations to improve your setup', 'yellow'); + + foreach ($messages['warning'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +echo PHP_EOL; +echo_style('title', 'Note'); +echo ' The command console could use a different php.ini file'.PHP_EOL; +echo_style('title', '~~~~'); +echo ' than the one used with your web server. To be on the'.PHP_EOL; +echo ' safe side, please check the requirements from your web'.PHP_EOL; +echo ' server using the '; +echo_style('yellow', 'web/config.php'); +echo ' script.'.PHP_EOL; +echo PHP_EOL; + +exit($checkPassed ? 0 : 1); + +function get_error_message(Requirement $requirement, $lineSize) +{ + if ($requirement->isFulfilled()) { + return; + } + + $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; + $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; + + return $errorMessage; +} + +function echo_title($title, $style = null) +{ + $style = $style ?: 'title'; + + echo PHP_EOL; + echo_style($style, $title.PHP_EOL); + echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); + echo PHP_EOL; +} + +function echo_style($style, $message) +{ + // ANSI color codes + $styles = array( + 'reset' => "\033[0m", + 'red' => "\033[31m", + 'green' => "\033[32m", + 'yellow' => "\033[33m", + 'error' => "\033[37;41m", + 'success' => "\033[37;42m", + 'title' => "\033[34m", + ); + $supports = has_color_support(); + + echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); +} + +function echo_block($style, $title, $message) +{ + $message = ' '.trim($message).' '; + $width = strlen($message); + + echo PHP_EOL.PHP_EOL; + + echo_style($style, str_repeat(' ', $width).PHP_EOL); + echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_repeat(' ', $width).PHP_EOL); +} + +function has_color_support() +{ + static $support; + + if (null === $support) { + if (DIRECTORY_SEPARATOR == '\\') { + $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } else { + $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); + } + } + + return $support; +} diff --git a/vendor/sensio/distribution-bundle/Resources/skeleton/web/config.php b/vendor/sensio/distribution-bundle/Resources/skeleton/web/config.php new file mode 100644 index 0000000..2e15309 --- /dev/null +++ b/vendor/sensio/distribution-bundle/Resources/skeleton/web/config.php @@ -0,0 +1,205 @@ +getFailedRequirements(); +$minorProblems = $symfonyRequirements->getFailedRecommendations(); + +?> + + + + + + Symfony Configuration Checker + + + + + +
    +
    + + + +
    + +
    +
    +
    +

    Configuration Checker

    +

    + This script analyzes your system to check whether is + ready to run Symfony applications. +

    + + +

    Major problems

    +

    Major problems have been detected and must be fixed before continuing:

    +
      + +
    1. getHelpHtml() ?>
    2. + +
    + + + +

    Recommendations

    +

    + Additionally, toTo enhance your Symfony experience, + it’s recommended that you fix the following: +

    +
      + +
    1. getHelpHtml() ?>
    2. + +
    + + + hasPhpIniConfigIssue()): ?> +

    * + getPhpIniConfigPath()): ?> + Changes to the php.ini file must be done in "getPhpIniConfigPath() ?>". + + To change settings, create a "php.ini". + +

    + + + +

    All checks passed successfully. Your system is ready to run Symfony applications.

    + + + +
    +
    +
    +
    Symfony Standard Edition
    +
    + + diff --git a/vendor/sensio/distribution-bundle/SensioDistributionBundle.php b/vendor/sensio/distribution-bundle/SensioDistributionBundle.php new file mode 100644 index 0000000..92abd88 --- /dev/null +++ b/vendor/sensio/distribution-bundle/SensioDistributionBundle.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\DistributionBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * SensioDistributionBundle. + * + * @author Fabien Potencier + * @author Marc Weistroff + * @author Jérôme Vieilledent + */ +class SensioDistributionBundle extends Bundle +{ +} diff --git a/vendor/sensio/distribution-bundle/composer.json b/vendor/sensio/distribution-bundle/composer.json new file mode 100644 index 0000000..6fc0805 --- /dev/null +++ b/vendor/sensio/distribution-bundle/composer.json @@ -0,0 +1,32 @@ +{ + "name": "sensio/distribution-bundle", + "description": "Base bundle for Symfony Distributions", + "keywords": ["distribution","configuration"], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.9", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/config": "~2.3|~3.0", + "symfony/filesystem": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/class-loader": "~2.3|~3.0", + "symfony/process": "~2.3|~3.0", + "sensiolabs/security-checker": "~3.0" + }, + "autoload": { + "psr-4": { "Sensio\\Bundle\\DistributionBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/sensio/framework-extra-bundle/.travis.yml b/vendor/sensio/framework-extra-bundle/.travis.yml new file mode 100644 index 0000000..ea3bced --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/.travis.yml @@ -0,0 +1,33 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: + include: + - php: 5.3 + env: deps=low + fast_finish: true + +env: + global: + - deps=no + +before_install: + - travis_retry composer self-update + +install: + - if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]] && [[ "$TRAVIS_PHP_VERSION" != "5.4" ]]; then composer require --no-update symfony/psr-http-message-bridge zendframework/zend-diactoros; fi; + - if [ "$deps" = "no" ]; then composer --prefer-source install; fi; + - if [ "$deps" = "low" ]; then composer --prefer-source --prefer-lowest --prefer-stable update; fi; diff --git a/vendor/sensio/framework-extra-bundle/Configuration/Cache.php b/vendor/sensio/framework-extra-bundle/Configuration/Cache.php new file mode 100644 index 0000000..db1100c --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/Cache.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +/** + * The Cache class handles the Cache annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class Cache extends ConfigurationAnnotation +{ + /** + * The expiration date as a valid date for the strtotime() function. + * + * @var string + */ + protected $expires; + + /** + * The number of seconds that the response is considered fresh by a private + * cache like a web browser. + * + * @var int + */ + protected $maxage; + + /** + * The number of seconds that the response is considered fresh by a public + * cache like a reverse proxy cache. + * + * @var int + */ + protected $smaxage; + + /** + * Whether the response is public or not. + * + * @var bool + */ + protected $public; + + /** + * Additional "Vary:"-headers. + * + * @var array + */ + protected $vary; + + /** + * An expression to compute the Last-Modified HTTP header. + * + * @var string + */ + protected $lastModified; + + /** + * An expression to compute the ETag HTTP header. + * + * @var string + */ + protected $etag; + + /** + * Returns the expiration date for the Expires header field. + * + * @return string + */ + public function getExpires() + { + return $this->expires; + } + + /** + * Sets the expiration date for the Expires header field. + * + * @param string $expires A valid php date + */ + public function setExpires($expires) + { + $this->expires = $expires; + } + + /** + * Sets the number of seconds for the max-age cache-control header field. + * + * @param int $maxage A number of seconds + */ + public function setMaxAge($maxage) + { + $this->maxage = $maxage; + } + + /** + * Returns the number of seconds the response is considered fresh by a + * private cache. + * + * @return int + */ + public function getMaxAge() + { + return $this->maxage; + } + + /** + * Sets the number of seconds for the s-maxage cache-control header field. + * + * @param int $smaxage A number of seconds + */ + public function setSMaxAge($smaxage) + { + $this->smaxage = $smaxage; + } + + /** + * Returns the number of seconds the response is considered fresh by a + * public cache. + * + * @return int + */ + public function getSMaxAge() + { + return $this->smaxage; + } + + /** + * Returns whether or not a response is public. + * + * @return bool + */ + public function isPublic() + { + return $this->public === true; + } + + /** + * Returns whether or not a response is private. + * + * @return bool + */ + public function isPrivate() + { + return $this->public === false; + } + + /** + * Sets a response public. + * + * @param bool $public A boolean value + */ + public function setPublic($public) + { + $this->public = (bool) $public; + } + + /** + * Returns the custom "Vary"-headers. + * + * @return array + */ + public function getVary() + { + return $this->vary; + } + + /** + * Add additional "Vary:"-headers. + * + * @param array $vary + */ + public function setVary($vary) + { + $this->vary = $vary; + } + + /** + * Sets the "Last-Modified"-header expression. + * + * @param string $expression + */ + public function setLastModified($expression) + { + $this->lastModified = $expression; + } + + /** + * Returns the "Last-Modified"-header expression. + * + * @return string + */ + public function getLastModified() + { + return $this->lastModified; + } + + /** + * Sets the "ETag"-header expression. + * + * @param string $expression + */ + public function setETag($expression) + { + $this->etag = $expression; + } + + /** + * Returns the "ETag"-header expression. + * + * @return string + */ + public function getETag() + { + return $this->etag; + } + + /** + * Returns the annotation alias name. + * + * @return string + * + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'cache'; + } + + /** + * Only one cache directive is allowed. + * + * @return bool + * + * @see ConfigurationInterface + */ + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/ConfigurationAnnotation.php b/vendor/sensio/framework-extra-bundle/Configuration/ConfigurationAnnotation.php new file mode 100644 index 0000000..9c7eb27 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/ConfigurationAnnotation.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +/** + * Base configuration annotation. + * + * @author Johannes M. Schmitt + */ +abstract class ConfigurationAnnotation implements ConfigurationInterface +{ + public function __construct(array $values) + { + foreach ($values as $k => $v) { + if (!method_exists($this, $name = 'set'.$k)) { + throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this))); + } + + $this->$name($v); + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/ConfigurationInterface.php b/vendor/sensio/framework-extra-bundle/Configuration/ConfigurationInterface.php new file mode 100644 index 0000000..98dbbb8 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/ConfigurationInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +/** + * ConfigurationInterface. + * + * @author Fabien Potencier + */ +interface ConfigurationInterface +{ + /** + * Returns the alias name for an annotated configuration. + * + * @return string + */ + public function getAliasName(); + + /** + * Returns whether multiple annotations of this type are allowed. + * + * @return bool + */ + public function allowArray(); +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/Method.php b/vendor/sensio/framework-extra-bundle/Configuration/Method.php new file mode 100644 index 0000000..1232a5b --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/Method.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +/** + * The Method class handles the Method annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class Method extends ConfigurationAnnotation +{ + /** + * An array of restricted HTTP methods. + * + * @var array + */ + protected $methods = array(); + + /** + * Returns the array of HTTP methods. + * + * @return array + */ + public function getMethods() + { + return $this->methods; + } + + /** + * Sets the HTTP methods. + * + * @param array|string $methods An HTTP method or an array of HTTP methods + */ + public function setMethods($methods) + { + $this->methods = is_array($methods) ? $methods : array($methods); + } + + /** + * Sets the HTTP methods. + * + * @param array|string $methods An HTTP method or an array of HTTP methods + */ + public function setValue($methods) + { + $this->setMethods($methods); + } + + /** + * Returns the annotation alias name. + * + * @return string + * + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'method'; + } + + /** + * Only one method directive is allowed. + * + * @return bool + * + * @see ConfigurationInterface + */ + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/ParamConverter.php b/vendor/sensio/framework-extra-bundle/Configuration/ParamConverter.php new file mode 100644 index 0000000..c74d868 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/ParamConverter.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +/** + * The ParamConverter class handles the ParamConverter annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class ParamConverter extends ConfigurationAnnotation +{ + /** + * The parameter name. + * + * @var string + */ + protected $name; + + /** + * The parameter class. + * + * @var string + */ + protected $class; + + /** + * An array of options. + * + * @var array + */ + protected $options = array(); + + /** + * Whether or not the parameter is optional. + * + * @var bool + */ + protected $optional = false; + + /** + * Use explicitly named converter instead of iterating by priorities. + * + * @var string + */ + protected $converter; + + /** + * Returns the parameter name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the parameter name. + * + * @param string $name The parameter name + */ + public function setValue($name) + { + $this->setName($name); + } + + /** + * Sets the parameter name. + * + * @param string $name The parameter name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Returns the parameter class name. + * + * @return string $name + */ + public function getClass() + { + return $this->class; + } + + /** + * Sets the parameter class name. + * + * @param string $class The parameter class name + */ + public function setClass($class) + { + $this->class = $class; + } + + /** + * Returns an array of options. + * + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sets an array of options. + * + * @param array $options An array of options + */ + public function setOptions($options) + { + $this->options = $options; + } + + /** + * Sets whether or not the parameter is optional. + * + * @param bool $optional Wether the parameter is optional + */ + public function setIsOptional($optional) + { + $this->optional = (bool) $optional; + } + + /** + * Returns whether or not the parameter is optional. + * + * @return bool + */ + public function isOptional() + { + return $this->optional; + } + + /** + * Get explicit converter name. + * + * @return string + */ + public function getConverter() + { + return $this->converter; + } + + /** + * Set explicit converter name. + * + * @param string $converter + */ + public function setConverter($converter) + { + $this->converter = $converter; + } + + /** + * Returns the annotation alias name. + * + * @return string + * + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'converters'; + } + + /** + * Multiple ParamConverters are allowed. + * + * @return bool + * + * @see ConfigurationInterface + */ + public function allowArray() + { + return true; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/Route.php b/vendor/sensio/framework-extra-bundle/Configuration/Route.php new file mode 100644 index 0000000..1f8e123 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/Route.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +use Symfony\Component\Routing\Annotation\Route as BaseRoute; + +/** + * @author Kris Wallsmith + * @Annotation + */ +class Route extends BaseRoute +{ + protected $service; + + public function setService($service) + { + // avoid a BC notice in case of @Route(service="") with sf ^2.7 + if (null === $this->getPath()) { + $this->setPath(''); + } + $this->service = $service; + } + + public function getService() + { + return $this->service; + } + + /** + * Multiple route annotations are allowed. + * + * @return bool + * + * @see ConfigurationInterface + */ + public function allowArray() + { + return true; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/Security.php b/vendor/sensio/framework-extra-bundle/Configuration/Security.php new file mode 100644 index 0000000..692c28e --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/Security.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +/** + * The Security class handles the Security annotation. + * + * @author Fabien Potencier + * @Annotation + */ +class Security extends ConfigurationAnnotation +{ + protected $expression; + + public function getExpression() + { + return $this->expression; + } + + public function setExpression($expression) + { + $this->expression = $expression; + } + + public function setValue($expression) + { + $this->setExpression($expression); + } + + public function getAliasName() + { + return 'security'; + } + + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Configuration/Template.php b/vendor/sensio/framework-extra-bundle/Configuration/Template.php new file mode 100644 index 0000000..1d6fc35 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Configuration/Template.php @@ -0,0 +1,161 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Configuration; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; + +/** + * The Template class handles the Template annotation parts. + * + * @author Fabien Potencier + * @Annotation + */ +class Template extends ConfigurationAnnotation +{ + /** + * The template reference. + * + * @var TemplateReference + */ + protected $template; + + /** + * The template engine used when a specific template isn't specified. + * + * @var string + */ + protected $engine = 'twig'; + + /** + * The associative array of template variables. + * + * @var array + */ + protected $vars = array(); + + /** + * Should the template be streamed? + * + * @var bool + */ + protected $streamable = false; + + /** + * Returns the array of templates variables. + * + * @return array + */ + public function getVars() + { + return $this->vars; + } + + /** + * @param bool $streamable + */ + public function setIsStreamable($streamable) + { + $this->streamable = $streamable; + } + + /** + * @return bool + */ + public function isStreamable() + { + return (bool) $this->streamable; + } + + /** + * Sets the template variables. + * + * @param array $vars The template variables + */ + public function setVars($vars) + { + $this->vars = $vars; + } + + /** + * Returns the engine used when guessing template names. + * + * @return string + */ + public function getEngine() + { + return $this->engine; + } + + /** + * Sets the engine used when guessing template names. + * + * @param string + */ + public function setEngine($engine) + { + $this->engine = $engine; + } + + /** + * Sets the template logic name. + * + * @param string $template The template logic name + */ + public function setValue($template) + { + $this->setTemplate($template); + } + + /** + * Returns the template reference. + * + * @return TemplateReference + */ + public function getTemplate() + { + return $this->template; + } + + /** + * Sets the template reference. + * + * @param TemplateReference|string $template The template reference + */ + public function setTemplate($template) + { + $this->template = $template; + } + + /** + * Returns the annotation alias name. + * + * @return string + * + * @see ConfigurationInterface + */ + public function getAliasName() + { + return 'template'; + } + + /** + * Only one template directive is allowed. + * + * @return bool + * + * @see ConfigurationInterface + */ + public function allowArray() + { + return false; + } +} diff --git a/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/AddParamConverterPass.php b/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/AddParamConverterPass.php new file mode 100644 index 0000000..3a5d430 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/AddParamConverterPass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged request.param_converter services to converter.manager service. + * + * @author Fabien Potencier + */ +class AddParamConverterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('sensio_framework_extra.converter.manager')) { + return; + } + + $definition = $container->getDefinition('sensio_framework_extra.converter.manager'); + + foreach ($container->findTaggedServiceIds('request.param_converter') as $id => $converters) { + foreach ($converters as $converter) { + $name = isset($converter['converter']) ? $converter['converter'] : null; + $priority = isset($converter['priority']) ? $converter['priority'] : 0; + + if ($priority === 'false') { + $priority = null; + } + + $definition->addMethodCall('add', array(new Reference($id), $priority, $name)); + } + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/LegacyPass.php b/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/LegacyPass.php new file mode 100644 index 0000000..c78b912 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/DependencyInjection/Compiler/LegacyPass.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * @author Fabien Potencier + */ +class LegacyPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('sensio_framework_extra.security.listener')) { + return; + } + + $definition = $container->getDefinition('sensio_framework_extra.security.listener'); + + if ($container->hasDefinition('security.token_storage')) { + $definition->replaceArgument(0, null); + } + } +} diff --git a/vendor/sensio/framework-extra-bundle/DependencyInjection/Configuration.php b/vendor/sensio/framework-extra-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..436ffbc --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,80 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * FrameworkExtraBundle configuration structure. + * + * @author Henrik Bjornskov + */ +class Configuration implements ConfigurationInterface +{ + /** + * Generates the configuration tree. + * + * @return NodeInterface + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('sensio_framework_extra', 'array'); + + $rootNode + ->children() + ->arrayNode('router') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('request') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('converters')->defaultTrue()->end() + ->booleanNode('auto_convert')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('view') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('cache') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->end() + ->end() + ->arrayNode('security') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('annotations')->defaultTrue()->end() + ->scalarNode('expression_language')->defaultValue('sensio_framework_extra.security.expression_language.default')->end() + ->end() + ->end() + ->arrayNode('psr_message') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled')->defaultValue(interface_exists('Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface'))->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/sensio/framework-extra-bundle/DependencyInjection/SensioFrameworkExtraExtension.php b/vendor/sensio/framework-extra-bundle/DependencyInjection/SensioFrameworkExtraExtension.php new file mode 100644 index 0000000..2ab6baa --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/DependencyInjection/SensioFrameworkExtraExtension.php @@ -0,0 +1,125 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +/** + * SensioFrameworkExtraExtension. + * + * @author Fabien Potencier + */ +class SensioFrameworkExtraExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + + $annotationsToLoad = array(); + + if ($config['router']['annotations']) { + $annotationsToLoad[] = 'routing.xml'; + + $this->addClassesToCompile(array( + 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ControllerListener', + )); + } + + if ($config['request']['converters']) { + $annotationsToLoad[] = 'converters.xml'; + + $this->addClassesToCompile(array( + // cannot be added because it has some annotations + //'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\ParamConverter', + 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\ParamConverterListener', + 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DateTimeParamConverter', + 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\DoctrineParamConverter', + 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterInterface', + 'Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterManager', + )); + } + + if ($config['view']['annotations']) { + $annotationsToLoad[] = 'view.xml'; + + $this->addClassesToCompile(array( + 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\TemplateListener', + )); + } + + if ($config['cache']['annotations']) { + $annotationsToLoad[] = 'cache.xml'; + + $this->addClassesToCompile(array( + 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\HttpCacheListener', + )); + } + + if ($config['security']['annotations']) { + $annotationsToLoad[] = 'security.xml'; + + if (class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage') && class_exists('Symfony\Component\Security\Core\Authorization\ExpressionLanguage')) { + $container->setAlias('sensio_framework_extra.security.expression_language', new Alias($config['security']['expression_language'], false)); + } else { + $container->removeDefinition('sensio_framework_extra.security.expression_language.default'); + } + + $this->addClassesToCompile(array( + 'Sensio\\Bundle\\FrameworkExtraBundle\\EventListener\\SecurityListener', + )); + } + + if ($annotationsToLoad) { + // must be first + $loader->load('annotations.xml'); + + foreach ($annotationsToLoad as $configFile) { + $loader->load($configFile); + } + + $this->addClassesToCompile(array( + 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\ConfigurationAnnotation', + )); + + if ($config['request']['converters']) { + $container->getDefinition('sensio_framework_extra.converter.listener')->replaceArgument(1, $config['request']['auto_convert']); + } + } + + if ($config['psr_message']['enabled']) { + $loader->load('psr7.xml'); + } + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/symfony_extra'; + } +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/CacheListener.php b/vendor/sensio/framework-extra-bundle/EventListener/CacheListener.php new file mode 100644 index 0000000..7d64fdb --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/CacheListener.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +/** + * CacheListener handles HTTP cache headers. + * + * It can be configured via the Cache, LastModified, and Etag annotations. + * + * @author Fabien Potencier + * + * @deprecated Deprecated since 3.0, to be removed in 4.0. Use the HttpCacheListener instead. + */ +class CacheListener extends HttpCacheListener +{ +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/ControllerListener.php b/vendor/sensio/framework-extra-bundle/EventListener/ControllerListener.php new file mode 100644 index 0000000..ffd8fcf --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/ControllerListener.php @@ -0,0 +1,113 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +use Doctrine\Common\Annotations\Reader; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface; +use Doctrine\Common\Util\ClassUtils; + +/** + * The ControllerListener class parses annotation blocks located in + * controller classes. + * + * @author Fabien Potencier + */ +class ControllerListener implements EventSubscriberInterface +{ + /** + * @var Reader + */ + protected $reader; + + /** + * Constructor. + * + * @param Reader $reader An Reader instance + */ + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + /** + * Modifies the Request object to apply configuration information found in + * controllers annotations like the template to render or HTTP caching + * configuration. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + if (!is_array($controller = $event->getController())) { + return; + } + + $className = class_exists('Doctrine\Common\Util\ClassUtils') ? ClassUtils::getClass($controller[0]) : get_class($controller[0]); + $object = new \ReflectionClass($className); + $method = $object->getMethod($controller[1]); + + $classConfigurations = $this->getConfigurations($this->reader->getClassAnnotations($object)); + $methodConfigurations = $this->getConfigurations($this->reader->getMethodAnnotations($method)); + + $configurations = array(); + foreach (array_merge(array_keys($classConfigurations), array_keys($methodConfigurations)) as $key) { + if (!array_key_exists($key, $classConfigurations)) { + $configurations[$key] = $methodConfigurations[$key]; + } elseif (!array_key_exists($key, $methodConfigurations)) { + $configurations[$key] = $classConfigurations[$key]; + } else { + if (is_array($classConfigurations[$key])) { + if (!is_array($methodConfigurations[$key])) { + throw new \UnexpectedValueException('Configurations should both be an array or both not be an array'); + } + $configurations[$key] = array_merge($classConfigurations[$key], $methodConfigurations[$key]); + } else { + // method configuration overrides class configuration + $configurations[$key] = $methodConfigurations[$key]; + } + } + } + + $request = $event->getRequest(); + foreach ($configurations as $key => $attributes) { + $request->attributes->set($key, $attributes); + } + } + + protected function getConfigurations(array $annotations) + { + $configurations = array(); + foreach ($annotations as $configuration) { + if ($configuration instanceof ConfigurationInterface) { + if ($configuration->allowArray()) { + $configurations['_'.$configuration->getAliasName()][] = $configuration; + } elseif (!isset($configurations['_'.$configuration->getAliasName()])) { + $configurations['_'.$configuration->getAliasName()] = $configuration; + } else { + throw new \LogicException(sprintf('Multiple "%s" annotations are not allowed.', $configuration->getAliasName())); + } + } + } + + return $configurations; + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => 'onKernelController', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/HttpCacheListener.php b/vendor/sensio/framework-extra-bundle/EventListener/HttpCacheListener.php new file mode 100644 index 0000000..4df3eeb --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/HttpCacheListener.php @@ -0,0 +1,168 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; + +/** + * HttpCacheListener handles HTTP cache headers. + * + * It can be configured via the Cache annotation. + * + * @author Fabien Potencier + */ +class HttpCacheListener implements EventSubscriberInterface +{ + private $lastModifiedDates; + private $etags; + private $expressionLanguage; + + public function __construct() + { + $this->lastModifiedDates = new \SplObjectStorage(); + $this->etags = new \SplObjectStorage(); + } + + /** + * Handles HTTP validation headers. + */ + public function onKernelController(FilterControllerEvent $event) + { + $request = $event->getRequest(); + if (!$configuration = $request->attributes->get('_cache')) { + return; + } + + $response = new Response(); + + $lastModifiedDate = ''; + if ($configuration->getLastModified()) { + $lastModifiedDate = $this->getExpressionLanguage()->evaluate($configuration->getLastModified(), $request->attributes->all()); + $response->setLastModified($lastModifiedDate); + } + + $etag = ''; + if ($configuration->getETag()) { + $etag = hash('sha256', $this->getExpressionLanguage()->evaluate($configuration->getETag(), $request->attributes->all())); + $response->setETag($etag); + } + + if ($response->isNotModified($request)) { + $event->setController(function () use ($response) { + return $response; + }); + } else { + if ($etag) { + $this->etags[$request] = $etag; + } + if ($lastModifiedDate) { + $this->lastModifiedDates[$request] = $lastModifiedDate; + } + } + } + + /** + * Modifies the response to apply HTTP cache headers when needed. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + $request = $event->getRequest(); + + if (!$configuration = $request->attributes->get('_cache')) { + return; + } + + $response = $event->getResponse(); + + // http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-12#section-3.1 + if (!in_array($response->getStatusCode(), array(200, 203, 300, 301, 302, 304, 404, 410))) { + return; + } + + if (null !== $age = $configuration->getSMaxAge()) { + if (!is_numeric($age)) { + $now = microtime(true); + + $age = ceil(strtotime($configuration->getSMaxAge(), $now) - $now); + } + + $response->setSharedMaxAge($age); + } + + if (null !== $age = $configuration->getMaxAge()) { + if (!is_numeric($age)) { + $now = microtime(true); + + $age = ceil(strtotime($configuration->getMaxAge(), $now) - $now); + } + + $response->setMaxAge($age); + } + + if (null !== $configuration->getExpires()) { + $date = \DateTime::createFromFormat('U', strtotime($configuration->getExpires()), new \DateTimeZone('UTC')); + $response->setExpires($date); + } + + if (null !== $configuration->getVary()) { + $response->setVary($configuration->getVary()); + } + + if ($configuration->isPublic()) { + $response->setPublic(); + } + + if ($configuration->isPrivate()) { + $response->setPrivate(); + } + + if (isset($this->lastModifiedDates[$request])) { + $response->setLastModified($this->lastModifiedDates[$request]); + + unset($this->lastModifiedDates[$request]); + } + + if (isset($this->etags[$request])) { + $response->setETag($this->etags[$request]); + + unset($this->etags[$request]); + } + + $event->setResponse($response); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => 'onKernelController', + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } + + private function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $this->expressionLanguage = new ExpressionLanguage(); + } + + return $this->expressionLanguage; + } +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/ParamConverterListener.php b/vendor/sensio/framework-extra-bundle/EventListener/ParamConverterListener.php new file mode 100644 index 0000000..0417b69 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/ParamConverterListener.php @@ -0,0 +1,116 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * The ParamConverterListener handles the ParamConverter annotation. + * + * @author Fabien Potencier + */ +class ParamConverterListener implements EventSubscriberInterface +{ + /** + * @var ParamConverterManager + */ + protected $manager; + + protected $autoConvert; + + /** + * Constructor. + * + * @param ParamConverterManager $manager A ParamConverterManager instance + * @param bool $autoConvert Auto convert non-configured objects + */ + public function __construct(ParamConverterManager $manager, $autoConvert = true) + { + $this->manager = $manager; + $this->autoConvert = $autoConvert; + } + + /** + * Modifies the ParamConverterManager instance. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + $controller = $event->getController(); + $request = $event->getRequest(); + $configurations = array(); + + if ($configuration = $request->attributes->get('_converters')) { + foreach (is_array($configuration) ? $configuration : array($configuration) as $configuration) { + $configurations[$configuration->getName()] = $configuration; + } + } + + if (is_array($controller)) { + $r = new \ReflectionMethod($controller[0], $controller[1]); + } elseif (is_object($controller) && is_callable($controller, '__invoke')) { + $r = new \ReflectionMethod($controller, '__invoke'); + } else { + $r = new \ReflectionFunction($controller); + } + + // automatically apply conversion for non-configured objects + if ($this->autoConvert) { + $configurations = $this->autoConfigure($r, $request, $configurations); + } + + $this->manager->apply($request, $configurations); + } + + private function autoConfigure(\ReflectionFunctionAbstract $r, Request $request, $configurations) + { + foreach ($r->getParameters() as $param) { + if (!$param->getClass() || $param->getClass()->isInstance($request)) { + continue; + } + + $name = $param->getName(); + + if (!isset($configurations[$name])) { + $configuration = new ParamConverter(array()); + $configuration->setName($name); + $configuration->setClass($param->getClass()->getName()); + + $configurations[$name] = $configuration; + } elseif (null === $configurations[$name]->getClass()) { + $configurations[$name]->setClass($param->getClass()->getName()); + } + + $configurations[$name]->setIsOptional($param->isOptional()); + } + + return $configurations; + } + + /** + * Get subscribed events. + * + * @return array Subscribed events + */ + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => 'onKernelController', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/PsrResponseListener.php b/vendor/sensio/framework-extra-bundle/EventListener/PsrResponseListener.php new file mode 100644 index 0000000..24b6bd4 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/PsrResponseListener.php @@ -0,0 +1,63 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +use Psr\Http\Message\ResponseInterface; +use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; +use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Converts PSR-7 Response to HttpFoundation Response using the bridge. + * + * @author Kévin Dunglas + */ +class PsrResponseListener implements EventSubscriberInterface +{ + /** + * @var HttpFoundationFactoryInterface + */ + private $httpFoundationFactory; + + public function __construct(HttpFoundationFactoryInterface $httpFoundationFactory) + { + $this->httpFoundationFactory = $httpFoundationFactory; + } + + /** + * Do the conversion if applicable and update the response of the event. + * + * @param GetResponseForControllerResultEvent $event + */ + public function onKernelView(GetResponseForControllerResultEvent $event) + { + $controllerResult = $event->getControllerResult(); + + if (!$controllerResult instanceof ResponseInterface) { + return; + } + + $event->setResponse($this->httpFoundationFactory->createResponse($controllerResult)); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + KernelEvents::VIEW => 'onKernelView', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/SecurityListener.php b/vendor/sensio/framework-extra-bundle/EventListener/SecurityListener.php new file mode 100644 index 0000000..b85791d --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/SecurityListener.php @@ -0,0 +1,102 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; + +/** + * SecurityListener handles security restrictions on controllers. + * + * @author Fabien Potencier + */ +class SecurityListener implements EventSubscriberInterface +{ + private $tokenStorage; + private $authChecker; + private $language; + private $trustResolver; + private $roleHierarchy; + + public function __construct(SecurityContextInterface $securityContext = null, ExpressionLanguage $language = null, AuthenticationTrustResolverInterface $trustResolver = null, RoleHierarchyInterface $roleHierarchy = null, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authChecker = null) + { + $this->tokenStorage = $tokenStorage ?: $securityContext; + $this->authChecker = $authChecker ?: $securityContext; + $this->language = $language; + $this->trustResolver = $trustResolver; + $this->roleHierarchy = $roleHierarchy; + } + + public function onKernelController(FilterControllerEvent $event) + { + $request = $event->getRequest(); + if (!$configuration = $request->attributes->get('_security')) { + return; + } + + if (null === $this->tokenStorage || null === $this->trustResolver) { + throw new \LogicException('To use the @Security tag, you need to install the Symfony Security bundle.'); + } + + if (null === $this->tokenStorage->getToken()) { + throw new \LogicException('To use the @Security tag, your controller needs to be behind a firewall.'); + } + + if (null === $this->language) { + throw new \LogicException('To use the @Security tag, you need to use the Security component 2.4 or newer and to install the ExpressionLanguage component.'); + } + + if (!$this->language->evaluate($configuration->getExpression(), $this->getVariables($request))) { + throw new AccessDeniedException(sprintf('Expression "%s" denied access.', $configuration->getExpression())); + } + } + + // code should be sync with Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter + private function getVariables(Request $request) + { + $token = $this->tokenStorage->getToken(); + + if (null !== $this->roleHierarchy) { + $roles = $this->roleHierarchy->getReachableRoles($token->getRoles()); + } else { + $roles = $token->getRoles(); + } + + $variables = array( + 'token' => $token, + 'user' => $token->getUser(), + 'object' => $request, + 'request' => $request, + 'roles' => array_map(function ($role) { return $role->getRole(); }, $roles), + 'trust_resolver' => $this->trustResolver, + // needed for the is_granted expression function + 'auth_checker' => $this->authChecker, + ); + + // controller variables should also be accessible + return array_merge($request->attributes->all(), $variables); + } + + public static function getSubscribedEvents() + { + return array(KernelEvents::CONTROLLER => 'onKernelController'); + } +} diff --git a/vendor/sensio/framework-extra-bundle/EventListener/TemplateListener.php b/vendor/sensio/framework-extra-bundle/EventListener/TemplateListener.php new file mode 100644 index 0000000..cab038e --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/EventListener/TemplateListener.php @@ -0,0 +1,136 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\EventListener; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; + +/** + * The TemplateListener class handles the Template annotation. + * + * @author Fabien Potencier + */ +class TemplateListener implements EventSubscriberInterface +{ + /** + * @var ContainerInterface + */ + protected $container; + + /** + * Constructor. + * + * @param ContainerInterface $container The service container instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Guesses the template name to render and its variables and adds them to + * the request object. + * + * @param FilterControllerEvent $event A FilterControllerEvent instance + */ + public function onKernelController(FilterControllerEvent $event) + { + if (!is_array($controller = $event->getController())) { + return; + } + + $request = $event->getRequest(); + + if (!$configuration = $request->attributes->get('_template')) { + return; + } + + if (!$configuration->getTemplate()) { + $guesser = $this->container->get('sensio_framework_extra.view.guesser'); + $configuration->setTemplate($guesser->guessTemplateName($controller, $request, $configuration->getEngine())); + } + + $request->attributes->set('_template', $configuration->getTemplate()); + $request->attributes->set('_template_vars', $configuration->getVars()); + $request->attributes->set('_template_streamable', $configuration->isStreamable()); + + // all controller method arguments + if (!$configuration->getVars()) { + $r = new \ReflectionObject($controller[0]); + + $vars = array(); + foreach ($r->getMethod($controller[1])->getParameters() as $param) { + $vars[] = $param->getName(); + } + + $request->attributes->set('_template_default_vars', $vars); + } + } + + /** + * Renders the template and initializes a new response object with the + * rendered template content. + * + * @param GetResponseForControllerResultEvent $event A GetResponseForControllerResultEvent instance + */ + public function onKernelView(GetResponseForControllerResultEvent $event) + { + $request = $event->getRequest(); + $parameters = $event->getControllerResult(); + + if (null === $parameters) { + if (!$vars = $request->attributes->get('_template_vars')) { + if (!$vars = $request->attributes->get('_template_default_vars')) { + return; + } + } + + $parameters = array(); + foreach ($vars as $var) { + $parameters[$var] = $request->attributes->get($var); + } + } + + if (!is_array($parameters)) { + return $parameters; + } + + if (!$template = $request->attributes->get('_template')) { + return $parameters; + } + + $templating = $this->container->get('templating'); + + if (!$request->attributes->get('_template_streamable')) { + $event->setResponse($templating->renderResponse($template, $parameters)); + } else { + $callback = function () use ($templating, $template, $parameters) { + return $templating->stream($template, $parameters); + }; + + $event->setResponse(new StreamedResponse($callback)); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => array('onKernelController', -128), + KernelEvents::VIEW => 'onKernelView', + ); + } +} diff --git a/vendor/sensio/framework-extra-bundle/README.md b/vendor/sensio/framework-extra-bundle/README.md new file mode 100644 index 0000000..4c75ae0 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/README.md @@ -0,0 +1,12 @@ +SensioFrameworkExtraBundle +========================== + +This bundle provides a way to configure your controllers with annotations. + +Read about it on its [official homepage](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html). + +As of v3.0.0 of the bundle, the release cycle is de-synchronized from the +framework's. It means you can just require "sensio/framework-extra-bundle": +"~3.0" in your composer.json and Composer will automatically pick the latest +version of the bundle that works with your current version of Symfony. The +minimum version of Symfony2 for this workflow is 2.3. diff --git a/vendor/sensio/framework-extra-bundle/Request/ParamConverter/DateTimeParamConverter.php b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/DateTimeParamConverter.php new file mode 100644 index 0000000..bea90f3 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/DateTimeParamConverter.php @@ -0,0 +1,76 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; + +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use DateTime; + +/** + * Convert DateTime instances from request attribute variable. + * + * @author Benjamin Eberlei + */ +class DateTimeParamConverter implements ParamConverterInterface +{ + /** + * {@inheritdoc} + * + * @throws NotFoundHttpException When invalid date given + */ + public function apply(Request $request, ParamConverter $configuration) + { + $param = $configuration->getName(); + + if (!$request->attributes->has($param)) { + return false; + } + + $options = $configuration->getOptions(); + $value = $request->attributes->get($param); + + if (!$value && $configuration->isOptional()) { + return false; + } + + if (isset($options['format'])) { + $date = DateTime::createFromFormat($options['format'], $value); + + if (!$date) { + throw new NotFoundHttpException('Invalid date given.'); + } + } else { + if (false === strtotime($value)) { + throw new NotFoundHttpException('Invalid date given.'); + } + + $date = new DateTime($value); + } + + $request->attributes->set($param, $date); + + return true; + } + + /** + * {@inheritdoc} + */ + public function supports(ParamConverter $configuration) + { + if (null === $configuration->getClass()) { + return false; + } + + return 'DateTime' === $configuration->getClass(); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Request/ParamConverter/DoctrineParamConverter.php b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/DoctrineParamConverter.php new file mode 100644 index 0000000..e57c1f3 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/DoctrineParamConverter.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; + +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpFoundation\Request; +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\NoResultException; + +/** + * DoctrineParamConverter. + * + * @author Fabien Potencier + */ +class DoctrineParamConverter implements ParamConverterInterface +{ + /** + * @var ManagerRegistry + */ + protected $registry; + + public function __construct(ManagerRegistry $registry = null) + { + $this->registry = $registry; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException When unable to guess how to get a Doctrine instance from the request information + * @throws NotFoundHttpException When object not found + */ + public function apply(Request $request, ParamConverter $configuration) + { + $name = $configuration->getName(); + $class = $configuration->getClass(); + $options = $this->getOptions($configuration); + + if (null === $request->attributes->get($name, false)) { + $configuration->setIsOptional(true); + } + + // find by identifier? + if (false === $object = $this->find($class, $request, $options, $name)) { + // find by criteria + if (false === $object = $this->findOneBy($class, $request, $options)) { + if ($configuration->isOptional()) { + $object = null; + } else { + throw new \LogicException('Unable to guess how to get a Doctrine instance from the request information.'); + } + } + } + + if (null === $object && false === $configuration->isOptional()) { + throw new NotFoundHttpException(sprintf('%s object not found.', $class)); + } + + $request->attributes->set($name, $object); + + return true; + } + + protected function find($class, Request $request, $options, $name) + { + if ($options['mapping'] || $options['exclude']) { + return false; + } + + $id = $this->getIdentifier($request, $options, $name); + + if (false === $id || null === $id) { + return false; + } + + if (isset($options['repository_method'])) { + $method = $options['repository_method']; + } else { + $method = 'find'; + } + + try { + return $this->getManager($options['entity_manager'], $class)->getRepository($class)->$method($id); + } catch (NoResultException $e) { + return; + } + } + + protected function getIdentifier(Request $request, $options, $name) + { + if (isset($options['id'])) { + if (!is_array($options['id'])) { + $name = $options['id']; + } elseif (is_array($options['id'])) { + $id = array(); + foreach ($options['id'] as $field) { + $id[$field] = $request->attributes->get($field); + } + + return $id; + } + } + + if ($request->attributes->has($name)) { + return $request->attributes->get($name); + } + + if ($request->attributes->has('id') && !isset($options['id'])) { + return $request->attributes->get('id'); + } + + return false; + } + + protected function findOneBy($class, Request $request, $options) + { + if (!$options['mapping']) { + $keys = $request->attributes->keys(); + $options['mapping'] = $keys ? array_combine($keys, $keys) : array(); + } + + foreach ($options['exclude'] as $exclude) { + unset($options['mapping'][$exclude]); + } + + if (!$options['mapping']) { + return false; + } + + // if a specific id has been defined in the options and there is no corresponding attribute + // return false in order to avoid a fallback to the id which might be of another object + if (isset($options['id']) && null === $request->attributes->get($options['id'])) { + return false; + } + + $criteria = array(); + $em = $this->getManager($options['entity_manager'], $class); + $metadata = $em->getClassMetadata($class); + + $mapMethodSignature = isset($options['repository_method']) + && isset($options['map_method_signature']) + && $options['map_method_signature'] === true; + + foreach ($options['mapping'] as $attribute => $field) { + if ($metadata->hasField($field) + || ($metadata->hasAssociation($field) && $metadata->isSingleValuedAssociation($field)) + || $mapMethodSignature) { + $criteria[$field] = $request->attributes->get($attribute); + } + } + + if ($options['strip_null']) { + $criteria = array_filter($criteria, function ($value) { return !is_null($value); }); + } + + if (!$criteria) { + return false; + } + + if (isset($options['repository_method'])) { + $repositoryMethod = $options['repository_method']; + } else { + $repositoryMethod = 'findOneBy'; + } + + try { + if ($mapMethodSignature) { + return $this->findDataByMapMethodSignature($em, $class, $repositoryMethod, $criteria); + } + + return $em->getRepository($class)->$repositoryMethod($criteria); + } catch (NoResultException $e) { + return; + } + } + + private function findDataByMapMethodSignature($em, $class, $repositoryMethod, $criteria) + { + $arguments = array(); + $repository = $em->getRepository($class); + $ref = new \ReflectionMethod($repository, $repositoryMethod); + foreach ($ref->getParameters() as $parameter) { + if (array_key_exists($parameter->name, $criteria)) { + $arguments[] = $criteria[$parameter->name]; + } elseif ($parameter->isDefaultValueAvailable()) { + $arguments[] = $parameter->getDefaultValue(); + } else { + throw new \InvalidArgumentException(sprintf('Repository method "%s::%s" requires that you provide a value for the "$%s" argument.', get_class($repository), $repositoryMethod, $parameter->name)); + } + } + + return $ref->invokeArgs($repository, $arguments); + } + + /** + * {@inheritdoc} + */ + public function supports(ParamConverter $configuration) + { + // if there is no manager, this means that only Doctrine DBAL is configured + if (null === $this->registry || !count($this->registry->getManagers())) { + return false; + } + + if (null === $configuration->getClass()) { + return false; + } + + $options = $this->getOptions($configuration); + + // Doctrine Entity? + $em = $this->getManager($options['entity_manager'], $configuration->getClass()); + if (null === $em) { + return false; + } + + return !$em->getMetadataFactory()->isTransient($configuration->getClass()); + } + + protected function getOptions(ParamConverter $configuration) + { + return array_replace(array( + 'entity_manager' => null, + 'exclude' => array(), + 'mapping' => array(), + 'strip_null' => false, + ), $configuration->getOptions()); + } + + private function getManager($name, $class) + { + if (null === $name) { + return $this->registry->getManagerForClass($class); + } + + return $this->registry->getManager($name); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Request/ParamConverter/ParamConverterInterface.php b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/ParamConverterInterface.php new file mode 100644 index 0000000..d54abba --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/ParamConverterInterface.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; + +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Symfony\Component\HttpFoundation\Request; + +/** + * Converts request parameters to objects and stores them as request + * attributes, so they can be injected as controller method arguments. + * + * @author Fabien Potencier + */ +interface ParamConverterInterface +{ + /** + * Stores the object in the request. + * + * @param Request $request The request + * @param ParamConverter $configuration Contains the name, class and options of the object + * + * @return bool True if the object has been successfully set, else false + */ + public function apply(Request $request, ParamConverter $configuration); + + /** + * Checks if the object is supported. + * + * @param ParamConverter $configuration Should be an instance of ParamConverter + * + * @return bool True if the object is supported, else false + */ + public function supports(ParamConverter $configuration); +} diff --git a/vendor/sensio/framework-extra-bundle/Request/ParamConverter/ParamConverterManager.php b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/ParamConverterManager.php new file mode 100644 index 0000000..5842107 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/ParamConverterManager.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; + +use Symfony\Component\HttpFoundation\Request; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface; + +/** + * Managers converters. + * + * @author Fabien Potencier + * @author Henrik Bjornskov + */ +class ParamConverterManager +{ + /** + * @var array + */ + protected $converters = array(); + + /** + * @var array + */ + protected $namedConverters = array(); + + /** + * Applies all converters to the passed configurations and stops when a + * converter is applied it will move on to the next configuration and so on. + * + * @param Request $request + * @param array|object $configurations + */ + public function apply(Request $request, $configurations) + { + if (is_object($configurations)) { + $configurations = array($configurations); + } + + foreach ($configurations as $configuration) { + $this->applyConverter($request, $configuration); + } + } + + /** + * Apply converter on request based on the given configuration. + * + * @param Request $request + * @param ConfigurationInterface $configuration + */ + protected function applyConverter(Request $request, ConfigurationInterface $configuration) + { + $value = $request->attributes->get($configuration->getName()); + $className = $configuration->getClass(); + + // If the value is already an instance of the class we are trying to convert it into + // we should continue as no conversion is required + if (is_object($value) && $value instanceof $className) { + return; + } + + if ($converterName = $configuration->getConverter()) { + if (!isset($this->namedConverters[$converterName])) { + throw new \RuntimeException(sprintf( + "No converter named '%s' found for conversion of parameter '%s'.", + $converterName, $configuration->getName() + )); + } + + $converter = $this->namedConverters[$converterName]; + + if (!$converter->supports($configuration)) { + throw new \RuntimeException(sprintf( + "Converter '%s' does not support conversion of parameter '%s'.", + $converterName, $configuration->getName() + )); + } + + $converter->apply($request, $configuration); + + return; + } + + foreach ($this->all() as $converter) { + if ($converter->supports($configuration)) { + if ($converter->apply($request, $configuration)) { + return; + } + } + } + } + + /** + * Adds a parameter converter. + * + * Converters match either explicitly via $name or by iteration over all + * converters with a $priority. If you pass a $priority = null then the + * added converter will not be part of the iteration chain and can only + * be invoked explicitly. + * + * @param ParamConverterInterface $converter A ParamConverterInterface instance + * @param int $priority The priority (between -10 and 10). + * @param string $name Name of the converter. + */ + public function add(ParamConverterInterface $converter, $priority = 0, $name = null) + { + if ($priority !== null) { + if (!isset($this->converters[$priority])) { + $this->converters[$priority] = array(); + } + + $this->converters[$priority][] = $converter; + } + + if (null !== $name) { + $this->namedConverters[$name] = $converter; + } + } + + /** + * Returns all registered param converters. + * + * @return array An array of param converters + */ + public function all() + { + krsort($this->converters); + + $converters = array(); + foreach ($this->converters as $all) { + $converters = array_merge($converters, $all); + } + + return $converters; + } +} diff --git a/vendor/sensio/framework-extra-bundle/Request/ParamConverter/PsrServerRequestParamConverter.php b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/PsrServerRequestParamConverter.php new file mode 100644 index 0000000..a336674 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Request/ParamConverter/PsrServerRequestParamConverter.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter; + +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Converts HttpFoundation Request to PSR-7 ServerRequest using the bridge. + * + * @author Kévin Dunglas + */ +class PsrServerRequestParamConverter implements ParamConverterInterface +{ + /** + * @var array + */ + private static $supportedTypes = array( + 'Psr\Http\Message\ServerRequestInterface' => true, + 'Psr\Http\Message\RequestInterface' => true, + 'Psr\Http\Message\MessageInterface' => true, + ); + /** + * @var HttpMessageFactoryInterface + */ + private $httpMessageFactory; + + public function __construct(HttpMessageFactoryInterface $httpMessageFactory) + { + $this->httpMessageFactory = $httpMessageFactory; + } + + /** + * {@inheritdoc} + */ + public function apply(Request $request, ParamConverter $configuration) + { + $request->attributes->set($configuration->getName(), $this->httpMessageFactory->createRequest($request)); + } + + /** + * {@inheritdoc} + */ + public function supports(ParamConverter $configuration) + { + if (null === $configuration->getClass()) { + return false; + } + + return isset(self::$supportedTypes[$configuration->getClass()]); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/annotations.xml b/vendor/sensio/framework-extra-bundle/Resources/config/annotations.xml new file mode 100644 index 0000000..4e0c5b4 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/annotations.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/cache.xml b/vendor/sensio/framework-extra-bundle/Resources/config/cache.xml new file mode 100644 index 0000000..dd06973 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/cache.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/converters.xml b/vendor/sensio/framework-extra-bundle/Resources/config/converters.xml new file mode 100644 index 0000000..1f95134 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/converters.xml @@ -0,0 +1,32 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter + Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DateTimeParamConverter + + + + + + + true + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/psr7.xml b/vendor/sensio/framework-extra-bundle/Resources/config/psr7.xml new file mode 100644 index 0000000..214119f --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/psr7.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/routing.xml b/vendor/sensio/framework-extra-bundle/Resources/config/routing.xml new file mode 100644 index 0000000..b3b58cd --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/routing.xml @@ -0,0 +1,31 @@ + + + + + + Symfony\Component\Routing\Loader\AnnotationDirectoryLoader + Symfony\Component\Routing\Loader\AnnotationFileLoader + Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/security.xml b/vendor/sensio/framework-extra-bundle/Resources/config/security.xml new file mode 100644 index 0000000..dd2edba --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/security.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/services.xml b/vendor/sensio/framework-extra-bundle/Resources/config/services.xml new file mode 100644 index 0000000..6c76e61 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/services.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/config/view.xml b/vendor/sensio/framework-extra-bundle/Resources/config/view.xml new file mode 100644 index 0000000..350b6a1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/config/view.xml @@ -0,0 +1,17 @@ + + + + + + Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener + + + + + + + + + diff --git a/vendor/sensio/framework-extra-bundle/Resources/meta/LICENSE b/vendor/sensio/framework-extra-bundle/Resources/meta/LICENSE new file mode 100644 index 0000000..ad32bc5 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010,2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/sensio/framework-extra-bundle/Routing/AnnotatedRouteControllerLoader.php b/vendor/sensio/framework-extra-bundle/Routing/AnnotatedRouteControllerLoader.php new file mode 100644 index 0000000..bd47bf8 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Routing/AnnotatedRouteControllerLoader.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Routing; + +use Symfony\Component\Routing\Loader\AnnotationClassLoader; +use Symfony\Component\Routing\Route; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route as FrameworkExtraBundleRoute; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; + +/** + * AnnotatedRouteControllerLoader is an implementation of AnnotationClassLoader + * that sets the '_controller' default based on the class and method names. + * + * It also parse the @Method annotation. + * + * @author Fabien Potencier + */ +class AnnotatedRouteControllerLoader extends AnnotationClassLoader +{ + /** + * Configures the _controller default parameter and eventually the HTTP method + * requirement of a given Route instance. + * + * @param Route $route A route instance + * @param \ReflectionClass $class A ReflectionClass instance + * @param \ReflectionMethod $method A ReflectionClass method + * @param mixed $annot The annotation class instance + * + * @throws \LogicException When the service option is specified on a method + */ + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + // controller + $classAnnot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass); + if ($classAnnot instanceof FrameworkExtraBundleRoute && $service = $classAnnot->getService()) { + $route->setDefault('_controller', $service.':'.$method->getName()); + } else { + $route->setDefault('_controller', $class->getName().'::'.$method->getName()); + } + + // requirements (@Method) + foreach ($this->reader->getMethodAnnotations($method) as $configuration) { + if ($configuration instanceof Method) { + $route->setMethods(implode('|', $configuration->getMethods())); + } elseif ($configuration instanceof FrameworkExtraBundleRoute && $configuration->getService()) { + throw new \LogicException('The service option can only be specified at class level.'); + } + } + } + + protected function getGlobals(\ReflectionClass $class) + { + $globals = parent::getGlobals($class); + + foreach ($this->reader->getClassAnnotations($class) as $configuration) { + if ($configuration instanceof Method) { + $globals['methods'] = array_merge($globals['methods'], $configuration->getMethods()); + } + } + + return $globals; + } + + /** + * Makes the default route name more sane by removing common keywords. + * + * @param \ReflectionClass $class A ReflectionClass instance + * @param \ReflectionMethod $method A ReflectionMethod instance + * + * @return string The default route name + */ + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + { + $routeName = parent::getDefaultRouteName($class, $method); + + return preg_replace(array( + '/(bundle|controller)_/', + '/action(_\d+)?$/', + '/__/', + ), array( + '_', + '\\1', + '_', + ), $routeName); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Security/ExpressionLanguage.php b/vendor/sensio/framework-extra-bundle/Security/ExpressionLanguage.php new file mode 100644 index 0000000..86b6417 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Security/ExpressionLanguage.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Security; + +use Symfony\Component\Security\Core\Authorization\ExpressionLanguage as BaseExpressionLanguage; + +/** + * Adds some function to the default Symfony Security ExpressionLanguage. + * + * @author Fabien Potencier + */ +class ExpressionLanguage extends BaseExpressionLanguage +{ + protected function registerFunctions() + { + parent::registerFunctions(); + + $this->register('is_granted', function ($attributes, $object = 'null') { + return sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object); + }, function (array $variables, $attributes, $object = null) { + return $variables['auth_checker']->isGranted($attributes, $object); + }); + } +} diff --git a/vendor/sensio/framework-extra-bundle/SensioFrameworkExtraBundle.php b/vendor/sensio/framework-extra-bundle/SensioFrameworkExtraBundle.php new file mode 100644 index 0000000..5c95a15 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/SensioFrameworkExtraBundle.php @@ -0,0 +1,33 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * SensioFrameworkExtraBundle. + * + * @author Fabien Potencier + */ +class SensioFrameworkExtraBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new AddParamConverterPass()); + $container->addCompilerPass(new LegacyPass()); + } +} diff --git a/vendor/sensio/framework-extra-bundle/Templating/TemplateGuesser.php b/vendor/sensio/framework-extra-bundle/Templating/TemplateGuesser.php new file mode 100644 index 0000000..f6587b8 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/Templating/TemplateGuesser.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\FrameworkExtraBundle\Templating; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Doctrine\Common\Util\ClassUtils; + +/** + * The TemplateGuesser class handles the guessing of template name based on controller. + * + * @author Fabien Potencier + */ +class TemplateGuesser +{ + /** + * @var KernelInterface + */ + protected $kernel; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + /** + * Guesses and returns the template name to render based on the controller + * and action names. + * + * @param array $controller An array storing the controller object and action method + * @param Request $request A Request instance + * @param string $engine + * + * @return TemplateReference template reference + * + * @throws \InvalidArgumentException + */ + public function guessTemplateName($controller, Request $request, $engine = 'twig') + { + $className = class_exists('Doctrine\Common\Util\ClassUtils') ? ClassUtils::getClass($controller[0]) : get_class($controller[0]); + + if (!preg_match('/Controller\\\(.+)Controller$/', $className, $matchController)) { + throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', get_class($controller[0]))); + } + if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) { + throw new \InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1])); + } + + $bundle = $this->getBundleForClass($className); + + if ($bundle) { + while ($bundleName = $bundle->getName()) { + if (null === $parentBundleName = $bundle->getParent()) { + $bundleName = $bundle->getName(); + + break; + } + + $bundles = $this->kernel->getBundle($parentBundleName, false); + $bundle = array_pop($bundles); + } + } else { + $bundleName = null; + } + + return new TemplateReference($bundleName, $matchController[1], $matchAction[1], $request->getRequestFormat(), $engine); + } + + /** + * Returns the Bundle instance in which the given class name is located. + * + * @param string $class A fully qualified controller class name + * + * @return Bundle|null $bundle A Bundle instance + */ + protected function getBundleForClass($class) + { + $reflectionClass = new \ReflectionClass($class); + $bundles = $this->kernel->getBundles(); + + do { + $namespace = $reflectionClass->getNamespaceName(); + foreach ($bundles as $bundle) { + if (0 === strpos($namespace, $bundle->getNamespace())) { + return $bundle; + } + } + $reflectionClass = $reflectionClass->getParentClass(); + } while ($reflectionClass); + } +} diff --git a/vendor/sensio/framework-extra-bundle/UPGRADE.md b/vendor/sensio/framework-extra-bundle/UPGRADE.md new file mode 100644 index 0000000..692f1f1 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/UPGRADE.md @@ -0,0 +1,50 @@ +UPGRADE FROM 2.0 to 2.1 +======================= + +### DoctrineParamConverter: Request Attributes with same name as Arguments + +Previously the DoctrineParamConverter defaulted to finding objects by 'id' +parameter. This is unintuitive and is now overwritten by a behavior where +the request attributes with the same name as entity arguments is matched +with higher priority. + +This might cause problems if you are using this parameter for another object conversion. + +### DoctrineParamConverter with multiple Arguments may clash + +In 2.0 the parameter converter matched only entity fields against route parameters. +With 2.1, the matching now also includes single-valued associations. Depending +on fields in entities this might lead to clashes when you update to the latest version. + +Example that may break with the latest (2.1) version: + + /** + * @Route("/user/{email}/{address}") + * @ParamConverter("address", class="MyBundle:Address", options={"id": "address"}) + */ + public function showAction(User $user, Address $address) + { + } + + class User + { + /** @ORM\Column(type="string") */ + public $email; + /** @ORM\ManyToOne(targetEntity="Address") */ + public $address; + } + +Since address exists as field in `User` and User is not searched by primary key but +by field, this scenario now adds `address` to the criteria to find a user instance. +In scenarios of related entities this might even (just) work, but you never know. + +You can fix this by configuring explicit mapping for `User`: + + /** + * @Route("/user/{email}/{address}") + * @ParamConverter("address", options={"id": "address"}) + * @ParamConverter("email", options={"exclude": ["address"]}) + */ + public function showAction(User $user, Address $address) + { + } diff --git a/vendor/sensio/framework-extra-bundle/composer.json b/vendor/sensio/framework-extra-bundle/composer.json new file mode 100644 index 0000000..ac1af57 --- /dev/null +++ b/vendor/sensio/framework-extra-bundle/composer.json @@ -0,0 +1,35 @@ +{ + "name": "sensio/framework-extra-bundle", + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": ["annotations","controllers"], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "symfony/framework-bundle": "~2.3|~3.0", + "doctrine/common": "~2.2" + }, + "require-dev": { + "symfony/expression-language": "~2.4|~3.0", + "symfony/security-bundle": "~2.4|~3.0" + }, + "suggest": { + "symfony/psr-http-message-bridge": "To use the PSR-7 converters", + "symfony/expression-language": "", + "symfony/security-bundle": "" + }, + "autoload": { + "psr-4": { "Sensio\\Bundle\\FrameworkExtraBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "minimum-stability": "dev" +} diff --git a/vendor/sensio/generator-bundle/.travis.yml b/vendor/sensio/generator-bundle/.travis.yml new file mode 100644 index 0000000..7a7bb70 --- /dev/null +++ b/vendor/sensio/generator-bundle/.travis.yml @@ -0,0 +1,21 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +matrix: + include: + - php: hhvm + - php: 5.3 + - php: 5.4 + - php: 5.5 + - php: 5.6 + - php: 7.0 + env: composer_extra="--prefer-lowest --prefer-stable" + fast_finish: true + +install: + - composer --prefer-source $composer_extra update diff --git a/vendor/sensio/generator-bundle/Command/AutoComplete/EntitiesAutoCompleter.php b/vendor/sensio/generator-bundle/Command/AutoComplete/EntitiesAutoCompleter.php new file mode 100644 index 0000000..c14d64a --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/AutoComplete/EntitiesAutoCompleter.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command\AutoComplete; + +use Doctrine\ORM\EntityManagerInterface; + +/** + * Provides auto-completion suggestions for entities. + * + * @author Charles Sarrazin + */ +class EntitiesAutoCompleter +{ + private $manager; + + public function __construct(EntityManagerInterface $manager) + { + $this->manager = $manager; + } + + public function getSuggestions() + { + $configuration = $this->manager + ->getConfiguration() + ; + + $namespaceReplacements = array(); + + foreach ($configuration->getEntityNamespaces() as $alias => $namespace) { + $namespaceReplacements[$namespace.'\\'] = $alias.':'; + } + + $entities = $configuration + ->getMetadataDriverImpl() + ->getAllClassNames() + ; + + return array_map(function ($entity) use ($namespaceReplacements) { + return strtr($entity, $namespaceReplacements); + }, $entities); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateBundleCommand.php b/vendor/sensio/generator-bundle/Command/GenerateBundleCommand.php new file mode 100644 index 0000000..5edfd54 --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateBundleCommand.php @@ -0,0 +1,408 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Sensio\Bundle\GeneratorBundle\Manipulator\ConfigurationManipulator; +use Sensio\Bundle\GeneratorBundle\Model\Bundle; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\HttpKernel\KernelInterface; +use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator; +use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator; +use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; + +/** + * Generates bundles. + * + * @author Fabien Potencier + */ +class GenerateBundleCommand extends GeneratorCommand +{ + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('generate:bundle') + ->setDescription('Generates a bundle') + ->setDefinition(array( + new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'), + new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle', 'src/'), + new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'), + new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)'), + new InputOption('shared', '', InputOption::VALUE_NONE, 'Are you planning on sharing this bundle across multiple applications?'), + )) + ->setHelp(<<%command.name% command helps you generates new bundles. + +By default, the command interacts with the developer to tweak the generation. +Any passed option will be used as a default value for the interaction +(--namespace is the only one needed if you follow the +conventions): + +php %command.full_name% --namespace=Acme/BlogBundle + +Note that you can use / instead of \\ for the namespace delimiter to avoid any +problems. + +If you want to disable any user interaction, use --no-interaction but don't forget to pass all needed options: + +php %command.full_name% --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction + +Note that the bundle namespace must end with "Bundle". +EOT + ) + ; + } + + /** + * @see Command + * + * @throws \InvalidArgumentException When namespace doesn't end with Bundle + * @throws \RuntimeException When bundle can't be executed + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + + $bundle = $this->createBundleObject($input); + $questionHelper->writeSection($output, 'Bundle generation'); + + /** @var BundleGenerator $generator */ + $generator = $this->getGenerator(); + + $output->writeln(sprintf( + '> Generating a sample bundle skeleton into %s OK!', + $this->makePathRelative($bundle->getTargetDirectory()) + )); + $generator->generateBundle($bundle); + + $errors = array(); + $runner = $questionHelper->getRunner($output, $errors); + + // check that the namespace is already autoloaded + $runner($this->checkAutoloader($output, $bundle)); + + // register the bundle in the Kernel class + $runner($this->updateKernel($output, $this->getContainer()->get('kernel'), $bundle)); + + // routing importing + $runner($this->updateRouting($output, $bundle)); + + if (!$bundle->shouldGenerateDependencyInjectionDirectory()) { + // we need to import their services.yml manually! + $runner($this->updateConfiguration($output, $bundle)); + } + + $questionHelper->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Symfony bundle generator!'); + + /* + * shared option + */ + $shared = $input->getOption('shared'); + // ask, but use $shared as the default + $question = new ConfirmationQuestion($questionHelper->getQuestion( + 'Are you planning on sharing this bundle across multiple applications?', + $shared ? 'yes' : 'no' + ), $shared); + $shared = $questionHelper->ask($input, $output, $question); + $input->setOption('shared', $shared); + + /* + * namespace option + */ + $namespace = $input->getOption('namespace'); + $output->writeln(array( + '', + 'Your application code must be written in bundles. This command helps', + 'you generate them easily.', + '', + )); + + $askForBundleName = true; + if ($shared) { + // a shared bundle, so it should probably have a vendor namespace + $output->writeln(array( + 'Each bundle is hosted under a namespace (like Acme/BlogBundle).', + 'The namespace should begin with a "vendor" name like your company name, your', + 'project name, or your client name, followed by one or more optional category', + 'sub-namespaces, and it should end with the bundle name itself', + '(which must have Bundle as a suffix).', + '', + 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#bundle-name for more', + 'details on bundle naming conventions.', + '', + 'Use / instead of \\ for the namespace delimiter to avoid any problem.', + '', + )); + + $question = new Question($questionHelper->getQuestion( + 'Bundle namespace', + $namespace + ), $namespace); + $question->setValidator(function ($answer) { + return Validators::validateBundleNamespace($answer, true); + }); + $namespace = $questionHelper->ask($input, $output, $question); + } else { + // a simple application bundle + $output->writeln(array( + 'Give your bundle a descriptive name, like BlogBundle.', + )); + + $question = new Question($questionHelper->getQuestion( + 'Bundle name', + $namespace + ), $namespace); + $question->setValidator(function ($inputNamespace) { + return Validators::validateBundleNamespace($inputNamespace, false); + }); + $namespace = $questionHelper->ask($input, $output, $question); + + if (strpos($namespace, '\\') === false) { + // this is a bundle name (FooBundle) not a namespace (Acme\FooBundle) + // so this is the bundle name (and it is also the namespace) + $input->setOption('bundle-name', $namespace); + $askForBundleName = false; + } + } + $input->setOption('namespace', $namespace); + + /* + * bundle-name option + */ + if ($askForBundleName) { + $bundle = $input->getOption('bundle-name'); + // no bundle yet? Get a default from the namespace + if (!$bundle) { + $bundle = strtr($namespace, array('\\Bundle\\' => '', '\\' => '')); + } + + $output->writeln(array( + '', + 'In your code, a bundle is often referenced by its name. It can be the', + 'concatenation of all namespace parts but it\'s really up to you to come', + 'up with a unique name (a good practice is to start with the vendor name).', + 'Based on the namespace, we suggest '.$bundle.'.', + '', + )); + $question = new Question($questionHelper->getQuestion( + 'Bundle name', + $bundle + ), $bundle); + $question->setValidator( + array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName') + ); + $bundle = $questionHelper->ask($input, $output, $question); + $input->setOption('bundle-name', $bundle); + } + + /* + * dir option + */ + // defaults to src/ in the option + $dir = $input->getOption('dir'); + $output->writeln(array( + '', + 'Bundles are usually generated into the src/ directory. Unless you\'re', + 'doing something custom, hit enter to keep this default!', + '', + )); + + $question = new Question($questionHelper->getQuestion( + 'Target Directory', + $dir + ), $dir); + $dir = $questionHelper->ask($input, $output, $question); + $input->setOption('dir', $dir); + + /* + * format option + */ + $format = $input->getOption('format'); + if (!$format) { + $format = $shared ? 'xml' : 'annotation'; + } + $output->writeln(array( + '', + 'What format do you want to use for your generated configuration?', + '', + )); + + $question = new Question($questionHelper->getQuestion( + 'Configuration format (annotation, yml, xml, php)', + $format + ), $format); + $question->setValidator(function ($format) { + return Validators::validateFormat($format); + }); + $question->setAutocompleterValues(array('annotation', 'yml', 'xml', 'php')); + $format = $questionHelper->ask($input, $output, $question); + $input->setOption('format', $format); + } + + protected function checkAutoloader(OutputInterface $output, Bundle $bundle) + { + $output->write('> Checking that the bundle is autoloaded: '); + if (!class_exists($bundle->getBundleClassName())) { + return array( + '- Edit the composer.json file and register the bundle', + ' namespace in the "autoload" section:', + '', + ); + } + } + + protected function updateKernel(OutputInterface $output, KernelInterface $kernel, Bundle $bundle) + { + $kernelManipulator = new KernelManipulator($kernel); + + $output->write(sprintf( + '> Enabling the bundle inside %s: ', + $this->makePathRelative($kernelManipulator->getFilename()) + )); + + try { + $ret = $kernelManipulator->addBundle($bundle->getBundleClassName()); + + if (!$ret) { + $reflected = new \ReflectionObject($kernel); + + return array( + sprintf('- Edit %s', $reflected->getFilename()), + ' and add the following bundle in the AppKernel::registerBundles() method:', + '', + sprintf(' new %s(),', $bundle->getBundleClassName()), + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already defined in AppKernel::registerBundles().', $bundle->getBundleClassName()), + '', + ); + } + } + + protected function updateRouting(OutputInterface $output, Bundle $bundle) + { + $targetRoutingPath = $this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml'; + $output->write(sprintf( + '> Importing the bundle\'s routes from the %s file: ', + $this->makePathRelative($targetRoutingPath) + )); + $routing = new RoutingManipulator($targetRoutingPath); + try { + $ret = $routing->addResource($bundle->getName(), $bundle->getConfigurationFormat()); + if (!$ret) { + if ('annotation' === $bundle->getConfigurationFormat()) { + $help = sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle->getName()); + } else { + $help = sprintf(" resource: \"@%s/Resources/config/routing.%s\"\n", $bundle->getName(), $bundle->getConfigurationFormat()); + } + $help .= " prefix: /\n"; + + return array( + '- Import the bundle\'s routing resource in the app\'s main routing file:', + '', + sprintf(' %s:', $bundle->getName()), + $help, + '', + ); + } + } catch (\RuntimeException $e) { + return array( + sprintf('Bundle %s is already imported.', $bundle->getName()), + '', + ); + } + } + + protected function updateConfiguration(OutputInterface $output, Bundle $bundle) + { + $targetConfigurationPath = $this->getContainer()->getParameter('kernel.root_dir').'/config/config.yml'; + $output->write(sprintf( + '> Importing the bundle\'s %s from the %s file: ', + $bundle->getServicesConfigurationFilename(), + $this->makePathRelative($targetConfigurationPath) + )); + $manipulator = new ConfigurationManipulator($targetConfigurationPath); + try { + $manipulator->addResource($bundle); + } catch (\RuntimeException $e) { + return array( + '- Import the bundle\'s %s resource in the app\'s main configuration file:', + '', + $manipulator->getImportCode($bundle), + '', + ); + } + } + + /** + * Creates the Bundle object based on the user's (non-interactive) input. + * + * @param InputInterface $input + * + * @return Bundle + */ + protected function createBundleObject(InputInterface $input) + { + foreach (array('namespace', 'dir') as $option) { + if (null === $input->getOption($option)) { + throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); + } + } + + $shared = $input->getOption('shared'); + + $namespace = Validators::validateBundleNamespace($input->getOption('namespace'), $shared); + if (!$bundleName = $input->getOption('bundle-name')) { + $bundleName = strtr($namespace, array('\\' => '')); + } + $bundleName = Validators::validateBundleName($bundleName); + $dir = $input->getOption('dir'); + if (null === $input->getOption('format')) { + $input->setOption('format', 'annotation'); + } + $format = Validators::validateFormat($input->getOption('format')); + + if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { + $dir = getcwd().'/'.$dir; + } + // add trailing / if necessary + $dir = '/' === substr($dir, -1, 1) ? $dir : $dir.'/'; + + return new Bundle( + $namespace, + $bundleName, + $dir, + $format, + $shared + ); + } + + protected function createGenerator() + { + return new BundleGenerator($this->getContainer()->get('filesystem')); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateCommandCommand.php b/vendor/sensio/generator-bundle/Command/GenerateCommandCommand.php new file mode 100644 index 0000000..73d6e6c --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateCommandCommand.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; +use Sensio\Bundle\GeneratorBundle\Generator\CommandGenerator; + +/** + * Generates commands. + * + * @author Javier Eguiluz + */ +class GenerateCommandCommand extends GeneratorCommand +{ + const MAX_ATTEMPTS = 5; + + /** + * @see Command + */ + public function configure() + { + $this + ->setName('generate:command') + ->setDescription('Generates a console command') + ->setDefinition(array( + new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle where the command is generated'), + new InputArgument('name', InputArgument::OPTIONAL, 'The command\'s name (e.g. app:my-command)'), + )) + ->setHelp(<<generate:command command helps you generate new commands +inside bundles. Provide the bundle name as the first argument and the command +name as the second argument: + +php app/console generate:command AppBundle blog:publish-posts + +If any of the arguments is missing, the command will ask for their values +interactively. If you want to disable any user interaction, use +--no-interaction, but don't forget to pass all needed arguments. + +Every generated file is based on a template. There are default templates but they can +be overridden by placing custom templates in one of the following locations, by order of priority: + +BUNDLE_PATH/Resources/SensioGeneratorBundle/skeleton/command +APP_PATH/Resources/SensioGeneratorBundle/skeleton/command + +You can check https://github.com/sensio/SensioGeneratorBundle/tree/master/Resources/skeleton +in order to know the file structure of the skeleton. +EOT + ) + ; + } + + public function interact(InputInterface $input, OutputInterface $output) + { + $bundle = $input->getArgument('bundle'); + $name = $input->getArgument('name'); + + if (null !== $bundle && null !== $name) { + return; + } + + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Symfony command generator'); + + // bundle + if (null !== $bundle) { + $output->writeln(sprintf('Bundle name: %s', $bundle)); + } else { + $output->writeln(array( + '', + 'First, you need to give the name of the bundle where the command will', + 'be generated (e.g. AppBundle)', + '', + )); + + $question = new Question($questionHelper->getQuestion('Bundle name', $bundle), $bundle); + $container = $this->getContainer(); + $question->setValidator(function ($answer) use ($container) { + try { + $b = $container->get('kernel')->getBundle($answer); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Bundle "%s" does not exist.', $answer)); + } + + return $answer; + }); + $question->setMaxAttempts(self::MAX_ATTEMPTS); + + $bundle = $questionHelper->ask($input, $output, $question); + $input->setArgument('bundle', $bundle); + } + + // command name + if (null !== $name) { + $output->writeln(sprintf('Command name: %s', $name)); + } else { + $output->writeln(array( + '', + 'Now, provide the name of the command as you type it in the console', + '(e.g. app:my-command)', + '', + )); + + $question = new Question($questionHelper->getQuestion('Command name', $name), $name); + $question->setValidator(function ($answer) { + if (empty($answer)) { + throw new \RuntimeException('The command name cannot be empty.'); + } + + return $answer; + }); + $question->setMaxAttempts(self::MAX_ATTEMPTS); + + $name = $questionHelper->ask($input, $output, $question); + $input->setArgument('name', $name); + } + + // summary and confirmation + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg-white', true), + '', + sprintf('You are going to generate a %s command inside %s bundle.', $name, $bundle), + )); + + $question = new Question($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); + if (!$questionHelper->ask($input, $output, $question)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + $bundle = $input->getArgument('bundle'); + $name = $input->getArgument('name'); + + try { + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + } catch (\Exception $e) { + $output->writeln(sprintf('Bundle "%s" does not exist.', $bundle)); + } + + $generator = $this->getGenerator($bundle); + $generator->generate($bundle, $name); + + $output->writeln(sprintf('Generated the %s command in %s', $name, $bundle->getName())); + $questionHelper->writeGeneratorSummary($output, array()); + } + + protected function createGenerator() + { + return new CommandGenerator($this->getContainer()->get('filesystem')); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateControllerCommand.php b/vendor/sensio/generator-bundle/Command/GenerateControllerCommand.php new file mode 100644 index 0000000..03a60d4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateControllerCommand.php @@ -0,0 +1,333 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; +use Sensio\Bundle\GeneratorBundle\Generator\ControllerGenerator; + +/** + * Generates controllers. + * + * @author Wouter J + */ +class GenerateControllerCommand extends GeneratorCommand +{ + /** + * @see Command + */ + public function configure() + { + $this + ->setName('generate:controller') + ->setDescription('Generates a controller') + ->setDefinition(array( + new InputOption('controller', '', InputOption::VALUE_REQUIRED, 'The name of the controller to create'), + new InputOption('route-format', '', InputOption::VALUE_REQUIRED, 'The format that is used for the routing (yml, xml, php, annotation)', 'annotation'), + new InputOption('template-format', '', InputOption::VALUE_REQUIRED, 'The format that is used for templating (twig, php)', 'twig'), + new InputOption('actions', '', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The actions in the controller'), + )) + ->setHelp(<<%command.name% command helps you generates new controllers +inside bundles. + +By default, the command interacts with the developer to tweak the generation. +Any passed option will be used as a default value for the interaction +(--controller is the only one needed if you follow the conventions): + +php %command.full_name% --controller=AcmeBlogBundle:Post + +If you want to disable any user interaction, use --no-interaction +but don't forget to pass all needed options: + +php %command.full_name% --controller=AcmeBlogBundle:Post --no-interaction + +Every generated file is based on a template. There are default templates but they can +be overridden by placing custom templates in one of the following locations, by order of priority: + +BUNDLE_PATH/Resources/SensioGeneratorBundle/skeleton/controller +APP_PATH/Resources/SensioGeneratorBundle/skeleton/controller + +You can check https://github.com/sensio/SensioGeneratorBundle/tree/master/Resources/skeleton +in order to know the file structure of the skeleton +EOT + ) + ; + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + + if ($input->isInteractive()) { + $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); + if (!$questionHelper->ask($input, $output, $question)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + if (null === $input->getOption('controller')) { + throw new \RuntimeException('The controller option must be provided.'); + } + + list($bundle, $controller) = $this->parseShortcutNotation($input->getOption('controller')); + if (is_string($bundle)) { + $bundle = Validators::validateBundleName($bundle); + + try { + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + } catch (\Exception $e) { + $output->writeln(sprintf('Bundle "%s" does not exist.', $bundle)); + } + } + + $questionHelper->writeSection($output, 'Controller generation'); + + $generator = $this->getGenerator($bundle); + $generator->generate($bundle, $controller, $input->getOption('route-format'), $input->getOption('template-format'), $this->parseActions($input->getOption('actions'))); + + $output->writeln('Generating the bundle code: OK'); + + $questionHelper->writeGeneratorSummary($output, array()); + } + + public function interact(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Symfony2 controller generator'); + + // namespace + $output->writeln(array( + '', + 'Every page, and even sections of a page, are rendered by a controller.', + 'This command helps you generate them easily.', + '', + 'First, you need to give the controller name you want to generate.', + 'You must use the shortcut notation like AcmeBlogBundle:Post', + '', + )); + + while (true) { + $question = new Question($questionHelper->getQuestion('Controller name', $input->getOption('controller')), $input->getOption('controller')); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateControllerName')); + $controller = $questionHelper->ask($input, $output, $question); + list($bundle, $controller) = $this->parseShortcutNotation($controller); + + try { + $b = $this->getContainer()->get('kernel')->getBundle($bundle); + + if (!file_exists($b->getPath().'/Controller/'.$controller.'Controller.php')) { + break; + } + + $output->writeln(sprintf('Controller "%s:%s" already exists.', $bundle, $controller)); + } catch (\Exception $e) { + $output->writeln(sprintf('Bundle "%s" does not exist.', $bundle)); + } + } + $input->setOption('controller', $bundle.':'.$controller); + + // routing format + $defaultFormat = (null !== $input->getOption('route-format') ? $input->getOption('route-format') : 'annotation'); + $output->writeln(array( + '', + 'Determine the format to use for the routing.', + '', + )); + $question = new Question($questionHelper->getQuestion('Routing format (php, xml, yml, annotation)', $defaultFormat), $defaultFormat); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat')); + $routeFormat = $questionHelper->ask($input, $output, $question); + $input->setOption('route-format', $routeFormat); + + // templating format + $validateTemplateFormat = function ($format) { + if (!in_array($format, array('twig', 'php'))) { + throw new \InvalidArgumentException(sprintf('The template format must be twig or php, "%s" given', $format)); + } + + return $format; + }; + + $defaultFormat = (null !== $input->getOption('template-format') ? $input->getOption('template-format') : 'twig'); + $output->writeln(array( + '', + 'Determine the format to use for templating.', + '', + )); + $question = new Question($questionHelper->getQuestion('Template format (twig, php)', $defaultFormat), $defaultFormat); + $question->setValidator($validateTemplateFormat); + + $templateFormat = $questionHelper->ask($input, $output, $question); + $input->setOption('template-format', $templateFormat); + + // actions + $input->setOption('actions', $this->addActions($input, $output, $questionHelper)); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg-white', true), + '', + sprintf('You are going to generate a "%s:%s" controller', $bundle, $controller), + sprintf('using the "%s" format for the routing and the "%s" format', $routeFormat, $templateFormat), + 'for templating', + )); + } + + public function addActions(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper) + { + $output->writeln(array( + '', + 'Instead of starting with a blank controller, you can add some actions now. An action', + 'is a PHP function or method that executes, for example, when a given route is matched.', + 'Actions should be suffixed by Action.', + '', + )); + + $templateNameValidator = function ($name) { + if ('default' == $name) { + return $name; + } + + if (2 != substr_count($name, ':')) { + throw new \InvalidArgumentException(sprintf('Template name "%s" does not have 2 colons', $name)); + } + + return $name; + }; + + $actions = $this->parseActions($input->getOption('actions')); + + while (true) { + // name + $output->writeln(''); + $question = new Question($questionHelper->getQuestion('New action name (press to stop adding actions)', null), null); + $question->setValidator(function ($name) use ($actions) { + if (null == $name) { + return $name; + } + + if (isset($actions[$name])) { + throw new \InvalidArgumentException(sprintf('Action "%s" is already defined', $name)); + } + + if ('Action' != substr($name, -6)) { + throw new \InvalidArgumentException(sprintf('Name "%s" is not suffixed by Action', $name)); + } + + return $name; + }); + + $actionName = $questionHelper->ask($input, $output, $question); + if (!$actionName) { + break; + } + + // route + $question = new Question($questionHelper->getQuestion('Action route', '/'.substr($actionName, 0, -6)), '/'.substr($actionName, 0, -6)); + $route = $questionHelper->ask($input, $output, $question); + $placeholders = $this->getPlaceholdersFromRoute($route); + + // template + $defaultTemplate = $input->getOption('controller').':'. + strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr(substr($actionName, 0, -6), '_', '.'))) + .'.html.'.$input->getOption('template-format'); + $question = new Question($questionHelper->getQuestion('Template name (optional)', $defaultTemplate), $defaultTemplate); + $template = $questionHelper->ask($input, $output, $question); + + // adding action + $actions[$actionName] = array( + 'name' => $actionName, + 'route' => $route, + 'placeholders' => $placeholders, + 'template' => $template, + ); + } + + return $actions; + } + + public function parseActions($actions) + { + if (empty($actions) || $actions !== array_values($actions)) { + return $actions; + } + + // '$actions' can be an array with just 1 element defining several actions + // separated by white spaces: $actions = array('... ... ...'); + if (1 === count($actions)) { + $actions = explode(' ', $actions[0]); + } + + $parsedActions = array(); + + foreach ($actions as $action) { + $data = explode(':', $action); + + // name + if (!isset($data[0])) { + throw new \InvalidArgumentException('An action must have a name'); + } + $name = array_shift($data); + + // route + $route = (isset($data[0]) && '' != $data[0]) ? array_shift($data) : '/'.substr($name, 0, -6); + if ($route) { + $placeholders = $this->getPlaceholdersFromRoute($route); + } else { + $placeholders = array(); + } + + // template + $template = (0 < count($data) && '' != $data[0]) ? implode(':', $data) : 'default'; + + $parsedActions[$name] = array( + 'name' => $name, + 'route' => $route, + 'placeholders' => $placeholders, + 'template' => $template, + ); + } + + return $parsedActions; + } + + public function getPlaceholdersFromRoute($route) + { + preg_match_all('/{(.*?)}/', $route, $placeholders); + $placeholders = $placeholders[1]; + + return $placeholders; + } + + public function parseShortcutNotation($shortcut) + { + $entity = str_replace('/', '\\', $shortcut); + + if (false === $pos = strpos($entity, ':')) { + throw new \InvalidArgumentException(sprintf('The controller name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Post)', $entity)); + } + + return array(substr($entity, 0, $pos), substr($entity, $pos + 1)); + } + + protected function createGenerator() + { + return new ControllerGenerator($this->getContainer()->get('filesystem')); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateDoctrineCommand.php b/vendor/sensio/generator-bundle/Command/GenerateDoctrineCommand.php new file mode 100644 index 0000000..11ac15a --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateDoctrineCommand.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory; + +abstract class GenerateDoctrineCommand extends GeneratorCommand +{ + public function isEnabled() + { + return class_exists('Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle'); + } + + protected function parseShortcutNotation($shortcut) + { + $entity = str_replace('/', '\\', $shortcut); + + if (false === $pos = strpos($entity, ':')) { + throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity)); + } + + return array(substr($entity, 0, $pos), substr($entity, $pos + 1)); + } + + protected function getEntityMetadata($entity) + { + $factory = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine')); + + return $factory->getClassMetadata($entity)->getMetadata(); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateDoctrineCrudCommand.php b/vendor/sensio/generator-bundle/Command/GenerateDoctrineCrudCommand.php new file mode 100644 index 0000000..ed7189b --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateDoctrineCrudCommand.php @@ -0,0 +1,346 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Sensio\Bundle\GeneratorBundle\Command\AutoComplete\EntitiesAutoCompleter; +use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator; +use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; + +/** + * Generates a CRUD for a Doctrine entity. + * + * @author Fabien Potencier + */ +class GenerateDoctrineCrudCommand extends GenerateDoctrineCommand +{ + private $formGenerator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('doctrine:generate:crud') + ->setAliases(array('generate:doctrine:crud')) + ->setDescription('Generates a CRUD based on a Doctrine entity') + ->setDefinition(array( + new InputArgument('entity', InputArgument::OPTIONAL, 'The entity class name to initialize (shortcut notation)'), + new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'), + new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'), + new InputOption('with-write', '', InputOption::VALUE_NONE, 'Whether or not to generate create, new and delete actions'), + new InputOption('format', '', InputOption::VALUE_REQUIRED, 'The format used for configuration files (php, xml, yml, or annotation)', 'annotation'), + new InputOption('overwrite', '', InputOption::VALUE_NONE, 'Overwrite any existing controller or form class when generating the CRUD contents'), + )) + ->setHelp(<<%command.name% command generates a CRUD based on a Doctrine entity. + +The default command only generates the list and show actions. + +php %command.full_name% --entity=AcmeBlogBundle:Post --route-prefix=post_admin + +Using the --with-write option allows to generate the new, edit and delete actions. + +php %command.full_name% doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin --with-write + +Every generated file is based on a template. There are default templates but they can be overridden by placing custom templates in one of the following locations, by order of priority: + +BUNDLE_PATH/Resources/SensioGeneratorBundle/skeleton/crud +APP_PATH/Resources/SensioGeneratorBundle/skeleton/crud + +And + +__bundle_path__/Resources/SensioGeneratorBundle/skeleton/form +__project_root__/app/Resources/SensioGeneratorBundle/skeleton/form + +You can check https://github.com/sensio/SensioGeneratorBundle/tree/master/Resources/skeleton +in order to know the file structure of the skeleton +EOT + ) + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + + if ($input->isInteractive()) { + $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); + if (!$questionHelper->ask($input, $output, $question)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + $entity = Validators::validateEntityName($input->getOption('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + $format = Validators::validateFormat($input->getOption('format')); + $prefix = $this->getRoutePrefix($input, $entity); + $withWrite = $input->getOption('with-write'); + $forceOverwrite = $input->getOption('overwrite'); + + $questionHelper->writeSection($output, 'CRUD generation'); + + try { + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Entity "%s" does not exist in the "%s" bundle. Create it with the "doctrine:generate:entity" command and then execute this command again.', $entity, $bundle)); + } + + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + + $generator = $this->getGenerator($bundle); + $generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite, $forceOverwrite); + + $output->writeln('Generating the CRUD code: OK'); + + $errors = array(); + $runner = $questionHelper->getRunner($output, $errors); + + // form + if ($withWrite) { + $this->generateForm($bundle, $entity, $metadata, $forceOverwrite); + $output->writeln('Generating the Form code: OK'); + } + + // routing + $output->write('Updating the routing: '); + if ('annotation' != $format) { + $runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format, $entity, $prefix)); + } else { + $runner($this->updateAnnotationRouting($bundle, $entity, $prefix)); + } + + $questionHelper->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Doctrine2 CRUD generator'); + + // namespace + $output->writeln(array( + '', + 'This command helps you generate CRUD controllers and templates.', + '', + 'First, give the name of the existing entity for which you want to generate a CRUD', + '(use the shortcut notation like AcmeBlogBundle:Post)', + '', + )); + + if ($input->hasArgument('entity') && $input->getArgument('entity') != '') { + $input->setOption('entity', $input->getArgument('entity')); + } + + $question = new Question($questionHelper->getQuestion('The Entity shortcut name', $input->getOption('entity')), $input->getOption('entity')); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName')); + + $autocompleter = new EntitiesAutoCompleter($this->getContainer()->get('doctrine')->getManager()); + $autocompleteEntities = $autocompleter->getSuggestions(); + $question->setAutocompleterValues($autocompleteEntities); + $entity = $questionHelper->ask($input, $output, $question); + + $input->setOption('entity', $entity); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + try { + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Entity "%s" does not exist in the "%s" bundle. You may have mistyped the bundle name or maybe the entity doesn\'t exist yet (create it first with the "doctrine:generate:entity" command).', $entity, $bundle)); + } + + // write? + $withWrite = $input->getOption('with-write') ?: false; + $output->writeln(array( + '', + 'By default, the generator creates two actions: list and show.', + 'You can also ask it to generate "write" actions: new, update, and delete.', + '', + )); + $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?', $withWrite), $withWrite); + + $withWrite = $questionHelper->ask($input, $output, $question); + $input->setOption('with-write', $withWrite); + + // format + $format = $input->getOption('format'); + $output->writeln(array( + '', + 'Determine the format to use for the generated CRUD.', + '', + )); + $question = new Question($questionHelper->getQuestion('Configuration format (yml, xml, php, or annotation)', $format), $format); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat')); + $format = $questionHelper->ask($input, $output, $question); + $input->setOption('format', $format); + + // route prefix + $prefix = $this->getRoutePrefix($input, $entity); + $output->writeln(array( + '', + 'Determine the routes prefix (all the routes will be "mounted" under this', + 'prefix: /prefix/, /prefix/new, ...).', + '', + )); + $prefix = $questionHelper->ask($input, $output, new Question($questionHelper->getQuestion('Routes prefix', '/'.$prefix), '/'.$prefix)); + $input->setOption('route-prefix', $prefix); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf('You are going to generate a CRUD controller for "%s:%s"', $bundle, $entity), + sprintf('using the "%s" format.', $format), + '', + )); + } + + /** + * Tries to generate forms if they don't exist yet and if we need write operations on entities. + */ + protected function generateForm($bundle, $entity, $metadata, $forceOverwrite = false) + { + $this->getFormGenerator($bundle)->generate($bundle, $entity, $metadata[0], $forceOverwrite); + } + + protected function updateRouting(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, BundleInterface $bundle, $format, $entity, $prefix) + { + $auto = true; + if ($input->isInteractive()) { + $question = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); + $auto = $questionHelper->ask($input, $output, $question); + } + + $output->write('Importing the CRUD routes: '); + $this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/'); + + // first, import the routing file from the bundle's main routing.yml file + $routing = new RoutingManipulator($bundle->getPath().'/Resources/config/routing.yml'); + try { + $ret = $auto ? $routing->addResource($bundle->getName(), $format, '/'.$prefix, 'routing/'.strtolower(str_replace('\\', '_', $entity))) : false; + } catch (\RuntimeException $exc) { + $ret = false; + } + + if (!$ret) { + $help = sprintf(" resource: \"@%s/Resources/config/routing/%s.%s\"\n", $bundle->getName(), strtolower(str_replace('\\', '_', $entity)), $format); + $help .= sprintf(" prefix: /%s\n", $prefix); + + return array( + '- Import the bundle\'s routing resource in the bundle routing file', + sprintf(' (%s).', $bundle->getPath().'/Resources/config/routing.yml'), + '', + sprintf(' %s:', $routing->getImportedResourceYamlKey($bundle->getName(), $prefix)), + $help, + '', + ); + } + + // second, import the bundle's routing.yml file from the application's routing.yml file + $routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml'); + try { + $ret = $auto ? $routing->addResource($bundle->getName(), 'yml') : false; + } catch (\RuntimeException $e) { + // the bundle is already imported form app's routing.yml file + $errorMessage = sprintf( + "\n\n[ERROR] The bundle's \"Resources/config/routing.yml\" file cannot be imported\n". + "from \"app/config/routing.yml\" because the \"%s\" bundle is\n". + "already imported. Make sure you are not using two different\n". + "configuration/routing formats in the same bundle because it won't work.\n", + $bundle->getName() + ); + $output->write($errorMessage); + $ret = true; + } catch (\Exception $e) { + $ret = false; + } + + if (!$ret) { + return array( + '- Import the bundle\'s routing.yml file in the application routing.yml file', + sprintf('# app/config/routing.yml'), + sprintf('%s:', $bundle->getName()), + sprintf(' resource: "@%s/Resources/config/routing.yml"', $bundle->getName()), + '', + '# ...', + '', + ); + } + } + + protected function updateAnnotationRouting(BundleInterface $bundle, $entity, $prefix) + { + $rootDir = $this->getContainer()->getParameter('kernel.root_dir'); + + $routing = new RoutingManipulator($rootDir.'/config/routing.yml'); + + if (!$routing->hasResourceInAnnotation($bundle->getName())) { + $parts = explode('\\', $entity); + $controller = array_pop($parts); + + $ret = $routing->addAnnotationController($bundle->getName(), $controller); + } + } + + protected function getRoutePrefix(InputInterface $input, $entity) + { + $prefix = $input->getOption('route-prefix') ?: strtolower(str_replace(array('\\', '/'), '_', $entity)); + + if ($prefix && '/' === $prefix[0]) { + $prefix = substr($prefix, 1); + } + + return $prefix; + } + + protected function createGenerator($bundle = null) + { + return new DoctrineCrudGenerator( + $this->getContainer()->get('filesystem'), + $this->getContainer()->getParameter('kernel.root_dir') + ); + } + + protected function getFormGenerator($bundle = null) + { + if (null === $this->formGenerator) { + $this->formGenerator = new DoctrineFormGenerator($this->getContainer()->get('filesystem')); + $this->formGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); + } + + return $this->formGenerator; + } + + public function setFormGenerator(DoctrineFormGenerator $formGenerator) + { + $this->formGenerator = $formGenerator; + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateDoctrineEntityCommand.php b/vendor/sensio/generator-bundle/Command/GenerateDoctrineEntityCommand.php new file mode 100644 index 0000000..b23a665 --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateDoctrineEntityCommand.php @@ -0,0 +1,399 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Console\Question\Question; +use Doctrine\DBAL\Types\Type; + +/** + * Initializes a Doctrine entity inside a bundle. + * + * @author Fabien Potencier + */ +class GenerateDoctrineEntityCommand extends GenerateDoctrineCommand +{ + protected function configure() + { + $this + ->setName('doctrine:generate:entity') + ->setAliases(array('generate:doctrine:entity')) + ->setDescription('Generates a new Doctrine entity inside a bundle') + ->addOption('entity', null, InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)') + ->addOption('fields', null, InputOption::VALUE_REQUIRED, 'The fields to create with the new entity') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation') + ->setHelp(<<%command.name% task generates a new Doctrine +entity inside a bundle: + +php %command.full_name% --entity=AcmeBlogBundle:Blog/Post + +The above command would initialize a new entity in the following entity +namespace Acme\BlogBundle\Entity\Blog\Post. + +You can also optionally specify the fields you want to generate in the new +entity: + +php %command.full_name% doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --fields="title:string(255) body:text" + +By default, the command uses annotations for the mapping information; change it +with --format: + +php %command.full_name% --entity=AcmeBlogBundle:Blog/Post --format=yml + +To deactivate the interaction mode, simply use the --no-interaction option +without forgetting to pass all needed options: + +php %command.full_name% --entity=AcmeBlogBundle:Blog/Post --format=annotation --fields="title:string(255) body:text" --no-interaction + +This also has support for passing field specific attributes: + +php %command.full_name% --entity=AcmeBlogBundle:Blog/Post --format=annotation --fields="title:string(length=255 nullable=true unique=true) body:text ranking:decimal(precision:10 scale:0)" --no-interaction +EOT + ); + } + + /** + * @throws \InvalidArgumentException When the bundle doesn't end with Bundle (Example: "Bundle/MySampleBundle") + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + + $entity = Validators::validateEntityName($input->getOption('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + $format = Validators::validateFormat($input->getOption('format')); + $fields = $this->parseFields($input->getOption('fields')); + + $questionHelper->writeSection($output, 'Entity generation'); + + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + + /** @var DoctrineEntityGenerator $generator */ + $generator = $this->getGenerator(); + $generatorResult = $generator->generate($bundle, $entity, $format, array_values($fields)); + + $output->writeln(sprintf( + '> Generating entity class %s: OK!', + $this->makePathRelative($generatorResult->getEntityPath()) + )); + $output->writeln(sprintf( + '> Generating repository class %s: OK!', + $this->makePathRelative($generatorResult->getRepositoryPath()) + )); + if ($generatorResult->getMappingPath()) { + $output->writeln(sprintf( + '> Generating mapping file %s: OK!', + $this->makePathRelative($generatorResult->getMappingPath()) + )); + } + + $questionHelper->writeGeneratorSummary($output, array()); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Doctrine2 entity generator'); + + // namespace + $output->writeln(array( + '', + 'This command helps you generate Doctrine2 entities.', + '', + 'First, you need to give the entity name you want to generate.', + 'You must use the shortcut notation like AcmeBlogBundle:Post.', + '', + )); + + $bundleNames = array_keys($this->getContainer()->get('kernel')->getBundles()); + + while (true) { + $question = new Question($questionHelper->getQuestion('The Entity shortcut name', $input->getOption('entity')), $input->getOption('entity')); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName')); + $question->setAutocompleterValues($bundleNames); + $entity = $questionHelper->ask($input, $output, $question); + + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + // check reserved words + if ($this->getGenerator()->isReservedKeyword($entity)) { + $output->writeln(sprintf(' "%s" is a reserved word.', $entity)); + continue; + } + + try { + $b = $this->getContainer()->get('kernel')->getBundle($bundle); + + if (!file_exists($b->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php')) { + break; + } + + $output->writeln(sprintf('Entity "%s:%s" already exists.', $bundle, $entity)); + } catch (\Exception $e) { + $output->writeln(sprintf('Bundle "%s" does not exist.', $bundle)); + } + } + $input->setOption('entity', $bundle.':'.$entity); + + // format + $output->writeln(array( + '', + 'Determine the format to use for the mapping information.', + '', + )); + + $formats = array('yml', 'xml', 'php', 'annotation'); + + $question = new Question($questionHelper->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), $input->getOption('format')); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat')); + $question->setAutocompleterValues($formats); + $format = $questionHelper->ask($input, $output, $question); + $input->setOption('format', $format); + + // fields + $input->setOption('fields', $this->addFields($input, $output, $questionHelper)); + } + + private function parseFields($input) + { + if (is_array($input)) { + return $input; + } + + $fields = array(); + foreach (preg_split('{(?:\([^\(]*\))(*SKIP)(*F)|\s+}', $input) as $value) { + $elements = explode(':', $value); + $name = $elements[0]; + $fieldAttributes = array(); + if (strlen($name)) { + $fieldAttributes['fieldName'] = $name; + $type = isset($elements[1]) ? $elements[1] : 'string'; + preg_match_all('{(.*)\((.*)\)}', $type, $matches); + $fieldAttributes['type'] = isset($matches[1][0]) ? $matches[1][0] : $type; + $length = null; + if ('string' === $fieldAttributes['type']) { + $fieldAttributes['length'] = $length; + } + if (isset($matches[2][0]) && $length = $matches[2][0]) { + $attributesFound = array(); + if (false !== strpos($length, '=')) { + preg_match_all('{([^,= ]+)=([^,= ]+)}', $length, $result); + $attributesFound = array_combine($result[1], $result[2]); + } else { + $fieldAttributes['length'] = $length; + } + $fieldAttributes = array_merge($fieldAttributes, $attributesFound); + foreach (array('length', 'precision', 'scale') as $intAttribute) { + if (isset($fieldAttributes[$intAttribute])) { + $fieldAttributes[$intAttribute] = (int) $fieldAttributes[$intAttribute]; + } + } + foreach (array('nullable', 'unique') as $boolAttribute) { + if (isset($fieldAttributes[$boolAttribute])) { + $fieldAttributes[$boolAttribute] = (bool) $fieldAttributes[$boolAttribute]; + } + } + } + + $fields[$name] = $fieldAttributes; + } + } + + return $fields; + } + + private function addFields(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper) + { + $fields = $this->parseFields($input->getOption('fields')); + $output->writeln(array( + '', + 'Instead of starting with a blank entity, you can add some fields now.', + 'Note that the primary key will be added automatically (named id).', + '', + )); + $output->write('Available types: '); + + $types = array_keys(Type::getTypesMap()); + $count = 20; + foreach ($types as $i => $type) { + if ($count > 50) { + $count = 0; + $output->writeln(''); + } + $count += strlen($type); + $output->write(sprintf('%s', $type)); + if (count($types) != $i + 1) { + $output->write(', '); + } else { + $output->write('.'); + } + } + $output->writeln(''); + + $fieldValidator = function ($type) use ($types) { + if (!in_array($type, $types)) { + throw new \InvalidArgumentException(sprintf('Invalid type "%s".', $type)); + } + + return $type; + }; + + $lengthValidator = function ($length) { + if (!$length) { + return $length; + } + + $result = filter_var($length, FILTER_VALIDATE_INT, array( + 'options' => array('min_range' => 1), + )); + + if (false === $result) { + throw new \InvalidArgumentException(sprintf('Invalid length "%s".', $length)); + } + + return $length; + }; + + $boolValidator = function ($value) { + if (null === $valueAsBool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { + throw new \InvalidArgumentException(sprintf('Invalid bool value "%s".', $value)); + } + + return $valueAsBool; + }; + + $precisionValidator = function ($precision) { + if (!$precision) { + return $precision; + } + + $result = filter_var($precision, FILTER_VALIDATE_INT, array( + 'options' => array('min_range' => 1, 'max_range' => 65), + )); + + if (false === $result) { + throw new \InvalidArgumentException(sprintf('Invalid precision "%s".', $precision)); + } + + return $precision; + }; + + $scaleValidator = function ($scale) { + if (!$scale) { + return $scale; + } + + $result = filter_var($scale, FILTER_VALIDATE_INT, array( + 'options' => array('min_range' => 0, 'max_range' => 30), + )); + + if (false === $result) { + throw new \InvalidArgumentException(sprintf('Invalid scale "%s".', $scale)); + } + + return $scale; + }; + + while (true) { + $output->writeln(''); + $generator = $this->getGenerator(); + $question = new Question($questionHelper->getQuestion('New field name (press to stop adding fields)', null), null); + $question->setValidator(function ($name) use ($fields, $generator) { + if (isset($fields[$name]) || 'id' == $name) { + throw new \InvalidArgumentException(sprintf('Field "%s" is already defined.', $name)); + } + + // check reserved words + if ($generator->isReservedKeyword($name)) { + throw new \InvalidArgumentException(sprintf('Name "%s" is a reserved word.', $name)); + } + + // check for valid PHP variable name + if (!is_null($name) && !$generator->isValidPhpVariableName($name)) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid PHP variable name.', $name)); + } + + return $name; + }); + + $columnName = $questionHelper->ask($input, $output, $question); + if (!$columnName) { + break; + } + + $defaultType = 'string'; + + // try to guess the type by the column name prefix/suffix + if (substr($columnName, -3) == '_at') { + $defaultType = 'datetime'; + } elseif (substr($columnName, -3) == '_id') { + $defaultType = 'integer'; + } elseif (substr($columnName, 0, 3) == 'is_') { + $defaultType = 'boolean'; + } elseif (substr($columnName, 0, 4) == 'has_') { + $defaultType = 'boolean'; + } + + $question = new Question($questionHelper->getQuestion('Field type', $defaultType), $defaultType); + $question->setValidator($fieldValidator); + $question->setAutocompleterValues($types); + $type = $questionHelper->ask($input, $output, $question); + + $data = array('columnName' => $columnName, 'fieldName' => lcfirst(Container::camelize($columnName)), 'type' => $type); + + if ($type == 'string') { + $question = new Question($questionHelper->getQuestion('Field length', 255), 255); + $question->setValidator($lengthValidator); + $data['length'] = $questionHelper->ask($input, $output, $question); + } elseif ('decimal' === $type) { + // 10 is the default value given in \Doctrine\DBAL\Schema\Column::$_precision + $question = new Question($questionHelper->getQuestion('Precision', 10), 10); + $question->setValidator($precisionValidator); + $data['precision'] = $questionHelper->ask($input, $output, $question); + + // 0 is the default value given in \Doctrine\DBAL\Schema\Column::$_scale + $question = new Question($questionHelper->getQuestion('Scale', 0), 0); + $question->setValidator($scaleValidator); + $data['scale'] = $questionHelper->ask($input, $output, $question); + } + + $question = new Question($questionHelper->getQuestion('Is nullable', 'false'), false); + $question->setValidator($boolValidator); + $question->setAutocompleterValues(array('true', 'false')); + if ($nullable = $questionHelper->ask($input, $output, $question)) { + $data['nullable'] = $nullable; + } + + $question = new Question($questionHelper->getQuestion('Unique', 'false'), false); + $question->setValidator($boolValidator); + $question->setAutocompleterValues(array('true', 'false')); + if ($unique = $questionHelper->ask($input, $output, $question)) { + $data['unique'] = $unique; + } + + $fields[$columnName] = $data; + } + + return $fields; + } + + protected function createGenerator() + { + return new DoctrineEntityGenerator($this->getContainer()->get('filesystem'), $this->getContainer()->get('doctrine')); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GenerateDoctrineFormCommand.php b/vendor/sensio/generator-bundle/Command/GenerateDoctrineFormCommand.php new file mode 100644 index 0000000..77c8726 --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GenerateDoctrineFormCommand.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Command\Command; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator; + +/** + * Generates a form type class for a given Doctrine entity. + * + * @author Fabien Potencier + * @author Hugo Hamon + */ +class GenerateDoctrineFormCommand extends GenerateDoctrineCommand +{ + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('doctrine:generate:form') + ->setAliases(array('generate:doctrine:form')) + ->setDescription('Generates a form type class based on a Doctrine entity') + ->setDefinition(array( + new InputArgument('entity', InputArgument::REQUIRED, 'The entity class name to initialize (shortcut notation)'), + )) + ->setHelp(<<%command.name% command generates a form class based on a Doctrine entity. + +php %command.full_name% AcmeBlogBundle:Post + +Every generated file is based on a template. There are default templates but they can be overriden by placing custom templates in one of the following locations, by order of priority: + +BUNDLE_PATH/Resources/SensioGeneratorBundle/skeleton/form +APP_PATH/Resources/SensioGeneratorBundle/skeleton/form + +You can check https://github.com/sensio/SensioGeneratorBundle/tree/master/Resources/skeleton +in order to know the file structure of the skeleton +EOT + ) + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $entity = Validators::validateEntityName($input->getArgument('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + $bundle = $this->getApplication()->getKernel()->getBundle($bundle); + $generator = $this->getGenerator($bundle); + + $generator->generate($bundle, $entity, $metadata[0]); + + $output->writeln(sprintf( + 'The new %s.php class file has been created under %s.', + $generator->getClassName(), + $generator->getClassPath() + )); + } + + protected function createGenerator() + { + return new DoctrineFormGenerator($this->getContainer()->get('filesystem')); + } +} diff --git a/vendor/sensio/generator-bundle/Command/GeneratorCommand.php b/vendor/sensio/generator-bundle/Command/GeneratorCommand.php new file mode 100644 index 0000000..39ff4ef --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/GeneratorCommand.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Sensio\Bundle\GeneratorBundle\Generator\Generator; +use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; + +/** + * Base class for generator commands. + * + * @author Fabien Potencier + */ +abstract class GeneratorCommand extends ContainerAwareCommand +{ + private $generator; + + // only useful for unit tests + public function setGenerator(Generator $generator) + { + $this->generator = $generator; + } + + abstract protected function createGenerator(); + + protected function getGenerator(BundleInterface $bundle = null) + { + if (null === $this->generator) { + $this->generator = $this->createGenerator(); + $this->generator->setSkeletonDirs($this->getSkeletonDirs($bundle)); + } + + return $this->generator; + } + + protected function getSkeletonDirs(BundleInterface $bundle = null) + { + $skeletonDirs = array(); + + if (isset($bundle) && is_dir($dir = $bundle->getPath().'/Resources/SensioGeneratorBundle/skeleton')) { + $skeletonDirs[] = $dir; + } + + if (is_dir($dir = $this->getContainer()->get('kernel')->getRootdir().'/Resources/SensioGeneratorBundle/skeleton')) { + $skeletonDirs[] = $dir; + } + + $skeletonDirs[] = __DIR__.'/../Resources/skeleton'; + $skeletonDirs[] = __DIR__.'/../Resources'; + + return $skeletonDirs; + } + + protected function getQuestionHelper() + { + $question = $this->getHelperSet()->get('question'); + if (!$question || get_class($question) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper') { + $this->getHelperSet()->set($question = new QuestionHelper()); + } + + return $question; + } + + /** + * Tries to make a path relative to the project, which prints nicer. + * + * @param string $absolutePath + * + * @return string + */ + protected function makePathRelative($absolutePath) + { + $projectRootDir = dirname($this->getContainer()->getParameter('kernel.root_dir')); + + return str_replace($projectRootDir.'/', '', realpath($absolutePath) ?: $absolutePath); + } +} diff --git a/vendor/sensio/generator-bundle/Command/Helper/QuestionHelper.php b/vendor/sensio/generator-bundle/Command/Helper/QuestionHelper.php new file mode 100644 index 0000000..b4d3e59 --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/Helper/QuestionHelper.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command\Helper; + +use Symfony\Component\Console\Helper\QuestionHelper as BaseQuestionHelper; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Generates bundles. + * + * @author Fabien Potencier + */ +class QuestionHelper extends BaseQuestionHelper +{ + public function writeGeneratorSummary(OutputInterface $output, $errors) + { + if (!$errors) { + $this->writeSection($output, 'Everything is OK! Now get to work :).'); + } else { + $this->writeSection($output, array( + 'The command was not able to configure everything automatically.', + 'You\'ll need to make the following changes manually.', + ), 'error'); + + $output->writeln($errors); + } + } + + public function getRunner(OutputInterface $output, &$errors) + { + $runner = function ($err) use ($output, &$errors) { + if ($err) { + $output->writeln('FAILED'); + $errors = array_merge($errors, $err); + } else { + $output->writeln('OK'); + } + }; + + return $runner; + } + + public function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white') + { + $output->writeln(array( + '', + $this->getHelperSet()->get('formatter')->formatBlock($text, $style, true), + '', + )); + } + + public function getQuestion($question, $default, $sep = ':') + { + return $default ? sprintf('%s [%s]%s ', $question, $default, $sep) : sprintf('%s%s ', $question, $sep); + } +} diff --git a/vendor/sensio/generator-bundle/Command/Validators.php b/vendor/sensio/generator-bundle/Command/Validators.php new file mode 100644 index 0000000..af0ee83 --- /dev/null +++ b/vendor/sensio/generator-bundle/Command/Validators.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Command; + +/** + * Validator functions. + * + * @author Fabien Potencier + */ +class Validators +{ + /** + * Validates that the given namespace (e.g. Acme\FooBundle) is a valid format. + * + * If $requireVendorNamespace is true, then we require you to have a vendor + * namespace (e.g. Acme). + * + * @param $namespace + * @param bool $requireVendorNamespace + * + * @return string + */ + public static function validateBundleNamespace($namespace, $requireVendorNamespace = true) + { + if (!preg_match('/Bundle$/', $namespace)) { + throw new \InvalidArgumentException('The namespace must end with Bundle.'); + } + + $namespace = strtr($namespace, '/', '\\'); + if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\?)+$/', $namespace)) { + throw new \InvalidArgumentException('The namespace contains invalid characters.'); + } + + // validate reserved keywords + $reserved = self::getReservedWords(); + foreach (explode('\\', $namespace) as $word) { + if (in_array(strtolower($word), $reserved)) { + throw new \InvalidArgumentException(sprintf('The namespace cannot contain PHP reserved words ("%s").', $word)); + } + } + + // validate that the namespace is at least one level deep + if ($requireVendorNamespace && false === strpos($namespace, '\\')) { + $msg = array(); + $msg[] = sprintf('The namespace must contain a vendor namespace (e.g. "VendorName\%s" instead of simply "%s").', $namespace, $namespace); + $msg[] = 'If you\'ve specified a vendor namespace, did you forget to surround it with quotes (init:bundle "Acme\BlogBundle")?'; + + throw new \InvalidArgumentException(implode("\n\n", $msg)); + } + + return $namespace; + } + + public static function validateBundleName($bundle) + { + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $bundle)) { + throw new \InvalidArgumentException(sprintf('The bundle name %s contains invalid characters.', $bundle)); + } + + if (!preg_match('/Bundle$/', $bundle)) { + throw new \InvalidArgumentException('The bundle name must end with Bundle.'); + } + + return $bundle; + } + + public static function validateControllerName($controller) + { + try { + self::validateEntityName($controller); + } catch (\InvalidArgumentException $e) { + throw new \InvalidArgumentException( + sprintf( + 'The controller name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Post)', + $controller + ) + ); + } + + return $controller; + } + + public static function validateFormat($format) + { + if (!$format) { + throw new \RuntimeException('Please enter a configuration format.'); + } + + $format = strtolower($format); + + // in case they typed "yaml", but ok with that + if ($format == 'yaml') { + $format = 'yml'; + } + + if (!in_array($format, array('php', 'xml', 'yml', 'annotation'))) { + throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format)); + } + + return $format; + } + + /** + * Performs basic checks in entity name. + * + * @param string $entity + * + * @return string + * + * @throws \InvalidArgumentException + */ + public static function validateEntityName($entity) + { + if (!preg_match('{^[a-zA-Z_\x7f-\xff]+:[a-zA-Z0-9_\x7f-\xff\\\/]+$}', $entity)) { + throw new \InvalidArgumentException(sprintf('The entity name isn\'t valid ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity)); + } + + return $entity; + } + + public static function getReservedWords() + { + return array( + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'do', + 'else', + 'elseif', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'extends', + 'final', + 'finally', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'interface', + 'instanceof', + 'insteadof', + 'namespace', + 'new', + 'or', + 'private', + 'protected', + 'public', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'use', + 'var', + 'while', + 'xor', + 'yield', + '__CLASS__', + '__DIR__', + '__FILE__', + '__LINE__', + '__FUNCTION__', + '__METHOD__', + '__NAMESPACE__', + '__TRAIT__', + '__halt_compiler', + 'die', + 'echo', + 'empty', + 'exit', + 'eval', + 'include', + 'include_once', + 'isset', + 'list', + 'require', + 'require_once', + 'return', + 'print', + 'unset', + ); + } +} diff --git a/vendor/sensio/generator-bundle/Generator/BundleGenerator.php b/vendor/sensio/generator-bundle/Generator/BundleGenerator.php new file mode 100644 index 0000000..3d821da --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/BundleGenerator.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Sensio\Bundle\GeneratorBundle\Model\Bundle; +use Symfony\Component\Filesystem\Filesystem; + +/** + * Generates a bundle. + * + * @author Fabien Potencier + */ +class BundleGenerator extends Generator +{ + private $filesystem; + + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function generateBundle(Bundle $bundle) + { + $dir = $bundle->getTargetDirectory(); + + if (file_exists($dir)) { + if (!is_dir($dir)) { + throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" exists but is a file.', realpath($dir))); + } + $files = scandir($dir); + if ($files != array('.', '..')) { + throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not empty.', realpath($dir))); + } + if (!is_writable($dir)) { + throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not writable.', realpath($dir))); + } + } + + $parameters = array( + 'namespace' => $bundle->getNamespace(), + 'bundle' => $bundle->getName(), + 'format' => $bundle->getConfigurationFormat(), + 'bundle_basename' => $bundle->getBasename(), + 'extension_alias' => $bundle->getExtensionAlias(), + ); + + $this->renderFile('bundle/Bundle.php.twig', $dir.'/'.$bundle->getName().'.php', $parameters); + if ($bundle->shouldGenerateDependencyInjectionDirectory()) { + $this->renderFile('bundle/Extension.php.twig', $dir.'/DependencyInjection/'.$bundle->getBasename().'Extension.php', $parameters); + $this->renderFile('bundle/Configuration.php.twig', $dir.'/DependencyInjection/Configuration.php', $parameters); + } + $this->renderFile('bundle/DefaultController.php.twig', $dir.'/Controller/DefaultController.php', $parameters); + $this->renderFile('bundle/DefaultControllerTest.php.twig', $dir.'/Tests/Controller/DefaultControllerTest.php', $parameters); + $this->renderFile('bundle/index.html.twig.twig', $dir.'/Resources/views/Default/index.html.twig', $parameters); + + // render the services.yml/xml file + $servicesFilename = $bundle->getServicesConfigurationFilename(); + $this->renderFile( + sprintf('bundle/%s.twig', $servicesFilename), + $dir.'/Resources/config/'.$servicesFilename, $parameters + ); + + if ($routingFilename = $bundle->getRoutingConfigurationFilename()) { + $this->renderFile( + sprintf('bundle/%s.twig', $routingFilename), + $dir.'/Resources/config/'.$routingFilename, $parameters + ); + } + } +} diff --git a/vendor/sensio/generator-bundle/Generator/CommandGenerator.php b/vendor/sensio/generator-bundle/Generator/CommandGenerator.php new file mode 100644 index 0000000..66a3f9f --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/CommandGenerator.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * Generates a Command inside a bundle. + * + * @author Javier Eguiluz + */ +class CommandGenerator extends Generator +{ + private $filesystem; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function generate(BundleInterface $bundle, $name) + { + $bundleDir = $bundle->getPath(); + $commandDir = $bundleDir.'/Command'; + $this->filesystem->mkdir($commandDir); + + $commandClassName = $this->classify($name).'Command'; + $commandFile = $commandDir.'/'.$commandClassName.'.php'; + if ($this->filesystem->exists($commandFile)) { + throw new \RuntimeException(sprintf('Command "%s" already exists', $name)); + } + + $parameters = array( + 'namespace' => $bundle->getNamespace(), + 'class_name' => $commandClassName, + 'name' => $name, + ); + + $this->renderFile('command/Command.php.twig', $commandFile, $parameters); + } + + /** + * Transforms the given string to a new string valid as a PHP class name + * ('app:my-project' -> 'AppMyProject', 'app:namespace:name' -> 'AppNamespaceName'). + * + * @param string $string + * + * @return The string transformed to be a valid PHP class name + */ + public function classify($string) + { + return str_replace(' ', '', ucwords(strtr($string, '_-:', ' '))); + } +} diff --git a/vendor/sensio/generator-bundle/Generator/ControllerGenerator.php b/vendor/sensio/generator-bundle/Generator/ControllerGenerator.php new file mode 100644 index 0000000..2863e14 --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/ControllerGenerator.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * Generates a Controller inside a bundle. + * + * @author Wouter J + */ +class ControllerGenerator extends Generator +{ + private $filesystem; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function generate(BundleInterface $bundle, $controller, $routeFormat, $templateFormat, array $actions = array()) + { + $dir = $bundle->getPath(); + $controllerFile = $dir.'/Controller/'.$controller.'Controller.php'; + if (file_exists($controllerFile)) { + throw new \RuntimeException(sprintf('Controller "%s" already exists', $controller)); + } + + $parameters = array( + 'namespace' => $bundle->getNamespace(), + 'bundle' => $bundle->getName(), + 'format' => array( + 'routing' => $routeFormat, + 'templating' => $templateFormat, + ), + 'controller' => $controller, + ); + + foreach ($actions as $i => $action) { + // get the action name without the suffix Action (for the template logical name) + $actions[$i]['basename'] = substr($action['name'], 0, -6); + $params = $parameters; + $params['action'] = $actions[$i]; + + // create a template + $template = $actions[$i]['template']; + if ('default' == $template) { + @trigger_error('The use of the "default" keyword is deprecated. Use the real template name instead.', E_USER_DEPRECATED); + $template = $bundle->getName().':'.$controller.':'. + strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr(substr($actionName, 0, -6), '_', '.'))) + .'.html.'.$templateFormat; + } + + if ('twig' == $templateFormat) { + $this->renderFile('controller/Template.html.twig.twig', $dir.'/Resources/views/'.$this->parseTemplatePath($template), $params); + } else { + $this->renderFile('controller/Template.html.php.twig', $dir.'/Resources/views/'.$this->parseTemplatePath($template), $params); + } + + $this->generateRouting($bundle, $controller, $actions[$i], $routeFormat); + } + + $parameters['actions'] = $actions; + + $this->renderFile('controller/Controller.php.twig', $controllerFile, $parameters); + $this->renderFile('controller/ControllerTest.php.twig', $dir.'/Tests/Controller/'.$controller.'ControllerTest.php', $parameters); + } + + public function generateRouting(BundleInterface $bundle, $controller, array $action, $format) + { + // annotation is generated in the templates + if ('annotation' == $format) { + return true; + } + + $file = $bundle->getPath().'/Resources/config/routing.'.$format; + if (file_exists($file)) { + $content = file_get_contents($file); + } elseif (!is_dir($dir = $bundle->getPath().'/Resources/config')) { + mkdir($dir); + } + + $controller = $bundle->getName().':'.$controller.':'.$action['basename']; + $name = strtolower(preg_replace('/([A-Z])/', '_\\1', $action['basename'])); + + if ('yml' == $format) { + // yaml + if (!isset($content)) { + $content = ''; + } + + $content .= sprintf( + "\n%s:\n path: %s\n defaults: { _controller: %s }\n", + $name, + $action['route'], + $controller + ); + } elseif ('xml' == $format) { + // xml + if (!isset($content)) { + // new file + $content = << + + +EOT; + } + + $sxe = simplexml_load_string($content); + + $route = $sxe->addChild('route'); + $route->addAttribute('id', $name); + $route->addAttribute('path', $action['route']); + + $default = $route->addChild('default', $controller); + $default->addAttribute('key', '_controller'); + + $dom = new \DOMDocument('1.0'); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML($sxe->asXML()); + $content = $dom->saveXML(); + } elseif ('php' == $format) { + // php + if (isset($content)) { + // edit current file + $pointer = strpos($content, 'return'); + if (!preg_match('/(\$[^ ]*).*?new RouteCollection\(\)/', $content, $collection) || false === $pointer) { + throw new \RunTimeException('Routing.php file is not correct, please initialize RouteCollection.'); + } + + $content = substr($content, 0, $pointer); + $content .= sprintf("%s->add('%s', new Route('%s', array(", $collection[1], $name, $action['route']); + $content .= sprintf("\n '_controller' => '%s',", $controller); + $content .= "\n)));\n\nreturn ".$collection[1].';'; + } else { + // new file + $content = <<add('%s', new Route('%s', array(", $name, $action['route']); + $content .= sprintf("\n '_controller' => '%s',", $controller); + $content .= "\n)));\n\nreturn \$collection;"; + } + } + + $flink = fopen($file, 'w'); + if ($flink) { + $write = fwrite($flink, $content); + + if ($write) { + fclose($flink); + } else { + throw new \RunTimeException(sprintf('We cannot write into file "%s", has that file the correct access level?', $file)); + } + } else { + throw new \RunTimeException(sprintf('Problems with generating file "%s", did you gave write access to that directory?', $file)); + } + } + + protected function parseTemplatePath($template) + { + $data = $this->parseLogicalTemplateName($template); + + return $data['controller'].'/'.$data['template']; + } + + protected function parseLogicalTemplateName($logicalName, $part = '') + { + if (2 !== substr_count($logicalName, ':')) { + throw new \RuntimeException(sprintf('The given template name ("%s") is not correct (it must contain two colons).', $logicalName)); + } + + $data = array(); + + list($data['bundle'], $data['controller'], $data['template']) = explode(':', $logicalName); + + return ($part ? $data[$part] : $data); + } +} diff --git a/vendor/sensio/generator-bundle/Generator/DoctrineCrudGenerator.php b/vendor/sensio/generator-bundle/Generator/DoctrineCrudGenerator.php new file mode 100644 index 0000000..1698d0d --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/DoctrineCrudGenerator.php @@ -0,0 +1,310 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\Common\Inflector\Inflector; + +/** + * Generates a CRUD controller. + * + * @author Fabien Potencier + */ +class DoctrineCrudGenerator extends Generator +{ + protected $filesystem; + protected $rootDir; + protected $routePrefix; + protected $routeNamePrefix; + protected $bundle; + protected $entity; + protected $entitySingularized; + protected $entityPluralized; + protected $metadata; + protected $format; + protected $actions; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + * @param string $rootDir The root dir + */ + public function __construct(Filesystem $filesystem, $rootDir) + { + $this->filesystem = $filesystem; + $this->rootDir = $rootDir; + } + + /** + * Generate the CRUD controller. + * + * @param BundleInterface $bundle A bundle object + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity class metadata + * @param string $format The configuration format (xml, yaml, annotation) + * @param string $routePrefix The route name prefix + * @param array $needWriteActions Whether or not to generate write actions + * + * @throws \RuntimeException + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $format, $routePrefix, $needWriteActions, $forceOverwrite) + { + $this->routePrefix = $routePrefix; + $this->routeNamePrefix = self::getRouteNamePrefix($routePrefix); + $this->actions = $needWriteActions ? array('index', 'show', 'new', 'edit', 'delete') : array('index', 'show'); + + if (count($metadata->identifier) != 1) { + throw new \RuntimeException('The CRUD generator does not support entity classes with multiple or no primary keys.'); + } + + $this->entity = $entity; + $this->entitySingularized = lcfirst(Inflector::singularize($entity)); + $this->entityPluralized = lcfirst(Inflector::pluralize($entity)); + $this->bundle = $bundle; + $this->metadata = $metadata; + $this->setFormat($format); + + $this->generateControllerClass($forceOverwrite); + + $dir = sprintf('%s/Resources/views/%s', $this->rootDir, str_replace('\\', '/', strtolower($this->entity))); + + if (!file_exists($dir)) { + $this->filesystem->mkdir($dir, 0777); + } + + $this->generateIndexView($dir); + + if (in_array('show', $this->actions)) { + $this->generateShowView($dir); + } + + if (in_array('new', $this->actions)) { + $this->generateNewView($dir); + } + + if (in_array('edit', $this->actions)) { + $this->generateEditView($dir); + } + + $this->generateTestClass(); + $this->generateConfiguration(); + } + + /** + * Sets the configuration format. + * + * @param string $format The configuration format + */ + protected function setFormat($format) + { + switch ($format) { + case 'yml': + case 'xml': + case 'php': + case 'annotation': + $this->format = $format; + break; + default: + $this->format = 'yml'; + break; + } + } + + /** + * Generates the routing configuration. + */ + protected function generateConfiguration() + { + if (!in_array($this->format, array('yml', 'xml', 'php'))) { + return; + } + + $target = sprintf( + '%s/Resources/config/routing/%s.%s', + $this->bundle->getPath(), + strtolower(str_replace('\\', '_', $this->entity)), + $this->format + ); + + $this->renderFile('crud/config/routing.'.$this->format.'.twig', $target, array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + )); + } + + /** + * Generates the controller class only. + */ + protected function generateControllerClass($forceOverwrite) + { + $dir = $this->bundle->getPath(); + + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); + $entityNamespace = implode('\\', $parts); + + $target = sprintf( + '%s/Controller/%s/%sController.php', + $dir, + str_replace('\\', '/', $entityNamespace), + $entityClass + ); + + if (!$forceOverwrite && file_exists($target)) { + throw new \RuntimeException('Unable to generate the controller as it already exists.'); + } + + $this->renderFile('crud/controller.php.twig', $target, array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entity_singularized' => $this->entitySingularized, + 'entity_pluralized' => $this->entityPluralized, + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'format' => $this->format, + )); + } + + /** + * Generates the functional test class only. + */ + protected function generateTestClass() + { + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); + $entityNamespace = implode('\\', $parts); + + $dir = $this->bundle->getPath().'/Tests/Controller'; + $target = $dir.'/'.str_replace('\\', '/', $entityNamespace).'/'.$entityClass.'ControllerTest.php'; + + $this->renderFile('crud/tests/test.php.twig', $target, array( + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'bundle' => $this->bundle->getName(), + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'actions' => $this->actions, + 'form_type_name' => strtolower(str_replace('\\', '_', $this->bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.$entityClass), + )); + } + + /** + * Generates the index.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateIndexView($dir) + { + $this->renderFile('crud/views/index.html.twig.twig', $dir.'/index.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entity_pluralized' => $this->entityPluralized, + 'entity_singularized' => $this->entitySingularized, + 'identifier' => $this->metadata->identifier[0], + 'fields' => $this->metadata->fieldMappings, + 'actions' => $this->actions, + 'record_actions' => $this->getRecordActions(), + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + )); + } + + /** + * Generates the show.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateShowView($dir) + { + $this->renderFile('crud/views/show.html.twig.twig', $dir.'/show.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entity_singularized' => $this->entitySingularized, + 'identifier' => $this->metadata->identifier[0], + 'fields' => $this->metadata->fieldMappings, + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + )); + } + + /** + * Generates the new.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateNewView($dir) + { + $this->renderFile('crud/views/new.html.twig.twig', $dir.'/new.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entity_singularized' => $this->entitySingularized, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'actions' => $this->actions, + 'fields' => $this->metadata->fieldMappings, + )); + } + + /** + * Generates the edit.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateEditView($dir) + { + $this->renderFile('crud/views/edit.html.twig.twig', $dir.'/edit.html.twig', array( + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'identifier' => $this->metadata->identifier[0], + 'entity' => $this->entity, + 'entity_singularized' => $this->entitySingularized, + 'fields' => $this->metadata->fieldMappings, + 'bundle' => $this->bundle->getName(), + 'actions' => $this->actions, + )); + } + + /** + * Returns an array of record actions to generate (edit, show). + * + * @return array + */ + protected function getRecordActions() + { + return array_filter($this->actions, function ($item) { + return in_array($item, array('show', 'edit')); + }); + } + + public static function getRouteNamePrefix($prefix) + { + $prefix = preg_replace('/{(.*?)}/', '', $prefix); // {foo}_bar -> _bar + $prefix = str_replace('/', '_', $prefix); + $prefix = preg_replace('/_+/', '_', $prefix); // foo__bar -> foo_bar + $prefix = trim($prefix, '_'); + + return $prefix; + } +} diff --git a/vendor/sensio/generator-bundle/Generator/DoctrineEntityGenerator.php b/vendor/sensio/generator-bundle/Generator/DoctrineEntityGenerator.php new file mode 100644 index 0000000..4138e0e --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/DoctrineEntityGenerator.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Sensio\Bundle\GeneratorBundle\Model\EntityGeneratorResult; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Bridge\Doctrine\RegistryInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Tools\EntityGenerator; +use Doctrine\ORM\Tools\EntityRepositoryGenerator; +use Doctrine\ORM\Tools\Export\ClassMetadataExporter; +use Doctrine\Common\Util\Inflector; + +/** + * Generates a Doctrine entity class based on its name, fields and format. + * + * @author Fabien Potencier + * @author Jonathan H. Wage + */ +class DoctrineEntityGenerator extends Generator +{ + private $filesystem; + private $registry; + + public function __construct(Filesystem $filesystem, RegistryInterface $registry) + { + $this->filesystem = $filesystem; + $this->registry = $registry; + } + + /** + * @param BundleInterface $bundle + * @param string $entity + * @param string $format + * @param array $fields + * + * @return EntityGeneratorResult + * + * @throws \Doctrine\ORM\Tools\Export\ExportException + */ + public function generate(BundleInterface $bundle, $entity, $format, array $fields) + { + // configure the bundle (needed if the bundle does not contain any Entities yet) + $config = $this->registry->getManager(null)->getConfiguration(); + $config->setEntityNamespaces(array_merge( + array($bundle->getName() => $bundle->getNamespace().'\\Entity'), + $config->getEntityNamespaces() + )); + + $entityClass = $this->registry->getAliasNamespace($bundle->getName()).'\\'.$entity; + $entityPath = $bundle->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php'; + if (file_exists($entityPath)) { + throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass)); + } + + $class = new ClassMetadataInfo($entityClass); + $class->customRepositoryClassName = str_replace('\\Entity\\', '\\Repository\\', $entityClass).'Repository'; + $class->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true)); + $class->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); + foreach ($fields as $field) { + $class->mapField($field); + } + + $entityGenerator = $this->getEntityGenerator(); + if ('annotation' === $format) { + $entityGenerator->setGenerateAnnotations(true); + $class->setPrimaryTable(array('name' => Inflector::tableize($entity))); + $entityCode = $entityGenerator->generateEntityClass($class); + $mappingPath = $mappingCode = false; + } else { + $cme = new ClassMetadataExporter(); + $exporter = $cme->getExporter('yml' == $format ? 'yaml' : $format); + $mappingPath = $bundle->getPath().'/Resources/config/doctrine/'.str_replace('\\', '.', $entity).'.orm.'.$format; + + if (file_exists($mappingPath)) { + throw new \RuntimeException(sprintf('Cannot generate entity when mapping "%s" already exists.', $mappingPath)); + } + + $mappingCode = $exporter->exportClassMetadata($class); + $entityGenerator->setGenerateAnnotations(false); + $entityCode = $entityGenerator->generateEntityClass($class); + } + $entityCode = str_replace( + array("@var integer\n", "@var boolean\n", "@param integer\n", "@param boolean\n", "@return integer\n", "@return boolean\n"), + array("@var int\n", "@var bool\n", "@param int\n", "@param bool\n", "@return int\n", "@return bool\n"), + $entityCode + ); + + $this->filesystem->mkdir(dirname($entityPath)); + file_put_contents($entityPath, $entityCode); + + if ($mappingPath) { + $this->filesystem->mkdir(dirname($mappingPath)); + file_put_contents($mappingPath, $mappingCode); + } + + $path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\')); + $this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path); + $repositoryPath = $path.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class->customRepositoryClassName).'.php'; + + return new EntityGeneratorResult($entityPath, $repositoryPath, $mappingPath); + } + + public function isReservedKeyword($keyword) + { + return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword); + } + + protected function getEntityGenerator() + { + $entityGenerator = new EntityGenerator(); + $entityGenerator->setGenerateAnnotations(false); + $entityGenerator->setGenerateStubMethods(true); + $entityGenerator->setRegenerateEntityIfExists(false); + $entityGenerator->setUpdateEntityIfExists(true); + $entityGenerator->setNumSpaces(4); + $entityGenerator->setAnnotationPrefix('ORM\\'); + + return $entityGenerator; + } + + protected function getRepositoryGenerator() + { + return new EntityRepositoryGenerator(); + } + + /** + * Checks if the given name is a valid PHP variable name. + * + * @see http://php.net/manual/en/language.variables.basics.php + * + * @param $name string + * + * @return bool + */ + public function isValidPhpVariableName($name) + { + return (bool) preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $name, $matches); + } +} diff --git a/vendor/sensio/generator-bundle/Generator/DoctrineFormGenerator.php b/vendor/sensio/generator-bundle/Generator/DoctrineFormGenerator.php new file mode 100644 index 0000000..1696899 --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/DoctrineFormGenerator.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Generates a form class based on a Doctrine entity. + * + * @author Fabien Potencier + * @author Hugo Hamon + */ +class DoctrineFormGenerator extends Generator +{ + private $filesystem; + private $className; + private $classPath; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function getClassName() + { + return $this->className; + } + + public function getClassPath() + { + return $this->classPath; + } + + /** + * Generates the entity form class. + * + * @param BundleInterface $bundle The bundle in which to create the class + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity metadata class + * @param bool $forceOverwrite If true, remove any existing form class before generating it again + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $forceOverwrite = false) + { + $parts = explode('\\', $entity); + $entityClass = array_pop($parts); + + $this->className = $entityClass.'Type'; + $dirPath = $bundle->getPath().'/Form'; + $this->classPath = $dirPath.'/'.str_replace('\\', '/', $entity).'Type.php'; + + if (!$forceOverwrite && file_exists($this->classPath)) { + throw new \RuntimeException(sprintf('Unable to generate the %s form class as it already exists under the %s file', $this->className, $this->classPath)); + } + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The form generator does not support entity classes with multiple primary keys.'); + } + + $parts = explode('\\', $entity); + array_pop($parts); + + $this->renderFile('form/FormType.php.twig', $this->classPath, array( + 'fields' => $this->getFieldsFromMetadata($metadata), + 'fields_mapping' => $metadata->fieldMappings, + 'namespace' => $bundle->getNamespace(), + 'entity_namespace' => implode('\\', $parts), + 'entity_class' => $entityClass, + 'bundle' => $bundle->getName(), + 'form_class' => $this->className, + 'form_type_name' => strtolower(str_replace('\\', '_', $bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.substr($this->className, 0, -4)), + + // Add 'setDefaultOptions' method with deprecated type hint, if the new 'configureOptions' isn't available. + // Required as long as Symfony 2.6 is supported. + 'configure_options_available' => method_exists('Symfony\Component\Form\AbstractType', 'configureOptions'), + 'get_name_required' => !method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix'), + )); + } + + /** + * Returns an array of fields. Fields can be both column fields and + * association fields. + * + * @param ClassMetadataInfo $metadata + * + * @return array $fields + */ + private function getFieldsFromMetadata(ClassMetadataInfo $metadata) + { + $fields = (array) $metadata->fieldNames; + + // Remove the primary key field if it's not managed manually + if (!$metadata->isIdentifierNatural()) { + $fields = array_diff($fields, $metadata->identifier); + } + + foreach ($metadata->associationMappings as $fieldName => $relation) { + if ($relation['type'] !== ClassMetadataInfo::ONE_TO_MANY) { + $fields[] = $fieldName; + } + } + + return $fields; + } +} diff --git a/vendor/sensio/generator-bundle/Generator/Generator.php b/vendor/sensio/generator-bundle/Generator/Generator.php new file mode 100644 index 0000000..76c84b1 --- /dev/null +++ b/vendor/sensio/generator-bundle/Generator/Generator.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Generator; + +/** + * Generator is the base class for all generators. + * + * @author Fabien Potencier + */ +class Generator +{ + private $skeletonDirs; + + /** + * Sets an array of directories to look for templates. + * + * The directories must be sorted from the most specific to the most + * directory. + * + * @param array $skeletonDirs An array of skeleton dirs + */ + public function setSkeletonDirs($skeletonDirs) + { + $this->skeletonDirs = is_array($skeletonDirs) ? $skeletonDirs : array($skeletonDirs); + } + + protected function render($template, $parameters) + { + $twig = $this->getTwigEnvironment(); + + return $twig->render($template, $parameters); + } + + /** + * Get the twig environment that will render skeletons. + * + * @return \Twig_Environment + */ + protected function getTwigEnvironment() + { + return new \Twig_Environment(new \Twig_Loader_Filesystem($this->skeletonDirs), array( + 'debug' => true, + 'cache' => false, + 'strict_variables' => true, + 'autoescape' => false, + )); + } + + protected function renderFile($template, $target, $parameters) + { + if (!is_dir(dirname($target))) { + mkdir(dirname($target), 0777, true); + } + + return file_put_contents($target, $this->render($template, $parameters)); + } +} diff --git a/vendor/sensio/generator-bundle/Manipulator/ConfigurationManipulator.php b/vendor/sensio/generator-bundle/Manipulator/ConfigurationManipulator.php new file mode 100644 index 0000000..72eb0c4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Manipulator/ConfigurationManipulator.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + +use Sensio\Bundle\GeneratorBundle\Model\Bundle; +use Symfony\Component\Yaml\Yaml; + +/** + * Changes the PHP code of a YAML services configuration file. + * + * @author Fabien Potencier + * @author Ryan Weaver + */ +class ConfigurationManipulator extends Manipulator +{ + private $file; + + /** + * Constructor. + * + * @param string $file The YAML configuration file path + */ + public function __construct($file) + { + $this->file = $file; + } + + /** + * Adds a configuration resource at the top of the existing ones. + * + * @param Bundle $bundle + * + * @throws \RuntimeException If this process fails for any reason + */ + public function addResource(Bundle $bundle) + { + // if the config.yml file doesn't exist, don't even try. + if (!file_exists($this->file)) { + throw new \RuntimeException(sprintf('The target config file %s does not exist', $this->file)); + } + + $code = $this->getImportCode($bundle); + + $currentContents = file_get_contents($this->file); + // Don't add same bundle twice + if (false !== strpos($currentContents, $code)) { + throw new \RuntimeException(sprintf( + 'The %s configuration file from %s is already imported', + $bundle->getServicesConfigurationFilename(), + $bundle->getName() + )); + } + + // find the "imports" line and add this at the end of that list + $lastImportedPath = $this->findLastImportedPath($currentContents); + if (!$lastImportedPath) { + throw new \RuntimeException(sprintf('Could not find the imports key in %s', $this->file)); + } + + // find imports: + $importsPosition = strpos($currentContents, 'imports:'); + // find the last import + $lastImportPosition = strpos($currentContents, $lastImportedPath, $importsPosition); + // find the line break after the last import + $targetLinebreakPosition = strpos($currentContents, "\n", $lastImportPosition); + + $newContents = substr($currentContents, 0, $targetLinebreakPosition)."\n".$code.substr($currentContents, $targetLinebreakPosition); + + if (false === file_put_contents($this->file, $newContents)) { + throw new \RuntimeException(sprintf('Could not write file %s ', $this->file)); + } + } + + public function getImportCode(Bundle $bundle) + { + return sprintf(<<getName(), + $bundle->getServicesConfigurationFilename() + ); + } + + /** + * Finds the last imported resource path in the YAML file. + * + * @param $yamlContents + * + * @return bool|string + */ + private function findLastImportedPath($yamlContents) + { + $data = Yaml::parse($yamlContents); + if (!isset($data['imports'])) { + return false; + } + + // find the last imports entry + $lastImport = end($data['imports']); + if (!isset($lastImport['resource'])) { + return false; + } + + return $lastImport['resource']; + } +} diff --git a/vendor/sensio/generator-bundle/Manipulator/KernelManipulator.php b/vendor/sensio/generator-bundle/Manipulator/KernelManipulator.php new file mode 100644 index 0000000..8cc61d3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Manipulator/KernelManipulator.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * Changes the PHP code of a Kernel. + * + * @author Fabien Potencier + */ +class KernelManipulator extends Manipulator +{ + protected $kernel; + protected $reflected; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + $this->reflected = new \ReflectionObject($kernel); + } + + /** + * Adds a bundle at the end of the existing ones. + * + * @param string $bundle The bundle class name + * + * @return bool true if it worked, false otherwise + * + * @throws \RuntimeException If bundle is already defined + */ + public function addBundle($bundle) + { + if (!$this->getFilename()) { + return false; + } + + $src = file($this->getFilename()); + $method = $this->reflected->getMethod('registerBundles'); + $lines = array_slice($src, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1); + + // Don't add same bundle twice + if (false !== strpos(implode('', $lines), $bundle)) { + throw new \RuntimeException(sprintf('Bundle "%s" is already defined in "AppKernel::registerBundles()".', $bundle)); + } + + $this->setCode(token_get_all('getStartLine()); + + while ($token = $this->next()) { + // $bundles + if (T_VARIABLE !== $token[0] || '$bundles' !== $token[1]) { + continue; + } + + // = + $this->next(); + + // array start with traditional or short syntax + $token = $this->next(); + if (T_ARRAY !== $token[0] && '[' !== $this->value($token)) { + return false; + } + + // add the bundle at the end of the array + while ($token = $this->next()) { + // look for ); or ]; + if (')' !== $this->value($token) && ']' !== $this->value($token)) { + continue; + } + + if (';' !== $this->value($this->peek())) { + continue; + } + + $this->next(); + + $leadingContent = implode('', array_slice($src, 0, $this->line)); + + // trim semicolon + $leadingContent = rtrim(rtrim($leadingContent), ';'); + + // We want to match ) & ] + $closingSymbolRegex = '#(\)|])$#'; + + // get closing symbol used + preg_match($closingSymbolRegex, $leadingContent, $matches); + $closingSymbol = $matches[0]; + + // remove last close parentheses + $leadingContent = rtrim(preg_replace($closingSymbolRegex, '', rtrim($leadingContent))); + + if (substr($leadingContent, -1) !== '(' && substr($leadingContent, -1) !== '[' ) { + // end of leading content is not open parentheses or bracket, then assume that array contains at least one element + $leadingContent = rtrim($leadingContent, ',').','; + } + + $lines = array_merge( + array($leadingContent, "\n"), + array(str_repeat(' ', 12), sprintf('new %s(),', $bundle), "\n"), + array(str_repeat(' ', 8), $closingSymbol.';', "\n"), + array_slice($src, $this->line) + ); + + file_put_contents($this->getFilename(), implode('', $lines)); + + return true; + } + } + } + + public function getFilename() + { + return $this->reflected->getFileName(); + } +} diff --git a/vendor/sensio/generator-bundle/Manipulator/Manipulator.php b/vendor/sensio/generator-bundle/Manipulator/Manipulator.php new file mode 100644 index 0000000..a29ee31 --- /dev/null +++ b/vendor/sensio/generator-bundle/Manipulator/Manipulator.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + +/** + * Changes the PHP code of a Kernel. + * + * @author Fabien Potencier + */ +class Manipulator +{ + protected $tokens; + protected $line; + + /** + * Sets the code to manipulate. + * + * @param array $tokens An array of PHP tokens + * @param int $line The start line of the code + */ + protected function setCode(array $tokens, $line = 0) + { + $this->tokens = $tokens; + $this->line = $line; + } + + /** + * Gets the next token. + * + * @return mixed + */ + protected function next() + { + while ($token = array_shift($this->tokens)) { + $this->line += substr_count($this->value($token), "\n"); + + if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + return $token; + } + } + + /** + * Peeks the next token. + * + * @param int $nb + * + * @return mixed + */ + protected function peek($nb = 1) + { + $i = 0; + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) { + continue; + } + + ++$i; + if ($i == $nb) { + return $token; + } + } + } + + /** + * Gets the value of a token. + * + * @param string|string[] $token The token value + * + * @return string + */ + protected function value($token) + { + return is_array($token) ? $token[1] : $token; + } +} diff --git a/vendor/sensio/generator-bundle/Manipulator/RoutingManipulator.php b/vendor/sensio/generator-bundle/Manipulator/RoutingManipulator.php new file mode 100644 index 0000000..bb09feb --- /dev/null +++ b/vendor/sensio/generator-bundle/Manipulator/RoutingManipulator.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sensio\Bundle\GeneratorBundle\Manipulator; + +use Symfony\Component\DependencyInjection\Container; +use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator; +use Symfony\Component\Yaml\Yaml; + +/** + * Changes the PHP code of a YAML routing file. + * + * @author Fabien Potencier + */ +class RoutingManipulator extends Manipulator +{ + private $file; + + /** + * Constructor. + * + * @param string $file The YAML routing file path + */ + public function __construct($file) + { + $this->file = $file; + } + + /** + * Adds a routing resource at the top of the existing ones. + * + * @param string $bundle + * @param string $format + * @param string $prefix + * @param string $path + * + * @return bool true if it worked, false otherwise + * + * @throws \RuntimeException If bundle is already imported + */ + public function addResource($bundle, $format, $prefix = '/', $path = 'routing') + { + $current = ''; + $code = sprintf("%s:\n", $this->getImportedResourceYamlKey($bundle, $prefix)); + + if (file_exists($this->file)) { + $current = file_get_contents($this->file); + + // Don't add same bundle twice + if (false !== strpos($current, '@'.$bundle)) { + throw new \RuntimeException(sprintf('Bundle "%s" is already imported.', $bundle)); + } + } elseif (!is_dir($dir = dirname($this->file))) { + mkdir($dir, 0777, true); + } + + if ('annotation' == $format) { + $code .= sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle); + } else { + $code .= sprintf(" resource: \"@%s/Resources/config/%s.%s\"\n", $bundle, $path, $format); + } + $code .= sprintf(" prefix: %s\n", $prefix); + $code .= "\n"; + $code .= $current; + + if (false === file_put_contents($this->file, $code)) { + return false; + } + + return true; + } + + /** + * Check if the routing file contain a line for the bundle. + * + * @param string $bundle + * + * @return bool + */ + public function hasResourceInAnnotation($bundle) + { + if (!file_exists($this->file)) { + return false; + } + + $config = Yaml::parse(file_get_contents($this->file)); + + $search = sprintf('@%s/Controller/', $bundle); + + foreach ($config as $resource) { + if (array_key_exists('resource', $resource)) { + return $resource['resource'] === $search; + } + } + + return false; + } + + /** + * Add an annotation controller resource. + * + * @param string $bundle + * @param string $controller + * + * @return bool + */ + public function addAnnotationController($bundle, $controller) + { + $current = ''; + + if (file_exists($this->file)) { + $current = file_get_contents($this->file); + } elseif (!is_dir($dir = dirname($this->file))) { + mkdir($dir, 0777, true); + } + + $code = sprintf("%s:\n", Container::underscore(substr($bundle, 0, -6)).'_'.Container::underscore($controller)); + + $code .= sprintf(" resource: \"@%s/Controller/%sController.php\"\n type: annotation\n", $bundle, $controller); + + $code .= "\n"; + $code .= $current; + + return false !== file_put_contents($this->file, $code); + } + + public function getImportedResourceYamlKey($bundle, $prefix) + { + $snakeCasedBundleName = Container::underscore(substr($bundle, 0, -6)); + $routePrefix = DoctrineCrudGenerator::getRouteNamePrefix($prefix); + + return sprintf('%s%s%s', $snakeCasedBundleName, '' !== $routePrefix ? '_' : '' , $routePrefix); + } +} diff --git a/vendor/sensio/generator-bundle/Model/Bundle.php b/vendor/sensio/generator-bundle/Model/Bundle.php new file mode 100644 index 0000000..798e084 --- /dev/null +++ b/vendor/sensio/generator-bundle/Model/Bundle.php @@ -0,0 +1,130 @@ +namespace = $namespace; + $this->name = $name; + $this->targetDirectory = $targetDirectory; + $this->configurationFormat = $configurationFormat; + $this->isShared = $isShared; + } + + public function getNamespace() + { + return $this->namespace; + } + + public function getName() + { + return $this->name; + } + + public function getConfigurationFormat() + { + return $this->configurationFormat; + } + + public function isShared() + { + return $this->isShared; + } + + /** + * Returns the directory where the bundle will be generated. + * + * @return string + */ + public function getTargetDirectory() + { + return rtrim($this->targetDirectory, '/').'/'.trim(strtr($this->namespace, '\\', '/'), '/'); + } + + /** + * Returns the name of the bundle without the Bundle suffix. + * + * @return string + */ + public function getBasename() + { + return substr($this->name, 0, -6); + } + + /** + * Returns the dependency injection extension alias for this bundle. + * + * @return string + */ + public function getExtensionAlias() + { + return Container::underscore($this->getBasename()); + } + + /** + * Should a DependencyInjection directory be generated for this bundle? + * + * @return bool + */ + public function shouldGenerateDependencyInjectionDirectory() + { + return $this->isShared; + } + + /** + * What is the filename for the services.yml/xml file? + * + * @return string + */ + public function getServicesConfigurationFilename() + { + if ('yml' === $this->getConfigurationFormat() || 'annotation' === $this->configurationFormat) { + return 'services.yml'; + } else { + return 'services.'.$this->getConfigurationFormat(); + } + } + + /** + * What is the filename for the routing.yml/xml file? + * + * If false, no routing file will be generated + * + * @return string|bool + */ + public function getRoutingConfigurationFilename() + { + if ($this->getConfigurationFormat() == 'annotation') { + return false; + } + + return 'routing.'.$this->getConfigurationFormat(); + } + + /** + * Returns the class name of the Bundle class. + * + * @return string + */ + public function getBundleClassName() + { + return $this->namespace.'\\'.$this->name; + } +} diff --git a/vendor/sensio/generator-bundle/Model/EntityGeneratorResult.php b/vendor/sensio/generator-bundle/Model/EntityGeneratorResult.php new file mode 100644 index 0000000..843e6c0 --- /dev/null +++ b/vendor/sensio/generator-bundle/Model/EntityGeneratorResult.php @@ -0,0 +1,51 @@ +entityPath = $entityPath; + $this->repositoryPath = $repositoryPath; + $this->mappingPath = $mappingPath; + } + + /** + * @return string + */ + public function getEntityPath() + { + return $this->entityPath; + } + + /** + * @return string + */ + public function getRepositoryPath() + { + return $this->repositoryPath; + } + + /** + * @return string + */ + public function getMappingPath() + { + return $this->mappingPath; + } +} diff --git a/vendor/sensio/generator-bundle/README.md b/vendor/sensio/generator-bundle/README.md new file mode 100644 index 0000000..8b033db --- /dev/null +++ b/vendor/sensio/generator-bundle/README.md @@ -0,0 +1,10 @@ +SensioGeneratorBundle +===================== + +The `SensioGeneratorBundle` extends the default Symfony2 command line +interface by providing new interactive and intuitive commands for generating +code skeletons like bundles, form classes, or CRUD controllers based on a +Doctrine 2 schema. + +More information in the official +[documentation](http://symfony.com/doc/current/bundles/SensioGeneratorBundle/index.html). diff --git a/vendor/sensio/generator-bundle/Resources/meta/LICENSE b/vendor/sensio/generator-bundle/Resources/meta/LICENSE new file mode 100644 index 0000000..5f23672 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/Bundle.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/Bundle.php.twig new file mode 100644 index 0000000..2f26597 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/Bundle.php.twig @@ -0,0 +1,15 @@ +root('{{ extension_alias }}'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/DefaultController.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/DefaultController.php.twig new file mode 100644 index 0000000..8b9fe28 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/DefaultController.php.twig @@ -0,0 +1,27 @@ +render('{{ bundle }}:Default:index.html.twig'); + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/DefaultControllerTest.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/DefaultControllerTest.php.twig new file mode 100644 index 0000000..16e3fc4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/DefaultControllerTest.php.twig @@ -0,0 +1,23 @@ +request('GET', '/'); + + $this->assertContains('Hello World', $client->getResponse()->getContent()); + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/Extension.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/Extension.php.twig new file mode 100644 index 0000000..be45f1d --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/Extension.php.twig @@ -0,0 +1,45 @@ +processConfiguration($configuration, $configs); + + {% if format == 'yml' or format == 'annotation' -%} + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + {%- elseif format == 'xml' -%} + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + {%- elseif format == 'php' -%} + $loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.php'); + {%- endif %} + + } +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/index.html.twig.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/index.html.twig.twig new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/index.html.twig.twig @@ -0,0 +1 @@ +Hello World! diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.php.twig new file mode 100644 index 0000000..2e17c46 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.php.twig @@ -0,0 +1,20 @@ +add('{{ extension_alias }}_homepage', new Route('/', array( + '_controller' => '{{ bundle }}:Default:index', +))); +{% endblock body %} + +{% block return %} +return $collection; +{% endblock return %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.xml.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.xml.twig new file mode 100644 index 0000000..21fbb1d --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.xml.twig @@ -0,0 +1,12 @@ + + + + +{% block body %} + + {{ bundle }}:Default:index + +{% endblock body %} + diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.yml.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.yml.twig new file mode 100644 index 0000000..662bffd --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/routing.yml.twig @@ -0,0 +1,3 @@ +{{ extension_alias }}_homepage: + path: / + defaults: { _controller: {{ bundle }}:Default:index } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.php.twig new file mode 100644 index 0000000..c7b80d2 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.php.twig @@ -0,0 +1,25 @@ +setDefinition( + '{{ extension_alias }}.example', + new Definition( + '{{ namespace }}\Example', + array( + new Reference('service_id'), + "plain_value", + new Parameter('parameter_name'), + ) + ) +); +{% endblock services %} + +*/ diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.xml.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.xml.twig new file mode 100644 index 0000000..62d71a3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.xml.twig @@ -0,0 +1,18 @@ + + + + + + diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.yml.twig b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.yml.twig new file mode 100644 index 0000000..47e666b --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/bundle/services.yml.twig @@ -0,0 +1,6 @@ +services: +{% block services %} +# {{ extension_alias }}.example: +# class: {{ namespace }}\Example +# arguments: [@service_id, "plain_value", %parameter%] +{% endblock services %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/command/Command.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/command/Command.php.twig new file mode 100644 index 0000000..0a5d1f6 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/command/Command.php.twig @@ -0,0 +1,40 @@ +setName('{{ name }}') + ->setDescription('...') + ->addArgument('argument', InputArgument::OPTIONAL, 'Argument description') + ->addOption('option', null, InputOption::VALUE_NONE, 'Option description') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $argument = $input->getArgument('argument'); + + if ($input->getOption('option')) { + // ... + } + + $output->writeln('Command result.'); + } + +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/controller/Controller.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/controller/Controller.php.twig new file mode 100644 index 0000000..9d6e43e --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/controller/Controller.php.twig @@ -0,0 +1,42 @@ + 0 -%} + ${{- action.placeholders|join(', $') -}} + {%- endif -%}) + { + {% if 'default' == action.template -%} + return $this->render('{{ bundle }}:{{ controller }}:{{ action.name|slice(0, -6) }}.html.{{ format.templating }}', array( + // ... + )); + {%- else -%} + return $this->render('{{ action.template }}', array( + // ... + )); + {%- endif %} + + } + +{% endfor -%} +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/controller/ControllerTest.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/controller/ControllerTest.php.twig new file mode 100644 index 0000000..b824d7d --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/controller/ControllerTest.php.twig @@ -0,0 +1,24 @@ +request('GET', '{{ action.route }}'); + } + +{% endfor -%} +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/controller/Template.html.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/controller/Template.html.php.twig new file mode 100644 index 0000000..0652eb5 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/controller/Template.html.php.twig @@ -0,0 +1,7 @@ +extend('::base.html.twig') ?> + +set('title', '{{ bundle }}:{{ controller }}:{{ action.basename }}') ?> + +start('body') ?> +

    Welcome to the {{ controller }}:{{ action.basename }} page

    +stop() ?> diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/controller/Template.html.twig.twig b/vendor/sensio/generator-bundle/Resources/skeleton/controller/Template.html.twig.twig new file mode 100644 index 0000000..03ced82 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/controller/Template.html.twig.twig @@ -0,0 +1,9 @@ +{{ '{% extends "::base.html.twig" %}' }} + +{{ '{% block title %}' -}} +{{ bundle }}:{{ controller }}:{{ action.basename }} +{{- '{% endblock %}' }} + +{{ '{% block body %}' }} +

    Welcome to the {{ controller }}:{{ action.basename }} page

    +{{ '{% endblock %}' }} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/delete.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/delete.php.twig new file mode 100644 index 0000000..95a414d --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/delete.php.twig @@ -0,0 +1,50 @@ + + /** +{% block phpdoc_method_header %} + * Deletes a {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}", name="{{ route_name_prefix }}_delete") + * @Method("DELETE") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function deleteAction(Request $request, {{ entity_class }} ${{ entity_singularized }}) +{% endblock method_definition %} + { +{% block method_body %} + $form = $this->createDeleteForm(${{ entity_singularized }}); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->remove(${{ entity_singularized }}); + $em->flush(); + } +{% endblock method_body %} + +{% block method_return %} + return $this->redirectToRoute('{{ route_name_prefix }}_index'); +{% endblock method_return %} + } + +{% block form %} + /** + * Creates a form to delete a {{ entity }} entity. + * + * @param {{ entity_class }} ${{ entity_singularized }} The {{ entity }} entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createDeleteForm({{ entity_class }} ${{ entity_singularized }}) + { + return $this->createFormBuilder() + ->setAction($this->generateUrl('{{ route_name_prefix }}_delete', array('id' => ${{ entity_singularized }}->getId()))) + ->setMethod('DELETE') + ->getForm() + ; + } +{% endblock form %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/edit.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/edit.php.twig new file mode 100644 index 0000000..3e3d4bb --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/edit.php.twig @@ -0,0 +1,39 @@ + + /** +{% block phpdoc_method_header %} + * Displays a form to edit an existing {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}/edit", name="{{ route_name_prefix }}_edit") + * @Method({"GET", "POST"}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function editAction(Request $request, {{ entity_class }} ${{ entity_singularized }}) +{% endblock method_definition %} + { +{% block method_body %} + $deleteForm = $this->createDeleteForm(${{ entity_singularized }}); + $editForm = $this->createForm('{{ namespace }}\Form\{{ entity_class }}Type', ${{ entity_singularized }}); + $editForm->handleRequest($request); + + if ($editForm->isSubmitted() && $editForm->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist(${{ entity_singularized }}); + $em->flush(); + + return $this->redirectToRoute('{{ route_name_prefix }}_edit', array('id' => ${{ entity_singularized }}->getId())); + } +{% endblock method_body %} + +{% block method_return %} + return $this->render('{{ entity|lower|replace({'\\': '/'}) }}/edit.html.twig', array( + '{{ entity_singularized }}' => ${{ entity_singularized }}, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + )); +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/index.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/index.php.twig new file mode 100644 index 0000000..8e5f18f --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/index.php.twig @@ -0,0 +1,28 @@ + /** +{% block phpdoc_method_header %} + * Lists all {{ entity }} entities. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/", name="{{ route_name_prefix }}_index") + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function indexAction() +{% endblock method_definition %} + { +{% block method_body %} + $em = $this->getDoctrine()->getManager(); + + ${{ entity_pluralized }} = $em->getRepository('{{ bundle }}:{{ entity }}')->findAll(); +{% endblock method_body %} + +{% block method_return %} + return $this->render('{{ entity|lower|replace({'\\': '/'}) }}/index.html.twig', array( + '{{ entity_pluralized }}' => ${{ entity_pluralized }}, + )); +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/new.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/new.php.twig new file mode 100644 index 0000000..354f4af --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/new.php.twig @@ -0,0 +1,43 @@ + + /** +{% block phpdoc_method_header %} + * Creates a new {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/new", name="{{ route_name_prefix }}_new") + * @Method({"GET", "POST"}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function newAction(Request $request) +{% endblock method_definition %} + { +{% block method_body %} + ${{ entity_singularized }} = new {{ entity_class }}(); + $form = $this->createForm('{{ namespace }}\Form\{{ entity_class }}Type', ${{ entity_singularized }}); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist(${{ entity_singularized }}); + $em->flush(); + + {% if 'show' in actions -%} + return $this->redirectToRoute('{{ route_name_prefix }}_show', array('id' => ${{ entity_class|lower }}->getId())); + {%- else -%} + return $this->redirectToRoute('{{ route_name_prefix }}_index')); + {%- endif %} + + } +{% endblock method_body %} + +{% block method_return %} + return $this->render('{{ entity|lower|replace({'\\': '/'}) }}/new.html.twig', array( + '{{ entity_singularized }}' => ${{ entity_singularized }}, + 'form' => $form->createView(), + )); +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/show.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/show.php.twig new file mode 100644 index 0000000..c090567 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/actions/show.php.twig @@ -0,0 +1,32 @@ + + /** +{% block phpdoc_method_header %} + * Finds and displays a {{ entity }} entity. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}", name="{{ route_name_prefix }}_show") + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function showAction({{ entity_class }} ${{ entity_singularized }}) +{% endblock method_definition %} + { +{% block method_body %} +{% if 'delete' in actions %} + $deleteForm = $this->createDeleteForm(${{ entity_singularized }}); +{% endif %} +{% endblock method_body %} + +{% block method_return %} + return $this->render('{{ entity|lower|replace({'\\': '/'}) }}/show.html.twig', array( + '{{ entity_singularized }}' => ${{ entity_singularized }}, +{% if 'delete' in actions %} + 'delete_form' => $deleteForm->createView(), +{% endif %} + )); +{% endblock method_return %} + } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.php.twig new file mode 100644 index 0000000..ba226ab --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.php.twig @@ -0,0 +1,76 @@ +add('{{ route_name_prefix }}_index', new Route( + '/', + array('_controller' => '{{ bundle }}:{{ entity }}:index'), + array(), + array(), + '', + array(), + array('GET') +)); +{% endif %} + +{% if 'show' in actions %} +$collection->add('{{ route_name_prefix }}_show', new Route( + '/{id}/show', + array('_controller' => '{{ bundle }}:{{ entity }}:show'), + array(), + array(), + '', + array(), + array('GET') +)); +{% endif %} + +{% if 'new' in actions %} +$collection->add('{{ route_name_prefix }}_new', new Route( + '/new', + array('_controller' => '{{ bundle }}:{{ entity }}:new'), + array(), + array(), + '', + array(), + array('GET', 'POST') +)); +{% endif %} + +{% if 'edit' in actions %} +$collection->add('{{ route_name_prefix }}_edit', new Route( + '/{id}/edit', + array('_controller' => '{{ bundle }}:{{ entity }}:edit'), + array(), + array(), + '', + array(), + array('GET', 'POST') +)); +{% endif %} + +{% if 'delete' in actions %} +$collection->add('{{ route_name_prefix }}_delete', new Route( + '/{id}/delete', + array('_controller' => '{{ bundle }}:{{ entity }}:delete'), + array(), + array(), + '', + array(), + array('DELETE') +)); +{% endif %} +{% endblock body %} + +{% block return %} +return $collection; +{% endblock return %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.xml.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.xml.twig new file mode 100644 index 0000000..dfe6ed3 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.xml.twig @@ -0,0 +1,35 @@ + + + + +{% block body %} + + {{ bundle }}:{{ entity }}:index + + + + {{ bundle }}:{{ entity }}:show + + +{% if 'new' in actions %} + + {{ bundle }}:{{ entity }}:new + +{% endif %} + +{% if 'edit' in actions %} + + {{ bundle }}:{{ entity }}:edit + +{% endif %} + +{% if 'delete' in actions %} + + {{ bundle }}:{{ entity }}:delete + +{% endif %} +{% endblock body %} + + diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.yml.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.yml.twig new file mode 100644 index 0000000..33ee1a8 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/config/routing.yml.twig @@ -0,0 +1,34 @@ +{% if 'index' in actions %} +{{ route_name_prefix }}_index: + path: / + defaults: { _controller: "{{ bundle }}:{{ entity }}:index" } + methods: GET +{% endif %} + +{% if 'show' in actions %} +{{ route_name_prefix }}_show: + path: /{id}/show + defaults: { _controller: "{{ bundle }}:{{ entity }}:show" } + methods: GET +{% endif %} + +{% if 'new' in actions %} +{{ route_name_prefix }}_new: + path: /new + defaults: { _controller: "{{ bundle }}:{{ entity }}:new" } + methods: [GET, POST] +{% endif %} + +{% if 'edit' in actions %} +{{ route_name_prefix }}_edit: + path: /{id}/edit + defaults: { _controller: "{{ bundle }}:{{ entity }}:edit" } + methods: [GET, POST] +{% endif %} + +{% if 'delete' in actions %} +{{ route_name_prefix }}_delete: + path: /{id}/delete + defaults: { _controller: "{{ bundle }}:{{ entity }}:delete" } + methods: DELETE +{% endif %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/controller.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/controller.php.twig new file mode 100644 index 0000000..ed60588 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/controller.php.twig @@ -0,0 +1,58 @@ +request('GET', '/{{ route_prefix }}/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /{{ route_prefix }}/"); + $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); + + // Fill in the form and submit it + $form = $crawler->selectButton('Create')->form(array( + '{{ form_type_name|lower }}[field_name]' => 'Test', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")'); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Update')->form(array( + '{{ form_type_name|lower }}[field_name]' => 'Foo', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]'); + + // Delete the entity + $client->submit($crawler->selectButton('Delete')->form()); + $crawler = $client->followRedirect(); + + // Check the entity has been delete on the list + $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig new file mode 100644 index 0000000..9e1b1a4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig @@ -0,0 +1,14 @@ + + public function testCompleteScenario() + { + // Create a new client to browse the application + $client = static::createClient(); + + // Go to the list view + $crawler = $client->request('GET', '/{{ route_prefix }}/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /{{ route_prefix }}/"); + + // Go to the show view + $crawler = $client->click($crawler->selectLink('show')->link()); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code"); + } diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/tests/test.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/tests/test.php.twig new file mode 100644 index 0000000..c07a8fc --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/tests/test.php.twig @@ -0,0 +1,24 @@ +{{ entity }} edit + + {{ '{{ form_start(edit_form) }}' }} + {{ '{{ form_widget(edit_form) }}' }} + + {{ '{{ form_end(edit_form) }}' }} + +{% set hide_edit, hide_delete = true, false %} +{{ include('crud/views/others/record_actions.html.twig.twig') -}} +{{ "{% endblock %}" }} +{% endblock body %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/index.html.twig.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/index.html.twig.twig new file mode 100644 index 0000000..6781c65 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/index.html.twig.twig @@ -0,0 +1,89 @@ +{% block extends %} +{{ "{% extends 'base.html.twig' %}" }} +{% endblock extends %} + +{% block body %} +{{ "{% block body %}" }} +

    {{ entity }} list

    + + + + + {%- for field, metadata in fields %} + + + + {%- endfor %} + + + + + + {{ '{% for ' ~ entity_singularized ~ ' in ' ~ entity_pluralized ~ ' %}' }} + + + {%- for field, metadata in fields %} + {%- if loop.first and ('show' in actions) %} + + + + {%- elseif metadata.type in ['datetime'] %} + + + + {%- elseif metadata.type in ['date'] %} + + + + {%- elseif metadata.type in ['time'] %} + + + + {%- elseif metadata.type in ['array'] %} + + + + {%- elseif metadata.type in ['boolean'] %} + + + + {%- else %} + + + + {%- endif %} + + {%- if loop.last %} + + + + {%- endif %} + {%- endfor %} + + + {{ '{% endfor %}' }} + +
    {{ field|capitalize }}Actions
    {{ '{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' }}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|date(\'Y-m-d H:i:s\') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|date(\'Y-m-d\') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|date(\'H:i:s\') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|join(\', \') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}Yes{% else %}No{% endif %}' }}{{ '{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' }}' }} +
      + + {%- for action in record_actions %} + +
    • + {{ action }} +
    • + + {%- endfor %} + +
    +
    + + {% if 'new' in actions -%} + + {%- endif %} + +{{ "{% endblock %}" }} +{% endblock body %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/new.html.twig.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/new.html.twig.twig new file mode 100644 index 0000000..38c70dd --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/new.html.twig.twig @@ -0,0 +1,17 @@ +{% block extends %} +{{ "{% extends 'base.html.twig' %}" }} +{% endblock extends %} + +{% block body %} +{{ "{% block body %}" }} +

    {{ entity }} creation

    + + {{ '{{ form_start(form) }}' }} + {{ '{{ form_widget(form) }}' }} + + {{ '{{ form_end(form) }}' }} + +{% set hide_edit, hide_delete = true, true %} +{{ include('crud/views/others/record_actions.html.twig.twig') -}} +{{ "{% endblock %}" }} +{% endblock body %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig new file mode 100644 index 0000000..05b4f53 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig @@ -0,0 +1,24 @@ +
      +
    • + Back to the list +
    • + + {%- if ('edit' in actions) and (not hide_edit) %} + +
    • + Edit +
    • + + {%- endif %} + + {%- if ('delete' in actions) and (not hide_delete) %} + +
    • + {{ '{{ form_start(delete_form) }}' }} + + {{ '{{ form_end(delete_form) }}' }} +
    • + + {%- endif %} + +
    diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/show.html.twig.twig b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/show.html.twig.twig new file mode 100644 index 0000000..fc29852 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/crud/views/show.html.twig.twig @@ -0,0 +1,52 @@ +{% block extends %} +{{ "{% extends 'base.html.twig' %}" }} +{% endblock extends %} + +{% block body %} +{{ "{% block body %}" }} +

    {{ entity }}

    + + + + {%- for field, metadata in fields %} + + + + + {%- if metadata.type in ['datetime'] %} + + + + {%- elseif metadata.type in ['date'] %} + + + + {%- elseif metadata.type in ['time'] %} + + + + {%- elseif metadata.type in ['array'] %} + + + + {%- elseif metadata.type in ['boolean'] %} + + + + {%- else %} + + + + {%- endif %} + + + + {%- endfor %} + + +
    {{ field|capitalize }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|date(\'Y-m-d H:i:s\') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|date(\'Y-m-d\') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|date(\'H:i:s\') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ '|join(\', \') }}{% endif %}' }}{{ '{% if ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' %}Yes{% else %}No{% endif %}' }}{{ '{{ ' ~ entity_singularized ~ '.' ~ field|replace({'_': ''}) ~ ' }}' }}
    + +{% set hide_edit, hide_delete = false, false %} +{{ include('crud/views/others/record_actions.html.twig.twig') -}} +{{ "{% endblock %}" }} +{% endblock body %} diff --git a/vendor/sensio/generator-bundle/Resources/skeleton/form/FormType.php.twig b/vendor/sensio/generator-bundle/Resources/skeleton/form/FormType.php.twig new file mode 100644 index 0000000..71f23f4 --- /dev/null +++ b/vendor/sensio/generator-bundle/Resources/skeleton/form/FormType.php.twig @@ -0,0 +1,81 @@ + 0 %} + /** + * @param FormBuilderInterface $builder + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + + {%- for field in fields -%} + {%- if fields_mapping[field]['type'] in ['date', 'time', 'datetime'] %} + + ->add('{{ field }}', '{{ fields_mapping[field]['type'] }}') + + {%- else %} + + ->add('{{ field }}') + + {%- endif -%} + {%- endfor %} + + ; + } + {% endif %} + + {%- if not configure_options_available %} + + /** + * Sets the default options for this type. + * + * This method should be removed when upgrading to Symfony 2.7. + * + * @param OptionsResolverInterface $resolver + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $this->configureOptions($resolver); + } + {% endif %} + + /** + * @param OptionsResolver $resolver + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'data_class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ entity_class }}' + )); + } + + {%- if get_name_required %} + + /** + * @return string + */ + public function getName() + { + return '{{ form_type_name }}'; + } + {% endif %} + +{% endblock class_body %} +} diff --git a/vendor/sensio/generator-bundle/SensioGeneratorBundle.php b/vendor/sensio/generator-bundle/SensioGeneratorBundle.php new file mode 100644 index 0000000..5c4e799 --- /dev/null +++ b/vendor/sensio/generator-bundle/SensioGeneratorBundle.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Sensio\Bundle\GeneratorBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * SensioGeneratorBundle. + * + * @author Fabien Potencier + */ +class SensioGeneratorBundle extends Bundle +{ +} diff --git a/vendor/sensio/generator-bundle/composer.json b/vendor/sensio/generator-bundle/composer.json new file mode 100644 index 0000000..d4943e0 --- /dev/null +++ b/vendor/sensio/generator-bundle/composer.json @@ -0,0 +1,36 @@ +{ + "name": "sensio/generator-bundle", + "description": "This bundle generates code for you", + "keywords": [], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "symfony/framework-bundle": "~2.7|~3.0", + "symfony/console": "~2.7|~3.0", + "symfony/process": "~2.7|~3.0", + "symfony/yaml": "~2.7|~3.0" + }, + "require-dev": { + "symfony/doctrine-bridge": "~2.7|~3.0", + "doctrine/orm": "~2.4", + "twig/twig": "~1.18" + }, + "autoload": { + "psr-4": { "Sensio\\Bundle\\GeneratorBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + } +} diff --git a/vendor/sensiolabs/security-checker/LICENSE b/vendor/sensiolabs/security-checker/LICENSE new file mode 100644 index 0000000..77de3d2 --- /dev/null +++ b/vendor/sensiolabs/security-checker/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/sensiolabs/security-checker/README.md b/vendor/sensiolabs/security-checker/README.md new file mode 100644 index 0000000..8595121 --- /dev/null +++ b/vendor/sensiolabs/security-checker/README.md @@ -0,0 +1,38 @@ +SensioLabs Security Checker +=========================== + +The SensioLabs Security Checker is a command line tool that checks if your +application uses dependencies with known security vulnerabilities. It uses the +[SensioLabs Security Check Web service][1] and the [Security Advisories Database][2]. + +Usage +----- + +Download the [security-checker.phar][3] file: + + $ php security-checker.phar security:check /path/to/composer.lock + +Use the code from the repository directly: + + $ composer install + $ php security-checker security:check /path/to/composer.lock + +Integration +----------- + +The checker uses the Symfony Console component; so, you can easily integrate +the checker into your own project: + + * by using the `SecurityCheckerCommand` class into your Symfony Console + application; + + * by using the `SecurityChecker` class directly into your own code: + + use SensioLabs\Security\SecurityChecker; + + $checker = new SecurityChecker(); + $alerts = $checker->check('/path/to/composer.lock'); + +[1]: http://security.sensiolabs.org/ +[2]: https://github.com/FriendsOfPHP/security-advisories +[3]: http://get.sensiolabs.org/security-checker.phar diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Command/SecurityCheckerCommand.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Command/SecurityCheckerCommand.php new file mode 100644 index 0000000..e8effc5 --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Command/SecurityCheckerCommand.php @@ -0,0 +1,108 @@ +checker = $checker; + + parent::__construct(); + } + + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('security:check') + ->setDefinition(array( + new InputArgument('lockfile', InputArgument::OPTIONAL, 'The path to the composer.lock file', 'composer.lock'), + new InputOption('format', '', InputOption::VALUE_REQUIRED, 'The output format', 'text'), + new InputOption('end-point', '', InputOption::VALUE_REQUIRED, 'The security checker server URL'), + new InputOption('timeout', '', InputOption::VALUE_REQUIRED, 'The HTTP timeout in seconds'), + )) + ->setDescription('Checks security issues in your project dependencies') + ->setHelp(<<%command.name% command looks for security issues in the +project dependencies: + +php %command.full_name% + +You can also pass the path to a composer.lock file as an argument: + +php %command.full_name% /path/to/composer.lock + +By default, the command displays the result in plain text, but you can also +configure it to output JSON instead by using the --format option: + +php %command.full_name% /path/to/composer.lock --format=json +EOF + ); + } + + /** + * @see Command + * @see SecurityChecker + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($endPoint = $input->getOption('end-point')) { + $this->checker->getCrawler()->setEndPoint($endPoint); + } + + if ($timeout = $input->getOption('timeout')) { + $this->checker->getCrawler()->setTimeout($timeout); + } + + try { + $vulnerabilities = $this->checker->check($input->getArgument('lockfile')); + } catch (ExceptionInterface $e) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($e->getMessage(), 'error', true)); + + return 1; + } + + switch ($input->getOption('format')) { + case 'json': + $formatter = new JsonFormatter(); + break; + case 'simple': + $formatter = new SimpleFormatter($this->getHelperSet()->get('formatter')); + break; + case 'text': + default: + $formatter = new TextFormatter($this->getHelperSet()->get('formatter')); + } + + $formatter->displayResults($output, $input->getArgument('lockfile'), $vulnerabilities); + + if ($this->checker->getLastVulnerabilityCount() > 0) { + return 1; + } + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/BaseCrawler.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/BaseCrawler.php new file mode 100644 index 0000000..785ed07 --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/BaseCrawler.php @@ -0,0 +1,83 @@ +timeout = $timeout; + } + + /** + * {@inheritdoc} + */ + public function setEndPoint($endPoint) + { + $this->endPoint = $endPoint; + } + + /** + * {@inheritdoc} + */ + public function check($lock) + { + $certFile = $this->getCertFile(); + + try { + list($headers, $body) = $this->doCheck($lock, $certFile); + } catch (\Exception $e) { + if (__DIR__.'/../Resources/security.sensiolabs.org.crt' !== $certFile) { + unlink($certFile); + } + + throw $e; + } + + if (!(preg_match('/X-Alerts: (\d+)/', $headers, $matches) || 2 == count($matches))) { + throw new RuntimeException('The web service did not return alerts count.'); + } + + return array(intval($matches[1]), json_decode($body, true)); + } + + /** + * @return array An array where the first element is a headers string and second one the response body + */ + abstract protected function doCheck($lock, $certFile); + + private function getCertFile() + { + $certFile = __DIR__.'/../Resources/security.sensiolabs.org.crt'; + if ('phar://' !== substr(__FILE__, 0, 7)) { + return $certFile; + } + + $tmpFile = tempnam(sys_get_temp_dir(), 'sls'); + if (false === @copy($certFile, $tmpFile)) { + throw new RuntimeException(sprintf('Unable to copy the certificate in "%s".', $tmpFile)); + } + + return $tmpFile; + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/CrawlerInterface.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/CrawlerInterface.php new file mode 100644 index 0000000..14d0b67 --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/CrawlerInterface.php @@ -0,0 +1,31 @@ + PHP_VERSION_ID >= 50500 ? new \CurlFile($lock) : '@'.$lock); + + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_URL, $this->endPoint); + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/json')); + curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->timeout); + curl_setopt($curl, CURLOPT_TIMEOUT, 10); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($curl, CURLOPT_MAXREDIRS, 3); + curl_setopt($curl, CURLOPT_FAILONERROR, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($curl, CURLOPT_CAINFO, $certFile); + + $response = curl_exec($curl); + + if (false === $response) { + $error = curl_error($curl); + curl_close($curl); + + throw new RuntimeException(sprintf('An error occurred: %s.', $error)); + } + + $headersSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); + $headers = substr($response, 0, $headersSize); + $body = substr($response, $headersSize); + + $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + curl_close($curl); + + if (400 == $statusCode) { + $data = json_decode($body, true); + $error = $data['error']; + + throw new RuntimeException($error); + } + + if (200 != $statusCode) { + throw new RuntimeException(sprintf('The web service failed for an unknown reason (HTTP %s).', $statusCode)); + } + + return array($headers, $body); + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/DefaultCrawler.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/DefaultCrawler.php new file mode 100644 index 0000000..af06a9b --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/DefaultCrawler.php @@ -0,0 +1,49 @@ +crawler = function_exists('curl_init') ? new CurlCrawler() : new FileGetContentsCrawler(); + } + + /** + * {@inheritdoc} + */ + public function check($lock) + { + return $this->crawler->check($lock); + } + + /** + * {@inheritdoc} + */ + public function setTimeout($timeout) + { + $this->crawler->setTimeout($timeout); + } + + /** + * {@inheritdoc} + */ + public function setEndPoint($endPoint) + { + $this->crawler->setEndPoint($endPoint); + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/FileGetContentsCrawler.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/FileGetContentsCrawler.php new file mode 100644 index 0000000..c77921b --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Crawler/FileGetContentsCrawler.php @@ -0,0 +1,78 @@ + array( + 'method' => 'POST', + 'header' => "Content-Type: multipart/form-data; boundary=$boundary\r\nAccept: application/json", + 'content' => "--$boundary\r\nContent-Disposition: form-data; name=\"lock\"; filename=\"$lock\"\r\nContent-Type: application/octet-stream\r\n\r\n".file_get_contents($lock)."\r\n--$boundary\r\n--\r\n", + 'ignore_errors' => true, + 'follow_location' => true, + 'max_redirects' => 3, + 'timeout' => $this->timeout, + ), + 'ssl' => array( + 'cafile' => $certFile, + 'verify_peer' => 1, + 'verify_host' => 2, + ), + )); + + $level = error_reporting(0); + $body = file_get_contents($this->endPoint, 0, $context); + error_reporting($level); + if (false === $body) { + $error = error_get_last(); + + throw new RuntimeException(sprintf('An error occurred: %s.', $error['message'])); + } + + // status code + if (!preg_match('{HTTP/\d\.\d (\d+) }i', $http_response_header[0], $match)) { + throw new RuntimeException('An unknown error occurred.'); + } + + $statusCode = $match[1]; + if (400 == $statusCode) { + $data = json_decode($body, true); + + throw new RuntimeException($data['error']); + } + + if (200 != $statusCode) { + throw new RuntimeException(sprintf('The web service failed for an unknown reason (HTTP %s).', $statusCode)); + } + + $headers = ''; + foreach ($http_response_header as $header) { + if (false !== strpos($header, 'X-Alerts: ')) { + $headers = $header; + } + } + + return array($headers, $body); + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Exception/ExceptionInterface.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Exception/ExceptionInterface.php new file mode 100644 index 0000000..df9d8a0 --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Exception/ExceptionInterface.php @@ -0,0 +1,16 @@ +write(json_encode($vulnerabilities, JSON_PRETTY_PRINT)); + } else { + $output->write(json_encode($vulnerabilities)); + } + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Formatters/SimpleFormatter.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Formatters/SimpleFormatter.php new file mode 100644 index 0000000..9f3fa46 --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Formatters/SimpleFormatter.php @@ -0,0 +1,68 @@ +formatter = $formatter; + } + + /** + * Displays a security report as simple plain text. + * + * @param OutputInterface $output + * @param string $lockFilePath The file path to the checked lock file + * @param array $vulnerabilities An array of vulnerabilities + */ + public function displayResults(OutputInterface $output, $lockFilePath, array $vulnerabilities) + { + $output->writeln(sprintf('Security Check Report: %s', realpath($lockFilePath))); + + if ($count = count($vulnerabilities)) { + $status = 'CRITICAL'; + $style = 'error'; + } else { + $status = 'OK'; + $style = 'info'; + } + + $output->writeln(sprintf('<%s>[%s] %d %s known vulnerabilities', $style, $status, $count, 1 === $count ? 'package has' : 'packages have')); + + if (0 !== $count) { + $output->write("\n"); + + foreach ($vulnerabilities as $dependency => $issues) { + $dependencyFullName = $dependency.' ('.$issues['version'].')'; + $output->writeln(''.$dependencyFullName."\n".str_repeat('-', strlen($dependencyFullName))."\n"); + + foreach ($issues['advisories'] as $issue => $details) { + $output->write(' * '); + if ($details['cve']) { + $output->write(''.$details['cve'].': '); + } + $output->writeln($details['title']); + + if ('' !== $details['link']) { + $output->writeln(' '.$details['link']); + } + + $output->writeln(''); + } + } + } + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Formatters/TextFormatter.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/Formatters/TextFormatter.php new file mode 100644 index 0000000..32cb9ae --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Formatters/TextFormatter.php @@ -0,0 +1,74 @@ +formatter = $formatter; + } + + /** + * Displays a security report as plain text. + * + * @param OutputInterface $output + * @param string $lockFilePath The file path to the checked lock file + * @param array $vulnerabilities An array of vulnerabilities + */ + public function displayResults(OutputInterface $output, $lockFilePath, array $vulnerabilities) + { + $output->writeln("\nSecurity Check Report\n~~~~~~~~~~~~~~~~~~~~~\n"); + $output->writeln(sprintf('Checked file: %s', realpath($lockFilePath))); + $output->write("\n"); + + if ($count = count($vulnerabilities)) { + $status = 'CRITICAL'; + $style = 'error'; + } else { + $status = 'OK'; + $style = 'bg=green;fg=white'; + } + + $message = sprintf('%d %s known vulnerabilities', $count, 1 === $count ? 'package has' : 'packages have'); + $output->writeln($this->formatter->formatBlock(array('['.$status.']', $message), $style, true)); + $output->write("\n"); + + if (0 !== $count) { + foreach ($vulnerabilities as $dependency => $issues) { + $dependencyFullName = $dependency.' ('.$issues['version'].')'; + $output->writeln(''.$dependencyFullName."\n".str_repeat('-', strlen($dependencyFullName))."\n"); + + foreach ($issues['advisories'] as $issue => $details) { + $output->write(' * '); + if ($details['cve']) { + $output->write(''.$details['cve'].': '); + } + $output->writeln($details['title']); + + if ('' !== $details['link']) { + $output->writeln(' '.$details['link']); + } + + $output->writeln(''); + } + } + } + + $output->writeln(' This checker can only detect vulnerabilities that are referenced'); + $output->writeln(' Disclaimer in the SensioLabs security advisories database. Execute this'); + $output->writeln(" command regularly to check the newly discovered vulnerabilities.\n"); + } +} diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/Resources/security.sensiolabs.org.crt b/vendor/sensiolabs/security-checker/SensioLabs/Security/Resources/security.sensiolabs.org.crt new file mode 100644 index 0000000..20585f1 --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/Resources/security.sensiolabs.org.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- diff --git a/vendor/sensiolabs/security-checker/SensioLabs/Security/SecurityChecker.php b/vendor/sensiolabs/security-checker/SensioLabs/Security/SecurityChecker.php new file mode 100644 index 0000000..0f71c1c --- /dev/null +++ b/vendor/sensiolabs/security-checker/SensioLabs/Security/SecurityChecker.php @@ -0,0 +1,67 @@ +crawler = null === $crawler ? new DefaultCrawler() : $crawler; + } + + /** + * Checks a composer.lock file. + * + * @param string $lock The path to the composer.lock file + * + * @return array An array of vulnerabilities + * + * @throws RuntimeException When the lock file does not exist + * @throws RuntimeException When the certificate can not be copied + */ + public function check($lock) + { + if (is_dir($lock) && file_exists($lock.'/composer.lock')) { + $lock = $lock.'/composer.lock'; + } elseif (preg_match('/composer\.json$/', $lock)) { + $lock = str_replace('composer.json', 'composer.lock', $lock); + } + + if (!is_file($lock)) { + throw new RuntimeException('Lock file does not exist.'); + } + + list($this->vulnerabilityCount, $vulnerabilities) = $this->crawler->check($lock); + + return $vulnerabilities; + } + + public function getLastVulnerabilityCount() + { + return $this->vulnerabilityCount; + } + + /** + * @return CrawlerInterface + */ + public function getCrawler() + { + return $this->crawler; + } +} diff --git a/vendor/sensiolabs/security-checker/box.json b/vendor/sensiolabs/security-checker/box.json new file mode 100644 index 0000000..fd3d642 --- /dev/null +++ b/vendor/sensiolabs/security-checker/box.json @@ -0,0 +1,25 @@ +{ + "output": "security-checker.phar", + "chmod": "0755", + "compactors": [ + "Herrera\\Box\\Compactor\\Php" + ], + "extract": false, + "main": "security-checker", + "files": [ + "LICENSE" + ], + "finder": [ + { + "name": "*.*", + "exclude": ["Tests"], + "in": "vendor" + }, + { + "name": ["*.*", "*.crt"], + "in": "SensioLabs" + } + ], + "stub": true, + "web": false +} diff --git a/vendor/sensiolabs/security-checker/composer.json b/vendor/sensiolabs/security-checker/composer.json new file mode 100644 index 0000000..5219d12 --- /dev/null +++ b/vendor/sensiolabs/security-checker/composer.json @@ -0,0 +1,23 @@ +{ + "name": "sensiolabs/security-checker", + "description": "A security checker for your composer.lock", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "require": { + "symfony/console": "~2.0|~3.0" + }, + "bin": ["security-checker"], + "autoload": { + "psr-0": { "SensioLabs\\Security": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/sensiolabs/security-checker/security-checker b/vendor/sensiolabs/security-checker/security-checker new file mode 100755 index 0000000..d95dadd --- /dev/null +++ b/vendor/sensiolabs/security-checker/security-checker @@ -0,0 +1,32 @@ +#!/usr/bin/env php +add(new SecurityCheckerCommand(new SecurityChecker())); +$console->run(); diff --git a/vendor/swiftmailer/swiftmailer/.gitattributes b/vendor/swiftmailer/swiftmailer/.gitattributes new file mode 100644 index 0000000..33b8efd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.gitattributes @@ -0,0 +1,9 @@ +*.crt -crlf +*.key -crlf +*.srl -crlf +*.pub -crlf +*.priv -crlf +*.txt -crlf + +# ignore /notes in the git-generated distributed .zip archive +/notes export-ignore diff --git a/vendor/swiftmailer/swiftmailer/.travis.yml b/vendor/swiftmailer/swiftmailer/.travis.yml new file mode 100644 index 0000000..cadee01 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/.travis.yml @@ -0,0 +1,25 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm-nightly + +before_script: + - cp tests/acceptance.conf.php.default tests/acceptance.conf.php + - cp tests/smoke.conf.php.default tests/smoke.conf.php + - composer self-update + - composer update --no-interaction --prefer-source + - gem install mailcatcher + - mailcatcher --smtp-port 4456 + +script: + - phpunit --verbose + +matrix: + allow_failures: + - php: 5.6 + - php: hhvm-nightly + fast_finish: true diff --git a/vendor/swiftmailer/swiftmailer/LICENSE b/vendor/swiftmailer/swiftmailer/LICENSE new file mode 100644 index 0000000..674bb2a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/swiftmailer/swiftmailer/VERSION b/vendor/swiftmailer/swiftmailer/VERSION new file mode 100644 index 0000000..e61a6df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/VERSION @@ -0,0 +1 @@ +Swift-5.4.1 diff --git a/vendor/swiftmailer/swiftmailer/composer.json b/vendor/swiftmailer/swiftmailer/composer.json new file mode 100644 index 0000000..a7b5e44 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/composer.json @@ -0,0 +1,31 @@ +{ + "name": "swiftmailer/swiftmailer", + "type": "library", + "description": "Swiftmailer, free feature-rich PHP mailer", + "keywords": ["mail","mailer","email"], + "homepage": "http://swiftmailer.org", + "license": "MIT", + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1,<0.9.4" + }, + "autoload": { + "files": ["lib/swift_required.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/doc/headers.rst b/vendor/swiftmailer/swiftmailer/doc/headers.rst new file mode 100644 index 0000000..6aec23f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/headers.rst @@ -0,0 +1,742 @@ +Message Headers +=============== + +Sometimes you'll want to add your own headers to a message or modify/remove +headers that are already present. You work with the message's HeaderSet to do +this. + +Header Basics +------------- + +All MIME entities in Swift Mailer -- including the message itself -- +store their headers in a single object called a HeaderSet. This HeaderSet is +retrieved with the ``getHeaders()`` method. + +As mentioned in the previous chapter, everything that forms a part of a message +in Swift Mailer is a MIME entity that is represented by an instance of +``Swift_Mime_MimeEntity``. This includes -- most notably -- the message object +itself, attachments, MIME parts and embedded images. Each of these MIME entities +consists of a body and a set of headers that describe the body. + +For all of the "standard" headers in these MIME entities, such as the +``Content-Type``, there are named methods for working with them, such as +``setContentType()`` and ``getContentType()``. This is because headers are a +moderately complex area of the library. Each header has a slightly different +required structure that it must meet in order to comply with the standards that +govern email (and that are checked by spam blockers etc). + +You fetch the HeaderSet from a MIME entity like so: + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + // Fetch the HeaderSet from a Message object + $headers = $message->getHeaders(); + + $attachment = Swift_Attachment::fromPath('document.pdf'); + + // Fetch the HeaderSet from an attachment object + $headers = $attachment->getHeaders(); + +The job of the HeaderSet is to contain and manage instances of Header objects. +Depending upon the MIME entity the HeaderSet came from, the contents of the +HeaderSet will be different, since an attachment for example has a different +set of headers to those in a message. + +You can find out what the HeaderSet contains with a quick loop, dumping out +the names of the headers: + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + printf("%s
    \n", $header->getFieldName()); + } + + /* + Content-Transfer-Encoding + Content-Type + MIME-Version + Date + Message-ID + From + Subject + To + */ + +You can also dump out the rendered HeaderSet by calling its ``toString()`` +method: + +.. code-block:: php + + echo $headers->toString(); + + /* + Message-ID: <1234869991.499a9ee7f1d5e@swift.generated> + Date: Tue, 17 Feb 2009 22:26:31 +1100 + Subject: Awesome subject! + From: sender@example.org + To: recipient@example.org + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + */ + +Where the complexity comes in is when you want to modify an existing header. +This complexity comes from the fact that each header can be of a slightly +different type (such as a Date header, or a header that contains email +addresses, or a header that has key-value parameters on it!). Each header in the +HeaderSet is an instance of ``Swift_Mime_Header``. They all have common +functionality, but knowing exactly what type of header you're working with will +allow you a little more control. + +You can determine the type of header by comparing the return value of its +``getFieldType()`` method with the constants ``TYPE_TEXT``, +``TYPE_PARAMETERIZED``, ``TYPE_DATE``, ``TYPE_MAILBOX``, ``TYPE_ID`` and +``TYPE_PATH`` which are defined in ``Swift_Mime_Header``. + + +.. code-block:: php + + foreach ($headers->getAll() as $header) { + switch ($header->getFieldType()) { + case Swift_Mime_Header::TYPE_TEXT: $type = 'text'; + break; + case Swift_Mime_Header::TYPE_PARAMETERIZED: $type = 'parameterized'; + break; + case Swift_Mime_Header::TYPE_MAILBOX: $type = 'mailbox'; + break; + case Swift_Mime_Header::TYPE_DATE: $type = 'date'; + break; + case Swift_Mime_Header::TYPE_ID: $type = 'ID'; + break; + case Swift_Mime_Header::TYPE_PATH: $type = 'path'; + break; + } + printf("%s: is a %s header
    \n", $header->getFieldName(), $type); + } + + /* + Content-Transfer-Encoding: is a text header + Content-Type: is a parameterized header + MIME-Version: is a text header + Date: is a date header + Message-ID: is a ID header + From: is a mailbox header + Subject: is a text header + To: is a mailbox header + */ + +Headers can be removed from the set, modified within the set, or added to the +set. + +The following sections show you how to work with the HeaderSet and explain the +details of each implementation of ``Swift_Mime_Header`` that may +exist within the HeaderSet. + +Header Types +------------ + +Because all headers are modeled on different data (dates, addresses, text!) +there are different types of Header in Swift Mailer. Swift Mailer attempts to +categorize all possible MIME headers into more general groups, defined by a +small number of classes. + +Text Headers +~~~~~~~~~~~~ + +Text headers are the simplest type of Header. They contain textual information +with no special information included within it -- for example the Subject +header in a message. + +There's nothing particularly interesting about a text header, though it is +probably the one you'd opt to use if you need to add a custom header to a +message. It represents text just like you'd think it does. If the text +contains characters that are not permitted in a message header (such as new +lines, or non-ascii characters) then the header takes care of encoding the +text so that it can be used. + +No header -- including text headers -- in Swift Mailer is vulnerable to +header-injection attacks. Swift Mailer breaks any attempt at header injection by +encoding the dangerous data into a non-dangerous form. + +It's easy to add a new text header to a HeaderSet. You do this by calling the +HeaderSet's ``addTextHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addTextHeader('Your-Header-Name', 'the header value'); + +Changing the value of an existing text header is done by calling it's +``setValue()`` method. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('new subject'); + +When output via ``toString()``, a text header produces something like the +following: + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('amazing subject line'); + + echo $subject->toString(); + + /* + + Subject: amazing subject line + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded. This is nothing to be concerned about since +mail clients will decode them back. + +.. code-block:: php + + $subject = $message->getHeaders()->get('Subject'); + + $subject->setValue('contains – dash'); + + echo $subject->toString(); + + /* + + Subject: contains =?utf-8?Q?=E2=80=93?= dash + + */ + +Parameterized Headers +~~~~~~~~~~~~~~~~~~~~~ + +Parameterized headers are text headers that contain key-value parameters +following the textual content. The Content-Type header of a message is a +parameterized header since it contains charset information after the content +type. + +The parameterized header type is a special type of text header. It extends the +text header by allowing additional information to follow it. All of the methods +from text headers are available in addition to the methods described here. + +Adding a parameterized header to a HeaderSet is done by using the +``addParameterizedHeader()`` method which takes a text value like +``addTextHeader()`` but it also accepts an associative array of +key-value parameters. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addParameterizedHeader( + 'Header-Name', 'header value', + array('foo' => 'bar') + ); + +To change the text value of the header, call it's ``setValue()`` method just as +you do with text headers. + +To change the parameters in the header, call the header's ``setParameters()`` +method or the ``setParameter()`` method (note the pluralization). + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + // setParameters() takes an associative array + $type->setParameters(array( + 'name' => 'file.txt', + 'charset' => 'iso-8859-1' + )); + + // setParameter() takes two args for $key and $value + $type->setParameter('charset', 'iso-8859-1'); + +When output via ``toString()``, a parameterized header produces something like +the following: + +.. code-block:: php + + $type = $message->getHeaders()->get('Content-Type'); + + $type->setValue('text/html'); + $type->setParameter('charset', 'utf-8'); + + echo $type->toString(); + + /* + + Content-Type: text/html; charset=utf-8 + + */ + +If the header contains any characters that are outside of the US-ASCII range +however, they will be encoded, just like they are for text headers. This is +nothing to be concerned about since mail clients will decode them back. +Likewise, if the parameters contain any non-ascii characters they will be +encoded so that they can be transmitted safely. + +.. code-block:: php + + $attachment = Swift_Attachment::newInstance(); + + $disp = $attachment->getHeaders()->get('Content-Disposition'); + + $disp->setValue('attachment'); + $disp->setParameter('filename', 'report–may.pdf'); + + echo $disp->toString(); + + /* + + Content-Disposition: attachment; filename*=utf-8''report%E2%80%93may.pdf + + */ + +Date Headers +~~~~~~~~~~~~ + +Date headers contains an RFC 2822 formatted date (i.e. what PHP's ``date('r')`` +returns). They are used anywhere a date or time is needed to be presented as a +message header. + +The data on which a date header is modeled is simply a UNIX timestamp such as +that returned by ``time()`` or ``strtotime()``. The timestamp is used to create +a correctly structured RFC 2822 formatted date such as +``Tue, 17 Feb 2009 22:26:31 +1100``. + +The obvious place this header type is used is in the ``Date:`` header of the +message itself. + +It's easy to add a new date header to a HeaderSet. You do this by calling +the HeaderSet's ``addDateHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addDateHeader('Your-Header-Name', strtotime('3 days ago')); + +Changing the value of an existing date header is done by calling it's +``setTimestamp()`` method. + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + $date->setTimestamp(time()); + +When output via ``toString()``, a date header produces something like the +following: + +.. code-block:: php + + $date = $message->getHeaders()->get('Date'); + + echo $date->toString(); + + /* + + Date: Wed, 18 Feb 2009 13:35:02 +1100 + + */ + +Mailbox (e-mail address) Headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Mailbox headers contain one or more email addresses, possibly with +personalized names attached to them. The data on which they are modeled is +represented by an associative array of email addresses and names. + +Mailbox headers are probably the most complex header type to understand in +Swift Mailer because they accept their input as an array which can take various +forms, as described in the previous chapter. + +All of the headers that contain e-mail addresses in a message -- with the +exception of ``Return-Path:`` which has a stricter syntax -- use this header +type. That is, ``To:``, ``From:`` etc. + +You add a new mailbox header to a HeaderSet by calling the HeaderSet's +``addMailboxHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addMailboxHeader('Your-Header-Name', array( + 'person1@example.org' => 'Person Name One', + 'person2@example.org', + 'person3@example.org', + 'person4@example.org' => 'Another named person' + )); + +Changing the value of an existing mailbox header is done by calling it's +``setNameAddresses()`` method. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'joe@example.org' => 'Joe Bloggs', + 'john@example.org' => 'John Doe', + 'no-name@example.org' + )); + +If you don't wish to concern yourself with the complicated accepted input +formats accepted by ``setNameAddresses()`` as described in the previous chapter +and you only want to set one or more addresses (not names) then you can just +use the ``setAddresses()`` method instead. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses(array( + 'joe@example.org', + 'john@example.org', + 'no-name@example.org' + )); + +.. note:: + + Both methods will accept the above input format in practice. + +If all you want to do is set a single address in the header, you can use a +string as the input parameter to ``setAddresses()`` and/or +``setNameAddresses()``. + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setAddresses('joe-bloggs@example.org'); + +When output via ``toString()``, a mailbox header produces something like the +following: + +.. code-block:: php + + $to = $message->getHeaders()->get('To'); + + $to->setNameAddresses(array( + 'person1@example.org' => 'Name of Person', + 'person2@example.org', + 'person3@example.org' => 'Another Person' + )); + + echo $to->toString(); + + /* + + To: Name of Person , person2@example.org, Another Person + + + */ + +ID Headers +~~~~~~~~~~ + +ID headers contain identifiers for the entity (or the message). The most +notable ID header is the Message-ID header on the message itself. + +An ID that exists inside an ID header looks more-or-less less like an email +address. For example, ``<1234955437.499becad62ec2@example.org>``. +The part to the left of the @ sign is usually unique, based on the current time +and some random factor. The part on the right is usually a domain name. + +Any ID passed to the header's ``setId()`` method absolutely MUST conform to +this structure, otherwise you'll get an Exception thrown at you by Swift Mailer +(a ``Swift_RfcComplianceException``). This is to ensure that the generated +email complies with relevant RFC documents and therefore is less likely to be +blocked as spam. + +It's easy to add a new ID header to a HeaderSet. You do this by calling +the HeaderSet's ``addIdHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addIdHeader('Your-Header-Name', '123456.unqiue@example.org'); + +Changing the value of an existing date header is done by calling its +``setId()`` method. + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + $msgId->setId(time() . '.' . uniqid('thing') . '@example.org'); + +When output via ``toString()``, an ID header produces something like the +following: + +.. code-block:: php + + $msgId = $message->getHeaders()->get('Message-ID'); + + echo $msgId->toString(); + + /* + + Message-ID: <1234955437.499becad62ec2@example.org> + + */ + +Path Headers +~~~~~~~~~~~~ + +Path headers are like very-restricted mailbox headers. They contain a single +email address with no associated name. The Return-Path header of a message is +a path header. + +You add a new path header to a HeaderSet by calling the HeaderSet's +``addPathHeader()`` method. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $headers = $message->getHeaders(); + + $headers->addPathHeader('Your-Header-Name', 'person@example.org'); + + +Changing the value of an existing path header is done by calling its +``setAddress()`` method. + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('my-address@example.org'); + +When output via ``toString()``, a path header produces something like the +following: + +.. code-block:: php + + $return = $message->getHeaders()->get('Return-Path'); + + $return->setAddress('person@example.org'); + + echo $return->toString(); + + /* + + Return-Path: + + */ + +Header Operations +----------------- + +Working with the headers in a message involves knowing how to use the methods +on the HeaderSet and on the individual Headers within the HeaderSet. + +Adding new Headers +~~~~~~~~~~~~~~~~~~ + +New headers can be added to the HeaderSet by using one of the provided +``add..Header()`` methods. + +To add a header to a MIME entity (such as the message): + +Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Add the header to the HeaderSet by calling one of the ``add..Header()`` + methods. + +The added header will appear in the message when it is sent. + +.. code-block:: php + + // Adding a custom header to a message + $message = Swift_Message::newInstance(); + $headers = $message->getHeaders(); + $headers->addTextHeader('X-Mine', 'something here'); + + // Adding a custom header to an attachment + $attachment = Swift_Attachment::fromPath('/path/to/doc.pdf'); + $attachment->getHeaders()->addDateHeader('X-Created-Time', time()); + +Retrieving Headers +~~~~~~~~~~~~~~~~~~ + +Headers are retrieved through the HeaderSet's ``get()`` and ``getAll()`` +methods. + +To get a header, or several headers from a MIME entity: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the header(s) from the HeaderSet by calling either ``get()`` or + ``getAll()``. + +When using ``get()`` a single header is returned that matches the name (case +insensitive) that is passed to it. When using ``getAll()`` with a header name, +an array of headers with that name are returned. Calling ``getAll()`` with no +arguments returns an array of all headers present in the entity. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``getAll()`` exists to fetch all + headers with a specified name. In addition, ``get()`` accepts an optional + numerical index, starting from zero to specify which header you want more + specifically. + +.. note:: + + If you want to modify the contents of the header and you don't know for + sure what type of header it is then you may need to check the type by + calling its ``getFieldType()`` method. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Get the To: header + $toHeader = $headers->get('To'); + + // Get all headers named "X-Foo" + $fooHeaders = $headers->getAll('X-Foo'); + + // Get the second header named "X-Foo" + $foo = $headers->get('X-Foo', 1); + + // Get all headers that are present + $all = $headers->getAll(); + +Check if a Header Exists +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can check if a named header is present in a HeaderSet by calling its +``has()`` method. + +To check if a header exists: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``has()`` method specifying the header you're looking + for. + +If the header exists, ``true`` will be returned or ``false`` if not. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``has()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Check if the To: header exists + if ($headers->has('To')) { + echo 'To: exists'; + } + + // Check if an X-Foo header exists twice (i.e. check for the 2nd one) + if ($headers->has('X-Foo', 1)) { + echo 'Second X-Foo header exists'; + } + +Removing Headers +~~~~~~~~~~~~~~~~ + +Removing a Header from the HeaderSet is done by calling the HeaderSet's +``remove()`` or ``removeAll()`` methods. + +To remove an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Call the HeaderSet's ``remove()`` or ``removeAll()`` methods specifying the + header you want to remove. + +When calling ``remove()`` a single header will be removed. When calling +``removeAll()`` all headers with the given name will be removed. If no headers +exist with the given name, no errors will occur. + +.. note:: + + It's valid for some headers to appear more than once in a message (e.g. + the Received header). For this reason ``remove()`` accepts an optional + numerical index, starting from zero to specify which header you want to + check more specifically. For the same reason, ``removeAll()`` exists to + remove all headers that have the given name. + + .. code-block:: php + + $headers = $message->getHeaders(); + + // Remove the Subject: header + $headers->remove('Subject'); + + // Remove all X-Foo headers + $headers->removeAll('X-Foo'); + + // Remove only the second X-Foo header + $headers->remove('X-Foo', 1); + +Modifying a Header's Content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To change a Header's content you should know what type of header it is and then +call it's appropriate setter method. All headers also have a +``setFieldBodyModel()`` method that accepts a mixed parameter and delegates to +the correct setter. + +To modify an existing header: + +* Get the HeaderSet from the entity by via its ``getHeaders()`` method. + +* Get the Header by using the HeaderSet's ``get()``. + +* Call the Header's appropriate setter method or call the header's + ``setFieldBodyModel()`` method. + +The header will be updated inside the HeaderSet and the changes will be seen +when the message is sent. + +.. code-block:: php + + $headers = $message->getHeaders(); + + // Change the Subject: header + $subj = $headers->get('Subject'); + $subj->setValue('new subject here'); + + // Change the To: header + $to = $headers->get('To'); + $to->setNameAddresses(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); + + // Using the setFieldBodyModel() just delegates to the correct method + // So here to calls setNameAddresses() + $to->setFieldBodyModel(array( + 'person@example.org' => 'Person', + 'thing@example.org' + )); diff --git a/vendor/swiftmailer/swiftmailer/doc/help-resources.rst b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst new file mode 100644 index 0000000..4208935 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/help-resources.rst @@ -0,0 +1,44 @@ +Getting Help +============ + +There are a number of ways you can get help when using Swift Mailer, depending +upon the nature of your problem. For bug reports and feature requests create a +new ticket in GitHub. For general advice ask on the Google Group +(swiftmailer). + +Submitting Bugs & Feature Requests +---------------------------------- + +Bugs and feature requests should be posted on GitHub. + +If you post a bug or request a feature in the forum, or on the Google Group +you will most likely be asked to create a ticket in `GitHub`_ since it is +simply not feasible to manage such requests from a number of a different +sources. + +When you go to GitHub you will be asked to create a username and password +before you can create a ticket. This is free and takes very little time. + +When you create your ticket, do not assign it to any milestones. A developer +will assess your ticket and re-assign it as needed. + +If your ticket is reporting a bug present in the current version, which was +not present in the previous version please include the tag "regression" in +your ticket. + +GitHub will update you when work is performed on your ticket. + +Ask on the Google Group +----------------------- + +You can seek advice at Google Groups, within the "swiftmailer" `group`_. + +You can post messages to this group if you want help, or there's something you +wish to discuss with the developers and with other users. + +This is probably the fastest way to get help since it is primarily email-based +for most users, though bug reports should not be posted here since they may +not be resolved. + +.. _`GitHub`: https://github.com/swiftmailer/swiftmailer/issues +.. _`group`: http://groups.google.com/group/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst new file mode 100644 index 0000000..978dca2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/including-the-files.rst @@ -0,0 +1,46 @@ +Including Swift Mailer (Autoloading) +==================================== + +If you are using Composer, Swift Mailer will be automatically autoloaded. + +If not, you can use the built-in autoloader by requiring the +``swift_required.php`` file:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + /* rest of code goes here */ + +If you want to override the default Swift Mailer configuration, call the +``init()`` method on the ``Swift`` class and pass it a valid PHP callable (a +PHP function name, a PHP 5.3 anonymous function, ...):: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + function swiftmailer_configurator() { + // configure Swift Mailer + + Swift_DependencyContainer::getInstance()->... + Swift_Preferences::getInstance()->... + } + + Swift::init('swiftmailer_configurator'); + + /* rest of code goes here */ + +The advantage of using the ``init()`` method is that your code will be +executed only if you use Swift Mailer in your script. + +.. note:: + + While Swift Mailer's autoloader is designed to play nicely with other + autoloaders, sometimes you may have a need to avoid using Swift Mailer's + autoloader and use your own instead. Include the ``swift_init.php`` + instead of the ``swift_required.php`` if you need to do this. The very + minimum include is the ``swift_init.php`` file since Swift Mailer will not + work without the dependency injection this file sets up: + + .. code-block:: php + + require_once '/path/to/swift-mailer/lib/swift_init.php'; + + /* rest of code goes here */ diff --git a/vendor/swiftmailer/swiftmailer/doc/index.rst b/vendor/swiftmailer/swiftmailer/doc/index.rst new file mode 100644 index 0000000..a1a0a92 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/index.rst @@ -0,0 +1,16 @@ +Swiftmailer +=========== + +.. toctree:: + :maxdepth: 2 + + introduction + overview + installing + help-resources + including-the-files + messages + headers + sending + plugins + japanese diff --git a/vendor/swiftmailer/swiftmailer/doc/installing.rst b/vendor/swiftmailer/swiftmailer/doc/installing.rst new file mode 100644 index 0000000..557211d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/installing.rst @@ -0,0 +1,89 @@ +Installing the Library +====================== + +Installing with Composer +------------------------ + +The recommended way to install Swiftmailer is via Composer: + +.. code-block:: bash + + $ php composer.phar require swiftmailer/swiftmailer @stable + +Installing from Git +------------------- + +It's possible to download and install Swift Mailer directly from github.com if +you want to keep up-to-date with ease. + +Swift Mailer's source code is kept in a git repository at github.com so you +can get the source directly from the repository. + +.. note:: + + You do not need to have git installed to use Swift Mailer from GitHub. If + you don't have git installed, go to `GitHub`_ and click the "Download" + button. + +Cloning the Repository +~~~~~~~~~~~~~~~~~~~~~~ + +The repository can be cloned from git://github.com/swiftmailer/swiftmailer.git +using the ``git clone`` command. + +You will need to have ``git`` installed before you can use the +``git clone`` command. + +To clone the repository: + +* Open your favorite terminal environment (command line). + +* Move to the directory you want to clone to. + +* Run the command ``git clone git://github.com/swiftmailer/swiftmailer.git + swiftmailer``. + +The source code will be downloaded into a directory called "swiftmailer". + +The example shows the process on a UNIX-like system such as Linux, BSD or Mac +OS X. + +.. code-block:: bash + + $ cd source_code/ + $ git clone git://github.com/swiftmailer/swiftmailer.git swiftmailer + Initialized empty Git repository in /Users/chris/source_code/swiftmailer/.git/ + remote: Counting objects: 6815, done. + remote: Compressing objects: 100% (2761/2761), done. + remote: Total 6815 (delta 3641), reused 6326 (delta 3286) + Receiving objects: 100% (6815/6815), 4.35 MiB | 162 KiB/s, done. + Resolving deltas: 100% (3641/3641), done. + Checking out files: 100% (1847/1847), done. + $ cd swiftmailer/ + $ ls + CHANGES LICENSE ... + $ + +Troubleshooting +--------------- + +Swift Mailer does not work when used with function overloading as implemented +by ``mbstring`` (``mbstring.func_overload`` set to ``2``). A workaround is to +temporarily change the internal encoding to ``ASCII`` when sending an email: + +.. code-block:: php + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + // Create your message and send it with Swift Mailer + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + +.. _`GitHub`: http://github.com/swiftmailer/swiftmailer diff --git a/vendor/swiftmailer/swiftmailer/doc/introduction.rst b/vendor/swiftmailer/swiftmailer/doc/introduction.rst new file mode 100644 index 0000000..a85336b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/introduction.rst @@ -0,0 +1,135 @@ +Introduction +============ + +Swift Mailer is a component-based library for sending e-mails from PHP +applications. + +Organization of this Book +------------------------- + +This book has been written so that those who need information quickly are able +to find what they need, and those who wish to learn more advanced topics can +read deeper into each chapter. + +The book begins with an overview of Swift Mailer, discussing what's included +in the package and preparing you for the remainder of the book. + +It is possible to read this user guide just like any other book (from +beginning to end). Each chapter begins with a discussion of the contents it +contains, followed by a short code sample designed to give you a head start. +As you get further into a chapter you will learn more about Swift Mailer's +capabilities, but often you will be able to head directly to the topic you +wish to learn about. + +Throughout this book you will be presented with code samples, which most +people should find ample to implement Swift Mailer appropriately in their own +projects. We will also use diagrams where appropriate, and where we believe +readers may find it helpful we will discuss some related theory, including +reference to certain documents you are able to find online. + +Code Samples +------------ + +Code samples presented in this book will be displayed on a different colored +background in a monospaced font. Samples are not to be taken as copy & paste +code snippets. + +Code examples are used through the book to clarify what is written in text. +They will sometimes be usable as-is, but they should always be taken as +outline/pseudo code only. + +A code sample will look like this:: + + class AClass + { + ... + } + + // A Comment + $obj = new AClass($arg1, $arg2, ... ); + + /* A note about another way of doing something + $obj = AClass::newInstance($arg1, $arg2, ... ); + + */ + +The presence of 3 dots ``...`` in a code sample indicates that we have left +out a chunk of the code for brevity, they are not actually part of the code. + +We will often place multi-line comments ``/* ... */`` in the code so that we +can show alternative ways of achieving the same result. + +You should read the code examples given and try to understand them. They are +kept concise so that you are not overwhelmed with information. + +History of Swift Mailer +----------------------- + +Swift Mailer began back in 2005 as a one-class project for sending mail over +SMTP. It has since grown into the flexible component-based library that is in +development today. + +Chris Corbyn first posted Swift Mailer on a web forum asking for comments from +other developers. It was never intended as a fully supported open source +project, but members of the forum began to adopt it and make use of it. + +Very quickly feature requests were coming for the ability to add attachments +and use SMTP authentication, along with a number of other "obvious" missing +features. Considering the only alternative was PHPMailer it seemed like a good +time to bring some fresh tools to the table. Chris began working towards a +more component based, PHP5-like approach unlike the existing single-class, +legacy PHP4 approach taken by PHPMailer. + +Members of the forum offered a lot of advice and critique on the code as he +worked through this project and released versions 2 and 3 of the library in +2005 and 2006, which by then had been broken down into smaller classes +offering more flexibility and supporting plugins. To this day the Swift Mailer +team still receive a lot of feature requests from users both on the forum and +in by email. + +Until 2008 Chris was the sole developer of Swift Mailer, but entering 2009 he +gained the support of two experienced developers well-known to him: Paul +Annesley and Christopher Thompson. This has been an extremely welcome change. + +As of September 2009, Chris handed over the maintenance of Swift Mailer to +Fabien Potencier. + +Now 2009 and in its fourth major version Swift Mailer is more object-oriented +and flexible than ever, both from a usability standpoint and from a +development standpoint. + +By no means is Swift Mailer ready to call "finished". There are still many +features that can be added to the library along with the constant refactoring +that happens behind the scenes. + +It's a Library! +--------------- + +Swift Mailer is not an application - it's a library. + +To most experienced developers this is probably an obvious point to make, but +it's certainly worth mentioning. Many people often contact us having gotten +the completely wrong end of the stick in terms of what Swift Mailer is +actually for. + +It's not an application. It does not have a graphical user interface. It +cannot be opened in your web browser directly. + +It's a library (or a framework if you like). It provides a whole lot of +classes that do some very complicated things, so that you don't have to. You +"use" Swift Mailer within an application so that your application can have the +ability to send emails. + +The component-based structure of the library means that you are free to +implement it in a number of different ways and that you can pick and choose +what you want to use. + +An application on the other hand (such as a blog or a forum) is already "put +together" in a particular way, (usually) provides a graphical user interface +and most likely doesn't offer a great deal of integration with your own +application. + +Embrace the structure of the library and use the components it offers to your +advantage. Learning what the components do, rather than blindly copying and +pasting existing code will put you in a great position to build a powerful +application! diff --git a/vendor/swiftmailer/swiftmailer/doc/japanese.rst b/vendor/swiftmailer/swiftmailer/doc/japanese.rst new file mode 100644 index 0000000..34afa7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/japanese.rst @@ -0,0 +1,22 @@ +Using Swift Mailer for Japanese Emails +====================================== + +To send emails in Japanese, you need to tweak the default configuration. + +After requiring the Swift Mailer autoloader (by including the +``swift_required.php`` file), call the ``Swift::init()`` method with the +following code:: + + require_once '/path/to/swift-mailer/lib/swift_required.php'; + + Swift::init(function () { + Swift_DependencyContainer::getInstance() + ->register('mime.qpheaderencoder') + ->asAliasOf('mime.base64headerencoder'); + + Swift_Preferences::getInstance()->setCharset('iso-2022-jp'); + }); + + /* rest of code goes here */ + +That's all! diff --git a/vendor/swiftmailer/swiftmailer/doc/messages.rst b/vendor/swiftmailer/swiftmailer/doc/messages.rst new file mode 100644 index 0000000..7a19253 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/messages.rst @@ -0,0 +1,1057 @@ +Creating Messages +================= + +Creating messages in Swift Mailer is done by making use of the various MIME +entities provided with the library. Complex messages can be quickly created +with very little effort. + +Quick Reference for Creating a Message +--------------------------------------- + +You can think of creating a Message as being similar to the steps you perform +when you click the Compose button in your mail client. You give it a subject, +specify some recipients, add any attachments and write your message. + +To create a Message: + +* Call the ``newInstance()`` method of ``Swift_Message``. + +* Set your sender address (``From:``) with ``setFrom()`` or ``setSender()``. + +* Set a subject line with ``setSubject()``. + +* Set recipients with ``setTo()``, ``setCc()`` and/or ``setBcc()``. + +* Set a body with ``setBody()``. + +* Add attachments with ``attach()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the message + $message = Swift_Message::newInstance() + + // Give the message a subject + ->setSubject('Your subject') + + // Set the From address with an associative array + ->setFrom(array('john@doe.com' => 'John Doe')) + + // Set the To addresses with an associative array + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + + // Give it a body + ->setBody('Here is the message itself') + + // And optionally an alternative body + ->addPart('Here is the message itself', 'text/html') + + // Optionally add any attachments + ->attach(Swift_Attachment::fromPath('my-document.pdf')) + ; + +Message Basics +-------------- + +A message is a container for anything you want to send to somebody else. There +are several basic aspects of a message that you should know. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Header-Name: A header value + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +The Structure of a Message +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Of all of the MIME entities, a message -- ``Swift_Message`` +is the largest and most complex. It has many properties that can be updated +and it can contain other MIME entities -- attachments for example -- +nested inside it. + +A Message has a lot of different Headers which are there to present +information about the message to the recipients' mail client. Most of these +headers will be familiar to the majority of users, but we'll list the basic +ones. Although it's possible to work directly with the Headers of a Message +(or other MIME entity), the standard Headers have accessor methods provided to +abstract away the complex details for you. For example, although the Date on a +message is written with a strict format, you only need to pass a UNIX +timestamp to ``setDate()``. + ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| Header | Description | Accessors | ++===============================+====================================================================================================================================+=============================================+ +| ``Message-ID`` | Identifies this message with a unique ID, usually containing the domain name and time generated | ``getId()`` / ``setId()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Return-Path`` | Specifies where bounces should go (Swift Mailer reads this for other uses) | ``getReturnPath()`` / ``setReturnPath()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``From`` | Specifies the address of the person who the message is from. This can be multiple addresses if multiple people wrote the message. | ``getFrom()`` / ``setFrom()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Sender`` | Specifies the address of the person who physically sent the message (higher precedence than ``From:``) | ``getSender()`` / ``setSender()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``To`` | Specifies the addresses of the intended recipients | ``getTo()`` / ``setTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Cc`` | Specifies the addresses of recipients who will be copied in on the message | ``getCc()`` / ``setCc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Bcc`` | Specifies the addresses of recipients who the message will be blind-copied to. Other recipients will not be aware of these copies. | ``getBcc()`` / ``setBcc()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Reply-To`` | Specifies the address where replies are sent to | ``getReplyTo()`` / ``setReplyTo()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Subject`` | Specifies the subject line that is displayed in the recipients' mail client | ``getSubject()`` / ``setSubject()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Date`` | Specifies the date at which the message was sent | ``getDate()`` / ``setDate()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Type`` | Specifies the format of the message (usually text/plain or text/html) | ``getContentType()`` / ``setContentType()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ +| ``Content-Transfer-Encoding`` | Specifies the encoding scheme in the message | ``getEncoder()`` / ``setEncoder()`` | ++-------------------------------+------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+ + +Working with a Message Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although there are a lot of available methods on a message object, you only +need to make use of a small subset of them. Usually you'll use +``setSubject()``, ``setTo()`` and +``setFrom()`` before setting the body of your message with +``setBody()``. + +Calling methods is simple. You just call them like functions, but using the +object operator "``->``" to do so. If you've created +a message object and called it ``$message`` then you'd set a +subject on it like so: + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + $message = Swift_Message::newInstance(); + $message->setSubject('My subject'); + +All MIME entities (including a message) have a ``toString()`` +method that you can call if you want to take a look at what is going to be +sent. For example, if you ``echo +$message->toString();`` you would see something like this: + +.. code-block:: bash + + Message-ID: <1230173678.4952f5eeb1432@swift.generated> + Date: Thu, 25 Dec 2008 13:54:38 +1100 + Subject: Example subject + From: Chris Corbyn + To: Receiver Name + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: quoted-printable + + Here is the message + +We'll take a closer look at the methods you use to create your message in the +following sections. + +Adding Content to Your Message +------------------------------ + +Rich content can be added to messages in Swift Mailer with relative ease by +calling methods such as ``setSubject()``, ``setBody()``, ``addPart()`` and +``attach()``. + +Setting the Subject Line +~~~~~~~~~~~~~~~~~~~~~~~~ + +The subject line, displayed in the recipients' mail client can be set with the +``setSubject()`` method, or as a parameter to ``Swift_Message::newInstance()``. + +To set the subject of your Message: + +* Call the ``setSubject()`` method of the Message, or specify it at the time + you create the message. + + .. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('My amazing subject'); + + // Or set it after like this + $message->setSubject('My amazing subject'); + +Setting the Body Content +~~~~~~~~~~~~~~~~~~~~~~~~ + +The body of the message -- seen when the user opens the message -- +is specified by calling the ``setBody()`` method. If an alternative body is to +be included ``addPart()`` can be used. + +The body of a message is the main part that is read by the user. Often people +want to send a message in HTML format (``text/html``), other +times people want to send in plain text (``text/plain``), or +sometimes people want to send both versions and allow the recipient to choose +how they view the message. + +As a rule of thumb, if you're going to send a HTML email, always include a +plain-text equivalent of the same content so that users who prefer to read +plain text can do so. + +To set the body of your Message: + +* Call the ``setBody()`` method of the Message, or specify it at the time you + create the message. + +* Add any alternative bodies with ``addPart()``. + +If the recipient's mail client offers preferences for displaying text vs. HTML +then the mail client will present that part to the user where available. In +other cases the mail client will display the "best" part it can - usually HTML +if you've included HTML. + +.. code-block:: php + + // Pass it as a parameter when you create the message + $message = Swift_Message::newInstance('Subject here', 'My amazing body'); + + // Or set it after like this + $message->setBody('My amazing body', 'text/html'); + + // Add alternative parts with addPart() + $message->addPart('My amazing body in plain text', 'text/plain'); + +Attaching Files +--------------- + +Attachments are downloadable parts of a message and can be added by calling +the ``attach()`` method on the message. You can add attachments that exist on +disk, or you can create attachments on-the-fly. + +Attachments are actually an interesting area of Swift Mailer and something +that could put a lot of power at your fingertips if you grasp the concept +behind the way a message is held together. + +Although we refer to files sent over e-mails as "attachments" -- because +they're attached to the message -- lots of other parts of the message are +actually "attached" even if we don't refer to these parts as attachments. + +File attachments are created by the ``Swift_Attachment`` class +and then attached to the message via the ``attach()`` method on +it. For all of the "every day" MIME types such as all image formats, word +documents, PDFs and spreadsheets you don't need to explicitly set the +content-type of the attachment, though it would do no harm to do so. For less +common formats you should set the content-type -- which we'll cover in a +moment. + +Attaching Existing Files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that already exist, either on disk or at a URL can be attached to a +message with just one line of code, using ``Swift_Attachment::fromPath()``. + +You can attach files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can attach files from other +websites. + +To attach an existing file: + +* Create an attachment with ``Swift_Attachment::fromPath()``. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file with +the same filename as the one you attached. + +.. code-block:: php + + // Create the attachment + // * Note that you can technically leave the content-type parameter out + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg', 'image/jpeg'); + + // Attach it to the message + $message->attach($attachment); + + + // The two statements above could be written in one line instead + $message->attach(Swift_Attachment::fromPath('/path/to/image.jpg')); + + + // You can attach files from a URL if allow_url_fopen is on in php.ini + $message->attach(Swift_Attachment::fromPath('http://site.tld/logo.png')); + +Setting the Filename +~~~~~~~~~~~~~~~~~~~~ + +Usually you don't need to explicitly set the filename of an attachment because +the name of the attached file will be used by default, but if you want to set +the filename you use the ``setFilename()`` method of the Attachment. + +To change the filename of an attachment: + +* Call its ``setFilename()`` method. + +The attachment will be attached in the normal way, but meta-data sent inside +the email will rename the file to something else. + +.. code-block:: php + + // Create the attachment and call its setFilename() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setFilename('cool.jpg'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setFilename('cool.jpg') + ); + +Attaching Dynamic Content +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files that are generated at runtime, such as PDF documents or images created +via GD can be attached directly to a message without writing them out to disk. +Use the standard ``Swift_Attachment::newInstance()`` method. + +To attach dynamically created content: + +* Create your content as you normally would. + +* Create an attachment with ``Swift_Attachment::newInstance()``, specifying + the source data of your content along with a name and the content-type. + +* Add the attachment to the message with ``attach()``. + +The attachment will be presented to the recipient as a downloadable file +with the filename and content-type you specify. + +.. note:: + + If you would usually write the file to disk anyway you should just attach + it with ``Swift_Attachment::fromPath()`` since this will use less memory: + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $data = create_my_pdf_data(); + + // Create the attachment with your data + $attachment = Swift_Attachment::newInstance($data, 'my-file.pdf', 'application/pdf'); + + // Attach it to the message + $message->attach($attachment); + + + // You can alternatively use method chaining to build the attachment + $attachment = Swift_Attachment::newInstance() + ->setFilename('my-file.pdf') + ->setContentType('application/pdf') + ->setBody($data) + ; + +Changing the Disposition +~~~~~~~~~~~~~~~~~~~~~~~~ + +Attachments just appear as files that can be saved to the Desktop if desired. +You can make attachment appear inline where possible by using the +``setDisposition()`` method of an attachment. + +To make an attachment appear inline: + +* Call its ``setDisposition()`` method. + +The attachment will be displayed within the email viewing window if the mail +client knows how to display it. + +.. note:: + + If you try to create an inline attachment for a non-displayable file type + such as a ZIP file, the mail client should just present the attachment as + normal: + + .. code-block:: php + + // Create the attachment and call its setDisposition() method + $attachment = Swift_Attachment::fromPath('/path/to/image.jpg') + ->setDisposition('inline'); + + + // Because there's a fluid interface, you can do this in one statement + $message->attach( + Swift_Attachment::fromPath('/path/to/image.jpg')->setDisposition('inline') + ); + +Embedding Inline Media Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often people want to include an image or other content inline with a HTML +message. It's easy to do this with HTML linking to remote resources, but this +approach is usually blocked by mail clients. Swift Mailer allows you to embed +your media directly into the message. + +Mail clients usually block downloads from remote resources because this +technique was often abused as a mean of tracking who opened an email. If +you're sending a HTML email and you want to include an image in the message +another approach you can take is to embed the image directly. + +Swift Mailer makes embedding files into messages extremely streamlined. You +embed a file by calling the ``embed()`` method of the message, +which returns a value you can use in a ``src`` or +``href`` attribute in your HTML. + +Just like with attachments, it's possible to embed dynamically generated +content without having an existing file available. + +The embedded files are sent in the email as a special type of attachment that +has a unique ID used to reference them within your HTML attributes. On mail +clients that do not support embedded files they may appear as attachments. + +Although this is commonly done for images, in theory it will work for any +displayable (or playable) media type. Support for other media types (such as +video) is dependent on the mail client however. + +Embedding Existing Files +........................ + +Files that already exist, either on disk or at a URL can be embedded in a +message with just one line of code, using ``Swift_EmbeddedFile::fromPath()``. + +You can embed files that exist locally, or if your PHP installation has +``allow_url_fopen`` turned on you can embed files from other websites. + +To embed an existing file: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message with ``embed()``. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + // You can embed files from a URL if allow_url_fopen is on in php.ini + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::fromPath('image.png')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Embedding Dynamic Content +......................... + +Images that are generated at runtime, such as images created via GD can be +embedded directly to a message without writing them out to disk. Use the +standard ``Swift_Image::newInstance()`` method. + +To embed dynamically created content: + +* Create a message object with ``Swift_Message::newInstance()``. + +* Set the body as HTML, and embed a file at the correct point in the message + with ``embed()``. You will need to specify a filename and a content-type. + +The file will be displayed with the message inline with the HTML wherever its ID +is used as a ``src`` attribute. + +.. note:: + + ``Swift_Image`` and ``Swift_EmbeddedFile`` are just aliases of one + another. ``Swift_Image`` exists for semantic purposes. + +.. note:: + + You can embed files in two stages if you prefer. Just capture the return + value of ``embed()`` in a variable and use that as the ``src`` attribute. + + .. code-block:: php + + // Create your file contents in the normal way, but don't write them to disk + $img_data = create_my_image_data(); + + // Create the message + $message = Swift_Message::newInstance('My subject'); + + // Set the body + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + + + // If placing the embed() code inline becomes cumbersome + // it's easy to do this in two steps + $cid = $message->embed(Swift_Image::newInstance($img_data, 'image.jpg', 'image/jpeg')); + + $message->setBody( + '' . + ' ' . + ' ' . + ' Here is an image Image' . + ' Rest of message' . + ' ' . + '', + 'text/html' // Mark the content-type as HTML + ); + +Adding Recipients to Your Message +--------------------------------- + +Recipients are specified within the message itself via ``setTo()``, ``setCc()`` +and ``setBcc()``. Swift Mailer reads these recipients from the message when it +gets sent so that it knows where to send the message to. + +Message recipients are one of three types: + +* ``To:`` recipients -- the primary recipients (required) + +* ``Cc:`` recipients -- receive a copy of the message (optional) + +* ``Bcc:`` recipients -- hidden from other recipients (optional) + +Each type can contain one, or several addresses. It's possible to list only +the addresses of the recipients, or you can personalize the address by +providing the real name of the recipient. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +.. sidebar:: Syntax for Addresses + + If you only wish to refer to a single email address (for example your + ``From:`` address) then you can just use a string. + + .. code-block:: php + + $message->setFrom('some@address.tld'); + + If you want to include a name then you must use an associative array. + + .. code-block:: php + + $message->setFrom(array('some@address.tld' => 'The Name')); + + If you want to include multiple addresses then you must use an array. + + .. code-block:: php + + $message->setTo(array('some@address.tld', 'other@address.tld')); + + You can mix personalized (addresses with a name) and non-personalized + addresses in the same list by mixing the use of associative and + non-associative array syntax. + + .. code-block:: php + + $message->setTo(array( + 'recipient-with-name@example.org' => 'Recipient Name One', + 'no-name@example.org', // Note that this is not a key-value pair + 'named-recipient@example.org' => 'Recipient Name Two' + )); + +Setting ``To:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``To:`` recipients are required in a message and are set with the +``setTo()`` or ``addTo()`` methods of the message. + +To set ``To:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, +then call the ``setTo()`` method with a complete array of addresses, or use the +``addTo()`` method to iteratively add recipients. + +The ``setTo()`` method accepts input in various formats as described earlier in +this chapter. The ``addTo()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``To:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setTo()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add + recipients, use the ``addTo()`` method. + + .. code-block:: php + + // Using setTo() to set all recipients in one go + $message->setTo(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addTo() to add recipients iteratively + $message->addTo('person1@example.org'); + $message->addTo('person2@example.org', 'Person 2 Name'); + +Setting ``Cc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Cc:`` recipients are set with the ``setCc()`` or ``addCc()`` methods of the +message. + +To set ``Cc:`` recipients, create the message object using either +``new Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call +the ``setCc()`` method with a complete array of addresses, or use the +``addCc()`` method to iteratively add recipients. + +The ``setCc()`` method accepts input in various formats as described earlier in +this chapter. The ``addCc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +``Cc:`` recipients are visible in the message headers and will be +seen by the other recipients. + +.. note:: + + Multiple calls to ``setCc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Cc: + recipients, use the ``addCc()`` method. + + .. code-block:: php + + // Using setCc() to set all recipients in one go + $message->setCc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addCc() to add recipients iteratively + $message->addCc('person1@example.org'); + $message->addCc('person2@example.org', 'Person 2 Name'); + +Setting ``Bcc:`` Recipients +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Bcc:`` recipients receive a copy of the message without anybody else knowing +it, and are set with the ``setBcc()`` or ``addBcc()`` methods of the message. + +To set ``Bcc:`` recipients, create the message object using either ``new +Swift_Message( ... )`` or ``Swift_Message::newInstance( ... )``, then call the +``setBcc()`` method with a complete array of addresses, or use +the ``addBcc()`` method to iteratively add recipients. + +The ``setBcc()`` method accepts input in various formats as described earlier in +this chapter. The ``addBcc()`` method takes either one or two parameters. The +first being the email address and the second optional parameter being the name +of the recipient. + +Only the individual ``Bcc:`` recipient will see their address in the message +headers. Other recipients (including other ``Bcc:`` recipients) will not see the +address. + +.. note:: + + Multiple calls to ``setBcc()`` will not add new recipients -- each + call overrides the previous calls. If you want to iteratively add Bcc: + recipients, use the ``addBcc()`` method. + + .. code-block:: php + + // Using setBcc() to set all recipients in one go + $message->setBcc(array( + 'person1@example.org', + 'person2@otherdomain.org' => 'Person 2 Name', + 'person3@example.org', + 'person4@example.org', + 'person5@example.org' => 'Person 5 Name' + )); + + // Using addBcc() to add recipients iteratively + $message->addBcc('person1@example.org'); + $message->addBcc('person2@example.org', 'Person 2 Name'); + +Specifying Sender Details +------------------------- + +An email must include information about who sent it. Usually this is managed +by the ``From:`` address, however there are other options. + +The sender information is contained in three possible places: + +* ``From:`` -- the address(es) of who wrote the message (required) + +* ``Sender:`` -- the address of the single person who sent the message + (optional) + +* ``Return-Path:`` -- the address where bounces should go to (optional) + +You must always include a ``From:`` address by using ``setFrom()`` on the +message. Swift Mailer will use this as the default ``Return-Path:`` unless +otherwise specified. + +The ``Sender:`` address exists because the person who actually sent the email +may not be the person who wrote the email. It has a higher precedence than the +``From:`` address and will be used as the ``Return-Path:`` unless otherwise +specified. + +Setting the ``From:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``From:`` address is required and is set with the ``setFrom()`` method of the +message. ``From:`` addresses specify who actually wrote the email, and usually who sent it. + +What most people probably don't realise is that you can have more than one +``From:`` address if more than one person wrote the email -- for example if an +email was put together by a committee. + +To set the ``From:`` address(es): + +* Call the ``setFrom()`` method on the Message. + +The ``From:`` address(es) are visible in the message headers and +will be seen by the recipients. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + + .. code-block:: php + + // Set a single From: address + $message->setFrom('your@address.tld'); + + // Set a From: address including a name + $message->setFrom(array('your@address.tld' => 'Your Name')); + + // Set multiple From: addresses if multiple people wrote the email + $message->setFrom(array( + 'person1@example.org' => 'Sender One', + 'person2@example.org' => 'Sender Two' + )); + +Setting the ``Sender:`` Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Sender:`` address specifies who sent the message and is set with the +``setSender()`` method of the message. + +To set the ``Sender:`` address: + +* Call the ``setSender()`` method on the Message. + +The ``Sender:`` address is visible in the message headers and will be seen by +the recipients. + +This address will be used as the ``Return-Path:`` unless otherwise specified. + +.. note:: + + If you set multiple ``From:`` addresses then you absolutely must set a + ``Sender:`` address to indicate who physically sent the message. + +You must not set more than one sender address on a message because it's not +possible for more than one person to send a single message. + +.. code-block:: php + + $message->setSender('your@address.tld'); + +Setting the ``Return-Path:`` (Bounce) Address +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Return-Path:`` address specifies where bounce notifications should +be sent and is set with the ``setReturnPath()`` method of the message. + +You can only have one ``Return-Path:`` and it must not include +a personal name. + +To set the ``Return-Path:`` address: + +* Call the ``setReturnPath()`` method on the Message. + +Bounce notifications will be sent to this address. + +.. code-block:: php + + $message->setReturnPath('bounces@address.tld'); + + +Signed/Encrypted Message +------------------------ + +To increase the integrity/security of a message it is possible to sign and/or +encrypt an message using one or multiple signers. + +S/MIME +~~~~~~ + +S/MIME can sign and/or encrypt a message using the OpenSSL extension. + +When signing a message, the signer creates a signature of the entire content of the message (including attachments). + +The certificate and private key must be PEM encoded, and can be either created using for example OpenSSL or +obtained at an official Certificate Authority (CA). + +**The recipient must have the CA certificate in the list of trusted issuers in order to verify the signature.** + +**Make sure the certificate supports emailProtection.** + +When using OpenSSL this can done by the including the *-addtrust emailProtection* parameter when creating the certificate. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem'); + $message->attachSigner($smimeSigner); + +When the private key is secured using a passphrase use the following instead. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/certificate.pem', array('/path/to/private-key.pem', 'passphrase')); + $message->attachSigner($smimeSigner); + +By default the signature is added as attachment, +making the message still readable for mailing agents not supporting signed messages. + +Storing the message as binary is also possible but not recommended. + +.. code-block:: php + + $smimeSigner->setSignCertificate('/path/to/certificate.pem', '/path/to/private-key.pem', PKCS7_BINARY); + +When encrypting the message (also known as enveloping), the entire message (including attachments) +is encrypted using a certificate, and the recipient can then decrypt the message using corresponding private key. + +Encrypting ensures nobody can read the contents of the message without the private key. + +Normally the recipient provides a certificate for encrypting and keeping the decryption key private. + +Using both signing and encrypting is also possible. + +.. code-block:: php + + $message = Swift_Message::newInstance(); + + $smimeSigner = Swift_Signers_SMimeSigner::newInstance(); + $smimeSigner->setSignCertificate('/path/to/sign-certificate.pem', '/path/to/private-key.pem'); + $smimeSigner->setEncryptCertificate('/path/to/encrypt-certificate.pem'); + $message->attachSigner($smimeSigner); + +The used encryption cipher can be set as the second parameter of setEncryptCertificate() + +See http://php.net/manual/openssl.ciphers for a list of supported ciphers. + +By default the message is first signed and then encrypted, this can be changed by adding. + +.. code-block:: php + + $smimeSigner->setSignThenEncrypt(false); + +**Changing this is not recommended as most mail agents don't support this none-standard way.** + +Only when having trouble with sign then encrypt method, this should be changed. + +Requesting a Read Receipt +------------------------- + +It is possible to request a read-receipt to be sent to an address when the +email is opened. To request a read receipt set the address with +``setReadReceiptTo()``. + +To request a read receipt: + +* Set the address you want the receipt to be sent to with the + ``setReadReceiptTo()`` method on the Message. + +When the email is opened, if the mail client supports it a notification will be sent to this address. + +.. note:: + + Read receipts won't work for the majority of recipients since many mail + clients auto-disable them. Those clients that will send a read receipt + will make the user aware that one has been requested. + + .. code-block:: php + + $message->setReadReceiptTo('your@address.tld'); + +Setting the Character Set +------------------------- + +The character set of the message (and it's MIME parts) is set with the +``setCharset()`` method. You can also change the global default of UTF-8 by +working with the ``Swift_Preferences`` class. + +Swift Mailer will default to the UTF-8 character set unless otherwise +overridden. UTF-8 will work in most instances since it includes all of the +standard US keyboard characters in addition to most international characters. + +It is absolutely vital however that you know what character set your message +(or it's MIME parts) are written in otherwise your message may be received +completely garbled. + +There are two places in Swift Mailer where you can change the character set: + +* In the ``Swift_Preferences`` class + +* On each individual message and/or MIME part + +To set the character set of your Message: + +* Change the global UTF-8 setting by calling + ``Swift_Preferences::setCharset()``; or + +* Call the ``setCharset()`` method on the message or the MIME part. + + .. code-block:: php + + // Approach 1: Change the global setting (suggested) + Swift_Preferences::getInstance()->setCharset('iso-8859-2'); + + // Approach 2: Call the setCharset() method of the message + $message = Swift_Message::newInstance() + ->setCharset('iso-8859-2'); + + // Approach 3: Specify the charset when setting the body + $message->setBody('My body', 'text/html', 'iso-8859-2'); + + // Approach 4: Specify the charset for each part added + $message->addPart('My part', 'text/plain', 'iso-8859-2'); + +Setting the Line Length +----------------------- + +The length of lines in a message can be changed by using the ``setMaxLineLength()`` method on the message. It should be kept to less than +1000 characters. + +Swift Mailer defaults to using 78 characters per line in a message. This is +done for historical reasons and so that the message can be easily viewed in +plain-text terminals. + +To change the maximum length of lines in your Message: + +* Call the ``setMaxLineLength()`` method on the Message. + +Lines that are longer than the line length specified will be wrapped between +words. + +.. note:: + + You should never set a maximum length longer than 1000 characters + according to RFC 2822. Doing so could have unspecified side-effects such + as truncating parts of your message when it is transported between SMTP + servers. + + .. code-block:: php + + $message->setMaxLineLength(1000); + +Setting the Message Priority +---------------------------- + +You can change the priority of the message with ``setPriority()``. Setting the +priority will not change the way your email is sent -- it is purely an +indicative setting for the recipient. + +The priority of a message is an indication to the recipient what significance +it has. Swift Mailer allows you to set the priority by calling the ``setPriority`` method. This method takes an integer value between 1 and 5: + +* Highest +* High +* Normal +* Low +* Lowest + +To set the message priority: + +* Set the priority as an integer between 1 and 5 with the ``setPriority()`` + method on the Message. + +.. code-block:: php + + // Indicate "High" priority + $message->setPriority(2); diff --git a/vendor/swiftmailer/swiftmailer/doc/overview.rst b/vendor/swiftmailer/swiftmailer/doc/overview.rst new file mode 100644 index 0000000..c912617 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/overview.rst @@ -0,0 +1,161 @@ +Library Overview +================ + +Most features (and more) of your every day mail client software are provided +by Swift Mailer, using object-oriented PHP code as the interface. + +In this chapter we will take a short tour of the various components, which put +together form the Swift Mailer library as a whole. You will learn key +terminology used throughout the rest of this book and you will gain a little +understanding of the classes you will work with as you integrate Swift Mailer +into your application. + +This chapter is intended to prepare you for the information contained in the +subsequent chapters of this book. You may choose to skip this chapter if you +are fairly technically minded, though it is likely to save you some time in +the long run if you at least read between the lines here. + +System Requirements +------------------- + +The basic requirements to operate Swift Mailer are extremely minimal and +easily achieved. Historically, Swift Mailer has supported both PHP 4 and PHP 5 +by following a parallel development workflow. Now in it's fourth major +version, and Swift Mailer operates on servers running PHP 5.2 or higher. + +The library aims to work with as many PHP 5 projects as possible: + +* PHP 5.2 or higher, with the SPL extension (standard) + +* Limited network access to connect to remote SMTP servers + +* 8 MB or more memory limit (Swift Mailer uses around 2 MB) + +Component Breakdown +------------------- + +Swift Mailer is made up of many classes. Each of these classes can be grouped +into a general "component" group which describes the task it is designed to +perform. + +We'll take a brief look at the components which form Swift Mailer in this +section of the book. + +The Mailer +~~~~~~~~~~ + +The mailer class, ``Swift_Mailer`` is the central class in the library where +all of the other components meet one another. ``Swift_Mailer`` acts as a sort +of message dispatcher, communicating with the underlying Transport to deliver +your Message to all intended recipients. + +If you were to dig around in the source code for Swift Mailer you'd notice +that ``Swift_Mailer`` itself is pretty bare. It delegates to other objects for +most tasks and in theory, if you knew the internals of Swift Mailer well you +could by-pass this class entirely. We wouldn't advise doing such a thing +however -- there are reasons this class exists: + +* for consistency, regardless of the Transport used + +* to provide abstraction from the internals in the event internal API changes + are made + +* to provide convenience wrappers around aspects of the internal API + +An instance of ``Swift_Mailer`` is created by the developer before sending any +Messages. + +Transports +~~~~~~~~~~ + +Transports are the classes in Swift Mailer that are responsible for +communicating with a service in order to deliver a Message. There are several +types of Transport in Swift Mailer, all of which implement the Swift_Transport +interface and offer underlying start(), stop() and send() methods. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| Class | Features | Pros/cons | ++=================================+=============================================================================================+===============================================================================================================================================+ +| ``Swift_SmtpTransport`` | Sends messages over SMTP; Supports Authentication; Supports Encryption | Very portable; Pleasingly predictable results; Provides good feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_SendmailTransport`` | Communicates with a locally installed ``sendmail`` executable (Linux/UNIX) | Quick time-to-run; Provides less-accurate feedback than SMTP; Requires ``sendmail`` installation | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_MailTransport`` | Uses PHP's built-in ``mail()`` function | Very portable; Potentially unpredictable results; Provides extremely weak feedback | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_LoadBalancedTransport`` | Cycles through a collection of the other Transports to manage load-reduction | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down); Keeps the load on remote services down by spreading the work | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ +| ``Swift_FailoverTransport`` | Works in conjunction with a collection of the other Transports to provide high-availability | Provides graceful fallback if one Transport fails (e.g. an SMTP server is down) | ++---------------------------------+---------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+ + +MIME Entities +~~~~~~~~~~~~~ + +Everything that forms part of a Message is called a MIME Entity. All MIME +entities in Swift Mailer share a common set of features. There are various +types of MIME entity that serve different purposes such as Attachments and +MIME parts. + +An e-mail message is made up of several relatively simple entities that are +combined in different ways to achieve different results. All of these entities +have the same fundamental outline but serve a different purpose. The Message +itself can be defined as a MIME entity, an Attachment is a MIME entity, all +MIME parts are MIME entities -- and so on! + +The basic units of each MIME entity -- be it the Message itself, or an +Attachment -- are its Headers and its body: + +.. code-block:: text + + Other-Header: Another value + + The body content itself + +The Headers of a MIME entity, and its body must conform to some strict +standards defined by various RFC documents. Swift Mailer ensures that these +specifications are followed by using various types of object, including +Encoders and different Header types to generate the entity. + +Each MIME component implements the base ``Swift_Mime_MimeEntity`` interface, +which offers methods for retrieving Headers, adding new Headers, changing the +Encoder, updating the body and so on! + +All MIME entities have one Header in common -- the Content-Type Header, +updated with the entity's ``setContentType()`` method. + +Encoders +~~~~~~~~ + +Encoders are used to transform the content of Messages generated in Swift +Mailer into a format that is safe to send across the internet and that +conforms to RFC specifications. + +Generally speaking you will not need to interact with the Encoders in Swift +Mailer -- the correct settings will be handled by the library itself. +However they are probably worth a brief mention in the event that you do want +to play with them. + +Both the Headers and the body of all MIME entities (including the Message +itself) use Encoders to ensure the data they contain can be sent over the +internet without becoming corrupted or misinterpreted. + +There are two types of Encoder: Base64 and Quoted-Printable. + +Plugins +~~~~~~~ + +Plugins exist to extend, or modify the behaviour of Swift Mailer. They respond +to Events that are fired within the Transports during sending. + +There are a number of Plugins provided as part of the base Swift Mailer +package and they all follow a common interface to respond to Events fired +within the library. Interfaces are provided to "listen" to each type of Event +fired and to act as desired when a listened-to Event occurs. + +Although several plugins are provided with Swift Mailer out-of-the-box, the +Events system has been specifically designed to make it easy for experienced +object-oriented developers to write their own plugins in order to achieve +goals that may not be possible with the base library. diff --git a/vendor/swiftmailer/swiftmailer/doc/plugins.rst b/vendor/swiftmailer/swiftmailer/doc/plugins.rst new file mode 100644 index 0000000..16ae335 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/plugins.rst @@ -0,0 +1,385 @@ +Plugins +======= + +Plugins are provided with Swift Mailer and can be used to extend the behavior +of the library in situations where using simple class inheritance would be more complex. + +AntiFlood Plugin +---------------- + +Many SMTP servers have limits on the number of messages that may be sent +during any single SMTP connection. The AntiFlood plugin provides a way to stay +within this limit while still managing a large number of emails. + +A typical limit for a single connection is 100 emails. If the server you +connect to imposes such a limit, it expects you to disconnect after that +number of emails has been sent. You could manage this manually within a loop, +but the AntiFlood plugin provides the necessary wrapper code so that you don't +need to worry about this logic. + +Regardless of limits imposed by the server, it's usually a good idea to be +conservative with the resources of the SMTP server. Sending will become +sluggish if the server is being over-used so using the AntiFlood plugin will +not be a bad idea even if no limits exist. + +The AntiFlood plugin's logic is basically to disconnect and the immediately +re-connect with the SMTP server every X number of emails sent, where X is a +number you specify to the plugin. + +You can also specify a time period in seconds that Swift Mailer should pause +for between the disconnect/re-connect process. It's a good idea to pause for a +short time (say 30 seconds every 100 emails) simply to give the SMTP server a +chance to process its queue and recover some resources. + +Using the AntiFlood Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AntiFlood Plugin -- like all plugins -- is added with the Mailer class's +``registerPlugin()`` method. It takes two constructor parameters: the number of +emails to pause after, and optionally the number of seconds to pause for. + +To use the AntiFlood plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_AntiFloodPlugin`` class, passing + in one or two constructor parameters. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will count the number of messages that +have been sent since the last re-connect. Once the number hits your specified +threshold it will disconnect and re-connect, optionally pausing for a +specified amount of time. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Use AntiFlood to re-connect after 100 emails + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100)); + + // And specify a time in seconds to pause for (30 secs) + $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Throttler Plugin +---------------- + +If your SMTP server has restrictions in place to limit the rate at which you +send emails, then your code will need to be aware of this rate-limiting. The +Throttler plugin makes Swift Mailer run at a rate-limited speed. + +Many shared hosts don't open their SMTP servers as a free-for-all. Usually +they have policies in place (probably to discourage spammers) that only allow +you to send a fixed number of emails per-hour/day. + +The Throttler plugin supports two modes of rate-limiting and with each, you +will need to do that math to figure out the values you want. The plugin can +limit based on the number of emails per minute, or the number of +bytes-transferred per-minute. + +Using the Throttler Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Throttler Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It has two required constructor parameters that +tell it how to do its rate-limiting. + +To use the Throttler plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the ``Swift_Plugins_ThrottlerPlugin`` class, passing + the number of emails, or bytes you wish to limit by, along with the mode + you're using. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +When Swift Mailer sends messages it will keep track of the rate at which sending +messages is occurring. If it realises that sending is happening too fast, it +will cause your program to ``sleep()`` for enough time to average out the rate. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // Rate limit to 100 emails per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE + )); + + // Rate limit to 10MB per-minute + $mailer->registerPlugin(new Swift_Plugins_ThrottlerPlugin( + 1024 * 1024 * 10, Swift_Plugins_ThrottlerPlugin::BYTES_PER_MINUTE + )); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + +Logger Plugin +------------- + +The Logger plugins helps with debugging during the process of sending. It can +help to identify why an SMTP server is rejecting addresses, or any other +hard-to-find problems that may arise. + +The Logger plugin comes in two parts. There's the plugin itself, along with +one of a number of possible Loggers that you may choose to use. For example, +the logger may output messages directly in realtime, or it may capture +messages in an array. + +One other notable feature is the way in which the Logger plugin changes +Exception messages. If Exceptions are being thrown but the error message does +not provide conclusive information as to the source of the problem (such as an +ambiguous SMTP error) the Logger plugin includes the entire SMTP transcript in +the error message so that debugging becomes a simpler task. + +There are a few available Loggers included with Swift Mailer, but writing your +own implementation is incredibly simple and is achieved by creating a short +class that implements the ``Swift_Plugins_Logger`` interface. + +* ``Swift_Plugins_Loggers_ArrayLogger``: Keeps a collection of log messages + inside an array. The array content can be cleared or dumped out to the + screen. + +* ``Swift_Plugins_Loggers_EchoLogger``: Prints output to the screen in + realtime. Handy for very rudimentary debug output. + +Using the Logger Plugin +~~~~~~~~~~~~~~~~~~~~~~~ + +The Logger Plugin -- like all plugins -- is added with the Mailer class' +``registerPlugin()`` method. It accepts an instance of ``Swift_Plugins_Logger`` +in its constructor. + +To use the Logger plugin: + +* Create an instance of the Mailer using any Transport you choose. + +* Create an instance of the a Logger implementation of + ``Swift_Plugins_Logger``. + +* Create an instance of the ``Swift_Plugins_LoggerPlugin`` class, passing the + created Logger instance to its constructor. + +* Register the plugin using the Mailer's ``registerPlugin()`` method. + +* Continue using Swift Mailer to send messages as normal. + +* Dump the contents of the log with the logger's ``dump()`` method. + +When Swift Mailer sends messages it will keep a log of all the interactions +with the underlying Transport being used. Depending upon the Logger that has +been used the behaviour will differ, but all implementations offer a way to +get the contents of the log. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Mailer using any Transport + $mailer = Swift_Mailer::newInstance( + Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ); + + // To use the ArrayLogger + $logger = new Swift_Plugins_Loggers_ArrayLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Or to use the Echo Logger + $logger = new Swift_Plugins_Loggers_EchoLogger(); + $mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($logger)); + + // Continue sending as normal + for ($lotsOfRecipients as $recipient) { + ... + + $mailer->send( ... ); + } + + // Dump the log contents + // NOTE: The EchoLogger dumps in realtime so dump() does nothing for it + echo $logger->dump(); + +Decorator Plugin +---------------- + +Often there's a need to send the same message to multiple recipients, but with +tiny variations such as the recipient's name being used inside the message +body. The Decorator plugin aims to provide a solution for allowing these small +differences. + +The decorator plugin works by intercepting the sending process of Swift +Mailer, reading the email address in the To: field and then looking up a set +of replacements for a template. + +While the use of this plugin is simple, it is probably the most commonly +misunderstood plugin due to the way in which it works. The typical mistake +users make is to try registering the plugin multiple times (once for each +recipient) -- inside a loop for example. This is incorrect. + +The Decorator plugin should be registered just once, but containing the list +of all recipients prior to sending. It will use this list of recipients to +find the required replacements during sending. + +Using the Decorator Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Decorator plugin, simply create an associative array of replacements +based on email addresses and then use the mailer's ``registerPlugin()`` method +to add the plugin. + +First create an associative array of replacements based on the email addresses +you'll be sending the message to. + +.. note:: + + The replacements array becomes a 2-dimensional array whose keys are the + email addresses and whose values are an associative array of replacements + for that email address. The curly braces used in this example can be any + type of syntax you choose, provided they match the placeholders in your + email template. + + .. code-block:: php + + $replacements = array(); + foreach ($users as $user) { + $replacements[$user['email']] = array( + '{username}'=>$user['username'], + '{password}'=>$user['password'] + ); + } + +Now create an instance of the Decorator plugin using this array of replacements +and then register it with the Mailer. Do this only once! + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin($replacements); + + $mailer->registerPlugin($decorator); + +When you create your message, replace elements in the body (and/or the subject +line) with your placeholders. + +.. code-block:: php + + $message = Swift_Message::newInstance() + ->setSubject('Important notice for {username}') + ->setBody( + "Hello {username}, we have reset your password to {password}\n" . + "Please log in and change it at your earliest convenience." + ) + ; + + foreach ($users as $user) { + $message->addTo($user['email']); + } + +When you send this message to each of your recipients listed in your +``$replacements`` array they will receive a message customized for just +themselves. For example, the message used above when received may appear like +this to one user: + +.. code-block:: text + + Subject: Important notice for smilingsunshine2009 + + Hello smilingsunshine2009, we have reset your password to rainyDays + Please log in and change it at your earliest convenience. + +While another use may receive the message as: + +.. code-block:: text + + Subject: Important notice for billy-bo-bob + + Hello billy-bo-bob, we have reset your password to dancingOctopus + Please log in and change it at your earliest convenience. + +While the decorator plugin provides a means to solve this problem, there are +various ways you could tackle this problem without the need for a plugin. +We're trying to come up with a better way ourselves and while we have several +(obvious) ideas we don't quite have the perfect solution to go ahead and +implement it. Watch this space. + +Providing Your Own Replacements Lookup for the Decorator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Filling an array with replacements may not be the best solution for providing +replacement information to the decorator. If you have a more elegant algorithm +that performs replacement lookups on-the-fly you may provide your own +implementation. + +Providing your own replacements lookup implementation for the Decorator is +simply a matter of passing an instance of ``Swift_Plugins_Decorator_Replacements`` to the decorator plugin's constructor, +rather than passing in an array. + +The Replacements interface is very simple to implement since it has just one +method: ``getReplacementsFor($address)``. + +Imagine you want to look up replacements from a database on-the-fly, you might +provide an implementation that does this. You need to create a small class. + +.. code-block:: php + + class DbReplacements implements Swift_Plugins_Decorator_Replacements { + public function getReplacementsFor($address) { + $sql = sprintf( + "SELECT * FROM user WHERE email = '%s'", + mysql_real_escape_string($address) + ); + + $result = mysql_query($sql); + + if ($row = mysql_fetch_assoc($result)) { + return array( + '{username}'=>$row['username'], + '{password}'=>$row['password'] + ); + } + } + } + +Now all you need to do is pass an instance of your class into the Decorator +plugin's constructor instead of passing an array. + +.. code-block:: php + + $decorator = new Swift_Plugins_DecoratorPlugin(new DbReplacements()); + + $mailer->registerPlugin($decorator); + +For each message sent, the plugin will call your class' ``getReplacementsFor()`` +method to find the array of replacements it needs. + +.. note:: + + If your lookup algorithm is case sensitive, you should transform the + ``$address`` argument as appropriate -- for example by passing it + through ``strtolower()``. diff --git a/vendor/swiftmailer/swiftmailer/doc/sending.rst b/vendor/swiftmailer/swiftmailer/doc/sending.rst new file mode 100644 index 0000000..4460845 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/doc/sending.rst @@ -0,0 +1,607 @@ +Sending Messages +================ + +Quick Reference for Sending a Message +------------------------------------- + +Sending a message is very straightforward. You create a Transport, use it to +create the Mailer, then you use the Mailer to send the message. + +To send a Message: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, ``Swift_MailTransport`` + or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +.. caution:: + + The ``Swift_SmtpTransport`` and ``Swift_SendmailTransport`` transports use + ``proc_*`` PHP functions, which might not be available on your PHP + installation. You can easily check if that's the case by running the + following PHP script: ``setUsername('your username') + ->setPassword('your password') + ; + + /* + You could alternatively use a different transport such as Sendmail or Mail: + + // Sendmail + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'); + + // Mail + $transport = Swift_MailTransport::newInstance(); + */ + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $result = $mailer->send($message); + +Transport Types +~~~~~~~~~~~~~~~ + +A Transport is the component which actually does the sending. You need to +provide a Transport object to the Mailer class and there are several possible +options. + +Typically you will not need to know how a Transport works under-the-surface, +you will only need to know how to create an instance of one, and which one to +use for your environment. + +The SMTP Transport +.................. + +The SMTP Transport sends messages over the (standardized) Simple Message +Transfer Protocol. It can deal with encryption and authentication. + +The SMTP Transport, ``Swift_SmtpTransport`` is without doubt the most commonly +used Transport because it will work on 99% of web servers (I just made that +number up, but you get the idea). All the server needs is the ability to +connect to a remote (or even local) SMTP server on the correct port number +(usually 25). + +SMTP servers often require users to authenticate with a username and password +before any mail can be sent to other domains. This is easily achieved using +Swift Mailer with the SMTP Transport. + +SMTP is a protocol -- in other words it's a "way" of communicating a job +to be done (i.e. sending a message). The SMTP protocol is the fundamental +basis on which messages are delivered all over the internet 7 days a week, 365 +days a year. For this reason it's the most "direct" method of sending messages +you can use and it's the one that will give you the most power and feedback +(such as delivery failures) when using Swift Mailer. + +Because SMTP is generally run as a remote service (i.e. you connect to it over +the network/internet) it's extremely portable from server-to-server. You can +easily store the SMTP server address and port number in a configuration file +within your application and adjust the settings accordingly if the code is +moved or if the SMTP server is changed. + +Some SMTP servers -- Google for example -- use encryption for security reasons. +Swift Mailer supports using both SSL and TLS encryption settings. + +Using the SMTP Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +The SMTP Transport is easy to use. Most configuration options can be set with +the constructor. + +To use the SMTP Transport you need to know which SMTP server your code needs +to connect to. Ask your web host if you're not sure. Lots of people ask me who +to connect to -- I really can't answer that since it's a setting that's +extremely specific to your hosting environment. + +To use the SMTP Transport: + +* Call ``Swift_SmtpTransport::newInstance()`` with the SMTP server name and + optionally with a port number (defaults to 25). + +* Use the returned object to create the Mailer. + +A connection to the SMTP server will be established upon the first call to +``send()``. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(25) + ; + */ + +Encrypted SMTP +^^^^^^^^^^^^^^ + +You can use SSL or TLS encryption with the SMTP Transport by specifying it as +a parameter or with a method call. + +To use encryption with the SMTP Transport: + +* Pass the encryption setting as a third parameter to + ``Swift_SmtpTransport::newInstance()``; or + +* Call the ``setEncryption()`` method on the Transport. + +A connection to the SMTP server will be established upon the first call to +``send()``. The connection will be initiated with the correct encryption +settings. + +.. note:: + + For SSL or TLS encryption to work your PHP installation must have + appropriate OpenSSL transports wrappers. You can check if "tls" and/or + "ssl" are present in your PHP installation by using the PHP function + ``stream_get_transports()`` + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 587, 'ssl'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + /* + It's also possible to use multiple method calls + + $transport = Swift_SmtpTransport::newInstance() + ->setHost('smtp.example.org') + ->setPort(587) + ->setEncryption('ssl') + ; + */ + +SMTP with a Username and Password +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some servers require authentication. You can provide a username and password +with ``setUsername()`` and ``setPassword()`` methods. + +To use a username and password with the SMTP Transport: + +* Create the Transport with ``Swift_SmtpTransport::newInstance()``. + +* Call the ``setUsername()`` and ``setPassword()`` methods on the Transport. + +Your username and password will be used to authenticate upon first connect +when ``send()`` are first used on the Mailer. + +If authentication fails, an Exception of type ``Swift_TransportException`` will +be thrown. + +.. note:: + + If you need to know early whether or not authentication has failed and an + Exception is going to be thrown, call the ``start()`` method on the + created Transport. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport the call setUsername() and setPassword() + $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25) + ->setUsername('username') + ->setPassword('password') + ; + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Sendmail Transport +...................... + +The Sendmail Transport sends messages by communicating with a locally +installed MTA -- such as ``sendmail``. + +The Sendmail Transport, ``Swift_SendmailTransport`` does not directly connect to +any remote services. It is designed for Linux servers that have ``sendmail`` +installed. The Transport starts a local ``sendmail`` process and sends messages +to it. Usually the ``sendmail`` process will respond quickly as it spools your +messages to disk before sending them. + +The Transport is named the Sendmail Transport for historical reasons +(``sendmail`` was the "standard" UNIX tool for sending e-mail for years). It +will send messages using other transfer agents such as Exim or Postfix despite +its name, provided they have the relevant sendmail wrappers so that they can be +started with the correct command-line flags. + +It's a common misconception that because the Sendmail Transport returns a +result very quickly it must therefore deliver messages to recipients quickly +-- this is not true. It's not slow by any means, but it's certainly not +faster than SMTP when it comes to getting messages to the intended recipients. +This is because sendmail itself sends the messages over SMTP once they have +been quickly spooled to disk. + +The Sendmail Transport has the potential to be just as smart of the SMTP +Transport when it comes to notifying Swift Mailer about which recipients were +rejected, but in reality the majority of locally installed ``sendmail`` +instances are not configured well enough to provide any useful feedback. As such +Swift Mailer may report successful deliveries where they did in fact fail before +they even left your server. + +You can run the Sendmail Transport in two different modes specified by command +line flags: + +* "``-bs``" runs in SMTP mode so theoretically it will act like the SMTP + Transport + +* "``-t``" runs in piped mode with no feedback, but theoretically faster, + though not advised + +You can think of the Sendmail Transport as a sort of asynchronous SMTP Transport +-- though if you have problems with delivery failures you should try using the +SMTP Transport instead. Swift Mailer isn't doing the work here, it's simply +passing the work to somebody else (i.e. ``sendmail``). + +Using the Sendmail Transport +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Sendmail Transport you simply need to call +``Swift_SendmailTransport::newInstance()`` with the command as a parameter. + +To use the Sendmail Transport you need to know where ``sendmail`` or another MTA +exists on the server. Swift Mailer uses a default value of +``/usr/sbin/sendmail``, which should work on most systems. + +You specify the entire command as a parameter (i.e. including the command line +flags). Swift Mailer supports operational modes of "``-bs``" (default) and +"``-t``". + +.. note:: + + If you run sendmail in "``-t``" mode you will get no feedback as to whether + or not sending has succeeded. Use "``-bs``" unless you have a reason not to. + +To use the Sendmail Transport: + +* Call ``Swift_SendmailTransport::newInstance()`` with the command, including + the correct command line flags. The default is to use ``/usr/sbin/sendmail + -bs`` if this is not specified. + +* Use the returned object to create the Mailer. + +A sendmail process will be started upon the first call to ``send()``. If the +process cannot be started successfully an Exception of type +``Swift_TransportException`` will be thrown. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SendmailTransport::newInstance('/usr/sbin/exim -bs'); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +The Mail Transport +.................. + +The Mail Transport sends messages by delegating to PHP's internal +``mail()`` function. + +In my experience -- and others' -- the ``mail()`` function is not particularly +predictable, or helpful. + +Quite notably, the ``mail()`` function behaves entirely differently between +Linux and Windows servers. On linux it uses ``sendmail``, but on Windows it uses +SMTP. + +In order for the ``mail()`` function to even work at all ``php.ini`` needs to be +configured correctly, specifying the location of sendmail or of an SMTP server. + +The problem with ``mail()`` is that it "tries" to simplify things to the point +that it actually makes things more complex due to poor interface design. The +developers of Swift Mailer have gone to a lot of effort to make the Mail +Transport work with a reasonable degree of consistency. + +Serious drawbacks when using this Transport are: + +* Unpredictable message headers + +* Lack of feedback regarding delivery failures + +* Lack of support for several plugins that require real-time delivery feedback + +It's a last resort, and we say that with a passion! + +Using the Mail Transport +^^^^^^^^^^^^^^^^^^^^^^^^ + +To use the Mail Transport you simply need to call +``Swift_MailTransport::newInstance()``. It's unlikely you'll need to configure +the Transport. + +To use the Mail Transport: + +* Call ``Swift_MailTransport::newInstance()``. + +* Use the returned object to create the Mailer. + +Messages will be sent using the ``mail()`` function. + +.. note:: + + The ``mail()`` function can take a ``$additional_parameters`` parameter. + Swift Mailer sets this to "``-f%s``" by default, where the "``%s``" is + substituted with the address of the sender (via a ``sprintf()``) at send + time. You may override this default by passing an argument to + ``newInstance()``. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_MailTransport::newInstance(); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + +Available Methods for Sending Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Mailer class offers two methods for sending Messages -- ``send()``. +Each behaves in a slightly different way. + +When a message is sent in Swift Mailer, the Mailer class communicates with +whichever Transport class you have chosen to use. + +Each recipient in the message should either be accepted or rejected by the +Transport. For example, if the domain name on the email address is not +reachable the SMTP Transport may reject the address because it cannot process +it. Whichever method you use -- ``send()`` -- Swift Mailer will return +an integer indicating the number of accepted recipients. + +.. note:: + + It's possible to find out which recipients were rejected -- we'll cover that + later in this chapter. + +Using the ``send()`` Method +........................... + +The ``send()`` method of the ``Swift_Mailer`` class sends a message using +exactly the same logic as your Desktop mail client would use. Just pass it a +Message and get a result. + +To send a Message with ``send()``: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + ``Swift_MailTransport`` or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Send the message via the ``send()`` method on the Mailer object. + +The message will be sent just like it would be sent if you used your mail +client. An integer is returned which includes the number of successful +recipients. If none of the recipients could be sent to then zero will be +returned, which equates to a boolean ``false``. If you set two +``To:`` recipients and three ``Bcc:`` recipients in the message and all of the +recipients are delivered to successfully then the value 5 will be returned. + +.. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name')) + ->setBody('Here is the message itself') + ; + + // Send the message + $numSent = $mailer->send($message); + + printf("Sent %d messages\n", $numSent); + + /* Note that often that only the boolean equivalent of the + return value is of concern (zero indicates FALSE) + + if ($mailer->send($message)) + { + echo "Sent\n"; + } + else + { + echo "Failed\n"; + } + + */ + +Sending Emails in Batch +....................... + +If you want to send a separate message to each recipient so that only their +own address shows up in the ``To:`` field, follow the following recipe: + +* Create a Transport from one of the provided Transports -- + ``Swift_SmtpTransport``, ``Swift_SendmailTransport``, + ``Swift_MailTransport`` or one of the aggregate Transports. + +* Create an instance of the ``Swift_Mailer`` class, using the Transport as + it's constructor parameter. + +* Create a Message. + +* Iterate over the recipients and send message via the ``send()`` method on + the Mailer object. + +Each recipient of the messages receives a different copy with only their own +email address on the ``To:`` field. + +Make sure to add only valid email addresses as recipients. If you try to add an +invalid email address with ``setTo()``, ``setCc()`` or ``setBcc()``, Swift +Mailer will throw a ``Swift_RfcComplianceException``. + +If you add recipients automatically based on a data source that may contain +invalid email addresses, you can prevent possible exceptions by validating the +addresses using ``Swift_Validate::email($email)`` and only adding addresses +that validate. Another way would be to wrap your ``setTo()``, ``setCc()`` and +``setBcc()`` calls in a try-catch block and handle the +``Swift_RfcComplianceException`` in the catch block. + +Handling invalid addresses properly is especially important when sending emails +in large batches since a single invalid address might cause an unhandled +exception and stop the execution or your script early. + +.. note:: + + In the following example, two emails are sent. One to each of + ``receiver@domain.org`` and ``other@domain.org``. These recipients will + not be aware of each other. + + .. code-block:: php + + require_once 'lib/swift_required.php'; + + // Create the Transport + $transport = Swift_SmtpTransport::newInstance('localhost', 25); + + // Create the Mailer using your created Transport + $mailer = Swift_Mailer::newInstance($transport); + + // Create a message + $message = Swift_Message::newInstance('Wonderful Subject') + ->setFrom(array('john@doe.com' => 'John Doe')) + ->setBody('Here is the message itself') + ; + + // Send the message + $failedRecipients = array(); + $numSent = 0; + $to = array('receiver@domain.org', 'other@domain.org' => 'A name'); + + foreach ($to as $address => $name) + { + if (is_int($address)) { + $message->setTo($name); + } else { + $message->setTo(array($address => $name)); + } + + $numSent += $mailer->send($message, $failedRecipients); + } + + printf("Sent %d messages\n", $numSent); + +Finding out Rejected Addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's possible to get a list of addresses that were rejected by the Transport +by using a by-reference parameter to ``send()``. + +As Swift Mailer attempts to send the message to each address given to it, if a +recipient is rejected it will be added to the array. You can pass an existing +array, otherwise one will be created by-reference. + +Collecting the list of recipients that were rejected can be useful in +circumstances where you need to "prune" a mailing list for example when some +addresses cannot be delivered to. + +Getting Failures By-reference +............................. + +Collecting delivery failures by-reference with the ``send()`` method is as +simple as passing a variable name to the method call. + +To get failed recipients by-reference: + +* Pass a by-reference variable name to the ``send()`` method of the Mailer + class. + +If the Transport rejects any of the recipients, the culprit addresses will be +added to the array provided by-reference. + +.. note:: + + If the variable name does not yet exist, it will be initialized as an + empty array and then failures will be added to that array. If the variable + already exists it will be type-cast to an array and failures will be added + to it. + + .. code-block:: php + + $mailer = Swift_Mailer::newInstance( ... ); + + $message = Swift_Message::newInstance( ... ) + ->setFrom( ... ) + ->setTo(array( + 'receiver@bad-domain.org' => 'Receiver Name', + 'other@domain.org' => 'A name', + 'other-receiver@bad-domain.org' => 'Other Name' + )) + ->setBody( ... ) + ; + + // Pass a variable name to the send() method + if (!$mailer->send($message, $failures)) + { + echo "Failures:"; + print_r($failures); + } + + /* + Failures: + Array ( + 0 => receiver@bad-domain.org, + 1 => other-receiver@bad-domain.org + ) + */ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle new file mode 100644 index 0000000..f895752 Binary files /dev/null and b/vendor/swiftmailer/swiftmailer/doc/uml/Encoders.graffle differ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle new file mode 100644 index 0000000..e1e33cb Binary files /dev/null and b/vendor/swiftmailer/swiftmailer/doc/uml/Mime.graffle differ diff --git a/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle new file mode 100644 index 0000000..5670e2b Binary files /dev/null and b/vendor/swiftmailer/swiftmailer/doc/uml/Transports.graffle differ diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php new file mode 100644 index 0000000..72419b3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php @@ -0,0 +1,80 @@ +createDependenciesFor('mime.attachment') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Attachment. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_Attachment + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new Attachment from a filesystem path. + * + * @param string $path + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public static function fromPath($path, $contentType = null) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path), + $contentType + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php new file mode 100644 index 0000000..c397c8b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php @@ -0,0 +1,179 @@ +_filters[$key] = $filter; + } + + /** + * Remove an already present StreamFilter based on its $key. + * + * @param string $key + */ + public function removeFilter($key) + { + unset($this->_filters[$key]); + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_writeBuffer .= $bytes; + foreach ($this->_filters as $filter) { + if ($filter->shouldBuffer($this->_writeBuffer)) { + return; + } + } + $this->_doWrite($this->_writeBuffer); + + return ++$this->_sequence; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + $this->_doWrite($this->_writeBuffer); + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + if ($this->_writeBuffer !== '') { + $stream->write($this->_writeBuffer); + } + unset($this->_mirrors[$k]); + } + } + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + if ($this->_writeBuffer !== '') { + $this->_doWrite($this->_writeBuffer); + } + $this->_flush(); + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** Run $bytes through all filters */ + private function _filter($bytes) + { + foreach ($this->_filters as $filter) { + $bytes = $filter->filter($bytes); + } + + return $bytes; + } + + /** Just write the bytes to the stream */ + private function _doWrite($bytes) + { + $this->_commit($this->_filter($bytes)); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + + $this->_writeBuffer = ''; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php new file mode 100644 index 0000000..561cda8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php @@ -0,0 +1,184 @@ +_array = $stack; + $this->_arraySize = count($stack); + } elseif (is_string($stack)) { + $this->write($stack); + } else { + $this->_array = array(); + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_arraySize) { + return false; + } + + // Don't use array slice + $end = $length + $this->_offset; + $end = $this->_arraySize < $end + ? $this->_arraySize + : $end; + $ret = ''; + for (; $this->_offset < $end; ++$this->_offset) { + $ret .= $this->_array[$this->_offset]; + } + + return $ret; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + */ + public function write($bytes) + { + $to_add = str_split($bytes); + foreach ($to_add as $value) { + $this->_array[] = $value; + } + $this->_arraySize = count($this->_array); + + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if ($byteOffset > $this->_arraySize) { + $byteOffset = $this->_arraySize; + } elseif ($byteOffset < 0) { + $byteOffset = 0; + } + + $this->_offset = $byteOffset; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_offset = 0; + $this->_array = array(); + $this->_arraySize = 0; + + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php new file mode 100644 index 0000000..4061043 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php @@ -0,0 +1,229 @@ +_path = $path; + $this->_mode = $writable ? 'w+b' : 'rb'; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Get the complete path to the file. + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the + * remaining bytes are given instead. If no bytes are remaining at all, boolean + * false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + $fp = $this->_getReadHandle(); + if (!feof($fp)) { + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $bytes = fread($fp, $length); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_offset = ftell($fp); + + // If we read one byte after reaching the end of the file + // feof() will return false and an empty string is returned + if ($bytes === '' && feof($fp)) { + $this->_resetReadHandle(); + + return false; + } + + return $bytes; + } + + $this->_resetReadHandle(); + + return false; + } + + /** + * Move the internal read pointer to $byteOffset in the stream. + * + * @param int $byteOffset + * + * @return bool + */ + public function setReadPointer($byteOffset) + { + if (isset($this->_reader)) { + $this->_seekReadStreamToPosition($byteOffset); + } + $this->_offset = $byteOffset; + } + + /** Just write the bytes to the file */ + protected function _commit($bytes) + { + fwrite($this->_getWriteHandle(), $bytes); + $this->_resetReadHandle(); + } + + /** Not used */ + protected function _flush() + { + } + + /** Get the resource for reading */ + private function _getReadHandle() + { + if (!isset($this->_reader)) { + if (!$this->_reader = fopen($this->_path, 'rb')) { + throw new Swift_IoException( + 'Unable to open file for reading ['.$this->_path.']' + ); + } + if ($this->_offset != 0) { + $this->_getReadStreamSeekableStatus(); + $this->_seekReadStreamToPosition($this->_offset); + } + } + + return $this->_reader; + } + + /** Get the resource for writing */ + private function _getWriteHandle() + { + if (!isset($this->_writer)) { + if (!$this->_writer = fopen($this->_path, $this->_mode)) { + throw new Swift_IoException( + 'Unable to open file for writing ['.$this->_path.']' + ); + } + } + + return $this->_writer; + } + + /** Force a reload of the resource for reading */ + private function _resetReadHandle() + { + if (isset($this->_reader)) { + fclose($this->_reader); + $this->_reader = null; + } + } + + /** Check if ReadOnly Stream is seekable */ + private function _getReadStreamSeekableStatus() + { + $metas = stream_get_meta_data($this->_reader); + $this->_seekable = $metas['seekable']; + } + + /** Streams in a readOnly stream ensuring copy if needed */ + private function _seekReadStreamToPosition($offset) + { + if ($this->_seekable === null) { + $this->_getReadStreamSeekableStatus(); + } + if ($this->_seekable === false) { + $currentPos = ftell($this->_reader); + if ($currentPos < $offset) { + $toDiscard = $offset - $currentPos; + fread($this->_reader, $toDiscard); + + return; + } + $this->_copyReadStream(); + } + fseek($this->_reader, $offset, SEEK_SET); + } + + /** Copy a readOnly Stream to ensure seekability */ + private function _copyReadStream() + { + if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) { + /* We have opened a php:// Stream Should work without problem */ + } elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) { + /* We have opened a tmpfile */ + } else { + throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); + } + $currentPos = ftell($this->_reader); + fclose($this->_reader); + $source = fopen($this->_path, 'rb'); + if (!$source) { + throw new Swift_IoException('Unable to open file for copying ['.$this->_path.']'); + } + fseek($tmpFile, 0, SEEK_SET); + while (!feof($source)) { + fwrite($tmpFile, fread($source, 4096)); + } + fseek($tmpFile, $currentPos, SEEK_SET); + fclose($source); + $this->_reader = $tmpFile; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php new file mode 100644 index 0000000..1c9a80c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php @@ -0,0 +1,42 @@ +getPath())) === false) { + throw new Swift_IoException('Failed to get temporary file content.'); + } + + return $content; + } + + public function __destruct() + { + if (file_exists($this->getPath())) { + @unlink($this->getPath()); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php new file mode 100644 index 0000000..3d5e854 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader.php @@ -0,0 +1,67 @@ + + */ +interface Swift_CharacterReader +{ + const MAP_TYPE_INVALID = 0x01; + const MAP_TYPE_FIXED_LEN = 0x02; + const MAP_TYPE_POSITIONS = 0x03; + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars); + + /** + * Returns the mapType, see constants. + * + * @return int + */ + public function getMapType(); + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param integer[] $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size); + + /** + * Returns the number of bytes which should be read to start each character. + * + * For fixed width character sets this should be the number of octets-per-character. + * For multibyte character sets this will probably be 1. + * + * @return int + */ + public function getInitialByteSize(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php new file mode 100644 index 0000000..c1029b9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php @@ -0,0 +1,97 @@ + + */ +class Swift_CharacterReader_GenericFixedWidthReader implements Swift_CharacterReader +{ + /** + * The number of bytes in a single character. + * + * @var int + */ + private $_width; + + /** + * Creates a new GenericFixedWidthReader using $width bytes per character. + * + * @param int $width + */ + public function __construct($width) + { + $this->_width = $width; + } + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + $strlen = strlen($string); + // % and / are CPU intensive, so, maybe find a better way + $ignored = $strlen % $this->_width; + $ignoredChars = substr($string, -$ignored); + $currentMap = $this->_width; + + return ($strlen - $ignored) / $this->_width; + } + + /** + * Returns the mapType. + * + * @return int + */ + public function getMapType() + { + return self::MAP_TYPE_FIXED_LEN; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $needed = $this->_width - $size; + + return ($needed > -1) ? $needed : -1; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return $this->_width; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php new file mode 100644 index 0000000..ddc34ca --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php @@ -0,0 +1,84 @@ + "\x07F") { + // Invalid char + $currentMap[$i + $startOffset] = $string[$i]; + } + } + + return $strlen; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_INVALID; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + $byte = reset($bytes); + if (1 == count($bytes) && $byte >= 0x00 && $byte <= 0x7F) { + return 0; + } else { + return -1; + } + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php new file mode 100644 index 0000000..d5fa9c9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php @@ -0,0 +1,179 @@ + + */ +class Swift_CharacterReader_Utf8Reader implements Swift_CharacterReader +{ + /** Pre-computed for optimization */ + private static $length_map = array( + // N=0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x0N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x2N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x3N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x4N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x5N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x6N + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x7N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x8N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x9N + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xAN + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xBN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xCN + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xDN + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 0xEN + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, // 0xFN + ); + + private static $s_length_map = array( + "\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1, + "\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1, + "\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1, + "\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1, + "\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, + "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, + "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, + "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, + "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, + "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, + "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, + "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, + "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, + "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, + "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, + "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1, + "\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0, + "\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0, + "\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0, + "\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0, + "\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0, + "\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0, + "\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0, + "\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0, + "\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2, + "\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2, + "\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2, + "\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2, + "\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3, + "\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3, + "\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4, + "\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0, + ); + + /** + * Returns the complete character map. + * + * @param string $string + * @param int $startOffset + * @param array $currentMap + * @param mixed $ignoredChars + * + * @return int + */ + public function getCharPositions($string, $startOffset, &$currentMap, &$ignoredChars) + { + if (!isset($currentMap['i']) || !isset($currentMap['p'])) { + $currentMap['p'] = $currentMap['i'] = array(); + } + + $strlen = strlen($string); + $charPos = count($currentMap['p']); + $foundChars = 0; + $invalid = false; + for ($i = 0; $i < $strlen; ++$i) { + $char = $string[$i]; + $size = self::$s_length_map[$char]; + if ($size == 0) { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue; + } else { + if ($invalid == true) { + /* We mark the chars as invalid and start a new char */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i; + $currentMap['i'][$charPos + $foundChars] = true; + ++$foundChars; + $invalid = false; + } + if (($i + $size) > $strlen) { + $ignoredChars = substr($string, $i); + break; + } + for ($j = 1; $j < $size; ++$j) { + $char = $string[$i + $j]; + if ($char > "\x7F" && $char < "\xC0") { + // Valid - continue parsing + } else { + /* char is invalid, we must wait for a resync */ + $invalid = true; + continue 2; + } + } + /* Ok we got a complete char here */ + $currentMap['p'][$charPos + $foundChars] = $startOffset + $i + $size; + $i += $j - 1; + ++$foundChars; + } + } + + return $foundChars; + } + + /** + * Returns mapType. + * + * @return int mapType + */ + public function getMapType() + { + return self::MAP_TYPE_POSITIONS; + } + + /** + * Returns an integer which specifies how many more bytes to read. + * + * A positive integer indicates the number of more bytes to fetch before invoking + * this method again. + * A value of zero means this is already a valid character. + * A value of -1 means this cannot possibly be a valid character. + * + * @param string $bytes + * @param int $size + * + * @return int + */ + public function validateByteSequence($bytes, $size) + { + if ($size < 1) { + return -1; + } + $needed = self::$length_map[$bytes[0]] - $size; + + return ($needed > -1) + ? $needed + : -1 + ; + } + + /** + * Returns the number of bytes which should be read to start each character. + * + * @return int + */ + public function getInitialByteSize() + { + return 1; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php new file mode 100644 index 0000000..15b6c69 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php @@ -0,0 +1,26 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + public function init() + { + if (count(self::$_map) > 0) { + return; + } + + $prefix = 'Swift_CharacterReader_'; + + $singleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(1), + ); + + $doubleByte = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(2), + ); + + $fourBytes = array( + 'class' => $prefix.'GenericFixedWidthReader', + 'constructor' => array(4), + ); + + // Utf-8 + self::$_map['utf-?8'] = array( + 'class' => $prefix.'Utf8Reader', + 'constructor' => array(), + ); + + //7-8 bit charsets + self::$_map['(us-)?ascii'] = $singleByte; + self::$_map['(iso|iec)-?8859-?[0-9]+'] = $singleByte; + self::$_map['windows-?125[0-9]'] = $singleByte; + self::$_map['cp-?[0-9]+'] = $singleByte; + self::$_map['ansi'] = $singleByte; + self::$_map['macintosh'] = $singleByte; + self::$_map['koi-?7'] = $singleByte; + self::$_map['koi-?8-?.+'] = $singleByte; + self::$_map['mik'] = $singleByte; + self::$_map['(cork|t1)'] = $singleByte; + self::$_map['v?iscii'] = $singleByte; + + //16 bits + self::$_map['(ucs-?2|utf-?16)'] = $doubleByte; + + //32 bits + self::$_map['(ucs-?4|utf-?32)'] = $fourBytes; + + // Fallback + self::$_map['.*'] = $singleByte; + } + + /** + * Returns a CharacterReader suitable for the charset applied. + * + * @param string $charset + * + * @return Swift_CharacterReader + */ + public function getReaderFor($charset) + { + $charset = trim(strtolower($charset)); + foreach (self::$_map as $pattern => $spec) { + $re = '/^'.$pattern.'$/D'; + if (preg_match($re, $charset)) { + if (!array_key_exists($pattern, self::$_loaded)) { + $reflector = new ReflectionClass($spec['class']); + if ($reflector->getConstructor()) { + $reader = $reflector->newInstanceArgs($spec['constructor']); + } else { + $reader = $reflector->newInstance(); + } + self::$_loaded[$pattern] = $reader; + } + + return self::$_loaded[$pattern]; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php new file mode 100644 index 0000000..717924f --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php @@ -0,0 +1,89 @@ +setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * Overwrite this character stream using the byte sequence in the byte stream. + * + * @param Swift_OutputByteStream $os output stream to read from + */ + public function importByteStream(Swift_OutputByteStream $os) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory + ->getReaderFor($this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + while (false !== $bytes = $os->read($startLength)) { + $c = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + $size = count($c); + $need = $this->_charReader + ->validateByteSequence($c, $size); + if ($need > 0 && + false !== $bytes = $os->read($need)) { + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $c[] = self::$_byteMap[$bytes[$i]]; + } + } + $this->_array[] = $c; + ++$this->_array_size; + } + } + + /** + * Import a string a bytes into this CharacterStream, overwriting any existing + * data in the stream. + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * Read $length characters from the stream and move the internal pointer + * $length further into the stream. + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + + // Don't use array slice + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += $i - $this->_offset; // Limit function calls + $chars = false; + foreach ($arrays as $array) { + $chars .= implode('', array_map('chr', $array)); + } + + return $chars; + } + + /** + * Read $length characters from the stream and return a 1-dimensional array + * containing there octet values. + * + * @param int $length + * + * @return integer[] + */ + public function readBytes($length) + { + if ($this->_offset == $this->_array_size) { + return false; + } + $arrays = array(); + $end = $length + $this->_offset; + for ($i = $this->_offset; $i < $end; ++$i) { + if (!isset($this->_array[$i])) { + break; + } + $arrays[] = $this->_array[$i]; + } + $this->_offset += ($i - $this->_offset); // Limit function calls + + return call_user_func_array('array_merge', $arrays); + } + + /** + * Write $chars to the end of the stream. + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + } + + $startLength = $this->_charReader->getInitialByteSize(); + + $fp = fopen('php://memory', 'w+b'); + fwrite($fp, $chars); + unset($chars); + fseek($fp, 0, SEEK_SET); + + $buffer = array(0); + $buf_pos = 1; + $buf_len = 1; + $has_datas = true; + do { + $bytes = array(); + // Buffer Filing + if ($buf_len - $buf_pos < $startLength) { + $buf = array_splice($buffer, $buf_pos); + $new = $this->_reloadBuffer($fp, 100); + if ($new) { + $buffer = array_merge($buf, $new); + $buf_len = count($buffer); + $buf_pos = 0; + } else { + $has_datas = false; + } + } + if ($buf_len - $buf_pos > 0) { + $size = 0; + for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) { + ++$size; + $bytes[] = $buffer[$buf_pos++]; + } + $need = $this->_charReader->validateByteSequence( + $bytes, $size); + if ($need > 0) { + if ($buf_len - $buf_pos < $need) { + $new = $this->_reloadBuffer($fp, $need); + + if ($new) { + $buffer = array_merge($buffer, $new); + $buf_len = count($buffer); + } + } + for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) { + $bytes[] = $buffer[$buf_pos++]; + } + } + $this->_array[] = $bytes; + ++$this->_array_size; + } + } while ($has_datas); + + fclose($fp); + } + + /** + * Move the internal pointer to $charOffset in the stream. + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($charOffset > $this->_array_size) { + $charOffset = $this->_array_size; + } elseif ($charOffset < 0) { + $charOffset = 0; + } + $this->_offset = $charOffset; + } + + /** + * Empty the stream and reset the internal pointer. + */ + public function flushContents() + { + $this->_offset = 0; + $this->_array = array(); + $this->_array_size = 0; + } + + private function _reloadBuffer($fp, $len) + { + if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) { + $buf = array(); + for ($i = 0, $len = strlen($bytes); $i < $len; ++$i) { + $buf[] = self::$_byteMap[$bytes[$i]]; + } + + return $buf; + } + + return false; + } + + private static function _initializeMaps() + { + if (!isset(self::$_charMap)) { + self::$_charMap = array(); + for ($byte = 0; $byte < 256; ++$byte) { + self::$_charMap[$byte] = chr($byte); + } + self::$_byteMap = array_flip(self::$_charMap); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php new file mode 100644 index 0000000..7620d0e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php @@ -0,0 +1,275 @@ + + */ +class Swift_CharacterStream_NgCharacterStream implements Swift_CharacterStream +{ + /** + * The char reader (lazy-loaded) for the current charset. + * + * @var Swift_CharacterReader + */ + private $_charReader; + + /** + * A factory for creating CharacterReader instances. + * + * @var Swift_CharacterReaderFactory + */ + private $_charReaderFactory; + + /** + * The character set this stream is using. + * + * @var string + */ + private $_charset; + + /** + * The data's stored as-is. + * + * @var string + */ + private $_datas = ''; + + /** + * Number of bytes in the stream. + * + * @var int + */ + private $_datasSize = 0; + + /** + * Map. + * + * @var mixed + */ + private $_map; + + /** + * Map Type. + * + * @var int + */ + private $_mapType = 0; + + /** + * Number of characters in the stream. + * + * @var int + */ + private $_charCount = 0; + + /** + * Position in the stream. + * + * @var int + */ + private $_currentPos = 0; + + /** + * Constructor. + * + * @param Swift_CharacterReaderFactory $factory + * @param string $charset + */ + public function __construct(Swift_CharacterReaderFactory $factory, $charset) + { + $this->setCharacterReaderFactory($factory); + $this->setCharacterSet($charset); + } + + /* -- Changing parameters of the stream -- */ + + /** + * Set the character set used in this CharacterStream. + * + * @param string $charset + */ + public function setCharacterSet($charset) + { + $this->_charset = $charset; + $this->_charReader = null; + $this->_mapType = 0; + } + + /** + * Set the CharacterReaderFactory for multi charset support. + * + * @param Swift_CharacterReaderFactory $factory + */ + public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory) + { + $this->_charReaderFactory = $factory; + } + + /** + * @see Swift_CharacterStream::flushContents() + */ + public function flushContents() + { + $this->_datas = null; + $this->_map = null; + $this->_charCount = 0; + $this->_currentPos = 0; + $this->_datasSize = 0; + } + + /** + * @see Swift_CharacterStream::importByteStream() + * + * @param Swift_OutputByteStream $os + */ + public function importByteStream(Swift_OutputByteStream $os) + { + $this->flushContents(); + $blocks = 512; + $os->setReadPointer(0); + while (false !== ($read = $os->read($blocks))) { + $this->write($read); + } + } + + /** + * @see Swift_CharacterStream::importString() + * + * @param string $string + */ + public function importString($string) + { + $this->flushContents(); + $this->write($string); + } + + /** + * @see Swift_CharacterStream::read() + * + * @param int $length + * + * @return string + */ + public function read($length) + { + if ($this->_currentPos >= $this->_charCount) { + return false; + } + $ret = false; + $length = ($this->_currentPos + $length > $this->_charCount) + ? $this->_charCount - $this->_currentPos + : $length; + switch ($this->_mapType) { + case Swift_CharacterReader::MAP_TYPE_FIXED_LEN: + $len = $length * $this->_map; + $ret = substr($this->_datas, + $this->_currentPos * $this->_map, + $len); + $this->_currentPos += $length; + break; + + case Swift_CharacterReader::MAP_TYPE_INVALID: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount + ? $this->_charCount + : $end; + $ret = ''; + for (; $this->_currentPos < $length; ++$this->_currentPos) { + if (isset($this->_map[$this->_currentPos])) { + $ret .= '?'; + } else { + $ret .= $this->_datas[$this->_currentPos]; + } + } + break; + + case Swift_CharacterReader::MAP_TYPE_POSITIONS: + $end = $this->_currentPos + $length; + $end = $end > $this->_charCount + ? $this->_charCount + : $end; + $ret = ''; + $start = 0; + if ($this->_currentPos > 0) { + $start = $this->_map['p'][$this->_currentPos - 1]; + } + $to = $start; + for (; $this->_currentPos < $end; ++$this->_currentPos) { + if (isset($this->_map['i'][$this->_currentPos])) { + $ret .= substr($this->_datas, $start, $to - $start).'?'; + $start = $this->_map['p'][$this->_currentPos]; + } else { + $to = $this->_map['p'][$this->_currentPos]; + } + } + $ret .= substr($this->_datas, $start, $to - $start); + break; + } + + return $ret; + } + + /** + * @see Swift_CharacterStream::readBytes() + * + * @param int $length + * + * @return integer[] + */ + public function readBytes($length) + { + $read = $this->read($length); + if ($read !== false) { + $ret = array_map('ord', str_split($read, 1)); + + return $ret; + } + + return false; + } + + /** + * @see Swift_CharacterStream::setPointer() + * + * @param int $charOffset + */ + public function setPointer($charOffset) + { + if ($this->_charCount < $charOffset) { + $charOffset = $this->_charCount; + } + $this->_currentPos = $charOffset; + } + + /** + * @see Swift_CharacterStream::write() + * + * @param string $chars + */ + public function write($chars) + { + if (!isset($this->_charReader)) { + $this->_charReader = $this->_charReaderFactory->getReaderFor( + $this->_charset); + $this->_map = array(); + $this->_mapType = $this->_charReader->getMapType(); + } + $ignored = ''; + $this->_datas .= $chars; + $this->_charCount += $this->_charReader->getCharPositions(substr($this->_datas, $this->_datasSize), $this->_datasSize, $this->_map, $ignored); + if ($ignored !== false) { + $this->_datasSize = strlen($this->_datas) - strlen($ignored); + } else { + $this->_datasSize = strlen($this->_datas); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php new file mode 100644 index 0000000..4ae5bac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Base class for Spools (implements time and message limits). + * + * @author Fabien Potencier + */ +abstract class Swift_ConfigurableSpool implements Swift_Spool +{ + /** The maximum number of messages to send per flush */ + private $_message_limit; + + /** The time limit per flush */ + private $_time_limit; + + /** + * Sets the maximum number of messages to send per flush. + * + * @param int $limit + */ + public function setMessageLimit($limit) + { + $this->_message_limit = (int) $limit; + } + + /** + * Gets the maximum number of messages to send per flush. + * + * @return int The limit + */ + public function getMessageLimit() + { + return $this->_message_limit; + } + + /** + * Sets the time limit (in seconds) per flush. + * + * @param int $limit The limit + */ + public function setTimeLimit($limit) + { + $this->_time_limit = (int) $limit; + } + + /** + * Gets the time limit (in seconds) per flush. + * + * @return int The limit + */ + public function getTimeLimit() + { + return $this->_time_limit; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php new file mode 100644 index 0000000..660cc84 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php @@ -0,0 +1,373 @@ +_store); + } + + /** + * Test if an item is registered in this container with the given name. + * + * @see register() + * + * @param string $itemName + * + * @return bool + */ + public function has($itemName) + { + return array_key_exists($itemName, $this->_store) + && isset($this->_store[$itemName]['lookupType']); + } + + /** + * Lookup the item with the given $itemName. + * + * @see register() + * + * @param string $itemName + * + * @throws Swift_DependencyException If the dependency is not found + * + * @return mixed + */ + public function lookup($itemName) + { + if (!$this->has($itemName)) { + throw new Swift_DependencyException( + 'Cannot lookup dependency "'.$itemName.'" since it is not registered.' + ); + } + + switch ($this->_store[$itemName]['lookupType']) { + case self::TYPE_ALIAS: + return $this->_createAlias($itemName); + case self::TYPE_VALUE: + return $this->_getValue($itemName); + case self::TYPE_INSTANCE: + return $this->_createNewInstance($itemName); + case self::TYPE_SHARED: + return $this->_createSharedInstance($itemName); + } + } + + /** + * Create an array of arguments passed to the constructor of $itemName. + * + * @param string $itemName + * + * @return array + */ + public function createDependenciesFor($itemName) + { + $args = array(); + if (isset($this->_store[$itemName]['args'])) { + $args = $this->_resolveArgs($this->_store[$itemName]['args']); + } + + return $args; + } + + /** + * Register a new dependency with $itemName. + * + * This method returns the current DependencyContainer instance because it + * requires the use of the fluid interface to set the specific details for the + * dependency. + * + * @see asNewInstanceOf(), asSharedInstanceOf(), asValue() + * + * @param string $itemName + * + * @return Swift_DependencyContainer + */ + public function register($itemName) + { + $this->_store[$itemName] = array(); + $this->_endPoint = &$this->_store[$itemName]; + + return $this; + } + + /** + * Specify the previously registered item as a literal value. + * + * {@link register()} must be called before this will work. + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function asValue($value) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_VALUE; + $endPoint['value'] = $value; + + return $this; + } + + /** + * Specify the previously registered item as an alias of another item. + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function asAliasOf($lookup) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_ALIAS; + $endPoint['ref'] = $lookup; + + return $this; + } + + /** + * Specify the previously registered item as a new instance of $className. + * + * {@link register()} must be called before this will work. + * Any arguments can be set with {@link withDependencies()}, + * {@link addConstructorValue()} or {@link addConstructorLookup()}. + * + * @see withDependencies(), addConstructorValue(), addConstructorLookup() + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asNewInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_INSTANCE; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify the previously registered item as a shared instance of $className. + * + * {@link register()} must be called before this will work. + * + * @param string $className + * + * @return Swift_DependencyContainer + */ + public function asSharedInstanceOf($className) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['lookupType'] = self::TYPE_SHARED; + $endPoint['className'] = $className; + + return $this; + } + + /** + * Specify a list of injected dependencies for the previously registered item. + * + * This method takes an array of lookup names. + * + * @see addConstructorValue(), addConstructorLookup() + * + * @param array $lookups + * + * @return Swift_DependencyContainer + */ + public function withDependencies(array $lookups) + { + $endPoint = &$this->_getEndPoint(); + $endPoint['args'] = array(); + foreach ($lookups as $lookup) { + $this->addConstructorLookup($lookup); + } + + return $this; + } + + /** + * Specify a literal (non looked up) value for the constructor of the + * previously registered item. + * + * @see withDependencies(), addConstructorLookup() + * + * @param mixed $value + * + * @return Swift_DependencyContainer + */ + public function addConstructorValue($value) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'value', 'item' => $value); + + return $this; + } + + /** + * Specify a dependency lookup for the constructor of the previously + * registered item. + * + * @see withDependencies(), addConstructorValue() + * + * @param string $lookup + * + * @return Swift_DependencyContainer + */ + public function addConstructorLookup($lookup) + { + $endPoint = &$this->_getEndPoint(); + if (!isset($this->_endPoint['args'])) { + $endPoint['args'] = array(); + } + $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup); + + return $this; + } + + /** Get the literal value with $itemName */ + private function _getValue($itemName) + { + return $this->_store[$itemName]['value']; + } + + /** Resolve an alias to another item */ + private function _createAlias($itemName) + { + return $this->lookup($this->_store[$itemName]['ref']); + } + + /** Create a fresh instance of $itemName */ + private function _createNewInstance($itemName) + { + $reflector = new ReflectionClass($this->_store[$itemName]['className']); + if ($reflector->getConstructor()) { + return $reflector->newInstanceArgs( + $this->createDependenciesFor($itemName) + ); + } else { + return $reflector->newInstance(); + } + } + + /** Create and register a shared instance of $itemName */ + private function _createSharedInstance($itemName) + { + if (!isset($this->_store[$itemName]['instance'])) { + $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName); + } + + return $this->_store[$itemName]['instance']; + } + + /** Get the current endpoint in the store */ + private function &_getEndPoint() + { + if (!isset($this->_endPoint)) { + throw new BadMethodCallException( + 'Component must first be registered by calling register()' + ); + } + + return $this->_endPoint; + } + + /** Get an argument list with dependencies resolved */ + private function _resolveArgs(array $args) + { + $resolved = array(); + foreach ($args as $argDefinition) { + switch ($argDefinition['type']) { + case 'lookup': + $resolved[] = $this->_lookupRecursive($argDefinition['item']); + break; + case 'value': + $resolved[] = $argDefinition['item']; + break; + } + } + + return $resolved; + } + + /** Resolve a single dependency with an collections */ + private function _lookupRecursive($item) + { + if (is_array($item)) { + $collection = array(); + foreach ($item as $k => $v) { + $collection[$k] = $this->_lookupRecursive($v); + } + + return $collection; + } else { + return $this->lookup($item); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php new file mode 100644 index 0000000..799d38d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyException.php @@ -0,0 +1,27 @@ +createDependenciesFor('mime.embeddedfile') + ); + + $this->setBody($data); + $this->setFilename($filename); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new EmbeddedFile. + * + * @param string|Swift_OutputByteStream $data + * @param string $filename + * @param string $contentType + * + * @return Swift_Mime_EmbeddedFile + */ + public static function newInstance($data = null, $filename = null, $contentType = null) + { + return new self($data, $filename, $contentType); + } + + /** + * Create a new EmbeddedFile from a filesystem path. + * + * @param string $path + * + * @return Swift_Mime_EmbeddedFile + */ + public static function fromPath($path) + { + return self::newInstance()->setFile( + new Swift_ByteStream_FileByteStream($path) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php new file mode 100644 index 0000000..2073abc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php @@ -0,0 +1,28 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $encodedString = base64_encode($string); + $firstLine = ''; + + if (0 != $firstLineOffset) { + $firstLine = substr( + $encodedString, 0, $maxLineLength - $firstLineOffset + )."\r\n"; + $encodedString = substr( + $encodedString, $maxLineLength - $firstLineOffset + ); + } + + return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n")); + } + + /** + * Does nothing. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php new file mode 100644 index 0000000..f989c8d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php @@ -0,0 +1,289 @@ + '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04', + 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09', + 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E', + 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13', + 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18', + 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D', + 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22', + 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27', + 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C', + 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31', + 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36', + 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B', + 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40', + 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45', + 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A', + 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F', + 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54', + 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59', + 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E', + 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63', + 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68', + 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D', + 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72', + 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77', + 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C', + 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81', + 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86', + 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B', + 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90', + 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95', + 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A', + 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F', + 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4', + 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9', + 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE', + 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3', + 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8', + 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD', + 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2', + 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7', + 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC', + 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1', + 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6', + 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB', + 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0', + 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5', + 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA', + 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF', + 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4', + 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9', + 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE', + 255 => '=FF', + ); + + protected static $_safeMapShare = array(); + + /** + * A map of non-encoded ascii characters. + * + * @var string[] + */ + protected $_safeMap = array(); + + /** + * Creates a new QpEncoder for the given CharacterStream. + * + * @param Swift_CharacterStream $charStream to use for reading characters + * @param Swift_StreamFilter $filter if input should be canonicalized + */ + public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null) + { + $this->_charStream = $charStream; + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + $this->_filter = $filter; + } + + public function __sleep() + { + return array('_charStream', '_filter'); + } + + public function __wakeup() + { + if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) { + $this->initSafeMap(); + self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap; + } else { + $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()]; + } + } + + protected function getSafeMapShareId() + { + return get_class($this); + } + + protected function initSafeMap() + { + foreach (array_merge( + array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) { + $this->_safeMap[$byte] = chr($byte); + } + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param string $string to encode + * @param int $firstLineOffset, optional + * @param int $maxLineLength, optional 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $lines = array(); + $lNo = 0; + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $size = $lineLen = 0; + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + // Fetching more than 4 chars at one is slower, as is fetching fewer bytes + // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6 + // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + if ($currentLine && $lineLen + $size >= $thisLineLength) { + $lines[$lNo] = ''; + $currentLine = &$lines[$lNo++]; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + $lineLen += $size; + $currentLine .= $enc; + } + + return $this->_standardize(implode("=\r\n", $lines)); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Encode the given byte array into a verbatim QP form. + * + * @param integer[] $bytes + * @param int $size + * + * @return string + */ + protected function _encodeByteSequence(array $bytes, &$size) + { + $ret = ''; + $size = 0; + foreach ($bytes as $b) { + if (isset($this->_safeMap[$b])) { + $ret .= $this->_safeMap[$b]; + ++$size; + } else { + $ret .= self::$_qpMap[$b]; + $size += 3; + } + } + + return $ret; + } + + /** + * Get the next sequence of bytes to read from the char stream. + * + * @param int $size number of bytes to read + * + * @return integer[] + */ + protected function _nextSequence($size = 4) + { + return $this->_charStream->readBytes($size); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + $string = str_replace(array("\t=0D=0A", ' =0D=0A', '=0D=0A'), + array("=09\r\n", "=20\r\n", "\r\n"), $string + ); + switch ($end = ord(substr($string, -1))) { + case 0x09: + case 0x20: + $string = substr_replace($string, self::$_qpMap[$end], -1); + } + + return $string; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php new file mode 100644 index 0000000..b0215e8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php @@ -0,0 +1,92 @@ +_charStream = $charStream; + } + + /** + * Takes an unencoded string and produces a string encoded according to + * RFC 2231 from it. + * + * @param string $string + * @param int $firstLineOffset + * @param int $maxLineLength optional, 0 indicates the default of 75 bytes + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + $lines = array(); + $lineCount = 0; + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + if (0 >= $maxLineLength) { + $maxLineLength = 75; + } + + $this->_charStream->flushContents(); + $this->_charStream->importString($string); + + $thisLineLength = $maxLineLength - $firstLineOffset; + + while (false !== $char = $this->_charStream->read(4)) { + $encodedChar = rawurlencode($char); + if (0 != strlen($currentLine) + && strlen($currentLine.$encodedChar) > $thisLineLength) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + $thisLineLength = $maxLineLength; + } + $currentLine .= $encodedChar; + } + + return implode("\r\n", $lines); + } + + /** + * Updates the charset used. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charStream->setCharacterSet($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_charStream = clone $this->_charStream; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php new file mode 100644 index 0000000..253977b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoding.php @@ -0,0 +1,64 @@ +lookup($key); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php new file mode 100644 index 0000000..7dc381d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php @@ -0,0 +1,65 @@ +_command = $command; + $this->_successCodes = $successCodes; + } + + /** + * Get the command which was sent to the server. + * + * @return string + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Get the numeric response codes which indicate success for this command. + * + * @return integer[] + */ + public function getSuccessCodes() + { + return $this->_successCodes; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php new file mode 100644 index 0000000..7545404 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php @@ -0,0 +1,24 @@ +_source = $source; + } + + /** + * Get the source object of this event. + * + * @return object + */ + public function getSource() + { + return $this->_source; + } + + /** + * Prevent this Event from bubbling any further up the stack. + * + * @param bool $cancel, optional + */ + public function cancelBubble($cancel = true) + { + $this->_bubbleCancelled = $cancel; + } + + /** + * Returns true if this Event will not bubble any further up the stack. + * + * @return bool + */ + public function bubbleCancelled() + { + return $this->_bubbleCancelled; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php new file mode 100644 index 0000000..2e92ba9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php @@ -0,0 +1,65 @@ +_response = $response; + $this->_valid = $valid; + } + + /** + * Get the response which was received from the server. + * + * @return string + */ + public function getResponse() + { + return $this->_response; + } + + /** + * Get the success status of this Event. + * + * @return bool + */ + public function isValid() + { + return $this->_valid; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php new file mode 100644 index 0000000..c40919d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php @@ -0,0 +1,24 @@ +_message = $message; + $this->_result = self::RESULT_PENDING; + } + + /** + * Get the Transport used to send the Message. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->getSource(); + } + + /** + * Get the Message being sent. + * + * @return Swift_Mime_Message + */ + public function getMessage() + { + return $this->_message; + } + + /** + * Set the array of addresses that failed in sending. + * + * @param array $recipients + */ + public function setFailedRecipients($recipients) + { + $this->_failedRecipients = $recipients; + } + + /** + * Get an recipient addresses which were not accepted for delivery. + * + * @return string[] + */ + public function getFailedRecipients() + { + return $this->_failedRecipients; + } + + /** + * Set the result of sending. + * + * @param int $result + */ + public function setResult($result) + { + $this->_result = $result; + } + + /** + * Get the result of this Event. + * + * The return value is a bitmask from + * {@see RESULT_PENDING, RESULT_SUCCESS, RESULT_TENTATIVE, RESULT_FAILED} + * + * @return int + */ + public function getResult() + { + return $this->_result; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php new file mode 100644 index 0000000..d922e1b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php @@ -0,0 +1,31 @@ +_eventMap = array( + 'Swift_Events_CommandEvent' => 'Swift_Events_CommandListener', + 'Swift_Events_ResponseEvent' => 'Swift_Events_ResponseListener', + 'Swift_Events_SendEvent' => 'Swift_Events_SendListener', + 'Swift_Events_TransportChangeEvent' => 'Swift_Events_TransportChangeListener', + 'Swift_Events_TransportExceptionEvent' => 'Swift_Events_TransportExceptionListener', + ); + } + + /** + * Create a new SendEvent for $source and $message. + * + * @param Swift_Transport $source + * @param Swift_Mime_Message + * + * @return Swift_Events_SendEvent + */ + public function createSendEvent(Swift_Transport $source, Swift_Mime_Message $message) + { + return new Swift_Events_SendEvent($source, $message); + } + + /** + * Create a new CommandEvent for $source and $command. + * + * @param Swift_Transport $source + * @param string $command That will be executed + * @param array $successCodes That are needed + * + * @return Swift_Events_CommandEvent + */ + public function createCommandEvent(Swift_Transport $source, $command, $successCodes = array()) + { + return new Swift_Events_CommandEvent($source, $command, $successCodes); + } + + /** + * Create a new ResponseEvent for $source and $response. + * + * @param Swift_Transport $source + * @param string $response + * @param bool $valid If the response is valid + * + * @return Swift_Events_ResponseEvent + */ + public function createResponseEvent(Swift_Transport $source, $response, $valid) + { + return new Swift_Events_ResponseEvent($source, $response, $valid); + } + + /** + * Create a new TransportChangeEvent for $source. + * + * @param Swift_Transport $source + * + * @return Swift_Events_TransportChangeEvent + */ + public function createTransportChangeEvent(Swift_Transport $source) + { + return new Swift_Events_TransportChangeEvent($source); + } + + /** + * Create a new TransportExceptionEvent for $source. + * + * @param Swift_Transport $source + * @param Swift_TransportException $ex + * + * @return Swift_Events_TransportExceptionEvent + */ + public function createTransportExceptionEvent(Swift_Transport $source, Swift_TransportException $ex) + { + return new Swift_Events_TransportExceptionEvent($source, $ex); + } + + /** + * Bind an event listener to this dispatcher. + * + * @param Swift_Events_EventListener $listener + */ + public function bindEventListener(Swift_Events_EventListener $listener) + { + foreach ($this->_listeners as $l) { + // Already loaded + if ($l === $listener) { + return; + } + } + $this->_listeners[] = $listener; + } + + /** + * Dispatch the given Event to all suitable listeners. + * + * @param Swift_Events_EventObject $evt + * @param string $target method + */ + public function dispatchEvent(Swift_Events_EventObject $evt, $target) + { + $this->_prepareBubbleQueue($evt); + $this->_bubble($evt, $target); + } + + /** Queue listeners on a stack ready for $evt to be bubbled up it */ + private function _prepareBubbleQueue(Swift_Events_EventObject $evt) + { + $this->_bubbleQueue = array(); + $evtClass = get_class($evt); + foreach ($this->_listeners as $listener) { + if (array_key_exists($evtClass, $this->_eventMap) + && ($listener instanceof $this->_eventMap[$evtClass])) { + $this->_bubbleQueue[] = $listener; + } + } + } + + /** Bubble $evt up the stack calling $target() on each listener */ + private function _bubble(Swift_Events_EventObject $evt, $target) + { + if (!$evt->bubbleCancelled() && $listener = array_shift($this->_bubbleQueue)) { + $listener->$target($evt); + $this->_bubble($evt, $target); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php new file mode 100644 index 0000000..a8972fd --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php @@ -0,0 +1,27 @@ +getSource(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php new file mode 100644 index 0000000..253165d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php @@ -0,0 +1,45 @@ +_exception = $ex; + } + + /** + * Get the TransportException thrown. + * + * @return Swift_TransportException + */ + public function getException() + { + return $this->_exception; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php new file mode 100644 index 0000000..cc3c099 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php @@ -0,0 +1,24 @@ +createDependenciesFor('transport.failover') + ); + + $this->setTransports($transports); + } + + /** + * Create a new FailoverTransport instance. + * + * @param Swift_Transport[] $transports + * + * @return Swift_FailoverTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php new file mode 100644 index 0000000..2208539 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages on the filesystem. + * + * @author Fabien Potencier + * @author Xavier De Cock + */ +class Swift_FileSpool extends Swift_ConfigurableSpool +{ + /** The spool directory */ + private $_path; + + /** + * File WriteRetry Limit. + * + * @var int + */ + private $_retryLimit = 10; + + /** + * Create a new FileSpool. + * + * @param string $path + * + * @throws Swift_IoException + */ + public function __construct($path) + { + $this->_path = $path; + + if (!file_exists($this->_path)) { + if (!mkdir($this->_path, 0777, true)) { + throw new Swift_IoException('Unable to create Path ['.$this->_path.']'); + } + } + } + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Spool mechanism. + */ + public function start() + { + } + + /** + * Stops this Spool mechanism. + */ + public function stop() + { + } + + /** + * Allow to manage the enqueuing retry limit. + * + * Default, is ten and allows over 64^20 different fileNames + * + * @param int $limit + */ + public function setRetryLimit($limit) + { + $this->_retryLimit = $limit; + } + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @throws Swift_IoException + * + * @return bool + */ + public function queueMessage(Swift_Mime_Message $message) + { + $ser = serialize($message); + $fileName = $this->_path.'/'.$this->getRandomString(10); + for ($i = 0; $i < $this->_retryLimit; ++$i) { + /* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */ + $fp = @fopen($fileName.'.message', 'x'); + if (false !== $fp) { + if (false === fwrite($fp, $ser)) { + return false; + } + + return fclose($fp); + } else { + /* The file already exists, we try a longer fileName */ + $fileName .= $this->getRandomString(1); + } + } + + throw new Swift_IoException('Unable to create a file for enqueuing Message'); + } + + /** + * Execute a recovery if for any reason a process is sending for too long. + * + * @param int $timeout in second Defaults is for very slow smtp responses + */ + public function recover($timeout = 900) + { + foreach (new DirectoryIterator($this->_path) as $file) { + $file = $file->getRealPath(); + + if (substr($file, -16) == '.message.sending') { + $lockedtime = filectime($file); + if ((time() - $lockedtime) > $timeout) { + rename($file, substr($file, 0, -8)); + } + } + } + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + $directoryIterator = new DirectoryIterator($this->_path); + + /* Start the transport only if there are queued files to send */ + if (!$transport->isStarted()) { + foreach ($directoryIterator as $file) { + if (substr($file->getRealPath(), -8) == '.message') { + $transport->start(); + break; + } + } + } + + $failedRecipients = (array) $failedRecipients; + $count = 0; + $time = time(); + foreach ($directoryIterator as $file) { + $file = $file->getRealPath(); + + if (substr($file, -8) != '.message') { + continue; + } + + /* We try a rename, it's an atomic operation, and avoid locking the file */ + if (rename($file, $file.'.sending')) { + $message = unserialize(file_get_contents($file.'.sending')); + + $count += $transport->send($message, $failedRecipients); + + unlink($file.'.sending'); + } else { + /* This message has just been catched by another process */ + continue; + } + + if ($this->getMessageLimit() && $count >= $this->getMessageLimit()) { + break; + } + + if ($this->getTimeLimit() && (time() - $time) >= $this->getTimeLimit()) { + break; + } + } + + return $count; + } + + /** + * Returns a random string needed to generate a fileName for the queue. + * + * @param int $count + * + * @return string + */ + protected function getRandomString($count) + { + // This string MUST stay FS safe, avoid special chars + $base = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'; + $ret = ''; + $strlen = strlen($base); + for ($i = 0; $i < $count; ++$i) { + $ret .= $base[((int) rand(0, $strlen - 1))]; + } + + return $ret; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php new file mode 100644 index 0000000..0b24db1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/FileStream.php @@ -0,0 +1,24 @@ +setFile( + new Swift_ByteStream_FileByteStream($path) + ); + + return $image; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php new file mode 100644 index 0000000..56efc75 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php @@ -0,0 +1,75 @@ +_stream = $stream; + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->_contents[$nsKey][$itemKey] = $string; + break; + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + $this->_contents[$nsKey][$itemKey] .= $string; + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $this->clearKey($nsKey, $itemKey); + case self::MODE_APPEND: + if (!$this->hasKey($nsKey, $itemKey)) { + $this->_contents[$nsKey][$itemKey] = ''; + } + while (false !== $bytes = $os->read(8192)) { + $this->_contents[$nsKey][$itemKey] .= $bytes; + } + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + } + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + return $this->_contents[$nsKey][$itemKey]; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + $this->_prepareCache($nsKey); + $is->write($this->getString($nsKey, $itemKey)); + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + + return array_key_exists($itemKey, $this->_contents[$nsKey]); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + unset($this->_contents[$nsKey][$itemKey]); + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + unset($this->_contents[$nsKey]); + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + if (!array_key_exists($nsKey, $this->_contents)) { + $this->_contents[$nsKey] = array(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php new file mode 100644 index 0000000..dc1515a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php @@ -0,0 +1,324 @@ +_stream = $stream; + $this->_path = $path; + + if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) { + $this->_quotes = true; + } + } + + /** + * Set a string into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param string $string + * @param int $mode + * + * @throws Swift_IoException + */ + public function setString($nsKey, $itemKey, $string, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + fwrite($fp, $string); + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Set a ByteStream into the cache under $itemKey for the namespace $nsKey. + * + * @see MODE_WRITE, MODE_APPEND + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_OutputByteStream $os + * @param int $mode + * + * @throws Swift_IoException + */ + public function importFromByteStream($nsKey, $itemKey, Swift_OutputByteStream $os, $mode) + { + $this->_prepareCache($nsKey); + switch ($mode) { + case self::MODE_WRITE: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + break; + case self::MODE_APPEND: + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_END); + break; + default: + throw new Swift_SwiftException( + 'Invalid mode ['.$mode.'] used to set nsKey='. + $nsKey.', itemKey='.$itemKey + ); + break; + } + while (false !== $bytes = $os->read(8192)) { + fwrite($fp, $bytes); + } + $this->_freeHandle($nsKey, $itemKey); + } + + /** + * Provides a ByteStream which when written to, writes data to $itemKey. + * + * NOTE: The stream will always write in append mode. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $writeThrough + * + * @return Swift_InputByteStream + */ + public function getInputByteStream($nsKey, $itemKey, Swift_InputByteStream $writeThrough = null) + { + $is = clone $this->_stream; + $is->setKeyCache($this); + $is->setNsKey($nsKey); + $is->setItemKey($itemKey); + if (isset($writeThrough)) { + $is->setWriteThroughStream($writeThrough); + } + + return $is; + } + + /** + * Get data back out of the cache as a string. + * + * @param string $nsKey + * @param string $itemKey + * + * @throws Swift_IoException + * + * @return string + */ + public function getString($nsKey, $itemKey) + { + $this->_prepareCache($nsKey); + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + $str = ''; + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $str .= $bytes; + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + + return $str; + } + } + + /** + * Get data back out of the cache as a ByteStream. + * + * @param string $nsKey + * @param string $itemKey + * @param Swift_InputByteStream $is to write the data to + */ + public function exportToByteStream($nsKey, $itemKey, Swift_InputByteStream $is) + { + if ($this->hasKey($nsKey, $itemKey)) { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_START); + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 0); + } + while (!feof($fp) && false !== $bytes = fread($fp, 8192)) { + $is->write($bytes); + } + if ($this->_quotes) { + ini_set('magic_quotes_runtime', 1); + } + $this->_freeHandle($nsKey, $itemKey); + } + } + + /** + * Check if the given $itemKey exists in the namespace $nsKey. + * + * @param string $nsKey + * @param string $itemKey + * + * @return bool + */ + public function hasKey($nsKey, $itemKey) + { + return is_file($this->_path.'/'.$nsKey.'/'.$itemKey); + } + + /** + * Clear data for $itemKey in the namespace $nsKey if it exists. + * + * @param string $nsKey + * @param string $itemKey + */ + public function clearKey($nsKey, $itemKey) + { + if ($this->hasKey($nsKey, $itemKey)) { + $this->_freeHandle($nsKey, $itemKey); + unlink($this->_path.'/'.$nsKey.'/'.$itemKey); + } + } + + /** + * Clear all data in the namespace $nsKey if it exists. + * + * @param string $nsKey + */ + public function clearAll($nsKey) + { + if (array_key_exists($nsKey, $this->_keys)) { + foreach ($this->_keys[$nsKey] as $itemKey => $null) { + $this->clearKey($nsKey, $itemKey); + } + if (is_dir($this->_path.'/'.$nsKey)) { + rmdir($this->_path.'/'.$nsKey); + } + unset($this->_keys[$nsKey]); + } + } + + /** + * Initialize the namespace of $nsKey if needed. + * + * @param string $nsKey + */ + private function _prepareCache($nsKey) + { + $cacheDir = $this->_path.'/'.$nsKey; + if (!is_dir($cacheDir)) { + if (!mkdir($cacheDir)) { + throw new Swift_IoException('Failed to create cache directory '.$cacheDir); + } + $this->_keys[$nsKey] = array(); + } + } + + /** + * Get a file handle on the cache item. + * + * @param string $nsKey + * @param string $itemKey + * @param int $position + * + * @return resource + */ + private function _getHandle($nsKey, $itemKey, $position) + { + if (!isset($this->_keys[$nsKey][$itemKey])) { + $openMode = $this->hasKey($nsKey, $itemKey) + ? 'r+b' + : 'w+b' + ; + $fp = fopen($this->_path.'/'.$nsKey.'/'.$itemKey, $openMode); + $this->_keys[$nsKey][$itemKey] = $fp; + } + if (self::POSITION_START == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_SET); + } elseif (self::POSITION_END == $position) { + fseek($this->_keys[$nsKey][$itemKey], 0, SEEK_END); + } + + return $this->_keys[$nsKey][$itemKey]; + } + + private function _freeHandle($nsKey, $itemKey) + { + $fp = $this->_getHandle($nsKey, $itemKey, self::POSITION_CURRENT); + fclose($fp); + $this->_keys[$nsKey][$itemKey] = null; + } + + /** + * Destructor. + */ + public function __destruct() + { + foreach ($this->_keys as $nsKey => $null) { + $this->clearAll($nsKey); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php new file mode 100644 index 0000000..af80bdc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php @@ -0,0 +1,51 @@ +_keyCache = $keyCache; + } + + /** + * Specify a stream to write through for each write(). + * + * @param Swift_InputByteStream $is + */ + public function setWriteThroughStream(Swift_InputByteStream $is) + { + $this->_writeThrough = $is; + } + + /** + * Writes $bytes to the end of the stream. + * + * @param string $bytes + * @param Swift_InputByteStream $is optional + */ + public function write($bytes, Swift_InputByteStream $is = null) + { + $this->_keyCache->setString( + $this->_nsKey, $this->_itemKey, $bytes, Swift_KeyCache::MODE_APPEND + ); + if (isset($is)) { + $is->write($bytes); + } + if (isset($this->_writeThrough)) { + $this->_writeThrough->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Not used. + */ + public function bind(Swift_InputByteStream $is) + { + } + + /** + * Not used. + */ + public function unbind(Swift_InputByteStream $is) + { + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + */ + public function flushBuffers() + { + $this->_keyCache->clearKey($this->_nsKey, $this->_itemKey); + } + + /** + * Set the nsKey which will be written to. + * + * @param string $nsKey + */ + public function setNsKey($nsKey) + { + $this->_nsKey = $nsKey; + } + + /** + * Set the itemKey which will be written to. + * + * @param string $itemKey + */ + public function setItemKey($itemKey) + { + $this->_itemKey = $itemKey; + } + + /** + * Any implementation should be cloneable, allowing the clone to access a + * separate $nsKey and $itemKey. + */ + public function __clone() + { + $this->_writeThrough = null; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php new file mode 100644 index 0000000..fdba9df --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php @@ -0,0 +1,45 @@ +createDependenciesFor('transport.loadbalanced') + ); + + $this->setTransports($transports); + } + + /** + * Create a new LoadBalancedTransport instance. + * + * @param array $transports + * + * @return Swift_LoadBalancedTransport + */ + public static function newInstance($transports = array()) + { + return new self($transports); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php new file mode 100644 index 0000000..858ca81 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MailTransport.php @@ -0,0 +1,45 @@ +createDependenciesFor('transport.mail') + ); + + $this->setExtraParams($extraParams); + } + + /** + * Create a new MailTransport instance. + * + * @param string $extraParams To be passed to mail() + * + * @return Swift_MailTransport + */ + public static function newInstance($extraParams = '-f%s') + { + return new self($extraParams); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php new file mode 100644 index 0000000..34a78d4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php @@ -0,0 +1,114 @@ +_transport = $transport; + } + + /** + * Create a new Mailer instance. + * + * @param Swift_Transport $transport + * + * @return Swift_Mailer + */ + public static function newInstance(Swift_Transport $transport) + { + return new self($transport); + } + + /** + * Create a new class instance of one of the message services. + * + * For example 'mimepart' would create a 'message.mimepart' instance + * + * @param string $service + * + * @return object + */ + public function createMessage($service = 'message') + { + return Swift_DependencyContainer::getInstance() + ->lookup('message.'.$service); + } + + /** + * Send the given Message like it would be sent in a mail client. + * + * All recipients (with the exception of Bcc) will be able to see the other + * recipients this message was sent to. + * + * Recipient/sender data will be retrieved from the Message object. + * + * The return value is the number of recipients who were accepted for + * delivery. + * + * @param Swift_Mime_Message $message + * @param array $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if (!$this->_transport->isStarted()) { + $this->_transport->start(); + } + + $sent = 0; + + try { + $sent = $this->_transport->send($message, $failedRecipients); + } catch (Swift_RfcComplianceException $e) { + foreach ($message->getTo() as $address => $name) { + $failedRecipients[] = $address; + } + } + + return $sent; + } + + /** + * Register a plugin using a known unique key (e.g. myPlugin). + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_transport->registerPlugin($plugin); + } + + /** + * The Transport used to send messages. + * + * @return Swift_Transport + */ + public function getTransport() + { + return $this->_transport; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php new file mode 100644 index 0000000..e3e6cad --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php @@ -0,0 +1,55 @@ +_recipients = $recipients; + } + + /** + * Returns true only if there are more recipients to send to. + * + * @return bool + */ + public function hasNext() + { + return !empty($this->_recipients); + } + + /** + * Returns an array where the keys are the addresses of recipients and the + * values are the names. e.g. ('foo@bar' => 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient() + { + return array_splice($this->_recipients, 0, 1); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php new file mode 100644 index 0000000..650f3ec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/RecipientIterator.php @@ -0,0 +1,32 @@ + 'Foo') or ('foo@bar' => NULL). + * + * @return array + */ + public function nextRecipient(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php new file mode 100644 index 0000000..5b23969 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in memory. + * + * @author Fabien Potencier + */ +class Swift_MemorySpool implements Swift_Spool +{ + protected $messages = array(); + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Stores a message in the queue. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message) + { + //clone the message to make sure it is not changed while in the queue + $this->messages[] = clone $message; + + return true; + } + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null) + { + if (!$this->messages) { + return 0; + } + + if (!$transport->isStarted()) { + $transport->start(); + } + + $count = 0; + while ($message = array_pop($this->messages)) { + $count += $transport->send($message, $failedRecipients); + } + + return $count; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php new file mode 100644 index 0000000..11aa5a9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php @@ -0,0 +1,291 @@ +createDependenciesFor('mime.message') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setSubject($subject); + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new Message. + * + * @param string $subject + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Message + */ + public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) + { + return new self($subject, $body, $contentType, $charset); + } + + /** + * Add a MimePart to this Message. + * + * @param string|Swift_OutputByteStream $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Mime_SimpleMessage + */ + public function addPart($body, $contentType = null, $charset = null) + { + return $this->attach(Swift_MimePart::newInstance( + $body, $contentType, $charset + )); + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * + * @return Swift_Message + */ + public function attachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + $this->headerSigners[] = $signer; + } elseif ($signer instanceof Swift_Signers_BodySigner) { + $this->bodySigners[] = $signer; + } + + return $this; + } + + /** + * Attach a new signature handler to the message. + * + * @param Swift_Signer $signer + * + * @return Swift_Message + */ + public function detachSigner(Swift_Signer $signer) + { + if ($signer instanceof Swift_Signers_HeaderSigner) { + foreach ($this->headerSigners as $k => $headerSigner) { + if ($headerSigner === $signer) { + unset($this->headerSigners[$k]); + + return $this; + } + } + } elseif ($signer instanceof Swift_Signers_BodySigner) { + foreach ($this->bodySigners as $k => $bodySigner) { + if ($bodySigner === $signer) { + unset($this->bodySigners[$k]); + + return $this; + } + } + } + + return $this; + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + return parent::toString(); + } + + $this->saveMessage(); + + $this->doSign(); + + $string = parent::toString(); + + $this->restoreMessage(); + + return $string; + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (empty($this->headerSigners) && empty($this->bodySigners)) { + parent::toByteStream($is); + + return; + } + + $this->saveMessage(); + + $this->doSign(); + + parent::toByteStream($is); + + $this->restoreMessage(); + } + + public function __wakeup() + { + Swift_DependencyContainer::getInstance()->createDependenciesFor('mime.message'); + } + + /** + * loops through signers and apply the signatures. + */ + protected function doSign() + { + foreach ($this->bodySigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->signMessage($this); + } + + foreach ($this->headerSigners as $signer) { + $altered = $signer->getAlteredHeaders(); + $this->saveHeaders($altered); + $signer->reset(); + + $signer->setHeaders($this->getHeaders()); + + $signer->startBody(); + $this->_bodyToByteStream($signer); + $signer->endBody(); + + $signer->addSignature($this->getHeaders()); + } + } + + /** + * save the message before any signature is applied. + */ + protected function saveMessage() + { + $this->savedMessage = array('headers' => array()); + $this->savedMessage['body'] = $this->getBody(); + $this->savedMessage['children'] = $this->getChildren(); + if (count($this->savedMessage['children']) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $this->savedMessage['children'])); + $this->setBody(''); + } + } + + /** + * save the original headers. + * + * @param array $altered + */ + protected function saveHeaders(array $altered) + { + foreach ($altered as $head) { + $lc = strtolower($head); + + if (!isset($this->savedMessage['headers'][$lc])) { + $this->savedMessage['headers'][$lc] = $this->getHeaders()->getAll($head); + } + } + } + + /** + * Remove or restore altered headers. + */ + protected function restoreHeaders() + { + foreach ($this->savedMessage['headers'] as $name => $savedValue) { + $headers = $this->getHeaders()->getAll($name); + + foreach ($headers as $key => $value) { + if (!isset($savedValue[$key])) { + $this->getHeaders()->remove($name, $key); + } + } + } + } + + /** + * Restore message body. + */ + protected function restoreMessage() + { + $this->setBody($this->savedMessage['body']); + $this->setChildren($this->savedMessage['children']); + + $this->restoreHeaders(); + $this->savedMessage = array(); + } + + /** + * Clone Message Signers. + * + * @see Swift_Mime_SimpleMimeEntity::__clone() + */ + public function __clone() + { + parent::__clone(); + foreach ($this->bodySigners as $key => $bodySigner) { + $this->bodySigners[$key] = clone($bodySigner); + } + + foreach ($this->headerSigners as $key => $headerSigner) { + $this->headerSigners[$key] = clone($headerSigner); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php new file mode 100644 index 0000000..0a72606 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php @@ -0,0 +1,153 @@ +setDisposition('attachment'); + $this->setContentType('application/octet-stream'); + $this->_mimeTypes = $mimeTypes; + } + + /** + * Get the nesting level used for this attachment. + * + * Always returns {@link LEVEL_MIXED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_MIXED; + } + + /** + * Get the Content-Disposition of this attachment. + * + * By default attachments have a disposition of "attachment". + * + * @return string + */ + public function getDisposition() + { + return $this->_getHeaderFieldModel('Content-Disposition'); + } + + /** + * Set the Content-Disposition of this attachment. + * + * @param string $disposition + * + * @return Swift_Mime_Attachment + */ + public function setDisposition($disposition) + { + if (!$this->_setHeaderFieldModel('Content-Disposition', $disposition)) { + $this->getHeaders()->addParameterizedHeader( + 'Content-Disposition', $disposition + ); + } + + return $this; + } + + /** + * Get the filename of this attachment when downloaded. + * + * @return string + */ + public function getFilename() + { + return $this->_getHeaderParameter('Content-Disposition', 'filename'); + } + + /** + * Set the filename of this attachment. + * + * @param string $filename + * + * @return Swift_Mime_Attachment + */ + public function setFilename($filename) + { + $this->_setHeaderParameter('Content-Disposition', 'filename', $filename); + $this->_setHeaderParameter('Content-Type', 'name', $filename); + + return $this; + } + + /** + * Get the file size of this attachment. + * + * @return int + */ + public function getSize() + { + return $this->_getHeaderParameter('Content-Disposition', 'size'); + } + + /** + * Set the file size of this attachment. + * + * @param int $size + * + * @return Swift_Mime_Attachment + */ + public function setSize($size) + { + $this->_setHeaderParameter('Content-Disposition', 'size', $size); + + return $this; + } + + /** + * Set the file that this attachment is for. + * + * @param Swift_FileStream $file + * @param string $contentType optional + * + * @return Swift_Mime_Attachment + */ + public function setFile(Swift_FileStream $file, $contentType = null) + { + $this->setFilename(basename($file->getPath())); + $this->setBody($file, $contentType); + if (!isset($contentType)) { + $extension = strtolower(substr( + $file->getPath(), strrpos($file->getPath(), '.') + 1 + )); + + if (array_key_exists($extension, $this->_mimeTypes)) { + $this->setContentType($this->_mimeTypes[$extension]); + } + } + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php new file mode 100644 index 0000000..b49c3a8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php @@ -0,0 +1,24 @@ += $maxLineLength || 76 < $maxLineLength) { + $maxLineLength = 76; + } + + $remainder = 0; + $base64ReadBufferRemainderBytes = null; + + // To reduce memory usage, the output buffer is streamed to the input buffer like so: + // Output Stream => base64encode => wrap line length => Input Stream + // HOWEVER it's important to note that base64_encode() should only be passed whole triplets of data (except for the final chunk of data) + // otherwise it will assume the input data has *ended* and it will incorrectly pad/terminate the base64 data mid-stream. + // We use $base64ReadBufferRemainderBytes to carry over 1-2 "remainder" bytes from the each chunk from OutputStream and pre-pend those onto the + // chunk of bytes read in the next iteration. + // When the OutputStream is empty, we must flush any remainder bytes. + while (true) { + $readBytes = $os->read(8192); + $atEOF = ($readBytes === false); + + if ($atEOF) { + $streamTheseBytes = $base64ReadBufferRemainderBytes; + } else { + $streamTheseBytes = $base64ReadBufferRemainderBytes.$readBytes; + } + $base64ReadBufferRemainderBytes = null; + $bytesLength = strlen($streamTheseBytes); + + if ($bytesLength === 0) { // no data left to encode + break; + } + + // if we're not on the last block of the ouput stream, make sure $streamTheseBytes ends with a complete triplet of data + // and carry over remainder 1-2 bytes to the next loop iteration + if (!$atEOF) { + $excessBytes = $bytesLength % 3; + if ($excessBytes !== 0) { + $base64ReadBufferRemainderBytes = substr($streamTheseBytes, -$excessBytes); + $streamTheseBytes = substr($streamTheseBytes, 0, $bytesLength - $excessBytes); + } + } + + $encoded = base64_encode($streamTheseBytes); + $encodedTransformed = ''; + $thisMaxLineLength = $maxLineLength - $remainder - $firstLineOffset; + + while ($thisMaxLineLength < strlen($encoded)) { + $encodedTransformed .= substr($encoded, 0, $thisMaxLineLength)."\r\n"; + $firstLineOffset = 0; + $encoded = substr($encoded, $thisMaxLineLength); + $thisMaxLineLength = $maxLineLength; + $remainder = 0; + } + + if (0 < $remainingLength = strlen($encoded)) { + $remainder += $remainingLength; + $encodedTransformed .= $encoded; + $encoded = null; + } + + $is->write($encodedTransformed); + + if ($atEOF) { + break; + } + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'base64'. + * + * @return string + */ + public function getName() + { + return 'base64'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php new file mode 100644 index 0000000..710b5ac --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php @@ -0,0 +1,123 @@ +charset = $charset ? $charset : 'utf-8'; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + } + + /** + * Encode $in to $out. + * + * @param Swift_OutputByteStream $os to read from + * @param Swift_InputByteStream $is to write to + * @param int $firstLineOffset + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + $string = ''; + + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $is->write($this->encodeString($string)); + } + + /** + * Get the MIME name of this content encoding scheme. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset if first line needs to be shorter + * @param int $maxLineLength 0 indicates the default length for this encoding + * + * @throws RuntimeException + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->charset !== 'utf-8') { + throw new RuntimeException( + sprintf('Charset "%s" not supported. NativeQpContentEncoder only supports "utf-8"', $this->charset)); + } + + return $this->_standardize(quoted_printable_encode($string)); + } + + /** + * Make sure CRLF is correct and HT/SPACE are in valid places. + * + * @param string $string + * + * @return string + */ + protected function _standardize($string) + { + // transform CR or LF to CRLF + $string = preg_replace('~=0D(?!=0A)|(?_name = $name; + $this->_canonical = $canonical; + } + + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength - 0 means no wrapping will occur + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($this->_canonical) { + $string = $this->_canonicalize($string); + } + + return $this->_safeWordWrap($string, $maxLineLength, "\r\n"); + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $os + * @param Swift_InputByteStream $is + * @param int $firstLineOffset ignored + * @param int $maxLineLength optional, 0 means no wrapping will occur + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $leftOver = ''; + while (false !== $bytes = $os->read(8192)) { + $toencode = $leftOver.$bytes; + if ($this->_canonical) { + $toencode = $this->_canonicalize($toencode); + } + $wrapped = $this->_safeWordWrap($toencode, $maxLineLength, "\r\n"); + $lastLinePos = strrpos($wrapped, "\r\n"); + $leftOver = substr($wrapped, $lastLinePos); + $wrapped = substr($wrapped, 0, $lastLinePos); + + $is->write($wrapped); + } + if (strlen($leftOver)) { + $is->write($leftOver); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return $this->_name; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } + + /** + * A safer (but weaker) wordwrap for unicode. + * + * @param string $string + * @param int $length + * @param string $le + * + * @return string + */ + private function _safeWordwrap($string, $length = 75, $le = "\r\n") + { + if (0 >= $length) { + return $string; + } + + $originalLines = explode($le, $string); + + $lines = array(); + $lineCount = 0; + + foreach ($originalLines as $originalLine) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + + //$chunks = preg_split('/(?<=[\ \t,\.!\?\-&\+\/])/', $originalLine); + $chunks = preg_split('/(?<=\s)/', $originalLine); + + foreach ($chunks as $chunk) { + if (0 != strlen($currentLine) + && strlen($currentLine.$chunk) > $length) { + $lines[] = ''; + $currentLine = &$lines[$lineCount++]; + } + $currentLine .= $chunk; + } + } + + return implode("\r\n", $lines); + } + + /** + * Canonicalize string input (fix CRLF). + * + * @param string $string + * + * @return string + */ + private function _canonicalize($string) + { + return str_replace( + array("\r\n", "\r", "\n"), + array("\n", "\n", "\r\n"), + $string + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php new file mode 100644 index 0000000..8ee4c75 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php @@ -0,0 +1,123 @@ +_dotEscape = $dotEscape; + parent::__construct($charStream, $filter); + } + + public function __sleep() + { + return array('_charStream', '_filter', '_dotEscape'); + } + + protected function getSafeMapShareId() + { + return get_class($this).($this->_dotEscape ? '.dotEscape' : ''); + } + + protected function initSafeMap() + { + parent::initSafeMap(); + if ($this->_dotEscape) { + /* Encode . as =2e for buggy remote servers */ + unset($this->_safeMap[0x2e]); + } + } + + /** + * Encode stream $in to stream $out. + * + * QP encoded strings have a maximum line length of 76 characters. + * If the first line needs to be shorter, indicate the difference with + * $firstLineOffset. + * + * @param Swift_OutputByteStream $os output stream + * @param Swift_InputByteStream $is input stream + * @param int $firstLineOffset + * @param int $maxLineLength + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + if ($maxLineLength > 76 || $maxLineLength <= 0) { + $maxLineLength = 76; + } + + $thisLineLength = $maxLineLength - $firstLineOffset; + + $this->_charStream->flushContents(); + $this->_charStream->importByteStream($os); + + $currentLine = ''; + $prepend = ''; + $size = $lineLen = 0; + + while (false !== $bytes = $this->_nextSequence()) { + // If we're filtering the input + if (isset($this->_filter)) { + // If we can't filter because we need more bytes + while ($this->_filter->shouldBuffer($bytes)) { + // Then collect bytes into the buffer + if (false === $moreBytes = $this->_nextSequence(1)) { + break; + } + + foreach ($moreBytes as $b) { + $bytes[] = $b; + } + } + // And filter them + $bytes = $this->_filter->filter($bytes); + } + + $enc = $this->_encodeByteSequence($bytes, $size); + if ($currentLine && $lineLen + $size >= $thisLineLength) { + $is->write($prepend.$this->_standardize($currentLine)); + $currentLine = ''; + $prepend = "=\r\n"; + $thisLineLength = $maxLineLength; + $lineLen = 0; + } + $lineLen += $size; + $currentLine .= $enc; + } + if (strlen($currentLine)) { + $is->write($prepend.$this->_standardize($currentLine)); + } + } + + /** + * Get the name of this encoding scheme. + * Returns the string 'quoted-printable'. + * + * @return string + */ + public function getName() + { + return 'quoted-printable'; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php new file mode 100644 index 0000000..8113e52 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php @@ -0,0 +1,97 @@ + + */ +class Swift_Mime_ContentEncoder_QpContentEncoderProxy implements Swift_Mime_ContentEncoder +{ + /** + * @var Swift_Mime_ContentEncoder_QpContentEncoder + */ + private $safeEncoder; + + /** + * @var Swift_Mime_ContentEncoder_NativeQpContentEncoder + */ + private $nativeEncoder; + + /** + * @var null|string + */ + private $charset; + + /** + * Constructor. + * + * @param Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder + * @param Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder + * @param string|null $charset + */ + public function __construct(Swift_Mime_ContentEncoder_QpContentEncoder $safeEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder $nativeEncoder, $charset) + { + $this->safeEncoder = $safeEncoder; + $this->nativeEncoder = $nativeEncoder; + $this->charset = $charset; + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->safeEncoder = clone $this->safeEncoder; + $this->nativeEncoder = clone $this->nativeEncoder; + } + + /** + * {@inheritdoc} + */ + public function charsetChanged($charset) + { + $this->charset = $charset; + } + + /** + * {@inheritdoc} + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + $this->getEncoder()->encodeByteStream($os, $is, $firstLineOffset, $maxLineLength); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'quoted-printable'; + } + + /** + * {@inheritdoc} + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $this->getEncoder()->encodeString($string, $firstLineOffset, $maxLineLength); + } + + /** + * @return Swift_Mime_ContentEncoder + */ + private function getEncoder() + { + return 'utf-8' === $this->charset ? $this->nativeEncoder : $this->safeEncoder; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php new file mode 100644 index 0000000..0b8526e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php @@ -0,0 +1,64 @@ + + */ +class Swift_Mime_ContentEncoder_RawContentEncoder implements Swift_Mime_ContentEncoder +{ + /** + * Encode a given string to produce an encoded string. + * + * @param string $string + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return $string; + } + + /** + * Encode stream $in to stream $out. + * + * @param Swift_OutputByteStream $in + * @param Swift_InputByteStream $out + * @param int $firstLineOffset ignored + * @param int $maxLineLength ignored + */ + public function encodeByteStream(Swift_OutputByteStream $os, Swift_InputByteStream $is, $firstLineOffset = 0, $maxLineLength = 0) + { + while (false !== ($bytes = $os->read(8192))) { + $is->write($bytes); + } + } + + /** + * Get the name of this encoding scheme. + * + * @return string + */ + public function getName() + { + return 'raw'; + } + + /** + * Not used. + */ + public function charsetChanged($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php new file mode 100644 index 0000000..6af7571 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php @@ -0,0 +1,45 @@ +setDisposition('inline'); + $this->setId($this->getId()); + } + + /** + * Get the nesting level of this EmbeddedFile. + * + * Returns {@see LEVEL_RELATED}. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_RELATED; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php new file mode 100644 index 0000000..cc44a6e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php @@ -0,0 +1,24 @@ +init(); + } + + public function __wakeup() + { + $this->init(); + } + + protected function init() + { + if (count(self::$_specials) > 0) { + return; + } + + self::$_specials = array( + '(', ')', '<', '>', '[', ']', + ':', ';', '@', ',', '.', '"', + ); + + /*** Refer to RFC 2822 for ABNF grammar ***/ + + // All basic building blocks + self::$_grammar['NO-WS-CTL'] = '[\x01-\x08\x0B\x0C\x0E-\x19\x7F]'; + self::$_grammar['WSP'] = '[ \t]'; + self::$_grammar['CRLF'] = '(?:\r\n)'; + self::$_grammar['FWS'] = '(?:(?:'.self::$_grammar['WSP'].'*'. + self::$_grammar['CRLF'].')?'.self::$_grammar['WSP'].')'; + self::$_grammar['text'] = '[\x00-\x08\x0B\x0C\x0E-\x7F]'; + self::$_grammar['quoted-pair'] = '(?:\\\\'.self::$_grammar['text'].')'; + self::$_grammar['ctext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x27\x2A-\x5B\x5D-\x7E])'; + // Uses recursive PCRE (?1) -- could be a weak point?? + self::$_grammar['ccontent'] = '(?:'.self::$_grammar['ctext'].'|'. + self::$_grammar['quoted-pair'].'|(?1))'; + self::$_grammar['comment'] = '(\((?:'.self::$_grammar['FWS'].'|'. + self::$_grammar['ccontent'].')*'.self::$_grammar['FWS'].'?\))'; + self::$_grammar['CFWS'] = '(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')*(?:(?:'.self::$_grammar['FWS'].'?'. + self::$_grammar['comment'].')|'.self::$_grammar['FWS'].'))'; + self::$_grammar['qtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21\x23-\x5B\x5D-\x7E])'; + self::$_grammar['qcontent'] = '(?:'.self::$_grammar['qtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['quoted-string'] = '(?:'.self::$_grammar['CFWS'].'?"'. + '('.self::$_grammar['FWS'].'?'.self::$_grammar['qcontent'].')*'. + self::$_grammar['FWS'].'?"'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['atext'] = '[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]'; + self::$_grammar['atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['atext'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['dot-atom-text'] = '(?:'.self::$_grammar['atext'].'+'. + '(\.'.self::$_grammar['atext'].'+)*)'; + self::$_grammar['dot-atom'] = '(?:'.self::$_grammar['CFWS'].'?'. + self::$_grammar['dot-atom-text'].'+'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['word'] = '(?:'.self::$_grammar['atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['phrase'] = '(?:'.self::$_grammar['word'].'+?)'; + self::$_grammar['no-fold-quote'] = '(?:"(?:'.self::$_grammar['qtext']. + '|'.self::$_grammar['quoted-pair'].')*")'; + self::$_grammar['dtext'] = '(?:'.self::$_grammar['NO-WS-CTL']. + '|[\x21-\x5A\x5E-\x7E])'; + self::$_grammar['no-fold-literal'] = '(?:\[(?:'.self::$_grammar['dtext']. + '|'.self::$_grammar['quoted-pair'].')*\])'; + + // Message IDs + self::$_grammar['id-left'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-quote'].')'; + self::$_grammar['id-right'] = '(?:'.self::$_grammar['dot-atom-text'].'|'. + self::$_grammar['no-fold-literal'].')'; + + // Addresses, mailboxes and paths + self::$_grammar['local-part'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['quoted-string'].')'; + self::$_grammar['dcontent'] = '(?:'.self::$_grammar['dtext'].'|'. + self::$_grammar['quoted-pair'].')'; + self::$_grammar['domain-literal'] = '(?:'.self::$_grammar['CFWS'].'?\[('. + self::$_grammar['FWS'].'?'.self::$_grammar['dcontent'].')*?'. + self::$_grammar['FWS'].'?\]'.self::$_grammar['CFWS'].'?)'; + self::$_grammar['domain'] = '(?:'.self::$_grammar['dot-atom'].'|'. + self::$_grammar['domain-literal'].')'; + self::$_grammar['addr-spec'] = '(?:'.self::$_grammar['local-part'].'@'. + self::$_grammar['domain'].')'; + } + + /** + * Get the grammar defined for $name token. + * + * @param string $name exactly as written in the RFC + * + * @return string + */ + public function getDefinition($name) + { + if (array_key_exists($name, self::$_grammar)) { + return self::$_grammar[$name]; + } else { + throw new Swift_RfcComplianceException( + "No such grammar '".$name."' defined." + ); + } + } + + /** + * Returns the tokens defined in RFC 2822 (and some related RFCs). + * + * @return array + */ + public function getGrammarDefinitions() + { + return self::$_grammar; + } + + /** + * Returns the current special characters used in the syntax which need to be escaped. + * + * @return array + */ + public function getSpecials() + { + return self::$_specials; + } + + /** + * Escape special characters in a string (convert to quoted-pairs). + * + * @param string $token + * @param string[] $include additional chars to escape + * @param string[] $exclude chars from escaping + * + * @return string + */ + public function escapeSpecials($token, $include = array(), $exclude = array()) + { + foreach (array_merge(array('\\'), array_diff(self::$_specials, $exclude), $include) as $char) { + $token = str_replace($char, '\\'.$char, $token); + } + + return $token; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php new file mode 100644 index 0000000..a8ddd27 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php @@ -0,0 +1,93 @@ +getName(), "\r\n"); + mb_internal_encoding($old); + + return $newstring; + } + + return parent::encodeString($string, $firstLineOffset, $maxLineLength); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php new file mode 100644 index 0000000..510dd66 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php @@ -0,0 +1,65 @@ +_safeMap[$byte] = chr($byte); + } + } + + /** + * Get the name of this encoding scheme. + * + * Returns the string 'Q'. + * + * @return string + */ + public function getName() + { + return 'Q'; + } + + /** + * Takes an unencoded string and produces a QP encoded string from it. + * + * @param string $string string to encode + * @param int $firstLineOffset optional + * @param int $maxLineLength optional, 0 indicates the default of 76 chars + * + * @return string + */ + public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0) + { + return str_replace(array(' ', '=20', "=\r\n"), array('_', '_', "\r\n"), + parent::encodeString($string, $firstLineOffset, $maxLineLength) + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php new file mode 100644 index 0000000..c65f26d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php @@ -0,0 +1,78 @@ +setGrammar($grammar); + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->clearCachedValueIf($charset != $this->_charset); + $this->_charset = $charset; + if (isset($this->_encoder)) { + $this->_encoder->charsetChanged($charset); + } + } + + /** + * Get the character set used in this Header. + * + * @return string + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * Set the language used in this Header. + * + * For example, for US English, 'en-us'. + * This can be unspecified. + * + * @param string $lang + */ + public function setLanguage($lang) + { + $this->clearCachedValueIf($this->_lang != $lang); + $this->_lang = $lang; + } + + /** + * Get the language used in this Header. + * + * @return string + */ + public function getLanguage() + { + return $this->_lang; + } + + /** + * Set the encoder used for encoding the header. + * + * @param Swift_Mime_HeaderEncoder $encoder + */ + public function setEncoder(Swift_Mime_HeaderEncoder $encoder) + { + $this->_encoder = $encoder; + $this->setCachedValue(null); + } + + /** + * Get the encoder used for encoding this Header. + * + * @return Swift_Mime_HeaderEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the grammar used for the header. + * + * @param Swift_Mime_Grammar $grammar + */ + public function setGrammar(Swift_Mime_Grammar $grammar) + { + $this->_grammar = $grammar; + $this->setCachedValue(null); + } + + /** + * Get the grammar used for this Header. + * + * @return Swift_Mime_Grammar + */ + public function getGrammar() + { + return $this->_grammar; + } + + /** + * Get the name of this header (e.g. charset). + * + * @return string + */ + public function getFieldName() + { + return $this->_name; + } + + /** + * Set the maximum length of lines in the header (excluding EOL). + * + * @param int $lineLength + */ + public function setMaxLineLength($lineLength) + { + $this->clearCachedValueIf($this->_lineLength != $lineLength); + $this->_lineLength = $lineLength; + } + + /** + * Get the maximum permitted length of lines in this Header. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_lineLength; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function toString() + { + return $this->_tokensToString($this->toTokens()); + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + // -- Points of extension + + /** + * Set the name of this Header field. + * + * @param string $name + */ + protected function setFieldName($name) + { + $this->_name = $name; + } + + /** + * Produces a compliant, formatted RFC 2822 'phrase' based on the string given. + * + * @param Swift_Mime_Header $header + * @param string $string as displayed + * @param string $charset of the text + * @param Swift_Mime_HeaderEncoder $encoder + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false) + { + // Treat token as exactly what was given + $phraseStr = $string; + // If it's not valid + if (!preg_match('/^'.$this->getGrammar()->getDefinition('phrase').'$/D', $phraseStr)) { + // .. but it is just ascii text, try escaping some characters + // and make it a quoted-string + if (preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $phraseStr)) { + $phraseStr = $this->getGrammar()->escapeSpecials( + $phraseStr, array('"'), $this->getGrammar()->getSpecials() + ); + $phraseStr = '"'.$phraseStr.'"'; + } else { + // ... otherwise it needs encoding + // Determine space remaining on line if first line + if ($shorten) { + $usedLength = strlen($header->getFieldName().': '); + } else { + $usedLength = 0; + } + $phraseStr = $this->encodeWords($header, $string, $usedLength); + } + } + + return $phraseStr; + } + + /** + * Encode needed word tokens within a string of input. + * + * @param Swift_Mime_Header $header + * @param string $input + * @param string $usedLength optional + * + * @return string + */ + protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1) + { + $value = ''; + + $tokens = $this->getEncodableWordTokens($input); + + foreach ($tokens as $token) { + // See RFC 2822, Sect 2.2 (really 2.2 ??) + if ($this->tokenNeedsEncoding($token)) { + // Don't encode starting WSP + $firstChar = substr($token, 0, 1); + switch ($firstChar) { + case ' ': + case "\t": + $value .= $firstChar; + $token = substr($token, 1); + } + + if (-1 == $usedLength) { + $usedLength = strlen($header->getFieldName().': ') + strlen($value); + } + $value .= $this->getTokenAsEncodedWord($token, $usedLength); + + $header->setMaxLineLength(76); // Forcefully override + } else { + $value .= $token; + } + } + + return $value; + } + + /** + * Test if a token needs to be encoded or not. + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token); + } + + /** + * Splits a string into tokens in blocks of words which can be encoded quickly. + * + * @param string $string + * + * @return string[] + */ + protected function getEncodableWordTokens($string) + { + $tokens = array(); + + $encodedToken = ''; + // Split at all whitespace boundaries + foreach (preg_split('~(?=[\t ])~', $string) as $token) { + if ($this->tokenNeedsEncoding($token)) { + $encodedToken .= $token; + } else { + if (strlen($encodedToken) > 0) { + $tokens[] = $encodedToken; + $encodedToken = ''; + } + $tokens[] = $token; + } + } + if (strlen($encodedToken)) { + $tokens[] = $encodedToken; + } + + return $tokens; + } + + /** + * Get a token as an encoded word for safe insertion into headers. + * + * @param string $token token to encode + * @param int $firstLineOffset optional + * + * @return string + */ + protected function getTokenAsEncodedWord($token, $firstLineOffset = 0) + { + // Adjust $firstLineOffset to account for space needed for syntax + $charsetDecl = $this->_charset; + if (isset($this->_lang)) { + $charsetDecl .= '*'.$this->_lang; + } + $encodingWrapperLength = strlen( + '=?'.$charsetDecl.'?'.$this->_encoder->getName().'??=' + ); + + if ($firstLineOffset >= 75) { + //Does this logic need to be here? + $firstLineOffset = 0; + } + + $encodedTextLines = explode("\r\n", + $this->_encoder->encodeString( + $token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset + ) + ); + + if (strtolower($this->_charset) !== 'iso-2022-jp') { + // special encoding for iso-2022-jp using mb_encode_mimeheader + foreach ($encodedTextLines as $lineNum => $line) { + $encodedTextLines[$lineNum] = '=?'.$charsetDecl. + '?'.$this->_encoder->getName(). + '?'.$line.'?='; + } + } + + return implode("\r\n ", $encodedTextLines); + } + + /** + * Generates tokens from the given string which include CRLF as individual tokens. + * + * @param string $token + * + * @return string[] + */ + protected function generateTokenLines($token) + { + return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE); + } + + /** + * Set a value into the cache. + * + * @param string $value + */ + protected function setCachedValue($value) + { + $this->_cachedValue = $value; + } + + /** + * Get the value in the cache. + * + * @return string + */ + protected function getCachedValue() + { + return $this->_cachedValue; + } + + /** + * Clear the cached value if $condition is met. + * + * @param bool $condition + */ + protected function clearCachedValueIf($condition) + { + if ($condition) { + $this->setCachedValue(null); + } + } + + /** + * Generate a list of all tokens in the final header. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + if (is_null($string)) { + $string = $this->getFieldBody(); + } + + $tokens = array(); + + // Generate atoms; split at all invisible boundaries followed by WSP + foreach (preg_split('~(?=[ \t])~', $string) as $token) { + $newTokens = $this->generateTokenLines($token); + foreach ($newTokens as $newToken) { + $tokens[] = $newToken; + } + } + + return $tokens; + } + + /** + * Takes an array of tokens which appear in the header and turns them into + * an RFC 2822 compliant string, adding FWSP where needed. + * + * @param string[] $tokens + * + * @return string + */ + private function _tokensToString(array $tokens) + { + $lineCount = 0; + $headerLines = array(); + $headerLines[] = $this->_name.': '; + $currentLine = &$headerLines[$lineCount++]; + + // Build all tokens back into compliant header + foreach ($tokens as $i => $token) { + // Line longer than specified maximum or token was just a new line + if (("\r\n" == $token) || + ($i > 0 && strlen($currentLine.$token) > $this->_lineLength) + && 0 < strlen($currentLine)) { + $headerLines[] = ''; + $currentLine = &$headerLines[$lineCount++]; + } + + // Append token to the line + if ("\r\n" != $token) { + $currentLine .= $token; + } + } + + // Implode with FWS (RFC 2822, 2.2.3) + return implode("\r\n", $headerLines)."\r\n"; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php new file mode 100644 index 0000000..4fd6674 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php @@ -0,0 +1,125 @@ + + * + * + * + * @param string $name of Header + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name, Swift_Mime_Grammar $grammar) + { + $this->setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_DATE; + } + + /** + * Set the model for the field body. + * + * This method takes a UNIX timestamp. + * + * @param int $model + */ + public function setFieldBodyModel($model) + { + $this->setTimestamp($model); + } + + /** + * Get the model for the field body. + * + * This method returns a UNIX timestamp. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getTimestamp(); + } + + /** + * Get the UNIX timestamp of the Date in this Header. + * + * @return int + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Set the UNIX timestamp of the Date in this Header. + * + * @param int $timestamp + */ + public function setTimestamp($timestamp) + { + if (!is_null($timestamp)) { + $timestamp = (int) $timestamp; + } + $this->clearCachedValueIf($this->_timestamp != $timestamp); + $this->_timestamp = $timestamp; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_timestamp)) { + $this->setCachedValue(date('r', $this->_timestamp)); + } + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php new file mode 100644 index 0000000..b114506 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php @@ -0,0 +1,180 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_ID; + } + + /** + * Set the model for the field body. + * + * This method takes a string ID, or an array of IDs. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setId($model); + } + + /** + * Get the model for the field body. + * + * This method returns an array of IDs + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getIds(); + } + + /** + * Set the ID used in the value of this header. + * + * @param string|array $id + * + * @throws Swift_RfcComplianceException + */ + public function setId($id) + { + $this->setIds(is_array($id) ? $id : array($id)); + } + + /** + * Get the ID used in the value of this Header. + * + * If multiple IDs are set only the first is returned. + * + * @return string + */ + public function getId() + { + if (count($this->_ids) > 0) { + return $this->_ids[0]; + } + } + + /** + * Set a collection of IDs to use in the value of this Header. + * + * @param string[] $ids + * + * @throws Swift_RfcComplianceException + */ + public function setIds(array $ids) + { + $actualIds = array(); + + foreach ($ids as $id) { + $this->_assertValidId($id); + $actualIds[] = $id; + } + + $this->clearCachedValueIf($this->_ids != $actualIds); + $this->_ids = $actualIds; + } + + /** + * Get the list of IDs used in this Header. + * + * @return string[] + */ + public function getIds() + { + return $this->_ids; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@see toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $angleAddrs = array(); + + foreach ($this->_ids as $id) { + $angleAddrs[] = '<'.$id.'>'; + } + + $this->setCachedValue(implode(' ', $angleAddrs)); + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^'.$this->getGrammar()->getDefinition('id-left').'@'. + $this->getGrammar()->getDefinition('id-right').'$/D', + $id + )) { + throw new Swift_RfcComplianceException( + 'Invalid ID given <'.$id.'>' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php new file mode 100644 index 0000000..798e7f4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php @@ -0,0 +1,354 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_MAILBOX; + } + + /** + * Set the model for the field body. + * + * This method takes a string, or an array of addresses. + * + * @param mixed $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setNameAddresses($model); + } + + /** + * Get the model for the field body. + * + * This method returns an associative array like {@link getNameAddresses()} + * + * @throws Swift_RfcComplianceException + * + * @return array + */ + public function getFieldBodyModel() + { + return $this->getNameAddresses(); + } + + /** + * Set a list of mailboxes to be shown in this Header. + * + * The mailboxes can be a simple array of addresses, or an array of + * key=>value pairs where (email => personalName). + * Example: + * + * setNameAddresses(array( + * 'chris@swiftmailer.org' => 'Chris Corbyn', + * 'mark@swiftmailer.org' //No associated personal name + * )); + * ?> + * + * + * @see __construct() + * @see setAddresses() + * @see setValue() + * + * @param string|string[] $mailboxes + * + * @throws Swift_RfcComplianceException + */ + public function setNameAddresses($mailboxes) + { + $this->_mailboxes = $this->normalizeMailboxes((array) $mailboxes); + $this->setCachedValue(null); //Clear any cached value + } + + /** + * Get the full mailbox list of this Header as an array of valid RFC 2822 strings. + * + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddressStrings()); + * // array ( + * // 0 => Chris Corbyn , + * // 1 => Mark Corbyn + * // ) + * ?> + * + * + * @see getNameAddresses() + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string[] + */ + public function getNameAddressStrings() + { + return $this->_createNameAddressStrings($this->getNameAddresses()); + } + + /** + * Get all mailboxes in this Header as key=>value pairs. + * + * The key is the address and the value is the name (or null if none set). + * Example: + * + * 'Chris Corbyn', + * 'mark@swiftmailer.org' => 'Mark Corbyn') + * ); + * print_r($header->getNameAddresses()); + * // array ( + * // chris@swiftmailer.org => Chris Corbyn, + * // mark@swiftmailer.org => Mark Corbyn + * // ) + * ?> + * + * + * @see getAddresses() + * @see getNameAddressStrings() + * + * @return string[] + */ + public function getNameAddresses() + { + return $this->_mailboxes; + } + + /** + * Makes this Header represent a list of plain email addresses with no names. + * + * Example: + * + * setAddresses( + * array('one@domain.tld', 'two@domain.tld', 'three@domain.tld') + * ); + * ?> + * + * + * @see setNameAddresses() + * @see setValue() + * + * @param string[] $addresses + * + * @throws Swift_RfcComplianceException + */ + public function setAddresses($addresses) + { + $this->setNameAddresses(array_values((array) $addresses)); + } + + /** + * Get all email addresses in this Header. + * + * @see getNameAddresses() + * + * @return string[] + */ + public function getAddresses() + { + return array_keys($this->_mailboxes); + } + + /** + * Remove one or more addresses from this Header. + * + * @param string|string[] $addresses + */ + public function removeAddresses($addresses) + { + $this->setCachedValue(null); + foreach ((array) $addresses as $address) { + unset($this->_mailboxes[$address]); + } + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + public function getFieldBody() + { + // Compute the string value of the header only if needed + if (is_null($this->getCachedValue())) { + $this->setCachedValue($this->createMailboxListString($this->_mailboxes)); + } + + return $this->getCachedValue(); + } + + // -- Points of extension + + /** + * Normalizes a user-input list of mailboxes into consistent key=>value pairs. + * + * @param string[] $mailboxes + * + * @return string[] + */ + protected function normalizeMailboxes(array $mailboxes) + { + $actualMailboxes = array(); + + foreach ($mailboxes as $key => $value) { + if (is_string($key)) { + //key is email addr + $address = $key; + $name = $value; + } else { + $address = $value; + $name = null; + } + $this->_assertValidAddress($address); + $actualMailboxes[$address] = $name; + } + + return $actualMailboxes; + } + + /** + * Produces a compliant, formatted display-name based on the string given. + * + * @param string $displayName as displayed + * @param bool $shorten the first line to make remove for header name + * + * @return string + */ + protected function createDisplayNameString($displayName, $shorten = false) + { + return $this->createPhrase($this, $displayName, + $this->getCharset(), $this->getEncoder(), $shorten + ); + } + + /** + * Creates a string form of all the mailboxes in the passed array. + * + * @param string[] $mailboxes + * + * @throws Swift_RfcComplianceException + * + * @return string + */ + protected function createMailboxListString(array $mailboxes) + { + return implode(', ', $this->_createNameAddressStrings($mailboxes)); + } + + /** + * Redefine the encoding requirements for mailboxes. + * + * Commas and semicolons are used to separate + * multiple addresses, and should therefore be encoded + * + * @param string $token + * + * @return bool + */ + protected function tokenNeedsEncoding($token) + { + return preg_match('/[,;]/', $token) || parent::tokenNeedsEncoding($token); + } + + /** + * Return an array of strings conforming the the name-addr spec of RFC 2822. + * + * @param string[] $mailboxes + * + * @return string[] + */ + private function _createNameAddressStrings(array $mailboxes) + { + $strings = array(); + + foreach ($mailboxes as $email => $name) { + $mailboxStr = $email; + if (!is_null($name)) { + $nameStr = $this->createDisplayNameString($name, empty($strings)); + $mailboxStr = $nameStr.' <'.$mailboxStr.'>'; + } + $strings[] = $mailboxStr; + } + + return $strings; + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If invalid. + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address in mailbox given ['.$address. + '] does not comply with RFC 2822, 3.6.2.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php new file mode 100644 index 0000000..b52b964 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php @@ -0,0 +1,137 @@ + + */ +class Swift_Mime_Headers_OpenDKIMHeader implements Swift_Mime_Header +{ + /** + * The value of this Header. + * + * @var string + */ + private $_value; + + /** + * The name of this Header. + * + * @var string + */ + private $_fieldName; + + /** + * Creates a new SimpleHeader with $name. + * + * @param string $name + * @param Swift_Mime_HeaderEncoder $encoder + * @param Swift_Mime_Grammar $grammar + */ + public function __construct($name) + { + $this->_fieldName = $name; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + return $this->_value; + } + + /** + * Get this Header rendered as a RFC 2822 compliant string. + * + * @return string + */ + public function toString() + { + return $this->_fieldName.': '.$this->_value; + } + + /** + * Set the Header FieldName. + * + * @see Swift_Mime_Header::getFieldName() + */ + public function getFieldName() + { + return $this->_fieldName; + } + + /** + * Ignored. + */ + public function setCharset($charset) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php new file mode 100644 index 0000000..0dcf1fc --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php @@ -0,0 +1,260 @@ +_paramEncoder = $paramEncoder; + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PARAMETERIZED; + } + + /** + * Set the character set used in this Header. + * + * @param string $charset + */ + public function setCharset($charset) + { + parent::setCharset($charset); + if (isset($this->_paramEncoder)) { + $this->_paramEncoder->charsetChanged($charset); + } + } + + /** + * Set the value of $parameter. + * + * @param string $parameter + * @param string $value + */ + public function setParameter($parameter, $value) + { + $this->setParameters(array_merge($this->getParameters(), array($parameter => $value))); + } + + /** + * Get the value of $parameter. + * + * @param string $parameter + * + * @return string + */ + public function getParameter($parameter) + { + $params = $this->getParameters(); + + return array_key_exists($parameter, $params) + ? $params[$parameter] + : null; + } + + /** + * Set an associative array of parameter names mapped to values. + * + * @param string[] $parameters + */ + public function setParameters(array $parameters) + { + $this->clearCachedValueIf($this->_params != $parameters); + $this->_params = $parameters; + } + + /** + * Returns an associative array of parameter names mapped to values. + * + * @return string[] + */ + public function getParameters() + { + return $this->_params; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() //TODO: Check caching here + { + $body = parent::getFieldBody(); + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the parameter + $body .= '; '.$this->_createParameter($name, $value); + } + } + + return $body; + } + + /** + * Generate a list of all tokens in the final header. + * + * This doesn't need to be overridden in theory, but it is for implementation + * reasons to prevent potential breakage of attributes. + * + * @param string $string The string to tokenize + * + * @return array An array of tokens as strings + */ + protected function toTokens($string = null) + { + $tokens = parent::toTokens(parent::getFieldBody()); + + // Try creating any parameters + foreach ($this->_params as $name => $value) { + if (!is_null($value)) { + // Add the semi-colon separator + $tokens[count($tokens) - 1] .= ';'; + $tokens = array_merge($tokens, $this->generateTokenLines( + ' '.$this->_createParameter($name, $value) + )); + } + } + + return $tokens; + } + + /** + * Render a RFC 2047 compliant header parameter from the $name and $value. + * + * @param string $name + * @param string $value + * + * @return string + */ + private function _createParameter($name, $value) + { + $origValue = $value; + + $encoded = false; + // Allow room for parameter name, indices, "=" and DQUOTEs + $maxValueLength = $this->getMaxLineLength() - strlen($name.'=*N"";') - 1; + $firstLineOffset = 0; + + // If it's not already a valid parameter value... + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + // TODO: text, or something else?? + // ... and it's not ascii + if (!preg_match('/^'.$this->getGrammar()->getDefinition('text').'*$/D', $value)) { + $encoded = true; + // Allow space for the indices, charset and language + $maxValueLength = $this->getMaxLineLength() - strlen($name.'*N*="";') - 1; + $firstLineOffset = strlen( + $this->getCharset()."'".$this->getLanguage()."'" + ); + } + } + + // Encode if we need to + if ($encoded || strlen($value) > $maxValueLength) { + if (isset($this->_paramEncoder)) { + $value = $this->_paramEncoder->encodeString( + $origValue, $firstLineOffset, $maxValueLength, $this->getCharset() + ); + } else { + // We have to go against RFC 2183/2231 in some areas for interoperability + $value = $this->getTokenAsEncodedWord($origValue); + $encoded = false; + } + } + + $valueLines = isset($this->_paramEncoder) ? explode("\r\n", $value) : array($value); + + // Need to add indices + if (count($valueLines) > 1) { + $paramLines = array(); + foreach ($valueLines as $i => $line) { + $paramLines[] = $name.'*'.$i. + $this->_getEndOfParameterValue($line, true, $i == 0); + } + + return implode(";\r\n ", $paramLines); + } else { + return $name.$this->_getEndOfParameterValue( + $valueLines[0], $encoded, true + ); + } + } + + /** + * Returns the parameter value from the "=" and beyond. + * + * @param string $value to append + * @param bool $encoded + * @param bool $firstLine + * + * @return string + */ + private function _getEndOfParameterValue($value, $encoded = false, $firstLine = false) + { + if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) { + $value = '"'.$value.'"'; + } + $prepend = '='; + if ($encoded) { + $prepend = '*='; + if ($firstLine) { + $prepend = '*='.$this->getCharset()."'".$this->getLanguage(). + "'"; + } + } + + return $prepend.$value; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php new file mode 100644 index 0000000..2fffc7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php @@ -0,0 +1,143 @@ +setFieldName($name); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_PATH; + } + + /** + * Set the model for the field body. + * This method takes a string for an address. + * + * @param string $model + * + * @throws Swift_RfcComplianceException + */ + public function setFieldBodyModel($model) + { + $this->setAddress($model); + } + + /** + * Get the model for the field body. + * This method returns a string email address. + * + * @return mixed + */ + public function getFieldBodyModel() + { + return $this->getAddress(); + } + + /** + * Set the Address which should appear in this Header. + * + * @param string $address + * + * @throws Swift_RfcComplianceException + */ + public function setAddress($address) + { + if (is_null($address)) { + $this->_address = null; + } elseif ('' == $address) { + $this->_address = ''; + } else { + $this->_assertValidAddress($address); + $this->_address = $address; + } + $this->setCachedValue(null); + } + + /** + * Get the address which is used in this Header (if any). + * + * Null is returned if no address is set. + * + * @return string + */ + public function getAddress() + { + return $this->_address; + } + + /** + * Get the string value of the body in this Header. + * + * This is not necessarily RFC 2822 compliant since folding white space will + * not be added at this stage (see {@link toString()} for that). + * + * @see toString() + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + if (isset($this->_address)) { + $this->setCachedValue('<'.$this->_address.'>'); + } + } + + return $this->getCachedValue(); + } + + /** + * Throws an Exception if the address passed does not comply with RFC 2822. + * + * @param string $address + * + * @throws Swift_RfcComplianceException If address is invalid + */ + private function _assertValidAddress($address) + { + if (!preg_match('/^'.$this->getGrammar()->getDefinition('addr-spec').'$/D', + $address)) { + throw new Swift_RfcComplianceException( + 'Address set in PathHeader does not comply with addr-spec of RFC 2822.' + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php new file mode 100644 index 0000000..86177f1 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php @@ -0,0 +1,112 @@ +setFieldName($name); + $this->setEncoder($encoder); + parent::__construct($grammar); + } + + /** + * Get the type of Header that this instance represents. + * + * @see TYPE_TEXT, TYPE_PARAMETERIZED, TYPE_MAILBOX + * @see TYPE_DATE, TYPE_ID, TYPE_PATH + * + * @return int + */ + public function getFieldType() + { + return self::TYPE_TEXT; + } + + /** + * Set the model for the field body. + * + * This method takes a string for the field value. + * + * @param string $model + */ + public function setFieldBodyModel($model) + { + $this->setValue($model); + } + + /** + * Get the model for the field body. + * + * This method returns a string. + * + * @return string + */ + public function getFieldBodyModel() + { + return $this->getValue(); + } + + /** + * Get the (unencoded) value of this header. + * + * @return string + */ + public function getValue() + { + return $this->_value; + } + + /** + * Set the (unencoded) value of this header. + * + * @param string $value + */ + public function setValue($value) + { + $this->clearCachedValueIf($this->_value != $value); + $this->_value = $value; + } + + /** + * Get the value of this header prepared for rendering. + * + * @return string + */ + public function getFieldBody() + { + if (!$this->getCachedValue()) { + $this->setCachedValue( + $this->encodeWords($this, $this->_value) + ); + } + + return $this->getCachedValue(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php new file mode 100644 index 0000000..9b36d21 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php @@ -0,0 +1,223 @@ + 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $address + * @param string $name optional + */ + public function setSender($address, $name = null); + + /** + * Get the sender address for this message. + * + * This has a higher significance than the From address. + * + * @return string + */ + public function getSender(); + + /** + * Set the From address of this message. + * + * It is permissible for multiple From addresses to be set using an array. + * + * If multiple From addresses are used, you SHOULD set the Sender address and + * according to RFC 2822, MUST set the sender address. + * + * An array can be used if display names are to be provided: i.e. + * array('email@address.com' => 'Real Name'). + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setFrom($addresses, $name = null); + + /** + * Get the From address(es) of this message. + * + * This method always returns an associative array where the keys are the + * addresses. + * + * @return string[] + */ + public function getFrom(); + + /** + * Set the Reply-To address(es). + * + * Any replies from the receiver will be sent to this address. + * + * It is permissible for multiple reply-to addresses to be set using an array. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setReplyTo($addresses, $name = null); + + /** + * Get the Reply-To addresses for this message. + * + * This method always returns an associative array where the keys provide the + * email addresses. + * + * @return string[] + */ + public function getReplyTo(); + + /** + * Set the To address(es). + * + * Recipients set in this field will receive a copy of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setCc()}. + * + * If the second parameter is provided and the first is a string, then $name + * is associated with the address. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setTo($addresses, $name = null); + + /** + * Get the To addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getTo(); + + /** + * Set the Cc address(es). + * + * Recipients set in this field will receive a 'carbon-copy' of this message. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setCc($addresses, $name = null); + + /** + * Get the Cc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getCc(); + + /** + * Set the Bcc address(es). + * + * Recipients set in this field will receive a 'blind-carbon-copy' of this + * message. + * + * In other words, they will get the message, but any other recipients of the + * message will have no such knowledge of their receipt of it. + * + * This method has the same synopsis as {@link setFrom()} and {@link setTo()}. + * + * @param mixed $addresses + * @param string $name optional + */ + public function setBcc($addresses, $name = null); + + /** + * Get the Bcc addresses for this message. + * + * This method always returns an associative array, whereby the keys provide + * the actual email addresses. + * + * @return string[] + */ + public function getBcc(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php new file mode 100644 index 0000000..30f460c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php @@ -0,0 +1,117 @@ +setContentType('text/plain'); + if (!is_null($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * @param string $charset optional + * + * @return Swift_Mime_MimePart + */ + public function setBody($body, $contentType = null, $charset = null) + { + if (isset($charset)) { + $this->setCharset($charset); + } + $body = $this->_convertString($body); + + parent::setBody($body, $contentType); + + return $this; + } + + /** + * Get the character set of this entity. + * + * @return string + */ + public function getCharset() + { + return $this->_getHeaderParameter('Content-Type', 'charset'); + } + + /** + * Set the character set of this entity. + * + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public function setCharset($charset) + { + $this->_setHeaderParameter('Content-Type', 'charset', $charset); + if ($charset !== $this->_userCharset) { + $this->_clearCache(); + } + $this->_userCharset = $charset; + parent::charsetChanged($charset); + + return $this; + } + + /** + * Get the format of this entity (i.e. flowed or fixed). + * + * @return string + */ + public function getFormat() + { + return $this->_getHeaderParameter('Content-Type', 'format'); + } + + /** + * Set the format of this entity (flowed or fixed). + * + * @param string $format + * + * @return Swift_Mime_MimePart + */ + public function setFormat($format) + { + $this->_setHeaderParameter('Content-Type', 'format', $format); + $this->_userFormat = $format; + + return $this; + } + + /** + * Test if delsp is being used for this entity. + * + * @return bool + */ + public function getDelSp() + { + return ($this->_getHeaderParameter('Content-Type', 'delsp') == 'yes') + ? true + : false; + } + + /** + * Turn delsp on or off for this entity. + * + * @param bool $delsp + * + * @return Swift_Mime_MimePart + */ + public function setDelSp($delsp = true) + { + $this->_setHeaderParameter('Content-Type', 'delsp', $delsp ? 'yes' : null); + $this->_userDelSp = $delsp; + + return $this; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_ALTERNATIVE, LEVEL_MIXED, LEVEL_RELATED + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Receive notification that the charset has changed on this document, or a + * parent document. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** Fix the content-type and encoding of this entity */ + protected function _fixHeaders() + { + parent::_fixHeaders(); + if (count($this->getChildren())) { + $this->_setHeaderParameter('Content-Type', 'charset', null); + $this->_setHeaderParameter('Content-Type', 'format', null); + $this->_setHeaderParameter('Content-Type', 'delsp', null); + } else { + $this->setCharset($this->_userCharset); + $this->setFormat($this->_userFormat); + $this->setDelSp($this->_userDelSp); + } + } + + /** Set the nesting level of this entity */ + protected function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + /** Encode charset when charset is not utf-8 */ + protected function _convertString($string) + { + $charset = strtolower($this->getCharset()); + if (!in_array($charset, array('utf-8', 'iso-8859-1', ''))) { + // mb_convert_encoding must be the first one to check, since iconv cannot convert some words. + if (function_exists('mb_convert_encoding')) { + $string = mb_convert_encoding($string, $charset, 'utf-8'); + } elseif (function_exists('iconv')) { + $string = iconv('utf-8//TRANSLIT//IGNORE', $charset, $string); + } else { + throw new Swift_SwiftException('No suitable convert encoding function (use UTF-8 as your charset or install the mbstring or iconv extension).'); + } + + return $string; + } + + return $string; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php new file mode 100644 index 0000000..e15c6ef --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php @@ -0,0 +1,34 @@ +_encoder = $encoder; + $this->_paramEncoder = $paramEncoder; + $this->_grammar = $grammar; + $this->_charset = $charset; + } + + /** + * Create a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string|null $addresses + * + * @return Swift_Mime_Header + */ + public function createMailboxHeader($name, $addresses = null) + { + $header = new Swift_Mime_Headers_MailboxHeader($name, $this->_encoder, $this->_grammar); + if (isset($addresses)) { + $header->setFieldBodyModel($addresses); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int|null $timestamp + * + * @return Swift_Mime_Header + */ + public function createDateHeader($name, $timestamp = null) + { + $header = new Swift_Mime_Headers_DateHeader($name, $this->_grammar); + if (isset($timestamp)) { + $header->setFieldBodyModel($timestamp); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + * + * @return Swift_Mime_Header + */ + public function createTextHeader($name, $value = null) + { + $header = new Swift_Mime_Headers_UnstructuredHeader($name, $this->_encoder, $this->_grammar); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + * + * @return Swift_Mime_ParameterizedHeader + */ + public function createParameterizedHeader($name, $value = null, + $params = array()) + { + $header = new Swift_Mime_Headers_ParameterizedHeader($name, + $this->_encoder, (strtolower($name) == 'content-disposition') + ? $this->_paramEncoder + : null, + $this->_grammar + ); + if (isset($value)) { + $header->setFieldBodyModel($value); + } + foreach ($params as $k => $v) { + $header->setParameter($k, $v); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + * + * @return Swift_Mime_Header + */ + public function createIdHeader($name, $ids = null) + { + $header = new Swift_Mime_Headers_IdentificationHeader($name, $this->_grammar); + if (isset($ids)) { + $header->setFieldBodyModel($ids); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Create a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + * + * @return Swift_Mime_Header + */ + public function createPathHeader($name, $path = null) + { + $header = new Swift_Mime_Headers_PathHeader($name, $this->_grammar); + if (isset($path)) { + $header->setFieldBodyModel($path); + } + $this->_setHeaderCharset($header); + + return $header; + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_charset = $charset; + $this->_encoder->charsetChanged($charset); + $this->_paramEncoder->charsetChanged($charset); + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_encoder = clone $this->_encoder; + $this->_paramEncoder = clone $this->_paramEncoder; + } + + /** Apply the charset to the Header */ + private function _setHeaderCharset(Swift_Mime_Header $header) + { + if (isset($this->_charset)) { + $header->setCharset($this->_charset); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php new file mode 100644 index 0000000..0776240 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php @@ -0,0 +1,396 @@ +_factory = $factory; + if (isset($charset)) { + $this->setCharset($charset); + } + } + + /** + * Set the charset used by these headers. + * + * @param string $charset + */ + public function setCharset($charset) + { + $this->_charset = $charset; + $this->_factory->charsetChanged($charset); + $this->_notifyHeadersOfCharset($charset); + } + + /** + * Add a new Mailbox Header with a list of $addresses. + * + * @param string $name + * @param array|string $addresses + */ + public function addMailboxHeader($name, $addresses = null) + { + $this->_storeHeader($name, + $this->_factory->createMailboxHeader($name, $addresses)); + } + + /** + * Add a new Date header using $timestamp (UNIX time). + * + * @param string $name + * @param int $timestamp + */ + public function addDateHeader($name, $timestamp = null) + { + $this->_storeHeader($name, + $this->_factory->createDateHeader($name, $timestamp)); + } + + /** + * Add a new basic text header with $name and $value. + * + * @param string $name + * @param string $value + */ + public function addTextHeader($name, $value = null) + { + $this->_storeHeader($name, + $this->_factory->createTextHeader($name, $value)); + } + + /** + * Add a new ParameterizedHeader with $name, $value and $params. + * + * @param string $name + * @param string $value + * @param array $params + */ + public function addParameterizedHeader($name, $value = null, $params = array()) + { + $this->_storeHeader($name, $this->_factory->createParameterizedHeader($name, $value, $params)); + } + + /** + * Add a new ID header for Message-ID or Content-ID. + * + * @param string $name + * @param string|array $ids + */ + public function addIdHeader($name, $ids = null) + { + $this->_storeHeader($name, $this->_factory->createIdHeader($name, $ids)); + } + + /** + * Add a new Path header with an address (path) in it. + * + * @param string $name + * @param string $path + */ + public function addPathHeader($name, $path = null) + { + $this->_storeHeader($name, $this->_factory->createPathHeader($name, $path)); + } + + /** + * Returns true if at least one header with the given $name exists. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + * + * @return bool + */ + public function has($name, $index = 0) + { + $lowerName = strtolower($name); + + return array_key_exists($lowerName, $this->_headers) && array_key_exists($index, $this->_headers[$lowerName]); + } + + /** + * Set a header in the HeaderSet. + * + * The header may be a previously fetched header via {@link get()} or it may + * be one that has been created separately. + * + * If $index is specified, the header will be inserted into the set at this + * offset. + * + * @param Swift_Mime_Header $header + * @param int $index + */ + public function set(Swift_Mime_Header $header, $index = 0) + { + $this->_storeHeader($header->getFieldName(), $header, $index); + } + + /** + * Get the header with the given $name. + * + * If multiple headers match, the actual one may be specified by $index. + * Returns NULL if none present. + * + * @param string $name + * @param int $index + * + * @return Swift_Mime_Header + */ + public function get($name, $index = 0) + { + if ($this->has($name, $index)) { + $lowerName = strtolower($name); + + return $this->_headers[$lowerName][$index]; + } + } + + /** + * Get all headers with the given $name. + * + * @param string $name + * + * @return array + */ + public function getAll($name = null) + { + if (!isset($name)) { + $headers = array(); + foreach ($this->_headers as $collection) { + $headers = array_merge($headers, $collection); + } + + return $headers; + } + + $lowerName = strtolower($name); + if (!array_key_exists($lowerName, $this->_headers)) { + return array(); + } + + return $this->_headers[$lowerName]; + } + + /** + * Return the name of all Headers. + * + * @return array + */ + public function listAll() + { + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + + return array_keys($headers); + } + + /** + * Remove the header with the given $name if it's set. + * + * If multiple headers match, the actual one may be specified by $index. + * + * @param string $name + * @param int $index + */ + public function remove($name, $index = 0) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName][$index]); + } + + /** + * Remove all headers with the given $name. + * + * @param string $name + */ + public function removeAll($name) + { + $lowerName = strtolower($name); + unset($this->_headers[$lowerName]); + } + + /** + * Create a new instance of this HeaderSet. + * + * @return Swift_Mime_HeaderSet + */ + public function newInstance() + { + return new self($this->_factory); + } + + /** + * Define a list of Header names as an array in the correct order. + * + * These Headers will be output in the given order where present. + * + * @param array $sequence + */ + public function defineOrdering(array $sequence) + { + $this->_order = array_flip(array_map('strtolower', $sequence)); + } + + /** + * Set a list of header names which must always be displayed when set. + * + * Usually headers without a field value won't be output unless set here. + * + * @param array $names + */ + public function setAlwaysDisplayed(array $names) + { + $this->_required = array_flip(array_map('strtolower', $names)); + } + + /** + * Notify this observer that the entity's charset has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->setCharset($charset); + } + + /** + * Returns a string with a representation of all headers. + * + * @return string + */ + public function toString() + { + $string = ''; + $headers = $this->_headers; + if ($this->_canSort()) { + uksort($headers, array($this, '_sortHeaders')); + } + foreach ($headers as $collection) { + foreach ($collection as $header) { + if ($this->_isDisplayed($header) || $header->getFieldBody() != '') { + $string .= $header->toString(); + } + } + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @return string + * + * @see toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** Save a Header to the internal collection */ + private function _storeHeader($name, Swift_Mime_Header $header, $offset = null) + { + if (!isset($this->_headers[strtolower($name)])) { + $this->_headers[strtolower($name)] = array(); + } + if (!isset($offset)) { + $this->_headers[strtolower($name)][] = $header; + } else { + $this->_headers[strtolower($name)][$offset] = $header; + } + } + + /** Test if the headers can be sorted */ + private function _canSort() + { + return count($this->_order) > 0; + } + + /** uksort() algorithm for Header ordering */ + private function _sortHeaders($a, $b) + { + $lowerA = strtolower($a); + $lowerB = strtolower($b); + $aPos = array_key_exists($lowerA, $this->_order) + ? $this->_order[$lowerA] + : -1; + $bPos = array_key_exists($lowerB, $this->_order) + ? $this->_order[$lowerB] + : -1; + + if ($aPos == -1) { + return 1; + } elseif ($bPos == -1) { + return -1; + } + + return ($aPos < $bPos) ? -1 : 1; + } + + /** Test if the given Header is always displayed */ + private function _isDisplayed(Swift_Mime_Header $header) + { + return array_key_exists(strtolower($header->getFieldName()), $this->_required); + } + + /** Notify all Headers of the new charset */ + private function _notifyHeadersOfCharset($charset) + { + foreach ($this->_headers as $headerGroup) { + foreach ($headerGroup as $header) { + $header->setCharset($charset); + } + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_factory = clone $this->_factory; + foreach ($this->_headers as $groupKey => $headerGroup) { + foreach ($headerGroup as $key => $header) { + $this->_headers[$groupKey][$key] = clone $header; + } + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php new file mode 100644 index 0000000..72b6ad9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php @@ -0,0 +1,649 @@ +getHeaders()->defineOrdering(array( + 'Return-Path', + 'Received', + 'DKIM-Signature', + 'DomainKey-Signature', + 'Sender', + 'Message-ID', + 'Date', + 'Subject', + 'From', + 'Reply-To', + 'To', + 'Cc', + 'Bcc', + 'MIME-Version', + 'Content-Type', + 'Content-Transfer-Encoding', + )); + $this->getHeaders()->setAlwaysDisplayed(array('Date', 'Message-ID', 'From')); + $this->getHeaders()->addTextHeader('MIME-Version', '1.0'); + $this->setDate(time()); + $this->setId($this->getId()); + $this->getHeaders()->addMailboxHeader('From'); + } + + /** + * Always returns {@link LEVEL_TOP} for a message instance. + * + * @return int + */ + public function getNestingLevel() + { + return self::LEVEL_TOP; + } + + /** + * Set the subject of this message. + * + * @param string $subject + * + * @return Swift_Mime_SimpleMessage + */ + public function setSubject($subject) + { + if (!$this->_setHeaderFieldModel('Subject', $subject)) { + $this->getHeaders()->addTextHeader('Subject', $subject); + } + + return $this; + } + + /** + * Get the subject of this message. + * + * @return string + */ + public function getSubject() + { + return $this->_getHeaderFieldModel('Subject'); + } + + /** + * Set the date at which this message was created. + * + * @param int $date + * + * @return Swift_Mime_SimpleMessage + */ + public function setDate($date) + { + if (!$this->_setHeaderFieldModel('Date', $date)) { + $this->getHeaders()->addDateHeader('Date', $date); + } + + return $this; + } + + /** + * Get the date at which this message was created. + * + * @return int + */ + public function getDate() + { + return $this->_getHeaderFieldModel('Date'); + } + + /** + * Set the return-path (the bounce address) of this message. + * + * @param string $address + * + * @return Swift_Mime_SimpleMessage + */ + public function setReturnPath($address) + { + if (!$this->_setHeaderFieldModel('Return-Path', $address)) { + $this->getHeaders()->addPathHeader('Return-Path', $address); + } + + return $this; + } + + /** + * Get the return-path (bounce address) of this message. + * + * @return string + */ + public function getReturnPath() + { + return $this->_getHeaderFieldModel('Return-Path'); + } + + /** + * Set the sender of this message. + * + * This does not override the From field, but it has a higher significance. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setSender($address, $name = null) + { + if (!is_array($address) && isset($name)) { + $address = array($address => $name); + } + + if (!$this->_setHeaderFieldModel('Sender', (array) $address)) { + $this->getHeaders()->addMailboxHeader('Sender', (array) $address); + } + + return $this; + } + + /** + * Get the sender of this message. + * + * @return string + */ + public function getSender() + { + return $this->_getHeaderFieldModel('Sender'); + } + + /** + * Add a From: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addFrom($address, $name = null) + { + $current = $this->getFrom(); + $current[$address] = $name; + + return $this->setFrom($current); + } + + /** + * Set the from address of this message. + * + * You may pass an array of addresses if this message is from multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param string|array $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setFrom($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('From', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('From', (array) $addresses); + } + + return $this; + } + + /** + * Get the from address of this message. + * + * @return mixed + */ + public function getFrom() + { + return $this->_getHeaderFieldModel('From'); + } + + /** + * Add a Reply-To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addReplyTo($address, $name = null) + { + $current = $this->getReplyTo(); + $current[$address] = $name; + + return $this->setReplyTo($current); + } + + /** + * Set the reply-to address of this message. + * + * You may pass an array of addresses if replies will go to multiple people. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param string $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setReplyTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Reply-To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Reply-To', (array) $addresses); + } + + return $this; + } + + /** + * Get the reply-to address of this message. + * + * @return string + */ + public function getReplyTo() + { + return $this->_getHeaderFieldModel('Reply-To'); + } + + /** + * Add a To: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addTo($address, $name = null) + { + $current = $this->getTo(); + $current[$address] = $name; + + return $this->setTo($current); + } + + /** + * Set the to addresses of this message. + * + * If multiple recipients will receive the message an array should be used. + * Example: array('receiver@domain.org', 'other@domain.org' => 'A name') + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setTo($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('To', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('To', (array) $addresses); + } + + return $this; + } + + /** + * Get the To addresses of this message. + * + * @return array + */ + public function getTo() + { + return $this->_getHeaderFieldModel('To'); + } + + /** + * Add a Cc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addCc($address, $name = null) + { + $current = $this->getCc(); + $current[$address] = $name; + + return $this->setCc($current); + } + + /** + * Set the Cc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setCc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Cc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Cc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Cc address of this message. + * + * @return array + */ + public function getCc() + { + return $this->_getHeaderFieldModel('Cc'); + } + + /** + * Add a Bcc: address to this message. + * + * If $name is passed this name will be associated with the address. + * + * @param string $address + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function addBcc($address, $name = null) + { + $current = $this->getBcc(); + $current[$address] = $name; + + return $this->setBcc($current); + } + + /** + * Set the Bcc addresses of this message. + * + * If $name is passed and the first parameter is a string, this name will be + * associated with the address. + * + * @param mixed $addresses + * @param string $name optional + * + * @return Swift_Mime_SimpleMessage + */ + public function setBcc($addresses, $name = null) + { + if (!is_array($addresses) && isset($name)) { + $addresses = array($addresses => $name); + } + + if (!$this->_setHeaderFieldModel('Bcc', (array) $addresses)) { + $this->getHeaders()->addMailboxHeader('Bcc', (array) $addresses); + } + + return $this; + } + + /** + * Get the Bcc addresses of this message. + * + * @return array + */ + public function getBcc() + { + return $this->_getHeaderFieldModel('Bcc'); + } + + /** + * Set the priority of this message. + * + * The value is an integer where 1 is the highest priority and 5 is the lowest. + * + * @param int $priority + * + * @return Swift_Mime_SimpleMessage + */ + public function setPriority($priority) + { + $priorityMap = array( + 1 => 'Highest', + 2 => 'High', + 3 => 'Normal', + 4 => 'Low', + 5 => 'Lowest', + ); + $pMapKeys = array_keys($priorityMap); + if ($priority > max($pMapKeys)) { + $priority = max($pMapKeys); + } elseif ($priority < min($pMapKeys)) { + $priority = min($pMapKeys); + } + if (!$this->_setHeaderFieldModel('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority]))) { + $this->getHeaders()->addTextHeader('X-Priority', + sprintf('%d (%s)', $priority, $priorityMap[$priority])); + } + + return $this; + } + + /** + * Get the priority of this message. + * + * The returned value is an integer where 1 is the highest priority and 5 + * is the lowest. + * + * @return int + */ + public function getPriority() + { + list($priority) = sscanf($this->_getHeaderFieldModel('X-Priority'), + '%[1-5]' + ); + + return isset($priority) ? $priority : 3; + } + + /** + * Ask for a delivery receipt from the recipient to be sent to $addresses. + * + * @param array $addresses + * + * @return Swift_Mime_SimpleMessage + */ + public function setReadReceiptTo($addresses) + { + if (!$this->_setHeaderFieldModel('Disposition-Notification-To', $addresses)) { + $this->getHeaders() + ->addMailboxHeader('Disposition-Notification-To', $addresses); + } + + return $this; + } + + /** + * Get the addresses to which a read-receipt will be sent. + * + * @return string + */ + public function getReadReceiptTo() + { + return $this->_getHeaderFieldModel('Disposition-Notification-To'); + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} such as an Attachment or MimePart. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return Swift_Mime_SimpleMessage + */ + public function attach(Swift_Mime_MimeEntity $entity) + { + $this->setChildren(array_merge($this->getChildren(), array($entity))); + + return $this; + } + + /** + * Remove an already attached entity. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return Swift_Mime_SimpleMessage + */ + public function detach(Swift_Mime_MimeEntity $entity) + { + $newChildren = array(); + foreach ($this->getChildren() as $child) { + if ($entity !== $child) { + $newChildren[] = $child; + } + } + $this->setChildren($newChildren); + + return $this; + } + + /** + * Attach a {@link Swift_Mime_MimeEntity} and return it's CID source. + * This method should be used when embedding images or other data in a message. + * + * @param Swift_Mime_MimeEntity $entity + * + * @return string + */ + public function embed(Swift_Mime_MimeEntity $entity) + { + $this->attach($entity); + + return 'cid:'.$entity->getId(); + } + + /** + * Get this message as a complete string. + * + * @return string + */ + public function toString() + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + $string = parent::toString(); + $this->setChildren($children); + } else { + $string = parent::toString(); + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this message to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream $is + */ + public function toByteStream(Swift_InputByteStream $is) + { + if (count($children = $this->getChildren()) > 0 && $this->getBody() != '') { + $this->setChildren(array_merge(array($this->_becomeMimePart()), $children)); + parent::toByteStream($is); + $this->setChildren($children); + } else { + parent::toByteStream($is); + } + } + + /** @see Swift_Mime_SimpleMimeEntity::_getIdField() */ + protected function _getIdField() + { + return 'Message-ID'; + } + + /** Turn the body of this message into a child of itself if needed */ + protected function _becomeMimePart() + { + $part = new parent($this->getHeaders()->newInstance(), $this->getEncoder(), + $this->_getCache(), $this->_getGrammar(), $this->_userCharset + ); + $part->setContentType($this->_userContentType); + $part->setBody($this->getBody()); + $part->setFormat($this->_userFormat); + $part->setDelSp($this->_userDelSp); + $part->_setNestingLevel($this->_getTopNestingLevel()); + + return $part; + } + + /** Get the highest nesting level nested inside this message */ + private function _getTopNestingLevel() + { + $highestLevel = $this->getNestingLevel(); + foreach ($this->getChildren() as $child) { + $childLevel = $child->getNestingLevel(); + if ($highestLevel < $childLevel) { + $highestLevel = $childLevel; + } + } + + return $highestLevel; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php new file mode 100644 index 0000000..8e14ba8 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php @@ -0,0 +1,867 @@ + array(self::LEVEL_TOP, self::LEVEL_MIXED), + 'multipart/alternative' => array(self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE), + 'multipart/related' => array(self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED), + ); + + /** A set of filter rules to define what level an entity should be nested at */ + private $_compoundLevelFilters = array(); + + /** The nesting level of this entity */ + private $_nestingLevel = self::LEVEL_ALTERNATIVE; + + /** A KeyCache instance used during encoding and streaming */ + private $_cache; + + /** Direct descendants of this entity */ + private $_immediateChildren = array(); + + /** All descendants of this entity */ + private $_children = array(); + + /** The maximum line length of the body of this entity */ + private $_maxLineLength = 78; + + /** The order in which alternative mime types should appear */ + private $_alternativePartOrder = array( + 'text/plain' => 1, + 'text/html' => 2, + 'multipart/related' => 3, + ); + + /** The CID of this entity */ + private $_id; + + /** The key used for accessing the cache */ + private $_cacheKey; + + protected $_userContentType; + + /** + * Create a new SimpleMimeEntity with $headers, $encoder and $cache. + * + * @param Swift_Mime_HeaderSet $headers + * @param Swift_Mime_ContentEncoder $encoder + * @param Swift_KeyCache $cache + * @param Swift_Mime_Grammar $grammar + */ + public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_Mime_Grammar $grammar) + { + $this->_cacheKey = md5(uniqid(getmypid().mt_rand(), true)); + $this->_cache = $cache; + $this->_headers = $headers; + $this->_grammar = $grammar; + $this->setEncoder($encoder); + $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding')); + + // This array specifies that, when the entire MIME document contains + // $compoundLevel, then for each child within $level, if its Content-Type + // is $contentType then it should be treated as if it's level is + // $neededLevel instead. I tried to write that unambiguously! :-\ + // Data Structure: + // array ( + // $compoundLevel => array( + // $level => array( + // $contentType => $neededLevel + // ) + // ) + // ) + + $this->_compoundLevelFilters = array( + (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array( + self::LEVEL_ALTERNATIVE => array( + 'text/plain' => self::LEVEL_ALTERNATIVE, + 'text/html' => self::LEVEL_RELATED, + ), + ), + ); + + $this->_id = $this->getRandomId(); + } + + /** + * Generate a new Content-ID or Message-ID for this MIME entity. + * + * @return string + */ + public function generateId() + { + $this->setId($this->getRandomId()); + + return $this->_id; + } + + /** + * Get the {@link Swift_Mime_HeaderSet} for this entity. + * + * @return Swift_Mime_HeaderSet + */ + public function getHeaders() + { + return $this->_headers; + } + + /** + * Get the nesting level of this entity. + * + * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE + * + * @return int + */ + public function getNestingLevel() + { + return $this->_nestingLevel; + } + + /** + * Get the Content-type of this entity. + * + * @return string + */ + public function getContentType() + { + return $this->_getHeaderFieldModel('Content-Type'); + } + + /** + * Set the Content-type of this entity. + * + * @param string $type + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setContentType($type) + { + $this->_setContentTypeInHeaders($type); + // Keep track of the value so that if the content-type changes automatically + // due to added child entities, it can be restored if they are later removed + $this->_userContentType = $type; + + return $this; + } + + /** + * Get the CID of this entity. + * + * The CID will only be present in headers if a Content-ID header is present. + * + * @return string + */ + public function getId() + { + $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField()); + + return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id; + } + + /** + * Set the CID of this entity. + * + * @param string $id + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setId($id) + { + if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) { + $this->_headers->addIdHeader($this->_getIdField(), $id); + } + $this->_id = $id; + + return $this; + } + + /** + * Get the description of this entity. + * + * This value comes from the Content-Description header if set. + * + * @return string + */ + public function getDescription() + { + return $this->_getHeaderFieldModel('Content-Description'); + } + + /** + * Set the description of this entity. + * + * This method sets a value in the Content-ID header. + * + * @param string $description + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setDescription($description) + { + if (!$this->_setHeaderFieldModel('Content-Description', $description)) { + $this->_headers->addTextHeader('Content-Description', $description); + } + + return $this; + } + + /** + * Get the maximum line length of the body of this entity. + * + * @return int + */ + public function getMaxLineLength() + { + return $this->_maxLineLength; + } + + /** + * Set the maximum line length of lines in this body. + * + * Though not enforced by the library, lines should not exceed 1000 chars. + * + * @param int $length + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setMaxLineLength($length) + { + $this->_maxLineLength = $length; + + return $this; + } + + /** + * Get all children added to this entity. + * + * @return Swift_Mime_MimeEntity[] + */ + public function getChildren() + { + return $this->_children; + } + + /** + * Set all children of this entity. + * + * @param Swift_Mime_MimeEntity[] $children + * @param int $compoundLevel For internal use only + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setChildren(array $children, $compoundLevel = null) + { + // TODO: Try to refactor this logic + + $compoundLevel = isset($compoundLevel) + ? $compoundLevel + : $this->_getCompoundLevel($children) + ; + + $immediateChildren = array(); + $grandchildren = array(); + $newContentType = $this->_userContentType; + + foreach ($children as $child) { + $level = $this->_getNeededChildLevel($child, $compoundLevel); + if (empty($immediateChildren)) { + //first iteration + $immediateChildren = array($child); + } else { + $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + if ($nextLevel == $level) { + $immediateChildren[] = $child; + } elseif ($level < $nextLevel) { + // Re-assign immediateChildren to grandchildren + $grandchildren = array_merge($grandchildren, $immediateChildren); + // Set new children + $immediateChildren = array($child); + } else { + $grandchildren[] = $child; + } + } + } + + if (!empty($immediateChildren)) { + $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel); + + // Determine which composite media type is needed to accommodate the + // immediate children + foreach ($this->_compositeRanges as $mediaType => $range) { + if ($lowestLevel > $range[0] + && $lowestLevel <= $range[1]) { + $newContentType = $mediaType; + break; + } + } + + // Put any grandchildren in a subpart + if (!empty($grandchildren)) { + $subentity = $this->_createChild(); + $subentity->_setNestingLevel($lowestLevel); + $subentity->setChildren($grandchildren, $compoundLevel); + array_unshift($immediateChildren, $subentity); + } + } + + $this->_immediateChildren = $immediateChildren; + $this->_children = $children; + $this->_setContentTypeInHeaders($newContentType); + $this->_fixHeaders(); + $this->_sortChildren(); + + return $this; + } + + /** + * Get the body of this entity as a string. + * + * @return string + */ + public function getBody() + { + return ($this->_body instanceof Swift_OutputByteStream) + ? $this->_readStream($this->_body) + : $this->_body; + } + + /** + * Set the body of this entity, either as a string, or as an instance of + * {@link Swift_OutputByteStream}. + * + * @param mixed $body + * @param string $contentType optional + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setBody($body, $contentType = null) + { + if ($body !== $this->_body) { + $this->_clearCache(); + } + + $this->_body = $body; + if (isset($contentType)) { + $this->setContentType($contentType); + } + + return $this; + } + + /** + * Get the encoder used for the body of this entity. + * + * @return Swift_Mime_ContentEncoder + */ + public function getEncoder() + { + return $this->_encoder; + } + + /** + * Set the encoder used for the body of this entity. + * + * @param Swift_Mime_ContentEncoder $encoder + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setEncoder(Swift_Mime_ContentEncoder $encoder) + { + if ($encoder !== $this->_encoder) { + $this->_clearCache(); + } + + $this->_encoder = $encoder; + $this->_setEncoding($encoder->getName()); + $this->_notifyEncoderChanged($encoder); + + return $this; + } + + /** + * Get the boundary used to separate children in this entity. + * + * @return string + */ + public function getBoundary() + { + if (!isset($this->_boundary)) { + $this->_boundary = '_=_swift_v4_'.time().'_'.md5(getmypid().mt_rand().uniqid('', true)).'_=_'; + } + + return $this->_boundary; + } + + /** + * Set the boundary used to separate children in this entity. + * + * @param string $boundary + * + * @throws Swift_RfcComplianceException + * + * @return Swift_Mime_SimpleMimeEntity + */ + public function setBoundary($boundary) + { + $this->_assertValidBoundary($boundary); + $this->_boundary = $boundary; + + return $this; + } + + /** + * Receive notification that the charset of this entity, or a parent entity + * has changed. + * + * @param string $charset + */ + public function charsetChanged($charset) + { + $this->_notifyCharsetChanged($charset); + } + + /** + * Receive notification that the encoder of this entity or a parent entity + * has changed. + * + * @param Swift_Mime_ContentEncoder $encoder + */ + public function encoderChanged(Swift_Mime_ContentEncoder $encoder) + { + $this->_notifyEncoderChanged($encoder); + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + public function toString() + { + $string = $this->_headers->toString(); + $string .= $this->_bodyToString(); + + return $string; + } + + /** + * Get this entire entity as a string. + * + * @return string + */ + protected function _bodyToString() + { + $string = ''; + + if (isset($this->_body) && empty($this->_immediateChildren)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $body = $this->_cache->getString($this->_cacheKey, 'body'); + } else { + $body = "\r\n".$this->_encoder->encodeString($this->getBody(), 0, + $this->getMaxLineLength() + ); + $this->_cache->setString($this->_cacheKey, 'body', $body, + Swift_KeyCache::MODE_WRITE + ); + } + $string .= $body; + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $string .= "\r\n\r\n--".$this->getBoundary()."\r\n"; + $string .= $child->toString(); + } + $string .= "\r\n\r\n--".$this->getBoundary()."--\r\n"; + } + + return $string; + } + + /** + * Returns a string representation of this object. + * + * @see toString() + * + * @return string + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Write this entire entity to a {@see Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + public function toByteStream(Swift_InputByteStream $is) + { + $is->write($this->_headers->toString()); + $is->commit(); + + $this->_bodyToByteStream($is); + } + + /** + * Write this entire entity to a {@link Swift_InputByteStream}. + * + * @param Swift_InputByteStream + */ + protected function _bodyToByteStream(Swift_InputByteStream $is) + { + if (empty($this->_immediateChildren)) { + if (isset($this->_body)) { + if ($this->_cache->hasKey($this->_cacheKey, 'body')) { + $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is); + } else { + $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body'); + if ($cacheIs) { + $is->bind($cacheIs); + } + + $is->write("\r\n"); + + if ($this->_body instanceof Swift_OutputByteStream) { + $this->_body->setReadPointer(0); + + $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength()); + } else { + $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength())); + } + + if ($cacheIs) { + $is->unbind($cacheIs); + } + } + } + } + + if (!empty($this->_immediateChildren)) { + foreach ($this->_immediateChildren as $child) { + $is->write("\r\n\r\n--".$this->getBoundary()."\r\n"); + $child->toByteStream($is); + } + $is->write("\r\n\r\n--".$this->getBoundary()."--\r\n"); + } + } + + /** + * Get the name of the header that provides the ID of this entity. + */ + protected function _getIdField() + { + return 'Content-ID'; + } + + /** + * Get the model data (usually an array or a string) for $field. + */ + protected function _getHeaderFieldModel($field) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getFieldBodyModel(); + } + } + + /** + * Set the model data for $field. + */ + protected function _setHeaderFieldModel($field, $model) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setFieldBodyModel($model); + + return true; + } else { + return false; + } + } + + /** + * Get the parameter value of $parameter on $field header. + */ + protected function _getHeaderParameter($field, $parameter) + { + if ($this->_headers->has($field)) { + return $this->_headers->get($field)->getParameter($parameter); + } + } + + /** + * Set the parameter value of $parameter on $field header. + */ + protected function _setHeaderParameter($field, $parameter, $value) + { + if ($this->_headers->has($field)) { + $this->_headers->get($field)->setParameter($parameter, $value); + + return true; + } else { + return false; + } + } + + /** + * Re-evaluate what content type and encoding should be used on this entity. + */ + protected function _fixHeaders() + { + if (count($this->_immediateChildren)) { + $this->_setHeaderParameter('Content-Type', 'boundary', + $this->getBoundary() + ); + $this->_headers->remove('Content-Transfer-Encoding'); + } else { + $this->_setHeaderParameter('Content-Type', 'boundary', null); + $this->_setEncoding($this->_encoder->getName()); + } + } + + /** + * Get the KeyCache used in this entity. + * + * @return Swift_KeyCache + */ + protected function _getCache() + { + return $this->_cache; + } + + /** + * Get the grammar used for validation. + * + * @return Swift_Mime_Grammar + */ + protected function _getGrammar() + { + return $this->_grammar; + } + + /** + * Empty the KeyCache for this entity. + */ + protected function _clearCache() + { + $this->_cache->clearKey($this->_cacheKey, 'body'); + } + + /** + * Returns a random Content-ID or Message-ID. + * + * @return string + */ + protected function getRandomId() + { + $idLeft = md5(getmypid().'.'.time().'.'.uniqid(mt_rand(), true)); + $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated'; + $id = $idLeft.'@'.$idRight; + + try { + $this->_assertValidId($id); + } catch (Swift_RfcComplianceException $e) { + $id = $idLeft.'@swift.generated'; + } + + return $id; + } + + private function _readStream(Swift_OutputByteStream $os) + { + $string = ''; + while (false !== $bytes = $os->read(8192)) { + $string .= $bytes; + } + + $os->setReadPointer(0); + + return $string; + } + + private function _setEncoding($encoding) + { + if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) { + $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding); + } + } + + private function _assertValidBoundary($boundary) + { + if (!preg_match( + '/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', + $boundary)) { + throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.'); + } + } + + private function _setContentTypeInHeaders($type) + { + if (!$this->_setHeaderFieldModel('Content-Type', $type)) { + $this->_headers->addParameterizedHeader('Content-Type', $type); + } + } + + private function _setNestingLevel($level) + { + $this->_nestingLevel = $level; + } + + private function _getCompoundLevel($children) + { + $level = 0; + foreach ($children as $child) { + $level |= $child->getNestingLevel(); + } + + return $level; + } + + private function _getNeededChildLevel($child, $compoundLevel) + { + $filter = array(); + foreach ($this->_compoundLevelFilters as $bitmask => $rules) { + if (($compoundLevel & $bitmask) === $bitmask) { + $filter = $rules + $filter; + } + } + + $realLevel = $child->getNestingLevel(); + $lowercaseType = strtolower($child->getContentType()); + + if (isset($filter[$realLevel]) + && isset($filter[$realLevel][$lowercaseType])) { + return $filter[$realLevel][$lowercaseType]; + } else { + return $realLevel; + } + } + + private function _createChild() + { + return new self($this->_headers->newInstance(), + $this->_encoder, $this->_cache, $this->_grammar); + } + + private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder) + { + foreach ($this->_immediateChildren as $child) { + $child->encoderChanged($encoder); + } + } + + private function _notifyCharsetChanged($charset) + { + $this->_encoder->charsetChanged($charset); + $this->_headers->charsetChanged($charset); + foreach ($this->_immediateChildren as $child) { + $child->charsetChanged($charset); + } + } + + private function _sortChildren() + { + $shouldSort = false; + foreach ($this->_immediateChildren as $child) { + // NOTE: This include alternative parts moved into a related part + if ($child->getNestingLevel() == self::LEVEL_ALTERNATIVE) { + $shouldSort = true; + break; + } + } + + // Sort in order of preference, if there is one + if ($shouldSort) { + usort($this->_immediateChildren, array($this, '_childSortAlgorithm')); + } + } + + private function _childSortAlgorithm($a, $b) + { + $typePrefs = array(); + $types = array( + strtolower($a->getContentType()), + strtolower($b->getContentType()), + ); + foreach ($types as $type) { + $typePrefs[] = (array_key_exists($type, $this->_alternativePartOrder)) + ? $this->_alternativePartOrder[$type] + : (max($this->_alternativePartOrder) + 1); + } + + return ($typePrefs[0] >= $typePrefs[1]) ? 1 : -1; + } + + // -- Destructor + + /** + * Empties it's own contents from the cache. + */ + public function __destruct() + { + $this->_cache->clearAll($this->_cacheKey); + } + + /** + * Throws an Exception if the id passed does not comply with RFC 2822. + * + * @param string $id + * + * @throws Swift_RfcComplianceException + */ + private function _assertValidId($id) + { + if (!preg_match( + '/^'.$this->_grammar->getDefinition('id-left').'@'. + $this->_grammar->getDefinition('id-right').'$/D', + $id + )) { + throw new Swift_RfcComplianceException( + 'Invalid ID given <'.$id.'>' + ); + } + } + + /** + * Make a deep copy of object. + */ + public function __clone() + { + $this->_headers = clone $this->_headers; + $this->_encoder = clone $this->_encoder; + $this->_cacheKey = uniqid(); + $children = array(); + foreach ($this->_children as $pos => $child) { + $children[$pos] = clone $child; + } + $this->setChildren($children); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php new file mode 100644 index 0000000..215f8db --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php @@ -0,0 +1,59 @@ +createDependenciesFor('mime.part') + ); + + if (!isset($charset)) { + $charset = Swift_DependencyContainer::getInstance() + ->lookup('properties.charset'); + } + $this->setBody($body); + $this->setCharset($charset); + if ($contentType) { + $this->setContentType($contentType); + } + } + + /** + * Create a new MimePart. + * + * @param string $body + * @param string $contentType + * @param string $charset + * + * @return Swift_Mime_MimePart + */ + public static function newInstance($body = null, $contentType = null, $charset = null) + { + return new self($body, $contentType, $charset); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php new file mode 100644 index 0000000..b38e1cf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_NullTransport extends Swift_Transport_NullTransport +{ + /** + * Create a new NullTransport. + */ + public function __construct() + { + call_user_func_array( + array($this, 'Swift_Transport_NullTransport::__construct'), + Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.null') + ); + } + + /** + * Create a new NullTransport instance. + * + * @return Swift_NullTransport + */ + public static function newInstance() + { + return new self(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php new file mode 100644 index 0000000..1f26f9b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php @@ -0,0 +1,46 @@ +setThreshold($threshold); + $this->setSleepTime($sleep); + $this->_sleeper = $sleeper; + } + + /** + * Set the number of emails to send before restarting. + * + * @param int $threshold + */ + public function setThreshold($threshold) + { + $this->_threshold = $threshold; + } + + /** + * Get the number of emails to send before restarting. + * + * @return int + */ + public function getThreshold() + { + return $this->_threshold; + } + + /** + * Set the number of seconds to sleep for during a restart. + * + * @param int $sleep time + */ + public function setSleepTime($sleep) + { + $this->_sleep = $sleep; + } + + /** + * Get the number of seconds to sleep for during a restart. + * + * @return int + */ + public function getSleepTime() + { + return $this->_sleep; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + ++$this->_counter; + if ($this->_counter >= $this->_threshold) { + $transport = $evt->getTransport(); + $transport->stop(); + if ($this->_sleep) { + $this->sleep($this->_sleep); + } + $transport->start(); + $this->_counter = 0; + } + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php new file mode 100644 index 0000000..f7e18d0 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php @@ -0,0 +1,164 @@ +getMessage(); + $message->toByteStream($this); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_out += strlen($command); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_in += strlen($response); + } + + /** + * Called when a message is sent so that the outgoing counter can be increased. + * + * @param string $bytes + */ + public function write($bytes) + { + $this->_out += strlen($bytes); + foreach ($this->_mirrors as $stream) { + $stream->write($bytes); + } + } + + /** + * Not used. + */ + public function commit() + { + } + + /** + * Attach $is to this stream. + * + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + $this->_mirrors[] = $is; + } + + /** + * Remove an already bound stream. + * + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + foreach ($this->_mirrors as $k => $stream) { + if ($is === $stream) { + unset($this->_mirrors[$k]); + } + } + } + + /** + * Not used. + */ + public function flushBuffers() + { + foreach ($this->_mirrors as $stream) { + $stream->flushBuffers(); + } + } + + /** + * Get the total number of bytes sent to the server. + * + * @return int + */ + public function getBytesOut() + { + return $this->_out; + } + + /** + * Get the total number of bytes received from the server. + * + * @return int + */ + public function getBytesIn() + { + return $this->_in; + } + + /** + * Reset the internal counters to zero. + */ + public function reset() + { + $this->_out = 0; + $this->_in = 0; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php new file mode 100644 index 0000000..9f9f08b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Decorator/Replacements.php @@ -0,0 +1,31 @@ + + * $replacements = array( + * "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"), + * "address2@domain.tld" => array("{a}" => "x", "{c}" => "y") + * ) + * + * + * When using an instance of {@link Swift_Plugins_Decorator_Replacements}, + * the object should return just the array of replacements for the address + * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + */ + public function __construct($replacements) + { + $this->setReplacements($replacements); + } + + /** + * Sets replacements. + * + * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements + * + * @see __construct() + */ + public function setReplacements($replacements) + { + if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { + $this->_replacements = (array) $replacements; + } else { + $this->_replacements = $replacements; + } + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $this->_restoreMessage($message); + $to = array_keys($message->getTo()); + $address = array_shift($to); + if ($replacements = $this->getReplacementsFor($address)) { + $body = $message->getBody(); + $search = array_keys($replacements); + $replace = array_values($replacements); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $this->_originalBody = $body; + $message->setBody($bodyReplaced); + } + + foreach ($message->getHeaders()->getAll() as $header) { + $body = $header->getFieldBodyModel(); + $count = 0; + if (is_array($body)) { + $bodyReplaced = array(); + foreach ($body as $key => $value) { + $count1 = 0; + $count2 = 0; + $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key; + $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value; + $bodyReplaced[$key] = $value; + + if (!$count && ($count1 || $count2)) { + $count = 1; + } + } + } else { + $bodyReplaced = str_replace($search, $replace, $body, $count); + } + + if ($count) { + $this->_originalHeaders[$header->getFieldName()] = $body; + $header->setFieldBodyModel($bodyReplaced); + } + } + + $children = (array) $message->getChildren(); + foreach ($children as $child) { + list($type) = sscanf($child->getContentType(), '%[^/]/%s'); + if ('text' == $type) { + $body = $child->getBody(); + $bodyReplaced = str_replace( + $search, $replace, $body + ); + if ($body != $bodyReplaced) { + $child->setBody($bodyReplaced); + $this->_originalChildBodies[$child->getId()] = $body; + } + } + } + $this->_lastMessage = $message; + } + } + + /** + * Find a map of replacements for the address. + * + * If this plugin was provided with a delegate instance of + * {@link Swift_Plugins_Decorator_Replacements} then the call will be + * delegated to it. Otherwise, it will attempt to find the replacements + * from the array provided in the constructor. + * + * If no replacements can be found, an empty value (NULL) is returned. + * + * @param string $address + * + * @return array + */ + public function getReplacementsFor($address) + { + if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements) { + return $this->_replacements->getReplacementsFor($address); + } else { + return isset($this->_replacements[$address]) + ? $this->_replacements[$address] + : null + ; + } + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + /** Restore a changed message back to its original state */ + private function _restoreMessage(Swift_Mime_Message $message) + { + if ($this->_lastMessage === $message) { + if (isset($this->_originalBody)) { + $message->setBody($this->_originalBody); + $this->_originalBody = null; + } + if (!empty($this->_originalHeaders)) { + foreach ($message->getHeaders()->getAll() as $header) { + if (array_key_exists($header->getFieldName(), $this->_originalHeaders)) { + $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]); + } + } + $this->_originalHeaders = array(); + } + if (!empty($this->_originalChildBodies)) { + $children = (array) $message->getChildren(); + foreach ($children as $child) { + $id = $child->getId(); + if (array_key_exists($id, $this->_originalChildBodies)) { + $child->setBody($this->_originalChildBodies[$id]); + } + } + $this->_originalChildBodies = array(); + } + $this->_lastMessage = null; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php new file mode 100644 index 0000000..7552b67 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php @@ -0,0 +1,69 @@ +_sender = $sender; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // save current recipients + $headers->addPathHeader('X-Swift-Return-Path', $message->getReturnPath()); + + // replace them with the one to send to + $message->setReturnPath($this->_sender); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-Return-Path')) { + $message->setReturnPath($headers->get('X-Swift-Return-Path')->getAddress()); + $headers->removeAll('X-Swift-Return-Path'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php new file mode 100644 index 0000000..d9bce89 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Logger.php @@ -0,0 +1,36 @@ +_logger = $logger; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_logger->add($entry); + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_logger->clear(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return $this->_logger->dump(); + } + + /** + * Invoked immediately following a command being sent. + * + * @param Swift_Events_CommandEvent $evt + */ + public function commandSent(Swift_Events_CommandEvent $evt) + { + $command = $evt->getCommand(); + $this->_logger->add(sprintf('>> %s', $command)); + } + + /** + * Invoked immediately following a response coming back. + * + * @param Swift_Events_ResponseEvent $evt + */ + public function responseReceived(Swift_Events_ResponseEvent $evt) + { + $response = $evt->getResponse(); + $this->_logger->add(sprintf('<< %s', $response)); + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Starting %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s started', $transportName)); + } + + /** + * Invoked just before a Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ Stopping %s', $transportName)); + } + + /** + * Invoked immediately after the Transport is stopped. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + $transportName = get_class($evt->getSource()); + $this->_logger->add(sprintf('++ %s stopped', $transportName)); + } + + /** + * Invoked as a TransportException is thrown in the Transport system. + * + * @param Swift_Events_TransportExceptionEvent $evt + */ + public function exceptionThrown(Swift_Events_TransportExceptionEvent $evt) + { + $e = $evt->getException(); + $message = $e->getMessage(); + $code = $e->getCode(); + $this->_logger->add(sprintf('!! %s (code: %s)', $message, $code)); + $message .= PHP_EOL; + $message .= 'Log data:'.PHP_EOL; + $message .= $this->_logger->dump(); + $evt->cancelBubble(); + throw new Swift_TransportException($message, $code, $e->getPrevious()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php new file mode 100644 index 0000000..865bb0a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php @@ -0,0 +1,72 @@ +_size = $size; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + $this->_log[] = $entry; + while (count($this->_log) > $this->_size) { + array_shift($this->_log); + } + } + + /** + * Clear the log contents. + */ + public function clear() + { + $this->_log = array(); + } + + /** + * Get this log as a string. + * + * @return string + */ + public function dump() + { + return implode(PHP_EOL, $this->_log); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php new file mode 100644 index 0000000..3583297 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php @@ -0,0 +1,58 @@ +_isHtml = $isHtml; + } + + /** + * Add a log entry. + * + * @param string $entry + */ + public function add($entry) + { + if ($this->_isHtml) { + printf('%s%s%s', htmlspecialchars($entry, ENT_QUOTES), '
    ', PHP_EOL); + } else { + printf('%s%s', $entry, PHP_EOL); + } + } + + /** + * Not implemented. + */ + public function clear() + { + } + + /** + * Not implemented. + */ + public function dump() + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php new file mode 100644 index 0000000..e622cb3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php @@ -0,0 +1,74 @@ +messages = array(); + } + + /** + * Get the message list. + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Get the message count. + * + * @return int count + */ + public function countMessages() + { + return count($this->messages); + } + + /** + * Empty the message list. + */ + public function clear() + { + $this->messages = array(); + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $this->messages[] = clone $evt->getMessage(); + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php new file mode 100644 index 0000000..fb99e4c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Pop/Pop3Connection.php @@ -0,0 +1,31 @@ +_host = $host; + $this->_port = $port; + $this->_crypto = $crypto; + } + + /** + * Create a new PopBeforeSmtpPlugin for $host and $port. + * + * @param string $host + * @param int $port + * @param string $crypto as "tls" or "ssl" + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public static function newInstance($host, $port = 110, $crypto = null) + { + return new self($host, $port, $crypto); + } + + /** + * Set a Pop3Connection to delegate to instead of connecting directly. + * + * @param Swift_Plugins_Pop_Pop3Connection $connection + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setConnection(Swift_Plugins_Pop_Pop3Connection $connection) + { + $this->_connection = $connection; + + return $this; + } + + /** + * Bind this plugin to a specific SMTP transport instance. + * + * @param Swift_Transport + */ + public function bindSmtp(Swift_Transport $smtp) + { + $this->_transport = $smtp; + } + + /** + * Set the connection timeout in seconds (default 10). + * + * @param int $timeout + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setTimeout($timeout) + { + $this->_timeout = (int) $timeout; + + return $this; + } + + /** + * Set the username to use when connecting (if needed). + * + * @param string $username + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setUsername($username) + { + $this->_username = $username; + + return $this; + } + + /** + * Set the password to use when connecting (if needed). + * + * @param string $password + * + * @return Swift_Plugins_PopBeforeSmtpPlugin + */ + public function setPassword($password) + { + $this->_password = $password; + + return $this; + } + + /** + * Connect to the POP3 host and authenticate. + * + * @throws Swift_Plugins_Pop_Pop3Exception if connection fails + */ + public function connect() + { + if (isset($this->_connection)) { + $this->_connection->connect(); + } else { + if (!isset($this->_socket)) { + if (!$socket = fsockopen( + $this->_getHostString(), $this->_port, $errno, $errstr, $this->_timeout)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]: %s', $this->_host, $errstr) + ); + } + $this->_socket = $socket; + + if (false === $greeting = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to connect to POP3 host [%s]', trim($greeting)) + ); + } + + $this->_assertOk($greeting); + + if ($this->_username) { + $this->_command(sprintf("USER %s\r\n", $this->_username)); + $this->_command(sprintf("PASS %s\r\n", $this->_password)); + } + } + } + } + + /** + * Disconnect from the POP3 host. + */ + public function disconnect() + { + if (isset($this->_connection)) { + $this->_connection->disconnect(); + } else { + $this->_command("QUIT\r\n"); + if (!fclose($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 host [%s] connection could not be stopped', $this->_host) + ); + } + $this->_socket = null; + } + } + + /** + * Invoked just before a Transport is started. + * + * @param Swift_Events_TransportChangeEvent $evt + */ + public function beforeTransportStarted(Swift_Events_TransportChangeEvent $evt) + { + if (isset($this->_transport)) { + if ($this->_transport !== $evt->getTransport()) { + return; + } + } + + $this->connect(); + $this->disconnect(); + } + + /** + * Not used. + */ + public function transportStarted(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function beforeTransportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + /** + * Not used. + */ + public function transportStopped(Swift_Events_TransportChangeEvent $evt) + { + } + + private function _command($command) + { + if (!fwrite($this->_socket, $command)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to write command [%s] to POP3 host', trim($command)) + ); + } + + if (false === $response = fgets($this->_socket)) { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('Failed to read from POP3 host after command [%s]', trim($command)) + ); + } + + $this->_assertOk($response); + + return $response; + } + + private function _assertOk($response) + { + if (substr($response, 0, 3) != '+OK') { + throw new Swift_Plugins_Pop_Pop3Exception( + sprintf('POP3 command failed [%s]', trim($response)) + ); + } + } + + private function _getHostString() + { + $host = $this->_host; + switch (strtolower($this->_crypto)) { + case 'ssl': + $host = 'ssl://'.$host; + break; + + case 'tls': + $host = 'tls://'.$host; + break; + } + + return $host; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php new file mode 100644 index 0000000..c3a1f86 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php @@ -0,0 +1,213 @@ +_recipient = $recipient; + $this->_whitelist = $whitelist; + } + + /** + * Set the recipient of all messages. + * + * @param mixed $recipient + */ + public function setRecipient($recipient) + { + $this->_recipient = $recipient; + } + + /** + * Get the recipient of all messages. + * + * @return mixed + */ + public function getRecipient() + { + return $this->_recipient; + } + + /** + * Set a list of regular expressions to whitelist certain recipients. + * + * @param array $whitelist + */ + public function setWhitelist(array $whitelist) + { + $this->_whitelist = $whitelist; + } + + /** + * Get the whitelist. + * + * @return array + */ + public function getWhitelist() + { + return $this->_whitelist; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $headers = $message->getHeaders(); + + // conditionally save current recipients + + if ($headers->has('to')) { + $headers->addMailboxHeader('X-Swift-To', $message->getTo()); + } + + if ($headers->has('cc')) { + $headers->addMailboxHeader('X-Swift-Cc', $message->getCc()); + } + + if ($headers->has('bcc')) { + $headers->addMailboxHeader('X-Swift-Bcc', $message->getBcc()); + } + + // Filter remaining headers against whitelist + $this->_filterHeaderSet($headers, 'To'); + $this->_filterHeaderSet($headers, 'Cc'); + $this->_filterHeaderSet($headers, 'Bcc'); + + // Add each hard coded recipient + $to = $message->getTo(); + if (null === $to) { + $to = array(); + } + + foreach ((array) $this->_recipient as $recipient) { + if (!array_key_exists($recipient, $to)) { + $message->addTo($recipient); + } + } + } + + /** + * Filter header set against a whitelist of regular expressions. + * + * @param Swift_Mime_HeaderSet $headerSet + * @param string $type + */ + private function _filterHeaderSet(Swift_Mime_HeaderSet $headerSet, $type) + { + foreach ($headerSet->getAll($type) as $headers) { + $headers->setNameAddresses($this->_filterNameAddresses($headers->getNameAddresses())); + } + } + + /** + * Filtered list of addresses => name pairs. + * + * @param array $recipients + * + * @return array + */ + private function _filterNameAddresses(array $recipients) + { + $filtered = array(); + + foreach ($recipients as $address => $name) { + if ($this->_isWhitelisted($address)) { + $filtered[$address] = $name; + } + } + + return $filtered; + } + + /** + * Matches address against whitelist of regular expressions. + * + * @param $recipient + * + * @return bool + */ + protected function _isWhitelisted($recipient) + { + if (in_array($recipient, (array) $this->_recipient)) { + return true; + } + + foreach ($this->_whitelist as $pattern) { + if (preg_match($pattern, $recipient)) { + return true; + } + } + + return false; + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $this->_restoreMessage($evt->getMessage()); + } + + private function _restoreMessage(Swift_Mime_Message $message) + { + // restore original headers + $headers = $message->getHeaders(); + + if ($headers->has('X-Swift-To')) { + $message->setTo($headers->get('X-Swift-To')->getNameAddresses()); + $headers->removeAll('X-Swift-To'); + } else { + $message->setTo(null); + } + + if ($headers->has('X-Swift-Cc')) { + $message->setCc($headers->get('X-Swift-Cc')->getNameAddresses()); + $headers->removeAll('X-Swift-Cc'); + } + + if ($headers->has('X-Swift-Bcc')) { + $message->setBcc($headers->get('X-Swift-Bcc')->getNameAddresses()); + $headers->removeAll('X-Swift-Bcc'); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php new file mode 100644 index 0000000..0f21b7d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php @@ -0,0 +1,32 @@ +_reporter = $reporter; + } + + /** + * Not used. + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + } + + /** + * Invoked immediately after the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + $message = $evt->getMessage(); + $failures = array_flip($evt->getFailedRecipients()); + foreach ((array) $message->getTo() as $address => $null) { + $this->_reporter->notify( + $message, $address, (array_key_exists($address, $failures) + ? Swift_Plugins_Reporter::RESULT_FAIL + : Swift_Plugins_Reporter::RESULT_PASS) + ); + } + foreach ((array) $message->getCc() as $address => $null) { + $this->_reporter->notify( + $message, $address, (array_key_exists($address, $failures) + ? Swift_Plugins_Reporter::RESULT_FAIL + : Swift_Plugins_Reporter::RESULT_PASS) + ); + } + foreach ((array) $message->getBcc() as $address => $null) { + $this->_reporter->notify( + $message, $address, (array_key_exists($address, $failures) + ? Swift_Plugins_Reporter::RESULT_FAIL + : Swift_Plugins_Reporter::RESULT_PASS) + ); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php new file mode 100644 index 0000000..cad9d16 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php @@ -0,0 +1,59 @@ +_failures_cache[$address])) { + $this->_failures[] = $address; + $this->_failures_cache[$address] = true; + } + } + + /** + * Get an array of addresses for which delivery failed. + * + * @return array + */ + public function getFailedRecipients() + { + return $this->_failures; + } + + /** + * Clear the buffer (empty the list). + */ + public function clear() + { + $this->_failures = $this->_failures_cache = array(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php new file mode 100644 index 0000000..c625935 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php @@ -0,0 +1,39 @@ +'.PHP_EOL; + echo 'PASS '.$address.PHP_EOL; + echo ''.PHP_EOL; + flush(); + } else { + echo '
    '.PHP_EOL; + echo 'FAIL '.$address.PHP_EOL; + echo '
    '.PHP_EOL; + flush(); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php new file mode 100644 index 0000000..595c0f6 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Sleeper.php @@ -0,0 +1,24 @@ +_rate = $rate; + $this->_mode = $mode; + $this->_sleeper = $sleeper; + $this->_timer = $timer; + } + + /** + * Invoked immediately before the Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function beforeSendPerformed(Swift_Events_SendEvent $evt) + { + $time = $this->getTimestamp(); + if (!isset($this->_start)) { + $this->_start = $time; + } + $duration = $time - $this->_start; + + switch ($this->_mode) { + case self::BYTES_PER_MINUTE : + $sleep = $this->_throttleBytesPerMinute($duration); + break; + case self::MESSAGES_PER_SECOND : + $sleep = $this->_throttleMessagesPerSecond($duration); + break; + case self::MESSAGES_PER_MINUTE : + $sleep = $this->_throttleMessagesPerMinute($duration); + break; + default : + $sleep = 0; + break; + } + + if ($sleep > 0) { + $this->sleep($sleep); + } + } + + /** + * Invoked when a Message is sent. + * + * @param Swift_Events_SendEvent $evt + */ + public function sendPerformed(Swift_Events_SendEvent $evt) + { + parent::sendPerformed($evt); + ++$this->_messages; + } + + /** + * Sleep for $seconds. + * + * @param int $seconds + */ + public function sleep($seconds) + { + if (isset($this->_sleeper)) { + $this->_sleeper->sleep($seconds); + } else { + sleep($seconds); + } + } + + /** + * Get the current UNIX timestamp. + * + * @return int + */ + public function getTimestamp() + { + if (isset($this->_timer)) { + return $this->_timer->getTimestamp(); + } else { + return time(); + } + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleBytesPerMinute($timePassed) + { + $expectedDuration = $this->getBytesOut() / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerSecond($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate); + + return (int) ceil($expectedDuration - $timePassed); + } + + /** + * Get a number of seconds to sleep for. + * + * @param int $timePassed + * + * @return int + */ + private function _throttleMessagesPerMinute($timePassed) + { + $expectedDuration = $this->_messages / ($this->_rate / 60); + + return (int) ceil($expectedDuration - $timePassed); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php new file mode 100644 index 0000000..9c8deb3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Timer.php @@ -0,0 +1,24 @@ +register('properties.charset')->asValue($charset); + + return $this; + } + + /** + * Set the directory where temporary files can be saved. + * + * @param string $dir + * + * @return Swift_Preferences + */ + public function setTempDir($dir) + { + Swift_DependencyContainer::getInstance() + ->register('tempdir')->asValue($dir); + + return $this; + } + + /** + * Set the type of cache to use (i.e. "disk" or "array"). + * + * @param string $type + * + * @return Swift_Preferences + */ + public function setCacheType($type) + { + Swift_DependencyContainer::getInstance() + ->register('cache')->asAliasOf(sprintf('cache.%s', $type)); + + return $this; + } + + /** + * Set the QuotedPrintable dot escaper preference. + * + * @param bool $dotEscape + * + * @return Swift_Preferences + */ + public function setQPDotEscape($dotEscape) + { + $dotEscape = !empty($dotEscape); + Swift_DependencyContainer::getInstance() + ->register('mime.qpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + ->addConstructorValue($dotEscape); + + return $this; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php new file mode 100644 index 0000000..2897474 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php @@ -0,0 +1,27 @@ +createDependenciesFor('transport.sendmail') + ); + + $this->setCommand($command); + } + + /** + * Create a new SendmailTransport instance. + * + * @param string $command + * + * @return Swift_SendmailTransport + */ + public static function newInstance($command = '/usr/sbin/sendmail -bs') + { + return new self($command); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php new file mode 100644 index 0000000..2e7a872 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SignedMessage.php @@ -0,0 +1,23 @@ + + * + * @deprecated + */ +class Swift_SignedMessage extends Swift_Message +{ +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php new file mode 100644 index 0000000..2d8176d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php @@ -0,0 +1,20 @@ + + */ +interface Swift_Signer +{ + public function reset(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php new file mode 100644 index 0000000..9ffcef3 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php @@ -0,0 +1,33 @@ + + */ +interface Swift_Signers_BodySigner extends Swift_Signer +{ + /** + * Change the Swift_Signed_Message to apply the singing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_BodySigner + */ + public function signMessage(Swift_Message $message); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php new file mode 100644 index 0000000..514b61a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php @@ -0,0 +1,702 @@ + + */ +class Swift_Signers_DKIMSigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Body canon method. + * + * @var string + */ + protected $_bodyCanon = 'simple'; + + /** + * Header canon method. + * + * @var string + */ + protected $_headerCanon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array(); + + /** + * Signer identity. + * + * @var unknown_type + */ + protected $_signerIdentity; + + /** + * BodyLength. + * + * @var int + */ + protected $_bodyLen = 0; + + /** + * Maximum signedLen. + * + * @var int + */ + protected $_maxLen = PHP_INT_MAX; + + /** + * Embbed bodyLen in signature. + * + * @var bool + */ + protected $_showLen = false; + + /** + * When the signature has been applied (true means time()), false means not embedded. + * + * @var mixed + */ + protected $_signatureTimestamp = true; + + /** + * When will the signature expires false means not embedded, if sigTimestamp is auto + * Expiration is relative, otherwhise it's absolute. + * + * @var int + */ + protected $_signatureExpiration = false; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + protected $_signedHeaders = array(); + + /** + * If debugHeaders is set store debugDatas here. + * + * @var string + */ + private $_debugHeadersData = ''; + + /** + * Stores the bodyHash. + * + * @var string + */ + private $_bodyHash = ''; + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_dkimHeader; + + /** + * Hash Handler. + * + * @var hash_ressource + */ + private $_headerHashHandler; + + private $_bodyHashHandler; + + private $_headerHash; + + private $_headerCanonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DKIMSigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DKIMSigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Reset the Signer. + * + * @see Swift_Signer::reset() + */ + public function reset() + { + $this->_headerHash = null; + $this->_signedHeaders = array(); + $this->_headerHashHandler = null; + $this->_bodyHash = null; + $this->_bodyHashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + */ + public function commit() + { + // Nothing to do + return; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + */ + public function flushBuffers() + { + $this->reset(); + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DKIMSigner + */ + public function setHashAlgorithm($hash) + { + // Unable to sign with rsa-sha256 + if ($hash == 'rsa-sha1') { + $this->_hashAlgorithm = 'rsa-sha1'; + } else { + $this->_hashAlgorithm = 'rsa-sha256'; + } + + return $this; + } + + /** + * Set the body canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodyCanon($canon) + { + if ($canon == 'relaxed') { + $this->_bodyCanon = 'relaxed'; + } else { + $this->_bodyCanon = 'simple'; + } + + return $this; + } + + /** + * Set the header canonicalization algorithm. + * + * @param string $canon + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaderCanon($canon) + { + if ($canon == 'relaxed') { + $this->_headerCanon = 'relaxed'; + } else { + $this->_headerCanon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Set the length of the body to sign. + * + * @param mixed $len (bool or int) + * + * @return Swift_Signers_DKIMSigner + */ + public function setBodySignedLen($len) + { + if ($len === true) { + $this->_showLen = true; + $this->_maxLen = PHP_INT_MAX; + } elseif ($len === false) { + $this->showLen = false; + $this->_maxLen = PHP_INT_MAX; + } else { + $this->_showLen = true; + $this->_maxLen = (int) $len; + } + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha256' : + $this->_bodyHashHandler = hash_init('sha256'); + break; + case 'rsa-sha1' : + $this->_bodyHashHandler = hash_init('sha1'); + break; + } + $this->_bodyCanonLine = ''; + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DKIM-Signature', 'X-DebugHash'); + } else { + return array('DKIM-Signature'); + } + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DKIMSigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_headerCanonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DKIMSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DKIM-Signature + $params = array('v' => '1', 'a' => $this->_hashAlgorithm, 'bh' => base64_encode($this->_bodyHash), 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'i' => $this->_signerIdentity, 's' => $this->_selector); + if ($this->_bodyCanon != 'simple') { + $params['c'] = $this->_headerCanon.'/'.$this->_bodyCanon; + } elseif ($this->_headerCanon != 'simple') { + $params['c'] = $this->_headerCanon; + } + if ($this->_showLen) { + $params['l'] = $this->_bodyLen; + } + if ($this->_signatureTimestamp === true) { + $params['t'] = time(); + if ($this->_signatureExpiration !== false) { + $params['x'] = $params['t'] + $this->_signatureExpiration; + } + } else { + if ($this->_signatureTimestamp !== false) { + $params['t'] = $this->_signatureTimestamp; + } + if ($this->_signatureExpiration !== false) { + $params['x'] = $this->_signatureExpiration; + } + } + if ($this->_debugHeaders) { + $params['z'] = implode('|', $this->_debugHeadersData); + } + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DKIM-Signature', $string); + // Add the last DKIM-Signature + $tmp = $headers->getAll('DKIM-Signature'); + $this->_dkimHeader = end($tmp); + $this->_addHeader(trim($this->_dkimHeader->toString())."\r\n b=", true); + $this->_endOfHeaders(); + if ($this->_debugHeaders) { + $headers->addTextHeader('X-DebugHash', base64_encode($this->_headerHash)); + } + $this->_dkimHeader->setValue($string.' b='.trim(chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '))); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header, $is_sig = false) + { + switch ($this->_headerCanon) { + case 'relaxed' : + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value).($is_sig ? '' : "\r\n"); + case 'simple' : + // Nothing to do + } + $this->_addToHeaderHash($header); + } + + protected function _endOfHeaders() + { + //$this->_headerHash=hash_final($this->_headerHashHandler, true); + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $method = ($this->_bodyCanon == 'relaxed'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r" : + $this->_bodyCanonLastChar = "\r"; + break; + case "\n" : + if ($this->_bodyCanonLastChar == "\r") { + if ($method) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + // todo handle it but should never happen + } + break; + case ' ' : + case "\t" : + if ($method) { + $this->_bodyCanonSpace = true; + break; + } + default : + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + if ($this->_bodyCanonSpace) { + $this->_bodyCanonLine .= ' '; + $canon .= ' '; + $this->_bodyCanonSpace = false; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToBodyHash($canon); + } + + protected function _endOfBody() + { + // Add trailing Line return if last line is non empty + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToBodyHash("\r\n"); + } + $this->_bodyHash = hash_final($this->_bodyHashHandler, true); + } + + private function _addToBodyHash($string) + { + $len = strlen($string); + if ($len > ($new_len = ($this->_maxLen - $this->_bodyLen))) { + $string = substr($string, 0, $new_len); + $len = $new_len; + } + hash_update($this->_bodyHashHandler, $string); + $this->_bodyLen += $len; + } + + private function _addToHeaderHash($header) + { + if ($this->_debugHeaders) { + $this->_debugHeadersData[] = trim($header); + } + $this->_headerCanonData .= $header; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + switch ($this->_hashAlgorithm) { + case 'rsa-sha1': + $algorithm = OPENSSL_ALGO_SHA1; + break; + case 'rsa-sha256': + $algorithm = OPENSSL_ALGO_SHA256; + break; + } + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DKIM Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_headerCanonData, $signature, $pkeyId, $algorithm)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DKIM Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php new file mode 100644 index 0000000..bceda3d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php @@ -0,0 +1,525 @@ + + */ +class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner +{ + /** + * PrivateKey. + * + * @var string + */ + protected $_privateKey; + + /** + * DomainName. + * + * @var string + */ + protected $_domainName; + + /** + * Selector. + * + * @var string + */ + protected $_selector; + + /** + * Hash algorithm used. + * + * @var string + */ + protected $_hashAlgorithm = 'rsa-sha1'; + + /** + * Canonisation method. + * + * @var string + */ + protected $_canon = 'simple'; + + /** + * Headers not being signed. + * + * @var array + */ + protected $_ignoredHeaders = array(); + + /** + * Signer identity. + * + * @var string + */ + protected $_signerIdentity; + + /** + * Must we embed signed headers? + * + * @var bool + */ + protected $_debugHeaders = false; + + // work variables + /** + * Headers used to generate hash. + * + * @var array + */ + private $_signedHeaders = array(); + + /** + * Stores the signature header. + * + * @var Swift_Mime_Headers_ParameterizedHeader + */ + protected $_domainKeyHeader; + + /** + * Hash Handler. + * + * @var resource|null + */ + private $_hashHandler; + + private $_hash; + + private $_canonData = ''; + + private $_bodyCanonEmptyCounter = 0; + + private $_bodyCanonIgnoreStart = 2; + + private $_bodyCanonSpace = false; + + private $_bodyCanonLastChar = null; + + private $_bodyCanonLine = ''; + + private $_bound = array(); + + /** + * Constructor. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + */ + public function __construct($privateKey, $domainName, $selector) + { + $this->_privateKey = $privateKey; + $this->_domainName = $domainName; + $this->_signerIdentity = '@'.$domainName; + $this->_selector = $selector; + } + + /** + * Instanciate DomainKeySigner. + * + * @param string $privateKey + * @param string $domainName + * @param string $selector + * + * @return Swift_Signers_DomainKeySigner + */ + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + /** + * Resets internal states. + * + * @return Swift_Signers_DomainKeysSigner + */ + public function reset() + { + $this->_hash = null; + $this->_hashHandler = null; + $this->_bodyCanonIgnoreStart = 2; + $this->_bodyCanonEmptyCounter = 0; + $this->_bodyCanonLastChar = null; + $this->_bodyCanonSpace = false; + + return $this; + } + + /** + * Writes $bytes to the end of the stream. + * + * Writing may not happen immediately if the stream chooses to buffer. If + * you want to write these bytes with immediate effect, call {@link commit()} + * after calling write(). + * + * This method returns the sequence ID of the write (i.e. 1 for first, 2 for + * second, etc etc). + * + * @param string $bytes + * + * @throws Swift_IoException + * + * @return int + * @return Swift_Signers_DomainKeysSigner + */ + public function write($bytes) + { + $this->_canonicalizeBody($bytes); + foreach ($this->_bound as $is) { + $is->write($bytes); + } + + return $this; + } + + /** + * For any bytes that are currently buffered inside the stream, force them + * off the buffer. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeysSigner + */ + public function commit() + { + // Nothing to do + return $this; + } + + /** + * Attach $is to this stream. + * The stream acts as an observer, receiving all data that is written. + * All {@link write()} and {@link flushBuffers()} operations will be mirrored. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeysSigner + */ + public function bind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + $this->_bound[] = $is; + + return $this; + } + + /** + * Remove an already bound stream. + * If $is is not bound, no errors will be raised. + * If the stream currently has any buffered data it will be written to $is + * before unbinding occurs. + * + * @param Swift_InputByteStream $is + * + * @return Swift_Signers_DomainKeysSigner + */ + public function unbind(Swift_InputByteStream $is) + { + // Don't have to mirror anything + foreach ($this->_bound as $k => $stream) { + if ($stream === $is) { + unset($this->_bound[$k]); + + return; + } + } + + return $this; + } + + /** + * Flush the contents of the stream (empty it) and set the internal pointer + * to the beginning. + * + * @throws Swift_IoException + * + * @return Swift_Signers_DomainKeysSigner + */ + public function flushBuffers() + { + $this->reset(); + + return $this; + } + + /** + * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256. + * + * @param string $hash + * + * @return Swift_Signers_DomainKeysSigner + */ + public function setHashAlgorithm($hash) + { + $this->_hashAlgorithm = 'rsa-sha1'; + + return $this; + } + + /** + * Set the canonicalization algorithm. + * + * @param string $canon simple | nofws defaults to simple + * + * @return Swift_Signers_DomainKeysSigner + */ + public function setCanon($canon) + { + if ($canon == 'nofws') { + $this->_canon = 'nofws'; + } else { + $this->_canon = 'simple'; + } + + return $this; + } + + /** + * Set the signer identity. + * + * @param string $identity + * + * @return Swift_Signers_DomainKeySigner + */ + public function setSignerIdentity($identity) + { + $this->_signerIdentity = $identity; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DomainKeySigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + /** + * Start Body. + */ + public function startBody() + { + } + + /** + * End Body. + */ + public function endBody() + { + $this->_endOfBody(); + } + + /** + * Returns the list of Headers Tampered by this plugin. + * + * @return array + */ + public function getAlteredHeaders() + { + if ($this->_debugHeaders) { + return array('DomainKey-Signature', 'X-DebugHash'); + } else { + return array('DomainKey-Signature'); + } + } + + /** + * Adds an ignored Header. + * + * @param string $header_name + * + * @return Swift_Signers_DomainKeySigner + */ + public function ignoreHeader($header_name) + { + $this->_ignoredHeaders[strtolower($header_name)] = true; + + return $this; + } + + /** + * Set the headers to sign. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $this->_startHash(); + $this->_canonData = ''; + // Loop through Headers + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + if ($headers->has($hName)) { + $tmp = $headers->getAll($hName); + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $this->_addHeader($header->toString()); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + $this->_endOfHeaders(); + + return $this; + } + + /** + * Add the signature to the given Headers. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_DomainKeySigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers) + { + // Prepare the DomainKey-Signature Header + $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector); + $string = ''; + foreach ($params as $k => $v) { + $string .= $k.'='.$v.'; '; + } + $string = trim($string); + $headers->addTextHeader('DomainKey-Signature', $string); + + return $this; + } + + /* Private helpers */ + + protected function _addHeader($header) + { + switch ($this->_canon) { + case 'nofws' : + // Prepare Header and cascade + $exploded = explode(':', $header, 2); + $name = strtolower(trim($exploded[0])); + $value = str_replace("\r\n", '', $exploded[1]); + $value = preg_replace("/[ \t][ \t]+/", ' ', $value); + $header = $name.':'.trim($value)."\r\n"; + case 'simple' : + // Nothing to do + } + $this->_addToHash($header); + } + + protected function _endOfHeaders() + { + $this->_bodyCanonEmptyCounter = 1; + } + + protected function _canonicalizeBody($string) + { + $len = strlen($string); + $canon = ''; + $nofws = ($this->_canon == 'nofws'); + for ($i = 0; $i < $len; ++$i) { + if ($this->_bodyCanonIgnoreStart > 0) { + --$this->_bodyCanonIgnoreStart; + continue; + } + switch ($string[$i]) { + case "\r" : + $this->_bodyCanonLastChar = "\r"; + break; + case "\n" : + if ($this->_bodyCanonLastChar == "\r") { + if ($nofws) { + $this->_bodyCanonSpace = false; + } + if ($this->_bodyCanonLine == '') { + ++$this->_bodyCanonEmptyCounter; + } else { + $this->_bodyCanonLine = ''; + $canon .= "\r\n"; + } + } else { + // Wooops Error + throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r'); + } + break; + case ' ' : + case "\t" : + case "\x09": //HTAB + if ($nofws) { + $this->_bodyCanonSpace = true; + break; + } + default : + if ($this->_bodyCanonEmptyCounter > 0) { + $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter); + $this->_bodyCanonEmptyCounter = 0; + } + $this->_bodyCanonLine .= $string[$i]; + $canon .= $string[$i]; + } + } + $this->_addToHash($canon); + } + + protected function _endOfBody() + { + if (strlen($this->_bodyCanonLine) > 0) { + $this->_addToHash("\r\n"); + } + $this->_hash = hash_final($this->_hashHandler, true); + } + + private function _addToHash($string) + { + $this->_canonData .= $string; + hash_update($this->_hashHandler, $string); + } + + private function _startHash() + { + // Init + switch ($this->_hashAlgorithm) { + case 'rsa-sha1' : + $this->_hashHandler = hash_init('sha1'); + break; + } + $this->_canonLine = ''; + } + + /** + * @throws Swift_SwiftException + * + * @return string + */ + private function _getEncryptedHash() + { + $signature = ''; + $pkeyId = openssl_get_privatekey($this->_privateKey); + if (!$pkeyId) { + throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']'); + } + if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) { + return $signature; + } + throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php new file mode 100644 index 0000000..c75cb08 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php @@ -0,0 +1,65 @@ + + */ +interface Swift_Signers_HeaderSigner extends Swift_Signer, Swift_InputByteStream +{ + /** + * Exclude an header from the signed headers. + * + * @param string $header_name + * + * @return Swift_Signers_HeaderSigner + */ + public function ignoreHeader($header_name); + + /** + * Prepare the Signer to get a new Body. + * + * @return Swift_Signers_HeaderSigner + */ + public function startBody(); + + /** + * Give the signal that the body has finished streaming. + * + * @return Swift_Signers_HeaderSigner + */ + public function endBody(); + + /** + * Give the headers already given. + * + * @param Swift_Mime_SimpleHeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function setHeaders(Swift_Mime_HeaderSet $headers); + + /** + * Add the header(s) to the headerSet. + * + * @param Swift_Mime_HeaderSet $headers + * + * @return Swift_Signers_HeaderSigner + */ + public function addSignature(Swift_Mime_HeaderSet $headers); + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php new file mode 100644 index 0000000..9f1022c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php @@ -0,0 +1,189 @@ + + */ +class Swift_Signers_OpenDKIMSigner extends Swift_Signers_DKIMSigner +{ + private $_peclLoaded = false; + + private $_dkimHandler = null; + + private $dropFirstLF = true; + + const CANON_RELAXED = 1; + const CANON_SIMPLE = 2; + const SIG_RSA_SHA1 = 3; + const SIG_RSA_SHA256 = 4; + + public function __construct($privateKey, $domainName, $selector) + { + if (extension_loaded('opendkim')) { + $this->_peclLoaded = true; + } else { + throw new Swift_SwiftException('php-opendkim extension not found'); + } + parent::__construct($privateKey, $domainName, $selector); + } + + public static function newInstance($privateKey, $domainName, $selector) + { + return new static($privateKey, $domainName, $selector); + } + + public function addSignature(Swift_Mime_HeaderSet $headers) + { + $header = new Swift_Mime_Headers_OpenDKIMHeader('DKIM-Signature'); + $headerVal = $this->_dkimHandler->getSignatureHeader(); + if (!$headerVal) { + throw new Swift_SwiftException('OpenDKIM Error: '.$this->_dkimHandler->getError()); + } + $header->setValue($headerVal); + $headers->set($header); + + return $this; + } + + public function setHeaders(Swift_Mime_HeaderSet $headers) + { + $bodyLen = $this->_bodyLen; + if (is_bool($bodyLen)) { + $bodyLen = -1; + } + $hash = ($this->_hashAlgorithm == 'rsa-sha1') ? OpenDKIMSign::ALG_RSASHA1 : OpenDKIMSign::ALG_RSASHA256; + $bodyCanon = ($this->_bodyCanon == 'simple') ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $headerCanon = ($this->_headerCanon == 'simple') ? OpenDKIMSign::CANON_SIMPLE : OpenDKIMSign::CANON_RELAXED; + $this->_dkimHandler = new OpenDKIMSign($this->_privateKey, $this->_selector, $this->_domainName, $headerCanon, $bodyCanon, $hash, $bodyLen); + // Hardcode signature Margin for now + $this->_dkimHandler->setMargin(78); + + if (!is_numeric($this->_signatureTimestamp)) { + OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, time()); + } else { + if (!OpenDKIM::setOption(OpenDKIM::OPTS_FIXEDTIME, $this->_signatureTimestamp)) { + throw new Swift_SwiftException('Unable to force signature timestamp ['.openssl_error_string().']'); + } + } + if (isset($this->_signerIdentity)) { + $this->_dkimHandler->setSigner($this->_signerIdentity); + } + $listHeaders = $headers->listAll(); + foreach ($listHeaders as $hName) { + // Check if we need to ignore Header + if (!isset($this->_ignoredHeaders[strtolower($hName)])) { + $tmp = $headers->getAll($hName); + if ($headers->has($hName)) { + foreach ($tmp as $header) { + if ($header->getFieldBody() != '') { + $htosign = $header->toString(); + $this->_dkimHandler->header($htosign); + $this->_signedHeaders[] = $header->getFieldName(); + } + } + } + } + } + + return $this; + } + + public function startBody() + { + if (!$this->_peclLoaded) { + return parent::startBody(); + } + $this->dropFirstLF = true; + $this->_dkimHandler->eoh(); + + return $this; + } + + public function endBody() + { + if (!$this->_peclLoaded) { + return parent::endBody(); + } + $this->_dkimHandler->eom(); + + return $this; + } + + public function reset() + { + $this->_dkimHandler = null; + parent::reset(); + + return $this; + } + + /** + * Set the signature timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureTimestamp($time) + { + $this->_signatureTimestamp = $time; + + return $this; + } + + /** + * Set the signature expiration timestamp. + * + * @param timestamp $time + * + * @return Swift_Signers_DKIMSigner + */ + public function setSignatureExpiration($time) + { + $this->_signatureExpiration = $time; + + return $this; + } + + /** + * Enable / disable the DebugHeaders. + * + * @param bool $debug + * + * @return Swift_Signers_DKIMSigner + */ + public function setDebugHeaders($debug) + { + $this->_debugHeaders = (bool) $debug; + + return $this; + } + + // Protected + + protected function _canonicalizeBody($string) + { + if (!$this->_peclLoaded) { + return parent::_canonicalizeBody($string); + } + if (false && $this->dropFirstLF === true) { + if ($string[0] == "\r" && $string[1] == "\n") { + $string = substr($string, 2); + } + } + $this->dropFirstLF = false; + if (strlen($string)) { + $this->_dkimHandler->body($string); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php new file mode 100644 index 0000000..d4c77e7 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php @@ -0,0 +1,437 @@ + + */ +class Swift_Signers_SMimeSigner implements Swift_Signers_BodySigner +{ + protected $signCertificate; + protected $signPrivateKey; + protected $encryptCert; + protected $signThenEncrypt = true; + protected $signLevel; + protected $encryptLevel; + protected $signOptions; + protected $encryptOptions; + protected $encryptCipher; + protected $extraCerts = null; + + /** + * @var Swift_StreamFilters_StringReplacementFilterFactory + */ + protected $replacementFactory; + + /** + * @var Swift_Mime_HeaderFactory + */ + protected $headerFactory; + + /** + * Constructor. + * + * @param string $certificate + * @param string $privateKey + * @param string $encryptCertificate + */ + public function __construct($signCertificate = null, $signPrivateKey = null, $encryptCertificate = null) + { + if (null !== $signPrivateKey) { + $this->setSignCertificate($signCertificate, $signPrivateKey); + } + + if (null !== $encryptCertificate) { + $this->setEncryptCertificate($encryptCertificate); + } + + $this->replacementFactory = Swift_DependencyContainer::getInstance() + ->lookup('transport.replacementfactory'); + + $this->signOptions = PKCS7_DETACHED; + + // Supported since php5.4 + if (defined('OPENSSL_CIPHER_AES_128_CBC')) { + $this->encryptCipher = OPENSSL_CIPHER_AES_128_CBC; + } else { + $this->encryptCipher = OPENSSL_CIPHER_RC2_128; + } + } + + /** + * Returns an new Swift_Signers_SMimeSigner instance. + * + * @param string $certificate + * @param string $privateKey + * + * @return Swift_Signers_SMimeSigner + */ + public static function newInstance($certificate = null, $privateKey = null) + { + return new self($certificate, $privateKey); + } + + /** + * Set the certificate location to use for signing. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * + * @param string $certificate + * @param string|array $privateKey If the key needs an passphrase use array('file-location', 'passphrase') instead + * @param int $signOptions Bitwise operator options for openssl_pkcs7_sign() + * @param string $extraCerts A file containing intermediate certificates needed by the signing certificate + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignCertificate($certificate, $privateKey = null, $signOptions = PKCS7_DETACHED, $extraCerts = null) + { + $this->signCertificate = 'file://'.str_replace('\\', '/', realpath($certificate)); + + if (null !== $privateKey) { + if (is_array($privateKey)) { + $this->signPrivateKey = $privateKey; + $this->signPrivateKey[0] = 'file://'.str_replace('\\', '/', realpath($privateKey[0])); + } else { + $this->signPrivateKey = 'file://'.str_replace('\\', '/', realpath($privateKey)); + } + } + + $this->signOptions = $signOptions; + if (null !== $extraCerts) { + $this->extraCerts = str_replace('\\', '/', realpath($extraCerts)); + } + + return $this; + } + + /** + * Set the certificate location to use for encryption. + * + * @link http://www.php.net/manual/en/openssl.pkcs7.flags.php + * @link http://nl3.php.net/manual/en/openssl.ciphers.php + * + * @param string|array $recipientCerts Either an single X.509 certificate, or an assoc array of X.509 certificates. + * @param int $cipher + * + * @return Swift_Signers_SMimeSigner + */ + public function setEncryptCertificate($recipientCerts, $cipher = null) + { + if (is_array($recipientCerts)) { + $this->encryptCert = array(); + + foreach ($recipientCerts as $cert) { + $this->encryptCert[] = 'file://'.str_replace('\\', '/', realpath($cert)); + } + } else { + $this->encryptCert = 'file://'.str_replace('\\', '/', realpath($recipientCerts)); + } + + if (null !== $cipher) { + $this->encryptCipher = $cipher; + } + + return $this; + } + + /** + * @return string + */ + public function getSignCertificate() + { + return $this->signCertificate; + } + + /** + * @return string + */ + public function getSignPrivateKey() + { + return $this->signPrivateKey; + } + + /** + * Set perform signing before encryption. + * + * The default is to first sign the message and then encrypt. + * But some older mail clients, namely Microsoft Outlook 2000 will work when the message first encrypted. + * As this goes against the official specs, its recommended to only use 'encryption -> signing' when specifically targeting these 'broken' clients. + * + * @param string $signThenEncrypt + * + * @return Swift_Signers_SMimeSigner + */ + public function setSignThenEncrypt($signThenEncrypt = true) + { + $this->signThenEncrypt = $signThenEncrypt; + + return $this; + } + + /** + * @return bool + */ + public function isSignThenEncrypt() + { + return $this->signThenEncrypt; + } + + /** + * Resets internal states. + * + * @return Swift_Signers_SMimeSigner + */ + public function reset() + { + return $this; + } + + /** + * Change the Swift_Message to apply the signing. + * + * @param Swift_Message $message + * + * @return Swift_Signers_SMimeSigner + */ + public function signMessage(Swift_Message $message) + { + if (null === $this->signCertificate && null === $this->encryptCert) { + return $this; + } + + // Store the message using ByteStream to a file{1} + // Remove all Children + // Sign file{1}, parse the new MIME headers and set them on the primary MimeEntity + // Set the singed-body as the new body (without boundary) + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $this->toSMimeByteStream($messageStream, $message); + $message->setEncoder(Swift_DependencyContainer::getInstance()->lookup('mime.rawcontentencoder')); + + $message->setChildren(array()); + $this->streamToMime($messageStream, $message); + } + + /** + * Return the list of header a signer might tamper. + * + * @return array + */ + public function getAlteredHeaders() + { + return array('Content-Type', 'Content-Transfer-Encoding', 'Content-Disposition'); + } + + /** + * @param Swift_InputByteStream $inputStream + * @param Swift_Message $mimeEntity + */ + protected function toSMimeByteStream(Swift_InputByteStream $inputStream, Swift_Message $message) + { + $mimeEntity = $this->createMessage($message); + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $mimeEntity->toByteStream($messageStream); + $messageStream->commit(); + + if (null !== $this->signCertificate && null !== $this->encryptCert) { + $temporaryStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if ($this->signThenEncrypt) { + $this->messageStreamToSignedByteStream($messageStream, $temporaryStream); + $this->messageStreamToEncryptedByteStream($temporaryStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $temporaryStream); + $this->messageStreamToSignedByteStream($temporaryStream, $inputStream); + } + } elseif ($this->signCertificate !== null) { + $this->messageStreamToSignedByteStream($messageStream, $inputStream); + } else { + $this->messageStreamToEncryptedByteStream($messageStream, $inputStream); + } + } + + /** + * @param Swift_Message $message + * + * @return Swift_Message + */ + protected function createMessage(Swift_Message $message) + { + $mimeEntity = new Swift_Message('', $message->getBody(), $message->getContentType(), $message->getCharset()); + $mimeEntity->setChildren($message->getChildren()); + + $messageHeaders = $mimeEntity->getHeaders(); + $messageHeaders->remove('Message-ID'); + $messageHeaders->remove('Date'); + $messageHeaders->remove('Subject'); + $messageHeaders->remove('MIME-Version'); + $messageHeaders->remove('To'); + $messageHeaders->remove('From'); + + return $mimeEntity; + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $inputStream + * + * @throws Swift_IoException + */ + protected function messageStreamToSignedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $inputStream) + { + $signedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + $args = array($outputStream->getPath(), $signedMessageStream->getPath(), $this->signCertificate, $this->signPrivateKey, array(), $this->signOptions); + if (null !== $this->extraCerts) { + $args[] = $this->extraCerts; + } + + if (!call_user_func_array('openssl_pkcs7_sign', $args)) { + throw new Swift_IoException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($signedMessageStream, $inputStream); + } + + /** + * @param Swift_FileStream $outputStream + * @param Swift_InputByteStream $is + * + * @throws Swift_IoException + */ + protected function messageStreamToEncryptedByteStream(Swift_FileStream $outputStream, Swift_InputByteStream $is) + { + $encryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream(); + + if (!openssl_pkcs7_encrypt($outputStream->getPath(), $encryptedMessageStream->getPath(), $this->encryptCert, array(), 0, $this->encryptCipher)) { + throw new Swift_IoException(sprintf('Failed to encrypt S/Mime message. Error: "%s".', openssl_error_string())); + } + + $this->copyFromOpenSSLOutput($encryptedMessageStream, $is); + } + + /** + * @param Swift_OutputByteStream $fromStream + * @param Swift_InputByteStream $toStream + */ + protected function copyFromOpenSSLOutput(Swift_OutputByteStream $fromStream, Swift_InputByteStream $toStream) + { + $bufferLength = 4096; + $filteredStream = new Swift_ByteStream_TemporaryFileByteStream(); + $filteredStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $filteredStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $filteredStream->write($buffer); + } + + $filteredStream->flushBuffers(); + + while (false !== ($buffer = $filteredStream->read($bufferLength))) { + $toStream->write($buffer); + } + + $toStream->commit(); + } + + /** + * Merges an OutputByteStream to Swift_Message. + * + * @param Swift_OutputByteStream $fromStream + * @param Swift_Message $message + */ + protected function streamToMime(Swift_OutputByteStream $fromStream, Swift_Message $message) + { + $bufferLength = 78; + $headerData = ''; + + $fromStream->setReadPointer(0); + + while (($buffer = $fromStream->read($bufferLength)) !== false) { + $headerData .= $buffer; + + if (false !== strpos($buffer, "\r\n\r\n")) { + break; + } + } + + $headersPosEnd = strpos($headerData, "\r\n\r\n"); + $headerData = trim($headerData); + $headerData = substr($headerData, 0, $headersPosEnd); + $headerLines = explode("\r\n", $headerData); + unset($headerData); + + $headers = array(); + $currentHeaderName = ''; + + foreach ($headerLines as $headerLine) { + // Line separated + if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) { + $headers[$currentHeaderName] .= ' '.trim($headerLine); + continue; + } + + $header = explode(':', $headerLine, 2); + $currentHeaderName = strtolower($header[0]); + $headers[$currentHeaderName] = trim($header[1]); + } + + $messageStream = new Swift_ByteStream_TemporaryFileByteStream(); + $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF'); + $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF'); + + $messageHeaders = $message->getHeaders(); + + // No need to check for 'application/pkcs7-mime', as this is always base64 + if ('multipart/signed;' === substr($headers['content-type'], 0, 17)) { + if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $headers['content-type'], $contentTypeData)) { + throw new Swift_SwiftException('Failed to find Boundary parameter'); + } + + $boundary = trim($contentTypeData['1'], '"'); + $boundaryLen = strlen($boundary); + + // Skip the header and CRLF CRLF + $fromStream->setReadPointer($headersPosEnd + 4); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + + $messageHeaders->remove('Content-Transfer-Encoding'); + $message->setContentType($headers['content-type']); + $message->setBoundary($boundary); + $message->setBody($messageStream); + } else { + $fromStream->setReadPointer($headersPosEnd + 4); + + if (null === $this->headerFactory) { + $this->headerFactory = Swift_DependencyContainer::getInstance()->lookup('mime.headerfactory'); + } + + $message->setContentType($headers['content-type']); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Transfer-Encoding', $headers['content-transfer-encoding'])); + $messageHeaders->set($this->headerFactory->createTextHeader('Content-Disposition', $headers['content-disposition'])); + + while (false !== ($buffer = $fromStream->read($bufferLength))) { + $messageStream->write($buffer); + } + + $messageStream->commit(); + $message->setBody($messageStream); + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php new file mode 100644 index 0000000..6251611 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php @@ -0,0 +1,58 @@ +createDependenciesFor('transport.smtp') + ); + + $this->setHost($host); + $this->setPort($port); + $this->setEncryption($security); + } + + /** + * Create a new SmtpTransport instance. + * + * @param string $host + * @param int $port + * @param string $security + * + * @return Swift_SmtpTransport + */ + public static function newInstance($host = 'localhost', $port = 25, $security = null) + { + return new self($host, $port, $security); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php new file mode 100644 index 0000000..c16ab4b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for spools. + * + * @author Fabien Potencier + */ +interface Swift_Spool +{ + /** + * Starts this Spool mechanism. + */ + public function start(); + + /** + * Stops this Spool mechanism. + */ + public function stop(); + + /** + * Tests if this Spool mechanism has started. + * + * @return bool + */ + public function isStarted(); + + /** + * Queues a message. + * + * @param Swift_Mime_Message $message The message to store + * + * @return bool Whether the operation has succeeded + */ + public function queueMessage(Swift_Mime_Message $message); + + /** + * Sends messages using the given transport instance. + * + * @param Swift_Transport $transport A transport instance + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function flushQueue(Swift_Transport $transport, &$failedRecipients = null); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php new file mode 100644 index 0000000..cf9bf78 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_SpoolTransport extends Swift_Transport_SpoolTransport +{ + /** + * Create a new SpoolTransport. + * + * @param Swift_Spool $spool + */ + public function __construct(Swift_Spool $spool) + { + $arguments = Swift_DependencyContainer::getInstance() + ->createDependenciesFor('transport.spool'); + + $arguments[] = $spool; + + call_user_func_array( + array($this, 'Swift_Transport_SpoolTransport::__construct'), + $arguments + ); + } + + /** + * Create a new SpoolTransport instance. + * + * @param Swift_Spool $spool + * + * @return Swift_SpoolTransport + */ + public static function newInstance(Swift_Spool $spool) + { + return new self($spool); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php new file mode 100644 index 0000000..362be2e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php @@ -0,0 +1,35 @@ +_search = $search; + $this->_index = array(); + $this->_tree = array(); + $this->_replace = array(); + $this->_repSize = array(); + + $tree = null; + $i = null; + $last_size = $size = 0; + foreach ($search as $i => $search_element) { + if ($tree !== null) { + $tree[-1] = min(count($replace) - 1, $i - 1); + $tree[-2] = $last_size; + } + $tree = &$this->_tree; + if (is_array($search_element)) { + foreach ($search_element as $k => $char) { + $this->_index[$char] = true; + if (!isset($tree[$char])) { + $tree[$char] = array(); + } + $tree = &$tree[$char]; + } + $last_size = $k + 1; + $size = max($size, $last_size); + } else { + $last_size = 1; + if (!isset($tree[$search_element])) { + $tree[$search_element] = array(); + } + $tree = &$tree[$search_element]; + $size = max($last_size, $size); + $this->_index[$search_element] = true; + } + } + if ($i !== null) { + $tree[-1] = min(count($replace) - 1, $i); + $tree[-2] = $last_size; + $this->_treeMaxLen = $size; + } + foreach ($replace as $rep) { + if (!is_array($rep)) { + $rep = array($rep); + } + $this->_replace[] = $rep; + } + for ($i = count($this->_replace) - 1; $i >= 0; --$i) { + $this->_replace[$i] = $rep = $this->filter($this->_replace[$i], $i); + $this->_repSize[$i] = count($rep); + } + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param array $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = end($buffer); + + return isset($this->_index[$endOfBuffer]); + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param array $buffer + * @param int $_minReplaces + * + * @return array + */ + public function filter($buffer, $_minReplaces = -1) + { + if ($this->_treeMaxLen == 0) { + return $buffer; + } + + $newBuffer = array(); + $buf_size = count($buffer); + for ($i = 0; $i < $buf_size; ++$i) { + $search_pos = $this->_tree; + $last_found = PHP_INT_MAX; + // We try to find if the next byte is part of a search pattern + for ($j = 0; $j <= $this->_treeMaxLen; ++$j) { + // We have a new byte for a search pattern + if (isset($buffer [$p = $i + $j]) && isset($search_pos[$buffer[$p]])) { + $search_pos = $search_pos[$buffer[$p]]; + // We have a complete pattern, save, in case we don't find a better match later + if (isset($search_pos[-1]) && $search_pos[-1] < $last_found + && $search_pos[-1] > $_minReplaces) { + $last_found = $search_pos[-1]; + $last_size = $search_pos[-2]; + } + } + // We got a complete pattern + elseif ($last_found !== PHP_INT_MAX) { + // Adding replacement datas to output buffer + $rep_size = $this->_repSize[$last_found]; + for ($j = 0; $j < $rep_size; ++$j) { + $newBuffer[] = $this->_replace[$last_found][$j]; + } + // We Move cursor forward + $i += $last_size - 1; + // Edge Case, last position in buffer + if ($i >= $buf_size) { + $newBuffer[] = $buffer[$i]; + } + + // We start the next loop + continue 2; + } else { + // this byte is not in a pattern and we haven't found another pattern + break; + } + } + // Normal byte, move it to output buffer + $newBuffer[] = $buffer[$i]; + } + + return $newBuffer; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php new file mode 100644 index 0000000..d0db8b9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php @@ -0,0 +1,66 @@ +_search = $search; + $this->_replace = $replace; + } + + /** + * Returns true if based on the buffer passed more bytes should be buffered. + * + * @param string $buffer + * + * @return bool + */ + public function shouldBuffer($buffer) + { + $endOfBuffer = substr($buffer, -1); + foreach ((array) $this->_search as $needle) { + if (false !== strpos($needle, $endOfBuffer)) { + return true; + } + } + + return false; + } + + /** + * Perform the actual replacements on $buffer and return the result. + * + * @param string $buffer + * + * @return string + */ + public function filter($buffer) + { + return str_replace($this->_search, $this->_replace, $buffer); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php new file mode 100644 index 0000000..e98240b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php @@ -0,0 +1,45 @@ +_filters[$search][$replace])) { + if (!isset($this->_filters[$search])) { + $this->_filters[$search] = array(); + } + + if (!isset($this->_filters[$search][$replace])) { + $this->_filters[$search][$replace] = array(); + } + + $this->_filters[$search][$replace] = new Swift_StreamFilters_StringReplacementFilter($search, $replace); + } + + return $this->_filters[$search][$replace]; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php new file mode 100644 index 0000000..db3d310 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php @@ -0,0 +1,29 @@ +_eventDispatcher = $dispatcher; + $this->_buffer = $buf; + $this->_lookupHostname(); + } + + /** + * Set the name of the local domain which Swift will identify itself as. + * + * This should be a fully-qualified domain name and should be truly the domain + * you're using. + * + * If your server doesn't have a domain name, use the IP in square + * brackets (i.e. [127.0.0.1]). + * + * @param string $domain + * + * @return Swift_Transport_AbstractSmtpTransport + */ + public function setLocalDomain($domain) + { + $this->_domain = $domain; + + return $this; + } + + /** + * Get the name of the domain Swift will identify as. + * + * @return string + */ + public function getLocalDomain() + { + return $this->_domain; + } + + /** + * Sets the source IP. + * + * @param string $source + */ + public function setSourceIp($source) + { + $this->_sourceIp = $source; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return $this->_sourceIp; + } + + /** + * Start the SMTP connection. + */ + public function start() + { + if (!$this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->_buffer->initialize($this->_getBufferParams()); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_readGreeting(); + $this->_doHeloCommand(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStarted'); + } + + $this->_started = true; + } + } + + /** + * Test if an SMTP connection has been established. + * + * @return bool + */ + public function isStarted() + { + return $this->_started; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $sent = 0; + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (!$reversePath = $this->_getReversePath($message)) { + $this->_throwException(new Swift_TransportException( + 'Cannot send message without a sender address' + ) + ); + } + + $to = (array) $message->getTo(); + $cc = (array) $message->getCc(); + $tos = array_merge($to, $cc); + $bcc = (array) $message->getBcc(); + + $message->setBcc(array()); + + try { + $sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients); + $sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients); + } catch (Exception $e) { + $message->setBcc($bcc); + throw $e; + } + + $message->setBcc($bcc); + + if ($evt) { + if ($sent == count($to) + count($cc) + count($bcc)) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + } elseif ($sent > 0) { + $evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE); + } else { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + } + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); //Make sure a new Message ID is used + + return $sent; + } + + /** + * Stop the SMTP connection. + */ + public function stop() + { + if ($this->_started) { + if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped'); + if ($evt->bubbleCancelled()) { + return; + } + } + + try { + $this->executeCommand("QUIT\r\n", array(221)); + } catch (Swift_TransportException $e) { + } + + try { + $this->_buffer->terminate(); + + if ($evt) { + $this->_eventDispatcher->dispatchEvent($evt, 'transportStopped'); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + $this->_started = false; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** + * Reset the current mail transaction. + */ + public function reset() + { + $this->executeCommand("RSET\r\n", array(250)); + } + + /** + * Get the IoBuffer where read/writes are occurring. + * + * @return Swift_Transport_IoBuffer + */ + public function getBuffer() + { + return $this->_buffer; + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $seq = $this->_buffer->write($command); + $response = $this->_getFullResponse($seq); + if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) { + $this->_eventDispatcher->dispatchEvent($evt, 'commandSent'); + } + $this->_assertResponseCode($response, $codes); + + return $response; + } + + /** Read the opening SMTP greeting */ + protected function _readGreeting() + { + $this->_assertResponseCode($this->_getFullResponse(0), array(220)); + } + + /** Send the HELO welcome */ + protected function _doHeloCommand() + { + $this->executeCommand( + sprintf("HELO %s\r\n", $this->_domain), array(250) + ); + } + + /** Send the MAIL FROM command */ + protected function _doMailFromCommand($address) + { + $this->executeCommand( + sprintf("MAIL FROM:<%s>\r\n", $address), array(250) + ); + } + + /** Send the RCPT TO command */ + protected function _doRcptToCommand($address) + { + $this->executeCommand( + sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252) + ); + } + + /** Send the DATA command */ + protected function _doDataCommand() + { + $this->executeCommand("DATA\r\n", array(354)); + } + + /** Stream the contents of the message over the buffer */ + protected function _streamMessage(Swift_Mime_Message $message) + { + $this->_buffer->setWriteTranslations(array("\r\n." => "\r\n..")); + try { + $message->toByteStream($this->_buffer); + $this->_buffer->flushBuffers(); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + $this->_buffer->setWriteTranslations(array()); + $this->executeCommand("\r\n.\r\n", array(250)); + } + + /** Determine the best-use reverse path for this message */ + protected function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + // Don't use array_keys + reset($sender); // Reset Pointer to first pos + $path = key($sender); // Get key + } elseif (!empty($from)) { + reset($from); // Reset Pointer to first pos + $path = key($from); // Get key + } + + return $path; + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Throws an Exception if a response code is incorrect */ + protected function _assertResponseCode($response, $wanted) + { + list($code) = sscanf($response, '%3d'); + $valid = (empty($wanted) || in_array($code, $wanted)); + + if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response, + $valid)) { + $this->_eventDispatcher->dispatchEvent($evt, 'responseReceived'); + } + + if (!$valid) { + $this->_throwException( + new Swift_TransportException( + 'Expected response code '.implode('/', $wanted).' but got code '. + '"'.$code.'", with message "'.$response.'"', + $code) + ); + } + } + + /** Get an entire multi-line response using its sequence number */ + protected function _getFullResponse($seq) + { + $response = ''; + try { + do { + $line = $this->_buffer->readLine($seq); + $response .= $line; + } while (null !== $line && false !== $line && ' ' != $line{3}); + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } catch (Swift_IoException $e) { + $this->_throwException( + new Swift_TransportException( + $e->getMessage()) + ); + } + + return $response; + } + + /** Send an email to the given recipients from the given reverse path */ + private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients) + { + $sent = 0; + $this->_doMailFromCommand($reversePath); + foreach ($recipients as $forwardPath) { + try { + $this->_doRcptToCommand($forwardPath); + $sent++; + } catch (Swift_TransportException $e) { + $failedRecipients[] = $forwardPath; + } + } + + if ($sent != 0) { + $this->_doDataCommand(); + $this->_streamMessage($message); + } else { + $this->reset(); + } + + return $sent; + } + + /** Send a message to the given To: recipients */ + private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients) + { + if (empty($to)) { + return 0; + } + + return $this->_doMailTransaction($message, $reversePath, array_keys($to), + $failedRecipients); + } + + /** Send a message to all Bcc: recipients */ + private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients) + { + $sent = 0; + foreach ($bcc as $forwardPath => $name) { + $message->setBcc(array($forwardPath => $name)); + $sent += $this->_doMailTransaction( + $message, $reversePath, array($forwardPath), $failedRecipients + ); + } + + return $sent; + } + + /** Try to determine the hostname of the server this is run on */ + private function _lookupHostname() + { + if (!empty($_SERVER['SERVER_NAME']) + && $this->_isFqdn($_SERVER['SERVER_NAME'])) { + $this->_domain = $_SERVER['SERVER_NAME']; + } elseif (!empty($_SERVER['SERVER_ADDR'])) { + $this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']); + } + } + + /** Determine is the $hostname is a fully-qualified name */ + private function _isFqdn($hostname) + { + // We could do a really thorough check, but there's really no point + if (false !== $dotPos = strpos($hostname, '.')) { + return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1); + } else { + return false; + } + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->stop(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php new file mode 100644 index 0000000..53f721d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php @@ -0,0 +1,81 @@ +executeCommand("AUTH CRAM-MD5\r\n", array(334)); + $challenge = base64_decode(substr($challenge, 4)); + $message = base64_encode( + $username.' '.$this->_getResponse($password, $challenge) + ); + $agent->executeCommand(sprintf("%s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Generate a CRAM-MD5 response from a server challenge. + * + * @param string $secret + * @param string $challenge + * + * @return string + */ + private function _getResponse($secret, $challenge) + { + if (strlen($secret) > 64) { + $secret = pack('H32', md5($secret)); + } + + if (strlen($secret) < 64) { + $secret = str_pad($secret, 64, chr(0)); + } + + $k_ipad = substr($secret, 0, 64) ^ str_repeat(chr(0x36), 64); + $k_opad = substr($secret, 0, 64) ^ str_repeat(chr(0x5C), 64); + + $inner = pack('H32', md5($k_ipad.$challenge)); + $digest = md5($k_opad.$inner); + + return $digest; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php new file mode 100644 index 0000000..6ab6e33 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php @@ -0,0 +1,51 @@ +executeCommand("AUTH LOGIN\r\n", array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($username)), array(334)); + $agent->executeCommand(sprintf("%s\r\n", base64_encode($password)), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php new file mode 100644 index 0000000..f1102bb --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php @@ -0,0 +1,726 @@ + + */ +class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator +{ + const NTLMSIG = "NTLMSSP\x00"; + const DESCONST = 'KGS!@#$%'; + + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'NTLM'; + } + + /** + * Try to authenticate the user with $username and $password. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $username + * @param string $password + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password) + { + if (!function_exists('mcrypt_module_open')) { + throw new LogicException('The mcrypt functions need to be enabled to use the NTLM authenticator.'); + } + + if (!function_exists('openssl_random_pseudo_bytes')) { + throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.'); + } + + if (!function_exists('bcmul')) { + throw new LogicException('The BCMatch functions must be enabled to use the NTLM authenticator.'); + } + + try { + // execute AUTH command and filter out the code at the beginning + // AUTH NTLM xxxx + $response = base64_decode(substr(trim($this->sendMessage1($agent)), 4)); + + // extra parameters for our unit cases + $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000')); + $client = func_num_args() > 4 ? func_get_arg(4) : $this->getRandomBytes(8); + + // Message 3 response + $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + protected function si2bin($si, $bits = 32) + { + $bin = null; + if ($si >= -pow(2, $bits - 1) && ($si <= pow(2, $bits - 1))) { + // positive or zero + if ($si >= 0) { + $bin = base_convert($si, 10, 2); + // pad to $bits bit + $bin_length = strlen($bin); + if ($bin_length < $bits) { + $bin = str_repeat('0', $bits - $bin_length).$bin; + } + } else { + // negative + $si = -$si - pow(2, $bits); + $bin = base_convert($si, 10, 2); + $bin_length = strlen($bin); + if ($bin_length > $bits) { + $bin = str_repeat('1', $bits - $bin_length).$bin; + } + } + } + + return $bin; + } + + /** + * Send our auth message and returns the response. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return string SMTP Response + */ + protected function sendMessage1(Swift_Transport_SmtpAgent $agent) + { + $message = $this->createMessage1(); + + return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), array(334)); + } + + /** + * Fetch all details of our response (message 2). + * + * @param string $response + * + * @return array our response parsed + */ + protected function parseMessage2($response) + { + $responseHex = bin2hex($response); + $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2; + $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2; + $challenge = $this->hex2bin(substr($responseHex, 48, 16)); + $context = $this->hex2bin(substr($responseHex, 64, 16)); + $targetInfoH = $this->hex2bin(substr($responseHex, 80, 16)); + $targetName = $this->hex2bin(substr($responseHex, $offset, $length)); + $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2; + $targetInfoBlock = substr($responseHex, $offset); + list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock); + + return array( + $challenge, + $context, + $targetInfoH, + $targetName, + $domainName, + $serverName, + $DNSDomainName, + $DNSServerName, + $this->hex2bin($targetInfoBlock), + $terminatorByte, + ); + } + + /** + * Read the blob information in from message2. + * + * @param $block + * + * @return array + */ + protected function readSubBlock($block) + { + // remove terminatorByte cause it's always the same + $block = substr($block, 0, -8); + + $length = strlen($block); + $offset = 0; + $data = array(); + while ($offset < $length) { + $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256; + $offset += 8; + $data[] = $this->hex2bin(substr($block, $offset, $blockLength * 2)); + $offset += $blockLength * 2; + } + + if (count($data) == 3) { + $data[] = $data[2]; + $data[2] = ''; + } + + $data[] = $this->createByte('00'); + + return $data; + } + + /** + * Send our final message with all our data. + * + * @param string $response Message 1 response (message 2) + * @param string $username + * @param string $password + * @param string $timestamp + * @param string $client + * @param Swift_Transport_SmtpAgent $agent + * @param bool $v2 Use version2 of the protocol + * + * @return string + */ + protected function sendMessage3($response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true) + { + list($domain, $username) = $this->getDomainAndUsername($username); + //$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter + list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response); + + if (!$v2) { + // LMv1 + $lmResponse = $this->createLMPassword($password, $challenge); + // NTLMv1 + $ntlmResponse = $this->createNTLMPassword($password, $challenge); + } else { + // LMv2 + $lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client); + // NTLMv2 + $ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client); + } + + $message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse); + + return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), array(235)); + } + + /** + * Create our message 1. + * + * @return string + */ + protected function createMessage1() + { + return self::NTLMSIG + .$this->createByte('01') // Message 1 +.$this->createByte('0702'); // Flags + } + + /** + * Create our message 3. + * + * @param string $domain + * @param string $username + * @param string $workstation + * @param string $lmResponse + * @param string $ntlmResponse + * + * @return string + */ + protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse) + { + // Create security buffers + $domainSec = $this->createSecurityBuffer($domain, 64); + $domainInfo = $this->readSecurityBuffer(bin2hex($domainSec)); + $userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2); + $userInfo = $this->readSecurityBuffer(bin2hex($userSec)); + $workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2); + $workInfo = $this->readSecurityBuffer(bin2hex($workSec)); + $lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true); + $lmInfo = $this->readSecurityBuffer(bin2hex($lmSec)); + $ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true); + + return self::NTLMSIG + .$this->createByte('03') // TYPE 3 message +.$lmSec // LM response header +.$ntlmSec // NTLM response header +.$domainSec // Domain header +.$userSec // User header +.$workSec // Workstation header +.$this->createByte('000000009a', 8) // session key header (empty) +.$this->createByte('01020000') // FLAGS +.$this->convertTo16bit($domain) // domain name +.$this->convertTo16bit($username) // username +.$this->convertTo16bit($workstation) // workstation +.$lmResponse + .$ntlmResponse; + } + + /** + * @param string $timestamp Epoch timestamp in microseconds + * @param string $client Random bytes + * @param string $targetInfo + * + * @return string + */ + protected function createBlob($timestamp, $client, $targetInfo) + { + return $this->createByte('0101') + .$this->createByte('00') + .$timestamp + .$client + .$this->createByte('00') + .$targetInfo + .$this->createByte('00'); + } + + /** + * Get domain and username from our username. + * + * @example DOMAIN\username + * + * @param string $name + * + * @return array + */ + protected function getDomainAndUsername($name) + { + if (strpos($name, '\\') !== false) { + return explode('\\', $name); + } + + list($user, $domain) = explode('@', $name); + + return array($domain, $user); + } + + /** + * Create LMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createLMPassword($password, $challenge) + { + // FIRST PART + $password = $this->createByte(strtoupper($password), 14, false); + list($key1, $key2) = str_split($password, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + + $constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1).$this->desEncrypt(self::DESCONST, $desKey2), 21, false); + + // SECOND PART + list($key1, $key2, $key3) = str_split($constantDecrypt, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Create NTLMv1 response. + * + * @param string $password + * @param string $challenge + * + * @return string + */ + protected function createNTLMPassword($password, $challenge) + { + // FIRST PART + $ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false); + list($key1, $key2, $key3) = str_split($ntlmHash, 7); + + $desKey1 = $this->createDesKey($key1); + $desKey2 = $this->createDesKey($key2); + $desKey3 = $this->createDesKey($key3); + + return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3); + } + + /** + * Convert a normal timestamp to a tenth of a microtime epoch time. + * + * @param string $time + * + * @return string + */ + protected function getCorrectTimestamp($time) + { + // Get our timestamp (tricky!) + bcscale(0); + + $time = number_format($time, 0, '.', ''); // save microtime to string + $time = bcadd($time, '11644473600000'); // add epoch time + $time = bcmul($time, 10000); // tenths of a microsecond. + + $binary = $this->si2bin($time, 64); // create 64 bit binary string + $timestamp = ''; + for ($i = 0; $i < 8; $i++) { + $timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8))); + } + + return $timestamp; + } + + /** + * Create LMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge NTLM Challenge + * @param string $client Random string + * + * @return string + */ + protected function createLMv2Password($password, $username, $domain, $challenge, $client) + { + $lmPass = '00'; // by default 00 + // if $password > 15 than we can't use this method + if (strlen($password) <= 15) { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + $lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge.$client).$client); + } + + return $this->createByte($lmPass, 24); + } + + /** + * Create NTLMv2 response. + * + * @param string $password + * @param string $username + * @param string $domain + * @param string $challenge Hex values + * @param string $targetInfo Hex values + * @param string $timestamp + * @param string $client Random bytes + * + * @return string + * + * @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse + */ + protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client) + { + $ntlmHash = $this->md4Encrypt($password); + $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain)); + + // create blob + $blob = $this->createBlob($timestamp, $client, $targetInfo); + + $ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge.$blob); + + return $ntlmv2Response.$blob; + } + + protected function createDesKey($key) + { + $material = array(bin2hex($key[0])); + $len = strlen($key); + for ($i = 1; $i < $len; $i++) { + list($high, $low) = str_split(bin2hex($key[$i])); + $v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xf)), $i)); + $material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte + } + $material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0'); + + // odd parity + foreach ($material as $k => $v) { + $b = $this->castToByte(hexdec($v)); + $needsParity = (($this->uRShift($b, 7) ^ $this->uRShift($b, 6) ^ $this->uRShift($b, 5) + ^ $this->uRShift($b, 4) ^ $this->uRShift($b, 3) ^ $this->uRShift($b, 2) + ^ $this->uRShift($b, 1)) & 0x01) == 0; + + list($high, $low) = str_split($v); + if ($needsParity) { + $material[$k] = dechex(hexdec($high) | 0x0).dechex(hexdec($low) | 0x1); + } else { + $material[$k] = dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xe); + } + } + + return $this->hex2bin(implode('', $material)); + } + + /** HELPER FUNCTIONS */ + /** + * Create our security buffer depending on length and offset. + * + * @param string $value Value we want to put in + * @param int $offset start of value + * @param bool $is16 Do we 16bit string or not? + * + * @return string + */ + protected function createSecurityBuffer($value, $offset, $is16 = false) + { + $length = strlen(bin2hex($value)); + $length = $is16 ? $length / 2 : $length; + $length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2); + + return $length.$length.$this->createByte(dechex($offset), 4); + } + + /** + * Read our security buffer to fetch length and offset of our value. + * + * @param string $value Securitybuffer in hex + * + * @return array array with length and offset + */ + protected function readSecurityBuffer($value) + { + $length = floor(hexdec(substr($value, 0, 4)) / 256) * 2; + $offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2; + + return array($length, $offset); + } + + /** + * Cast to byte java equivalent to (byte). + * + * @param int $v + * + * @return int + */ + protected function castToByte($v) + { + return (($v + 128) % 256) - 128; + } + + /** + * Java unsigned right bitwise + * $a >>> $b. + * + * @param int $a + * @param int $b + * + * @return int + */ + protected function uRShift($a, $b) + { + if ($b == 0) { + return $a; + } + + return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1)); + } + + /** + * Right padding with 0 to certain length. + * + * @param string $input + * @param int $bytes Length of bytes + * @param bool $isHex Did we provided hex value + * + * @return string + */ + protected function createByte($input, $bytes = 4, $isHex = true) + { + if ($isHex) { + $byte = $this->hex2bin(str_pad($input, $bytes * 2, '00')); + } else { + $byte = str_pad($input, $bytes, "\x00"); + } + + return $byte; + } + + /** + * Create random bytes. + * + * @param $length + * + * @return string + */ + protected function getRandomBytes($length) + { + $bytes = openssl_random_pseudo_bytes($length, $strong); + + if (false !== $bytes && true === $strong) { + return $bytes; + } + + throw new RuntimeException('OpenSSL did not produce a secure random number.'); + } + + /** ENCRYPTION ALGORITHMS */ + /** + * DES Encryption. + * + * @param string $value + * @param string $key + * + * @return string + */ + protected function desEncrypt($value, $key) + { + $cipher = mcrypt_module_open(MCRYPT_DES, '', 'ecb', ''); + mcrypt_generic_init($cipher, $key, mcrypt_create_iv(mcrypt_enc_get_iv_size($cipher), MCRYPT_DEV_RANDOM)); + + return mcrypt_generic($cipher, $value); + } + + /** + * MD5 Encryption. + * + * @param string $key Encryption key + * @param string $msg Message to encrypt + * + * @return string + */ + protected function md5Encrypt($key, $msg) + { + $blocksize = 64; + if (strlen($key) > $blocksize) { + $key = pack('H*', md5($key)); + } + + $key = str_pad($key, $blocksize, "\0"); + $ipadk = $key ^ str_repeat("\x36", $blocksize); + $opadk = $key ^ str_repeat("\x5c", $blocksize); + + return pack('H*', md5($opadk.pack('H*', md5($ipadk.$msg)))); + } + + /** + * MD4 Encryption. + * + * @param string $input + * + * @return string + * + * @see http://php.net/manual/en/ref.hash.php + */ + protected function md4Encrypt($input) + { + $input = $this->convertTo16bit($input); + + return function_exists('hash') ? $this->hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input); + } + + /** + * Convert UTF-8 to UTF-16. + * + * @param string $input + * + * @return string + */ + protected function convertTo16bit($input) + { + return iconv('UTF-8', 'UTF-16LE', $input); + } + + /** + * Hex2bin replacement for < PHP 5.4. + * + * @param string $hex + * + * @return string Binary + */ + protected function hex2bin($hex) + { + if (function_exists('hex2bin')) { + return hex2bin($hex); + } else { + return pack('H*', $hex); + } + } + + /** + * @param string $message + */ + protected function debug($message) + { + $message = bin2hex($message); + $messageId = substr($message, 16, 8); + echo substr($message, 0, 16)." NTLMSSP Signature
    \n"; + echo $messageId." Type Indicator
    \n"; + + if ($messageId == '02000000') { + $map = array( + 'Challenge', + 'Context', + 'Target Information Security Buffer', + 'Target Name Data', + 'NetBIOS Domain Name', + 'NetBIOS Server Name', + 'DNS Domain Name', + 'DNS Server Name', + 'BLOB', + 'Target Information Terminator', + ); + + $data = $this->parseMessage2($this->hex2bin($message)); + + foreach ($map as $key => $value) { + echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."
    \n"; + } + } elseif ($messageId == '03000000') { + $i = 0; + $data[$i++] = substr($message, 24, 16); + list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 40, 16); + list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 56, 16); + list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 72, 16); + list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 88, 16); + list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]); + + $data[$i++] = substr($message, 104, 16); + $data[$i++] = substr($message, 120, 8); + $data[$i++] = substr($message, $targetOffset, $targetLength); + $data[$i++] = substr($message, $userOffset, $userLength); + $data[$i++] = substr($message, $workOffset, $workLength); + $data[$i++] = substr($message, $lmOffset, $lmLength); + $data[$i] = substr($message, $ntmlOffset, $ntmlLength); + + $map = array( + 'LM Response Security Buffer', + 'NTLM Response Security Buffer', + 'Target Name Security Buffer', + 'User Name Security Buffer', + 'Workstation Name Security Buffer', + 'Session Key Security Buffer', + 'Flags', + 'Target Name Data', + 'User Name Data', + 'Workstation Name Data', + 'LM Response Data', + 'NTLM Response Data', + ); + + foreach ($map as $key => $value) { + echo $data[$key].' - '.$this->hex2bin($data[$key]).' ||| '.$value."
    \n"; + } + } + + echo '

    '; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php new file mode 100644 index 0000000..43219f9 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php @@ -0,0 +1,50 @@ +executeCommand(sprintf("AUTH PLAIN %s\r\n", $message), array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php new file mode 100644 index 0000000..ca35e7b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php @@ -0,0 +1,70 @@ + + * $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls') + * ->setAuthMode('XOAUTH2') + * ->setUsername('YOUR_EMAIL_ADDRESS') + * ->setPassword('YOUR_ACCESS_TOKEN'); + * + * + * @author xu.li + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol + */ +class Swift_Transport_Esmtp_Auth_XOAuth2Authenticator implements Swift_Transport_Esmtp_Authenticator +{ + /** + * Get the name of the AUTH mechanism this Authenticator handles. + * + * @return string + */ + public function getAuthKeyword() + { + return 'XOAUTH2'; + } + + /** + * Try to authenticate the user with $email and $token. + * + * @param Swift_Transport_SmtpAgent $agent + * @param string $email + * @param string $token + * + * @return bool + */ + public function authenticate(Swift_Transport_SmtpAgent $agent, $email, $token) + { + try { + $param = $this->constructXOAuth2Params($email, $token); + $agent->executeCommand('AUTH XOAUTH2 '.$param."\r\n", array(235)); + + return true; + } catch (Swift_TransportException $e) { + $agent->executeCommand("RSET\r\n", array(250)); + + return false; + } + } + + /** + * Construct the auth parameter. + * + * @see https://developers.google.com/google-apps/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism + */ + protected function constructXOAuth2Params($email, $token) + { + return base64_encode("user=$email\1auth=Bearer $token\1\1"); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php new file mode 100644 index 0000000..2b0e682 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php @@ -0,0 +1,263 @@ +setAuthenticators($authenticators); + } + + /** + * Set the Authenticators which can process a login request. + * + * @param Swift_Transport_Esmtp_Authenticator[] $authenticators + */ + public function setAuthenticators(array $authenticators) + { + $this->_authenticators = $authenticators; + } + + /** + * Get the Authenticators which can process a login request. + * + * @return Swift_Transport_Esmtp_Authenticator[] + */ + public function getAuthenticators() + { + return $this->_authenticators; + } + + /** + * Set the username to authenticate with. + * + * @param string $username + */ + public function setUsername($username) + { + $this->_username = $username; + } + + /** + * Get the username to authenticate with. + * + * @return string + */ + public function getUsername() + { + return $this->_username; + } + + /** + * Set the password to authenticate with. + * + * @param string $password + */ + public function setPassword($password) + { + $this->_password = $password; + } + + /** + * Get the password to authenticate with. + * + * @return string + */ + public function getPassword() + { + return $this->_password; + } + + /** + * Set the auth mode to use to authenticate. + * + * @param string $mode + */ + public function setAuthMode($mode) + { + $this->_auth_mode = $mode; + } + + /** + * Get the auth mode to use to authenticate. + * + * @return string + */ + public function getAuthMode() + { + return $this->_auth_mode; + } + + /** + * Get the name of the ESMTP extension this handles. + * + * @return bool + */ + public function getHandledKeyword() + { + return 'AUTH'; + } + + /** + * Set the parameters which the EHLO greeting indicated. + * + * @param string[] $parameters + */ + public function setKeywordParams(array $parameters) + { + $this->_esmtpParams = $parameters; + } + + /** + * Runs immediately after a EHLO has been issued. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + */ + public function afterEhlo(Swift_Transport_SmtpAgent $agent) + { + if ($this->_username) { + $count = 0; + foreach ($this->_getAuthenticatorsForAgent() as $authenticator) { + if (in_array(strtolower($authenticator->getAuthKeyword()), + array_map('strtolower', $this->_esmtpParams))) { + $count++; + if ($authenticator->authenticate($agent, $this->_username, $this->_password)) { + return; + } + } + } + throw new Swift_TransportException( + 'Failed to authenticate on SMTP server with username "'. + $this->_username.'" using '.$count.' possible authenticators' + ); + } + } + + /** + * Not used. + */ + public function getMailParams() + { + return array(); + } + + /** + * Not used. + */ + public function getRcptParams() + { + return array(); + } + + /** + * Not used. + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false) + { + } + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword) + { + return 0; + } + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods() + { + return array('setUsername', 'getUsername', 'setPassword', 'getPassword', 'setAuthMode', 'getAuthMode'); + } + + /** + * Not used. + */ + public function resetState() + { + } + + /** + * Returns the authenticator list for the given agent. + * + * @param Swift_Transport_SmtpAgent $agent + * + * @return array + */ + protected function _getAuthenticatorsForAgent() + { + if (!$mode = strtolower($this->_auth_mode)) { + return $this->_authenticators; + } + + foreach ($this->_authenticators as $authenticator) { + if (strtolower($authenticator->getAuthKeyword()) == $mode) { + return array($authenticator); + } + } + + throw new Swift_TransportException('Auth mode '.$mode.' is invalid'); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php new file mode 100644 index 0000000..12a9abf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php @@ -0,0 +1,35 @@ +. + * + * @return string[] + */ + public function getMailParams(); + + /** + * Get params which are appended to RCPT TO:<>. + * + * @return string[] + */ + public function getRcptParams(); + + /** + * Runs when a command is due to be sent. + * + * @param Swift_Transport_SmtpAgent $agent to read/write + * @param string $command to send + * @param int[] $codes expected in response + * @param string[] $failedRecipients to collect failures + * @param bool $stop to be set true by-reference if the command is now sent + */ + public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false); + + /** + * Returns +1, -1 or 0 according to the rules for usort(). + * + * This method is called to ensure extensions can be execute in an appropriate order. + * + * @param string $esmtpKeyword to compare with + * + * @return int + */ + public function getPriorityOver($esmtpKeyword); + + /** + * Returns an array of method names which are exposed to the Esmtp class. + * + * @return string[] + */ + public function exposeMixinMethods(); + + /** + * Tells this handler to clear any buffers and reset its state. + */ + public function resetState(); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php new file mode 100644 index 0000000..5b4f50c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php @@ -0,0 +1,386 @@ + 'tcp', + 'host' => 'localhost', + 'port' => 25, + 'timeout' => 30, + 'blocking' => 1, + 'tls' => false, + 'type' => Swift_Transport_IoBuffer::TYPE_SOCKET, + ); + + /** + * Creates a new EsmtpTransport using the given I/O buffer. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Transport_EsmtpHandler[] $extensionHandlers + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + $this->setExtensionHandlers($extensionHandlers); + } + + /** + * Set the host to connect to. + * + * @param string $host + * + * @return Swift_Transport_EsmtpTransport + */ + public function setHost($host) + { + $this->_params['host'] = $host; + + return $this; + } + + /** + * Get the host to connect to. + * + * @return string + */ + public function getHost() + { + return $this->_params['host']; + } + + /** + * Set the port to connect to. + * + * @param int $port + * + * @return Swift_Transport_EsmtpTransport + */ + public function setPort($port) + { + $this->_params['port'] = (int) $port; + + return $this; + } + + /** + * Get the port to connect to. + * + * @return int + */ + public function getPort() + { + return $this->_params['port']; + } + + /** + * Set the connection timeout. + * + * @param int $timeout seconds + * + * @return Swift_Transport_EsmtpTransport + */ + public function setTimeout($timeout) + { + $this->_params['timeout'] = (int) $timeout; + $this->_buffer->setParam('timeout', (int) $timeout); + + return $this; + } + + /** + * Get the connection timeout. + * + * @return int + */ + public function getTimeout() + { + return $this->_params['timeout']; + } + + /** + * Set the encryption type (tls or ssl). + * + * @param string $encryption + * + * @return Swift_Transport_EsmtpTransport + */ + public function setEncryption($encryption) + { + if ('tls' == $encryption) { + $this->_params['protocol'] = 'tcp'; + $this->_params['tls'] = true; + } else { + $this->_params['protocol'] = $encryption; + $this->_params['tls'] = false; + } + + return $this; + } + + /** + * Get the encryption type. + * + * @return string + */ + public function getEncryption() + { + return $this->_params['tls'] ? 'tls' : $this->_params['protocol']; + } + + /** + * Sets the source IP. + * + * @param string $source + * + * @return Swift_Transport_EsmtpTransport + */ + public function setSourceIp($source) + { + $this->_params['sourceIp'] = $source; + + return $this; + } + + /** + * Returns the IP used to connect to the destination. + * + * @return string + */ + public function getSourceIp() + { + return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null; + } + + /** + * Set ESMTP extension handlers. + * + * @param Swift_Transport_EsmtpHandler[] $handlers + * + * @return Swift_Transport_EsmtpTransport + */ + public function setExtensionHandlers(array $handlers) + { + $assoc = array(); + foreach ($handlers as $handler) { + $assoc[$handler->getHandledKeyword()] = $handler; + } + uasort($assoc, array($this, '_sortHandlers')); + $this->_handlers = $assoc; + $this->_setHandlerParams(); + + return $this; + } + + /** + * Get ESMTP extension handlers. + * + * @return Swift_Transport_EsmtpHandler[] + */ + public function getExtensionHandlers() + { + return array_values($this->_handlers); + } + + /** + * Run a command against the buffer, expecting the given response codes. + * + * If no response codes are given, the response will not be validated. + * If codes are given, an exception will be thrown on an invalid response. + * + * @param string $command + * @param int[] $codes + * @param string[] $failures An array of failures by-reference + * + * @return string + */ + public function executeCommand($command, $codes = array(), &$failures = null) + { + $failures = (array) $failures; + $stopSignal = false; + $response = null; + foreach ($this->_getActiveHandlers() as $handler) { + $response = $handler->onCommand( + $this, $command, $codes, $failures, $stopSignal + ); + if ($stopSignal) { + return $response; + } + } + + return parent::executeCommand($command, $codes, $failures); + } + + // -- Mixin invocation code + + /** Mixin handling method for ESMTP handlers */ + public function __call($method, $args) + { + foreach ($this->_handlers as $handler) { + if (in_array(strtolower($method), + array_map('strtolower', (array) $handler->exposeMixinMethods()) + )) { + $return = call_user_func_array(array($handler, $method), $args); + // Allow fluid method calls + if (is_null($return) && substr($method, 0, 3) == 'set') { + return $this; + } else { + return $return; + } + } + } + trigger_error('Call to undefined method '.$method, E_USER_ERROR); + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } + + /** Overridden to perform EHLO instead */ + protected function _doHeloCommand() + { + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + + if ($this->_params['tls']) { + try { + $this->executeCommand("STARTTLS\r\n", array(220)); + + if (!$this->_buffer->startTLS()) { + throw new Swift_TransportException('Unable to connect with TLS encryption'); + } + + try { + $response = $this->executeCommand( + sprintf("EHLO %s\r\n", $this->_domain), array(250) + ); + } catch (Swift_TransportException $e) { + return parent::_doHeloCommand(); + } + } catch (Swift_TransportException $e) { + $this->_throwException($e); + } + } + + $this->_capabilities = $this->_getCapabilities($response); + $this->_setHandlerParams(); + foreach ($this->_getActiveHandlers() as $handler) { + $handler->afterEhlo($this); + } + } + + /** Overridden to add Extension support */ + protected function _doMailFromCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getMailParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("MAIL FROM:<%s>%s\r\n", $address, $paramStr), array(250) + ); + } + + /** Overridden to add Extension support */ + protected function _doRcptToCommand($address) + { + $handlers = $this->_getActiveHandlers(); + $params = array(); + foreach ($handlers as $handler) { + $params = array_merge($params, (array) $handler->getRcptParams()); + } + $paramStr = !empty($params) ? ' '.implode(' ', $params) : ''; + $this->executeCommand( + sprintf("RCPT TO:<%s>%s\r\n", $address, $paramStr), array(250, 251, 252) + ); + } + + /** Determine ESMTP capabilities by function group */ + private function _getCapabilities($ehloResponse) + { + $capabilities = array(); + $ehloResponse = trim($ehloResponse); + $lines = explode("\r\n", $ehloResponse); + array_shift($lines); + foreach ($lines as $line) { + if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) { + $keyword = strtoupper($matches[1]); + $paramStr = strtoupper(ltrim($matches[2], ' =')); + $params = !empty($paramStr) ? explode(' ', $paramStr) : array(); + $capabilities[$keyword] = $params; + } + } + + return $capabilities; + } + + /** Set parameters which are used by each extension handler */ + private function _setHandlerParams() + { + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handler->setKeywordParams($this->_capabilities[$keyword]); + } + } + } + + /** Get ESMTP handlers which are currently ok to use */ + private function _getActiveHandlers() + { + $handlers = array(); + foreach ($this->_handlers as $keyword => $handler) { + if (array_key_exists($keyword, $this->_capabilities)) { + $handlers[] = $handler; + } + } + + return $handlers; + } + + /** Custom sort for extension handler ordering */ + private function _sortHandlers($a, $b) + { + return $a->getPriorityOver($b->getHandledKeyword()); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php new file mode 100644 index 0000000..59c7302 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php @@ -0,0 +1,85 @@ +_transports); + $sent = 0; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + + return $transport->send($message, $failedRecipients); + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in FailoverTransport failed, or no Transports available' + ); + } + + return $sent; + } + + protected function _getNextTransport() + { + if (!isset($this->_currentTransport)) { + $this->_currentTransport = parent::_getNextTransport(); + } + + return $this->_currentTransport; + } + + protected function _killCurrentTransport() + { + $this->_currentTransport = null; + parent::_killCurrentTransport(); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php new file mode 100644 index 0000000..af97adf --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php @@ -0,0 +1,67 @@ +_transports = $transports; + $this->_deadTransports = array(); + } + + /** + * Get $transports to delegate to. + * + * @return Swift_Transport[] + */ + public function getTransports() + { + return array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Test if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return count($this->_transports) > 0; + } + + /** + * Start this Transport mechanism. + */ + public function start() + { + $this->_transports = array_merge($this->_transports, $this->_deadTransports); + } + + /** + * Stop this Transport mechanism. + */ + public function stop() + { + foreach ($this->_transports as $transport) { + $transport->stop(); + } + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $maxTransports = count($this->_transports); + $sent = 0; + + for ($i = 0; $i < $maxTransports + && $transport = $this->_getNextTransport(); ++$i) { + try { + if (!$transport->isStarted()) { + $transport->start(); + } + if ($sent = $transport->send($message, $failedRecipients)) { + break; + } + } catch (Swift_TransportException $e) { + $this->_killCurrentTransport(); + } + } + + if (count($this->_transports) == 0) { + throw new Swift_TransportException( + 'All Transports in LoadBalancedTransport failed, or no Transports available' + ); + } + + return $sent; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + foreach ($this->_transports as $transport) { + $transport->registerPlugin($plugin); + } + } + + /** + * Rotates the transport list around and returns the first instance. + * + * @return Swift_Transport + */ + protected function _getNextTransport() + { + if ($next = array_shift($this->_transports)) { + $this->_transports[] = $next; + } + + return $next; + } + + /** + * Tag the currently used (top of stack) transport as dead/useless. + */ + protected function _killCurrentTransport() + { + if ($transport = array_pop($this->_transports)) { + try { + $transport->stop(); + } catch (Exception $e) { + } + $this->_deadTransports[] = $transport; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php new file mode 100644 index 0000000..77489ce --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/MailInvoker.php @@ -0,0 +1,32 @@ +_invoker = $invoker; + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Not used. + */ + public function isStarted() + { + return false; + } + + /** + * Not used. + */ + public function start() + { + } + + /** + * Not used. + */ + public function stop() + { + } + + /** + * Set the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @param string $params + * + * @return Swift_Transport_MailTransport + */ + public function setExtraParams($params) + { + $this->_extraParams = $params; + + return $this; + } + + /** + * Get the additional parameters used on the mail() function. + * + * This string is formatted for sprintf() where %s is the sender address. + * + * @return string + */ + public function getExtraParams() + { + return $this->_extraParams; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients who were accepted for delivery. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + $toHeader = $message->getHeaders()->get('To'); + $subjectHeader = $message->getHeaders()->get('Subject'); + + if (!$toHeader) { + $this->_throwException(new Swift_TransportException('Cannot send message without a recipient')); + } + $to = $toHeader->getFieldBody(); + $subject = $subjectHeader ? $subjectHeader->getFieldBody() : ''; + + $reversePath = $this->_getReversePath($message); + + // Remove headers that would otherwise be duplicated + $message->getHeaders()->remove('To'); + $message->getHeaders()->remove('Subject'); + + $messageStr = $message->toString(); + + $message->getHeaders()->set($toHeader); + $message->getHeaders()->set($subjectHeader); + + // Separate headers from body + if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) { + $headers = substr($messageStr, 0, $endHeaders)."\r\n"; //Keep last EOL + $body = substr($messageStr, $endHeaders + 4); + } else { + $headers = $messageStr."\r\n"; + $body = ''; + } + + unset($messageStr); + + if ("\r\n" != PHP_EOL) { + // Non-windows (not using SMTP) + $headers = str_replace("\r\n", PHP_EOL, $headers); + $body = str_replace("\r\n", PHP_EOL, $body); + } else { + // Windows, using SMTP + $headers = str_replace("\r\n.", "\r\n..", $headers); + $body = str_replace("\r\n.", "\r\n..", $body); + } + + if ($this->_invoker->mail($to, $subject, $body, $headers, + sprintf($this->_extraParams, $reversePath))) { + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + } else { + $failedRecipients = array_merge( + $failedRecipients, + array_keys((array) $message->getTo()), + array_keys((array) $message->getCc()), + array_keys((array) $message->getBcc()) + ); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + + $count = 0; + } + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } + + /** Throw a TransportException, first sending it to any listeners */ + protected function _throwException(Swift_TransportException $e) + { + if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) { + $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown'); + if (!$evt->bubbleCancelled()) { + throw $e; + } + } else { + throw $e; + } + } + + /** Determine the best-use reverse path for this message */ + private function _getReversePath(Swift_Mime_Message $message) + { + $return = $message->getReturnPath(); + $sender = $message->getSender(); + $from = $message->getFrom(); + $path = null; + if (!empty($return)) { + $path = $return; + } elseif (!empty($sender)) { + $keys = array_keys($sender); + $path = array_shift($keys); + } elseif (!empty($from)) { + $keys = array_keys($from); + $path = array_shift($keys); + } + + return $path; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php new file mode 100644 index 0000000..ad20e0e --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Pretends messages have been sent, but just ignores them. + * + * @author Fabien Potencier + */ +class Swift_Transport_NullTransport implements Swift_Transport +{ + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher) + { + $this->_eventDispatcher = $eventDispatcher; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent emails + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $count = ( + count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ); + + return $count; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php new file mode 100644 index 0000000..3ef7dec --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php @@ -0,0 +1,159 @@ + 30, + 'blocking' => 1, + 'command' => '/usr/sbin/sendmail -bs', + 'type' => Swift_Transport_IoBuffer::TYPE_PROCESS, + ); + + /** + * Create a new SendmailTransport with $buf for I/O. + * + * @param Swift_Transport_IoBuffer $buf + * @param Swift_Events_EventDispatcher $dispatcher + */ + public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher) + { + parent::__construct($buf, $dispatcher); + } + + /** + * Start the standalone SMTP session if running in -bs mode. + */ + public function start() + { + if (false !== strpos($this->getCommand(), ' -bs')) { + parent::start(); + } + } + + /** + * Set the command to invoke. + * + * If using -t mode you are strongly advised to include -oi or -i in the flags. + * For example: /usr/sbin/sendmail -oi -t + * Swift will append a -f flag if one is not present. + * + * The recommended mode is "-bs" since it is interactive and failure notifications + * are hence possible. + * + * @param string $command + * + * @return Swift_Transport_SendmailTransport + */ + public function setCommand($command) + { + $this->_params['command'] = $command; + + return $this; + } + + /** + * Get the sendmail command which will be invoked. + * + * @return string + */ + public function getCommand() + { + return $this->_params['command']; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * + * The return value is the number of recipients who were accepted for delivery. + * NOTE: If using 'sendmail -t' you will not be aware of any failures until + * they bounce (i.e. send() will always return 100% success). + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + $failedRecipients = (array) $failedRecipients; + $command = $this->getCommand(); + $buffer = $this->getBuffer(); + + if (false !== strpos($command, ' -t')) { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + if (false === strpos($command, ' -f')) { + $command .= ' -f'.escapeshellarg($this->_getReversePath($message)); + } + + $buffer->initialize(array_merge($this->_params, array('command' => $command))); + + if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) { + $buffer->setWriteTranslations(array("\r\n" => "\n", "\n." => "\n..")); + } else { + $buffer->setWriteTranslations(array("\r\n" => "\n")); + } + + $count = count((array) $message->getTo()) + + count((array) $message->getCc()) + + count((array) $message->getBcc()) + ; + $message->toByteStream($buffer); + $buffer->flushBuffers(); + $buffer->setWriteTranslations(array()); + $buffer->terminate(); + + if ($evt) { + $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS); + $evt->setFailedRecipients($failedRecipients); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + $message->generateId(); + } elseif (false !== strpos($command, ' -bs')) { + $count = parent::send($message, $failedRecipients); + } else { + $this->_throwException(new Swift_TransportException( + 'Unsupported sendmail command flags ['.$command.']. '. + 'Must be one of "-bs" or "-t" but can include additional flags.' + )); + } + + return $count; + } + + /** Get the params to initialize the buffer */ + protected function _getBufferParams() + { + return $this->_params; + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php new file mode 100644 index 0000000..21e629a --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SimpleMailInvoker.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Stores Messages in a queue. + * + * @author Fabien Potencier + */ +class Swift_Transport_SpoolTransport implements Swift_Transport +{ + /** The spool instance */ + private $_spool; + + /** The event dispatcher from the plugin API */ + private $_eventDispatcher; + + /** + * Constructor. + */ + public function __construct(Swift_Events_EventDispatcher $eventDispatcher, Swift_Spool $spool = null) + { + $this->_eventDispatcher = $eventDispatcher; + $this->_spool = $spool; + } + + /** + * Sets the spool object. + * + * @param Swift_Spool $spool + * + * @return Swift_Transport_SpoolTransport + */ + public function setSpool(Swift_Spool $spool) + { + $this->_spool = $spool; + + return $this; + } + + /** + * Get the spool object. + * + * @return Swift_Spool + */ + public function getSpool() + { + return $this->_spool; + } + + /** + * Tests if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted() + { + return true; + } + + /** + * Starts this Transport mechanism. + */ + public function start() + { + } + + /** + * Stops this Transport mechanism. + */ + public function stop() + { + } + + /** + * Sends the given message. + * + * @param Swift_Mime_Message $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int The number of sent e-mail's + */ + public function send(Swift_Mime_Message $message, &$failedRecipients = null) + { + if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { + $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); + if ($evt->bubbleCancelled()) { + return 0; + } + } + + $success = $this->_spool->queueMessage($message); + + if ($evt) { + $evt->setResult($success ? Swift_Events_SendEvent::RESULT_SPOOLED : Swift_Events_SendEvent::RESULT_FAILED); + $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); + } + + return 1; + } + + /** + * Register a plugin. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) + { + $this->_eventDispatcher->bindEventListener($plugin); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php new file mode 100644 index 0000000..84ebbee --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php @@ -0,0 +1,321 @@ +_replacementFactory = $replacementFactory; + } + + /** + * Perform any initialization needed, using the given $params. + * + * Parameters will vary depending upon the type of IoBuffer used. + * + * @param array $params + */ + public function initialize(array $params) + { + $this->_params = $params; + switch ($params['type']) { + case self::TYPE_PROCESS: + $this->_establishProcessConnection(); + break; + case self::TYPE_SOCKET: + default: + $this->_establishSocketConnection(); + break; + } + } + + /** + * Set an individual param on the buffer (e.g. switching to SSL). + * + * @param string $param + * @param mixed $value + */ + public function setParam($param, $value) + { + if (isset($this->_stream)) { + switch ($param) { + case 'timeout': + if ($this->_stream) { + stream_set_timeout($this->_stream, $value); + } + break; + + case 'blocking': + if ($this->_stream) { + stream_set_blocking($this->_stream, 1); + } + + } + } + $this->_params[$param] = $value; + } + + public function startTLS() + { + return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + } + + /** + * Perform any shutdown logic needed. + */ + public function terminate() + { + if (isset($this->_stream)) { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + fclose($this->_in); + fclose($this->_out); + proc_close($this->_stream); + break; + case self::TYPE_SOCKET: + default: + fclose($this->_stream); + break; + } + } + $this->_stream = null; + $this->_out = null; + $this->_in = null; + } + + /** + * Set an array of string replacements which should be made on data written + * to the buffer. + * + * This could replace LF with CRLF for example. + * + * @param string[] $replacements + */ + public function setWriteTranslations(array $replacements) + { + foreach ($this->_translations as $search => $replace) { + if (!isset($replacements[$search])) { + $this->removeFilter($search); + unset($this->_translations[$search]); + } + } + + foreach ($replacements as $search => $replace) { + if (!isset($this->_translations[$search])) { + $this->addFilter( + $this->_replacementFactory->createFilter($search, $replace), $search + ); + $this->_translations[$search] = true; + } + } + } + + /** + * Get a line of output (including any CRLF). + * + * The $sequence number comes from any writes and may or may not be used + * depending upon the implementation. + * + * @param int $sequence of last write to scan from + * + * @throws Swift_IoException + * + * @return string + */ + public function readLine($sequence) + { + if (isset($this->_out) && !feof($this->_out)) { + $line = fgets($this->_out); + if (strlen($line) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $line; + } + } + + /** + * Reads $length bytes from the stream into a string and moves the pointer + * through the stream by $length. + * + * If less bytes exist than are requested the remaining bytes are given instead. + * If no bytes are remaining at all, boolean false is returned. + * + * @param int $length + * + * @throws Swift_IoException + * + * @return string|bool + */ + public function read($length) + { + if (isset($this->_out) && !feof($this->_out)) { + $ret = fread($this->_out, $length); + if (strlen($ret) == 0) { + $metas = stream_get_meta_data($this->_out); + if ($metas['timed_out']) { + throw new Swift_IoException( + 'Connection to '. + $this->_getReadConnectionDescription(). + ' Timed Out' + ); + } + } + + return $ret; + } + } + + /** Not implemented */ + public function setReadPointer($byteOffset) + { + } + + /** Flush the stream contents */ + protected function _flush() + { + if (isset($this->_in)) { + fflush($this->_in); + } + } + + /** Write this bytes to the stream */ + protected function _commit($bytes) + { + if (isset($this->_in)) { + $bytesToWrite = strlen($bytes); + $totalBytesWritten = 0; + + while ($totalBytesWritten < $bytesToWrite) { + $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten)); + if (false === $bytesWritten || 0 === $bytesWritten) { + break; + } + + $totalBytesWritten += $bytesWritten; + } + + if ($totalBytesWritten > 0) { + return ++$this->_sequence; + } + } + } + + /** + * Establishes a connection to a remote server. + */ + private function _establishSocketConnection() + { + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $timeout = 15; + if (!empty($this->_params['timeout'])) { + $timeout = $this->_params['timeout']; + } + $options = array(); + if (!empty($this->_params['sourceIp'])) { + $options['socket']['bindto'] = $this->_params['sourceIp'].':0'; + } + $this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, stream_context_create($options)); + if (false === $this->_stream) { + throw new Swift_TransportException( + 'Connection could not be established with host '.$this->_params['host']. + ' ['.$errstr.' #'.$errno.']' + ); + } + if (!empty($this->_params['blocking'])) { + stream_set_blocking($this->_stream, 1); + } else { + stream_set_blocking($this->_stream, 0); + } + stream_set_timeout($this->_stream, $timeout); + $this->_in = &$this->_stream; + $this->_out = &$this->_stream; + } + + /** + * Opens a process for input/output. + */ + private function _establishProcessConnection() + { + $command = $this->_params['command']; + $descriptorSpec = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w'), + ); + $this->_stream = proc_open($command, $descriptorSpec, $pipes); + stream_set_blocking($pipes[2], 0); + if ($err = stream_get_contents($pipes[2])) { + throw new Swift_TransportException( + 'Process could not be started ['.$err.']' + ); + } + $this->_in = &$pipes[0]; + $this->_out = &$pipes[1]; + } + + private function _getReadConnectionDescription() + { + switch ($this->_params['type']) { + case self::TYPE_PROCESS: + return 'Process '.$this->_params['command']; + break; + + case self::TYPE_SOCKET: + default: + $host = $this->_params['host']; + if (!empty($this->_params['protocol'])) { + $host = $this->_params['protocol'].'://'.$host; + } + $host .= ':'.$this->_params['port']; + + return $host; + break; + } + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php new file mode 100644 index 0000000..4ae2412 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php @@ -0,0 +1,29 @@ + + */ +class Swift_Validate +{ + /** + * Grammar Object. + * + * @var Swift_Mime_Grammar + */ + private static $grammar = null; + + /** + * Checks if an e-mail address matches the current grammars. + * + * @param string $email + * + * @return bool + */ + public static function email($email) + { + if (self::$grammar === null) { + self::$grammar = Swift_DependencyContainer::getInstance() + ->lookup('mime.grammar'); + } + + return (bool) preg_match( + '/^'.self::$grammar->getDefinition('addr-spec').'$/D', + $email + ); + } +} diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php new file mode 100644 index 0000000..6023448 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php @@ -0,0 +1,23 @@ +register('cache') + ->asAliasOf('cache.array') + + ->register('tempdir') + ->asValue('/tmp') + + ->register('cache.null') + ->asSharedInstanceOf('Swift_KeyCache_NullKeyCache') + + ->register('cache.array') + ->asSharedInstanceOf('Swift_KeyCache_ArrayKeyCache') + ->withDependencies(array('cache.inputstream')) + + ->register('cache.disk') + ->asSharedInstanceOf('Swift_KeyCache_DiskKeyCache') + ->withDependencies(array('cache.inputstream', 'tempdir')) + + ->register('cache.inputstream') + ->asNewInstanceOf('Swift_KeyCache_SimpleKeyCacheInputStream') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php new file mode 100644 index 0000000..64d69d2 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php @@ -0,0 +1,9 @@ +register('message.message') + ->asNewInstanceOf('Swift_Message') + + ->register('message.mimepart') + ->asNewInstanceOf('Swift_MimePart') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php new file mode 100644 index 0000000..04f394b --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php @@ -0,0 +1,123 @@ +register('properties.charset') + ->asValue('utf-8') + + ->register('mime.grammar') + ->asSharedInstanceOf('Swift_Mime_Grammar') + + ->register('mime.message') + ->asNewInstanceOf('Swift_Mime_SimpleMessage') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.part') + ->asNewInstanceOf('Swift_Mime_MimePart') + ->withDependencies(array( + 'mime.headerset', + 'mime.qpcontentencoder', + 'cache', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.attachment') + ->asNewInstanceOf('Swift_Mime_Attachment') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.embeddedfile') + ->asNewInstanceOf('Swift_Mime_EmbeddedFile') + ->withDependencies(array( + 'mime.headerset', + 'mime.base64contentencoder', + 'cache', + 'mime.grammar', + )) + ->addConstructorValue($swift_mime_types) + + ->register('mime.headerfactory') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderFactory') + ->withDependencies(array( + 'mime.qpheaderencoder', + 'mime.rfc2231encoder', + 'mime.grammar', + 'properties.charset', + )) + + ->register('mime.headerset') + ->asNewInstanceOf('Swift_Mime_SimpleHeaderSet') + ->withDependencies(array('mime.headerfactory', 'properties.charset')) + + ->register('mime.qpheaderencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_QpHeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.base64headerencoder') + ->asNewInstanceOf('Swift_Mime_HeaderEncoder_Base64HeaderEncoder') + ->withDependencies(array('mime.charstream')) + + ->register('mime.charstream') + ->asNewInstanceOf('Swift_CharacterStream_NgCharacterStream') + ->withDependencies(array('mime.characterreaderfactory', 'properties.charset')) + + ->register('mime.bytecanonicalizer') + ->asSharedInstanceOf('Swift_StreamFilters_ByteArrayReplacementFilter') + ->addConstructorValue(array(array(0x0D, 0x0A), array(0x0D), array(0x0A))) + ->addConstructorValue(array(array(0x0A), array(0x0A), array(0x0D, 0x0A))) + + ->register('mime.characterreaderfactory') + ->asSharedInstanceOf('Swift_CharacterReaderFactory_SimpleCharacterReaderFactory') + + ->register('mime.safeqpcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoder') + ->withDependencies(array('mime.charstream', 'mime.bytecanonicalizer')) + + ->register('mime.rawcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_RawContentEncoder') + + ->register('mime.nativeqpcontentencoder') + ->withDependencies(array('properties.charset')) + ->asNewInstanceOf('Swift_Mime_ContentEncoder_NativeQpContentEncoder') + + ->register('mime.qpcontentencoderproxy') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_QpContentEncoderProxy') + ->withDependencies(array('mime.safeqpcontentencoder', 'mime.nativeqpcontentencoder', 'properties.charset')) + + ->register('mime.7bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('7bit') + ->addConstructorValue(true) + + ->register('mime.8bitcontentencoder') + ->asNewInstanceOf('Swift_Mime_ContentEncoder_PlainContentEncoder') + ->addConstructorValue('8bit') + ->addConstructorValue(true) + + ->register('mime.base64contentencoder') + ->asSharedInstanceOf('Swift_Mime_ContentEncoder_Base64ContentEncoder') + + ->register('mime.rfc2231encoder') + ->asNewInstanceOf('Swift_Encoder_Rfc2231Encoder') + ->withDependencies(array('mime.charstream')) + + // As of PHP 5.4.7, the quoted_printable_encode() function behaves correctly. + // see https://github.com/php/php-src/commit/18bb426587d62f93c54c40bf8535eb8416603629 + ->register('mime.qpcontentencoder') + ->asAliasOf(version_compare(phpversion(), '5.4.7', '>=') ? 'mime.qpcontentencoderproxy' : 'mime.safeqpcontentencoder') +; + +unset($swift_mime_types); diff --git a/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php new file mode 100644 index 0000000..77e432c --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php @@ -0,0 +1,76 @@ +register('transport.smtp') + ->asNewInstanceOf('Swift_Transport_EsmtpTransport') + ->withDependencies(array( + 'transport.buffer', + array('transport.authhandler'), + 'transport.eventdispatcher', + )) + + ->register('transport.sendmail') + ->asNewInstanceOf('Swift_Transport_SendmailTransport') + ->withDependencies(array( + 'transport.buffer', + 'transport.eventdispatcher', + )) + + ->register('transport.mail') + ->asNewInstanceOf('Swift_Transport_MailTransport') + ->withDependencies(array('transport.mailinvoker', 'transport.eventdispatcher')) + + ->register('transport.loadbalanced') + ->asNewInstanceOf('Swift_Transport_LoadBalancedTransport') + + ->register('transport.failover') + ->asNewInstanceOf('Swift_Transport_FailoverTransport') + + ->register('transport.spool') + ->asNewInstanceOf('Swift_Transport_SpoolTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.null') + ->asNewInstanceOf('Swift_Transport_NullTransport') + ->withDependencies(array('transport.eventdispatcher')) + + ->register('transport.mailinvoker') + ->asSharedInstanceOf('Swift_Transport_SimpleMailInvoker') + + ->register('transport.buffer') + ->asNewInstanceOf('Swift_Transport_StreamBuffer') + ->withDependencies(array('transport.replacementfactory')) + + ->register('transport.authhandler') + ->asNewInstanceOf('Swift_Transport_Esmtp_AuthHandler') + ->withDependencies(array( + array( + 'transport.crammd5auth', + 'transport.loginauth', + 'transport.plainauth', + 'transport.ntlmauth', + 'transport.xoauth2auth', + ), + )) + + ->register('transport.crammd5auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_CramMd5Authenticator') + + ->register('transport.loginauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_LoginAuthenticator') + + ->register('transport.plainauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_PlainAuthenticator') + + ->register('transport.xoauth2auth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_XOAuth2Authenticator') + + ->register('transport.ntlmauth') + ->asNewInstanceOf('Swift_Transport_Esmtp_Auth_NTLMAuthenticator') + + ->register('transport.eventdispatcher') + ->asNewInstanceOf('Swift_Events_SimpleEventDispatcher') + + ->register('transport.replacementfactory') + ->asSharedInstanceOf('Swift_StreamFilters_StringReplacementFilterFactory') +; diff --git a/vendor/swiftmailer/swiftmailer/lib/mime_types.php b/vendor/swiftmailer/swiftmailer/lib/mime_types.php new file mode 100644 index 0000000..2d7b98d --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/mime_types.php @@ -0,0 +1,1007 @@ + 'text/vnd.in3d.3dml', + '3ds' => 'image/x-3ds', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'appcache' => 'text/cache-manifest', + 'apr' => 'application/vnd.lotus-approach', + 'aps' => 'application/postscript', + 'arc' => 'application/x-freearc', + 'asc' => 'application/pgp-signature', + 'asf' => 'video/x-ms-asf', + 'asm' => 'text/x-asm', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'blb' => 'application/x-blorb', + 'blorb' => 'application/x-blorb', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'caf' => 'audio/x-caf', + 'cap' => 'application/vnd.tcpdump.pcap', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cb7' => 'application/x-cbr', + 'cba' => 'application/x-cbr', + 'cbr' => 'application/x-cbr', + 'cbt' => 'application/x-cbr', + 'cbz' => 'application/x-cbr', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfs' => 'application/x-cfs-compressed', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dart' => 'application/vnd.dart', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dbk' => 'application/docbook+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dgc' => 'application/x-dgc-compressed', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/x-apple-diskimage', + 'dmp' => 'application/vnd.tcpdump.pcap', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvb' => 'video/vnd.dvb.file', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'emf' => 'application/x-msmetafile', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'emz' => 'application/x-msmetafile', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esa' => 'application/vnd.osgi.subsystem', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'eva' => 'application/x-eva', + 'evy' => 'application/x-envoy', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'flac' => 'audio/x-flac', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gam' => 'application/x-tads', + 'gbr' => 'application/rpki-ghostbusters', + 'gca' => 'application/x-gca-compressed', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gml' => 'application/gml+xml', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gpx' => 'application/gpx+xml', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gramps' => 'application/x-gramps-xml', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxf' => 'application/gxf', + 'gxt' => 'application/vnd.geonext', + 'gz' => 'application/x-gzip', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ink' => 'application/inkml+xml', + 'inkml' => 'application/inkml+xml', + 'install' => 'application/x-install-instructions', + 'iota' => 'application/vnd.astraea-software.iota', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/x-iso9660-image', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'jsonml' => 'application/jsonml+json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'kpxx' => 'application/vnd.ds-keypoint', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/x-lzh-compressed', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'lnk' => 'application/x-ms-shortcut', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/x-lzh-compressed', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mar' => 'application/octet-stream', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'metalink' => 'application/metalink+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mft' => 'application/rpki-manifest', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mie' => 'application/x-mie', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mk3d' => 'video/x-matroska', + 'mka' => 'audio/x-matroska', + 'mks' => 'video/x-matroska', + 'mkv' => 'video/x-matroska', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mng' => 'video/x-mng', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'nfo' => 'text/x-nfo', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nitf' => 'application/vnd.nitf', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsc' => 'application/x-conference', + 'nsf' => 'application/vnd.lotus-notes', + 'ntf' => 'application/vnd.nitf', + 'nzb' => 'application/x-nzb', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'obj' => 'application/x-tgif', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'omdoc' => 'application/omdoc+xml', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'opml' => 'text/x-opml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxps' => 'application/oxps', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcap' => 'application/vnd.tcpdump.pcap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'ris' => 'application/x-research-info-systems', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rmvb' => 'application/vnd.rn-realmedia-vbr', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roa' => 'application/rpki-roa', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 's3m' => 'audio/s3m', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sfv' => 'text/x-sfv', + 'sgi' => 'image/sgi', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sid' => 'image/x-mrsid-image', + 'sig' => 'application/pgp-signature', + 'sil' => 'audio/silk', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'smv' => 'video/x-smv', + 'smzip' => 'application/vnd.stepmania.package', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'sql' => 'application/x-sql', + 'src' => 'application/x-wais-source', + 'srt' => 'application/x-subrip', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'ssdl' => 'application/ssdl+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'text/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 't3' => 'application/x-t3vm-image', + 'taglet' => 'application/vnd.mynfc', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'tga' => 'image/x-tga', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'ulx' => 'application/x-glulx', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvvz' => 'application/vnd.dece.zip', + 'uvx' => 'application/vnd.dece.unspecified', + 'uvz' => 'application/vnd.dece.zip', + 'vcard' => 'text/vcard', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vob' => 'video/x-ms-vob', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wdp' => 'image/vnd.ms-photo', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-msmetafile', + 'woff' => 'application/font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'model/x3d+xml', + 'x3db' => 'model/x3d+binary', + 'x3dbz' => 'model/x3d+binary', + 'x3dv' => 'model/x3d+vrml', + 'x3dvz' => 'model/x3d+vrml', + 'x3dz' => 'model/x3d+xml', + 'xaml' => 'application/xaml+xml', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlf' => 'application/x-xliff+xml', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xm' => 'audio/xm', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpl' => 'application/xproc+xml', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'xz' => 'application/x-xz', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'z1' => 'application/x-zmachine', + 'z2' => 'application/x-zmachine', + 'z3' => 'application/x-zmachine', + 'z4' => 'application/x-zmachine', + 'z5' => 'application/x-zmachine', + 'z6' => 'application/x-zmachine', + 'z7' => 'application/x-zmachine', + 'z8' => 'application/x-zmachine', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + '123' => 'application/vnd.lotus-1-2-3', +); diff --git a/vendor/swiftmailer/swiftmailer/lib/preferences.php b/vendor/swiftmailer/swiftmailer/lib/preferences.php new file mode 100644 index 0000000..e519501 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/preferences.php @@ -0,0 +1,25 @@ +setCharset('utf-8'); + +// Without these lines the default caching mechanism is "array" but this uses a lot of memory. +// If possible, use a disk cache to enable attaching large attachments etc. +// You can override the default temporary directory by setting the TMPDIR environment variable. +if (@is_writable($tmpDir = sys_get_temp_dir())) { + $preferences->setTempDir($tmpDir)->setCacheType('disk'); +} + +// this should only be done when Swiftmailer won't use the native QP content encoder +// see mime_deps.php +if (version_compare(phpversion(), '5.4.7', '<')) { + $preferences->setQPDotEscape(false); +} diff --git a/vendor/swiftmailer/swiftmailer/lib/swift_init.php b/vendor/swiftmailer/swiftmailer/lib/swift_init.php new file mode 100644 index 0000000..5c4bae4 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/lib/swift_init.php @@ -0,0 +1,28 @@ + 'application/x-php', + 'php3' => 'application/x-php', + 'php4' => 'application/x-php', + 'php5' => 'application/x-php', + 'zip' => 'application/zip', + 'gif' => 'image/gif', + 'png' => 'image/png', + 'css' => 'text/css', + 'js' => 'text/javascript', + 'txt' => 'text/plain', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'avi' => 'video/avi', + 'bmp' => 'image/bmp', + 'bz2' => 'application/x-bz2', + 'csv' => 'text/csv', + 'dmg' => 'application/x-apple-diskimage', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'eml' => 'message/rfc822', + 'aps' => 'application/postscript', + 'exe' => 'application/x-ms-dos-executable', + 'flv' => 'video/x-flv', + 'gz' => 'application/x-gzip', + 'hqx' => 'application/stuffit', + 'htm' => 'text/html', + 'html' => 'text/html', + 'jar' => 'application/x-java-archive', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/mp4', + 'mdb' => 'application/x-msaccess', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'odg' => 'vnd.oasis.opendocument.graphics', + 'odp' => 'vnd.oasis.opendocument.presentation', + 'odt' => 'vnd.oasis.opendocument.text', + 'ods' => 'vnd.oasis.opendocument.spreadsheet', + 'ogg' => 'audio/ogg', + 'pdf' => 'application/pdf', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'ps' => 'application/postscript', + 'rar' => 'application/x-rar-compressed', + 'rtf' => 'application/rtf', + 'tar' => 'application/x-tar', + 'sit' => 'application/x-stuffit', + 'svg' => 'image/svg+xml', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'ttf' => 'application/x-font-truetype', + 'vcf' => 'text/x-vcard', + 'wav' => 'audio/wav', + 'wma' => 'audio/x-ms-wma', + 'wmv' => 'audio/x-ms-wmv', + 'xls' => 'application/excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xml' => 'application/xml', + ); + + // wrap array for generating file + foreach ($valid_mime_types_preset as $extension => $mime_type) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + + // collect extensions + $valid_extensions = array(); + + // all extensions from second match + foreach ($matches[2] as $i => $extensions) { + // explode multiple extensions from string + $extensions = explode(' ', strtolower($extensions)); + + // force array for foreach + if (!is_array($extensions)) { + $extensions = array($extensions); + } + + foreach ($extensions as $extension) { + // get mime type + $mime_type = $matches[1][$i]; + + // check if string length lower than 10 + if (strlen($extension) < 10) { + // add extension + $valid_extensions[] = $extension; + + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + } + } + + $xml = simplexml_load_string($mime_xml); + + foreach ($xml as $node) { + // check if there is no pattern + if (!isset($node->glob['pattern'])) { + continue; + } + + // get all matching extensions from match + foreach ((array) $node->glob['pattern'] as $extension) { + // skip none glob extensions + if (strpos($extension, '.') === false) { + continue; + } + + // remove get only last part + $extension = explode('.', strtolower($extension)); + $extension = end($extension); + + // maximum length in database column + if (strlen($extension) <= 9) { + $valid_extensions[] = $extension; + } + } + + if (isset($node->glob['pattern'][0])) { + // mime type + $mime_type = strtolower((string) $node['type']); + + // get first extension + $extension = strtolower(trim($node->glob['ddpattern'][0], '*.')); + + // skip none glob extensions and check if string length between 1 and 10 + if (strpos($extension, '.') !== false || strlen($extension) < 1 || strlen($extension) > 9) { + continue; + } + + // check if string length lower than 10 + if (!isset($valid_mime_types[$mime_type])) { + // generate array for mimetype to extension resolver (only first match) + $valid_mime_types[$extension] = "'{$extension}' => '{$mime_type}'"; + } + } + } + + // full list of valid extensions only + $valid_mime_types = array_unique($valid_mime_types); + ksort($valid_mime_types); + + // combine mime types and extensions array + $output = "$preamble\$swift_mime_types = array(\n ".implode($valid_mime_types, ",\n ")."\n);"; + + // write mime_types.php config file + @file_put_contents('./mime_types.php', $output); +} + +generateUpToDateMimeArray(); diff --git a/vendor/swiftmailer/swiftmailer/phpunit.xml.dist b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist new file mode 100644 index 0000000..2420586 --- /dev/null +++ b/vendor/swiftmailer/swiftmailer/phpunit.xml.dist @@ -0,0 +1,37 @@ + + + + + + + + + + + tests/unit + + + tests/acceptance + + + tests/bug + + + tests/smoke + + + + + + + diff --git a/vendor/symfony/monolog-bundle/.travis.yml b/vendor/symfony/monolog-bundle/.travis.yml new file mode 100644 index 0000000..5cb3e44 --- /dev/null +++ b/vendor/symfony/monolog-bundle/.travis.yml @@ -0,0 +1,42 @@ +sudo: false + +language: php + +cache: + directories: + - $HOME/.composer/cache/files + +php: + - 5.3 + - 5.4 + - 5.5 + - 7.0 + - hhvm + +matrix: + fast_finish: true + include: + - php: 5.3 + env: COMPOSER_FLAGS="--prefer-lowest" SYMFONY_DEPRECATIONS_HELPER=weak + # Test against Symfony LTS versions + - php: 5.6 + env: SYMFONY_VERSION="2.3.*" + - php: 5.6 + env: SYMFONY_VERSION="2.7.*" + - php: 5.6 + env: SYMFONY_VERSION="2.8.*" DEPENDENCIES=dev + # Test against dev versions + - php: 5.6 + env: DEPENDENCIES=dev + allow_failures: + - env: DEPENDENCIES=dev + +before_install: + - composer self-update + - if [ "$DEPENDENCIES" = "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi; + - if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony:"$SYMFONY_VERSION"; fi + +install: + - composer update $COMPOSER_FLAGS + +script: phpunit -v --coverage-text diff --git a/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddProcessorsPass.php b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddProcessorsPass.php new file mode 100644 index 0000000..21b51a9 --- /dev/null +++ b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddProcessorsPass.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers processors in Monolog loggers or handlers. + * + * @author Christophe Coevoet + */ +class AddProcessorsPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('monolog.logger')) { + return; + } + + foreach ($container->findTaggedServiceIds('monolog.processor') as $id => $tags) { + foreach ($tags as $tag) { + if (!empty($tag['channel']) && !empty($tag['handler'])) { + throw new \InvalidArgumentException(sprintf('you cannot specify both the "handler" and "channel" attributes for the "monolog.processor" tag on service "%s"', $id)); + } + + if (!empty($tag['handler'])) { + $definition = $container->findDefinition(sprintf('monolog.handler.%s', $tag['handler'])); + } elseif (!empty($tag['channel'])) { + if ('app' === $tag['channel']) { + $definition = $container->getDefinition('monolog.logger'); + } else { + $definition = $container->getDefinition(sprintf('monolog.logger.%s', $tag['channel'])); + } + } else { + $definition = $container->getDefinition('monolog.logger_prototype'); + } + + if (!empty($tag['method'])) { + $processor = array(new Reference($id), $tag['method']); + } else { + // If no method is defined, fallback to use __invoke + $processor = new Reference($id); + } + $definition->addMethodCall('pushProcessor', array($processor)); + } + } + } +} diff --git a/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddSwiftMailerTransportPass.php b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddSwiftMailerTransportPass.php new file mode 100644 index 0000000..bee1d19 --- /dev/null +++ b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/AddSwiftMailerTransportPass.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Sets the transport for Swiftmailer handlers depending on the existing + * container definitions. + * + * @author Christian Flothmann + */ +class AddSwiftMailerTransportPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $handlers = $container->getParameter('monolog.swift_mailer.handlers'); + + foreach ($handlers as $id) { + $definition = $container->getDefinition($id); + $mailerId = (string) $definition->getArgument(0); + + // Try to fetch the transport for a non-default mailer first, then go with the default swiftmailer + $possibleServices = array( + $mailerId.'.transport.real', + $mailerId.'.transport', + 'swiftmailer.transport.real', + 'swiftmailer.transport', + ); + + foreach ($possibleServices as $serviceId) { + if ($container->hasAlias($serviceId) || $container->hasDefinition($serviceId)) { + $definition->addMethodCall( + 'setTransport', + array(new Reference($serviceId)) + ); + + break; + } + } + } + } +} diff --git a/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/DebugHandlerPass.php b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/DebugHandlerPass.php new file mode 100644 index 0000000..0cca2f7 --- /dev/null +++ b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/DebugHandlerPass.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Definition; +use Monolog\Logger; + +/** + * Adds the DebugHandler when the profiler is enabled and kernel.debug is true. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class DebugHandlerPass implements CompilerPassInterface +{ + private $channelPass; + + public function __construct(LoggerChannelPass $channelPass) + { + $this->channelPass = $channelPass; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('profiler')) { + return; + } + + if (!$container->getParameter('kernel.debug')) { + return; + } + + $debugHandler = new Definition('%monolog.handler.debug.class%', array(Logger::DEBUG, true)); + $container->setDefinition('monolog.handler.debug', $debugHandler); + + foreach ($this->channelPass->getChannels() as $channel) { + $container + ->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel) + ->addMethodCall('pushHandler', array(new Reference('monolog.handler.debug'))); + } + } +} diff --git a/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/LoggerChannelPass.php b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/LoggerChannelPass.php new file mode 100644 index 0000000..9f1aba1 --- /dev/null +++ b/vendor/symfony/monolog-bundle/DependencyInjection/Compiler/LoggerChannelPass.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Replaces the default logger by another one with its own channel for tagged services. + * + * @author Christophe Coevoet + */ +class LoggerChannelPass implements CompilerPassInterface +{ + protected $channels = array('app'); + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('monolog.logger')) { + return; + } + + // create channels necessary for the handlers + foreach ($container->findTaggedServiceIds('monolog.logger') as $id => $tags) { + foreach ($tags as $tag) { + if (empty($tag['channel']) || 'app' === $tag['channel']) { + continue; + } + + $resolvedChannel = $container->getParameterBag()->resolveValue($tag['channel']); + + $definition = $container->getDefinition($id); + $loggerId = sprintf('monolog.logger.%s', $resolvedChannel); + $this->createLogger($resolvedChannel, $loggerId, $container); + + foreach ($definition->getArguments() as $index => $argument) { + if ($argument instanceof Reference && 'logger' === (string) $argument) { + $definition->replaceArgument($index, $this->changeReference($argument, $loggerId)); + } + } + + $calls = $definition->getMethodCalls(); + foreach ($calls as $i => $call) { + foreach ($call[1] as $index => $argument) { + if ($argument instanceof Reference && 'logger' === (string) $argument) { + $calls[$i][1][$index] = $this->changeReference($argument, $loggerId); + } + } + } + $definition->setMethodCalls($calls); + } + } + + // create additional channels + foreach ($container->getParameter('monolog.additional_channels') as $chan) { + $loggerId = sprintf('monolog.logger.%s', $chan); + $this->createLogger($chan, $loggerId, $container); + } + $container->getParameterBag()->remove('monolog.additional_channels'); + + // wire handlers to channels + $handlersToChannels = $container->getParameter('monolog.handlers_to_channels'); + foreach ($handlersToChannels as $handler => $channels) { + foreach ($this->processChannels($channels) as $channel) { + try { + $logger = $container->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel); + } catch (InvalidArgumentException $e) { + $msg = 'Monolog configuration error: The logging channel "'.$channel.'" assigned to the "'.substr($handler, 16).'" handler does not exist.'; + throw new \InvalidArgumentException($msg, 0, $e); + } + $logger->addMethodCall('pushHandler', array(new Reference($handler))); + } + } + } + + public function getChannels() + { + return $this->channels; + } + + protected function processChannels($configuration) + { + if (null === $configuration) { + return $this->channels; + } + + if ('inclusive' === $configuration['type']) { + return $configuration['elements'] ?: $this->channels; + } + + return array_diff($this->channels, $configuration['elements']); + } + + protected function createLogger($channel, $loggerId, ContainerBuilder $container) + { + if (!in_array($channel, $this->channels)) { + $logger = new DefinitionDecorator('monolog.logger_prototype'); + $logger->replaceArgument(0, $channel); + $container->setDefinition($loggerId, $logger); + $this->channels[] = $channel; + } + } + + /** + * Creates a copy of a reference and alters the service ID. + * + * @param Reference $reference + * @param string $serviceId + * + * @return Reference + */ + private function changeReference(Reference $reference, $serviceId) + { + if (method_exists($reference, 'isStrict')) { + // Stay compatible with Symfony 2 + return new Reference($serviceId, $reference->getInvalidBehavior(), $reference->isStrict(false)); + } + + return new Reference($serviceId, $reference->getInvalidBehavior()); + } +} diff --git a/vendor/symfony/monolog-bundle/DependencyInjection/Configuration.php b/vendor/symfony/monolog-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..0767f5f --- /dev/null +++ b/vendor/symfony/monolog-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,734 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This class contains the configuration information for the bundle + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * Possible handler types and related configurations (brackets indicate optional params): + * + * - service: + * - id + * + * - stream: + * - path: string + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - console: + * - [verbosity_levels]: level => verbosity configuration + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - firephp: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - browser_console: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - gelf: + * - publisher: {id: ...} or {hostname: ..., port: ..., chunk_size: ...} + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - chromephp: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - rotating_file: + * - path: string + * - [max_files]: files to keep, defaults to zero (infinite) + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - mongo: + * - mongo: + * - id: optional if host is given + * - host: database host name, optional if id is given + * - [port]: defaults to 27017 + * - [user]: database user name + * - pass: mandatory only if user is present + * - [database]: defaults to monolog + * - [collection]: defaults to logs + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - elasticsearch: + * - elasticsearch: + * - id: optional if host is given + * - host: elastic search host name + * - [port]: defaults to 9200 + * - [index]: index name, defaults to monolog + * - [document_type]: document_type, defaults to logs + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - fingers_crossed: + * - handler: the wrapped handler's name + * - [action_level|activation_strategy]: minimum level or service id to activate the handler, defaults to WARNING + * - [excluded_404s]: if set, the strategy will be changed to one that excludes 404s coming from URLs matching any of those patterns + * - [buffer_size]: defaults to 0 (unlimited) + * - [stop_buffering]: bool to disable buffering once the handler has been activated, defaults to true + * - [passthru_level]: level name or int value for messages to always flush, disabled by default + * - [bubble]: bool, defaults to true + * + * - filter: + * - handler: the wrapped handler's name + * - [accepted_levels]: list of levels to accept + * - [min_level]: minimum level to accept (only used if accepted_levels not specified) + * - [max_level]: maximum level to accept (only used if accepted_levels not specified) + * - [bubble]: bool, defaults to true + * + * - buffer: + * - handler: the wrapped handler's name + * - [buffer_size]: defaults to 0 (unlimited) + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * - [flush_on_overflow]: bool, defaults to false + * + * - group: + * - members: the wrapped handlers by name + * - [bubble]: bool, defaults to true + * + * - whatfailuregroup: + * - members: the wrapped handlers by name + * - [bubble]: bool, defaults to true + * + * - syslog: + * - ident: string + * - [facility]: defaults to LOG_USER + * - [logopts]: defaults to LOG_PID + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - syslogudp: + * - host: syslogd host name + * - [port]: defaults to 514 + * - [facility]: defaults to LOG_USER + * - [logopts]: defaults to LOG_PID + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - swift_mailer: + * - from_email: optional if email_prototype is given + * - to_email: optional if email_prototype is given + * - subject: optional if email_prototype is given + * - [email_prototype]: service id of a message, defaults to a default message with the three fields above + * - [content_type]: optional if email_prototype is given, defaults to text/plain + * - [mailer]: mailer service, defaults to mailer + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * - [lazy]: use service lazy loading, bool, defaults to true + * + * - native_mailer: + * - from_email: string + * - to_email: string + * - subject: string + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - socket: + * - connection_string: string + * - [timeout]: float + * - [connection_timeout]: float + * - [persistent]: bool + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - pushover: + * - token: pushover api token + * - user: user id or array of ids + * - [title]: optional title for messages, defaults to the server hostname + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - raven: + * - dsn: connection string + * - client_id: Raven client custom service id (optional) + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - newrelic: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - hipchat: + * - token: hipchat api token + * - room: room id or name + * - [notify]: defaults to false + * - [nickname]: defaults to Monolog + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * - [use_ssl]: bool, defaults to true + * - [message_format]: text or html, defaults to text + * + * - slack: + * - token: slack api token + * - channel: channel name + * - [bot_name]: defaults to Monolog + * - [icon_emoji]: defaults to null + * - [use_attachment]: bool, defaults to true + * - [use_short_attachment]: bool, defaults to false + * - [include_extra]: bool, defaults to false + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - cube: + * - url: http/udp url to the cube server + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - amqp: + * - exchange: service id of an AMQPExchange + * - [exchange_name]: string, defaults to log + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - error_log: + * - [message_type]: int 0 or 4, defaults to 0 + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - null: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - test: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - debug: + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - loggly: + * - token: loggly api token + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * - [tags]: tag names + * + * - logentries: + * - token: logentries api token + * - [use_ssl]: whether or not SSL encryption should be used, defaults to true + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - flowdock: + * - token: flowdock api token + * - source: human readable identifier of the application + * - from_email: email address of the message sender + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * - rollbar: + * - id: RollbarNotifier service (mandatory if token is not provided) + * - token: rollbar api token (skip if you provide a RollbarNotifier service id) + * - [config]: config values from https://github.com/rollbar/rollbar-php#configuration-reference + * - [level]: level name or int value, defaults to DEBUG + * - [bubble]: bool, defaults to true + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class Configuration implements ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('monolog'); + + $rootNode + ->fixXmlConfig('channel') + ->fixXmlConfig('handler') + ->children() + ->arrayNode('channels') + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->arrayNode('handlers') + ->canBeUnset() + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('member') + ->fixXmlConfig('excluded_404') + ->fixXmlConfig('tag') + ->fixXmlConfig('accepted_level') + ->canBeUnset() + ->children() + ->scalarNode('type') + ->isRequired() + ->treatNullLike('null') + ->beforeNormalization() + ->always() + ->then(function ($v) { return strtolower($v); }) + ->end() + ->end() + ->scalarNode('id')->end() // service & rollbar + ->scalarNode('priority')->defaultValue(0)->end() + ->scalarNode('level')->defaultValue('DEBUG')->end() + ->booleanNode('bubble')->defaultTrue()->end() + ->scalarNode('path')->defaultValue('%kernel.logs_dir%/%kernel.environment%.log')->end() // stream and rotating + ->scalarNode('file_permission') // stream and rotating + ->defaultNull() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { + if (substr($v, 0, 1) === '0') { + return octdec($v); + } + + return (int) $v; + }) + ->end() + ->end() + ->scalarNode('ident')->defaultFalse()->end() // syslog + ->scalarNode('logopts')->defaultValue(LOG_PID)->end() // syslog + ->scalarNode('facility')->defaultValue('user')->end() // syslog + ->scalarNode('max_files')->defaultValue(0)->end() // rotating + ->scalarNode('action_level')->defaultValue('WARNING')->end() // fingers_crossed + ->scalarNode('activation_strategy')->defaultNull()->end() // fingers_crossed + ->booleanNode('stop_buffering')->defaultTrue()->end()// fingers_crossed + ->scalarNode('passthru_level')->defaultNull()->end() // fingers_crossed + ->arrayNode('excluded_404s') // fingers_crossed + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->arrayNode('accepted_levels') // filter + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->scalarNode('min_level')->defaultValue('DEBUG')->end() // filter + ->scalarNode('max_level')->defaultValue('EMERGENCY')->end() //filter + ->scalarNode('buffer_size')->defaultValue(0)->end() // fingers_crossed and buffer + ->booleanNode('flush_on_overflow')->defaultFalse()->end() // buffer + ->scalarNode('handler')->end() // fingers_crossed and buffer + ->scalarNode('url')->end() // cube + ->scalarNode('exchange')->end() // amqp + ->scalarNode('exchange_name')->defaultValue('log')->end() // amqp + ->scalarNode('room')->end() // hipchat + ->scalarNode('message_format')->defaultValue('text')->end() // hipchat + ->scalarNode('channel')->end() // slack + ->scalarNode('bot_name')->defaultValue('Monolog')->end() // slack + ->scalarNode('use_attachment')->defaultTrue()->end() // slack + ->scalarNode('use_short_attachment')->defaultFalse()->end() // slack + ->scalarNode('include_extra')->defaultFalse()->end() // slack + ->scalarNode('icon_emoji')->defaultNull()->end() // slack + ->scalarNode('notify')->defaultFalse()->end() // hipchat + ->scalarNode('nickname')->defaultValue('Monolog')->end() // hipchat + ->scalarNode('token')->end() // pushover & hipchat & loggly & logentries & flowdock & rollbar & slack + ->scalarNode('source')->end() // flowdock + ->booleanNode('use_ssl')->defaultTrue()->end() // logentries & hipchat + ->variableNode('user') // pushover + ->validate() + ->ifTrue(function ($v) { + return !is_string($v) && !is_array($v); + }) + ->thenInvalid('User must be a string or an array.') + ->end() + ->end() + ->scalarNode('title')->defaultNull()->end() // pushover + ->scalarNode('host')->end() // syslogudp + ->scalarNode('port')->defaultValue(514)->end() // syslogudp + ->arrayNode('publisher') + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('id'=> $v); }) + ->end() + ->children() + ->scalarNode('id')->end() + ->scalarNode('hostname')->end() + ->scalarNode('port')->defaultValue(12201)->end() + ->scalarNode('chunk_size')->defaultValue(1420)->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return !isset($v['id']) && !isset($v['hostname']); + }) + ->thenInvalid('What must be set is either the hostname or the id.') + ->end() + ->end() // gelf + ->arrayNode('mongo') + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('id'=> $v); }) + ->end() + ->children() + ->scalarNode('id')->end() + ->scalarNode('host')->end() + ->scalarNode('port')->defaultValue(27017)->end() + ->scalarNode('user')->end() + ->scalarNode('pass')->end() + ->scalarNode('database')->defaultValue('monolog')->end() + ->scalarNode('collection')->defaultValue('logs')->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return !isset($v['id']) && !isset($v['host']); + }) + ->thenInvalid('What must be set is either the host or the id.') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['user']) && !isset($v['pass']); + }) + ->thenInvalid('If you set user, you must provide a password.') + ->end() + ->end() // mongo + ->arrayNode('elasticsearch') + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('id'=> $v); }) + ->end() + ->children() + ->scalarNode('id')->end() + ->scalarNode('host')->end() + ->scalarNode('port')->defaultValue(9200)->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return !isset($v['id']) && !isset($v['host']); + }) + ->thenInvalid('What must be set is either the host or the id.') + ->end() + ->end() // elasticsearch + ->scalarNode('index')->defaultValue('monolog')->end() // elasticsearch + ->scalarNode('document_type')->defaultValue('logs')->end() // elasticsearch + ->scalarNode('ignore_error')->defaultValue(false)->end() // elasticsearch + ->arrayNode('config') + ->canBeUnset() + ->prototype('scalar')->end() + ->end() // rollbar + ->arrayNode('members') // group, whatfailuregroup + ->canBeUnset() + ->performNoDeepMerging() + ->prototype('scalar')->end() + ->end() + ->scalarNode('from_email')->end() // swift_mailer, native_mailer and flowdock + ->arrayNode('to_email') // swift_mailer and native_mailer + ->prototype('scalar')->end() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array($v); }) + ->end() + ->end() + ->scalarNode('subject')->end() // swift_mailer and native_mailer + ->scalarNode('content_type')->defaultNull()->end() // swift_mailer + ->scalarNode('mailer')->defaultValue('mailer')->end() // swift_mailer + ->arrayNode('email_prototype') // swift_mailer + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('id' => $v); }) + ->end() + ->children() + ->scalarNode('id')->isRequired()->end() + ->scalarNode('method')->defaultNull()->end() + ->end() + ->end() + ->booleanNode('lazy')->defaultValue(true)->end() // swift_mailer + ->scalarNode('connection_string')->end() // socket_handler + ->scalarNode('timeout')->end() // socket_handler + ->scalarNode('connection_timeout')->end() // socket_handler + ->booleanNode('persistent')->end() // socket_handler + ->scalarNode('dsn')->end() // raven_handler + ->scalarNode('client_id')->defaultNull()->end() // raven_handler + ->scalarNode('message_type')->defaultValue(0)->end() // error_log + ->arrayNode('tags') // loggly + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return explode(',', $v); }) + ->end() + ->beforeNormalization() + ->ifArray() + ->then(function ($v) { return array_filter(array_map('trim', $v)); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->arrayNode('verbosity_levels') // console + ->beforeNormalization() + ->ifArray() + ->then(function ($v) { + $map = array(); + $verbosities = array('VERBOSITY_NORMAL', 'VERBOSITY_VERBOSE', 'VERBOSITY_VERY_VERBOSE', 'VERBOSITY_DEBUG'); + // allow numeric indexed array with ascendning verbosity and lowercase names of the constants + foreach ($v as $verbosity => $level) { + if (is_int($verbosity) && isset($verbosities[$verbosity])) { + $map[$verbosities[$verbosity]] = strtoupper($level); + } else { + $map[strtoupper($verbosity)] = strtoupper($level); + } + } + + return $map; + }) + ->end() + ->children() + ->scalarNode('VERBOSITY_NORMAL')->defaultValue('WARNING')->end() + ->scalarNode('VERBOSITY_VERBOSE')->defaultValue('NOTICE')->end() + ->scalarNode('VERBOSITY_VERY_VERBOSE')->defaultValue('INFO')->end() + ->scalarNode('VERBOSITY_DEBUG')->defaultValue('DEBUG')->end() + ->end() + ->validate() + ->always(function ($v) { + $map = array(); + foreach ($v as $verbosity => $level) { + $verbosityConstant = 'Symfony\Component\Console\Output\OutputInterface::'.$verbosity; + + if (!defined($verbosityConstant)) { + throw new InvalidConfigurationException(sprintf( + 'The configured verbosity "%s" is invalid as it is not defined in Symfony\Component\Console\Output\OutputInterface.', + $verbosity + )); + } + if (!is_numeric($level)) { + $levelConstant = 'Monolog\Logger::'.$level; + if (!defined($levelConstant)) { + throw new InvalidConfigurationException(sprintf( + 'The configured minimum log level "%s" for verbosity "%s" is invalid as it is not defined in Monolog\Logger.', + $level, $verbosity + )); + } + $level = constant($levelConstant); + } else { + $level = (int) $level; + } + + $map[constant($verbosityConstant)] = $level; + } + + return $map; + }) + ->end() + ->end() + ->arrayNode('channels') + ->fixXmlConfig('channel', 'elements') + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array('elements' => array($v)); }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && is_numeric(key($v)); }) + ->then(function ($v) { return array('elements' => $v); }) + ->end() + ->validate() + ->ifTrue(function ($v) { return empty($v); }) + ->thenUnset() + ->end() + ->validate() + ->always(function ($v) { + $isExclusive = null; + if (isset($v['type'])) { + $isExclusive = 'exclusive' === $v['type']; + } + + $elements = array(); + foreach ($v['elements'] as $element) { + if (0 === strpos($element, '!')) { + if (false === $isExclusive) { + throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list.'); + } + $elements[] = substr($element, 1); + $isExclusive = true; + } else { + if (true === $isExclusive) { + throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list'); + } + $elements[] = $element; + $isExclusive = false; + } + } + + if (!count($elements)) { + return null; + } + + return array('type' => $isExclusive ? 'exclusive' : 'inclusive', 'elements' => $elements); + }) + ->end() + ->children() + ->scalarNode('type') + ->validate() + ->ifNotInArray(array('inclusive', 'exclusive')) + ->thenInvalid('The type of channels has to be inclusive or exclusive') + ->end() + ->end() + ->arrayNode('elements') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->scalarNode('formatter')->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return 'service' === $v['type'] && !empty($v['formatter']); }) + ->thenInvalid('Service handlers can not have a formatter configured in the bundle, you must reconfigure the service itself instead') + ->end() + ->validate() + ->ifTrue(function ($v) { return ('fingers_crossed' === $v['type'] || 'buffer' === $v['type'] || 'filter' === $v['type']) && 1 !== count($v['handler']); }) + ->thenInvalid('The handler has to be specified to use a FingersCrossedHandler or BufferHandler or FilterHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'fingers_crossed' === $v['type'] && !empty($v['excluded_404s']) && !empty($v['activation_strategy']); }) + ->thenInvalid('You can not use excluded_404s together with a custom activation_strategy in a FingersCrossedHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'filter' === $v['type'] && "DEBUG" !== $v['min_level'] && !empty($v['accepted_levels']); }) + ->thenInvalid('You can not use min_level together with accepted_levels in a FilterHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'filter' === $v['type'] && "EMERGENCY" !== $v['max_level'] && !empty($v['accepted_levels']); }) + ->thenInvalid('You can not use max_level together with accepted_levels in a FilterHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'rollbar' === $v['type'] && !empty($v['id']) && !empty($v['token']); }) + ->thenInvalid('You can not use both an id and a token in a RollbarHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'rollbar' === $v['type'] && empty($v['id']) && empty($v['token']); }) + ->thenInvalid('The id or the token has to be specified to use a RollbarHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'swift_mailer' === $v['type'] && empty($v['email_prototype']) && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); }) + ->thenInvalid('The sender, recipient and subject or an email prototype have to be specified to use a SwiftMailerHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'native_mailer' === $v['type'] && (empty($v['from_email']) || empty($v['to_email']) || empty($v['subject'])); }) + ->thenInvalid('The sender, recipient and subject have to be specified to use a NativeMailerHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'service' === $v['type'] && !isset($v['id']); }) + ->thenInvalid('The id has to be specified to use a service as handler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'syslogudp' === $v['type'] && !isset($v['host']); }) + ->thenInvalid('The host has to be specified to use a syslogudp as handler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'gelf' === $v['type'] && !isset($v['publisher']); }) + ->thenInvalid('The publisher has to be specified to use a GelfHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'socket' === $v['type'] && !isset($v['connection_string']); }) + ->thenInvalid('The connection_string has to be specified to use a SocketHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'pushover' === $v['type'] && (empty($v['token']) || empty($v['user'])); }) + ->thenInvalid('The token and user have to be specified to use a PushoverHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'raven' === $v['type'] && !array_key_exists('dsn', $v) && null === $v['client_id']; }) + ->thenInvalid('The DSN has to be specified to use a RavenHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'hipchat' === $v['type'] && (empty($v['token']) || empty($v['room'])); }) + ->thenInvalid('The token and room have to be specified to use a HipChatHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'hipchat' === $v['type'] && !in_array($v['message_format'], array('text', 'html')); }) + ->thenInvalid('The message_format has to be "text" or "html" in a HipChatHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'slack' === $v['type'] && (empty($v['token']) || empty($v['channel'])); }) + ->thenInvalid('The token and channel have to be specified to use a SlackHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'cube' === $v['type'] && empty($v['url']); }) + ->thenInvalid('The url has to be specified to use a CubeHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'mongo' === $v['type'] && !isset($v['mongo']); }) + ->thenInvalid('The mongo configuration has to be specified to use a MongoHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'amqp' === $v['type'] && empty($v['exchange']); }) + ->thenInvalid('The exchange has to be specified to use a AmqpHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'loggly' === $v['type'] && empty($v['token']); }) + ->thenInvalid('The token has to be specified to use a LogglyHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'loggly' === $v['type'] && !empty($v['tags']); }) + ->then(function ($v) { + $invalidTags = preg_grep('/^[a-z0-9][a-z0-9\.\-_]*$/i', $v['tags'], PREG_GREP_INVERT); + if (!empty($invalidTags)) { + throw new InvalidConfigurationException(sprintf('The following Loggly tags are invalid: %s.', implode(', ', $invalidTags))); + } + + return $v; + }) + ->end() + ->validate() + ->ifTrue(function ($v) { return 'logentries' === $v['type'] && empty($v['token']); }) + ->thenInvalid('The token has to be specified to use a LogEntriesHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['token']); }) + ->thenInvalid('The token has to be specified to use a FlowdockHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['from_email']); }) + ->thenInvalid('The from_email has to be specified to use a FlowdockHandler') + ->end() + ->validate() + ->ifTrue(function ($v) { return 'flowdock' === $v['type'] && empty($v['source']); }) + ->thenInvalid('The source has to be specified to use a FlowdockHandler') + ->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return isset($v['debug']); }) + ->thenInvalid('The "debug" name cannot be used as it is reserved for the handler of the profiler') + ->end() + ->example(array( + 'syslog' => array( + 'type' => 'stream', + 'path' => '/var/log/symfony.log', + 'level' => 'ERROR', + 'bubble' => 'false', + 'formatter' => 'my_formatter', + ), + 'main' => array( + 'type' => 'fingers_crossed', + 'action_level' => 'WARNING', + 'buffer_size' => 30, + 'handler' => 'custom', + ), + 'custom' => array( + 'type' => 'service', + 'id' => 'my_handler', + ) + )) + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/symfony/monolog-bundle/DependencyInjection/MonologExtension.php b/vendor/symfony/monolog-bundle/DependencyInjection/MonologExtension.php new file mode 100644 index 0000000..ca098cf --- /dev/null +++ b/vendor/symfony/monolog-bundle/DependencyInjection/MonologExtension.php @@ -0,0 +1,621 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Elastica\Client; + +/** + * MonologExtension is an extension for the Monolog library. + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class MonologExtension extends Extension +{ + private $nestedHandlers = array(); + + private $swiftMailerHandlers = array(); + + private function levelToMonologConst($level) + { + return is_int($level) ? $level : constant('Monolog\Logger::'.strtoupper($level)); + } + + /** + * Loads the Monolog configuration. + * + * @param array $configs An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (isset($config['handlers'])) { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('monolog.xml'); + $container->setAlias('logger', 'monolog.logger'); + + $handlers = array(); + + foreach ($config['handlers'] as $name => $handler) { + $handlers[$handler['priority']][] = array( + 'id' => $this->buildHandler($container, $name, $handler), + 'channels' => empty($handler['channels']) ? null : $handler['channels'], + ); + } + + $container->setParameter( + 'monolog.swift_mailer.handlers', + $this->swiftMailerHandlers + ); + + ksort($handlers); + $sortedHandlers = array(); + foreach ($handlers as $priorityHandlers) { + foreach (array_reverse($priorityHandlers) as $handler) { + $sortedHandlers[] = $handler; + } + } + + $handlersToChannels = array(); + foreach ($sortedHandlers as $handler) { + if (!in_array($handler['id'], $this->nestedHandlers)) { + $handlersToChannels[$handler['id']] = $handler['channels']; + } + } + $container->setParameter('monolog.handlers_to_channels', $handlersToChannels); + + $this->addClassesToCompile(array( + 'Monolog\\Formatter\\FormatterInterface', + 'Monolog\\Formatter\\LineFormatter', + 'Monolog\\Handler\\HandlerInterface', + 'Monolog\\Handler\\AbstractHandler', + 'Monolog\\Handler\\AbstractProcessingHandler', + 'Monolog\\Handler\\StreamHandler', + 'Monolog\\Handler\\FingersCrossedHandler', + 'Monolog\\Handler\\FilterHandler', + 'Monolog\\Handler\\TestHandler', + 'Monolog\\Logger', + 'Symfony\\Bridge\\Monolog\\Logger', + 'Symfony\\Bridge\\Monolog\\Handler\\DebugHandler', + 'Monolog\\Handler\\FingersCrossed\\ActivationStrategyInterface', + 'Monolog\\Handler\\FingersCrossed\\ErrorLevelActivationStrategy', + )); + } + + $container->setParameter('monolog.additional_channels', isset($config['channels']) ? $config['channels'] : array()); + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/monolog'; + } + + private function buildHandler(ContainerBuilder $container, $name, array $handler) + { + $handlerId = $this->getHandlerId($name); + $definition = new Definition(sprintf('%%monolog.handler.%s.class%%', $handler['type'])); + $handler['level'] = $this->levelToMonologConst($handler['level']); + + switch ($handler['type']) { + case 'service': + $container->setAlias($handlerId, $handler['id']); + + return $handlerId; + + case 'stream': + $definition->setArguments(array( + $handler['path'], + $handler['level'], + $handler['bubble'], + $handler['file_permission'], + )); + break; + + case 'console': + if (!class_exists('Symfony\Bridge\Monolog\Handler\ConsoleHandler')) { + throw new \RuntimeException('The console handler requires symfony/monolog-bridge 2.4+'); + } + + $definition->setArguments(array( + null, + $handler['bubble'], + isset($handler['verbosity_levels']) ? $handler['verbosity_levels'] : array(), + )); + $definition->addTag('kernel.event_subscriber'); + break; + + case 'firephp': + $definition->setArguments(array( + $handler['level'], + $handler['bubble'], + )); + $definition->addTag('kernel.event_listener', array('event' => 'kernel.response', 'method' => 'onKernelResponse')); + break; + + case 'gelf': + if (isset($handler['publisher']['id'])) { + $publisherId = $handler['publisher']['id']; + } elseif (class_exists('Gelf\Transport\UdpTransport')) { + $transport = new Definition("Gelf\Transport\UdpTransport", array( + $handler['publisher']['hostname'], + $handler['publisher']['port'], + $handler['publisher']['chunk_size'], + )); + $transportId = uniqid('monolog.gelf.transport.'); + $transport->setPublic(false); + $container->setDefinition($transportId, $transport); + + $publisher = new Definition('%monolog.gelfphp.publisher.class%', array()); + $publisher->addMethodCall('addTransport', array(new Reference($transportId))); + $publisherId = uniqid('monolog.gelf.publisher.'); + $publisher->setPublic(false); + $container->setDefinition($publisherId, $publisher); + } elseif (class_exists('Gelf\MessagePublisher')) { + $publisher = new Definition('%monolog.gelf.publisher.class%', array( + $handler['publisher']['hostname'], + $handler['publisher']['port'], + $handler['publisher']['chunk_size'], + )); + + $publisherId = uniqid('monolog.gelf.publisher.'); + $publisher->setPublic(false); + $container->setDefinition($publisherId, $publisher); + } else { + throw new \RuntimeException('The gelf handler requires the graylog2/gelf-php package to be installed'); + } + + $definition->setArguments(array( + new Reference($publisherId), + $handler['level'], + $handler['bubble'], + )); + break; + + case 'mongo': + if (isset($handler['mongo']['id'])) { + $clientId = $handler['mongo']['id']; + } else { + $server = 'mongodb://'; + + if (isset($handler['mongo']['user'])) { + $server .= $handler['mongo']['user'].':'.$handler['mongo']['pass'].'@'; + } + + $server .= $handler['mongo']['host'].':'.$handler['mongo']['port']; + + $client = new Definition('%monolog.mongo.client.class%', array( + $server, + )); + + $clientId = uniqid('monolog.mongo.client.'); + $client->setPublic(false); + $container->setDefinition($clientId, $client); + } + + $definition->setArguments(array( + new Reference($clientId), + $handler['mongo']['database'], + $handler['mongo']['collection'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'elasticsearch': + if (isset($handler['elasticsearch']['id'])) { + $clientId = $handler['elasticsearch']['id']; + } else { + // elastica client new definition + $elasticaClient = new Definition('%monolog.elastica.client.class%'); + $elasticaClient->setArguments(array( + array( + 'host' => $handler['elasticsearch']['host'], + 'port' => $handler['elasticsearch']['port'], + ), + )); + + $clientId = uniqid('monolog.elastica.client.'); + $elasticaClient->setPublic(false); + $container->setDefinition($clientId, $elasticaClient); + } + + // elastica handler definition + $definition->setArguments(array( + new Reference($clientId), + array( + 'index' => $handler['index'], + 'type' => $handler['document_type'], + 'ignore_error' => $handler['ignore_error'] + ), + $handler['level'], + $handler['bubble'], + )); + break; + + case 'chromephp': + $definition->setArguments(array( + $handler['level'], + $handler['bubble'], + )); + $definition->addTag('kernel.event_listener', array('event' => 'kernel.response', 'method' => 'onKernelResponse')); + break; + + case 'rotating_file': + $definition->setArguments(array( + $handler['path'], + $handler['max_files'], + $handler['level'], + $handler['bubble'], + $handler['file_permission'], + )); + break; + + case 'fingers_crossed': + $handler['action_level'] = $this->levelToMonologConst($handler['action_level']); + if (null !== $handler['passthru_level']) { + $handler['passthru_level'] = $this->levelToMonologConst($handler['passthru_level']); + } + $nestedHandlerId = $this->getHandlerId($handler['handler']); + $this->nestedHandlers[] = $nestedHandlerId; + + if (isset($handler['activation_strategy'])) { + $activation = new Reference($handler['activation_strategy']); + } elseif (!empty($handler['excluded_404s'])) { + $activationDef = new Definition('%monolog.activation_strategy.not_found.class%', array($handler['excluded_404s'], $handler['action_level'])); + $activationDef->addMethodCall('setRequest', array(new Reference('request', ContainerInterface::NULL_ON_INVALID_REFERENCE, false))); + $container->setDefinition($handlerId.'.not_found_strategy', $activationDef); + $activation = new Reference($handlerId.'.not_found_strategy'); + } else { + $activation = $handler['action_level']; + } + + $definition->setArguments(array( + new Reference($nestedHandlerId), + $activation, + $handler['buffer_size'], + $handler['bubble'], + $handler['stop_buffering'], + $handler['passthru_level'], + )); + break; + + case 'filter': + $handler['min_level'] = $this->levelToMonologConst($handler['min_level']); + $handler['max_level'] = $this->levelToMonologConst($handler['max_level']); + foreach (array_keys($handler['accepted_levels']) as $k) { + $handler['accepted_levels'][$k] = $this->levelToMonologConst($handler['accepted_levels'][$k]); + } + + $nestedHandlerId = $this->getHandlerId($handler['handler']); + $this->nestedHandlers[] = $nestedHandlerId; + $minLevelOrList = !empty($handler['accepted_levels']) ? $handler['accepted_levels'] : $handler['min_level']; + + $definition->setArguments(array( + new Reference($nestedHandlerId), + $minLevelOrList, + $handler['max_level'], + $handler['bubble'], + )); + break; + + case 'buffer': + $nestedHandlerId = $this->getHandlerId($handler['handler']); + $this->nestedHandlers[] = $nestedHandlerId; + + $definition->setArguments(array( + new Reference($nestedHandlerId), + $handler['buffer_size'], + $handler['level'], + $handler['bubble'], + $handler['flush_on_overflow'], + )); + break; + + case 'group': + case 'whatfailuregroup': + $references = array(); + foreach ($handler['members'] as $nestedHandler) { + $nestedHandlerId = $this->getHandlerId($nestedHandler); + $this->nestedHandlers[] = $nestedHandlerId; + $references[] = new Reference($nestedHandlerId); + } + + $definition->setArguments(array( + $references, + $handler['bubble'], + )); + break; + + case 'syslog': + $definition->setArguments(array( + $handler['ident'], + $handler['facility'], + $handler['level'], + $handler['bubble'], + $handler['logopts'], + )); + break; + + case 'syslogudp': + $definition->setArguments(array( + $handler['host'], + $handler['port'], + $handler['facility'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'swift_mailer': + $oldHandler = false; + // fallback for older symfony versions that don't have the new SwiftMailerHandler in the bridge + $newHandlerClass = $container->getParameterBag()->resolveValue($definition->getClass()); + if (!class_exists($newHandlerClass)) { + $definition = new Definition('Monolog\Handler\SwiftMailerHandler'); + $oldHandler = true; + } + + if (isset($handler['email_prototype'])) { + if (!empty($handler['email_prototype']['method'])) { + $prototype = array(new Reference($handler['email_prototype']['id']), $handler['email_prototype']['method']); + } else { + $prototype = new Reference($handler['email_prototype']['id']); + } + } else { + $messageFactory = new Definition('Symfony\Bundle\MonologBundle\SwiftMailer\MessageFactory'); + $messageFactory->setLazy(true); + $messageFactory->setPublic(false); + $messageFactory->setArguments(array( + new Reference($handler['mailer']), + $handler['from_email'], + $handler['to_email'], + $handler['subject'], + $handler['content_type'] + )); + + $messageFactoryId = sprintf('%s.mail_message_factory', $handlerId); + $container->setDefinition($messageFactoryId, $messageFactory); + // set the prototype as a callable + $prototype = array(new Reference($messageFactoryId), 'createMessage'); + } + $definition->setArguments(array( + new Reference($handler['mailer']), + $prototype, + $handler['level'], + $handler['bubble'], + )); + if (!$oldHandler) { + $this->swiftMailerHandlers[] = $handlerId; + $definition->addTag('kernel.event_listener', array('event' => 'kernel.terminate', 'method' => 'onKernelTerminate')); + if (method_exists($newHandlerClass, 'onCliTerminate')) { + $definition->addTag('kernel.event_listener', array('event' => 'console.terminate', 'method' => 'onCliTerminate')); + } + } + break; + + case 'native_mailer': + $definition->setArguments(array( + $handler['to_email'], + $handler['subject'], + $handler['from_email'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'socket': + $definition->setArguments(array( + $handler['connection_string'], + $handler['level'], + $handler['bubble'], + )); + if (isset($handler['timeout'])) { + $definition->addMethodCall('setTimeout', array($handler['timeout'])); + } + if (isset($handler['connection_timeout'])) { + $definition->addMethodCall('setConnectionTimeout', array($handler['connection_timeout'])); + } + if (isset($handler['persistent'])) { + $definition->addMethodCall('setPersistent', array($handler['persistent'])); + } + break; + + case 'pushover': + $definition->setArguments(array( + $handler['token'], + $handler['user'], + $handler['title'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'hipchat': + $definition->setArguments(array( + $handler['token'], + $handler['room'], + $handler['nickname'], + $handler['notify'], + $handler['level'], + $handler['bubble'], + $handler['use_ssl'], + $handler['message_format'], + )); + break; + + case 'slack': + $definition->setArguments(array( + $handler['token'], + $handler['channel'], + $handler['bot_name'], + $handler['use_attachment'], + $handler['icon_emoji'], + $handler['level'], + $handler['bubble'], + $handler['use_short_attachment'], + $handler['include_extra'], + )); + break; + + case 'cube': + $definition->setArguments(array( + $handler['url'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'amqp': + $definition->setArguments(array( + new Reference($handler['exchange']), + $handler['exchange_name'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'error_log': + $definition->setArguments(array( + $handler['message_type'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'raven': + if (null !== $handler['client_id']) { + $clientId = $handler['client_id']; + } else { + $client = new Definition('Raven_Client', array( + $handler['dsn'], + )); + $client->setPublic(false); + $clientId = 'monolog.raven.client.'.sha1($handler['dsn']); + $container->setDefinition($clientId, $client); + } + $definition->setArguments(array( + new Reference($clientId), + $handler['level'], + $handler['bubble'], + )); + break; + + case 'loggly': + $definition->setArguments(array( + $handler['token'], + $handler['level'], + $handler['bubble'], + )); + if (!empty($handler['tags'])) { + $definition->addMethodCall('setTag', array(implode(',', $handler['tags']))); + } + break; + + case 'logentries': + $definition->setArguments(array( + $handler['token'], + $handler['use_ssl'], + $handler['level'], + $handler['bubble'], + )); + break; + + case 'flowdock': + $definition->setArguments(array( + $handler['token'], + $handler['level'], + $handler['bubble'], + )); + + if (empty($handler['formatter'])) { + $formatter = new Definition("Monolog\Formatter\FlowdockFormatter", array( + $handler['source'], + $handler['from_email'], + )); + $formatterId = 'monolog.flowdock.formatter.'.sha1($handler['source'].'|'.$handler['from_email']); + $formatter->setPublic(false); + $container->setDefinition($formatterId, $formatter); + + $definition->addMethodCall('setFormatter', array(new Reference($formatterId))); + } + break; + + case 'rollbar': + if (!empty($handler['id'])) { + $rollbarId = $handler['id']; + } else { + $config = $handler['config'] ?: array(); + $config['access_token'] = $handler['token']; + $rollbar = new Definition('RollbarNotifier', array( + $config, + )); + $rollbarId = 'monolog.rollbar.notifier.'.sha1(json_encode($config)); + $rollbar->setPublic(false); + $container->setDefinition($rollbarId, $rollbar); + } + + $definition->setArguments(array( + new Reference($rollbarId), + $handler['level'], + $handler['bubble'], + )); + break; + + // Handlers using the constructor of AbstractHandler without adding their own arguments + case 'browser_console': + case 'newrelic': + case 'test': + case 'null': + case 'debug': + $definition->setArguments(array( + $handler['level'], + $handler['bubble'], + )); + break; + + default: + throw new \InvalidArgumentException(sprintf('Invalid handler type "%s" given for handler "%s"', $handler['type'], $name)); + } + + if (!empty($handler['formatter'])) { + $definition->addMethodCall('setFormatter', array(new Reference($handler['formatter']))); + } + $container->setDefinition($handlerId, $definition); + + return $handlerId; + } + + private function getHandlerId($name) + { + return sprintf('monolog.handler.%s', $name); + } +} diff --git a/vendor/symfony/monolog-bundle/LICENSE b/vendor/symfony/monolog-bundle/LICENSE new file mode 100644 index 0000000..cdffe7a --- /dev/null +++ b/vendor/symfony/monolog-bundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/monolog-bundle/MonologBundle.php b/vendor/symfony/monolog-bundle/MonologBundle.php new file mode 100644 index 0000000..9a846bf --- /dev/null +++ b/vendor/symfony/monolog-bundle/MonologBundle.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle; + +use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\AddSwiftMailerTransportPass; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\LoggerChannelPass; +use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\DebugHandlerPass; +use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\AddProcessorsPass; + +/** + * Bundle. + * + * @author Jordi Boggiano + */ +class MonologBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass($channelPass = new LoggerChannelPass()); + $container->addCompilerPass(new DebugHandlerPass($channelPass)); + $container->addCompilerPass(new AddProcessorsPass()); + $container->addCompilerPass(new AddSwiftMailerTransportPass()); + } +} diff --git a/vendor/symfony/monolog-bundle/NotFoundActivationStrategy.php b/vendor/symfony/monolog-bundle/NotFoundActivationStrategy.php new file mode 100644 index 0000000..4e94be2 --- /dev/null +++ b/vendor/symfony/monolog-bundle/NotFoundActivationStrategy.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpFoundation\Request; + +/** + * Activation strategy that ignores 404s for certain URLs + * + * Configure with: + * + * monolog: + * handlers: + * main: + * type: fingers_crossed + * action_level: error + * handler: nested + * excluded_404s: + * - ^/foo/ + * + * @author Jordi Boggiano + */ +class NotFoundActivationStrategy extends ErrorLevelActivationStrategy +{ + private $blacklist; + private $request; + + public function __construct(array $excludedUrls, $actionLevel) + { + parent::__construct($actionLevel); + $this->blacklist = '{('.implode('|', $excludedUrls).')}i'; + } + + public function isHandlerActivated(array $record) + { + $isActivated = parent::isHandlerActivated($record); + if ( + $isActivated + && $this->request + && isset($record['context']['exception']) + && $record['context']['exception'] instanceof HttpException + && $record['context']['exception']->getStatusCode() == 404 + ) { + return !preg_match($this->blacklist, $this->request->getPathInfo()); + } + + return $isActivated; + } + + public function setRequest(Request $req = null) + { + $this->request = $req; + } +} diff --git a/vendor/symfony/monolog-bundle/README.md b/vendor/symfony/monolog-bundle/README.md new file mode 100644 index 0000000..e8fd02a --- /dev/null +++ b/vendor/symfony/monolog-bundle/README.md @@ -0,0 +1,18 @@ +MonologBundle +============= + +The `MonologBundle` provides integration of the [Monolog](https://github.com/Seldaek/monolog) +library into the Symfony2 framework. + +As of v2.4.0 of the bundle, the release cycle is de-synchronized from the framework's. +It means you can just require `"symfony/monolog-bundle": "~2.4"` in your composer.json +and Composer will automatically pick the latest version of the bundle that works with +your current version of Symfony. The minimum version of Symfony2 for this workflow +is 2.3.0. + +More information in the official [documentation](http://symfony.com/doc/current/cookbook/logging/index.html). + +License +======= + +This bundle is released under the [MIT license](LICENSE) diff --git a/vendor/symfony/monolog-bundle/Resources/config/monolog.xml b/vendor/symfony/monolog-bundle/Resources/config/monolog.xml new file mode 100644 index 0000000..8a897b4 --- /dev/null +++ b/vendor/symfony/monolog-bundle/Resources/config/monolog.xml @@ -0,0 +1,78 @@ + + + + + + Symfony\Bridge\Monolog\Logger + Gelf\MessagePublisher + Gelf\Publisher + Monolog\Handler\StreamHandler + Symfony\Bridge\Monolog\Handler\ConsoleHandler + Monolog\Handler\GroupHandler + Monolog\Handler\BufferHandler + Monolog\Handler\RotatingFileHandler + Monolog\Handler\SyslogHandler + Monolog\Handler\SyslogUdpHandler + Monolog\Handler\NullHandler + Monolog\Handler\TestHandler + Monolog\Handler\GelfHandler + Monolog\Handler\RollbarHandler + Monolog\Handler\FlowdockHandler + Monolog\Handler\BrowserConsoleHandler + Symfony\Bridge\Monolog\Handler\FirePHPHandler + Symfony\Bridge\Monolog\Handler\ChromePhpHandler + Symfony\Bridge\Monolog\Handler\DebugHandler + Symfony\Bridge\Monolog\Handler\SwiftMailerHandler + Monolog\Handler\NativeMailerHandler + Monolog\Handler\SocketHandler + Monolog\Handler\PushoverHandler + Monolog\Handler\RavenHandler + Monolog\Handler\NewRelicHandler + Monolog\Handler\HipChatHandler + Monolog\Handler\SlackHandler + Monolog\Handler\CubeHandler + Monolog\Handler\AmqpHandler + Monolog\Handler\ErrorLogHandler + Monolog\Handler\LogglyHandler + Monolog\Handler\LogEntriesHandler + Monolog\Handler\WhatFailureGroupHandler + Symfony\Bundle\MonologBundle\NotFoundActivationStrategy + + Monolog\Handler\FingersCrossedHandler + Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy + Monolog\Handler\FilterHandler + Monolog\Handler\MongoDBHandler + MongoClient + + Monolog\Handler\ElasticSearchHandler + Elastica\Client + + + + + app + + + + + + + + + + + + + + + + + + + + app + + + diff --git a/vendor/symfony/monolog-bundle/Resources/config/schema/monolog-1.0.xsd b/vendor/symfony/monolog-bundle/Resources/config/schema/monolog-1.0.xsd new file mode 100644 index 0000000..aa3f1ff --- /dev/null +++ b/vendor/symfony/monolog-bundle/Resources/config/schema/monolog-1.0.xsd @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/monolog-bundle/SwiftMailer/MessageFactory.php b/vendor/symfony/monolog-bundle/SwiftMailer/MessageFactory.php new file mode 100644 index 0000000..f2db249 --- /dev/null +++ b/vendor/symfony/monolog-bundle/SwiftMailer/MessageFactory.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\SwiftMailer; + +/** + * Helps create Swift_Message objects, lazily + * + * @author Ryan Weaver + */ +class MessageFactory +{ + private $mailer; + + private $fromEmail; + + private $toEmail; + + private $subject; + + private $contentType; + + public function __construct(\Swift_Mailer $mailer, $fromEmail, $toEmail, $subject, $contentType = null) + { + $this->mailer = $mailer; + $this->fromEmail = $fromEmail; + $this->toEmail = $toEmail; + $this->subject = $subject; + $this->contentType = $contentType; + } + + /** + * Creates a Swift_Message template that will be used to send the log message + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return \Swift_Message + */ + public function createMessage($content, array $records) + { + /** @var \Swift_Message $message */ + $message = $this->mailer->createMessage(); + $message->setTo($this->toEmail); + $message->setFrom($this->fromEmail); + $message->setSubject($this->subject); + + if ($this->contentType) { + $message->setContentType($this->contentType); + } + + return $message; + } +} diff --git a/vendor/symfony/monolog-bundle/composer.json b/vendor/symfony/monolog-bundle/composer.json new file mode 100644 index 0000000..d87302b --- /dev/null +++ b/vendor/symfony/monolog-bundle/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/monolog-bundle", + "type": "symfony-bundle", + "description": "Symfony MonologBundle", + "keywords": ["log", "logging"], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2", + "symfony/monolog-bridge": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/config": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "monolog/monolog": "~1.8" + }, + "require-dev": { + "symfony/yaml": "~2.3|~3.0", + "symfony/console": "~2.3|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\MonologBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.8.x-dev" + } + } +} diff --git a/vendor/symfony/monolog-bundle/phpunit.xml.dist b/vendor/symfony/monolog-bundle/phpunit.xml.dist new file mode 100644 index 0000000..90560f7 --- /dev/null +++ b/vendor/symfony/monolog-bundle/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./Tests + + + + + + . + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/phpunit-bridge/ClockMock.php b/vendor/symfony/phpunit-bridge/ClockMock.php new file mode 100644 index 0000000..b81fee9 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/ClockMock.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +/** + * @author Nicolas Grekas + */ +class ClockMock +{ + private static $now; + + public static function withClockMock($enable = null) + { + if (null === $enable) { + return null !== self::$now; + } + + self::$now = is_numeric($enable) ? (float) $enable : ($enable ? microtime(true) : null); + } + + public static function time() + { + if (null === self::$now) { + return \time(); + } + + return (int) self::$now; + } + + public static function sleep($s) + { + if (null === self::$now) { + return \sleep($s); + } + + self::$now += (int) $s; + + return 0; + } + + public static function usleep($us) + { + if (null === self::$now) { + return \usleep($us); + } + + self::$now += $us / 1000000; + } + + public static function microtime($asFloat = false) + { + if (null === self::$now) { + return \microtime($asFloat); + } + + if ($asFloat) { + return self::$now; + } + + return sprintf("%0.6f %d\n", self::$now - (int) self::$now, (int) self::$now); + } + + public static function register($class) + { + $self = get_called_class(); + + $mockedNs = array(substr($class, 0, strrpos($class, '\\'))); + if (strpos($class, '\\Tests\\')) { + $ns = str_replace('\\Tests\\', '\\', $class); + $mockedNs[] = substr($ns, 0, strrpos($ns, '\\')); + } + foreach ($mockedNs as $ns) { + if (function_exists($ns.'\time')) { + continue; + } + eval(<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +/** + * Catch deprecation notices and print a summary report at the end of the test suite. + * + * @author Nicolas Grekas + */ +class DeprecationErrorHandler +{ + const MODE_WEAK = 'weak'; + + private static $isRegistered = false; + + /** + * Registers and configures the deprecation handler. + * + * The following reporting modes are supported: + * - use "weak" to hide the deprecation report but keep a global count; + * - use "/some-regexp/" to stop the test suite whenever a deprecation + * message matches the given regular expression; + * - use a number to define the upper bound of allowed deprecations, + * making the test suite fail whenever more notices are trigerred. + * + * @param int|string|false $mode The reporting mode. Defaults to not allowing any deprecations. + */ + public static function register($mode = 0) + { + if (self::$isRegistered) { + return; + } + if (self::MODE_WEAK !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) { + $mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0; + } + $deprecations = array( + 'unsilencedCount' => 0, + 'remainingCount' => 0, + 'legacyCount' => 0, + 'otherCount' => 0, + 'unsilenced' => array(), + 'remaining' => array(), + 'legacy' => array(), + 'other' => array(), + ); + $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $mode) { + if (E_USER_DEPRECATED !== $type) { + return \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context); + } + + $trace = debug_backtrace(PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT : true); + + $i = count($trace); + while (isset($trace[--$i]['class']) && ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_'))) { + // No-op + } + + if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) { + $class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class']; + $method = $trace[$i]['function']; + + if (0 !== error_reporting()) { + $group = 'unsilenced'; + } elseif (0 === strpos($method, 'testLegacy') + || 0 === strpos($method, 'provideLegacy') + || 0 === strpos($method, 'getLegacy') + || strpos($class, '\Legacy') + || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true) + ) { + $group = 'legacy'; + } else { + $group = 'remaining'; + } + + if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) { + $e = new \Exception($msg); + $r = new \ReflectionProperty($e, 'trace'); + $r->setAccessible(true); + $r->setValue($e, array_slice($trace, 1, $i)); + + echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':'; + echo "\n".$msg; + echo "\nStack trace:"; + echo "\n".str_replace(' '.getcwd().DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString()); + echo "\n"; + + exit(1); + } + if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) { + $ref = &$deprecations[$group][$msg]['count']; + ++$ref; + $ref = &$deprecations[$group][$msg][$class.'::'.$method]; + ++$ref; + } + } else { + $group = 'other'; + $ref = &$deprecations[$group][$msg]['count']; + ++$ref; + } + ++$deprecations[$group.'Count']; + }; + $oldErrorHandler = set_error_handler($deprecationHandler); + + if (null !== $oldErrorHandler) { + restore_error_handler(); + if (array('PHPUnit_Util_ErrorHandler', 'handleError') === $oldErrorHandler) { + restore_error_handler(); + self::register($mode); + } + } elseif (!isset($mode[0]) || '/' !== $mode[0]) { + self::$isRegistered = true; + if (self::hasColorSupport()) { + $colorize = function ($str, $red) { + $color = $red ? '41;37' : '43;30'; + + return "\x1B[{$color}m{$str}\x1B[0m"; + }; + } else { + $colorize = function ($str) {return $str;}; + } + register_shutdown_function(function () use ($mode, &$deprecations, $deprecationHandler, $colorize) { + $currErrorHandler = set_error_handler('var_dump'); + restore_error_handler(); + + if ($currErrorHandler !== $deprecationHandler) { + echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n"; + } + + $cmp = function ($a, $b) { + return $b['count'] - $a['count']; + }; + + foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) { + if ($deprecations[$group.'Count']) { + echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n"; + + uasort($deprecations[$group], $cmp); + + foreach ($deprecations[$group] as $msg => $notices) { + echo "\n", rtrim($msg, '.'), ': ', $notices['count'], "x\n"; + + arsort($notices); + + foreach ($notices as $method => $count) { + if ('count' !== $method) { + echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n"; + } + } + } + } + } + if (!empty($notices)) { + echo "\n"; + } + if (DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) { + exit(1); + } + }); + } + } + + private static function hasColorSupport() + { + if ('\\' === DIRECTORY_SEPARATOR) { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + } + + return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + } +} diff --git a/vendor/symfony/phpunit-bridge/LICENSE b/vendor/symfony/phpunit-bridge/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/phpunit-bridge/README.md b/vendor/symfony/phpunit-bridge/README.md new file mode 100644 index 0000000..ef5f76f --- /dev/null +++ b/vendor/symfony/phpunit-bridge/README.md @@ -0,0 +1,61 @@ +PHPUnit Bridge +============== + +Provides utilities for PHPUnit, especially user deprecation notices management. + +It comes with the following features: + + * disable the garbage collector; + * enforce a consistent `C` locale; + * auto-register `class_exists` to load Doctrine annotations; + * print a user deprecation notices summary at the end of the test suite; + * display the stack trace of a deprecation on-demand. + +By default any non-legacy-tagged or any non-@-silenced deprecation notices will +make tests fail. This can be changed by setting the `SYMFONY_DEPRECATIONS_HELPER` +environment variable to the maximum number of deprecations that are allowed to be +triggered before making the test suite fail. Alternatively, setting it to `weak` +will make the bridge ignore any deprecation notices and is useful to projects +that must use deprecated interfaces for backward compatibility reasons. + +A summary of deprecation notices is displayed at the end of the test suite: + + * **Unsilenced** reports deprecation notices that were triggered without the + recommended @-silencing operator; + * **Legacy** deprecation notices denote tests that explicitly test some legacy + interfaces. There are four ways to mark a test as legacy: + - make its class start with the `Legacy` prefix; + - make its method start with `testLegacy`; + - make its data provider start with `provideLegacy` or `getLegacy`; + - add the `@group legacy` annotation to its class or method. + * **Remaining/Other** deprecation notices are all other (non-legacy) + notices, grouped by message, test class and method. + +Usage +----- + +Add this bridge to the `require-dev` section of your `composer.json` file +(not in `require`) with e.g. `composer require --dev "symfony/phpunit-bridge"`. + +When running `phpunit`, you will see a summary of deprecation notices at the end +of the test suite. + +Deprecation notices in the **Unsilenced** section should just be @-silenced: +`@trigger_error('...', E_USER_DEPRECATED);`. Without the @-silencing operator, +users would need to opt-out from deprecation notices. Silencing by default swaps +this behavior and allows users to opt-in when they are ready to cope with them +(by adding a custom error handler like the one provided by this bridge.) + +Deprecation notices in the **Remaining/Other** section need some thought. +You have to decide either to: + + * update your code to not use deprecated interfaces anymore, thus gaining better + forward compatibility; + * or move them to the **Legacy** section (by using one of the above way). + +In case you need to inspect the stack trace of a particular deprecation triggered +by your unit tests, you can set the `SYMFONY_DEPRECATIONS_HELPER` env var to a +regular expression that matches this deprecation's message, encapsed between `/`. +For example, `SYMFONY_DEPRECATIONS_HELPER=/foobar/ phpunit` will stop your test +suite once a deprecation notice is triggered whose message contains the "foobar" +string. diff --git a/vendor/symfony/phpunit-bridge/SymfonyTestsListener.php b/vendor/symfony/phpunit-bridge/SymfonyTestsListener.php new file mode 100644 index 0000000..4f4dc78 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/SymfonyTestsListener.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +use Doctrine\Common\Annotations\AnnotationRegistry; + +/** + * Collects and replays skipped tests. + * + * @author Nicolas Grekas + */ +class SymfonyTestsListener extends \PHPUnit_Framework_BaseTestListener +{ + private static $globallyEnabled = false; + private $state = -1; + private $skippedFile = false; + private $wasSkipped = array(); + private $isSkipped = array(); + + public function __construct(array $extraClockMockedNamespaces = array()) + { + if ($extraClockMockedNamespaces) { + foreach ($extraClockMockedNamespaces as $ns) { + ClockMock::register($ns.'\DummyClass'); + } + } + if (self::$globallyEnabled) { + $this->state = -2; + } else { + self::$globallyEnabled = true; + } + } + + public function __destruct() + { + if (0 < $this->state) { + file_put_contents($this->skippedFile, 'isSkipped, true).';'); + } + } + + public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) + { + $suiteName = $suite->getName(); + + if (-1 === $this->state) { + echo "Testing $suiteName\n"; + $this->state = 0; + + if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) { + AnnotationRegistry::registerLoader('class_exists'); + } + + if ($this->skippedFile = getenv('SYMFONY_PHPUNIT_SKIPPED_TESTS')) { + $this->state = 1; + + if (file_exists($this->skippedFile)) { + $this->state = 2; + + if (!$this->wasSkipped = require $this->skippedFile) { + echo "All tests already ran successfully.\n"; + $suite->setTests(array()); + } + } + } + $testSuites = array($suite); + for ($i = 0; isset($testSuites[$i]); ++$i) { + foreach ($testSuites[$i]->tests() as $test) { + if ($test instanceof \PHPUnit_Framework_TestSuite) { + if (class_exists($test->getName(), false) && in_array('time-sensitive', \PHPUnit_Util_Test::getGroups($test->getName()), true)) { + ClockMock::register($test->getName()); + } else { + $testSuites[] = $test; + } + } + } + } + } elseif (2 === $this->state) { + $skipped = array(); + foreach ($suite->tests() as $test) { + if (!$test instanceof \PHPUnit_Framework_TestCase + || isset($this->wasSkipped[$suiteName]['*']) + || isset($this->wasSkipped[$suiteName][$test->getName()])) { + $skipped[] = $test; + } + } + $suite->setTests($skipped); + } + } + + public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + if (0 < $this->state) { + if ($test instanceof \PHPUnit_Framework_TestCase) { + $class = get_class($test); + $method = $test->getName(); + } else { + $class = $test->getName(); + $method = '*'; + } + + $this->isSkipped[$class][$method] = 1; + } + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { + $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName()); + + if (in_array('time-sensitive', $groups, true)) { + ClockMock::register(get_class($test)); + ClockMock::withClockMock(true); + } + } + } + + public function endTest(\PHPUnit_Framework_Test $test, $time) + { + if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { + $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName()); + + if (in_array('time-sensitive', $groups, true)) { + ClockMock::withClockMock(false); + } + } + } +} diff --git a/vendor/symfony/phpunit-bridge/TextUI/Command.php b/vendor/symfony/phpunit-bridge/TextUI/Command.php new file mode 100644 index 0000000..203fd16 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/TextUI/Command.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\TextUI; + +/** + * {@inheritdoc} + */ +class Command extends \PHPUnit_TextUI_Command +{ + /** + * {@inheritdoc} + */ + protected function createRunner() + { + return new TestRunner($this->arguments['loader']); + } +} diff --git a/vendor/symfony/phpunit-bridge/TextUI/TestRunner.php b/vendor/symfony/phpunit-bridge/TextUI/TestRunner.php new file mode 100644 index 0000000..94602bb --- /dev/null +++ b/vendor/symfony/phpunit-bridge/TextUI/TestRunner.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\TextUI; + +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; + +/** + * {@inheritdoc} + */ +class TestRunner extends \PHPUnit_TextUI_TestRunner +{ + /** + * {@inheritdoc} + */ + protected function handleConfiguration(array &$arguments) + { + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + $arguments['listeners'][] = new SymfonyTestsListener(); + + return parent::handleConfiguration($arguments); + } +} diff --git a/vendor/symfony/phpunit-bridge/bootstrap.php b/vendor/symfony/phpunit-bridge/bootstrap.php new file mode 100644 index 0000000..0a43d98 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/bootstrap.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Bridge\PhpUnit\DeprecationErrorHandler; + +// Detect if we're loaded by an actual run of phpunit +if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Command', false)) { + return; +} + +if (PHP_VERSION_ID >= 50400 && gc_enabled()) { + // Disabling Zend Garbage Collection to prevent segfaults with PHP5.4+ + // https://bugs.php.net/bug.php?id=53976 + gc_disable(); +} + +// Enforce a consistent locale +setlocale(LC_ALL, 'C'); + +if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) { + AnnotationRegistry::registerLoader('class_exists'); +} + +DeprecationErrorHandler::register(getenv('SYMFONY_DEPRECATIONS_HELPER')); diff --git a/vendor/symfony/phpunit-bridge/composer.json b/vendor/symfony/phpunit-bridge/composer.json new file mode 100644 index 0000000..4e94236 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/phpunit-bridge", + "type": "symfony-bridge", + "description": "Symfony PHPUnit Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "autoload": { + "files": [ "bootstrap.php" ], + "psr-4": { "Symfony\\Bridge\\PhpUnit\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + } +} diff --git a/vendor/symfony/phpunit-bridge/phpunit.xml.dist b/vendor/symfony/phpunit-bridge/phpunit.xml.dist new file mode 100644 index 0000000..7f631b2 --- /dev/null +++ b/vendor/symfony/phpunit-bridge/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/polyfill-intl-icu/LICENSE b/vendor/symfony/polyfill-intl-icu/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/polyfill-intl-icu/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-intl-icu/README.md b/vendor/symfony/polyfill-intl-icu/README.md new file mode 100644 index 0000000..207e006 --- /dev/null +++ b/vendor/symfony/polyfill-intl-icu/README.md @@ -0,0 +1,23 @@ +Symfony Polyfill / Intl: ICU +============================ + +This component maps a collection of functions/classes using the +[`symfony/intl`](https://github.com/symfony/intl) package in the abscense of the +[Intl](http://php.net/intl) extension, including: + +- [`intl_is_failure()`](http://php.net/manual/en/function.intl-is-failure.php) +- [`intl_get_error_code()`](http://php.net/manual/en/function.intl-get-error-code.php) +- [`intl_get_error_message()`](http://php.net/manual/en/function.intl-get-error-message.php) +- [`intl_error_name()`](http://php.net/manual/en/function.intl-error-name.php) +- [`Collator`](http://php.net/Collator) +- [`NumberFormatter`](http://php.net/NumberFormatter) +- [`Locale`](http://php.net/Locale) +- [`IntlDateFormatter`](http://php.net/IntlDateFormatter) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). \ No newline at end of file diff --git a/vendor/symfony/polyfill-intl-icu/bootstrap.php b/vendor/symfony/polyfill-intl-icu/bootstrap.php new file mode 100644 index 0000000..e51ba5e --- /dev/null +++ b/vendor/symfony/polyfill-intl-icu/bootstrap.php @@ -0,0 +1,19 @@ + + * + * 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')) { + function intl_is_failure($errorCode) { return IntlGlobals::isFailure($errorCode); } + function intl_get_error_code() { return IntlGlobals::getErrorCode(); } + function intl_get_error_message() { return IntlGlobals::getErrorMessage(); } + function intl_error_name($errorCode) { return IntlGlobals::getErrorName($errorCode); } +} diff --git a/vendor/symfony/polyfill-intl-icu/composer.json b/vendor/symfony/polyfill-intl-icu/composer.json new file mode 100644 index 0000000..10c8aee --- /dev/null +++ b/vendor/symfony/polyfill-intl-icu/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-intl-icu", + "type": "library", + "description": "Symfony polyfill for intl's ICU-related data and classes", + "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "icu"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0" + }, + "autoload": { + "files": [ "bootstrap.php" ] + }, + "suggests": { + "ext-intl": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 0000000..31f678b --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,604 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_convert_case - Perform case folding on a string + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_strlen - Get string length + * - mb_strpos - Find position of first occurrence of string in a string + * - mb_strrpos - Find position of last occurrence of a string in a string + * - mb_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within anothers + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas + * + * @internal + */ +final class Mbstring +{ + const MB_CASE_FOLD = PHP_INT_MAX; + + private static $encodingList = array('ASCII', 'UTF-8'); + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = array( + array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), + array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'), + ); + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding, $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + { + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + if ('' === $s .= '') { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + } else { + $s = iconv($encoding, 'UTF-8', $s); + } + + if (MB_CASE_TITLE == $mode) { + $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); + $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); + } else { + if (MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $i = 0; + $len = strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding, $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { + self::$internalEncoding = $encoding; + + return true; + } + + return false; + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($lang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $lang; + + return true; + } + + return false; + } + + public static function mb_list_encodings() + { + return array('UTF-8'); + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return array('utf8'); + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + return iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('' === $needle .= '') { + trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + + return false; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (0 === strcasecmp($c, 'none')) { + return true; + } + + return null !== $c ? false : 'none'; + } + + public static function mb_substr($s, $start, $length = null, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return iconv_substr($s, $start, $length, $encoding).''; + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = array( + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ); + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback($m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= chr($m[$i++]); + continue; + } + 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; + } + + private static function title_case_lower($s) + { + return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); + } + + private static function title_case_upper($s) + { + return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.ser')) { + return unserialize(file_get_contents($file)); + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 0000000..091152f --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](http://php.net/mbstring) extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). \ No newline at end of file diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.ser b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.ser new file mode 100644 index 0000000..13dabdd --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.ser @@ -0,0 +1 @@ +a:1092:{s:1:"A";s:1:"a";s:1:"B";s:1:"b";s:1:"C";s:1:"c";s:1:"D";s:1:"d";s:1:"E";s:1:"e";s:1:"F";s:1:"f";s:1:"G";s:1:"g";s:1:"H";s:1:"h";s:1:"I";s:1:"i";s:1:"J";s:1:"j";s:1:"K";s:1:"k";s:1:"L";s:1:"l";s:1:"M";s:1:"m";s:1:"N";s:1:"n";s:1:"O";s:1:"o";s:1:"P";s:1:"p";s:1:"Q";s:1:"q";s:1:"R";s:1:"r";s:1:"S";s:1:"s";s:1:"T";s:1:"t";s:1:"U";s:1:"u";s:1:"V";s:1:"v";s:1:"W";s:1:"w";s:1:"X";s:1:"x";s:1:"Y";s:1:"y";s:1:"Z";s:1:"z";s:2:"À";s:2:"à";s:2:"Á";s:2:"á";s:2:"Â";s:2:"â";s:2:"Ã";s:2:"ã";s:2:"Ä";s:2:"ä";s:2:"Å";s:2:"å";s:2:"Æ";s:2:"æ";s:2:"Ç";s:2:"ç";s:2:"È";s:2:"è";s:2:"É";s:2:"é";s:2:"Ê";s:2:"ê";s:2:"Ë";s:2:"ë";s:2:"Ì";s:2:"ì";s:2:"Í";s:2:"í";s:2:"Î";s:2:"î";s:2:"Ï";s:2:"ï";s:2:"Ð";s:2:"ð";s:2:"Ñ";s:2:"ñ";s:2:"Ò";s:2:"ò";s:2:"Ó";s:2:"ó";s:2:"Ô";s:2:"ô";s:2:"Õ";s:2:"õ";s:2:"Ö";s:2:"ö";s:2:"Ø";s:2:"ø";s:2:"Ù";s:2:"ù";s:2:"Ú";s:2:"ú";s:2:"Û";s:2:"û";s:2:"Ü";s:2:"ü";s:2:"Ý";s:2:"ý";s:2:"Þ";s:2:"þ";s:2:"Ā";s:2:"ā";s:2:"Ă";s:2:"ă";s:2:"Ą";s:2:"ą";s:2:"Ć";s:2:"ć";s:2:"Ĉ";s:2:"ĉ";s:2:"Ċ";s:2:"ċ";s:2:"Č";s:2:"č";s:2:"Ď";s:2:"ď";s:2:"Đ";s:2:"đ";s:2:"Ē";s:2:"ē";s:2:"Ĕ";s:2:"ĕ";s:2:"Ė";s:2:"ė";s:2:"Ę";s:2:"ę";s:2:"Ě";s:2:"ě";s:2:"Ĝ";s:2:"ĝ";s:2:"Ğ";s:2:"ğ";s:2:"Ġ";s:2:"ġ";s:2:"Ģ";s:2:"ģ";s:2:"Ĥ";s:2:"ĥ";s:2:"Ħ";s:2:"ħ";s:2:"Ĩ";s:2:"ĩ";s:2:"Ī";s:2:"ī";s:2:"Ĭ";s:2:"ĭ";s:2:"Į";s:2:"į";s:2:"İ";s:1:"i";s:2:"IJ";s:2:"ij";s:2:"Ĵ";s:2:"ĵ";s:2:"Ķ";s:2:"ķ";s:2:"Ĺ";s:2:"ĺ";s:2:"Ļ";s:2:"ļ";s:2:"Ľ";s:2:"ľ";s:2:"Ŀ";s:2:"ŀ";s:2:"Ł";s:2:"ł";s:2:"Ń";s:2:"ń";s:2:"Ņ";s:2:"ņ";s:2:"Ň";s:2:"ň";s:2:"Ŋ";s:2:"ŋ";s:2:"Ō";s:2:"ō";s:2:"Ŏ";s:2:"ŏ";s:2:"Ő";s:2:"ő";s:2:"Œ";s:2:"œ";s:2:"Ŕ";s:2:"ŕ";s:2:"Ŗ";s:2:"ŗ";s:2:"Ř";s:2:"ř";s:2:"Ś";s:2:"ś";s:2:"Ŝ";s:2:"ŝ";s:2:"Ş";s:2:"ş";s:2:"Š";s:2:"š";s:2:"Ţ";s:2:"ţ";s:2:"Ť";s:2:"ť";s:2:"Ŧ";s:2:"ŧ";s:2:"Ũ";s:2:"ũ";s:2:"Ū";s:2:"ū";s:2:"Ŭ";s:2:"ŭ";s:2:"Ů";s:2:"ů";s:2:"Ű";s:2:"ű";s:2:"Ų";s:2:"ų";s:2:"Ŵ";s:2:"ŵ";s:2:"Ŷ";s:2:"ŷ";s:2:"Ÿ";s:2:"ÿ";s:2:"Ź";s:2:"ź";s:2:"Ż";s:2:"ż";s:2:"Ž";s:2:"ž";s:2:"Ɓ";s:2:"ɓ";s:2:"Ƃ";s:2:"ƃ";s:2:"Ƅ";s:2:"ƅ";s:2:"Ɔ";s:2:"ɔ";s:2:"Ƈ";s:2:"ƈ";s:2:"Ɖ";s:2:"ɖ";s:2:"Ɗ";s:2:"ɗ";s:2:"Ƌ";s:2:"ƌ";s:2:"Ǝ";s:2:"ǝ";s:2:"Ə";s:2:"ə";s:2:"Ɛ";s:2:"ɛ";s:2:"Ƒ";s:2:"ƒ";s:2:"Ɠ";s:2:"ɠ";s:2:"Ɣ";s:2:"ɣ";s:2:"Ɩ";s:2:"ɩ";s:2:"Ɨ";s:2:"ɨ";s:2:"Ƙ";s:2:"ƙ";s:2:"Ɯ";s:2:"ɯ";s:2:"Ɲ";s:2:"ɲ";s:2:"Ɵ";s:2:"ɵ";s:2:"Ơ";s:2:"ơ";s:2:"Ƣ";s:2:"ƣ";s:2:"Ƥ";s:2:"ƥ";s:2:"Ʀ";s:2:"ʀ";s:2:"Ƨ";s:2:"ƨ";s:2:"Ʃ";s:2:"ʃ";s:2:"Ƭ";s:2:"ƭ";s:2:"Ʈ";s:2:"ʈ";s:2:"Ư";s:2:"ư";s:2:"Ʊ";s:2:"ʊ";s:2:"Ʋ";s:2:"ʋ";s:2:"Ƴ";s:2:"ƴ";s:2:"Ƶ";s:2:"ƶ";s:2:"Ʒ";s:2:"ʒ";s:2:"Ƹ";s:2:"ƹ";s:2:"Ƽ";s:2:"ƽ";s:2:"DŽ";s:2:"dž";s:2:"Dž";s:2:"dž";s:2:"LJ";s:2:"lj";s:2:"Lj";s:2:"lj";s:2:"NJ";s:2:"nj";s:2:"Nj";s:2:"nj";s:2:"Ǎ";s:2:"ǎ";s:2:"Ǐ";s:2:"ǐ";s:2:"Ǒ";s:2:"ǒ";s:2:"Ǔ";s:2:"ǔ";s:2:"Ǖ";s:2:"ǖ";s:2:"Ǘ";s:2:"ǘ";s:2:"Ǚ";s:2:"ǚ";s:2:"Ǜ";s:2:"ǜ";s:2:"Ǟ";s:2:"ǟ";s:2:"Ǡ";s:2:"ǡ";s:2:"Ǣ";s:2:"ǣ";s:2:"Ǥ";s:2:"ǥ";s:2:"Ǧ";s:2:"ǧ";s:2:"Ǩ";s:2:"ǩ";s:2:"Ǫ";s:2:"ǫ";s:2:"Ǭ";s:2:"ǭ";s:2:"Ǯ";s:2:"ǯ";s:2:"DZ";s:2:"dz";s:2:"Dz";s:2:"dz";s:2:"Ǵ";s:2:"ǵ";s:2:"Ƕ";s:2:"ƕ";s:2:"Ƿ";s:2:"ƿ";s:2:"Ǹ";s:2:"ǹ";s:2:"Ǻ";s:2:"ǻ";s:2:"Ǽ";s:2:"ǽ";s:2:"Ǿ";s:2:"ǿ";s:2:"Ȁ";s:2:"ȁ";s:2:"Ȃ";s:2:"ȃ";s:2:"Ȅ";s:2:"ȅ";s:2:"Ȇ";s:2:"ȇ";s:2:"Ȉ";s:2:"ȉ";s:2:"Ȋ";s:2:"ȋ";s:2:"Ȍ";s:2:"ȍ";s:2:"Ȏ";s:2:"ȏ";s:2:"Ȑ";s:2:"ȑ";s:2:"Ȓ";s:2:"ȓ";s:2:"Ȕ";s:2:"ȕ";s:2:"Ȗ";s:2:"ȗ";s:2:"Ș";s:2:"ș";s:2:"Ț";s:2:"ț";s:2:"Ȝ";s:2:"ȝ";s:2:"Ȟ";s:2:"ȟ";s:2:"Ƞ";s:2:"ƞ";s:2:"Ȣ";s:2:"ȣ";s:2:"Ȥ";s:2:"ȥ";s:2:"Ȧ";s:2:"ȧ";s:2:"Ȩ";s:2:"ȩ";s:2:"Ȫ";s:2:"ȫ";s:2:"Ȭ";s:2:"ȭ";s:2:"Ȯ";s:2:"ȯ";s:2:"Ȱ";s:2:"ȱ";s:2:"Ȳ";s:2:"ȳ";s:2:"Ⱥ";s:3:"ⱥ";s:2:"Ȼ";s:2:"ȼ";s:2:"Ƚ";s:2:"ƚ";s:2:"Ⱦ";s:3:"ⱦ";s:2:"Ɂ";s:2:"ɂ";s:2:"Ƀ";s:2:"ƀ";s:2:"Ʉ";s:2:"ʉ";s:2:"Ʌ";s:2:"ʌ";s:2:"Ɇ";s:2:"ɇ";s:2:"Ɉ";s:2:"ɉ";s:2:"Ɋ";s:2:"ɋ";s:2:"Ɍ";s:2:"ɍ";s:2:"Ɏ";s:2:"ɏ";s:2:"Ͱ";s:2:"ͱ";s:2:"Ͳ";s:2:"ͳ";s:2:"Ͷ";s:2:"ͷ";s:2:"Ϳ";s:2:"ϳ";s:2:"Ά";s:2:"ά";s:2:"Έ";s:2:"έ";s:2:"Ή";s:2:"ή";s:2:"Ί";s:2:"ί";s:2:"Ό";s:2:"ό";s:2:"Ύ";s:2:"ύ";s:2:"Ώ";s:2:"ώ";s:2:"Α";s:2:"α";s:2:"Β";s:2:"β";s:2:"Γ";s:2:"γ";s:2:"Δ";s:2:"δ";s:2:"Ε";s:2:"ε";s:2:"Ζ";s:2:"ζ";s:2:"Η";s:2:"η";s:2:"Θ";s:2:"θ";s:2:"Ι";s:2:"ι";s:2:"Κ";s:2:"κ";s:2:"Λ";s:2:"λ";s:2:"Μ";s:2:"μ";s:2:"Ν";s:2:"ν";s:2:"Ξ";s:2:"ξ";s:2:"Ο";s:2:"ο";s:2:"Π";s:2:"π";s:2:"Ρ";s:2:"ρ";s:2:"Σ";s:2:"σ";s:2:"Τ";s:2:"τ";s:2:"Υ";s:2:"υ";s:2:"Φ";s:2:"φ";s:2:"Χ";s:2:"χ";s:2:"Ψ";s:2:"ψ";s:2:"Ω";s:2:"ω";s:2:"Ϊ";s:2:"ϊ";s:2:"Ϋ";s:2:"ϋ";s:2:"Ϗ";s:2:"ϗ";s:2:"Ϙ";s:2:"ϙ";s:2:"Ϛ";s:2:"ϛ";s:2:"Ϝ";s:2:"ϝ";s:2:"Ϟ";s:2:"ϟ";s:2:"Ϡ";s:2:"ϡ";s:2:"Ϣ";s:2:"ϣ";s:2:"Ϥ";s:2:"ϥ";s:2:"Ϧ";s:2:"ϧ";s:2:"Ϩ";s:2:"ϩ";s:2:"Ϫ";s:2:"ϫ";s:2:"Ϭ";s:2:"ϭ";s:2:"Ϯ";s:2:"ϯ";s:2:"ϴ";s:2:"θ";s:2:"Ϸ";s:2:"ϸ";s:2:"Ϲ";s:2:"ϲ";s:2:"Ϻ";s:2:"ϻ";s:2:"Ͻ";s:2:"ͻ";s:2:"Ͼ";s:2:"ͼ";s:2:"Ͽ";s:2:"ͽ";s:2:"Ѐ";s:2:"ѐ";s:2:"Ё";s:2:"ё";s:2:"Ђ";s:2:"ђ";s:2:"Ѓ";s:2:"ѓ";s:2:"Є";s:2:"є";s:2:"Ѕ";s:2:"ѕ";s:2:"І";s:2:"і";s:2:"Ї";s:2:"ї";s:2:"Ј";s:2:"ј";s:2:"Љ";s:2:"љ";s:2:"Њ";s:2:"њ";s:2:"Ћ";s:2:"ћ";s:2:"Ќ";s:2:"ќ";s:2:"Ѝ";s:2:"ѝ";s:2:"Ў";s:2:"ў";s:2:"Џ";s:2:"џ";s:2:"А";s:2:"а";s:2:"Б";s:2:"б";s:2:"В";s:2:"в";s:2:"Г";s:2:"г";s:2:"Д";s:2:"д";s:2:"Е";s:2:"е";s:2:"Ж";s:2:"ж";s:2:"З";s:2:"з";s:2:"И";s:2:"и";s:2:"Й";s:2:"й";s:2:"К";s:2:"к";s:2:"Л";s:2:"л";s:2:"М";s:2:"м";s:2:"Н";s:2:"н";s:2:"О";s:2:"о";s:2:"П";s:2:"п";s:2:"Р";s:2:"р";s:2:"С";s:2:"с";s:2:"Т";s:2:"т";s:2:"У";s:2:"у";s:2:"Ф";s:2:"ф";s:2:"Х";s:2:"х";s:2:"Ц";s:2:"ц";s:2:"Ч";s:2:"ч";s:2:"Ш";s:2:"ш";s:2:"Щ";s:2:"щ";s:2:"Ъ";s:2:"ъ";s:2:"Ы";s:2:"ы";s:2:"Ь";s:2:"ь";s:2:"Э";s:2:"э";s:2:"Ю";s:2:"ю";s:2:"Я";s:2:"я";s:2:"Ѡ";s:2:"ѡ";s:2:"Ѣ";s:2:"ѣ";s:2:"Ѥ";s:2:"ѥ";s:2:"Ѧ";s:2:"ѧ";s:2:"Ѩ";s:2:"ѩ";s:2:"Ѫ";s:2:"ѫ";s:2:"Ѭ";s:2:"ѭ";s:2:"Ѯ";s:2:"ѯ";s:2:"Ѱ";s:2:"ѱ";s:2:"Ѳ";s:2:"ѳ";s:2:"Ѵ";s:2:"ѵ";s:2:"Ѷ";s:2:"ѷ";s:2:"Ѹ";s:2:"ѹ";s:2:"Ѻ";s:2:"ѻ";s:2:"Ѽ";s:2:"ѽ";s:2:"Ѿ";s:2:"ѿ";s:2:"Ҁ";s:2:"ҁ";s:2:"Ҋ";s:2:"ҋ";s:2:"Ҍ";s:2:"ҍ";s:2:"Ҏ";s:2:"ҏ";s:2:"Ґ";s:2:"ґ";s:2:"Ғ";s:2:"ғ";s:2:"Ҕ";s:2:"ҕ";s:2:"Җ";s:2:"җ";s:2:"Ҙ";s:2:"ҙ";s:2:"Қ";s:2:"қ";s:2:"Ҝ";s:2:"ҝ";s:2:"Ҟ";s:2:"ҟ";s:2:"Ҡ";s:2:"ҡ";s:2:"Ң";s:2:"ң";s:2:"Ҥ";s:2:"ҥ";s:2:"Ҧ";s:2:"ҧ";s:2:"Ҩ";s:2:"ҩ";s:2:"Ҫ";s:2:"ҫ";s:2:"Ҭ";s:2:"ҭ";s:2:"Ү";s:2:"ү";s:2:"Ұ";s:2:"ұ";s:2:"Ҳ";s:2:"ҳ";s:2:"Ҵ";s:2:"ҵ";s:2:"Ҷ";s:2:"ҷ";s:2:"Ҹ";s:2:"ҹ";s:2:"Һ";s:2:"һ";s:2:"Ҽ";s:2:"ҽ";s:2:"Ҿ";s:2:"ҿ";s:2:"Ӏ";s:2:"ӏ";s:2:"Ӂ";s:2:"ӂ";s:2:"Ӄ";s:2:"ӄ";s:2:"Ӆ";s:2:"ӆ";s:2:"Ӈ";s:2:"ӈ";s:2:"Ӊ";s:2:"ӊ";s:2:"Ӌ";s:2:"ӌ";s:2:"Ӎ";s:2:"ӎ";s:2:"Ӑ";s:2:"ӑ";s:2:"Ӓ";s:2:"ӓ";s:2:"Ӕ";s:2:"ӕ";s:2:"Ӗ";s:2:"ӗ";s:2:"Ә";s:2:"ә";s:2:"Ӛ";s:2:"ӛ";s:2:"Ӝ";s:2:"ӝ";s:2:"Ӟ";s:2:"ӟ";s:2:"Ӡ";s:2:"ӡ";s:2:"Ӣ";s:2:"ӣ";s:2:"Ӥ";s:2:"ӥ";s:2:"Ӧ";s:2:"ӧ";s:2:"Ө";s:2:"ө";s:2:"Ӫ";s:2:"ӫ";s:2:"Ӭ";s:2:"ӭ";s:2:"Ӯ";s:2:"ӯ";s:2:"Ӱ";s:2:"ӱ";s:2:"Ӳ";s:2:"ӳ";s:2:"Ӵ";s:2:"ӵ";s:2:"Ӷ";s:2:"ӷ";s:2:"Ӹ";s:2:"ӹ";s:2:"Ӻ";s:2:"ӻ";s:2:"Ӽ";s:2:"ӽ";s:2:"Ӿ";s:2:"ӿ";s:2:"Ԁ";s:2:"ԁ";s:2:"Ԃ";s:2:"ԃ";s:2:"Ԅ";s:2:"ԅ";s:2:"Ԇ";s:2:"ԇ";s:2:"Ԉ";s:2:"ԉ";s:2:"Ԋ";s:2:"ԋ";s:2:"Ԍ";s:2:"ԍ";s:2:"Ԏ";s:2:"ԏ";s:2:"Ԑ";s:2:"ԑ";s:2:"Ԓ";s:2:"ԓ";s:2:"Ԕ";s:2:"ԕ";s:2:"Ԗ";s:2:"ԗ";s:2:"Ԙ";s:2:"ԙ";s:2:"Ԛ";s:2:"ԛ";s:2:"Ԝ";s:2:"ԝ";s:2:"Ԟ";s:2:"ԟ";s:2:"Ԡ";s:2:"ԡ";s:2:"Ԣ";s:2:"ԣ";s:2:"Ԥ";s:2:"ԥ";s:2:"Ԧ";s:2:"ԧ";s:2:"Ԩ";s:2:"ԩ";s:2:"Ԫ";s:2:"ԫ";s:2:"Ԭ";s:2:"ԭ";s:2:"Ԯ";s:2:"ԯ";s:2:"Ա";s:2:"ա";s:2:"Բ";s:2:"բ";s:2:"Գ";s:2:"գ";s:2:"Դ";s:2:"դ";s:2:"Ե";s:2:"ե";s:2:"Զ";s:2:"զ";s:2:"Է";s:2:"է";s:2:"Ը";s:2:"ը";s:2:"Թ";s:2:"թ";s:2:"Ժ";s:2:"ժ";s:2:"Ի";s:2:"ի";s:2:"Լ";s:2:"լ";s:2:"Խ";s:2:"խ";s:2:"Ծ";s:2:"ծ";s:2:"Կ";s:2:"կ";s:2:"Հ";s:2:"հ";s:2:"Ձ";s:2:"ձ";s:2:"Ղ";s:2:"ղ";s:2:"Ճ";s:2:"ճ";s:2:"Մ";s:2:"մ";s:2:"Յ";s:2:"յ";s:2:"Ն";s:2:"ն";s:2:"Շ";s:2:"շ";s:2:"Ո";s:2:"ո";s:2:"Չ";s:2:"չ";s:2:"Պ";s:2:"պ";s:2:"Ջ";s:2:"ջ";s:2:"Ռ";s:2:"ռ";s:2:"Ս";s:2:"ս";s:2:"Վ";s:2:"վ";s:2:"Տ";s:2:"տ";s:2:"Ր";s:2:"ր";s:2:"Ց";s:2:"ց";s:2:"Ւ";s:2:"ւ";s:2:"Փ";s:2:"փ";s:2:"Ք";s:2:"ք";s:2:"Օ";s:2:"օ";s:2:"Ֆ";s:2:"ֆ";s:3:"Ⴀ";s:3:"ⴀ";s:3:"Ⴁ";s:3:"ⴁ";s:3:"Ⴂ";s:3:"ⴂ";s:3:"Ⴃ";s:3:"ⴃ";s:3:"Ⴄ";s:3:"ⴄ";s:3:"Ⴅ";s:3:"ⴅ";s:3:"Ⴆ";s:3:"ⴆ";s:3:"Ⴇ";s:3:"ⴇ";s:3:"Ⴈ";s:3:"ⴈ";s:3:"Ⴉ";s:3:"ⴉ";s:3:"Ⴊ";s:3:"ⴊ";s:3:"Ⴋ";s:3:"ⴋ";s:3:"Ⴌ";s:3:"ⴌ";s:3:"Ⴍ";s:3:"ⴍ";s:3:"Ⴎ";s:3:"ⴎ";s:3:"Ⴏ";s:3:"ⴏ";s:3:"Ⴐ";s:3:"ⴐ";s:3:"Ⴑ";s:3:"ⴑ";s:3:"Ⴒ";s:3:"ⴒ";s:3:"Ⴓ";s:3:"ⴓ";s:3:"Ⴔ";s:3:"ⴔ";s:3:"Ⴕ";s:3:"ⴕ";s:3:"Ⴖ";s:3:"ⴖ";s:3:"Ⴗ";s:3:"ⴗ";s:3:"Ⴘ";s:3:"ⴘ";s:3:"Ⴙ";s:3:"ⴙ";s:3:"Ⴚ";s:3:"ⴚ";s:3:"Ⴛ";s:3:"ⴛ";s:3:"Ⴜ";s:3:"ⴜ";s:3:"Ⴝ";s:3:"ⴝ";s:3:"Ⴞ";s:3:"ⴞ";s:3:"Ⴟ";s:3:"ⴟ";s:3:"Ⴠ";s:3:"ⴠ";s:3:"Ⴡ";s:3:"ⴡ";s:3:"Ⴢ";s:3:"ⴢ";s:3:"Ⴣ";s:3:"ⴣ";s:3:"Ⴤ";s:3:"ⴤ";s:3:"Ⴥ";s:3:"ⴥ";s:3:"Ⴧ";s:3:"ⴧ";s:3:"Ⴭ";s:3:"ⴭ";s:3:"Ḁ";s:3:"ḁ";s:3:"Ḃ";s:3:"ḃ";s:3:"Ḅ";s:3:"ḅ";s:3:"Ḇ";s:3:"ḇ";s:3:"Ḉ";s:3:"ḉ";s:3:"Ḋ";s:3:"ḋ";s:3:"Ḍ";s:3:"ḍ";s:3:"Ḏ";s:3:"ḏ";s:3:"Ḑ";s:3:"ḑ";s:3:"Ḓ";s:3:"ḓ";s:3:"Ḕ";s:3:"ḕ";s:3:"Ḗ";s:3:"ḗ";s:3:"Ḙ";s:3:"ḙ";s:3:"Ḛ";s:3:"ḛ";s:3:"Ḝ";s:3:"ḝ";s:3:"Ḟ";s:3:"ḟ";s:3:"Ḡ";s:3:"ḡ";s:3:"Ḣ";s:3:"ḣ";s:3:"Ḥ";s:3:"ḥ";s:3:"Ḧ";s:3:"ḧ";s:3:"Ḩ";s:3:"ḩ";s:3:"Ḫ";s:3:"ḫ";s:3:"Ḭ";s:3:"ḭ";s:3:"Ḯ";s:3:"ḯ";s:3:"Ḱ";s:3:"ḱ";s:3:"Ḳ";s:3:"ḳ";s:3:"Ḵ";s:3:"ḵ";s:3:"Ḷ";s:3:"ḷ";s:3:"Ḹ";s:3:"ḹ";s:3:"Ḻ";s:3:"ḻ";s:3:"Ḽ";s:3:"ḽ";s:3:"Ḿ";s:3:"ḿ";s:3:"Ṁ";s:3:"ṁ";s:3:"Ṃ";s:3:"ṃ";s:3:"Ṅ";s:3:"ṅ";s:3:"Ṇ";s:3:"ṇ";s:3:"Ṉ";s:3:"ṉ";s:3:"Ṋ";s:3:"ṋ";s:3:"Ṍ";s:3:"ṍ";s:3:"Ṏ";s:3:"ṏ";s:3:"Ṑ";s:3:"ṑ";s:3:"Ṓ";s:3:"ṓ";s:3:"Ṕ";s:3:"ṕ";s:3:"Ṗ";s:3:"ṗ";s:3:"Ṙ";s:3:"ṙ";s:3:"Ṛ";s:3:"ṛ";s:3:"Ṝ";s:3:"ṝ";s:3:"Ṟ";s:3:"ṟ";s:3:"Ṡ";s:3:"ṡ";s:3:"Ṣ";s:3:"ṣ";s:3:"Ṥ";s:3:"ṥ";s:3:"Ṧ";s:3:"ṧ";s:3:"Ṩ";s:3:"ṩ";s:3:"Ṫ";s:3:"ṫ";s:3:"Ṭ";s:3:"ṭ";s:3:"Ṯ";s:3:"ṯ";s:3:"Ṱ";s:3:"ṱ";s:3:"Ṳ";s:3:"ṳ";s:3:"Ṵ";s:3:"ṵ";s:3:"Ṷ";s:3:"ṷ";s:3:"Ṹ";s:3:"ṹ";s:3:"Ṻ";s:3:"ṻ";s:3:"Ṽ";s:3:"ṽ";s:3:"Ṿ";s:3:"ṿ";s:3:"Ẁ";s:3:"ẁ";s:3:"Ẃ";s:3:"ẃ";s:3:"Ẅ";s:3:"ẅ";s:3:"Ẇ";s:3:"ẇ";s:3:"Ẉ";s:3:"ẉ";s:3:"Ẋ";s:3:"ẋ";s:3:"Ẍ";s:3:"ẍ";s:3:"Ẏ";s:3:"ẏ";s:3:"Ẑ";s:3:"ẑ";s:3:"Ẓ";s:3:"ẓ";s:3:"Ẕ";s:3:"ẕ";s:3:"ẞ";s:2:"ß";s:3:"Ạ";s:3:"ạ";s:3:"Ả";s:3:"ả";s:3:"Ấ";s:3:"ấ";s:3:"Ầ";s:3:"ầ";s:3:"Ẩ";s:3:"ẩ";s:3:"Ẫ";s:3:"ẫ";s:3:"Ậ";s:3:"ậ";s:3:"Ắ";s:3:"ắ";s:3:"Ằ";s:3:"ằ";s:3:"Ẳ";s:3:"ẳ";s:3:"Ẵ";s:3:"ẵ";s:3:"Ặ";s:3:"ặ";s:3:"Ẹ";s:3:"ẹ";s:3:"Ẻ";s:3:"ẻ";s:3:"Ẽ";s:3:"ẽ";s:3:"Ế";s:3:"ế";s:3:"Ề";s:3:"ề";s:3:"Ể";s:3:"ể";s:3:"Ễ";s:3:"ễ";s:3:"Ệ";s:3:"ệ";s:3:"Ỉ";s:3:"ỉ";s:3:"Ị";s:3:"ị";s:3:"Ọ";s:3:"ọ";s:3:"Ỏ";s:3:"ỏ";s:3:"Ố";s:3:"ố";s:3:"Ồ";s:3:"ồ";s:3:"Ổ";s:3:"ổ";s:3:"Ỗ";s:3:"ỗ";s:3:"Ộ";s:3:"ộ";s:3:"Ớ";s:3:"ớ";s:3:"Ờ";s:3:"ờ";s:3:"Ở";s:3:"ở";s:3:"Ỡ";s:3:"ỡ";s:3:"Ợ";s:3:"ợ";s:3:"Ụ";s:3:"ụ";s:3:"Ủ";s:3:"ủ";s:3:"Ứ";s:3:"ứ";s:3:"Ừ";s:3:"ừ";s:3:"Ử";s:3:"ử";s:3:"Ữ";s:3:"ữ";s:3:"Ự";s:3:"ự";s:3:"Ỳ";s:3:"ỳ";s:3:"Ỵ";s:3:"ỵ";s:3:"Ỷ";s:3:"ỷ";s:3:"Ỹ";s:3:"ỹ";s:3:"Ỻ";s:3:"ỻ";s:3:"Ỽ";s:3:"ỽ";s:3:"Ỿ";s:3:"ỿ";s:3:"Ἀ";s:3:"ἀ";s:3:"Ἁ";s:3:"ἁ";s:3:"Ἂ";s:3:"ἂ";s:3:"Ἃ";s:3:"ἃ";s:3:"Ἄ";s:3:"ἄ";s:3:"Ἅ";s:3:"ἅ";s:3:"Ἆ";s:3:"ἆ";s:3:"Ἇ";s:3:"ἇ";s:3:"Ἐ";s:3:"ἐ";s:3:"Ἑ";s:3:"ἑ";s:3:"Ἒ";s:3:"ἒ";s:3:"Ἓ";s:3:"ἓ";s:3:"Ἔ";s:3:"ἔ";s:3:"Ἕ";s:3:"ἕ";s:3:"Ἠ";s:3:"ἠ";s:3:"Ἡ";s:3:"ἡ";s:3:"Ἢ";s:3:"ἢ";s:3:"Ἣ";s:3:"ἣ";s:3:"Ἤ";s:3:"ἤ";s:3:"Ἥ";s:3:"ἥ";s:3:"Ἦ";s:3:"ἦ";s:3:"Ἧ";s:3:"ἧ";s:3:"Ἰ";s:3:"ἰ";s:3:"Ἱ";s:3:"ἱ";s:3:"Ἲ";s:3:"ἲ";s:3:"Ἳ";s:3:"ἳ";s:3:"Ἴ";s:3:"ἴ";s:3:"Ἵ";s:3:"ἵ";s:3:"Ἶ";s:3:"ἶ";s:3:"Ἷ";s:3:"ἷ";s:3:"Ὀ";s:3:"ὀ";s:3:"Ὁ";s:3:"ὁ";s:3:"Ὂ";s:3:"ὂ";s:3:"Ὃ";s:3:"ὃ";s:3:"Ὄ";s:3:"ὄ";s:3:"Ὅ";s:3:"ὅ";s:3:"Ὑ";s:3:"ὑ";s:3:"Ὓ";s:3:"ὓ";s:3:"Ὕ";s:3:"ὕ";s:3:"Ὗ";s:3:"ὗ";s:3:"Ὠ";s:3:"ὠ";s:3:"Ὡ";s:3:"ὡ";s:3:"Ὢ";s:3:"ὢ";s:3:"Ὣ";s:3:"ὣ";s:3:"Ὤ";s:3:"ὤ";s:3:"Ὥ";s:3:"ὥ";s:3:"Ὦ";s:3:"ὦ";s:3:"Ὧ";s:3:"ὧ";s:3:"ᾈ";s:3:"ᾀ";s:3:"ᾉ";s:3:"ᾁ";s:3:"ᾊ";s:3:"ᾂ";s:3:"ᾋ";s:3:"ᾃ";s:3:"ᾌ";s:3:"ᾄ";s:3:"ᾍ";s:3:"ᾅ";s:3:"ᾎ";s:3:"ᾆ";s:3:"ᾏ";s:3:"ᾇ";s:3:"ᾘ";s:3:"ᾐ";s:3:"ᾙ";s:3:"ᾑ";s:3:"ᾚ";s:3:"ᾒ";s:3:"ᾛ";s:3:"ᾓ";s:3:"ᾜ";s:3:"ᾔ";s:3:"ᾝ";s:3:"ᾕ";s:3:"ᾞ";s:3:"ᾖ";s:3:"ᾟ";s:3:"ᾗ";s:3:"ᾨ";s:3:"ᾠ";s:3:"ᾩ";s:3:"ᾡ";s:3:"ᾪ";s:3:"ᾢ";s:3:"ᾫ";s:3:"ᾣ";s:3:"ᾬ";s:3:"ᾤ";s:3:"ᾭ";s:3:"ᾥ";s:3:"ᾮ";s:3:"ᾦ";s:3:"ᾯ";s:3:"ᾧ";s:3:"Ᾰ";s:3:"ᾰ";s:3:"Ᾱ";s:3:"ᾱ";s:3:"Ὰ";s:3:"ὰ";s:3:"Ά";s:3:"ά";s:3:"ᾼ";s:3:"ᾳ";s:3:"Ὲ";s:3:"ὲ";s:3:"Έ";s:3:"έ";s:3:"Ὴ";s:3:"ὴ";s:3:"Ή";s:3:"ή";s:3:"ῌ";s:3:"ῃ";s:3:"Ῐ";s:3:"ῐ";s:3:"Ῑ";s:3:"ῑ";s:3:"Ὶ";s:3:"ὶ";s:3:"Ί";s:3:"ί";s:3:"Ῠ";s:3:"ῠ";s:3:"Ῡ";s:3:"ῡ";s:3:"Ὺ";s:3:"ὺ";s:3:"Ύ";s:3:"ύ";s:3:"Ῥ";s:3:"ῥ";s:3:"Ὸ";s:3:"ὸ";s:3:"Ό";s:3:"ό";s:3:"Ὼ";s:3:"ὼ";s:3:"Ώ";s:3:"ώ";s:3:"ῼ";s:3:"ῳ";s:3:"Ω";s:2:"ω";s:3:"K";s:1:"k";s:3:"Å";s:2:"å";s:3:"Ⅎ";s:3:"ⅎ";s:3:"Ⅰ";s:3:"ⅰ";s:3:"Ⅱ";s:3:"ⅱ";s:3:"Ⅲ";s:3:"ⅲ";s:3:"Ⅳ";s:3:"ⅳ";s:3:"Ⅴ";s:3:"ⅴ";s:3:"Ⅵ";s:3:"ⅵ";s:3:"Ⅶ";s:3:"ⅶ";s:3:"Ⅷ";s:3:"ⅷ";s:3:"Ⅸ";s:3:"ⅸ";s:3:"Ⅹ";s:3:"ⅹ";s:3:"Ⅺ";s:3:"ⅺ";s:3:"Ⅻ";s:3:"ⅻ";s:3:"Ⅼ";s:3:"ⅼ";s:3:"Ⅽ";s:3:"ⅽ";s:3:"Ⅾ";s:3:"ⅾ";s:3:"Ⅿ";s:3:"ⅿ";s:3:"Ↄ";s:3:"ↄ";s:3:"Ⓐ";s:3:"ⓐ";s:3:"Ⓑ";s:3:"ⓑ";s:3:"Ⓒ";s:3:"ⓒ";s:3:"Ⓓ";s:3:"ⓓ";s:3:"Ⓔ";s:3:"ⓔ";s:3:"Ⓕ";s:3:"ⓕ";s:3:"Ⓖ";s:3:"ⓖ";s:3:"Ⓗ";s:3:"ⓗ";s:3:"Ⓘ";s:3:"ⓘ";s:3:"Ⓙ";s:3:"ⓙ";s:3:"Ⓚ";s:3:"ⓚ";s:3:"Ⓛ";s:3:"ⓛ";s:3:"Ⓜ";s:3:"ⓜ";s:3:"Ⓝ";s:3:"ⓝ";s:3:"Ⓞ";s:3:"ⓞ";s:3:"Ⓟ";s:3:"ⓟ";s:3:"Ⓠ";s:3:"ⓠ";s:3:"Ⓡ";s:3:"ⓡ";s:3:"Ⓢ";s:3:"ⓢ";s:3:"Ⓣ";s:3:"ⓣ";s:3:"Ⓤ";s:3:"ⓤ";s:3:"Ⓥ";s:3:"ⓥ";s:3:"Ⓦ";s:3:"ⓦ";s:3:"Ⓧ";s:3:"ⓧ";s:3:"Ⓨ";s:3:"ⓨ";s:3:"Ⓩ";s:3:"ⓩ";s:3:"Ⰰ";s:3:"ⰰ";s:3:"Ⰱ";s:3:"ⰱ";s:3:"Ⰲ";s:3:"ⰲ";s:3:"Ⰳ";s:3:"ⰳ";s:3:"Ⰴ";s:3:"ⰴ";s:3:"Ⰵ";s:3:"ⰵ";s:3:"Ⰶ";s:3:"ⰶ";s:3:"Ⰷ";s:3:"ⰷ";s:3:"Ⰸ";s:3:"ⰸ";s:3:"Ⰹ";s:3:"ⰹ";s:3:"Ⰺ";s:3:"ⰺ";s:3:"Ⰻ";s:3:"ⰻ";s:3:"Ⰼ";s:3:"ⰼ";s:3:"Ⰽ";s:3:"ⰽ";s:3:"Ⰾ";s:3:"ⰾ";s:3:"Ⰿ";s:3:"ⰿ";s:3:"Ⱀ";s:3:"ⱀ";s:3:"Ⱁ";s:3:"ⱁ";s:3:"Ⱂ";s:3:"ⱂ";s:3:"Ⱃ";s:3:"ⱃ";s:3:"Ⱄ";s:3:"ⱄ";s:3:"Ⱅ";s:3:"ⱅ";s:3:"Ⱆ";s:3:"ⱆ";s:3:"Ⱇ";s:3:"ⱇ";s:3:"Ⱈ";s:3:"ⱈ";s:3:"Ⱉ";s:3:"ⱉ";s:3:"Ⱊ";s:3:"ⱊ";s:3:"Ⱋ";s:3:"ⱋ";s:3:"Ⱌ";s:3:"ⱌ";s:3:"Ⱍ";s:3:"ⱍ";s:3:"Ⱎ";s:3:"ⱎ";s:3:"Ⱏ";s:3:"ⱏ";s:3:"Ⱐ";s:3:"ⱐ";s:3:"Ⱑ";s:3:"ⱑ";s:3:"Ⱒ";s:3:"ⱒ";s:3:"Ⱓ";s:3:"ⱓ";s:3:"Ⱔ";s:3:"ⱔ";s:3:"Ⱕ";s:3:"ⱕ";s:3:"Ⱖ";s:3:"ⱖ";s:3:"Ⱗ";s:3:"ⱗ";s:3:"Ⱘ";s:3:"ⱘ";s:3:"Ⱙ";s:3:"ⱙ";s:3:"Ⱚ";s:3:"ⱚ";s:3:"Ⱛ";s:3:"ⱛ";s:3:"Ⱜ";s:3:"ⱜ";s:3:"Ⱝ";s:3:"ⱝ";s:3:"Ⱞ";s:3:"ⱞ";s:3:"Ⱡ";s:3:"ⱡ";s:3:"Ɫ";s:2:"ɫ";s:3:"Ᵽ";s:3:"ᵽ";s:3:"Ɽ";s:2:"ɽ";s:3:"Ⱨ";s:3:"ⱨ";s:3:"Ⱪ";s:3:"ⱪ";s:3:"Ⱬ";s:3:"ⱬ";s:3:"Ɑ";s:2:"ɑ";s:3:"Ɱ";s:2:"ɱ";s:3:"Ɐ";s:2:"ɐ";s:3:"Ɒ";s:2:"ɒ";s:3:"Ⱳ";s:3:"ⱳ";s:3:"Ⱶ";s:3:"ⱶ";s:3:"Ȿ";s:2:"ȿ";s:3:"Ɀ";s:2:"ɀ";s:3:"Ⲁ";s:3:"ⲁ";s:3:"Ⲃ";s:3:"ⲃ";s:3:"Ⲅ";s:3:"ⲅ";s:3:"Ⲇ";s:3:"ⲇ";s:3:"Ⲉ";s:3:"ⲉ";s:3:"Ⲋ";s:3:"ⲋ";s:3:"Ⲍ";s:3:"ⲍ";s:3:"Ⲏ";s:3:"ⲏ";s:3:"Ⲑ";s:3:"ⲑ";s:3:"Ⲓ";s:3:"ⲓ";s:3:"Ⲕ";s:3:"ⲕ";s:3:"Ⲗ";s:3:"ⲗ";s:3:"Ⲙ";s:3:"ⲙ";s:3:"Ⲛ";s:3:"ⲛ";s:3:"Ⲝ";s:3:"ⲝ";s:3:"Ⲟ";s:3:"ⲟ";s:3:"Ⲡ";s:3:"ⲡ";s:3:"Ⲣ";s:3:"ⲣ";s:3:"Ⲥ";s:3:"ⲥ";s:3:"Ⲧ";s:3:"ⲧ";s:3:"Ⲩ";s:3:"ⲩ";s:3:"Ⲫ";s:3:"ⲫ";s:3:"Ⲭ";s:3:"ⲭ";s:3:"Ⲯ";s:3:"ⲯ";s:3:"Ⲱ";s:3:"ⲱ";s:3:"Ⲳ";s:3:"ⲳ";s:3:"Ⲵ";s:3:"ⲵ";s:3:"Ⲷ";s:3:"ⲷ";s:3:"Ⲹ";s:3:"ⲹ";s:3:"Ⲻ";s:3:"ⲻ";s:3:"Ⲽ";s:3:"ⲽ";s:3:"Ⲿ";s:3:"ⲿ";s:3:"Ⳁ";s:3:"ⳁ";s:3:"Ⳃ";s:3:"ⳃ";s:3:"Ⳅ";s:3:"ⳅ";s:3:"Ⳇ";s:3:"ⳇ";s:3:"Ⳉ";s:3:"ⳉ";s:3:"Ⳋ";s:3:"ⳋ";s:3:"Ⳍ";s:3:"ⳍ";s:3:"Ⳏ";s:3:"ⳏ";s:3:"Ⳑ";s:3:"ⳑ";s:3:"Ⳓ";s:3:"ⳓ";s:3:"Ⳕ";s:3:"ⳕ";s:3:"Ⳗ";s:3:"ⳗ";s:3:"Ⳙ";s:3:"ⳙ";s:3:"Ⳛ";s:3:"ⳛ";s:3:"Ⳝ";s:3:"ⳝ";s:3:"Ⳟ";s:3:"ⳟ";s:3:"Ⳡ";s:3:"ⳡ";s:3:"Ⳣ";s:3:"ⳣ";s:3:"Ⳬ";s:3:"ⳬ";s:3:"Ⳮ";s:3:"ⳮ";s:3:"Ⳳ";s:3:"ⳳ";s:3:"Ꙁ";s:3:"ꙁ";s:3:"Ꙃ";s:3:"ꙃ";s:3:"Ꙅ";s:3:"ꙅ";s:3:"Ꙇ";s:3:"ꙇ";s:3:"Ꙉ";s:3:"ꙉ";s:3:"Ꙋ";s:3:"ꙋ";s:3:"Ꙍ";s:3:"ꙍ";s:3:"Ꙏ";s:3:"ꙏ";s:3:"Ꙑ";s:3:"ꙑ";s:3:"Ꙓ";s:3:"ꙓ";s:3:"Ꙕ";s:3:"ꙕ";s:3:"Ꙗ";s:3:"ꙗ";s:3:"Ꙙ";s:3:"ꙙ";s:3:"Ꙛ";s:3:"ꙛ";s:3:"Ꙝ";s:3:"ꙝ";s:3:"Ꙟ";s:3:"ꙟ";s:3:"Ꙡ";s:3:"ꙡ";s:3:"Ꙣ";s:3:"ꙣ";s:3:"Ꙥ";s:3:"ꙥ";s:3:"Ꙧ";s:3:"ꙧ";s:3:"Ꙩ";s:3:"ꙩ";s:3:"Ꙫ";s:3:"ꙫ";s:3:"Ꙭ";s:3:"ꙭ";s:3:"Ꚁ";s:3:"ꚁ";s:3:"Ꚃ";s:3:"ꚃ";s:3:"Ꚅ";s:3:"ꚅ";s:3:"Ꚇ";s:3:"ꚇ";s:3:"Ꚉ";s:3:"ꚉ";s:3:"Ꚋ";s:3:"ꚋ";s:3:"Ꚍ";s:3:"ꚍ";s:3:"Ꚏ";s:3:"ꚏ";s:3:"Ꚑ";s:3:"ꚑ";s:3:"Ꚓ";s:3:"ꚓ";s:3:"Ꚕ";s:3:"ꚕ";s:3:"Ꚗ";s:3:"ꚗ";s:3:"Ꚙ";s:3:"ꚙ";s:3:"Ꚛ";s:3:"ꚛ";s:3:"Ꜣ";s:3:"ꜣ";s:3:"Ꜥ";s:3:"ꜥ";s:3:"Ꜧ";s:3:"ꜧ";s:3:"Ꜩ";s:3:"ꜩ";s:3:"Ꜫ";s:3:"ꜫ";s:3:"Ꜭ";s:3:"ꜭ";s:3:"Ꜯ";s:3:"ꜯ";s:3:"Ꜳ";s:3:"ꜳ";s:3:"Ꜵ";s:3:"ꜵ";s:3:"Ꜷ";s:3:"ꜷ";s:3:"Ꜹ";s:3:"ꜹ";s:3:"Ꜻ";s:3:"ꜻ";s:3:"Ꜽ";s:3:"ꜽ";s:3:"Ꜿ";s:3:"ꜿ";s:3:"Ꝁ";s:3:"ꝁ";s:3:"Ꝃ";s:3:"ꝃ";s:3:"Ꝅ";s:3:"ꝅ";s:3:"Ꝇ";s:3:"ꝇ";s:3:"Ꝉ";s:3:"ꝉ";s:3:"Ꝋ";s:3:"ꝋ";s:3:"Ꝍ";s:3:"ꝍ";s:3:"Ꝏ";s:3:"ꝏ";s:3:"Ꝑ";s:3:"ꝑ";s:3:"Ꝓ";s:3:"ꝓ";s:3:"Ꝕ";s:3:"ꝕ";s:3:"Ꝗ";s:3:"ꝗ";s:3:"Ꝙ";s:3:"ꝙ";s:3:"Ꝛ";s:3:"ꝛ";s:3:"Ꝝ";s:3:"ꝝ";s:3:"Ꝟ";s:3:"ꝟ";s:3:"Ꝡ";s:3:"ꝡ";s:3:"Ꝣ";s:3:"ꝣ";s:3:"Ꝥ";s:3:"ꝥ";s:3:"Ꝧ";s:3:"ꝧ";s:3:"Ꝩ";s:3:"ꝩ";s:3:"Ꝫ";s:3:"ꝫ";s:3:"Ꝭ";s:3:"ꝭ";s:3:"Ꝯ";s:3:"ꝯ";s:3:"Ꝺ";s:3:"ꝺ";s:3:"Ꝼ";s:3:"ꝼ";s:3:"Ᵹ";s:3:"ᵹ";s:3:"Ꝿ";s:3:"ꝿ";s:3:"Ꞁ";s:3:"ꞁ";s:3:"Ꞃ";s:3:"ꞃ";s:3:"Ꞅ";s:3:"ꞅ";s:3:"Ꞇ";s:3:"ꞇ";s:3:"Ꞌ";s:3:"ꞌ";s:3:"Ɥ";s:2:"ɥ";s:3:"Ꞑ";s:3:"ꞑ";s:3:"Ꞓ";s:3:"ꞓ";s:3:"Ꞗ";s:3:"ꞗ";s:3:"Ꞙ";s:3:"ꞙ";s:3:"Ꞛ";s:3:"ꞛ";s:3:"Ꞝ";s:3:"ꞝ";s:3:"Ꞟ";s:3:"ꞟ";s:3:"Ꞡ";s:3:"ꞡ";s:3:"Ꞣ";s:3:"ꞣ";s:3:"Ꞥ";s:3:"ꞥ";s:3:"Ꞧ";s:3:"ꞧ";s:3:"Ꞩ";s:3:"ꞩ";s:3:"Ɦ";s:2:"ɦ";s:3:"Ɜ";s:2:"ɜ";s:3:"Ɡ";s:2:"ɡ";s:3:"Ɬ";s:2:"ɬ";s:3:"Ʞ";s:2:"ʞ";s:3:"Ʇ";s:2:"ʇ";s:3:"A";s:3:"a";s:3:"B";s:3:"b";s:3:"C";s:3:"c";s:3:"D";s:3:"d";s:3:"E";s:3:"e";s:3:"F";s:3:"f";s:3:"G";s:3:"g";s:3:"H";s:3:"h";s:3:"I";s:3:"i";s:3:"J";s:3:"j";s:3:"K";s:3:"k";s:3:"L";s:3:"l";s:3:"M";s:3:"m";s:3:"N";s:3:"n";s:3:"O";s:3:"o";s:3:"P";s:3:"p";s:3:"Q";s:3:"q";s:3:"R";s:3:"r";s:3:"S";s:3:"s";s:3:"T";s:3:"t";s:3:"U";s:3:"u";s:3:"V";s:3:"v";s:3:"W";s:3:"w";s:3:"X";s:3:"x";s:3:"Y";s:3:"y";s:3:"Z";s:3:"z";s:4:"𐐀";s:4:"𐐨";s:4:"𐐁";s:4:"𐐩";s:4:"𐐂";s:4:"𐐪";s:4:"𐐃";s:4:"𐐫";s:4:"𐐄";s:4:"𐐬";s:4:"𐐅";s:4:"𐐭";s:4:"𐐆";s:4:"𐐮";s:4:"𐐇";s:4:"𐐯";s:4:"𐐈";s:4:"𐐰";s:4:"𐐉";s:4:"𐐱";s:4:"𐐊";s:4:"𐐲";s:4:"𐐋";s:4:"𐐳";s:4:"𐐌";s:4:"𐐴";s:4:"𐐍";s:4:"𐐵";s:4:"𐐎";s:4:"𐐶";s:4:"𐐏";s:4:"𐐷";s:4:"𐐐";s:4:"𐐸";s:4:"𐐑";s:4:"𐐹";s:4:"𐐒";s:4:"𐐺";s:4:"𐐓";s:4:"𐐻";s:4:"𐐔";s:4:"𐐼";s:4:"𐐕";s:4:"𐐽";s:4:"𐐖";s:4:"𐐾";s:4:"𐐗";s:4:"𐐿";s:4:"𐐘";s:4:"𐑀";s:4:"𐐙";s:4:"𐑁";s:4:"𐐚";s:4:"𐑂";s:4:"𐐛";s:4:"𐑃";s:4:"𐐜";s:4:"𐑄";s:4:"𐐝";s:4:"𐑅";s:4:"𐐞";s:4:"𐑆";s:4:"𐐟";s:4:"𐑇";s:4:"𐐠";s:4:"𐑈";s:4:"𐐡";s:4:"𐑉";s:4:"𐐢";s:4:"𐑊";s:4:"𐐣";s:4:"𐑋";s:4:"𐐤";s:4:"𐑌";s:4:"𐐥";s:4:"𐑍";s:4:"𐐦";s:4:"𐑎";s:4:"𐐧";s:4:"𐑏";s:4:"𑢠";s:4:"𑣀";s:4:"𑢡";s:4:"𑣁";s:4:"𑢢";s:4:"𑣂";s:4:"𑢣";s:4:"𑣃";s:4:"𑢤";s:4:"𑣄";s:4:"𑢥";s:4:"𑣅";s:4:"𑢦";s:4:"𑣆";s:4:"𑢧";s:4:"𑣇";s:4:"𑢨";s:4:"𑣈";s:4:"𑢩";s:4:"𑣉";s:4:"𑢪";s:4:"𑣊";s:4:"𑢫";s:4:"𑣋";s:4:"𑢬";s:4:"𑣌";s:4:"𑢭";s:4:"𑣍";s:4:"𑢮";s:4:"𑣎";s:4:"𑢯";s:4:"𑣏";s:4:"𑢰";s:4:"𑣐";s:4:"𑢱";s:4:"𑣑";s:4:"𑢲";s:4:"𑣒";s:4:"𑢳";s:4:"𑣓";s:4:"𑢴";s:4:"𑣔";s:4:"𑢵";s:4:"𑣕";s:4:"𑢶";s:4:"𑣖";s:4:"𑢷";s:4:"𑣗";s:4:"𑢸";s:4:"𑣘";s:4:"𑢹";s:4:"𑣙";s:4:"𑢺";s:4:"𑣚";s:4:"𑢻";s:4:"𑣛";s:4:"𑢼";s:4:"𑣜";s:4:"𑢽";s:4:"𑣝";s:4:"𑢾";s:4:"𑣞";s:4:"𑢿";s:4:"𑣟";} \ No newline at end of file diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.ser b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.ser new file mode 100644 index 0000000..e9e0ec2 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.ser @@ -0,0 +1 @@ +a:1100:{s:1:"a";s:1:"A";s:1:"b";s:1:"B";s:1:"c";s:1:"C";s:1:"d";s:1:"D";s:1:"e";s:1:"E";s:1:"f";s:1:"F";s:1:"g";s:1:"G";s:1:"h";s:1:"H";s:1:"i";s:1:"I";s:1:"j";s:1:"J";s:1:"k";s:1:"K";s:1:"l";s:1:"L";s:1:"m";s:1:"M";s:1:"n";s:1:"N";s:1:"o";s:1:"O";s:1:"p";s:1:"P";s:1:"q";s:1:"Q";s:1:"r";s:1:"R";s:1:"s";s:1:"S";s:1:"t";s:1:"T";s:1:"u";s:1:"U";s:1:"v";s:1:"V";s:1:"w";s:1:"W";s:1:"x";s:1:"X";s:1:"y";s:1:"Y";s:1:"z";s:1:"Z";s:2:"µ";s:2:"Μ";s:2:"à";s:2:"À";s:2:"á";s:2:"Á";s:2:"â";s:2:"Â";s:2:"ã";s:2:"Ã";s:2:"ä";s:2:"Ä";s:2:"å";s:2:"Å";s:2:"æ";s:2:"Æ";s:2:"ç";s:2:"Ç";s:2:"è";s:2:"È";s:2:"é";s:2:"É";s:2:"ê";s:2:"Ê";s:2:"ë";s:2:"Ë";s:2:"ì";s:2:"Ì";s:2:"í";s:2:"Í";s:2:"î";s:2:"Î";s:2:"ï";s:2:"Ï";s:2:"ð";s:2:"Ð";s:2:"ñ";s:2:"Ñ";s:2:"ò";s:2:"Ò";s:2:"ó";s:2:"Ó";s:2:"ô";s:2:"Ô";s:2:"õ";s:2:"Õ";s:2:"ö";s:2:"Ö";s:2:"ø";s:2:"Ø";s:2:"ù";s:2:"Ù";s:2:"ú";s:2:"Ú";s:2:"û";s:2:"Û";s:2:"ü";s:2:"Ü";s:2:"ý";s:2:"Ý";s:2:"þ";s:2:"Þ";s:2:"ÿ";s:2:"Ÿ";s:2:"ā";s:2:"Ā";s:2:"ă";s:2:"Ă";s:2:"ą";s:2:"Ą";s:2:"ć";s:2:"Ć";s:2:"ĉ";s:2:"Ĉ";s:2:"ċ";s:2:"Ċ";s:2:"č";s:2:"Č";s:2:"ď";s:2:"Ď";s:2:"đ";s:2:"Đ";s:2:"ē";s:2:"Ē";s:2:"ĕ";s:2:"Ĕ";s:2:"ė";s:2:"Ė";s:2:"ę";s:2:"Ę";s:2:"ě";s:2:"Ě";s:2:"ĝ";s:2:"Ĝ";s:2:"ğ";s:2:"Ğ";s:2:"ġ";s:2:"Ġ";s:2:"ģ";s:2:"Ģ";s:2:"ĥ";s:2:"Ĥ";s:2:"ħ";s:2:"Ħ";s:2:"ĩ";s:2:"Ĩ";s:2:"ī";s:2:"Ī";s:2:"ĭ";s:2:"Ĭ";s:2:"į";s:2:"Į";s:2:"ı";s:1:"I";s:2:"ij";s:2:"IJ";s:2:"ĵ";s:2:"Ĵ";s:2:"ķ";s:2:"Ķ";s:2:"ĺ";s:2:"Ĺ";s:2:"ļ";s:2:"Ļ";s:2:"ľ";s:2:"Ľ";s:2:"ŀ";s:2:"Ŀ";s:2:"ł";s:2:"Ł";s:2:"ń";s:2:"Ń";s:2:"ņ";s:2:"Ņ";s:2:"ň";s:2:"Ň";s:2:"ŋ";s:2:"Ŋ";s:2:"ō";s:2:"Ō";s:2:"ŏ";s:2:"Ŏ";s:2:"ő";s:2:"Ő";s:2:"œ";s:2:"Œ";s:2:"ŕ";s:2:"Ŕ";s:2:"ŗ";s:2:"Ŗ";s:2:"ř";s:2:"Ř";s:2:"ś";s:2:"Ś";s:2:"ŝ";s:2:"Ŝ";s:2:"ş";s:2:"Ş";s:2:"š";s:2:"Š";s:2:"ţ";s:2:"Ţ";s:2:"ť";s:2:"Ť";s:2:"ŧ";s:2:"Ŧ";s:2:"ũ";s:2:"Ũ";s:2:"ū";s:2:"Ū";s:2:"ŭ";s:2:"Ŭ";s:2:"ů";s:2:"Ů";s:2:"ű";s:2:"Ű";s:2:"ų";s:2:"Ų";s:2:"ŵ";s:2:"Ŵ";s:2:"ŷ";s:2:"Ŷ";s:2:"ź";s:2:"Ź";s:2:"ż";s:2:"Ż";s:2:"ž";s:2:"Ž";s:2:"ſ";s:1:"S";s:2:"ƀ";s:2:"Ƀ";s:2:"ƃ";s:2:"Ƃ";s:2:"ƅ";s:2:"Ƅ";s:2:"ƈ";s:2:"Ƈ";s:2:"ƌ";s:2:"Ƌ";s:2:"ƒ";s:2:"Ƒ";s:2:"ƕ";s:2:"Ƕ";s:2:"ƙ";s:2:"Ƙ";s:2:"ƚ";s:2:"Ƚ";s:2:"ƞ";s:2:"Ƞ";s:2:"ơ";s:2:"Ơ";s:2:"ƣ";s:2:"Ƣ";s:2:"ƥ";s:2:"Ƥ";s:2:"ƨ";s:2:"Ƨ";s:2:"ƭ";s:2:"Ƭ";s:2:"ư";s:2:"Ư";s:2:"ƴ";s:2:"Ƴ";s:2:"ƶ";s:2:"Ƶ";s:2:"ƹ";s:2:"Ƹ";s:2:"ƽ";s:2:"Ƽ";s:2:"ƿ";s:2:"Ƿ";s:2:"Dž";s:2:"DŽ";s:2:"dž";s:2:"DŽ";s:2:"Lj";s:2:"LJ";s:2:"lj";s:2:"LJ";s:2:"Nj";s:2:"NJ";s:2:"nj";s:2:"NJ";s:2:"ǎ";s:2:"Ǎ";s:2:"ǐ";s:2:"Ǐ";s:2:"ǒ";s:2:"Ǒ";s:2:"ǔ";s:2:"Ǔ";s:2:"ǖ";s:2:"Ǖ";s:2:"ǘ";s:2:"Ǘ";s:2:"ǚ";s:2:"Ǚ";s:2:"ǜ";s:2:"Ǜ";s:2:"ǝ";s:2:"Ǝ";s:2:"ǟ";s:2:"Ǟ";s:2:"ǡ";s:2:"Ǡ";s:2:"ǣ";s:2:"Ǣ";s:2:"ǥ";s:2:"Ǥ";s:2:"ǧ";s:2:"Ǧ";s:2:"ǩ";s:2:"Ǩ";s:2:"ǫ";s:2:"Ǫ";s:2:"ǭ";s:2:"Ǭ";s:2:"ǯ";s:2:"Ǯ";s:2:"Dz";s:2:"DZ";s:2:"dz";s:2:"DZ";s:2:"ǵ";s:2:"Ǵ";s:2:"ǹ";s:2:"Ǹ";s:2:"ǻ";s:2:"Ǻ";s:2:"ǽ";s:2:"Ǽ";s:2:"ǿ";s:2:"Ǿ";s:2:"ȁ";s:2:"Ȁ";s:2:"ȃ";s:2:"Ȃ";s:2:"ȅ";s:2:"Ȅ";s:2:"ȇ";s:2:"Ȇ";s:2:"ȉ";s:2:"Ȉ";s:2:"ȋ";s:2:"Ȋ";s:2:"ȍ";s:2:"Ȍ";s:2:"ȏ";s:2:"Ȏ";s:2:"ȑ";s:2:"Ȑ";s:2:"ȓ";s:2:"Ȓ";s:2:"ȕ";s:2:"Ȕ";s:2:"ȗ";s:2:"Ȗ";s:2:"ș";s:2:"Ș";s:2:"ț";s:2:"Ț";s:2:"ȝ";s:2:"Ȝ";s:2:"ȟ";s:2:"Ȟ";s:2:"ȣ";s:2:"Ȣ";s:2:"ȥ";s:2:"Ȥ";s:2:"ȧ";s:2:"Ȧ";s:2:"ȩ";s:2:"Ȩ";s:2:"ȫ";s:2:"Ȫ";s:2:"ȭ";s:2:"Ȭ";s:2:"ȯ";s:2:"Ȯ";s:2:"ȱ";s:2:"Ȱ";s:2:"ȳ";s:2:"Ȳ";s:2:"ȼ";s:2:"Ȼ";s:2:"ȿ";s:3:"Ȿ";s:2:"ɀ";s:3:"Ɀ";s:2:"ɂ";s:2:"Ɂ";s:2:"ɇ";s:2:"Ɇ";s:2:"ɉ";s:2:"Ɉ";s:2:"ɋ";s:2:"Ɋ";s:2:"ɍ";s:2:"Ɍ";s:2:"ɏ";s:2:"Ɏ";s:2:"ɐ";s:3:"Ɐ";s:2:"ɑ";s:3:"Ɑ";s:2:"ɒ";s:3:"Ɒ";s:2:"ɓ";s:2:"Ɓ";s:2:"ɔ";s:2:"Ɔ";s:2:"ɖ";s:2:"Ɖ";s:2:"ɗ";s:2:"Ɗ";s:2:"ə";s:2:"Ə";s:2:"ɛ";s:2:"Ɛ";s:2:"ɜ";s:3:"Ɜ";s:2:"ɠ";s:2:"Ɠ";s:2:"ɡ";s:3:"Ɡ";s:2:"ɣ";s:2:"Ɣ";s:2:"ɥ";s:3:"Ɥ";s:2:"ɦ";s:3:"Ɦ";s:2:"ɨ";s:2:"Ɨ";s:2:"ɩ";s:2:"Ɩ";s:2:"ɫ";s:3:"Ɫ";s:2:"ɬ";s:3:"Ɬ";s:2:"ɯ";s:2:"Ɯ";s:2:"ɱ";s:3:"Ɱ";s:2:"ɲ";s:2:"Ɲ";s:2:"ɵ";s:2:"Ɵ";s:2:"ɽ";s:3:"Ɽ";s:2:"ʀ";s:2:"Ʀ";s:2:"ʃ";s:2:"Ʃ";s:2:"ʇ";s:3:"Ʇ";s:2:"ʈ";s:2:"Ʈ";s:2:"ʉ";s:2:"Ʉ";s:2:"ʊ";s:2:"Ʊ";s:2:"ʋ";s:2:"Ʋ";s:2:"ʌ";s:2:"Ʌ";s:2:"ʒ";s:2:"Ʒ";s:2:"ʞ";s:3:"Ʞ";s:2:"ͅ";s:2:"Ι";s:2:"ͱ";s:2:"Ͱ";s:2:"ͳ";s:2:"Ͳ";s:2:"ͷ";s:2:"Ͷ";s:2:"ͻ";s:2:"Ͻ";s:2:"ͼ";s:2:"Ͼ";s:2:"ͽ";s:2:"Ͽ";s:2:"ά";s:2:"Ά";s:2:"έ";s:2:"Έ";s:2:"ή";s:2:"Ή";s:2:"ί";s:2:"Ί";s:2:"α";s:2:"Α";s:2:"β";s:2:"Β";s:2:"γ";s:2:"Γ";s:2:"δ";s:2:"Δ";s:2:"ε";s:2:"Ε";s:2:"ζ";s:2:"Ζ";s:2:"η";s:2:"Η";s:2:"θ";s:2:"Θ";s:2:"ι";s:2:"Ι";s:2:"κ";s:2:"Κ";s:2:"λ";s:2:"Λ";s:2:"μ";s:2:"Μ";s:2:"ν";s:2:"Ν";s:2:"ξ";s:2:"Ξ";s:2:"ο";s:2:"Ο";s:2:"π";s:2:"Π";s:2:"ρ";s:2:"Ρ";s:2:"ς";s:2:"Σ";s:2:"σ";s:2:"Σ";s:2:"τ";s:2:"Τ";s:2:"υ";s:2:"Υ";s:2:"φ";s:2:"Φ";s:2:"χ";s:2:"Χ";s:2:"ψ";s:2:"Ψ";s:2:"ω";s:2:"Ω";s:2:"ϊ";s:2:"Ϊ";s:2:"ϋ";s:2:"Ϋ";s:2:"ό";s:2:"Ό";s:2:"ύ";s:2:"Ύ";s:2:"ώ";s:2:"Ώ";s:2:"ϐ";s:2:"Β";s:2:"ϑ";s:2:"Θ";s:2:"ϕ";s:2:"Φ";s:2:"ϖ";s:2:"Π";s:2:"ϗ";s:2:"Ϗ";s:2:"ϙ";s:2:"Ϙ";s:2:"ϛ";s:2:"Ϛ";s:2:"ϝ";s:2:"Ϝ";s:2:"ϟ";s:2:"Ϟ";s:2:"ϡ";s:2:"Ϡ";s:2:"ϣ";s:2:"Ϣ";s:2:"ϥ";s:2:"Ϥ";s:2:"ϧ";s:2:"Ϧ";s:2:"ϩ";s:2:"Ϩ";s:2:"ϫ";s:2:"Ϫ";s:2:"ϭ";s:2:"Ϭ";s:2:"ϯ";s:2:"Ϯ";s:2:"ϰ";s:2:"Κ";s:2:"ϱ";s:2:"Ρ";s:2:"ϲ";s:2:"Ϲ";s:2:"ϳ";s:2:"Ϳ";s:2:"ϵ";s:2:"Ε";s:2:"ϸ";s:2:"Ϸ";s:2:"ϻ";s:2:"Ϻ";s:2:"а";s:2:"А";s:2:"б";s:2:"Б";s:2:"в";s:2:"В";s:2:"г";s:2:"Г";s:2:"д";s:2:"Д";s:2:"е";s:2:"Е";s:2:"ж";s:2:"Ж";s:2:"з";s:2:"З";s:2:"и";s:2:"И";s:2:"й";s:2:"Й";s:2:"к";s:2:"К";s:2:"л";s:2:"Л";s:2:"м";s:2:"М";s:2:"н";s:2:"Н";s:2:"о";s:2:"О";s:2:"п";s:2:"П";s:2:"р";s:2:"Р";s:2:"с";s:2:"С";s:2:"т";s:2:"Т";s:2:"у";s:2:"У";s:2:"ф";s:2:"Ф";s:2:"х";s:2:"Х";s:2:"ц";s:2:"Ц";s:2:"ч";s:2:"Ч";s:2:"ш";s:2:"Ш";s:2:"щ";s:2:"Щ";s:2:"ъ";s:2:"Ъ";s:2:"ы";s:2:"Ы";s:2:"ь";s:2:"Ь";s:2:"э";s:2:"Э";s:2:"ю";s:2:"Ю";s:2:"я";s:2:"Я";s:2:"ѐ";s:2:"Ѐ";s:2:"ё";s:2:"Ё";s:2:"ђ";s:2:"Ђ";s:2:"ѓ";s:2:"Ѓ";s:2:"є";s:2:"Є";s:2:"ѕ";s:2:"Ѕ";s:2:"і";s:2:"І";s:2:"ї";s:2:"Ї";s:2:"ј";s:2:"Ј";s:2:"љ";s:2:"Љ";s:2:"њ";s:2:"Њ";s:2:"ћ";s:2:"Ћ";s:2:"ќ";s:2:"Ќ";s:2:"ѝ";s:2:"Ѝ";s:2:"ў";s:2:"Ў";s:2:"џ";s:2:"Џ";s:2:"ѡ";s:2:"Ѡ";s:2:"ѣ";s:2:"Ѣ";s:2:"ѥ";s:2:"Ѥ";s:2:"ѧ";s:2:"Ѧ";s:2:"ѩ";s:2:"Ѩ";s:2:"ѫ";s:2:"Ѫ";s:2:"ѭ";s:2:"Ѭ";s:2:"ѯ";s:2:"Ѯ";s:2:"ѱ";s:2:"Ѱ";s:2:"ѳ";s:2:"Ѳ";s:2:"ѵ";s:2:"Ѵ";s:2:"ѷ";s:2:"Ѷ";s:2:"ѹ";s:2:"Ѹ";s:2:"ѻ";s:2:"Ѻ";s:2:"ѽ";s:2:"Ѽ";s:2:"ѿ";s:2:"Ѿ";s:2:"ҁ";s:2:"Ҁ";s:2:"ҋ";s:2:"Ҋ";s:2:"ҍ";s:2:"Ҍ";s:2:"ҏ";s:2:"Ҏ";s:2:"ґ";s:2:"Ґ";s:2:"ғ";s:2:"Ғ";s:2:"ҕ";s:2:"Ҕ";s:2:"җ";s:2:"Җ";s:2:"ҙ";s:2:"Ҙ";s:2:"қ";s:2:"Қ";s:2:"ҝ";s:2:"Ҝ";s:2:"ҟ";s:2:"Ҟ";s:2:"ҡ";s:2:"Ҡ";s:2:"ң";s:2:"Ң";s:2:"ҥ";s:2:"Ҥ";s:2:"ҧ";s:2:"Ҧ";s:2:"ҩ";s:2:"Ҩ";s:2:"ҫ";s:2:"Ҫ";s:2:"ҭ";s:2:"Ҭ";s:2:"ү";s:2:"Ү";s:2:"ұ";s:2:"Ұ";s:2:"ҳ";s:2:"Ҳ";s:2:"ҵ";s:2:"Ҵ";s:2:"ҷ";s:2:"Ҷ";s:2:"ҹ";s:2:"Ҹ";s:2:"һ";s:2:"Һ";s:2:"ҽ";s:2:"Ҽ";s:2:"ҿ";s:2:"Ҿ";s:2:"ӂ";s:2:"Ӂ";s:2:"ӄ";s:2:"Ӄ";s:2:"ӆ";s:2:"Ӆ";s:2:"ӈ";s:2:"Ӈ";s:2:"ӊ";s:2:"Ӊ";s:2:"ӌ";s:2:"Ӌ";s:2:"ӎ";s:2:"Ӎ";s:2:"ӏ";s:2:"Ӏ";s:2:"ӑ";s:2:"Ӑ";s:2:"ӓ";s:2:"Ӓ";s:2:"ӕ";s:2:"Ӕ";s:2:"ӗ";s:2:"Ӗ";s:2:"ә";s:2:"Ә";s:2:"ӛ";s:2:"Ӛ";s:2:"ӝ";s:2:"Ӝ";s:2:"ӟ";s:2:"Ӟ";s:2:"ӡ";s:2:"Ӡ";s:2:"ӣ";s:2:"Ӣ";s:2:"ӥ";s:2:"Ӥ";s:2:"ӧ";s:2:"Ӧ";s:2:"ө";s:2:"Ө";s:2:"ӫ";s:2:"Ӫ";s:2:"ӭ";s:2:"Ӭ";s:2:"ӯ";s:2:"Ӯ";s:2:"ӱ";s:2:"Ӱ";s:2:"ӳ";s:2:"Ӳ";s:2:"ӵ";s:2:"Ӵ";s:2:"ӷ";s:2:"Ӷ";s:2:"ӹ";s:2:"Ӹ";s:2:"ӻ";s:2:"Ӻ";s:2:"ӽ";s:2:"Ӽ";s:2:"ӿ";s:2:"Ӿ";s:2:"ԁ";s:2:"Ԁ";s:2:"ԃ";s:2:"Ԃ";s:2:"ԅ";s:2:"Ԅ";s:2:"ԇ";s:2:"Ԇ";s:2:"ԉ";s:2:"Ԉ";s:2:"ԋ";s:2:"Ԋ";s:2:"ԍ";s:2:"Ԍ";s:2:"ԏ";s:2:"Ԏ";s:2:"ԑ";s:2:"Ԑ";s:2:"ԓ";s:2:"Ԓ";s:2:"ԕ";s:2:"Ԕ";s:2:"ԗ";s:2:"Ԗ";s:2:"ԙ";s:2:"Ԙ";s:2:"ԛ";s:2:"Ԛ";s:2:"ԝ";s:2:"Ԝ";s:2:"ԟ";s:2:"Ԟ";s:2:"ԡ";s:2:"Ԡ";s:2:"ԣ";s:2:"Ԣ";s:2:"ԥ";s:2:"Ԥ";s:2:"ԧ";s:2:"Ԧ";s:2:"ԩ";s:2:"Ԩ";s:2:"ԫ";s:2:"Ԫ";s:2:"ԭ";s:2:"Ԭ";s:2:"ԯ";s:2:"Ԯ";s:2:"ա";s:2:"Ա";s:2:"բ";s:2:"Բ";s:2:"գ";s:2:"Գ";s:2:"դ";s:2:"Դ";s:2:"ե";s:2:"Ե";s:2:"զ";s:2:"Զ";s:2:"է";s:2:"Է";s:2:"ը";s:2:"Ը";s:2:"թ";s:2:"Թ";s:2:"ժ";s:2:"Ժ";s:2:"ի";s:2:"Ի";s:2:"լ";s:2:"Լ";s:2:"խ";s:2:"Խ";s:2:"ծ";s:2:"Ծ";s:2:"կ";s:2:"Կ";s:2:"հ";s:2:"Հ";s:2:"ձ";s:2:"Ձ";s:2:"ղ";s:2:"Ղ";s:2:"ճ";s:2:"Ճ";s:2:"մ";s:2:"Մ";s:2:"յ";s:2:"Յ";s:2:"ն";s:2:"Ն";s:2:"շ";s:2:"Շ";s:2:"ո";s:2:"Ո";s:2:"չ";s:2:"Չ";s:2:"պ";s:2:"Պ";s:2:"ջ";s:2:"Ջ";s:2:"ռ";s:2:"Ռ";s:2:"ս";s:2:"Ս";s:2:"վ";s:2:"Վ";s:2:"տ";s:2:"Տ";s:2:"ր";s:2:"Ր";s:2:"ց";s:2:"Ց";s:2:"ւ";s:2:"Ւ";s:2:"փ";s:2:"Փ";s:2:"ք";s:2:"Ք";s:2:"օ";s:2:"Օ";s:2:"ֆ";s:2:"Ֆ";s:3:"ᵹ";s:3:"Ᵹ";s:3:"ᵽ";s:3:"Ᵽ";s:3:"ḁ";s:3:"Ḁ";s:3:"ḃ";s:3:"Ḃ";s:3:"ḅ";s:3:"Ḅ";s:3:"ḇ";s:3:"Ḇ";s:3:"ḉ";s:3:"Ḉ";s:3:"ḋ";s:3:"Ḋ";s:3:"ḍ";s:3:"Ḍ";s:3:"ḏ";s:3:"Ḏ";s:3:"ḑ";s:3:"Ḑ";s:3:"ḓ";s:3:"Ḓ";s:3:"ḕ";s:3:"Ḕ";s:3:"ḗ";s:3:"Ḗ";s:3:"ḙ";s:3:"Ḙ";s:3:"ḛ";s:3:"Ḛ";s:3:"ḝ";s:3:"Ḝ";s:3:"ḟ";s:3:"Ḟ";s:3:"ḡ";s:3:"Ḡ";s:3:"ḣ";s:3:"Ḣ";s:3:"ḥ";s:3:"Ḥ";s:3:"ḧ";s:3:"Ḧ";s:3:"ḩ";s:3:"Ḩ";s:3:"ḫ";s:3:"Ḫ";s:3:"ḭ";s:3:"Ḭ";s:3:"ḯ";s:3:"Ḯ";s:3:"ḱ";s:3:"Ḱ";s:3:"ḳ";s:3:"Ḳ";s:3:"ḵ";s:3:"Ḵ";s:3:"ḷ";s:3:"Ḷ";s:3:"ḹ";s:3:"Ḹ";s:3:"ḻ";s:3:"Ḻ";s:3:"ḽ";s:3:"Ḽ";s:3:"ḿ";s:3:"Ḿ";s:3:"ṁ";s:3:"Ṁ";s:3:"ṃ";s:3:"Ṃ";s:3:"ṅ";s:3:"Ṅ";s:3:"ṇ";s:3:"Ṇ";s:3:"ṉ";s:3:"Ṉ";s:3:"ṋ";s:3:"Ṋ";s:3:"ṍ";s:3:"Ṍ";s:3:"ṏ";s:3:"Ṏ";s:3:"ṑ";s:3:"Ṑ";s:3:"ṓ";s:3:"Ṓ";s:3:"ṕ";s:3:"Ṕ";s:3:"ṗ";s:3:"Ṗ";s:3:"ṙ";s:3:"Ṙ";s:3:"ṛ";s:3:"Ṛ";s:3:"ṝ";s:3:"Ṝ";s:3:"ṟ";s:3:"Ṟ";s:3:"ṡ";s:3:"Ṡ";s:3:"ṣ";s:3:"Ṣ";s:3:"ṥ";s:3:"Ṥ";s:3:"ṧ";s:3:"Ṧ";s:3:"ṩ";s:3:"Ṩ";s:3:"ṫ";s:3:"Ṫ";s:3:"ṭ";s:3:"Ṭ";s:3:"ṯ";s:3:"Ṯ";s:3:"ṱ";s:3:"Ṱ";s:3:"ṳ";s:3:"Ṳ";s:3:"ṵ";s:3:"Ṵ";s:3:"ṷ";s:3:"Ṷ";s:3:"ṹ";s:3:"Ṹ";s:3:"ṻ";s:3:"Ṻ";s:3:"ṽ";s:3:"Ṽ";s:3:"ṿ";s:3:"Ṿ";s:3:"ẁ";s:3:"Ẁ";s:3:"ẃ";s:3:"Ẃ";s:3:"ẅ";s:3:"Ẅ";s:3:"ẇ";s:3:"Ẇ";s:3:"ẉ";s:3:"Ẉ";s:3:"ẋ";s:3:"Ẋ";s:3:"ẍ";s:3:"Ẍ";s:3:"ẏ";s:3:"Ẏ";s:3:"ẑ";s:3:"Ẑ";s:3:"ẓ";s:3:"Ẓ";s:3:"ẕ";s:3:"Ẕ";s:3:"ẛ";s:3:"Ṡ";s:3:"ạ";s:3:"Ạ";s:3:"ả";s:3:"Ả";s:3:"ấ";s:3:"Ấ";s:3:"ầ";s:3:"Ầ";s:3:"ẩ";s:3:"Ẩ";s:3:"ẫ";s:3:"Ẫ";s:3:"ậ";s:3:"Ậ";s:3:"ắ";s:3:"Ắ";s:3:"ằ";s:3:"Ằ";s:3:"ẳ";s:3:"Ẳ";s:3:"ẵ";s:3:"Ẵ";s:3:"ặ";s:3:"Ặ";s:3:"ẹ";s:3:"Ẹ";s:3:"ẻ";s:3:"Ẻ";s:3:"ẽ";s:3:"Ẽ";s:3:"ế";s:3:"Ế";s:3:"ề";s:3:"Ề";s:3:"ể";s:3:"Ể";s:3:"ễ";s:3:"Ễ";s:3:"ệ";s:3:"Ệ";s:3:"ỉ";s:3:"Ỉ";s:3:"ị";s:3:"Ị";s:3:"ọ";s:3:"Ọ";s:3:"ỏ";s:3:"Ỏ";s:3:"ố";s:3:"Ố";s:3:"ồ";s:3:"Ồ";s:3:"ổ";s:3:"Ổ";s:3:"ỗ";s:3:"Ỗ";s:3:"ộ";s:3:"Ộ";s:3:"ớ";s:3:"Ớ";s:3:"ờ";s:3:"Ờ";s:3:"ở";s:3:"Ở";s:3:"ỡ";s:3:"Ỡ";s:3:"ợ";s:3:"Ợ";s:3:"ụ";s:3:"Ụ";s:3:"ủ";s:3:"Ủ";s:3:"ứ";s:3:"Ứ";s:3:"ừ";s:3:"Ừ";s:3:"ử";s:3:"Ử";s:3:"ữ";s:3:"Ữ";s:3:"ự";s:3:"Ự";s:3:"ỳ";s:3:"Ỳ";s:3:"ỵ";s:3:"Ỵ";s:3:"ỷ";s:3:"Ỷ";s:3:"ỹ";s:3:"Ỹ";s:3:"ỻ";s:3:"Ỻ";s:3:"ỽ";s:3:"Ỽ";s:3:"ỿ";s:3:"Ỿ";s:3:"ἀ";s:3:"Ἀ";s:3:"ἁ";s:3:"Ἁ";s:3:"ἂ";s:3:"Ἂ";s:3:"ἃ";s:3:"Ἃ";s:3:"ἄ";s:3:"Ἄ";s:3:"ἅ";s:3:"Ἅ";s:3:"ἆ";s:3:"Ἆ";s:3:"ἇ";s:3:"Ἇ";s:3:"ἐ";s:3:"Ἐ";s:3:"ἑ";s:3:"Ἑ";s:3:"ἒ";s:3:"Ἒ";s:3:"ἓ";s:3:"Ἓ";s:3:"ἔ";s:3:"Ἔ";s:3:"ἕ";s:3:"Ἕ";s:3:"ἠ";s:3:"Ἠ";s:3:"ἡ";s:3:"Ἡ";s:3:"ἢ";s:3:"Ἢ";s:3:"ἣ";s:3:"Ἣ";s:3:"ἤ";s:3:"Ἤ";s:3:"ἥ";s:3:"Ἥ";s:3:"ἦ";s:3:"Ἦ";s:3:"ἧ";s:3:"Ἧ";s:3:"ἰ";s:3:"Ἰ";s:3:"ἱ";s:3:"Ἱ";s:3:"ἲ";s:3:"Ἲ";s:3:"ἳ";s:3:"Ἳ";s:3:"ἴ";s:3:"Ἴ";s:3:"ἵ";s:3:"Ἵ";s:3:"ἶ";s:3:"Ἶ";s:3:"ἷ";s:3:"Ἷ";s:3:"ὀ";s:3:"Ὀ";s:3:"ὁ";s:3:"Ὁ";s:3:"ὂ";s:3:"Ὂ";s:3:"ὃ";s:3:"Ὃ";s:3:"ὄ";s:3:"Ὄ";s:3:"ὅ";s:3:"Ὅ";s:3:"ὑ";s:3:"Ὑ";s:3:"ὓ";s:3:"Ὓ";s:3:"ὕ";s:3:"Ὕ";s:3:"ὗ";s:3:"Ὗ";s:3:"ὠ";s:3:"Ὠ";s:3:"ὡ";s:3:"Ὡ";s:3:"ὢ";s:3:"Ὢ";s:3:"ὣ";s:3:"Ὣ";s:3:"ὤ";s:3:"Ὤ";s:3:"ὥ";s:3:"Ὥ";s:3:"ὦ";s:3:"Ὦ";s:3:"ὧ";s:3:"Ὧ";s:3:"ὰ";s:3:"Ὰ";s:3:"ά";s:3:"Ά";s:3:"ὲ";s:3:"Ὲ";s:3:"έ";s:3:"Έ";s:3:"ὴ";s:3:"Ὴ";s:3:"ή";s:3:"Ή";s:3:"ὶ";s:3:"Ὶ";s:3:"ί";s:3:"Ί";s:3:"ὸ";s:3:"Ὸ";s:3:"ό";s:3:"Ό";s:3:"ὺ";s:3:"Ὺ";s:3:"ύ";s:3:"Ύ";s:3:"ὼ";s:3:"Ὼ";s:3:"ώ";s:3:"Ώ";s:3:"ᾀ";s:3:"ᾈ";s:3:"ᾁ";s:3:"ᾉ";s:3:"ᾂ";s:3:"ᾊ";s:3:"ᾃ";s:3:"ᾋ";s:3:"ᾄ";s:3:"ᾌ";s:3:"ᾅ";s:3:"ᾍ";s:3:"ᾆ";s:3:"ᾎ";s:3:"ᾇ";s:3:"ᾏ";s:3:"ᾐ";s:3:"ᾘ";s:3:"ᾑ";s:3:"ᾙ";s:3:"ᾒ";s:3:"ᾚ";s:3:"ᾓ";s:3:"ᾛ";s:3:"ᾔ";s:3:"ᾜ";s:3:"ᾕ";s:3:"ᾝ";s:3:"ᾖ";s:3:"ᾞ";s:3:"ᾗ";s:3:"ᾟ";s:3:"ᾠ";s:3:"ᾨ";s:3:"ᾡ";s:3:"ᾩ";s:3:"ᾢ";s:3:"ᾪ";s:3:"ᾣ";s:3:"ᾫ";s:3:"ᾤ";s:3:"ᾬ";s:3:"ᾥ";s:3:"ᾭ";s:3:"ᾦ";s:3:"ᾮ";s:3:"ᾧ";s:3:"ᾯ";s:3:"ᾰ";s:3:"Ᾰ";s:3:"ᾱ";s:3:"Ᾱ";s:3:"ᾳ";s:3:"ᾼ";s:3:"ι";s:2:"Ι";s:3:"ῃ";s:3:"ῌ";s:3:"ῐ";s:3:"Ῐ";s:3:"ῑ";s:3:"Ῑ";s:3:"ῠ";s:3:"Ῠ";s:3:"ῡ";s:3:"Ῡ";s:3:"ῥ";s:3:"Ῥ";s:3:"ῳ";s:3:"ῼ";s:3:"ⅎ";s:3:"Ⅎ";s:3:"ⅰ";s:3:"Ⅰ";s:3:"ⅱ";s:3:"Ⅱ";s:3:"ⅲ";s:3:"Ⅲ";s:3:"ⅳ";s:3:"Ⅳ";s:3:"ⅴ";s:3:"Ⅴ";s:3:"ⅵ";s:3:"Ⅵ";s:3:"ⅶ";s:3:"Ⅶ";s:3:"ⅷ";s:3:"Ⅷ";s:3:"ⅸ";s:3:"Ⅸ";s:3:"ⅹ";s:3:"Ⅹ";s:3:"ⅺ";s:3:"Ⅺ";s:3:"ⅻ";s:3:"Ⅻ";s:3:"ⅼ";s:3:"Ⅼ";s:3:"ⅽ";s:3:"Ⅽ";s:3:"ⅾ";s:3:"Ⅾ";s:3:"ⅿ";s:3:"Ⅿ";s:3:"ↄ";s:3:"Ↄ";s:3:"ⓐ";s:3:"Ⓐ";s:3:"ⓑ";s:3:"Ⓑ";s:3:"ⓒ";s:3:"Ⓒ";s:3:"ⓓ";s:3:"Ⓓ";s:3:"ⓔ";s:3:"Ⓔ";s:3:"ⓕ";s:3:"Ⓕ";s:3:"ⓖ";s:3:"Ⓖ";s:3:"ⓗ";s:3:"Ⓗ";s:3:"ⓘ";s:3:"Ⓘ";s:3:"ⓙ";s:3:"Ⓙ";s:3:"ⓚ";s:3:"Ⓚ";s:3:"ⓛ";s:3:"Ⓛ";s:3:"ⓜ";s:3:"Ⓜ";s:3:"ⓝ";s:3:"Ⓝ";s:3:"ⓞ";s:3:"Ⓞ";s:3:"ⓟ";s:3:"Ⓟ";s:3:"ⓠ";s:3:"Ⓠ";s:3:"ⓡ";s:3:"Ⓡ";s:3:"ⓢ";s:3:"Ⓢ";s:3:"ⓣ";s:3:"Ⓣ";s:3:"ⓤ";s:3:"Ⓤ";s:3:"ⓥ";s:3:"Ⓥ";s:3:"ⓦ";s:3:"Ⓦ";s:3:"ⓧ";s:3:"Ⓧ";s:3:"ⓨ";s:3:"Ⓨ";s:3:"ⓩ";s:3:"Ⓩ";s:3:"ⰰ";s:3:"Ⰰ";s:3:"ⰱ";s:3:"Ⰱ";s:3:"ⰲ";s:3:"Ⰲ";s:3:"ⰳ";s:3:"Ⰳ";s:3:"ⰴ";s:3:"Ⰴ";s:3:"ⰵ";s:3:"Ⰵ";s:3:"ⰶ";s:3:"Ⰶ";s:3:"ⰷ";s:3:"Ⰷ";s:3:"ⰸ";s:3:"Ⰸ";s:3:"ⰹ";s:3:"Ⰹ";s:3:"ⰺ";s:3:"Ⰺ";s:3:"ⰻ";s:3:"Ⰻ";s:3:"ⰼ";s:3:"Ⰼ";s:3:"ⰽ";s:3:"Ⰽ";s:3:"ⰾ";s:3:"Ⰾ";s:3:"ⰿ";s:3:"Ⰿ";s:3:"ⱀ";s:3:"Ⱀ";s:3:"ⱁ";s:3:"Ⱁ";s:3:"ⱂ";s:3:"Ⱂ";s:3:"ⱃ";s:3:"Ⱃ";s:3:"ⱄ";s:3:"Ⱄ";s:3:"ⱅ";s:3:"Ⱅ";s:3:"ⱆ";s:3:"Ⱆ";s:3:"ⱇ";s:3:"Ⱇ";s:3:"ⱈ";s:3:"Ⱈ";s:3:"ⱉ";s:3:"Ⱉ";s:3:"ⱊ";s:3:"Ⱊ";s:3:"ⱋ";s:3:"Ⱋ";s:3:"ⱌ";s:3:"Ⱌ";s:3:"ⱍ";s:3:"Ⱍ";s:3:"ⱎ";s:3:"Ⱎ";s:3:"ⱏ";s:3:"Ⱏ";s:3:"ⱐ";s:3:"Ⱐ";s:3:"ⱑ";s:3:"Ⱑ";s:3:"ⱒ";s:3:"Ⱒ";s:3:"ⱓ";s:3:"Ⱓ";s:3:"ⱔ";s:3:"Ⱔ";s:3:"ⱕ";s:3:"Ⱕ";s:3:"ⱖ";s:3:"Ⱖ";s:3:"ⱗ";s:3:"Ⱗ";s:3:"ⱘ";s:3:"Ⱘ";s:3:"ⱙ";s:3:"Ⱙ";s:3:"ⱚ";s:3:"Ⱚ";s:3:"ⱛ";s:3:"Ⱛ";s:3:"ⱜ";s:3:"Ⱜ";s:3:"ⱝ";s:3:"Ⱝ";s:3:"ⱞ";s:3:"Ⱞ";s:3:"ⱡ";s:3:"Ⱡ";s:3:"ⱥ";s:2:"Ⱥ";s:3:"ⱦ";s:2:"Ⱦ";s:3:"ⱨ";s:3:"Ⱨ";s:3:"ⱪ";s:3:"Ⱪ";s:3:"ⱬ";s:3:"Ⱬ";s:3:"ⱳ";s:3:"Ⱳ";s:3:"ⱶ";s:3:"Ⱶ";s:3:"ⲁ";s:3:"Ⲁ";s:3:"ⲃ";s:3:"Ⲃ";s:3:"ⲅ";s:3:"Ⲅ";s:3:"ⲇ";s:3:"Ⲇ";s:3:"ⲉ";s:3:"Ⲉ";s:3:"ⲋ";s:3:"Ⲋ";s:3:"ⲍ";s:3:"Ⲍ";s:3:"ⲏ";s:3:"Ⲏ";s:3:"ⲑ";s:3:"Ⲑ";s:3:"ⲓ";s:3:"Ⲓ";s:3:"ⲕ";s:3:"Ⲕ";s:3:"ⲗ";s:3:"Ⲗ";s:3:"ⲙ";s:3:"Ⲙ";s:3:"ⲛ";s:3:"Ⲛ";s:3:"ⲝ";s:3:"Ⲝ";s:3:"ⲟ";s:3:"Ⲟ";s:3:"ⲡ";s:3:"Ⲡ";s:3:"ⲣ";s:3:"Ⲣ";s:3:"ⲥ";s:3:"Ⲥ";s:3:"ⲧ";s:3:"Ⲧ";s:3:"ⲩ";s:3:"Ⲩ";s:3:"ⲫ";s:3:"Ⲫ";s:3:"ⲭ";s:3:"Ⲭ";s:3:"ⲯ";s:3:"Ⲯ";s:3:"ⲱ";s:3:"Ⲱ";s:3:"ⲳ";s:3:"Ⲳ";s:3:"ⲵ";s:3:"Ⲵ";s:3:"ⲷ";s:3:"Ⲷ";s:3:"ⲹ";s:3:"Ⲹ";s:3:"ⲻ";s:3:"Ⲻ";s:3:"ⲽ";s:3:"Ⲽ";s:3:"ⲿ";s:3:"Ⲿ";s:3:"ⳁ";s:3:"Ⳁ";s:3:"ⳃ";s:3:"Ⳃ";s:3:"ⳅ";s:3:"Ⳅ";s:3:"ⳇ";s:3:"Ⳇ";s:3:"ⳉ";s:3:"Ⳉ";s:3:"ⳋ";s:3:"Ⳋ";s:3:"ⳍ";s:3:"Ⳍ";s:3:"ⳏ";s:3:"Ⳏ";s:3:"ⳑ";s:3:"Ⳑ";s:3:"ⳓ";s:3:"Ⳓ";s:3:"ⳕ";s:3:"Ⳕ";s:3:"ⳗ";s:3:"Ⳗ";s:3:"ⳙ";s:3:"Ⳙ";s:3:"ⳛ";s:3:"Ⳛ";s:3:"ⳝ";s:3:"Ⳝ";s:3:"ⳟ";s:3:"Ⳟ";s:3:"ⳡ";s:3:"Ⳡ";s:3:"ⳣ";s:3:"Ⳣ";s:3:"ⳬ";s:3:"Ⳬ";s:3:"ⳮ";s:3:"Ⳮ";s:3:"ⳳ";s:3:"Ⳳ";s:3:"ⴀ";s:3:"Ⴀ";s:3:"ⴁ";s:3:"Ⴁ";s:3:"ⴂ";s:3:"Ⴂ";s:3:"ⴃ";s:3:"Ⴃ";s:3:"ⴄ";s:3:"Ⴄ";s:3:"ⴅ";s:3:"Ⴅ";s:3:"ⴆ";s:3:"Ⴆ";s:3:"ⴇ";s:3:"Ⴇ";s:3:"ⴈ";s:3:"Ⴈ";s:3:"ⴉ";s:3:"Ⴉ";s:3:"ⴊ";s:3:"Ⴊ";s:3:"ⴋ";s:3:"Ⴋ";s:3:"ⴌ";s:3:"Ⴌ";s:3:"ⴍ";s:3:"Ⴍ";s:3:"ⴎ";s:3:"Ⴎ";s:3:"ⴏ";s:3:"Ⴏ";s:3:"ⴐ";s:3:"Ⴐ";s:3:"ⴑ";s:3:"Ⴑ";s:3:"ⴒ";s:3:"Ⴒ";s:3:"ⴓ";s:3:"Ⴓ";s:3:"ⴔ";s:3:"Ⴔ";s:3:"ⴕ";s:3:"Ⴕ";s:3:"ⴖ";s:3:"Ⴖ";s:3:"ⴗ";s:3:"Ⴗ";s:3:"ⴘ";s:3:"Ⴘ";s:3:"ⴙ";s:3:"Ⴙ";s:3:"ⴚ";s:3:"Ⴚ";s:3:"ⴛ";s:3:"Ⴛ";s:3:"ⴜ";s:3:"Ⴜ";s:3:"ⴝ";s:3:"Ⴝ";s:3:"ⴞ";s:3:"Ⴞ";s:3:"ⴟ";s:3:"Ⴟ";s:3:"ⴠ";s:3:"Ⴠ";s:3:"ⴡ";s:3:"Ⴡ";s:3:"ⴢ";s:3:"Ⴢ";s:3:"ⴣ";s:3:"Ⴣ";s:3:"ⴤ";s:3:"Ⴤ";s:3:"ⴥ";s:3:"Ⴥ";s:3:"ⴧ";s:3:"Ⴧ";s:3:"ⴭ";s:3:"Ⴭ";s:3:"ꙁ";s:3:"Ꙁ";s:3:"ꙃ";s:3:"Ꙃ";s:3:"ꙅ";s:3:"Ꙅ";s:3:"ꙇ";s:3:"Ꙇ";s:3:"ꙉ";s:3:"Ꙉ";s:3:"ꙋ";s:3:"Ꙋ";s:3:"ꙍ";s:3:"Ꙍ";s:3:"ꙏ";s:3:"Ꙏ";s:3:"ꙑ";s:3:"Ꙑ";s:3:"ꙓ";s:3:"Ꙓ";s:3:"ꙕ";s:3:"Ꙕ";s:3:"ꙗ";s:3:"Ꙗ";s:3:"ꙙ";s:3:"Ꙙ";s:3:"ꙛ";s:3:"Ꙛ";s:3:"ꙝ";s:3:"Ꙝ";s:3:"ꙟ";s:3:"Ꙟ";s:3:"ꙡ";s:3:"Ꙡ";s:3:"ꙣ";s:3:"Ꙣ";s:3:"ꙥ";s:3:"Ꙥ";s:3:"ꙧ";s:3:"Ꙧ";s:3:"ꙩ";s:3:"Ꙩ";s:3:"ꙫ";s:3:"Ꙫ";s:3:"ꙭ";s:3:"Ꙭ";s:3:"ꚁ";s:3:"Ꚁ";s:3:"ꚃ";s:3:"Ꚃ";s:3:"ꚅ";s:3:"Ꚅ";s:3:"ꚇ";s:3:"Ꚇ";s:3:"ꚉ";s:3:"Ꚉ";s:3:"ꚋ";s:3:"Ꚋ";s:3:"ꚍ";s:3:"Ꚍ";s:3:"ꚏ";s:3:"Ꚏ";s:3:"ꚑ";s:3:"Ꚑ";s:3:"ꚓ";s:3:"Ꚓ";s:3:"ꚕ";s:3:"Ꚕ";s:3:"ꚗ";s:3:"Ꚗ";s:3:"ꚙ";s:3:"Ꚙ";s:3:"ꚛ";s:3:"Ꚛ";s:3:"ꜣ";s:3:"Ꜣ";s:3:"ꜥ";s:3:"Ꜥ";s:3:"ꜧ";s:3:"Ꜧ";s:3:"ꜩ";s:3:"Ꜩ";s:3:"ꜫ";s:3:"Ꜫ";s:3:"ꜭ";s:3:"Ꜭ";s:3:"ꜯ";s:3:"Ꜯ";s:3:"ꜳ";s:3:"Ꜳ";s:3:"ꜵ";s:3:"Ꜵ";s:3:"ꜷ";s:3:"Ꜷ";s:3:"ꜹ";s:3:"Ꜹ";s:3:"ꜻ";s:3:"Ꜻ";s:3:"ꜽ";s:3:"Ꜽ";s:3:"ꜿ";s:3:"Ꜿ";s:3:"ꝁ";s:3:"Ꝁ";s:3:"ꝃ";s:3:"Ꝃ";s:3:"ꝅ";s:3:"Ꝅ";s:3:"ꝇ";s:3:"Ꝇ";s:3:"ꝉ";s:3:"Ꝉ";s:3:"ꝋ";s:3:"Ꝋ";s:3:"ꝍ";s:3:"Ꝍ";s:3:"ꝏ";s:3:"Ꝏ";s:3:"ꝑ";s:3:"Ꝑ";s:3:"ꝓ";s:3:"Ꝓ";s:3:"ꝕ";s:3:"Ꝕ";s:3:"ꝗ";s:3:"Ꝗ";s:3:"ꝙ";s:3:"Ꝙ";s:3:"ꝛ";s:3:"Ꝛ";s:3:"ꝝ";s:3:"Ꝝ";s:3:"ꝟ";s:3:"Ꝟ";s:3:"ꝡ";s:3:"Ꝡ";s:3:"ꝣ";s:3:"Ꝣ";s:3:"ꝥ";s:3:"Ꝥ";s:3:"ꝧ";s:3:"Ꝧ";s:3:"ꝩ";s:3:"Ꝩ";s:3:"ꝫ";s:3:"Ꝫ";s:3:"ꝭ";s:3:"Ꝭ";s:3:"ꝯ";s:3:"Ꝯ";s:3:"ꝺ";s:3:"Ꝺ";s:3:"ꝼ";s:3:"Ꝼ";s:3:"ꝿ";s:3:"Ꝿ";s:3:"ꞁ";s:3:"Ꞁ";s:3:"ꞃ";s:3:"Ꞃ";s:3:"ꞅ";s:3:"Ꞅ";s:3:"ꞇ";s:3:"Ꞇ";s:3:"ꞌ";s:3:"Ꞌ";s:3:"ꞑ";s:3:"Ꞑ";s:3:"ꞓ";s:3:"Ꞓ";s:3:"ꞗ";s:3:"Ꞗ";s:3:"ꞙ";s:3:"Ꞙ";s:3:"ꞛ";s:3:"Ꞛ";s:3:"ꞝ";s:3:"Ꞝ";s:3:"ꞟ";s:3:"Ꞟ";s:3:"ꞡ";s:3:"Ꞡ";s:3:"ꞣ";s:3:"Ꞣ";s:3:"ꞥ";s:3:"Ꞥ";s:3:"ꞧ";s:3:"Ꞧ";s:3:"ꞩ";s:3:"Ꞩ";s:3:"a";s:3:"A";s:3:"b";s:3:"B";s:3:"c";s:3:"C";s:3:"d";s:3:"D";s:3:"e";s:3:"E";s:3:"f";s:3:"F";s:3:"g";s:3:"G";s:3:"h";s:3:"H";s:3:"i";s:3:"I";s:3:"j";s:3:"J";s:3:"k";s:3:"K";s:3:"l";s:3:"L";s:3:"m";s:3:"M";s:3:"n";s:3:"N";s:3:"o";s:3:"O";s:3:"p";s:3:"P";s:3:"q";s:3:"Q";s:3:"r";s:3:"R";s:3:"s";s:3:"S";s:3:"t";s:3:"T";s:3:"u";s:3:"U";s:3:"v";s:3:"V";s:3:"w";s:3:"W";s:3:"x";s:3:"X";s:3:"y";s:3:"Y";s:3:"z";s:3:"Z";s:4:"𐐨";s:4:"𐐀";s:4:"𐐩";s:4:"𐐁";s:4:"𐐪";s:4:"𐐂";s:4:"𐐫";s:4:"𐐃";s:4:"𐐬";s:4:"𐐄";s:4:"𐐭";s:4:"𐐅";s:4:"𐐮";s:4:"𐐆";s:4:"𐐯";s:4:"𐐇";s:4:"𐐰";s:4:"𐐈";s:4:"𐐱";s:4:"𐐉";s:4:"𐐲";s:4:"𐐊";s:4:"𐐳";s:4:"𐐋";s:4:"𐐴";s:4:"𐐌";s:4:"𐐵";s:4:"𐐍";s:4:"𐐶";s:4:"𐐎";s:4:"𐐷";s:4:"𐐏";s:4:"𐐸";s:4:"𐐐";s:4:"𐐹";s:4:"𐐑";s:4:"𐐺";s:4:"𐐒";s:4:"𐐻";s:4:"𐐓";s:4:"𐐼";s:4:"𐐔";s:4:"𐐽";s:4:"𐐕";s:4:"𐐾";s:4:"𐐖";s:4:"𐐿";s:4:"𐐗";s:4:"𐑀";s:4:"𐐘";s:4:"𐑁";s:4:"𐐙";s:4:"𐑂";s:4:"𐐚";s:4:"𐑃";s:4:"𐐛";s:4:"𐑄";s:4:"𐐜";s:4:"𐑅";s:4:"𐐝";s:4:"𐑆";s:4:"𐐞";s:4:"𐑇";s:4:"𐐟";s:4:"𐑈";s:4:"𐐠";s:4:"𐑉";s:4:"𐐡";s:4:"𐑊";s:4:"𐐢";s:4:"𐑋";s:4:"𐐣";s:4:"𐑌";s:4:"𐐤";s:4:"𐑍";s:4:"𐐥";s:4:"𐑎";s:4:"𐐦";s:4:"𐑏";s:4:"𐐧";s:4:"𑣀";s:4:"𑢠";s:4:"𑣁";s:4:"𑢡";s:4:"𑣂";s:4:"𑢢";s:4:"𑣃";s:4:"𑢣";s:4:"𑣄";s:4:"𑢤";s:4:"𑣅";s:4:"𑢥";s:4:"𑣆";s:4:"𑢦";s:4:"𑣇";s:4:"𑢧";s:4:"𑣈";s:4:"𑢨";s:4:"𑣉";s:4:"𑢩";s:4:"𑣊";s:4:"𑢪";s:4:"𑣋";s:4:"𑢫";s:4:"𑣌";s:4:"𑢬";s:4:"𑣍";s:4:"𑢭";s:4:"𑣎";s:4:"𑢮";s:4:"𑣏";s:4:"𑢯";s:4:"𑣐";s:4:"𑢰";s:4:"𑣑";s:4:"𑢱";s:4:"𑣒";s:4:"𑢲";s:4:"𑣓";s:4:"𑢳";s:4:"𑣔";s:4:"𑢴";s:4:"𑣕";s:4:"𑢵";s:4:"𑣖";s:4:"𑢶";s:4:"𑣗";s:4:"𑢷";s:4:"𑣘";s:4:"𑢸";s:4:"𑣙";s:4:"𑢹";s:4:"𑣚";s:4:"𑢺";s:4:"𑣛";s:4:"𑢻";s:4:"𑣜";s:4:"𑢼";s:4:"𑣝";s:4:"𑢽";s:4:"𑣞";s:4:"𑢾";s:4:"𑣟";s:4:"𑢿";} \ No newline at end of file diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 0000000..4b01b61 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_strlen')) { + define('MB_CASE_UPPER', 0); + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + + function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } + function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } + function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } + function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } + function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } + function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } + function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } + function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } + function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } + function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } + function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } + function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } + function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } + function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } + function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } + function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } + function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } + function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } + function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } + function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } + function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } + function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } + function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } + function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } + function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } + function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } + function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $v0, $a, $b, $c, $d, $e, $f); } +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 0000000..5e629ea --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/symfony/polyfill-php56/LICENSE b/vendor/symfony/polyfill-php56/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/polyfill-php56/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-php56/Php56.php b/vendor/symfony/polyfill-php56/Php56.php new file mode 100644 index 0000000..2e809fe --- /dev/null +++ b/vendor/symfony/polyfill-php56/Php56.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php56; + +use Symfony\Polyfill\Util\Binary; + +/** + * @internal + */ +final class Php56 +{ + const LDAP_ESCAPE_FILTER = 1; + const LDAP_ESCAPE_DN = 2; + + public static function hash_equals($knownString, $userInput) + { + if (!is_string($knownString)) { + trigger_error('Expected known_string to be a string, '.gettype($knownString).' given', E_USER_WARNING); + + return false; + } + + if (!is_string($userInput)) { + trigger_error('Expected user_input to be a string, '.gettype($userInput).' given', E_USER_WARNING); + + return false; + } + + $knownLen = Binary::strlen($knownString); + $userLen = Binary::strlen($userInput); + + if ($knownLen !== $userLen) { + return false; + } + + $result = 0; + + for ($i = 0; $i < $knownLen; ++$i) { + $result |= ord($knownString[$i]) ^ ord($userInput[$i]); + } + + return 0 === $result; + } + + /** + * 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 + * + * @param string $subject + * @param string $ignore + * @param int $flags + * + * @return string + * + * @see http://stackoverflow.com/a/8561604 + */ + public static function ldap_escape($subject, $ignore = '', $flags = 0) + { + static $charMaps = null; + + if (null === $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]; + } + } + + // 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); + + return $result; + } +} diff --git a/vendor/symfony/polyfill-php56/README.md b/vendor/symfony/polyfill-php56/README.md new file mode 100644 index 0000000..7805cdf --- /dev/null +++ b/vendor/symfony/polyfill-php56/README.md @@ -0,0 +1,15 @@ +Symfony Polyfill / Php56 +======================== + +This component provides functions unavailable in releases prior to PHP 5.6: + +- [`hash_equals`](http://php.net/hash_equals) (part of [hash](http://php.net/hash) extension) +- [`ldap_escape`](http://php.net/ldap_escape) (part of [ldap](http://php.net/ldap) extension) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). \ No newline at end of file diff --git a/vendor/symfony/polyfill-php56/bootstrap.php b/vendor/symfony/polyfill-php56/bootstrap.php new file mode 100644 index 0000000..587c2a8 --- /dev/null +++ b/vendor/symfony/polyfill-php56/bootstrap.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php56 as p; + +if (PHP_VERSION_ID < 50600) { + if (!function_exists('hash_equals')) { + function hash_equals($knownString, $userInput) { return p\Php56::hash_equals($knownString, $userInput); } + } + if (extension_loaded('ldap') && !function_exists('ldap_escape')) { + define('LDAP_ESCAPE_FILTER', 1); + define('LDAP_ESCAPE_DN', 2); + + function ldap_escape($subject, $ignore = '', $flags = 0) { return p\Php56::ldap_escape($subject, $ignore, $flags); } + } + + if (50509 === PHP_VERSION_ID && 4 === PHP_INT_SIZE) { + // Missing functions in PHP 5.5.9 - affects 32 bit builds of Ubuntu 14.04LTS + // See https://bugs.launchpad.net/ubuntu/+source/php5/+bug/1315888 + if (!function_exists('gzopen') && function_exists('gzopen64')) { + function gzopen($filename, $mode, $use_include_path = 0) { return gzopen64($filename, $mode, $use_include_path); } + } + if (!function_exists('gzseek') && function_exists('gzseek64')) { + function gzseek($zp, $offset, $whence = SEEK_SET) { return gzseek64($zp, $offset, $whence); } + } + if (!function_exists('gztell') && function_exists('gztell64')) { + function gztell($zp) { return gztell64($zp); } + } + } +} diff --git a/vendor/symfony/polyfill-php56/composer.json b/vendor/symfony/polyfill-php56/composer.json new file mode 100644 index 0000000..2d1bf2a --- /dev/null +++ b/vendor/symfony/polyfill-php56/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony/polyfill-php56", + "type": "library", + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php56\\": "" }, + "files": [ "bootstrap.php" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/symfony/polyfill-php70/LICENSE b/vendor/symfony/polyfill-php70/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/polyfill-php70/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-php70/Php70.php b/vendor/symfony/polyfill-php70/Php70.php new file mode 100644 index 0000000..326b659 --- /dev/null +++ b/vendor/symfony/polyfill-php70/Php70.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php70; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class Php70 +{ + public static function intdiv($dividend, $divisor) + { + $dividend = self::intArg($dividend, __FUNCTION__, 1); + $divisor = self::intArg($divisor, __FUNCTION__, 2); + + if (0 === $divisor) { + throw new \DivisionByZeroError('Division by zero'); + } + if (-1 === $divisor && ~PHP_INT_MAX === $dividend) { + throw new \ArithmeticError('Division of PHP_INT_MIN by -1 is not an integer'); + } + + return ($dividend - ($dividend % $divisor)) / $divisor; + } + + public static function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) + { + $count = 0; + $result = ''.$subject; + if (0 === $limit = self::intArg($limit, __FUNCTION__, 3)) { + return $result; + } + + foreach ($patterns as $pattern => $callback) { + $result = preg_replace_callback($pattern, $callback, $result, $limit, $c); + $count += $c; + } + + return $result; + } + + public static function error_clear_last() + { + set_error_handler('var_dump', 0); + @trigger_error(''); + restore_error_handler(); + } + + public static function intArg($value, $caller, $pos) + { + if (is_int($value)) { + return $value; + } + if (!is_numeric($value) || PHP_INT_MAX <= ($value += 0) || ~PHP_INT_MAX >= $value) { + throw new \TypeError(sprintf('%s() expects parameter %d to be integer, %s given', $caller, $pos, gettype($value))); + } + + return (int) $value; + } +} diff --git a/vendor/symfony/polyfill-php70/README.md b/vendor/symfony/polyfill-php70/README.md new file mode 100644 index 0000000..a03b8a7 --- /dev/null +++ b/vendor/symfony/polyfill-php70/README.md @@ -0,0 +1,18 @@ +Symfony Polyfill / Php70 +======================== + +This component provides functions unavailable in releases prior to PHP 7.0: + +- [`intdiv`](http://php.net/intdiv) +- [`preg_replace_callback_array`](http://php.net/preg_replace_callback_array) +- [`error_clear_last`](http://php.net/error_clear_last) +- `random_bytes` and `random_int` (from [paragonie/random_compat](https://github.com/paragonie/random_compat)) +- [`*Error` throwable classes](http://php.net/Error) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). \ No newline at end of file diff --git a/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php new file mode 100644 index 0000000..6819124 --- /dev/null +++ b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php @@ -0,0 +1,5 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php70 as p; + +if (PHP_VERSION_ID < 70000) { + if (!function_exists('intdiv')) { + function intdiv($dividend, $divisor) { return p\Php70::intdiv($dividend, $divisor); } + } + if (!function_exists('preg_replace_callback_array')) { + function preg_replace_callback_array(array $patterns, $subject, $limit = -1, &$count = 0) { return p\Php70::preg_replace_callback_array($patterns, $subject, $limit, $count); } + } + if (!function_exists('error_clear_last')) { + function error_clear_last() { return p\Php70::error_clear_last(); } + } +} diff --git a/vendor/symfony/polyfill-php70/composer.json b/vendor/symfony/polyfill-php70/composer.json new file mode 100644 index 0000000..794a5a6 --- /dev/null +++ b/vendor/symfony/polyfill-php70/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/polyfill-php70", + "type": "library", + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "paragonie/random_compat": "~1.0" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php70\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/symfony/polyfill-util/Binary.php b/vendor/symfony/polyfill-util/Binary.php new file mode 100644 index 0000000..815bc75 --- /dev/null +++ b/vendor/symfony/polyfill-util/Binary.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +if (extension_loaded('mbstring')) { + class Binary extends BinaryOnFuncOverload {} +} else { + class Binary extends BinaryNoFuncOverload {} +} diff --git a/vendor/symfony/polyfill-util/BinaryNoFuncOverload.php b/vendor/symfony/polyfill-util/BinaryNoFuncOverload.php new file mode 100644 index 0000000..5ef3c6c --- /dev/null +++ b/vendor/symfony/polyfill-util/BinaryNoFuncOverload.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class BinaryNoFuncOverload +{ + public static function strlen($s) + { + return strlen($s); + } + + public static function strpos($haystack, $needle, $offset = 0) + { + return strpos($haystack, $needle, $offset); + } + + public static function strrpos($haystack, $needle, $offset = 0) + { + return strrpos($haystack, $needle, $offset); + } + + public static function substr($string, $start, $length = PHP_INT_MAX) + { + return substr($string, $start, $length); + } + + public static function stripos($s, $needle, $offset = 0) + { + return stripos($s, $needle, $offset); + } + + public static function stristr($s, $needle, $part = false) + { + return stristr($s, $needle, $part); + } + + public static function strrchr($s, $needle, $part = false) + { + return strrchr($s, $needle, $part); + } + + public static function strripos($s, $needle, $offset = 0) + { + return strripos($s, $needle, $offset); + } + + public static function strstr($s, $needle, $part = false) + { + return strstr($s, $needle, $part); + } +} diff --git a/vendor/symfony/polyfill-util/BinaryOnFuncOverload.php b/vendor/symfony/polyfill-util/BinaryOnFuncOverload.php new file mode 100644 index 0000000..e1b886e --- /dev/null +++ b/vendor/symfony/polyfill-util/BinaryOnFuncOverload.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * Binary safe version of string functions overloaded when MB_OVERLOAD_STRING is enabled. + * + * @author Nicolas Grekas + * + * @internal + */ +class BinaryOnFuncOverload +{ + public static function strlen($s) + { + return mb_strlen($s, '8bit'); + } + + public static function strpos($haystack, $needle, $offset = 0) + { + return mb_strpos($haystack, $needle, $offset, '8bit'); + } + + public static function strrpos($haystack, $needle, $offset = 0) + { + return mb_strrpos($haystack, $needle, $offset, '8bit'); + } + + public static function substr($string, $start, $length = 2147483647) + { + return mb_substr($string, $start, $length, '8bit'); + } + + public static function stripos($s, $needle, $offset = 0) + { + return mb_stripos($s, $needle, $offset, '8bit'); + } + + public static function stristr($s, $needle, $part = false) + { + return mb_stristr($s, $needle, $part, '8bit'); + } + + public static function strrchr($s, $needle, $part = false) + { + return mb_strrchr($s, $needle, $part, '8bit'); + } + + public static function strripos($s, $needle, $offset = 0) + { + return mb_strripos($s, $needle, $offset, '8bit'); + } + + public static function strstr($s, $needle, $part = false) + { + return mb_strstr($s, $needle, $part, '8bit'); + } +} diff --git a/vendor/symfony/polyfill-util/LICENSE b/vendor/symfony/polyfill-util/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/polyfill-util/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/polyfill-util/README.md b/vendor/symfony/polyfill-util/README.md new file mode 100644 index 0000000..96e8a04 --- /dev/null +++ b/vendor/symfony/polyfill-util/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Util +======================= + +This component provides binary-safe string functions, using the +[mbstring](https://php.net/mbstring) extension when available. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). \ No newline at end of file diff --git a/vendor/symfony/polyfill-util/TestListener.php b/vendor/symfony/polyfill-util/TestListener.php new file mode 100644 index 0000000..4b9f856 --- /dev/null +++ b/vendor/symfony/polyfill-util/TestListener.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * @author Nicolas Grekas + */ +class TestListener extends \PHPUnit_Framework_TestSuite implements \PHPUnit_Framework_TestListener +{ + public static $enabledPolyfills; + private $suite; + + public function __construct(\PHPUnit_Framework_TestSuite $suite = null) + { + if ($suite) { + $this->suite = $suite; + $this->setName($suite->getName().' with polyfills enabled'); + $this->addTest($suite); + } + } + + public function startTestSuite(\PHPUnit_Framework_TestSuite $mainSuite) + { + if (null !== self::$enabledPolyfills) { + return; + } + self::$enabledPolyfills = false; + + foreach ($mainSuite->tests() as $suite) { + $testClass = $suite->getName(); + if (!$tests = $suite->tests()) { + continue; + } + if (!preg_match('/^(.+)\\\\Tests(\\\\.*)Test$/', $testClass, $m)) { + $mainSuite->addTest(self::warning('Unknown naming convention for '.$testClass)); + continue; + } + $testedClass = new \ReflectionClass($m[1].$m[2]); + $bootstrap = new \SplFileObject(dirname($testedClass->getFileName()).'/bootstrap.php'); + $warnings = array(); + $defLine = null; + + foreach (new \RegexIterator($bootstrap, '/return p\\\\'.$testedClass->getShortName().'::/') as $defLine) { + if (!preg_match('/^\s*function (?P[^\(]++)(?P\([^\)]*+\)) \{ (?return p\\\\'.$testedClass->getShortName().'::[^\(]++)(?P\([^\)]*+\)); \}$/', $defLine, $f)) { + $warnings[] = self::warning('Invalid line in bootstrap.php: '.trim($defLine)); + continue; + } + $testNamespace = substr($testClass, 0, strrpos($testClass, '\\')); + if (function_exists($testNamespace.'\\'.$f['name'])) { + continue; + } + + try { + $r = new \ReflectionFunction($f['name']); + if ($r->isUserDefined()) { + throw new \ReflectionException(); + } + if (false !== strpos($f['signature'], '&')) { + $defLine = sprintf('return \\%s%s', $f['name'], $f['args']); + } else { + $defLine = sprintf("return \\call_user_func_array('%s', func_get_args())", $f['name']); + } + } catch (\ReflectionException $e) { + $defLine = sprintf("throw new \PHPUnit_Framework_SkippedTestError('Internal function not found: %s')", $f['name']); + } + + eval(<<getNamespaceName()} as p; + +function {$f['name']}{$f['signature']} +{ + if ('{$testClass}' === TestListener::\$enabledPolyfills) { + {$f['return']}{$f['args']}; + } + + {$defLine}; +} +EOPHP + ); + } + if (!$warnings && null === $defLine) { + $warnings[] = new \PHPUnit_Framework_SkippedTestCase('No Polyfills found in bootstrap.php for '.$testClass); + } else { + $mainSuite->addTest(new static($suite)); + } + } + foreach ($warnings as $w) { + $mainSuite->addTest($w); + } + } + + protected function setUp() + { + self::$enabledPolyfills = $this->suite->getName(); + } + + protected function tearDown() + { + self::$enabledPolyfills = false; + } + + public function addError(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + if (false !== self::$enabledPolyfills) { + $r = new \ReflectionProperty('Exception', 'message'); + $r->setAccessible(true); + $r->setValue($e, 'Polyfills enabled, '.$r->getValue($e)); + } + } + + public function addFailure(\PHPUnit_Framework_Test $test, \PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->addError($test, $e, $time); + } + + public function addIncompleteTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + } + + public function addRiskyTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + } + + public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + } + + public function endTestSuite(\PHPUnit_Framework_TestSuite $suite) + { + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + } + + public function endTest(\PHPUnit_Framework_Test $test, $time) + { + } +} diff --git a/vendor/symfony/polyfill-util/composer.json b/vendor/symfony/polyfill-util/composer.json new file mode 100644 index 0000000..0bfdd8f --- /dev/null +++ b/vendor/symfony/polyfill-util/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/polyfill-util", + "type": "library", + "description": "Symfony utilities for portability of PHP codes", + "keywords": ["polyfill", "shim", "compat", "compatibility"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Util\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + } +} diff --git a/vendor/symfony/swiftmailer-bundle/.travis.yml b/vendor/symfony/swiftmailer-bundle/.travis.yml new file mode 100644 index 0000000..910427c --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/.travis.yml @@ -0,0 +1,32 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +sudo: false + +cache: + directories: + - $HOME/.composer/cache/files + +matrix: + fast_finish: true + include: + - php: 5.3 + env: COMPOSER_FLAGS="--prefer-lowest" SYMFONY_DEPRECATIONS_HELPER=weak + - php: 5.6 + env: SYMFONY_VERSION=2.8.*@dev + - php: 5.6 + env: DEPENDENCIES=dev + +before_install: + - composer self-update + - if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update "symfony/symfony:$SYMFONY_VERSION"; fi + - if [ "$DEPENDENCIES" = "dev" ]; then perl -pi -e 's/^}$/,"minimum-stability":"dev"}/' composer.json; fi; + +install: composer update $COMPOSER_FLAGS diff --git a/vendor/symfony/swiftmailer-bundle/Command/DebugCommand.php b/vendor/symfony/swiftmailer-bundle/Command/DebugCommand.php new file mode 100644 index 0000000..c3061f1 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Command/DebugCommand.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +/** + * A console command for retrieving information about mailers + * + * @author Jérémy Romey + */ +class DebugCommand extends ContainerAwareCommand +{ + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('debug:swiftmailer') + ->setAliases(array( + 'swiftmailer:debug', + )) + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A mailer name'), + )) + ->setDescription('Displays current mailers for an application') + ->setHelp(<<%command.name%
    displays the configured mailers: + + php %command.full_name% mailer-name +EOF + ) + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $name = $input->getArgument('name'); + + if ($name) { + $this->outputMailer($output, $name); + } else { + $this->outputMailers($output); + } + } + + protected function outputMailers(OutputInterface $output, $routes = null) + { + $output->writeln($this->getHelper('formatter')->formatSection('swiftmailer', 'Current mailers')); + + $maxName = strlen('name'); + $maxTransport = strlen('transport'); + $maxSpool = strlen('spool'); + $maxDelivery = strlen('delivery'); + $maxSingleAddress = strlen('single address'); + + $mailers = $this->getContainer()->getParameter('swiftmailer.mailers'); + foreach ($mailers as $name => $mailer) { + $mailer = $this->getContainer()->get($mailer); + $transport = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.transport.name', $name)); + $spool = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name)) ? 'YES' : 'NO'; + $delivery = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.delivery.enabled', $name)) ? 'YES' : 'NO'; + $singleAddress = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.single_address', $name)); + + if ($this->isDefaultMailer($name)) { + $name = sprintf('%s (default mailer)', $name); + } + $maxName = max($maxName, strlen($name)); + $maxTransport = max($maxTransport, strlen($transport)); + $maxSpool = max($maxSpool, strlen($spool)); + $maxDelivery = max($maxDelivery, strlen($delivery)); + $maxSingleAddress = max($maxSingleAddress, strlen($singleAddress)); + } + $format = '%-'.$maxName.'s %-'.$maxTransport.'s %-'.$maxSpool.'s %-'.$maxDelivery.'s %-'.$maxSingleAddress.'s'; + + $format1 = '%-'.($maxName + 19).'s %-'.($maxTransport + 19).'s %-'.($maxSpool + 19).'s %-'.($maxDelivery + 19).'s %-'.($maxSingleAddress + 19).'s'; + $output->writeln(sprintf($format1, 'Name', 'Transport', 'Spool', 'Delivery', 'Single Address')); + foreach ($mailers as $name => $mailer) { + $mailer = $this->getContainer()->get($mailer); + $transport = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.transport.name', $name)); + $spool = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name)) ? 'YES' : 'NO'; + $delivery = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.delivery.enabled', $name)) ? 'YES' : 'NO'; + $singleAddress = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.single_address', $name)); + if ($this->isDefaultMailer($name)) { + $name = sprintf('%s (default mailer)', $name); + } + $output->writeln(sprintf($format, $name, $transport, $spool, $delivery, $singleAddress)); + } + } + + /** + * @throws \InvalidArgumentException When route does not exist + */ + protected function outputMailer(OutputInterface $output, $name) + { + try { + $service = sprintf('swiftmailer.mailer.%s', $name); + $mailer = $this->getContainer()->get($service); + } catch (ServiceNotFoundException $e) { + throw new \InvalidArgumentException(sprintf('The mailer "%s" does not exist.', $name)); + } + + $transport = $mailer->getTransport(); + $spool = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name)) ? 'YES' : 'NO'; + $delivery = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.delivery.enabled', $name)) ? 'YES' : 'NO'; + $singleAddress = $this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.single_address', $name)); + + $output->writeln($this->getHelper('formatter')->formatSection('swiftmailer', sprintf('Mailer "%s"', $name))); + if ($this->isDefaultMailer($name)) { + $output->writeln('This is the default mailer'); + } + + $output->writeln(sprintf('Name %s', $name)); + $output->writeln(sprintf('Service %s', $service)); + $output->writeln(sprintf('Class %s', get_class($mailer))); + $output->writeln(sprintf('Transport %s (%s)', sprintf('swiftmailer.mailer.%s.transport.name', $name), get_class($transport))); + $output->writeln(sprintf('Spool %s', $spool)); + if ($this->getContainer()->hasParameter(sprintf('swiftmailer.spool.%s.file.path', $name))) { + $output->writeln(sprintf('Spool file %s', $this->getContainer()->getParameter(sprintf('swiftmailer.spool.%s.file.path', $name)))); + } + $output->writeln(sprintf('Delivery %s', $delivery)); + $output->writeln(sprintf('Single Address %s', $singleAddress)); + } + + private function isDefaultMailer($name) + { + return ($this->getContainer()->getParameter('swiftmailer.default_mailer') == $name || 'default' == $name) ? true : false; + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Command/NewEmailCommand.php b/vendor/symfony/swiftmailer-bundle/Command/NewEmailCommand.php new file mode 100644 index 0000000..117abb2 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Command/NewEmailCommand.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * A console command for creating and sending simple emails + * + * @author Gusakov Nikita + */ +class NewEmailCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('swiftmailer:email:send') + ->setDescription('Send simple email message') + ->addOption('from', null, InputOption::VALUE_REQUIRED, 'The from address of the message') + ->addOption('to', null, InputOption::VALUE_REQUIRED, 'The to address of the message') + ->addOption('subject', null, InputOption::VALUE_REQUIRED, 'The subject of the message') + ->addOption('body', null, InputOption::VALUE_REQUIRED, 'The body of the message') + ->addOption('mailer', null, InputOption::VALUE_REQUIRED, 'The mailer name', 'default') + ->addOption('content-type', null, InputOption::VALUE_REQUIRED, 'The body content type of the message', 'text/html') + ->addOption('charset', null, InputOption::VALUE_REQUIRED, 'The body charset of the message', 'UTF8') + ->addOption('body-source', null, InputOption::VALUE_REQUIRED, 'The source where body come from [stdin|file]', 'stdin') + ->setHelp(<<%command.name%
    command creates and send simple email message. + +php %command.full_name% --mailer=custom_mailer --content-type=text/xml + +You can get body of message from file: +php %command.full_name% --body-source=file --body=/path/to/file + +EOF + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $mailerServiceName = sprintf('swiftmailer.mailer.%s', $input->getOption('mailer')); + if (!$this->getContainer()->has($mailerServiceName)) { + throw new \InvalidArgumentException(sprintf('The mailer "%s" does not exist', $this->getOption('mailer'))); + } + switch ($input->getOption('body-source')) { + case 'file': + $filename = $input->getOption('body'); + $content = file_get_contents($filename); + if ($content === false) { + throw new \Exception('Could not get contents from ' . $filename); + } + $input->setOption('body', $content); + break; + case 'stdin': + break; + default: + throw new \InvalidArgumentException('Body-input option should be "stdin" or "file"'); + } + + $message = $this->createMessage($input); + $mailer = $this->getContainer()->get($mailerServiceName); + $output->writeln(sprintf('Sent %s emails', $mailer->send($message))); + } + + /** + * {@inheritdoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getHelper('dialog'); + foreach ($input->getOptions() as $option => $value) { + if ($value === null) { + $input->setOption($option, $dialog->ask($output, + sprintf('%s: ', ucfirst($option)) + )); + } + } + } + + /** + * {@inheritdoc} + */ + public function isEnabled() + { + return $this->getContainer()->has('mailer'); + } + + /** + * Creates new message from input options. + * + * @param InputInterface $input An InputInterface instance + * + * @return \Swift_Message New message + */ + private function createMessage(InputInterface $input) + { + $message = \Swift_Message::newInstance( + $input->getOption('subject'), + $input->getOption('body'), + $input->getOption('content-type'), + $input->getOption('charset') + ); + $message->setFrom($input->getOption('from')); + $message->setTo($input->getOption('to')); + + return $message; + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Command/SendEmailCommand.php b/vendor/symfony/swiftmailer-bundle/Command/SendEmailCommand.php new file mode 100644 index 0000000..5ef4f38 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Command/SendEmailCommand.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; + +/** + * Send Emails from the spool. + * + * @author Fabien Potencier + * @author Clément JOBEILI + */ +class SendEmailCommand extends ContainerAwareCommand +{ + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('swiftmailer:spool:send') + ->setDescription('Sends emails from the spool') + ->addOption('message-limit', 0, InputOption::VALUE_OPTIONAL, 'The maximum number of messages to send.') + ->addOption('time-limit', 0, InputOption::VALUE_OPTIONAL, 'The time limit for sending messages (in seconds).') + ->addOption('recover-timeout', 0, InputOption::VALUE_OPTIONAL, 'The timeout for recovering messages that have taken too long to send (in seconds).') + ->addOption('mailer', null, InputOption::VALUE_OPTIONAL, 'The mailer name.') + ->setHelp(<<swiftmailer:spool:send command sends all emails from the spool. + +php %command.full_name% --message-limit=10 --time-limit=10 --recover-timeout=900 --mailer=default + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $name = $input->getOption('mailer'); + if ($name) { + $this->processMailer($name, $input, $output); + } else { + $mailers = array_keys($this->getContainer()->getParameter('swiftmailer.mailers')); + foreach ($mailers as $name) { + $this->processMailer($name, $input, $output); + } + } + } + + private function processMailer($name, $input, $output) + { + if (!$this->getContainer()->has(sprintf('swiftmailer.mailer.%s', $name))) { + throw new \InvalidArgumentException(sprintf('The mailer "%s" does not exist.', $name)); + } + + $output->write(sprintf('[%s] Processing %s mailer... ', date('Y-m-d H:i:s'), $name)); + if ($this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name))) { + $mailer = $this->getContainer()->get(sprintf('swiftmailer.mailer.%s', $name)); + $transport = $mailer->getTransport(); + if ($transport instanceof \Swift_Transport_SpoolTransport) { + $spool = $transport->getSpool(); + if ($spool instanceof \Swift_ConfigurableSpool) { + $spool->setMessageLimit($input->getOption('message-limit')); + $spool->setTimeLimit($input->getOption('time-limit')); + } + if ($spool instanceof \Swift_FileSpool) { + if (null !== $input->getOption('recover-timeout')) { + $spool->recover($input->getOption('recover-timeout')); + } else { + $spool->recover(); + } + } + $sent = $spool->flushQueue($this->getContainer()->get(sprintf('swiftmailer.mailer.%s.transport.real', $name))); + + $output->writeln(sprintf('%d emails sent', $sent)); + } + } else { + $output->writeln('No email to send as the spool is disabled.'); + } + } +} diff --git a/vendor/symfony/swiftmailer-bundle/DataCollector/MessageDataCollector.php b/vendor/symfony/swiftmailer-bundle/DataCollector/MessageDataCollector.php new file mode 100644 index 0000000..85733bb --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/DataCollector/MessageDataCollector.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * MessageDataCollector. + * + * @author Fabien Potencier + * @author Clément JOBEILI + * @author Jérémy Romey + */ +class MessageDataCollector extends DataCollector +{ + private $container; + + /** + * Constructor. + * + * We don't inject the message logger and mailer here + * to avoid the creation of these objects when no emails are sent. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'mailer' => array(), + 'messageCount' => 0, + 'defaultMailer' => '', + ); + // only collect when Swiftmailer has already been initialized + if (class_exists('Swift_Mailer', false)) { + $mailers = $this->container->getParameter('swiftmailer.mailers'); + foreach ($mailers as $name => $mailer) { + if ($this->container->getParameter('swiftmailer.default_mailer') == $name) { + $this->data['defaultMailer'] = $name; + } + $loggerName = sprintf('swiftmailer.mailer.%s.plugin.messagelogger', $name); + if ($this->container->has($loggerName)) { + $logger = $this->container->get($loggerName); + $this->data['mailer'][$name] = array( + 'messages' => $logger->getMessages(), + 'messageCount' => $logger->countMessages(), + 'isSpool' => $this->container->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name)), + ); + $this->data['messageCount'] += $logger->countMessages(); + } + } + } + } + + /** + * Returns the mailer names. + * + * @return array The mailer names. + */ + public function getMailers() + { + return array_keys($this->data['mailer']); + } + + /** + * Returns the data collected of a mailer. + * + * @return array The data of the mailer. + */ + + public function getMailerData($name) + { + if (!isset($this->data['mailer'][$name])) { + throw new \LogicException(sprintf("Missing %s data in %s", $name, get_class())); + } + + return $this->data['mailer'][$name]; + } + + /** + * Returns the message count of a mailer or the total. + * + * @return int The number of messages. + */ + public function getMessageCount($name = null) + { + if (is_null($name)) { + return $this->data['messageCount']; + } elseif ($data = $this->getMailerData($name)) { + return $data['messageCount']; + } + + return null; + } + + /** + * Returns the messages of a mailer. + * + * @return array The messages. + */ + public function getMessages($name = 'default') + { + if ($data = $this->getMailerData($name)) { + return $data['messages']; + } + + return array(); + } + + /** + * Returns if the mailer has spool. + * + * @return boolean + */ + public function isSpool($name) + { + if ($data = $this->getMailerData($name)) { + return $data['isSpool']; + } + + return null; + } + + /** + * Returns if the mailer is the default mailer. + * + * @return boolean + */ + public function isDefaultMailer($name) + { + return $this->data['defaultMailer'] == $name; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'swiftmailer'; + } +} diff --git a/vendor/symfony/swiftmailer-bundle/DependencyInjection/Compiler/RegisterPluginsPass.php b/vendor/symfony/swiftmailer-bundle/DependencyInjection/Compiler/RegisterPluginsPass.php new file mode 100644 index 0000000..7692348 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/DependencyInjection/Compiler/RegisterPluginsPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * RegisterPluginsPass registers Swiftmailer plugins. + * + * @author Fabien Potencier + */ +class RegisterPluginsPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->findDefinition('swiftmailer.mailer') || !$container->getParameter('swiftmailer.mailers')) { + return; + } + + $mailers = $container->getParameter('swiftmailer.mailers'); + foreach ($mailers as $name => $mailer) { + $plugins = $container->findTaggedServiceIds(sprintf('swiftmailer.%s.plugin', $name)); + $transport = sprintf('swiftmailer.mailer.%s.transport', $name); + $definition = $container->findDefinition($transport); + foreach ($plugins as $id => $args) { + $definition->addMethodCall('registerPlugin', array(new Reference($id))); + } + } + } +} diff --git a/vendor/symfony/swiftmailer-bundle/DependencyInjection/Configuration.php b/vendor/symfony/swiftmailer-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..5343ca5 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This class contains the configuration information for the bundle + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * @author Christophe Coevoet + */ +class Configuration implements ConfigurationInterface +{ + private $debug; + + /** + * Constructor. + * + * @param Boolean $debug The kernel.debug value + */ + public function __construct($debug) + { + $this->debug = (Boolean) $debug; + } + + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('swiftmailer'); + + $rootNode + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && !array_key_exists('mailers', $v) && !array_key_exists('mailer', $v); }) + ->then(function ($v) { + $mailer = array(); + foreach ($v as $key => $value) { + if ('default_mailer' == $key) { + continue; + } + $mailer[$key] = $v[$key]; + unset($v[$key]); + } + $v['default_mailer'] = isset($v['default_mailer']) ? (string) $v['default_mailer'] : 'default'; + $v['mailers'] = array($v['default_mailer'] => $mailer); + + return $v; + }) + ->end() + ->children() + ->scalarNode('default_mailer')->end() + ->append($this->getMailersNode()) + ->end() + ->fixXmlConfig('mailer') + ; + + return $treeBuilder; + } + + /** + * Return the mailers node + * + * @return ArrayNodeDefinition + */ + private function getMailersNode() + { + $treeBuilder = new TreeBuilder(); + $node = $treeBuilder->root('mailers'); + + $node + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('transport')->defaultValue('smtp')->end() + ->scalarNode('username')->defaultNull()->end() + ->scalarNode('password')->defaultNull()->end() + ->scalarNode('host')->defaultValue('localhost')->end() + ->scalarNode('port')->defaultNull()->end() + ->scalarNode('timeout')->defaultValue(30)->end() + ->scalarNode('source_ip')->defaultNull()->end() + ->scalarNode('encryption') + ->defaultNull() + ->validate() + ->ifNotInArray(array('tls', 'ssl', null)) + ->thenInvalid('The %s encryption is not supported') + ->end() + ->end() + ->scalarNode('auth_mode') + ->defaultNull() + ->validate() + ->ifNotInArray(array('plain', 'login', 'cram-md5', null)) + ->thenInvalid('The %s authentication mode is not supported') + ->end() + ->end() + ->scalarNode('sender_address')->end() + ->scalarNode('delivery_address')->end() + ->arrayNode('antiflood') + ->children() + ->scalarNode('threshold')->defaultValue(99)->end() + ->scalarNode('sleep')->defaultValue(0)->end() + ->end() + ->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->arrayNode('spool') + ->children() + ->scalarNode('type')->defaultValue('file')->end() + ->scalarNode('path')->defaultValue('%kernel.cache_dir%/swiftmailer/spool')->end() + ->scalarNode('id')->defaultNull()->info('Used by "service" type')->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return 'service' === $v['type'] && empty($v['id']); }) + ->thenInvalid('You have to configure the service id') + ->end() + ->end() + ->end() + ->fixXmlConfig('delivery_whitelist_pattern', 'delivery_whitelist') + ->children() + ->arrayNode('delivery_whitelist') + ->prototype('scalar') + ->end() + ->end() + ->booleanNode('disable_delivery')->end() + ->end() + ; + + return $node; + } +} diff --git a/vendor/symfony/swiftmailer-bundle/DependencyInjection/SwiftmailerExtension.php b/vendor/symfony/swiftmailer-bundle/DependencyInjection/SwiftmailerExtension.php new file mode 100644 index 0000000..02aa1d8 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/DependencyInjection/SwiftmailerExtension.php @@ -0,0 +1,337 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +/** + * SwiftmailerExtension is an extension for the SwiftMailer library. + * + * @author Fabien Potencier + */ +class SwiftmailerExtension extends Extension +{ + /** + * Loads the Swift Mailer configuration. + * + * Usage example: + * + * + * fabien + * xxxxx + * + * + * + * @param array $configs An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('swiftmailer.xml'); + + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $mailers = array(); + foreach ($config['mailers'] as $name => $mailer) { + $isDefaultMailer = $config['default_mailer'] === $name; + $this->configureMailer($name, $mailer, $container, $isDefaultMailer); + $mailers[$name] = sprintf('swiftmailer.mailer.%s', $name); + } + ksort($mailers); + $container->setParameter('swiftmailer.mailers', $mailers); + $container->setParameter('swiftmailer.default_mailer', $config['default_mailer']); + + $container->findDefinition('swiftmailer.data_collector')->addTag('data_collector', array('template' => '@Swiftmailer/Collector/swiftmailer.html.twig', 'id' => 'swiftmailer', 'priority' => 245)); + + $container->setAlias('mailer', 'swiftmailer.mailer'); + } + + protected function configureMailer($name, array $mailer, ContainerBuilder $container, $isDefaultMailer = false) + { + if (null === $mailer['transport']) { + $transport = 'null'; + } elseif ('gmail' === $mailer['transport']) { + $mailer['encryption'] = 'ssl'; + $mailer['auth_mode'] = 'login'; + $mailer['host'] = 'smtp.gmail.com'; + $transport = 'smtp'; + } else { + $transport = $mailer['transport']; + } + + $container->setParameter(sprintf('swiftmailer.mailer.%s.transport.name', $name), $transport); + + if (isset($mailer['disable_delivery']) && $mailer['disable_delivery']) { + $transport = 'null'; + $container->setParameter(sprintf('swiftmailer.mailer.%s.delivery.enabled', $name), false); + } else { + $container->setParameter(sprintf('swiftmailer.mailer.%s.delivery.enabled', $name), true); + } + + if (empty($mailer['port'])) { + $mailer['port'] = 'ssl' === $mailer['encryption'] ? 465 : 25; + } + + $this->configureMailerTransport($name, $mailer, $container, $transport, $isDefaultMailer); + $this->configureMailerSpool($name, $mailer, $container, $transport, $isDefaultMailer); + $this->configureMailerSenderAddress($name, $mailer, $container, $isDefaultMailer); + $this->configureMailerAntiFlood($name, $mailer, $container, $isDefaultMailer); + $this->configureMailerDeliveryAddress($name, $mailer, $container, $isDefaultMailer); + $this->configureMailerLogging($name, $mailer, $container, $isDefaultMailer); + + // alias + if ($isDefaultMailer) { + $container->setAlias('swiftmailer.mailer', sprintf('swiftmailer.mailer.%s', $name)); + $container->setAlias('swiftmailer.transport', sprintf('swiftmailer.mailer.%s.transport', $name)); + $container->setParameter('swiftmailer.spool.enabled', $container->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name))); + $container->setParameter('swiftmailer.delivery.enabled', $container->getParameter(sprintf('swiftmailer.mailer.%s.delivery.enabled', $name))); + $container->setParameter('swiftmailer.single_address', $container->getParameter(sprintf('swiftmailer.mailer.%s.single_address', $name))); + } + } + + protected function configureMailerTransport($name, array $mailer, ContainerBuilder $container, $transport, $isDefaultMailer = false) + { + foreach (array('encryption', 'port', 'host', 'username', 'password', 'auth_mode', 'timeout', 'source_ip') as $key) { + $container->setParameter(sprintf('swiftmailer.mailer.%s.transport.smtp.%s', $name, $key), $mailer[$key]); + } + $definitionDecorator = new DefinitionDecorator('swiftmailer.transport.eventdispatcher.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.eventdispatcher', $name), $definitionDecorator) + ; + if ('smtp' === $transport) { + $authDecorator = new DefinitionDecorator('swiftmailer.transport.authhandler.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.authhandler', $name), $authDecorator) + ->addMethodCall('setUsername', array('%swiftmailer.mailer.' . $name . '.transport.smtp.username%')) + ->addMethodCall('setPassword', array('%swiftmailer.mailer.' . $name . '.transport.smtp.password%')) + ->addMethodCall('setAuthMode', array('%swiftmailer.mailer.' . $name . '.transport.smtp.auth_mode%')); + + $bufferDecorator = new DefinitionDecorator('swiftmailer.transport.buffer.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.buffer', $name), $bufferDecorator); + + $definitionDecorator = new DefinitionDecorator('swiftmailer.transport.smtp.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.smtp', $name), $definitionDecorator) + ->setArguments(array( + new Reference(sprintf('swiftmailer.mailer.%s.transport.buffer', $name)), + array(new Reference(sprintf('swiftmailer.mailer.%s.transport.authhandler', $name))), + new Reference(sprintf('swiftmailer.mailer.%s.transport.eventdispatcher', $name)), + )) + ->addMethodCall('setHost', array('%swiftmailer.mailer.' . $name . '.transport.smtp.host%')) + ->addMethodCall('setPort', array('%swiftmailer.mailer.' . $name . '.transport.smtp.port%')) + ->addMethodCall('setEncryption', array('%swiftmailer.mailer.' . $name . '.transport.smtp.encryption%')) + ->addMethodCall('setTimeout', array('%swiftmailer.mailer.' . $name . '.transport.smtp.timeout%')) + ->addMethodCall('setSourceIp', array('%swiftmailer.mailer.' . $name . '.transport.smtp.source_ip%')) + ; + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport', $name), sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport)); + } elseif ('sendmail' === $transport) { + $bufferDecorator = new DefinitionDecorator('swiftmailer.transport.buffer.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.buffer', $name), $bufferDecorator); + + $definitionDecorator = new DefinitionDecorator(sprintf('swiftmailer.transport.%s.abstract', $transport)); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport), $definitionDecorator) + ->setArguments(array( + new Reference(sprintf('swiftmailer.mailer.%s.transport.buffer', $name)), + new Reference(sprintf('swiftmailer.mailer.%s.transport.eventdispatcher', $name)) + )) + ; + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport', $name), sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport)); + } elseif ('mail' === $transport) { + $definitionDecorator = new DefinitionDecorator(sprintf('swiftmailer.transport.%s.abstract', $transport)); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport), $definitionDecorator) + ->addArgument(new Reference(sprintf('swiftmailer.mailer.%s.transport.eventdispatcher', $name))) + ; + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport', $name), sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport)); + } elseif ('null' === $transport) { + $definitionDecorator = new DefinitionDecorator('swiftmailer.transport.null.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.null', $name, $transport), $definitionDecorator) + ->setArguments(array( + new Reference(sprintf('swiftmailer.mailer.%s.transport.eventdispatcher', $name)), + )) + ; + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport', $name), sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport)); + } else { + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport', $name), sprintf('swiftmailer.mailer.transport.%s', $transport)); + } + + $definitionDecorator = new DefinitionDecorator('swiftmailer.mailer.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s', $name), $definitionDecorator) + ->replaceArgument(0, new Reference(sprintf('swiftmailer.mailer.%s.transport', $name))) + ; + } + + protected function configureMailerSpool($name, array $mailer, ContainerBuilder $container, $transport, $isDefaultMailer = false) + { + if (isset($mailer['spool'])) { + $type = $mailer['spool']['type']; + if ('service' === $type) { + $container->setAlias(sprintf('swiftmailer.mailer.%s.spool.service', $name), $mailer['spool']['id']); + } else { + foreach (array('path') as $key) { + $container->setParameter(sprintf('swiftmailer.spool.%s.%s.%s', $name, $type, $key), $mailer['spool'][$key] . '/' . $name); + } + } + + $definitionDecorator = new DefinitionDecorator(sprintf('swiftmailer.spool.%s.abstract', $type)); + if ('file' === $type) { + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.spool.file', $name), $definitionDecorator) + ->replaceArgument(0, sprintf('%%swiftmailer.spool.%s.file.path%%', $name)) + ; + } elseif ('memory' === $type) { + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.spool.memory', $name), $definitionDecorator) + ; + } + $container->setAlias(sprintf('swiftmailer.mailer.%s.spool', $name), sprintf('swiftmailer.mailer.%s.spool.%s', $name, $type)); + + $definitionDecorator = new DefinitionDecorator('swiftmailer.transport.spool.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.transport.spool', $name), $definitionDecorator) + ->setArguments(array( + new Reference(sprintf('swiftmailer.mailer.%s.transport.eventdispatcher', $name)), + new Reference(sprintf('swiftmailer.mailer.%s.spool', $name)), + )) + ; + + if (in_array($transport, array('smtp', 'mail', 'sendmail', 'null'))) { + // built-in transport + $transport = sprintf('swiftmailer.mailer.%s.transport.%s', $name, $transport); + } + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport.real', $name), $transport); + $container->setAlias(sprintf('swiftmailer.mailer.%s.transport', $name), sprintf('swiftmailer.mailer.%s.transport.spool', $name)); + $container->setParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name), true); + if (true === $isDefaultMailer) { + $container->setAlias('swiftmailer.spool', sprintf('swiftmailer.mailer.%s.spool', $name)); + $container->setAlias('swiftmailer.transport.real', sprintf('swiftmailer.mailer.%s.transport.real', $name)); + } + } else { + $container->setParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name), false); + } + } + + protected function configureMailerSenderAddress($name, array $mailer, ContainerBuilder $container, $isDefaultMailer = false) + { + if (isset($mailer['sender_address']) && $mailer['sender_address']) { + $container->setParameter(sprintf('swiftmailer.mailer.%s.sender_address', $name), $mailer['sender_address']); + $definitionDecorator = new DefinitionDecorator('swiftmailer.plugin.impersonate.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.plugin.impersonate', $name), $definitionDecorator) + ->setArguments(array( + sprintf('%%swiftmailer.mailer.%s.sender_address%%', $name), + )) + ; + $container->getDefinition(sprintf('swiftmailer.mailer.%s.plugin.impersonate', $name))->addTag(sprintf('swiftmailer.%s.plugin', $name)); + if (true === $isDefaultMailer) { + $container->setAlias('swiftmailer.plugin.impersonate', sprintf('swiftmailer.mailer.%s.plugin.impersonate', $name)); + $container->setParameter('swiftmailer.sender_address', $container->getParameter(sprintf('swiftmailer.mailer.%s.sender_address', $name))); + } + } else { + $container->setParameter(sprintf('swiftmailer.mailer.%s.plugin.impersonate', $name), null); + } + } + + protected function configureMailerAntiFlood($name, array $mailer, ContainerBuilder $container, $isDefaultMailer = false) + { + if (isset($mailer['antiflood'])) { + $container->setParameter(sprintf('swiftmailer.mailer.%s.antiflood.threshold', $name), $mailer['antiflood']['threshold']); + $container->setParameter(sprintf('swiftmailer.mailer.%s.antiflood.sleep', $name), $mailer['antiflood']['sleep']); + $definitionDecorator = new DefinitionDecorator('swiftmailer.plugin.antiflood.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.plugin.antiflood', $name), $definitionDecorator) + ->setArguments(array( + sprintf('%%swiftmailer.mailer.%s.antiflood.threshold%%', $name), + sprintf('%%swiftmailer.mailer.%s.antiflood.sleep%%', $name), + )) + ; + $container->getDefinition(sprintf('swiftmailer.mailer.%s.plugin.antiflood', $name))->addTag(sprintf('swiftmailer.%s.plugin', $name)); + if (true === $isDefaultMailer) { + $container->setAlias('swiftmailer.mailer.plugin.antiflood', sprintf('swiftmailer.mailer.%s.plugin.antiflood', $name)); + } + } + } + + protected function configureMailerDeliveryAddress($name, array $mailer, ContainerBuilder $container, $isDefaultMailer = false) + { + if (isset($mailer['delivery_address']) && $mailer['delivery_address']) { + $container->setParameter(sprintf('swiftmailer.mailer.%s.single_address', $name), $mailer['delivery_address']); + $container->setParameter(sprintf('swiftmailer.mailer.%s.delivery_whitelist', $name), $mailer['delivery_whitelist']); + $definitionDecorator = new DefinitionDecorator('swiftmailer.plugin.redirecting.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.plugin.redirecting', $name), $definitionDecorator) + ->setArguments(array( + sprintf('%%swiftmailer.mailer.%s.single_address%%', $name), + sprintf('%%swiftmailer.mailer.%s.delivery_whitelist%%', $name), + )) + ; + $container->getDefinition(sprintf('swiftmailer.mailer.%s.plugin.redirecting', $name))->addTag(sprintf('swiftmailer.%s.plugin', $name)); + if (true === $isDefaultMailer) { + $container->setAlias('swiftmailer.plugin.redirecting', sprintf('swiftmailer.mailer.%s.plugin.redirecting', $name)); + } + } else { + $container->setParameter(sprintf('swiftmailer.mailer.%s.single_address', $name), null); + } + } + + protected function configureMailerLogging($name, array $mailer, ContainerBuilder $container, $isDefaultMailer = false) + { + if ($mailer['logging']) { + $container->getDefinition('swiftmailer.plugin.messagelogger.abstract'); + $definitionDecorator = new DefinitionDecorator('swiftmailer.plugin.messagelogger.abstract'); + $container + ->setDefinition(sprintf('swiftmailer.mailer.%s.plugin.messagelogger', $name), $definitionDecorator) + ; + $container->getDefinition(sprintf('swiftmailer.mailer.%s.plugin.messagelogger', $name))->addTag(sprintf('swiftmailer.%s.plugin', $name)); + if (true === $isDefaultMailer) { + $container->setAlias('swiftmailer.plugin.messagelogger', sprintf('swiftmailer.mailer.%s.plugin.messagelogger', $name)); + } + } + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + */ + public function getNamespace() + { + return 'http://symfony.com/schema/dic/swiftmailer'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($container->getParameter('kernel.debug')); + } +} diff --git a/vendor/symfony/swiftmailer-bundle/EventListener/EmailSenderListener.php b/vendor/symfony/swiftmailer-bundle/EventListener/EmailSenderListener.php new file mode 100644 index 0000000..8ad33f6 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/EventListener/EmailSenderListener.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\IntrospectableContainerInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Sends emails for the memory spool. + * + * Emails are sent on the kernel.terminate event. + * + * @author Fabien Potencier + */ +class EmailSenderListener implements EventSubscriberInterface +{ + private $container; + + private $logger; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(ContainerInterface $container, LoggerInterface $logger = null) + { + $this->container = $container; + $this->logger = $logger; + } + + public function onTerminate() + { + if (!$this->container->has('mailer')) { + return; + } + $mailers = array_keys($this->container->getParameter('swiftmailer.mailers')); + foreach ($mailers as $name) { + if ($this->container instanceof IntrospectableContainerInterface ? $this->container->initialized(sprintf('swiftmailer.mailer.%s', $name)) : true) { + if ($this->container->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name))) { + $mailer = $this->container->get(sprintf('swiftmailer.mailer.%s', $name)); + $transport = $mailer->getTransport(); + if ($transport instanceof \Swift_Transport_SpoolTransport) { + $spool = $transport->getSpool(); + if ($spool instanceof \Swift_MemorySpool) { + try { + $spool->flushQueue($this->container->get(sprintf('swiftmailer.mailer.%s.transport.real', $name))); + } catch (\Swift_TransportException $exception) { + if (null !== $this->logger) { + $this->logger->error(sprintf('Exception occurred while flushing email queue: %s', $exception->getMessage())); + } + } + } + } + } + } + } + } + + public static function getSubscribedEvents() + { + $listeners = array(KernelEvents::TERMINATE => 'onTerminate'); + + if (class_exists('Symfony\Component\Console\ConsoleEvents')) { + $listeners[ConsoleEvents::TERMINATE] = 'onTerminate'; + } + + return $listeners; + } +} diff --git a/vendor/symfony/swiftmailer-bundle/Resources/config/schema/swiftmailer-1.0.xsd b/vendor/symfony/swiftmailer-bundle/Resources/config/schema/swiftmailer-1.0.xsd new file mode 100644 index 0000000..86eae69 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Resources/config/schema/swiftmailer-1.0.xsd @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Resources/config/swiftmailer.xml b/vendor/symfony/swiftmailer-bundle/Resources/config/swiftmailer.xml new file mode 100644 index 0000000..cd3cb4e --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Resources/config/swiftmailer.xml @@ -0,0 +1,95 @@ + + + + + + Swift_Mailer + + Swift_Transport_SendmailTransport + Swift_Transport_MailTransport + + Swift_Transport_FailoverTransport + + Swift_Plugins_RedirectingPlugin + Swift_Plugins_ImpersonatePlugin + Swift_Plugins_MessageLogger + Swift_Plugins_AntiFloodPlugin + + Swift_Transport_EsmtpTransport + + Swift_Plugins_BlackholePlugin + + Swift_FileSpool + + Swift_MemorySpool + Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener + + Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.root_dir%/../data/swiftmailer/spool + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/swiftmailer-bundle/Resources/meta/LICENSE b/vendor/symfony/swiftmailer-bundle/Resources/meta/LICENSE new file mode 100644 index 0000000..0b3292c --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/swiftmailer-bundle/Resources/views/Collector/icon.svg b/vendor/symfony/swiftmailer-bundle/Resources/views/Collector/icon.svg new file mode 100644 index 0000000..2618a67 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Resources/views/Collector/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/swiftmailer-bundle/Resources/views/Collector/swiftmailer.html.twig b/vendor/symfony/swiftmailer-bundle/Resources/views/Collector/swiftmailer.html.twig new file mode 100644 index 0000000..405101e --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/Resources/views/Collector/swiftmailer.html.twig @@ -0,0 +1,182 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set profiler_markup_version = profiler_markup_version|default(1) %} + + {% if collector.messageCount %} + {% set icon %} + {% if profiler_markup_version == 1 %} + Swiftmailer + {{ collector.messageCount }} + {% else %} + {{ include('@Swiftmailer/Collector/icon.svg') }} + {{ collector.messageCount }} + {% endif %} + {% endset %} + + {% set text %} +
    + Sent messages + {{ collector.messageCount }} +
    + + {% if profiler_markup_version == 1 %} + {% for name in collector.mailers %} +
    + {{ name }} + {{ collector.messageCount(name) }} +
    +
    + Is spooled? + {{ collector.isSpool(name) ? 'yes' : 'no' }} +
    + + {% if not loop.first %} +
    + {% endif %} + {% endfor %} + {% else %} + {% for name in collector.mailers %} +
    + {{ name }} mailer + {{ collector.messageCount(name)|default(0) }} +   ({{ collector.isSpool(name) ? 'spooled' : 'sent' }}) +
    + {% endfor %} + {% endif %} + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': profiler_url }) }} + {% endif %} +{% endblock %} + +{% block menu %} + {% set profiler_markup_version = profiler_markup_version|default(1) %} + + + {% if profiler_markup_version == 1 %} + Swiftmailer + {% else %} + {{ include('@Swiftmailer/Collector/icon.svg') }} + {% endif %} + + E-Mails + {% if collector.messageCount > 0 %} + + {{ collector.messageCount }} + + {% endif %} + +{% endblock %} + +{% block panel %} + {% set profiler_markup_version = profiler_markup_version|default(1) %} + + {% if profiler_markup_version == 1 %} + + {% endif %} + +

    E-mails

    + + {% if not collector.mailers %} +
    +

    No e-mail messages were sent.

    +
    + {% endif %} + + {% if profiler_markup_version == 1 or collector.mailers|length > 1 %} + + + + + + + + + + + {% for name in collector.mailers %} + + + + + + + {% endfor %} + +
    Mailer NameNum. of messagesMessages statusNotes
    {{ name }}{{ collector.messageCount(name) }}{{ collector.isSpool(name) ? 'spooled' : 'sent' }}{{ collector.isDefaultMailer(name) ? 'This is the default mailer' }}
    + {% else %} +
    + {% for name in collector.mailers %} +
    + {{ collector.messageCount(name) }} + {{ collector.isSpool(name) ? 'spooled' : 'sent' }} {{ collector.messageCount(name) == 1 ? 'message' : 'messages' }} +
    + {% endfor %} +
    + {% endif %} + + {% for name in collector.mailers %} + {% if collector.mailers|length > 1 %} +

    + {{ name }} mailer + {{ collector.isDefaultMailer(name) ? '(default app mailer)' }} +

    + {% endif %} + + {% if not collector.messages(name) %} +
    +

    No e-mail messages were sent.

    +
    + {% else %} + {% for message in collector.messages(name) %} + {% if loop.length > 1 %} +

    E-mail #{{ loop.index }} details

    + {% else %} +

    E-mail details

    + {% endif %} + +
    +
    + Message headers +
    {% for header in message.headers.all %}
    +                            {{- header -}}
    +                        {% endfor %}
    +
    + +
    + Message body +
    +                            {%- if messagePart.charset is defined and message.charset %}
    +                                {{- message.body|convert_encoding('UTF-8', message.charset) }}
    +                            {%- else %}
    +                                {{- message.body }}
    +                            {%- endif -%}
    +                        
    +
    + + {% for messagePart in message.children if messagePart.contentType in ['text/plain', 'text/html'] %} +
    + Alternative part ({{ messagePart.contentType }}) +
    +                                {%- if messagePart.charset %}
    +                                    {{- messagePart.body|convert_encoding('UTF-8', messagePart.charset) }}
    +                                {%- else %}
    +                                    {{- messagePart.body }}
    +                                {%- endif -%}
    +                            
    +
    + {% endfor %} +
    + {% endfor %} + {% endif %} + {% endfor %} +{% endblock %} diff --git a/vendor/symfony/swiftmailer-bundle/SwiftmailerBundle.php b/vendor/symfony/swiftmailer-bundle/SwiftmailerBundle.php new file mode 100644 index 0000000..8456bbf --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/SwiftmailerBundle.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SwiftmailerBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Bundle\SwiftmailerBundle\DependencyInjection\Compiler\RegisterPluginsPass; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class SwiftmailerBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new RegisterPluginsPass()); + } +} diff --git a/vendor/symfony/swiftmailer-bundle/composer.json b/vendor/symfony/swiftmailer-bundle/composer.json new file mode 100644 index 0000000..fa16d88 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/swiftmailer-bundle", + "type": "symfony-bundle", + "description": "Symfony SwiftmailerBundle", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2", + "swiftmailer/swiftmailer": ">=4.2.0,~5.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/config": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7|~3.0" + }, + "suggest": { + "psr/log": "Allows logging" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\SwiftmailerBundle\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + } +} diff --git a/vendor/symfony/swiftmailer-bundle/phpunit.xml.dist b/vendor/symfony/swiftmailer-bundle/phpunit.xml.dist new file mode 100644 index 0000000..67d0dc9 --- /dev/null +++ b/vendor/symfony/swiftmailer-bundle/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./Tests + + + + + + . + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/.editorconfig b/vendor/symfony/symfony/.editorconfig new file mode 100644 index 0000000..153cf3e --- /dev/null +++ b/vendor/symfony/symfony/.editorconfig @@ -0,0 +1,10 @@ +; top-most EditorConfig file +root = true + +; Unix-style newlines +[*] +end_of_line = LF + +[*.php] +indent_style = space +indent_size = 4 diff --git a/vendor/symfony/symfony/.php_cs b/vendor/symfony/symfony/.php_cs new file mode 100644 index 0000000..11d42d1 --- /dev/null +++ b/vendor/symfony/symfony/.php_cs @@ -0,0 +1,28 @@ +setUsingLinter(false) + ->setUsingCache(true) + ->finder( + Symfony\CS\Finder\DefaultFinder::create() + ->in(__DIR__) + ->exclude(array( + // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code + 'src/Symfony/Component/DependencyInjection/Tests/Fixtures', + 'src/Symfony/Component/Routing/Tests/Fixtures/dumper', + // fixture templates + 'src/Symfony/Component/Templating/Tests/Fixtures/templates', + // resource templates + 'src/Symfony/Bundle/FrameworkBundle/Resources/views/Form', + )) + // file content autogenerated by `var_export` + ->notPath('src/Symfony/Component/Translation/Tests/fixtures/resources.php') + // autogenerated xmls + ->notPath('src/Symfony/Component/Console/Tests/Fixtures/application_1.xml') + ->notPath('src/Symfony/Component/Console/Tests/Fixtures/application_2.xml') + // yml + ->notPath('src/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml') + // test template + ->notPath('src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php') + ) +; diff --git a/vendor/symfony/symfony/.travis.php b/vendor/symfony/symfony/.travis.php new file mode 100644 index 0000000..fdf223b --- /dev/null +++ b/vendor/symfony/symfony/.travis.php @@ -0,0 +1,50 @@ + $_SERVER['argc']) { + echo "Usage: commit-range branch dir1 dir2 ... dirN\n"; + exit(1); +} + +$dirs = $_SERVER['argv']; +array_shift($dirs); +$range = array_shift($dirs); +$branch = array_shift($dirs); + +$packages = array(); +$flags = PHP_VERSION_ID >= 50400 ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0; + +foreach ($dirs as $dir) { + if (!`git diff --name-only $range -- $dir`) { + continue; + } + echo "$dir\n"; + + $json = ltrim(file_get_contents($dir.'/composer.json')); + if (null === $package = json_decode($json)) { + passthru("composer validate $dir/composer.json"); + exit(1); + } + + $package->repositories = array(array( + 'type' => 'composer', + 'url' => 'file://'.__DIR__.'/', + )); + $json = rtrim(json_encode(array('repositories' => $package->repositories), $flags), "\n}").','.substr($json, 1); + file_put_contents($dir.'/composer.json', $json); + passthru("cd $dir && tar -cf package.tar --exclude='package.tar' *"); + + $package->version = 'master' !== $branch ? $branch.'.x-dev' : 'dev-master'; + $package->dist['type'] = 'tar'; + $package->dist['url'] = 'file://'.__DIR__."/$dir/package.tar"; + + $packages[$package->name][$package->version] = $package; + + $versions = file_get_contents('https://packagist.org/packages/'.$package->name.'.json'); + $versions = json_decode($versions); + + foreach ($versions->package->versions as $version => $package) { + $packages[$package->name] += array($version => $package); + } +} + +file_put_contents('packages.json', json_encode(compact('packages'), $flags)); diff --git a/vendor/symfony/symfony/.travis.yml b/vendor/symfony/symfony/.travis.yml new file mode 100644 index 0000000..ac722f4 --- /dev/null +++ b/vendor/symfony/symfony/.travis.yml @@ -0,0 +1,64 @@ +language: php + +sudo: false + +addons: + apt_packages: + - parallel + - language-pack-fr-base + +env: + global: + - MIN_PHP=5.5.9 + +matrix: + include: + - php: hhvm + - php: 5.5 + - php: 5.6 + env: deps=high + - php: 7.0 + env: deps=low + fast_finish: true + +cache: + directories: + - .phpunit + - php-$MIN_PHP + +services: mongodb + +before_install: + - if [[ ! $deps && ! $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} && $TRAVIS_PHP_VERSION != hhvm && $TRAVIS_PULL_REQUEST != false ]]; then deps=skip; fi; + - if [[ ! $deps && ! -d php-$MIN_PHP/sapi ]]; then wget http://museum.php.net/php5/php-$MIN_PHP.tar.bz2 -O - | tar -xj; (cd php-$MIN_PHP; ./configure --enable-sigchild --enable-pcntl; make -j2); fi; + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi; + - echo memory_limit = -1 >> $INI_FILE + - echo session.gc_probability = 0 >> $INI_FILE + - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi; + - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi; + - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE) || echo "Let's continue without apcu extension"; fi; + - if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then pecl install -f memcached-2.1.0 || echo "Let's continue without memcached extension"; fi; + - if [[ $TRAVIS_PHP_VERSION = 5.* && ! $deps ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo extension = $(pwd)/modules/symfony_debug.so >> $INI_FILE); fi; + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo extension = ldap.so >> $INI_FILE; fi; + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi; + - if [[ $deps != skip ]]; then composer self-update; fi; + - if [[ $deps != skip ]]; then ./phpunit install; fi; + - export PHPUNIT=$(readlink -f ./phpunit) + +install: + - if [[ $deps != skip ]]; then COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n'); fi; + - if [[ $deps != skip && $deps ]]; then php .travis.php $TRAVIS_COMMIT_RANGE $TRAVIS_BRANCH $COMPONENTS; fi; + - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then SYMFONY_VERSION=$(git ls-remote --heads | grep -o '/[1-9].*' | tail -n 1 | sed s/.//); else SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*'); fi; + - if [[ $deps = high && $TRAVIS_BRANCH = master ]]; then git fetch origin $SYMFONY_VERSION; git checkout -m FETCH_HEAD; fi; + - if [[ $deps = high && ${SYMFONY_VERSION%.*} != $(git show $(git ls-remote --heads | grep -FA1 /$SYMFONY_VERSION | tail -n 1):composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9]*' | head -n 1) ]]; then LEGACY=,legacy; fi; + - export COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev; + - if [[ ! $deps ]]; then composer --prefer-source install; else export SYMFONY_DEPRECATIONS_HELPER=weak; fi; + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then php -i; else hhvm --php -r 'print_r($_SERVER);print_r(ini_get_all());'; fi; + +script: + - if [[ ! $deps ]]; then echo "$COMPONENTS" | parallel --gnu '$PHPUNIT --exclude-group tty,benchmark,intl-data {}'; fi; + - if [[ ! $deps ]]; then echo -e "\\nRunning tests requiring tty"; $PHPUNIT --group tty; fi; + - if [[ ! $deps && $TRAVIS_PHP_VERSION = ${MIN_PHP%.*} ]]; then echo -e "1\\n0" | parallel --gnu 'echo -e "\\nPHP --enable-sigchild enhanced={}" && ENHANCE_SIGCHLD={} php-$MIN_PHP/sapi/cli/php .phpunit/phpunit-4.8/phpunit --colors=always src/Symfony/Component/Process/'; fi; + - if [[ $deps = high ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer --prefer-source update; $PHPUNIT --exclude-group tty,benchmark,intl-data'$LEGACY; fi; + - if [[ $deps = low ]]; then echo "$COMPONENTS" | parallel --gnu -j10% 'cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; $PHPUNIT --exclude-group tty,benchmark,intl-data'; fi; + - if [[ $deps = skip ]]; then echo This matrix line is skipped for pull requests.; fi; diff --git a/vendor/symfony/symfony/CHANGELOG-3.0.md b/vendor/symfony/symfony/CHANGELOG-3.0.md new file mode 100644 index 0000000..da9b973 --- /dev/null +++ b/vendor/symfony/symfony/CHANGELOG-3.0.md @@ -0,0 +1,220 @@ +CHANGELOG for 3.0.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 3.0 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/v3.0.0...v3.0.1 + +* 3.0.1 (2015-12-26) + + * bug #16864 [Yaml] fix indented line handling in folded blocks (xabbuh) + * bug #17052 Fixed flatten exception recursion with errors (GrahamCampbell) + * bug #16826 Embedded identifier support (mihai-stancu) + * bug #17079 Also transform inline mappings to objects (WouterJ) + * bug #17129 [Config] Fix array sort on normalization in edge case (romainneutron) + * feature #17035 [DomCrawler] Revert previous restriction, allow selection of every DOMNode object (EdgarPE) + * bug #17094 [Process] More robustness and deterministic tests (nicolas-grekas) + * bug #17112 [PropertyAccess] Reorder elements array after PropertyPathBuilder::replace (alekitto) + * bug #17109 Improved the design of the web debug toolbar (javiereguiluz) + * bug #16797 [Filesystem] Recursivly widen non-executable directories (Slamdunk) + * bug #16926 [DependencyInjection] fixed definition loosing property shared when decorated by a parent definition (wahler) + * bug #17040 [Console] Avoid extra blank lines when rendering exceptions (ogizanagi) + * bug #17044 [Form] fix BC break introduced with prototype_data option (memphys) + * bug #17055 [Security] Verify if a password encoded with bcrypt is no longer than 72 characters (jakzal) + * bug #16959 [Form] fix #15544 when a collection type attribute "required" is false, "prototype" should too (HeahDude) + * bug #16806 [Validator] BicValidator - fixed raising violations to a maximum of one (mvhirsch) + * bug #16842 [Ldap] Escape carriage returns in LDAP DNs. (ChadSikorra) + * bug #16860 [Yaml] do not remove "comments" in scalar blocks (xabbuh) + * bug #17002 [Console][Table] fixed render row that contains multiple cells. (aitboudad) + * bug #16964 CSS min-height and min-width should not be "auto" (aschempp) + * bug #16971 [HttpFoundation] Added the ability of using BinaryFileResponse with stream wrappers (jakzal, Sander-Toonen) + * bug #17048 Fix the logout path when not using the router (stof) + * bug #17049 Fix the logout path when not using the router (stof) + * bug #17057 [FrameworkBundle][HttpKernel] the finder is required to discover bundle commands (xabbuh) + * bug #17059 [HttpFoundation] fix error level for deprecation (xabbuh) + * bug #17006 [Form] Fix casting regression in DoctrineChoiceLoader (bendavies) + * bug #16911 [PropertyInfo] Update List Information from ReflectionExtractor (zanderbaldwin) + * bug #16955 [FrameworkBundle] ContainerDebugCommand: pass the right object to the descriptors (xabbuh) + * bug #16970 [HttpKernel] HttpCache: remove an ESI instance checking (voronkovich) + * feature #16760 Show silenced errors in separate tab (peterrehm) + * feature #16937 [PhpUnitBridge] Replace "weak-verbose" by "deprecations upper bound" mode (nicolas-grekas) + * bug #16953 return ajax collector to collectors.php (NothingWeAre) + * bug #16915 [Process] Enhance compatiblity with --enable-sigchild (nicolas-grekas) + * bug #16829 [FrameworkBundle] prevent cache:clear creating too long paths (Tobion) + * bug #16922 [FrameworkBundle] [Bug] Fixes new InputStyle bug #16920 (AlmogBaku) + * bug #16921 Fix short array syntax for php 5.3 (ewgRa) + * bug #16450 [Serializer] Fixed `array_unique` on array of objects in `getAllowedAttributes`. (CornyPhoenix) + * bug #16757 [FrameworkBundle] [Translation] Fixed translations not written when no translations directory in update command (jeremyFreeAgent) + * bug #16902 [Security] Fix a Polyfill import statement in StringUtils (magnetik) + * bug #16871 [FrameworkBundle] Disable built-in server commands when Process component is missing (gnugat, xabbuh) + * bug #16870 [FrameworkBundle] Disable the server:run command when Process component is missing (gnugat, xabbuh) + * feature #16789 [PhpUnitBridge] Add weak-verbose mode and match against message instead of test name (nicolas-grekas) + * minor #16850 [MonologBridge] Added a test case for the Logger class (derrabus) + * bug #16796 [Form] Fix choices defined as Traversable (nicolas-grekas) + * bug #16742 [Console][ProgressBar] redrawFrequency should never be 0 (dritter) + * bug #16846 [MonologBridge] Monolog Bridge 2.8 is incompatible with HttpKernel 3.0 (derrabus) + * bug #16816 [Config] Throw an exception when using cannotBeEmpty() with numeric or boolean nodes (Ener-Getick) + * bug #16799 Improve error message for undefined DIC aliases (mpdude) + * bug #16825 [VarDumper] fix .sf-dump z-index (debug bar conflict) (Antoine LA) + * bug #16772 Refactoring EntityUserProvider::__construct() to not do work, cause cache warm error (weaverryan) + * bug #16788 Reapply the Yaml bugfix of #16745 (stof) + +* 3.0.0 (2015-11-30) + + * bug #16758 Fix BC for the default root form name (stof) + * feature #16754 [Security] allow arbitrary types in VoterInterface::vote() (xabbuh) + * bug #16753 [Process] Fix signaling/stopping logic on Windows (nicolas-grekas) + * feature #16755 [Security] add subject variable to expression context (xabbuh) + * bug #16642 [DI][autowiring] throw exception when many services use the same class. (aitboudad) + * bug #16745 [Yaml] look for colon in parsed inline string (xabbuh) + * bug #16733 [Console] do not encode backslashes in console default description (Tobion) + * feature #16735 [WIP] [Ldap] Marked the Ldap component as internal (csarrazi) + * bug #16734 Make sure security.role_hierarchy.roles always exists (WouterJ) + * feature #16723 [Form] remove deprecated CSRF options (xabbuh) + * feature #16725 [Form] Removed useless code (webmozart) + * feature #16724 Added getBlockPrefix to FormTypeInterface (WouterJ) + * feature #16722 [Security][SecurityBundle] Use csrf_token_id instead of deprecated intention (jakzal) + * feature #16727 [Form] remove deprecated getTimezones() method (xabbuh) + * bug #16312 [HttpKernel] clearstatcache() so the Cache sees when a .lck file has been released (mpdude) + * bug #16351 [WIP] [Form] [TwigBridge] Bootstrap horizontal theme missing tests (pieter2627) + * feature #16715 [Form] Remove choices_as_values option on ChoiceType (nicolas-grekas) + * feature #16692 [Form] Drop remaing CsrfProviderAdapter/Interface mentions (nicolas-grekas) + * feature #16719 [Security] remove deprecated HTTP digest auth key (xabbuh) + * bug #16685 [Form] Fixed: Duplicate choice labels are remembered when using "choices_as_values" = false (webmozart) + * feature #16709 [Bridge\PhpUnit] Display the stack trace of a deprecation on-demand (nicolas-grekas) + * bug #16704 [Form+SecurityBundle] Trigger deprecation for csrf_provider+intention options (nicolas-grekas) + * feature #16629 [HttpFoundation] Remove deprecated class method parameter (belka-ew) + * feature #16706 [HttpFoundation] Deprecate $deep parameter on ParameterBag (nicolas-grekas) + * bug #16705 [Form] Deprecated setting "choices_as_values" to "false" (webmozart) + * feature #16690 [Form] Deprecated ArrayKeyChoiceList (webmozart) + * feature #16687 [Form] Deprecated TimezoneType::getTimezones() (webmozart) + * bug #16681 [Form] Deprecated setting "choices_as_values" to "false" (webmozart) + * feature #16694 [SecurityBundle] make ACL an optional dependency (Tobion) + * bug #16695 [SecurityBundle] disable the init:acl command if ACL is not used (Tobion) + * bug #16677 [Form] Fixed wrong usages of the "text" type (webmozart) + * bug #16679 [Form] Disabled view data validation if "data_class" is set to null (webmozart) + * bug #16621 [Console] Fix bug with $output overloading (WouterJ) + * feature #16601 [Security] Deprecate "AbstractVoter" in favor of "Voter" (nicolas-grekas, lyrixx) + * bug #16676 [HttpFoundation] Workaround HHVM rewriting HTTP response line (nicolas-grekas) + * bug #16668 [ClassLoader] Fix parsing namespace when token_get_all() is missing (nicolas-grekas) + * bug #16615 fix type assignement (rande) + * bug #16386 Bug #16343 [Router] Too many Routes ? (jelte) + * bug #16498 fix unused variable warning (eventhorizonpl) + * feature #16031 [Translation][Form] Do not translate form labels and placeholders when 'translation_domain' is false (Restless-ET) + * bug #16651 [Debug] Ensure class declarations are loaded only once (nicolas-grekas) + * feature #16637 Removed unneeded polyfill (GrahamCampbell) + * security #16631 n/a (xabbuh) + * security #16630 n/a (xabbuh) + * bug #16633 [Filesystem] Fixed failing test due to tempdir symlink (toretto460) + * bug #16607 [HttpFoundation] Delete not existing session handler proxy member (belka-ew) + * bug #16609 [HttpKernel] Don't reset on shutdown but in FrameworkBundle/Test/KernelTestCase (nicolas-grekas) + * bug #16477 [Routing] Changing RouteCollectionBuilder::import() behavior to add to the builder (weaverryan) + * bug #16588 Sent out a status text for unknown HTTP headers. (dawehner) + * bug #16295 [DependencyInjection] Unescape parameters for all types of injection (Nicofuma) + * bug #16377 [WebProfilerBundle] Fix minitoolbar height (rvanlaak) + * bug #16585 Add support for HTTP status code 418 back (dawehner) + * bug #16574 [Process] Fix PhpProcess with phpdbg runtime (nicolas-grekas) + * bug #16581 Fix call to undefined function json_last_error_message (dawehner) + * bug #16573 [FrameworkBundle] Fix PropertyInfo extractor namespace in framework bundle (jvasseur) + * bug #16578 [Console] Fix bug in windows detection (kbond) + * bug #16546 [Serializer] ObjectNormalizer: don't serialize static methods and props (dunglas) + * bug #16352 Fix the server variables in the router_*.php files (leofeyer) + * bug #16537 [Validator] Allow an empty path with a non empty fragment or a query (jakzal) + * bug #16528 [Translation] Add support for Armenian pluralization. (marcosdsanchez) + * bug #16510 [Process] fix Proccess run with pts enabled (ewgRa) + +* 3.0.0-BETA1 (2015-11-16) + + * feature #16516 Remove some more legacy code (nicolas-grekas) + * feature #11431 [Console] End of options (--) signal support (Seldaek) + * feature #16411 [3.0] use ContainerAwareTrait (blanchonvincent, Tobion) + * feature #16400 [3.0] [Templating] remove deprecated method (TomasVotruba) + * feature #16381 remove polyfills for unsupported php versions (Tobion) + * feature #16392 [3.0] [Security] remove deprecated SrtingUtils class (TomasVotruba) + * feature #16390 [3.0] [Serializer] JsonEncoder: remove deprecated method (TomasVotruba) + * feature #16301 [EventDispatcher] add method getListenerPriority() to interface (xabbuh) + * feature #12119 [Console] Add progress indicator helper (kbond) + * feature #16203 [Yaml] removed YAML parser \ escaping in double-quotes (fabpot) + * feature #16107 [3.0] Various deprecations cleanups (nicolas-grekas) + * feature #16125 Replace is_callable checks with type hints (mpajunen, nicolas-grekas) + * feature #16076 [HttpFoundation] change precedence of parameters in Request::get (Tobion) + * feature #16075 [3.0] Clean Form, Validator, DowCrawler and some more (nicolas-grekas) + * feature #16035 [3.0][Security] Remove deprecated features (follow up of #15899) (Koc) + * feature #8967 [HttpFoundation] Request->getRequestFormat should only rely on the request attributes (pvandommelen) + * feature #16067 [3.0] Remove more deprecated interfaces in Form and Validator (nicolas-grekas) + * feature #16020 [CssSelector] removed the deprecated CssSelector class (fabpot) + * feature #15196 [HttpKernel] make RequestStack parameter required (Tobion) + * feature #16024 [Validator] removed deprecated features in Validator and Form (fabpot) + * feature #15900 [3.0][DoctrineBridge] Removed deprecated features (WouterJ) + * feature #15904 [3.0][FrameworkBundle] Removed deprecated features (WouterJ) + * feature #16019 [HttpFoundation] removed the ParameterBag::get() deep argument (fabpot) + * feature #16018 [HttpKernel] removed deprecated profiler storages (fabpot) + * feature #15899 [3.0][Security] Remove deprecated features (WouterJ) + * feature #15929 [3.0][Config] Remove ResourceInterface::getResource() which was deprecated in 2.8 (mpdude) + * feature #15965 [Finder] removed obsolete code (fabpot) + * feature #15905 [3.0][Config] Removed isFresh() related functionality (WouterJ) + * feature #15936 [Console] remove deprecated shell (Tobion) + * feature #10788 [HttpKernel] Add better error message when controller action isn't callable (pierredup) + * feature #15868 [Finder] Remove deprecated classes (nicolas-grekas) + * feature #15869 [Translation][file dump] remove deprecated format method. (aitboudad) + * feature #15801 [WebProfilerBundle][HttpKernel] removed import/export commands (jakzal) + * feature #15759 [3.0][Translator] remove deprecated DiffOperation class. (aitboudad) + * feature #15684 [Security] Remove deprecated interfaces (nicolas-grekas) + * feature #15685 [3.0] Various deprecation removal (nicolas-grekas) + * feature #15693 [DI] Remove deprecated scope concept (nicolas-grekas) + * feature #15695 [FrameworkBundle] Removed deprecated code paths (nicolas-grekas) + * feature #15430 [SecurityBundle] Remove deprecated code (dosten) + * feature #15422 [Debug] Remove deprecated ExceptionHandler::createResponse (nicolas-grekas) + * feature #15347 [DependencyInjection][3.0] Add initialized to container interface (znerol) + * feature #15219 [DependencyInjection] Added ParameterBagInterface::remove (lyrixx) + * feature #14927 Removed deprecated stuff from the Config component (dosten) + * feature #14693 [3.0][Translator] changed the visibility of the locale from protected to private. (aitboudad) + * feature #14979 [Security] removed obsolete translations (fabpot) + * feature #14928 Removed deprecated stuff from the Console component (dosten) + * feature #14155 Removed deprecations in DependencyInjection component (dosten) + * feature #14145 Removed deprecations in Filesystem component (dosten) + * feature #14153 Remove the legacy PDO session handler (dosten) + * feature #14634 [3.0][HttpKernel] remove deprecated functions and classes (vincentaubert) + * feature #14317 [3.0][FrameworkBundle][lint commands ] remove deprecated alias. (vincentaubert) + * feature #14220 [3.0][Console][OutputStyle] Implements verbosity levels methods (ogizanagi) + * feature #14169 [Debug] Removed deprecated interfaces (nicolas-grekas) + * feature #14147 Removed deprecations in Process component (dosten) + * feature #14150 Removed deprecations in Templating component (dosten) + * feature #14156 Remove deprecated Locale component (stloyd) + * feature #14120 Removed deprecation in translation component (saro0h) + * feature #14118 Removed deprecations in Console component (saro0h) + * feature #14119 Removed deprecation in YAML component (saro0h) + * feature #14091 [3.0] [FrameworkBundle] Drop backward compatibility for debug commands (matthieuauger) + * feature #14070 removed all *.class parameters (fabpot) + * feature #13808 [OptionsResolver] removed deprecated functionality (Tobion) + * feature #13756 [3.0][Console] Added type hint (francisbesset) + * feature #13752 [PropertyAccess] remove deprecations for 3.0 (Tobion) + * feature #13666 removed deprecated asset feature (fabpot) + * feature #13407 [Form] Remove deprecated setDefaultOptions and OptionsResolverInterface (peterrehm) + * feature #13396 [Routing] remove deprecations for 3.0 (Tobion) + * feature #13444 [Serializer] Remove deprecated JSON error methods (dunglas) + * feature #13258 [HttpFoundation] remove deprecated : FlashBag don't implement anymore IteratorAggregate (FlorianLB) + * feature #13086 [Console] Define isVerbose(), etc. methods in OutputInterface (frne) + * feature #13348 [3.0][Monolog] Remove deprecated interface and deprecated methods (rosier) + * feature #13260 [3.0][EventDispatcher][Event] removed deprecated methods (aitboudad) + * feature #13203 [3.0] [ClassLoader] removed deprecated UniversalClassLoader and DebugClassLoader classes. (hhamon) + * feature #13409 removed deprecated Twig features (fabpot) + * feature #13233 [TwigBundle] removed deprecated ActionsExtension (fabpot) + * feature #12663 [FrameworkBundle] remove deprecated method 'createEsi' (FlorianLB) + * feature #13122 [3.0][Form] Removed depracted events PRE_BIND, BIND and POST_BIND (saro0h) + * feature #13216 [3.0] [Config] removed deprecated ReferenceDumper class. (hhamon) + * feature #12457 [FrameworkBundle] REFS #11294 Controller class become abstract (mickaelandrieu) + * feature #12460 [3.0] [FrameworkBundle] removed request service occurrences. (hhamon) + * feature #13121 [Console] Removed DialogHelper, ProgressHelper and TableHelper (saro0h) + * feature #13127 [FrameworkBundle] Removed the deprecated RouterApacheDumperCommand (saro0h) + * feature #13128 [Form] Removed deprecated form_enctype() (saro0h) + * feature #13130 [HttpKernel] Removed deprecated ErrorHandler and ExceptionHandler classes (saro0h) + * feature #13129 [Yaml] Removed the ability to parse a file in Yaml::parse() (saro0h) + * feature #12994 Add LegacyPdoSessionHandler class (jeremylivingston) + * feature #13046 [3.0] [Bridge] [Swiftmailer] removed Swiftmailer bridge namespace. (hhamon) + * feature #12854 [3.0][HttpKernel] Remove unused method Kernel::isClassInActiveBundle (hacfi) + * feature #12697 [3.0][Process] Remove deprecated methods (romainneutron) + * feature #12731 [Monolog Bridge] Remove deprecated log methods + add unit tests (FlorianLB) + * feature #12461 [HttpKernel] Removed deprecated Kernel::init() method (saro0h) diff --git a/vendor/symfony/symfony/CONTRIBUTING.md b/vendor/symfony/symfony/CONTRIBUTING.md new file mode 100644 index 0000000..123d5aa --- /dev/null +++ b/vendor/symfony/symfony/CONTRIBUTING.md @@ -0,0 +1,33 @@ +Contributing +------------ + +Symfony is an open source, community-driven project. + +If you'd like to contribute, please read the following documents: + +* [Contributing Code][1]: The document index related to contributions; + +* [Submitting a Patch][2]: Guidelines for submitting a pull request; + +* [Pull Request Template][3]: Template header to use in your pull request + description; + +```markdown +| Q | A +| ------------- | --- +| Bug fix? | yes/no +| New feature? | yes/no +| BC breaks? | no +| Deprecations? | no +| Tests pass? | yes +| Fixed tickets | #1234 +| License | MIT +| Doc PR | symfony/symfony-docs#1234 +``` + +* [Backwards Compatibility][4]: Backward compatibility rules. + +[1]: https://symfony.com/doc/current/contributing/code/index.html +[2]: https://symfony.com/doc/current/contributing/code/patches.html#check-list +[3]: https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request +[4]: https://symfony.com/doc/current/contributing/code/bc.html#working-on-symfony-code diff --git a/vendor/symfony/symfony/CONTRIBUTORS.md b/vendor/symfony/symfony/CONTRIBUTORS.md new file mode 100644 index 0000000..9b30d35 --- /dev/null +++ b/vendor/symfony/symfony/CONTRIBUTORS.md @@ -0,0 +1,1387 @@ +CONTRIBUTORS +============ + +Symfony is the result of the work of many people who made the code better +(see https://symfony.com/contributors for more information): + + - Fabien Potencier (fabpot) + - Bernhard Schussek (bschussek) + - Nicolas Grekas (nicolas-grekas) + - Tobias Schultze (tobion) + - Victor Berchet (victor) + - Jordi Boggiano (seldaek) + - Christophe Coevoet (stof) + - Johannes S (johannes) + - Kris Wallsmith (kriswallsmith) + - Jakub Zalas (jakubzalas) + - Christian Flothmann (xabbuh) + - Ryan Weaver (weaverryan) + - Pascal Borreli (pborreli) + - Hugo Hamon (hhamon) + - Joseph Bielawski (stloyd) + - Karma Dordrak (drak) + - Lukas Kahwe Smith (lsmith) + - Romain Neutron (romain) + - Abdellatif Ait boudad (aitboudad) + - Jeremy Mikola (jmikola) + - Jean-François Simon (jfsimon) + - Benjamin Eberlei (beberlei) + - Igor Wiedler (igorw) + - Martin Hasoň (hason) + - Wouter De Jong (wouterj) + - Eriksen Costa (eriksencosta) + - Grégoire Pineau (lyrixx) + - Javier Eguiluz (javier.eguiluz) + - Kévin Dunglas (dunglas) + - Jonathan Wage (jwage) + - Alexandre Salomé (alexandresalome) + - William Durand (couac) + - ornicar + - stealth35 ‏ (stealth35) + - Alexander Mols (asm89) + - Bulat Shakirzyanov (avalanche123) + - Francis Besset (francisbesset) + - Saša Stamenković (umpirsky) + - Henrik Bjørnskov (henrikbjorn) + - Miha Vrhovnik + - Diego Saint Esteben (dii3g0) + - Sarah Khalil (saro0h) + - Konstantin Kudryashov (everzet) + - Bilal Amarni (bamarni) + - Florin Patan (florinpatan) + - Eric Clemmons (ericclemmons) + - Andrej Hudec (pulzarraider) + - Maxime Steinhausser (ogizanagi) + - Deni + - Henrik Westphal (snc) + - Dariusz Górecki (canni) + - Gábor Egyed (1ed) + - Christian Raue + - Arnout Boks (aboks) + - Kevin Bond (kbond) + - Michel Weimerskirch (mweimerskirch) + - Douglas Greenshields (shieldo) + - Lee McDermott + - Brandon Turner + - Luis Cordova (cordoval) + - Daniel Holmes (dholmes) + - Bart van den Burg (burgov) + - Jordan Alliot (jalliot) + - John Wards (johnwards) + - Fran Moreno (franmomu) + - Antoine Hérault (herzult) + - Toni Uebernickel (havvg) + - Matthias Pigulla (mpdude) + - Arnaud Le Blanc (arnaud-lb) + - Tim Nagel (merk) + - Brice BERNARD (brikou) + - Jérôme Tamarelle (gromnan) + - marc.weistroff + - lenar + - Graham Campbell (graham) + - Włodzimierz Gajda (gajdaw) + - Florian Voutzinos (florianv) + - Colin Frei + - Adrien Brault (adrienbrault) + - excelwebzone + - Jacob Dreesen (jdreesen) + - Michal Piotrowski (eventhorizon) + - Peter Kokot (maastermedia) + - Fabien Pennequin (fabienpennequin) + - Peter Rehm (rpet) + - Pierre du Plessis (pierredup) + - Alexander Schwenn (xelaris) + - Gordon Franke (gimler) + - Robert Schönthal (digitalkaoz) + - Jérémy DERUSSÉ (jderusse) + - Dariusz Ruminski + - Joshua Thijssen + - Stefano Sala (stefano.sala) + - David Buchmann (dbu) + - Issei Murasawa (issei_m) + - Iltar van der Berg (kjarli) + - Juti Noppornpitak (shiroyuki) + - Eric GELOEN (gelo) + - Sebastian Hörl (blogsh) + - Daniel Gomes (danielcsgomes) + - Hidenori Goto (hidenorigoto) + - Guilherme Blanco (guilhermeblanco) + - Pablo Godel (pgodel) + - Vladimir Reznichenko (kalessil) + - Jérémie Augustin (jaugustin) + - Sebastiaan Stok (sstok) + - Rafael Dohms (rdohms) + - Arnaud Kleinpeter (nanocom) + - Tigran Azatyan (tigranazatyan) + - Richard Shank (iampersistent) + - Clemens Tolboom + - Helmer Aaviksoo + - Baptiste Clavié (talus) + - Tugdual Saunier (tucksaun) + - Hiromi Hishida (77web) + - Matthieu Ouellette-Vachon (maoueh) + - Michał Pipa (michal.pipa) + - Amal Raghav (kertz) + - Jonathan Ingram (jonathaningram) + - Artur Kotyrba + - Rouven Weßling (realityking) + - Charles Sarrazin (csarrazi) + - Warnar Boekkooi (boekkooi) + - Andréia Bohner (andreia) + - Dmitrii Chekaliuk (lazyhammer) + - Clément JOBEILI (dator) + - Dorian Villet (gnutix) + - Javier Spagnoletti (phansys) + - Richard Miller (mr_r_miller) + - hacfi (hifi) + - Mario A. Alvarez Garcia (nomack84) + - Dennis Benkert (denderello) + - Alexander M. Turek (derrabus) + - Konstantin Myakshin (koc) + - Benjamin Dulau (dbenjamin) + - Andreas Hucks (meandmymonkey) + - Mikael Pajunen + - Noel Guilbert (noel) + - Joel Wurtz (brouznouf) + - bronze1man + - sun (sun) + - Larry Garfield (crell) + - Martin Schuhfuß (usefulthink) + - Thomas Rabaix (rande) + - Matthieu Bontemps (mbontemps) + - Pierre Minnieur (pminnieur) + - fivestar + - Dominique Bongiraud + - Leszek Prabucki (l3l0) + - François Zaninotto (fzaninotto) + - Dustin Whittle (dustinwhittle) + - jeff + - John Kary (johnkary) + - Justin Hileman (bobthecow) + - Michele Orselli (orso) + - Sven Paulus (subsven) + - Lars Strojny (lstrojny) + - Daniel Wehner + - Rui Marinho (ruimarinho) + - Evgeniy (ewgraf) + - Julien Brochet (mewt) + - Sergey Linnik (linniksa) + - Jáchym Toušek + - Marcel Beerta (mazen) + - Vincent AUBERT (vincent) + - julien pauli (jpauli) + - Florian Lonqueu-Brochard (florianlb) + - Francois Zaninotto + - Alexander Kotynia (olden) + - Daniel Tschinder + - Elnur Abdurrakhimov (elnur) + - Manuel Reinhard (sprain) + - Danny Berger (dpb587) + - Roman Marintšenko (inori) + - Xavier Montaña Carreras (xmontana) + - Chris Wilkinson (thewilkybarkid) + - Xavier Perez + - Arjen Brouwer (arjenjb) + - Katsuhiro OGAWA + - Alif Rachmawadi + - Pierre-Yves LEBECQ (pylebecq) + - Eugene Leonovich (rybakit) + - Joseph Rouff (rouffj) + - Félix Labrecque (woodspire) + - Tomáš Votruba (tomas_votruba) + - GordonsLondon + - Jan Sorgalla (jsor) + - Ray + - Chekote + - Thomas Adam + - Albert Casademont (acasademont) + - jdhoek + - Jeremy Livingston (jeremylivingston) + - Nikita Konstantinov + - Wodor Wodorski + - Matthieu Auger (matthieuauger) + - Michael Lee (zerustech) + - Sébastien Lavoie (lavoiesl) + - Beau Simensen (simensen) + - Robert Kiss (kepten) + - Ruben Gonzalez (rubenrua) + - Marcos Sánchez + - Kim Hemsø Rasmussen (kimhemsoe) + - Diego Saint Esteben (dosten) + - Tom Van Looy (tvlooy) + - Wouter Van Hecke + - Peter Kruithof (pkruithof) + - Michael Holm (hollo) + - Marc Weistroff (futurecat) + - Blanchon Vincent (blanchonvincent) + - Dawid Nowak + - Kristen Gilden (kgilden) + - Chris Smith (cs278) + - Richard van Laak (rvanlaak) + - Florian Klein (docteurklein) + - Manuel Kiessling (manuelkiessling) + - Daniel Wehner + - Atsuhiro KUBO (iteman) + - Andrew Moore (finewolf) + - Bertrand Zuchuat (garfield-fr) + - Gabor Toth (tgabi333) + - Grégoire Paris (greg0ire) + - Alex Pott + - realmfoo + - Thomas Tourlourat (armetiz) + - Andrey Esaulov (andremaha) + - Grégoire Passault (gregwar) + - Ismael Ambrosi (iambrosi) + - Uwe Jäger (uwej711) + - Aurelijus Valeiša (aurelijus) + - Jan Decavele (jandc) + - Gustavo Piltcher + - Stepan Tanasiychuk (stfalcon) + - Tiago Ribeiro (fixe) + - Bob den Otter (bopp) + - Adrian Rudnik (kreischweide) + - Francesc Rosàs (frosas) + - Julien Galenski (ruian) + - Bongiraud Dominique + - janschoenherr + - Thomas Schulz (king2500) + - Marco Pivetta (ocramius) + - Ricard Clau (ricardclau) + - Lorenz Schori + - Giorgio Premi + - Erin Millard + - Matthew Lewinski (lewinski) + - Antonio J. García Lagar (ajgarlag) + - alquerci + - Francesco Levorato + - Vitaliy Zakharov (zakharovvi) + - Tobias Sjösten (tobiassjosten) + - Gyula Sallai (salla) + - Inal DJAFAR (inalgnu) + - Christian Gärtner (dagardner) + - Tomasz Kowalczyk (thunderer) + - François-Xavier de Guillebon (de-gui_f) + - Felix Labrecque + - Yaroslav Kiliba + - Stepan Anchugov (kix) + - Terje Bråten + - Robbert Klarenbeek (robbertkl) + - hossein zolfi (ocean) + - Clément Gautier (clementgautier) + - Eduardo Gulias (egulias) + - giulio de donato (liuggio) + - Stéphane PY (steph_py) + - Philipp Kräutli (pkraeutli) + - Kirill chEbba Chebunin (chebba) + - Greg Thornton (xdissent) + - Costin Bereveanu (schniper) + - Loïc Chardonnet (gnusat) + - Marek Kalnik (marekkalnik) + - Tobias Nyholm (tobias) + - Vyacheslav Salakhutdinov (megazoll) + - Hassan Amouhzi + - Tamas Szijarto + - Michaël Perrin (michael.perrin) + - Pavel Volokitin (pvolok) + - Endre Fejes + - Tobias Naumann (tna) + - Shein Alexey + - Joe Lencioni + - Daniel Tschinder + - Diego Agulló (aeoris) + - Kai + - Lee Rowlands + - Maximilian Reichel (phramz) + - Karoly Negyesi (chx) + - Xavier HAUSHERR + - Albert Jessurum (ajessu) + - Laszlo Korte + - Miha Vrhovnik + - Alessandro Desantis + - hubert lecorche (hlecorche) + - Marc Morales Valldepérez (kuert) + - Vadim Kharitonov (virtuozzz) + - Oscar Cubo Medina (ocubom) + - Karel Souffriau + - Christophe L. (christophelau) + - Massimiliano Arione (garak) + - Anthon Pang (robocoder) + - Jannik Zschiesche (apfelbox) + - Emanuele Gaspari (inmarelibero) + - Dariusz Rumiński + - Brian King + - Michel Salib (michelsalib) + - geoffrey + - Jeanmonod David (jeanmonod) + - Berny Cantos (xphere81) + - Thomas Lallement (raziel057) + - Jan Schumann + - Niklas Fiekas + - Mark Challoner (markchalloner) + - Markus Bachmann (baachi) + - lancergr + - Olivier Dolbeau (odolbeau) + - Roumen Damianoff (roumen) + - vagrant + - Asier Illarramendi (doup) + - Chris Sedlmayr (catchamonkey) + - Seb Koelen + - Christoph Mewes (xrstf) + - Vitaliy Tverdokhlib (vitaliytv) + - Ariel Ferrandini (aferrandini) + - Dirk Pahl (dirkaholic) + - cedric lombardot (cedriclombardot) + - Jonas Flodén (flojon) + - Christian Schmidt + - Marcin Sikoń (marphi) + - Dominik Zogg (dominik.zogg) + - Mathieu Lemoine + - franek (franek) + - Damien Alexandre (damienalexandre) + - Adam Harvey + - Alex Bakhturin + - boombatower + - Fabrice Bernhard (fabriceb) + - Jérôme Macias (jeromemacias) + - Fabian Lange (codingfabian) + - Yoshio HANAWA + - Sebastian Bergmann + - Pablo Díez (pablodip) + - Kevin McBride + - Philipp Rieber (bicpi) + - Manuel de Ruiter (manuel) + - Jérémy Romey (jeremyfreeagent) + - Eduardo Oliveira (entering) + - Iker Ibarguren (ikerib) + - Ricardo Oliveira (ricardolotr) + - ondrowan + - Barry vd. Heuvel (barryvdh) + - Jerzy Zawadzki (jzawadzki) + - Evan S Kaufman (evanskaufman) + - mcben + - Jérôme Vieilledent (lolautruche) + - Maks Slesarenko + - mmoreram + - Markus Lanthaler (lanthaler) + - Vicent Soria Durá (vicentgodella) + - Anthony Ferrara + - Ioan Negulescu + - Jakub Škvára (jskvara) + - Daniel Beyer + - Andrew Udvare (audvare) + - alexpods + - Tristan Darricau (nicofuma) + - Erik Trapman (eriktrapman) + - De Cock Xavier (xdecock) + - Possum + - Scott Arciszewski + - Norbert Orzechowicz (norzechowicz) + - Matthijs van den Bos (matthijs) + - Loick Piera (pyrech) + - Lenard Palko + - Nils Adermann (naderman) + - Gábor Fási + - Benjamin Leveque (benji07) + - Ivan Kurnosov + - sasezaki + - Dawid Pakuła (zulusx) + - Florian Rey (nervo) + - Rodrigo Borrego Bernabé (rodrigobb) + - Leo Feyer + - MatTheCat + - John Bafford (jbafford) + - Denis Gorbachev (starfall) + - Titouan Galopin (tgalopin) + - Steven Surowiec + - Kevin Saliou (kbsali) + - Ryan + - Alexander Deruwe (aderuwe) + - Alain Hippolyte (aloneh) + - Dave Hulbert (dave1010) + - François Pluchino (francoispluchino) + - Ivan Rey (ivanrey) + - Marcin Chyłek (songoq) + - Ned Schwartz + - Ziumin + - Lenar Lõhmus + - Benjamin Laugueux (yzalis) + - Zach Badgett (zachbadgett) + - Aurélien Fredouelle + - Pavel Campr (pcampr) + - Johnny Robeson (johnny) + - Disquedur + - Geoffrey Tran (geoff) + - Jan Behrens + - Mantas Var (mvar) + - Sebastian Krebs + - Christopher Davis (chrisguitarguy) + - alcaeus + - vitaliytv + - Sebastian Blum + - aubx + - Ricky Su (ricky) + - Gildas Quéméner (gquemener) + - Max Rath (drak3) + - Stéphane Escandell (sescandell) + - Sinan Eldem + - Gennady Telegin (gtelegin) + - Alexandre Dupuy (satchette) + - Nahuel Cuesta (ncuesta) + - Chris Boden (cboden) + - Asmir Mustafic (goetas) + - Josip Kruslin + - Hany el-Kerdany + - Wang Jingyu + - Åsmund Garfors + - Maxime Douailin + - Gregor Harlan + - Javier López (loalf) + - Reinier Kip + - Dustin Dobervich (dustin10) + - Sebastian Marek (proofek) + - Erkhembayar Gantulga (erheme318) + - David Fuhr + - Kamil Kokot (pamil) + - Rostyslav Kinash + - Maciej Malarz (malarzm) + - Daisuke Ohata + - Vincent Simonin + - Stefan Warman + - Tristan Maindron (tmaindron) + - Ke WANG (yktd26) + - Strate + - Jakub Kucharovic + - Miquel Rodríguez Telep (mrtorrent) + - Sergey Kolodyazhnyy (skolodyazhnyy) + - umpirski + - Chris Heng (gigablah) + - Ulumuddin Yunus (joenoez) + - Florian Pfitzer (marmelatze) + - Luc Vieillescazes (iamluc) + - Johann Saunier (prophet777) + - Antoine Corcy + - Artur Eshenbrener + - Arturs Vonda + - Sascha Grossenbacher + - Szijarto Tamas + - Benjamin Zikarsky (bzikarsky) + - Ben Davies (bendavies) + - Mickaël Andrieu (mickaelandrieu) + - Simon Schick (simonsimcity) + - redstar504 + - Hossein Bukhamsin + - origaminal + - Paweł Wacławczyk (pwc) + - Oleg Zinchenko (cystbear) + - Johannes Klauss (cloppy) + - Evan Villemez + - fzerorubigd + - Thomas Ploch + - Benjamin Grandfond (benjamin) + - Tiago Brito (blackmx) + - Richard van den Brand (ricbra) + - develop + - Hidde Wieringa + - Mark Sonnabaum + - Alexander Obuhovich (aik099) + - jochenvdv + - Filippo Tessarotto + - Arturas Smorgun (asarturas) + - Alexander Volochnev (exelenz) + - Michael Piecko + - yclian + - Sergio Santoro + - Sebastian Grodzicki (sgrodzicki) + - Pascal Helfenstein + - Baldur Rensch (brensch) + - Vladyslav Petrovych + - Alex Xandra Albert Sim + - Yuen-Chi Lian + - Besnik Br + - Joshua Nye + - Dave Marshall (davedevelopment) + - avorobiev + - Venu + - Lars Vierbergen + - Dennis Hotson + - Andrew Tchircoff (andrewtch) + - michaelwilliams + - 1emming + - Leevi Graham (leevigraham) + - Casper Valdemar Poulsen + - Josiah (josiah) + - Marek Štípek (maryo) + - John Bohn (jbohn) + - Marc Morera (mmoreram) + - Andrew Hilobok (hilobok) + - Christian Soronellas (theunic) + - Romain Gautier (mykiwi) + - Yosmany Garcia (yosmanyga) + - Degory Valentine + - Benoit Lévêque (benoit_leveque) + - Jeroen Fiege (fieg) + - Krzysiek Łabuś + - Nicolas Dewez (nicolas_dewez) + - Xavier Lacot (xavier) + - Olivier Maisonneuve (olineuve) + - Francis Turmel (fturmel) + - cgonzalez + - Ben + - Jayson Xu (superjavason) + - Jaik Dean (jaikdean) + - fago + - Harm van Tilborg + - Jan Prieser + - Artur Melo (restless) + - James Michael DuPont + - Tom Klingenberg + - Christopher Hall (mythmakr) + - Paul Kamer (pkamer) + - Rafał Wrzeszcz (rafalwrzeszcz) + - Reen Lokum + - Martin Parsiegla (spea) + - Denis Charrier (brucewouaigne) + - Quentin Schuler + - Pierre Vanliefland (pvanliefland) + - frost-nzcr4 + - Oskar Stark (oskarstark) + - Abhoryo + - Fabian Vogler (fabian) + - Korvin Szanto + - Alaattin Kahramanlar (alaattin) + - Maksim Kotlyar (makasim) + - Neil Ferreira + - Dmitry Parnas (parnas) + - DQNEO + - Emanuele Iannone + - Tony Malzhacker + - DUPUCH (bdupuch) + - Cyril Quintin (cyqui) + - Gerard van Helden (drm) + - Johnny Peck (johnnypeck) + - David Romaní + - Patrick Allaert + - Gustavo Falco (gfalco) + - Matt Robinson (inanimatt) + - Aleksey Podskrebyshev + - Steffen Roßkamp + - David Marín Carreño (davefx) + - Jörn Lang (j.lang) + - mwsaz + - Benoît Bourgeois + - corphi + - grizlik + - Derek ROTH + - Shin Ohno (ganchiku) + - Geert De Deckere (geertdd) + - Jan Kramer (jankramer) + - Jean-Baptiste GOMOND (mjbgo) + - abdul malik ikhsan (samsonasik) + - Henry Snoek (snoek09) + - Timothée Barray (tyx) + - Christian Morgan + - Alexander Miehe (engerim) + - Morgan Auchede (mauchede) + - Don Pinkster + - Maksim Muruev + - Emil Einarsson + - Thibault Duplessis + - Marc Abramowitz + - Martijn Evers + - Jacques Moati + - Balazs Csaba (balazscsaba2006) + - Harry Walter (haswalt) + - Johnson Page (jwpage) + - Michael Roterman (wtfzdotnet) + - Arno Geurts + - Adán Lobato (adanlobato) + - Matthew Davis (mdavis1982) + - Maks + - Gábor Tóth + - Daniel Cestari + - Brunet Laurent (lbrunet) + - Magnus Nordlander (magnusnordlander) + - Michiel Boeckaert (milio) + - Mikhail Yurasov (mym) + - LOUARDI Abdeltif (ouardisoft) + - Robert Gruendler (pulse00) + - Simon Terrien (sterrien) + - Benoît Merlet (trompette) + - Koen Kuipers + - datibbaw + - Raul Fraile (raulfraile) + - sensio + - Patrick Kaufmann + - Reece Fowell (reecefowell) + - stefan.r + - Matthieu Napoli (mnapoli) + - Alexandru Furculita (afurculita) + - Ben Ramsey (ramsey) + - Christian Jul Jensen + - The Whole Life to Learn + - Farhad Safarov + - Liverbool (liverbool) + - Sam Malone + - Phan Thanh Ha (haphan) + - Chris Jones (leek) + - Colin O'Dell (colinodell) + - xaav + - Jean-Christophe Cuvelier [Artack] + - Mahmoud Mostafa (mahmoud) + - Michael Tibben + - Sander Marechal + - Radosław Benkel + - ttomor + - Mei Gwilym (meigwilym) + - Michael H. Arieli (excelwebzone) + - Luciano Mammino (loige) + - Michael Hirschler (mvhirsch) + - fabios + - Jérôme Vasseur + - Sander Coolen (scoolen) + - Nicolas Le Goff (nlegoff) + - Anne-Sophie Bachelard (annesophie) + - Manuele Menozzi + - Anton Babenko (antonbabenko) + - Irmantas Šiupšinskas (irmantas) + - Charles-Henri Bruyand + - Danilo Silva + - Zachary Tong (polyfractal) + - Hryhorii Hrebiniuk + - dantleech + - Xavier Leune + - Christian Schmidt + - Tero Alén (tero) + - DerManoMann + - Guillaume Royer + - Artem (digi) + - dantleech + - Vadim Tyukov (vatson) + - Sortex + - chispita + - Wojciech Sznapka + - Máximo Cuadros (mcuadros) + - Stefan Gehrig (sgehrig) + - Alex Bogomazov + - tamirvs + - julien.galenski + - Christian Neff + - Per Sandström (per) + - Goran Juric + - Laurent Ghirardotti (laurentg) + - Nicolas Macherey + - Jan Rosier (rosier) + - Lin Clark + - Jeremy David (jeremy.david) + - Troy McCabe + - Ville Mattila + - Boris Vujicic (boris.vujicic) + - Max Beutel + - Michal Trojanowski + - Catalin Dan + - nacho + - Piotr Antosik (antek88) + - Artem Lopata + - Samuel ROZE (sroze) + - Sergey Novikov (s12v) + - Marcos Quesada (marcos_quesada) + - Matthew Vickery (mattvick) + - Dan Finnie + - Ken Marfilla (marfillaster) + - Max Grigorian (maxakawizard) + - benatespina (benatespina) + - Denis Kop + - jfcixmedia + - Martijn Evers + - Benjamin Paap (benjaminpaap) + - Christian + - Sergii Smertin (nfx) + - Bailey Parker + - Eddie Jaoude + - Haritz Iturbe (hizai) + - Nerijus Arlauskas (nercury) + - SPolischook + - Diego Sapriza + - Joan Cruz + - inspiran + - Cristobal Dabed + - Daniel Mecke (daniel_mecke) + - Matteo Giachino (matteosister) + - Alex Demchenko (pilot) + - Tadas Gliaubicas (tadcka) + - Benoit Garret + - Thomas Royer (cydonia7) + - DerManoMann + - Julien Bianchi (jubianchi) + - Marcin Chwedziak + - Roland Franssen (ro0) + - Tony Cosentino (tony-co) + - Rodrigo Díez Villamuera (rodrigodiez) + - e-ivanov + - Jochen Bayer (jocl) + - Jeremy Bush + - wizhippo + - Viacheslav Sychov + - rpg600 + - Péter Buri (burci) + - Davide Borsatto (davide.borsatto) + - Teoh Han Hui (teohhanhui) + - kaiwa + - Charles Sanquer (csanquer) + - Albert Ganiev (helios-ag) + - Neil Katin + - David Otton + - Will Donohoe + - peter + - Jérémy Jourdin (jjk801) + - Artem Kolesnikov (tyomo4ka) + - Gustavo Adrian + - Yannick + - spdionis + - Eduardo García Sanz (coma) + - James Gilliland + - Roy Van Ginneken + - Stephan Vock + - David de Boer (ddeboer) + - Gilles Doge (gido) + - abulford + - Brooks Boyd + - Roger Webb + - Dmitriy Simushev + - Martin Hujer (martinhujer) + - Max Voloshin (maxvoloshin) + - Nicolas Fabre (nfabre) + - Raul Rodriguez (raul782) + - Patrick Landolt (scube) + - WybrenKoelmans + - Derek Lambert + - MightyBranch + - Kacper Gunia (cakper) + - Peter Thompson (petert82) + - Felicitus + - Krzysztof Przybyszewski + - Paul Matthews + - Juan Traverso + - Philipp Strube + - Christian Sciberras + - Anton Bakai + - Clement Herreman (clemherreman) + - Nyro (nyro) + - Trent Steel (trsteel88) + - Marco + - Marc Torres + - Alberto Aldegheri + - heccjj + - Alexandre Melard + - Sergey Yuferev + - Tobias Stöckler + - Mario Young + - Jakub Kulhan + - Mo Di (modi) + - Jeroen van den Enden (stoefke) + - Quique Porta (quiqueporta) + - Tomasz Szymczyk (karion) + - ConneXNL + - Aharon Perkel + - Abdul.Mohsen B. A. A + - Gintautas Miselis + - Benoît Burnichon + - Remi Collet + - pthompson + - Malaney J. Hill + - Christian Flach (cmfcmf) + - Cédric Girard (enk_) + - Lars Ambrosius Wallenborn (larsborn) + - Oriol Mangas Abellan (oriolman) + - Sebastian Göttschkes (sgoettschkes) + - Tatsuya Tsuruoka + - Ross Tuck + - Kévin Gomez (kevin) + - azine + - Dawid Sajdak + - Ludek Stepan + - Geoffrey Brier + - Aaron Stephens (astephens) + - Balázs Benyó (duplabe) + - Erika Heidi Reinaldo (erikaheidi) + - Pierre Tachoire (krichprollsch) + - Marc J. Schmidt (marcjs) + - Marco Jantke + - Saem Ghani + - Sebastian Utz + - Adrien Gallou (agallou) + - Karol Sójko (karolsojko) + - sl_toto (sl_toto) + - Walter Dal Mut (wdalmut) + - Sébastien HOUZÉ + - Jingyu Wang + - Daniel Espendiller + - steveYeah + - Samy Dindane (dinduks) + - Keri Henare (kerihenare) + - Cédric Lahouste (rapotor) + - Samuel Vogel (samuelvogel) + - Berat Doğan + - twifty + - Anthony Ferrara + - Klaas Cuvelier (kcuvelier) + - ShiraNai7 + - Janusz Jabłoński (yanoosh) + - George Giannoulopoulos + - Daniel Richter (richtermeister) + - ChrisC + - Ilya Biryukov + - Jason Desrosiers + - m.chwedziak + - Philip Frank + - Lance McNearney + - Frank Neff (fneff) + - Giorgio Premi + - caponica + - Matt Daum (daum) + - Alberto Pirovano (geezmo) + - Pete Mitchell (peterjmit) + - Tom Corrigan (tomcorrigan) + - Martin Pärtel + - Miroslav Sustek + - Patrick Daley (padrig) + - Xavier Briand (xavierbriand) + - Max Summe + - WedgeSama + - Felds Liscia + - James Halsall (jaitsu) + - Maxime Veber (nek-) + - Sullivan SENECHAL + - Tadcka + - Beth Binkovitz + - Romain Geissler + - Tomaz Ahlin + - Benjamin Cremer (bcremer) + - Marcus Stöhr (dafish) + - Emmanuel Vella (emmanuel.vella) + - Carsten Nielsen (phreaknerd) + - Mathieu Rochette + - Jay Severson + - René Kerner + - Nathaniel Catchpole + - Jose Gonzalez + - Adrien Samson (adriensamson) + - Samuel Gordalina (gordalina) + - Max Romanovsky (maxromanovsky) + - Mathieu Morlon + - Daniel Tschinder + - Rafał Muszyński (rafmus90) + - Timothy Anido (xanido) + - Rick Prent + - Martin Eckhardt + - Pieter Jordaan + - Damien Tournoud + - Jon Gotlin (jongotlin) + - Michael Dowling (mtdowling) + - BilgeXA + - r1pp3rj4ck + - Robert Queck + - mlively + - Fabian Steiner (fabstei) + - Klaus Silveira (klaussilveira) + - Thomas Chmielowiec (chmielot) + - Jānis Lukss + - rkerner + - Alex Silcock + - Rob Bast + - Matthew J Mucklo + - fdgdfg (psampaz) + - Stéphane Seng + - Maxwell Vandervelde + - kaywalker + - Mike Meier + - Sebastian Ionescu + - Thomas Ploch + - Simon Neidhold + - Valentin VALCIU + - Kevin Dew + - James Cowgill + - Nicolas Schwartz (nicoschwartz) + - Patrik Gmitter (patie) + - Jonathan Gough + - Benjamin Bender + - Konrad Mohrfeldt + - Lance Chen + - kor3k kor3k (kor3k) + - Stelian Mocanita (stelian) + - Flavian (2much) + - mike + - Keith Maika + - Mephistofeles + - Hoffmann András + - Olivier + - pscheit + - Dan Harper + - moldcraft + - Ramon Kleiss (akathos) + - César Suárez (csuarez) + - Nicolas Badey (nico-b) + - Shane Preece (shane) + - wusuopu + - povilas + - Alessandro Tagliapietra (alex88) + - Biji (biji) + - Gunnar Lium (gunnarlium) + - Tiago Garcia (tiagojsag) + - Artiom + - Jakub Simon + - Bouke Haarsma + - Martin Eckhardt + - Denis Zunke + - Jonathan Poston + - Adrian Olek (adrianolek) + - Przemysław Piechota (kibao) + - Leonid Terentyev (li0n) + - Adam Prager (padam87) + - ryunosuke + - victoria + - Francisco Facioni (fran6co) + - Iwan van Staveren (istaveren) + - Povilas S. (povilas) + - pborreli + - Eric Caron + - 2manypeople + - Wing + - Thomas Bibb + - Matt Farmer + - catch + - Alexandre Segura + - Josef Cech + - Arnau González (arnaugm) + - Nate (frickenate) + - Matthew Foster (mfoster) + - Paul Seiffert (seiffert) + - Vasily Khayrulin (sirian) + - Stefan Koopmanschap (skoop) + - Stefan Hüsges (tronsha) + - stloyd + - Chris Tickner + - Andrew Coulton + - Michal Gebauer + - Gleb Sidora + - David Stone + - Pablo Maria Martelletti (pmartelletti) + - Yassine Guedidi (yguedidi) + - Luis Muñoz + - Andreas + - Thomas Chmielowiec + - Andrey Ryaguzov + - Manatsawin Hanmongkolchai + - Gunther Konig + - Maciej Schmidt + - nuncanada + - flack + - František Bereň + - Christoph Nissle (derstoffel) + - Ionel Scutelnicu (ionelscutelnicu) + - Nicolas Tallefourtané (nicolab) + - Botond Dani (picur) + - Thierry Marianne (thierrymarianne) + - Nick Stemerdink + - David Stone + - jjanvier + - Julius Beckmann + - Romain Dorgueil + - Grayson Koonce (breerly) + - Karim Cassam Chenaï (ka) + - Nicolas Bastien (nicolas_bastien) + - Denis (yethee) + - Andrew Zhilin (zhil) + - Andy Stanberry + - Luiz “Felds” Liscia + - Thomas Rothe + - alefranz + - avi123 + - alsar + - Aarón Nieves Fernández + - Mike Meier + - Kirill Saksin + - michalmarcinkowski + - Warwick + - Chris + - JakeFr + - efeen + - Michał Dąbrowski (defrag) + - Nathanael Noblet (gnat) + - Simone Fumagalli (hpatoio) + - Brian Graham (incognito) + - Kevin Vergauwen (innocenzo) + - Alessio Baglio (ioalessio) + - Jordi Llonch (jordillonch) + - Cédric Dugat (ph3nol) + - Philip Dahlstrøm (phidah) + - Milos Colakovic (project2481) + - Rénald Casagraude (rcasagraude) + - Robin Duval (robin-duval) + - Grinbergs Reinis (shima5) + - Artem Lopata (bumz) + - Alexey Popkov + - Artyom Protaskin + - Nathanael d. Noblet + - helmer + - Daan van Renterghem + - Bram Van der Sype (brammm) + - Julien Moulin (lizjulien) + - Nikita Nefedov (nikita2206) + - Mauro Foti (skler) + - Yannick Warnier (ywarnier) + - Kevin Decherf + - Jason Woods + - dened + - Dmitry Korotovsky + - Sam Ward + - Walther Lalk + - Adam + - devel + - Trevor Suarez + - gedrox + - dropfen + - Andrey Chernykh + - Edvinas Klovas + - Drew Butler + - J Bruni + - Alexey Prilipko + - bertillon + - Victor Bocharsky (bocharsky_bw) + - Luca Genuzio (genuzio) + - Hans Nilsson (hansnilsson) + - Andrew Marcinkevičius (ifdattic) + - Ioana Hazsda (ioana-hazsda) + - Jan Marek (janmarek) + - Mark de Haan (markdehaan) + - Dan Patrick (mdpatrick) + - Rares Vlaseanu (raresvla) + - Sofiane HADDAG (sofhad) + - tante kinast (tante) + - Vincent LEFORT (vlefort) + - Sadicov Vladimir (xtech) + - Peter van Dommelen + - Alexander Zogheb + - Rémi Blaise + - Joel Marcey + - David Christmann + - root + - James Hudson + - Tom Maguire + - David Zuelke + - Pierre Rineau + - adenkejawen + - Ari Pringle (apringle) + - Dan Ordille (dordille) + - Jan Eichhorn (exeu) + - Grégory Pelletier (ip512) + - John Nickell (jrnickell) + - Julien DIDIER (juliendidier) + - Martin Mayer (martin) + - Grzegorz Łukaszewicz (newicz) + - Omar Yepez (oyepez003) + - Veres Lajos + - grifx + - Robert Campbell + - Matt Lehner + - Hein Zaw Htet™ + - Ruben Kruiswijk + - Michael J + - Joseph Maarek + - Alexander Menk + - Alex Pods + - hadriengem + - timaschew + - Ian Phillips + - Haritz + - Matthieu Prat + - Grummfy + - Thomas Landauer + - Filipe Guerra + - Gerben Wijnja + - Rowan Manning + - Per Modin + - David Windell + - Gabriel Birke + - skafandri + - Alan Chen + - Maerlyn + - Even André Fiskvik + - Dane Powell + - Gerrit Drost + - Lenar Lõhmus + - Cristian Gonzalez + - AlberT + - Juan M Martínez + - Gilles Gauthier + - ddebree + - Alex + - Klaas Naaijkens + - Daniel González Cerviño + - possum + - Rafał + - Adria Lopez (adlpz) + - Rosio (ben-rosio) + - Simon Paarlberg (blamh) + - Jeroen Thora (bolle) + - Masao Maeda (brtriver) + - Darius Leskauskas (darles) + - David Joos (djoos) + - Denis Klementjev (dklementjev) + - Tomáš Polívka (draczris) + - Vincent Composieux (eko) + - Franz Liedke (franzliedke) + - Christophe BECKER (goabonga) + - gondo (gondo) + - Gusakov Nikita (hell0w0rd) + - Osman Üngür (import) + - Javier Núñez Berrocoso (javiernuber) + - Jelle Bekker (jbekker) + - Ian Jenkins (jenkoian) + - Jorge Martin (jorgemartind) + - Kevin Herrera (kherge) + - Luis Ramón López López (lrlopez) + - Muriel (metalmumu) + - Michael Pohlers (mick_the_big) + - Cayetano Soriano Gallego (neoshadybeat) + - Pablo Monterde Perez (plebs) + - Jimmy Leger (redpanda) + - Pavel Batanov (scaytrase) + - Cyrille Jouineau (tuxosaurus) + - Yorkie Chadwick (yorkie76) + - Yanick Witschi + - Ondrej Mirtes + - akimsko + - Youpie + - srsbiz + - Taylan Kasap + - Nicolas A. Bérard-Nault + - Gladhon + - Saem Ghani + - Stefan Oderbolz + - Curtis + - Alexey Popkov + - Joseph Deray + - Damian Sromek + - Ben + - Arnaud Buathier (arnapou) + - chesteroni (chesteroni) + - Mauricio Lopez (diaspar) + - Daniele Cesarini (ijanki) + - Ismail Asci (ismailasci) + - Simon CONSTANS (kosssi) + - Kristof Van Cauwenbergh (kristofvc) + - Ramon Henrique Ornelas (ramonornela) + - Markus S. (staabm) + - Till Klampaeckel (till) + - Tobias Weinert (tweini) + - Ulf Reimers (ureimers) + - Wotre + - goohib + - Xavier HAUSHERR + - Mantas Urnieža + - Cas + - Dusan Kasan + - Myke79 + - Brian Debuire + - Piers Warmers + - Sylvain Lorinet + - klyk50 + - Andreas Lutro + - jc + - BenjaminBeck + - Aurelijus Rožėnas + - znerol + - Christian Eikermann + - Antonio Angelino + - Vladimir Sazhin + - lol768 + - jamogon + - Vyacheslav Slinko + - Johannes + - Jörg Rühl + - wesleyh + - sergey + - Michael Genereux + - patrick-mcdougle + - Dariusz Czech + - Anonymous User + - Eric J. Duran + - cmfcmf + - Drew Butler + - Steve Müller + - Andras Ratz + - andreabreu98 + - Michael Schneider + - n-aleha + - Şəhriyar İmanov + - Kaipi Yann + - Sam Williams + - Adrian Philipp + - James Michael DuPont + - Eugene Wissner + - Kasperki + - Tammy D + - Ondrej Slinták + - vlechemin + - Brian Corrigan + - Ladislav Tánczos + - Brian Freytag + - Skorney + - mieszko4 + - Steve Preston + - Neophy7e + - bokonet + - Arrilot + - Markus Staab + - Pierre-Louis LAUNAY + - djama + - Eduardo Conceição + - Jon Cave + - Sébastien HOUZE + - Abdulkadir N. A. + - Yevgen Kovalienia + - Sema + - Elan Ruusamäe + - Thorsten Hallwas + - Michael Squires + - Norman Soetbeer + - Benjamin Long + - Matt Janssen + - Peter Gribanov + - kwiateusz + - David Soria Parra + - Sergiy Sokolenko + - dinitrol + - Penny Leach + - Richard Trebichavský + - g123456789l + - oscartv + - DanSync + - Peter Zwosta + - parhs + - Diego Campoy + - TeLiXj + - Oncle Tom + - Christian Stocker + - Dawid Nowak + - Richard Quadling + - Karolis Daužickas + - tirnanog06 + - phc + - Дмитрий Пацура + - ilyes kooli + - Matthias Althaus + - Michaël VEROUX + - Julia + - arduanov + - sualko + - Nicolas Roudaire + - Jérôme Vasseur + - Alfonso (afgar) + - Andreas Forsblom (aforsblo) + - Alex Olmos (alexolmos) + - Antonio Mansilla (amansilla) + - Juan Ases García (ases) + - Siragusa (asiragusa) + - Daniel Basten (axhm3a) + - Bill Hance (billhance) + - Bernd Matzner (bmatzner) + - Choong Wei Tjeng (choonge) + - Kousuke Ebihara (co3k) + - Loïc Vernet (coil) + - Christoph Schaefer (cvschaefer) + - Damon Jones (damon__jones) + - David Badura (davidbadura) + - Daniel Londero (dlondero) + - Adel ELHAIBA (eadel) + - Damián Nohales (eagleoneraptor) + - Elliot Anderson (elliot) + - Fabien D. (fabd) + - Sorin Gitlan (forapathy) + - Yohan Giarelli (frequence-web) + - Gerry Vandermaesen (gerryvdm) + - Ghazy Ben Ahmed (ghazy) + - Arash Tabriziyan (ghost098) + - ibasaw (ibasaw) + - Vladislav Krupenkin (ideea) + - joris de wit (jdewit) + - Jérémy CROMBEZ (jeremy) + - Jose Manuel Gonzalez (jgonzalez) + - Jorge Maiden (jorgemaiden) + - Justin Rainbow (jrainbow) + - JuntaTom (juntatom) + - Ismail Faizi (kanafghan) + - Sébastien Armand (khepin) + - Krzysztof Menżyk (krymen) + - samuel laulhau (lalop) + - Laurent Bachelier (laurentb) + - Jérôme Parmentier (lctrs) + - Florent Viel (luxifer) + - Matthieu Moquet (mattketmo) + - Moritz Borgmann (mborgmann) + - Matt Drollette (mdrollette) + - Adam Monsen (meonkeys) + - Ala Eddine Khefifi (nayzo) + - emilienbouard (neime) + - Nicholas Byfleet (nickbyfleet) + - ollie harridge (ollietb) + - Paul Andrieux (paulandrieux) + - Paweł Szczepanek (pauluz) + - Christian López Espínola (penyaskito) + - Petr Jaroš (petajaros) + - Philipp Hoffmann (philipphoffmann) + - Alex Carol (picard89) + - Daniel Perez Pinazo (pitiflautico) + - Brayden Williams (redstar504) + - Rich Sage (richsage) + - Ruud Kamphuis (ruudk) + - Bart Ruysseveldt (ruyss) + - Sascha Dens (saschadens) + - scourgen hung (scourgen) + - Sebastian Busch (sebu) + - André Filipe Gonçalves Neves (seven) + - Bruno Ziegler (sfcoder) + - Andrea Giuliano (shark) + - Schuyler Jager (sjager) + - Volker (skydiablo) + - Julien Sanchez (sumbobyboys) + - Guillermo Gisinger (t3chn0r) + - Markus Tacker (tacker) + - Tyler Stroud (tystr) + - Moritz Kraft (userfriendly) + - Víctor Mateo (victormateo) + - Vincent (vincent1870) + - Eugene Babushkin (warl) + - Xavier Amado (xamado) + - Yonel Ceruto González (yonelceruto) + - Jesper Søndergaard Pedersen (zerrvox) + - Florent Cailhol + - szymek + - Kovacs Nicolas + - craigmarvelley + - Stano Turza + - simpson + - drublic + - Andreas Streichardt + - smokeybear87 + - Gustavo Adrian + - Michael + - fh-github@fholzhauer.de + - Mark Topper + - Xavier REN + - Zander Baldwin + - Philipp Scheit + - max + - Mohamed Karnichi (amiral) + - Daniel Kolvik (dkvk) + - Jeroen De Dauw (jeroendedauw) + - Maxime COLIN (maximecolin) + - Muharrem Demirci (mdemirci) + - Evgeny Z (meze) + - Nicolas de Marqué (nicola) + - Kevin (oxfouzer) + - Pierre Geyer (ptheg) + - Erik Saunier (snickers) + - Matej Žilák (teo_sk) + - Vladislav Vlastovskiy (vlastv) diff --git a/vendor/symfony/symfony/LICENSE b/vendor/symfony/symfony/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/UPGRADE-3.0.md b/vendor/symfony/symfony/UPGRADE-3.0.md new file mode 100644 index 0000000..a115df1 --- /dev/null +++ b/vendor/symfony/symfony/UPGRADE-3.0.md @@ -0,0 +1,1468 @@ +UPGRADE FROM 2.x to 3.0 +======================= + +### ClassLoader + + * The `UniversalClassLoader` class has been removed in favor of + `ClassLoader`. The only difference is that some method names are different: + + | Old name | New name + | -------- | --- + | `registerNamespaces()` | `addPrefixes()` + | `registerPrefixes()` | `addPrefixes()` + | `registerNamespaces()` | `addPrefix()` + | `registerPrefix()` | `addPrefix()` + | `getNamespaces()` | `getPrefixes()` + | `getNamespaceFallbacks()` | `getFallbackDirs()` + | `getPrefixFallbacks()` | `getFallbackDirs()` + + * The `DebugUniversalClassLoader` class has been removed in favor of + `DebugClassLoader`. The difference is that the constructor now takes a + loader to wrap. + +### Console + + * The `dialog` helper has been removed in favor of the `question` helper. + + * The methods `isQuiet`, `isVerbose`, `isVeryVerbose` and `isDebug` were added + to `Symfony\Component\Console\Output\OutputInterface`. + + * `ProgressHelper` has been removed in favor of `ProgressBar`. + + Before: + + ```php + $h = new ProgressHelper(); + $h->start($output, 10); + for ($i = 1; $i < 5; $i++) { + usleep(200000); + $h->advance(); + } + $h->finish(); + ``` + + After: + + ```php + $bar = new ProgressBar($output, 10); + $bar->start(); + for ($i = 1; $i < 5; $i++) { + usleep(200000); + $bar->advance(); + } + ``` + + * `TableHelper` has been removed in favor of `Table`. + + Before: + + ```php + $table = $app->getHelperSet()->get('table'); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + )) + ; + $table->render($output); + ``` + + After: + + ```php + use Symfony\Component\Console\Helper\Table; + + $table = new Table($output); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + )) + ; + $table->render(); + ``` + +* Parameters of `renderException()` method of the + `Symfony\Component\Console\Application` are type hinted. + You must add the type hint to your implementations. + +### DependencyInjection + + * The method `remove` was added to `Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface`. + + * The methods `Definition::setFactoryClass()`, + `Definition::setFactoryMethod()`, and `Definition::setFactoryService()` have + been removed in favor of `Definition::setFactory()`. Services defined using + YAML or XML use the same syntax as configurators. + + * Synchronized services are deprecated and the following methods have been + removed: `ContainerBuilder::synchronize()`, `Definition::isSynchronized()`, + and `Definition::setSynchronized()`. + +### DomCrawler + + * The interface of the `Symfony\Component\DomCrawler\Crawler` changed. It does no longer implement `\Iterator` but `\IteratorAggregate`. If you rely on methods of the `\Iterator` interface, call the `getIterator` method of the `\IteratorAggregate` interface before. No changes are required in a `\Traversable`-aware control structure, such as `foreach`. + + Before: + + ```php + $crawler->current(); + ``` + + After: + + ```php + $crawler->getIterator()->current(); + ``` + +### EventDispatcher + + * The method `getListenerPriority($eventName, $listener)` has been added to the + `EventDispatcherInterface`. + * The interface `Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface` + extends `Symfony\Component\EventDispatcher\EventDispatcherInterface`. + +### Form + + * The `getBlockPrefix()` method was added to the `FormTypeInterface` in replacement of + the `getName()` method which has been has been removed. + * The `configureOptions()` method was added to the `FormTypeInterface` in replacement + of the `setDefaultOptions()` method which has been removed. + * The `getBlockPrefix()` method was added to the `ResolvedFormTypeInterface` in + replacement of the `getName()` method which has been has been removed. + * The option "precision" was renamed to "scale". + + Before: + + ```php + $builder->add('length', 'number', array( + 'precision' => 3, + )); + ``` + + After: + + ```php + $builder->add('length', 'number', array( + 'scale' => 3, + )); + ``` + + * The option "`virtual`" was renamed to "`inherit_data`". + + Before: + + ```php + $builder->add('address', 'form', array( + 'virtual' => true, + )); + ``` + + After: + + ```php + $builder->add('address', 'form', array( + 'inherit_data' => true, + )); + ``` + + * The method `AbstractType::setDefaultOptions(OptionsResolverInterface $resolver)` and + `AbstractTypeExtension::setDefaultOptions(OptionsResolverInterface $resolver)` have been + renamed. You should use `AbstractType::configureOptions(OptionsResolver $resolver)` and + `AbstractTypeExtension::configureOptions(OptionsResolver $resolver)` instead. + + * The methods `Form::bind()` and `Form::isBound()` were removed. You should + use `Form::submit()` and `Form::isSubmitted()` instead. + + Before: + + ```php + $form->bind(array(...)); + ``` + + After: + + ```php + $form->submit(array(...)); + ``` + + * Passing a `Symfony\Component\HttpFoundation\Request` instance, as was + supported by `FormInterface::bind()`, is not possible with + `FormInterface::submit()` anymore. You should use `FormInterface::handleRequest()` + instead. + + Before: + + ```php + if ('POST' === $request->getMethod()) { + $form->bind($request); + + if ($form->isValid()) { + // ... + } + } + ``` + + After: + + ```php + $form->handleRequest($request); + + if ($form->isValid()) { + // ... + } + ``` + + If you want to test whether the form was submitted separately, you can use + the method `isSubmitted()`: + + ```php + $form->handleRequest($request); + + if ($form->isSubmitted()) { + // ... + + if ($form->isValid()) { + // ... + } + } + ``` + + * The events `PRE_BIND`, `BIND` and `POST_BIND` were renamed to `PRE_SUBMIT`, `SUBMIT` + and `POST_SUBMIT`. + + Before: + + ```php + $builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) { + // ... + }); + ``` + + After: + + ```php + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { + // ... + }); + ``` + + * The class `VirtualFormAwareIterator` was renamed to `InheritDataAwareIterator`. + + Before: + + ```php + use Symfony\Component\Form\Util\VirtualFormAwareIterator; + + $iterator = new VirtualFormAwareIterator($forms); + ``` + + After: + + ```php + use Symfony\Component\Form\Util\InheritDataAwareIterator; + + $iterator = new InheritDataAwareIterator($forms); + ``` + + * The `TypeTestCase` class was moved from the `Symfony\Component\Form\Tests\Extension\Core\Type` namespace to the `Symfony\Component\Form\Test` namespace. + + Before: + + ```php + use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase + + class MyTypeTest extends TypeTestCase + { + // ... + } + ``` + + After: + + ```php + use Symfony\Component\Form\Test\TypeTestCase; + + class MyTypeTest extends TypeTestCase + { + // ... + } + ``` + + * The option "options" of the CollectionType has been renamed to "entry_options". + + * The option "type" of the CollectionType has been renamed to "entry_type". + As a value for the option you must provide the fully-qualified class name (FQCN) + now as well. + + * The `FormIntegrationTestCase` and `FormPerformanceTestCase` classes were moved form the `Symfony\Component\Form\Tests` namespace to the `Symfony\Component\Form\Test` namespace. + + * The constants `ROUND_HALFEVEN`, `ROUND_HALFUP` and `ROUND_HALFDOWN` in class + `NumberToLocalizedStringTransformer` were renamed to `ROUND_HALF_EVEN`, + `ROUND_HALF_UP` and `ROUND_HALF_DOWN`. + + * The methods `ChoiceListInterface::getIndicesForChoices()` and + `ChoiceListInterface::getIndicesForValues()` were removed. No direct + replacement exists, although in most cases + `ChoiceListInterface::getChoicesForValues()` and + `ChoiceListInterface::getValuesForChoices()` should be sufficient. + + * The interface `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface` + and all of its implementations were removed. Use the new interface + `Symfony\Component\Security\Csrf\CsrfTokenManagerInterface` instead. + + * The options "`csrf_provider`" and "`intention`" were renamed to "`csrf_token_generator`" + and "`csrf_token_id`". + + * The method `Form::getErrorsAsString()` was removed. Use `Form::getErrors()` + instead with the argument `$deep` set to true and `$flatten` set to false + and cast the returned iterator to a string (if not done implicitly by PHP). + + Before: + + ```php + echo $form->getErrorsAsString(); + ``` + + After: + + ```php + echo $form->getErrors(true, false); + ``` + + * The `Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList` class has been removed in + favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + + * The `Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList` class has been removed in + favor of `Symfony\Component\Form\ChoiceList\LazyChoiceList`. + + * The `Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList` class has been removed in + favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + + * The `Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList` class has been removed in + favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + + * The `TimezoneType::getTimezones()` method was removed. You should not use + this method. + + * The `Symfony\Component\Form\ChoiceList\ArrayKeyChoiceList` class has been removed in + favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`. + +### FrameworkBundle + + * The `config:debug`, `container:debug`, `router:debug`, `translation:debug` + and `yaml:lint` commands have been deprecated since Symfony 2.7 and will + be removed in Symfony 3.0. Use the `debug:config`, `debug:container`, + `debug:router`, `debug:translation` and `lint:yaml` commands instead. + + * The `getRequest` method of the base `Controller` class has been deprecated + since Symfony 2.4 and must be therefore removed in 3.0. The only reliable + way to get the `Request` object is to inject it in the action method. + + Before: + + ```php + namespace Acme\FooBundle\Controller; + + class DemoController + { + public function showAction() + { + $request = $this->getRequest(); + // ... + } + } + ``` + + After: + + ```php + namespace Acme\FooBundle\Controller; + + use Symfony\Component\HttpFoundation\Request; + + class DemoController + { + public function showAction(Request $request) + { + // ... + } + } + ``` + + * In Symfony 2.7 a small BC break was introduced with the new choices_as_values + option. In order to have the choice values populated to the html value attribute + you had to define the choice_value option. This is now not any more needed. + + Before: + + ```php + $form->add('status', 'choice', array( + 'choices' => array( + 'Enabled' => Status::ENABLED, + 'Disabled' => Status::DISABLED, + 'Ignored' => Status::IGNORED, + ), + // choices_as_values defaults to true in Symfony 3.0 + // and setting it to anything else is deprecated as of 3.0 + 'choices_as_values' => true, + // important if you rely on your option value attribute (e.g. for JavaScript) + // this will keep the same functionality as before + 'choice_value' => function ($choice) { + return $choice; + }, + )); + ``` + + After: + + ```php + $form->add('status', ChoiceType::class, array( + 'choices' => array( + 'Enabled' => Status::ENABLED, + 'Disabled' => Status::DISABLED, + 'Ignored' => Status::IGNORED, + ) + )); + ``` + + * The `request` service was removed. You must inject the `request_stack` + service instead. + + * The `templating.helper.assets` was removed in Symfony 3.0. You should + use the `assets.package` service instead. + + Before: + + ```php + use Symfony\Component\Templating\Helper\CoreAssetsHelper; + + class DemoService + { + private $assetsHelper; + + public function __construct(CoreAssetsHelper $assetsHelper) + { + $this->assetsHelper = $assetsHelper; + } + + public function testMethod() + { + return $this->assetsHelper->getUrl('thumbnail.png', null, $this->assetsHelper->getVersion()); + } + } + ``` + + After: + + ```php + use Symfony\Component\Asset\Packages; + + class DemoService + { + private $assetPackages; + + public function __construct(Packages $assetPackages) + { + $this->assetPackages = $assetPackages; + } + + public function testMethod() + { + return $this->assetPackages->getUrl('thumbnail.png').$this->assetPackages->getVersion(); + } + } + ``` + + * The `enctype` method of the `form` helper was removed. You should use the + new method `start` instead. + + Before: + + ```php +
    enctype($form) ?>> + ... +
    + ``` + + After: + + ```php + start($form) ?> + ... + end($form) ?> + ``` + + The method and action of the form default to "POST" and the current + document. If you want to change these values, you can set them explicitly in + the controller. + + Alternative 1: + + ```php + $form = $this->createForm('my_form', $formData, array( + 'method' => 'PUT', + 'action' => $this->generateUrl('target_route'), + )); + ``` + + Alternative 2: + + ```php + $form = $this->createFormBuilder($formData) + // ... + ->setMethod('PUT') + ->setAction($this->generateUrl('target_route')) + ->getForm(); + ``` + + It is also possible to override the method and the action in the template: + + ```php + start($form, array('method' => 'GET', 'action' => 'http://example.com')) ?> + ... + end($form) ?> + ``` + + * The `RouterApacheDumperCommand` was removed. + + * The `createEsi` method of `Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache` was removed. Use `createSurrogate` instead. + + * The `templating.helper.router` service was moved to `templating_php.xml`. You + have to ensure that the PHP templating engine is enabled to be able to use it: + + ```yaml + framework: + templating: + engines: ['php'] + ``` + + * The `form.csrf_provider` service is removed as it implements an adapter for + the new token manager to the deprecated + `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface` + interface. + The `security.csrf.token_manager` should be used instead. + +### HttpKernel + + * The `Symfony\Component\HttpKernel\Log\LoggerInterface` has been removed in + favor of `Psr\Log\LoggerInterface`. The only difference is that some method + names are different: + + | Old name | New name + | -------- | --- + | `emerg()` | `emergency()` + | `crit()` | `critical()` + | `err()` | `error()` + | `warn()` | `warning()` + + The previous method renames also happened to the following classes: + + * `Symfony\Bridge\Monolog\Logger` + * `Symfony\Component\HttpKernel\Log\NullLogger` + + * The `Symfony\Component\HttpKernel\Kernel::init()` method has been removed. + + * The following classes have been renamed as they have been moved to the + Debug component: + + | Old name | New name + | -------- | --- + | `Symfony\Component\HttpKernel\Debug\ErrorHandler` | `Symfony\Component\Debug\ErrorHandler` + | `Symfony\Component\HttpKernel\Debug\ExceptionHandler` | `Symfony\Component\Debug\ExceptionHandler` + | `Symfony\Component\HttpKernel\Exception\FatalErrorException` | `Symfony\Component\Debug\Exception\FatalErrorException` + | `Symfony\Component\HttpKernel\Exception\FlattenException` | `Symfony\Component\Debug\Exception\FlattenException` + + * The `Symfony\Component\HttpKernel\EventListener\ExceptionListener` now + passes the Request format as the `_format` argument instead of `format`. + + * The `Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass` has been renamed to + `Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass` and moved to the EventDispatcher component. + +### Locale + + * The Locale component was removed and replaced by the Intl component. + Instead of the methods in `Symfony\Component\Locale\Locale`, you should use + these equivalent methods in `Symfony\Component\Intl\Intl` now: + + | Old way | New way + | ------- | --- + | `Locale::getDisplayCountries()` | `Intl::getRegionBundle()->getCountryNames()` + | `Locale::getCountries()` | `array_keys(Intl::getRegionBundle()->getCountryNames())` + | `Locale::getDisplayLanguages()` | `Intl::getLanguageBundle()->getLanguageNames()` + | `Locale::getLanguages()` | `array_keys(Intl::getLanguageBundle()->getLanguageNames())` + | `Locale::getDisplayLocales()` | `Intl::getLocaleBundle()->getLocaleNames()` + | `Locale::getLocales()` | `array_keys(Intl::getLocaleBundle()->getLocaleNames())` + +### PropertyAccess + + * Renamed `PropertyAccess::getPropertyAccessor` to `createPropertyAccessor`. + + Before: + + ```php + use Symfony\Component\PropertyAccess\PropertyAccess; + + $accessor = PropertyAccess::getPropertyAccessor(); + ``` + + After: + + ```php + use Symfony\Component\PropertyAccess\PropertyAccess; + + $accessor = PropertyAccess::createPropertyAccessor(); + ``` + +### Routing + + * Some route settings have been renamed: + + * The `pattern` setting for a route has been deprecated in favor of `path` + * The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings + + Before: + + ```yaml + article_edit: + pattern: /article/{id} + requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' } + ``` + + ```xml + + POST|PUT + https + \d+ + + ``` + + ```php + $route = new Route(); + $route->setPattern('/article/{id}'); + $route->setRequirement('_method', 'POST|PUT'); + $route->setRequirement('_scheme', 'https'); + ``` + + After: + + ```yaml + article_edit: + path: /article/{id} + methods: [POST, PUT] + schemes: https + requirements: { 'id': '\d+' } + ``` + + ```xml + + \d+ + + ``` + + ```php + $route = new Route(); + $route->setPath('/article/{id}'); + $route->setMethods(array('POST', 'PUT')); + $route->setSchemes('https'); + ``` + + * The `ApacheMatcherDumper` and `ApacheUrlMatcher` were removed since + the performance gains were minimal and it's hard to replicate the behaviour + of PHP implementation. + +### Security + + * The `vote()` method from the `VoterInterface` was changed to now accept arbitrary + types and not only objects. You can rely on the new abstract `Voter` class introduced + in 2.8 to ease integrating your own voters. + + * The `Resources/` directory was moved to `Core/Resources/` + + * The `key` settings of `anonymous`, `remember_me` and `http_digest` are + renamed to `secret`. + + Before: + + ```yaml + security: + # ... + firewalls: + default: + # ... + anonymous: { key: "%secret%" } + remember_me: + key: "%secret%" + http_digest: + key: "%secret%" + ``` + + ```xml + + + + + + + + + + + + + ``` + + ```php + // ... + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'anonymous' => array('key' => '%secret%'), + 'remember_me' => array('key' => '%secret%'), + 'http_digest' => array('key' => '%secret%'), + ), + )); + ``` + + After: + + ```yaml + security: + # ... + firewalls: + default: + # ... + anonymous: { secret: "%secret%" } + remember_me: + secret: "%secret%" + http_digest: + secret: "%secret%" + ``` + + ```xml + + + + + + + + + + + + + ``` + + ```php + // ... + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'anonymous' => array('secret' => '%secret%'), + 'remember_me' => array('secret' => '%secret%'), + 'http_digest' => array('secret' => '%secret%'), + ), + )); + ``` + + * The `AbstractVoter::getSupportedAttributes()` and `AbstractVoter::getSupportedClasses()` + methods have been removed in favor of `AbstractVoter::supports()`. + + Before: + + ```php + class MyVoter extends AbstractVoter + { + protected function getSupportedAttributes() + { + return array('CREATE', 'EDIT'); + } + + protected function getSupportedClasses() + { + return array('AppBundle\Entity\Post'); + } + + // ... + } + ``` + + After: + + ```php + class MyVoter extends AbstractVoter + { + protected function supports($attribute, $object) + { + return $object instanceof Post && in_array($attribute, array('CREATE', 'EDIT')); + } + + // ... + } + ``` + + * The `AbstractVoter::isGranted()` method have been replaced by `AbstractVoter::voteOnAttribute()`. + + Before: + + ```php + class MyVoter extends AbstractVoter + { + protected function isGranted($attribute, $object, $user = null) + { + return 'EDIT' === $attribute && $user === $object->getAuthor(); + } + + // ... + } + ``` + + After: + + ```php + class MyVoter extends AbstractVoter + { + protected function voteOnAttribute($attribute, $object, TokenInterface $token) + { + return 'EDIT' === $attribute && $token->getUser() === $object->getAuthor(); + } + + // ... + } + ``` + + * The `supportsAttribute()` and `supportsClass()` methods of classes `AuthenticatedVoter`, `ExpressionVoter` + and `RoleVoter` have been removed. + +### Translator + + * The `Translator::setFallbackLocale()` method has been removed in favor of + `Translator::setFallbackLocales()`. + + * The visibility of the `locale` property has been changed from protected to private. Rely on `getLocale` and `setLocale` + instead. + + Before: + + ```php + class CustomTranslator extends Translator + { + public function fooMethod() + { + // get locale + $locale = $this->locale; + + // update locale + $this->locale = $locale; + } + } + ``` + + After: + + ```php + class CustomTranslator extends Translator + { + public function fooMethod() + { + // get locale + $locale = $this->getLocale(); + + // update locale + $this->setLocale($locale); + } + } + ``` + + * The method `FileDumper::format()` was removed. You should use + `FileDumper::formatCatalogue()` instead. + + Before: + + ```php + class CustomDumper extends FileDumper + { + protected function format(MessageCatalogue $messages, $domain) + { + ... + } + } + ``` + + After: + + ```php + class CustomDumper extends FileDumper + { + public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = array()) + { + ... + } + } + ``` + +### Twig Bridge + + * The `twig:lint` command has been deprecated since Symfony 2.7 and will be + removed in Symfony 3.0. Use the `lint:twig` command instead. + + * The `render` tag is deprecated in favor of the `render` function. + + * The `form_enctype` helper was removed. You should use the new `form_start` + function instead. + + Before: + + ```php +
    + ... +
    + ``` + + After: + + ```jinja + {{ form_start(form) }} + ... + {{ form_end(form) }} + ``` + + The method and action of the form default to "POST" and the current + document. If you want to change these values, you can set them explicitly in + the controller. + + Alternative 1: + + ```php + $form = $this->createForm('my_form', $formData, array( + 'method' => 'PUT', + 'action' => $this->generateUrl('target_route'), + )); + ``` + + Alternative 2: + + ```php + $form = $this->createFormBuilder($formData) + // ... + ->setMethod('PUT') + ->setAction($this->generateUrl('target_route')) + ->getForm(); + ``` + + It is also possible to override the method and the action in the template: + + ```jinja + {{ form_start(form, {'method': 'GET', 'action': 'http://example.com'}) }} + ... + {{ form_end(form) }} + ``` + +### TwigBundle + + * The `twig:debug` command has been deprecated since Symfony 2.7 and will be + removed in Symfony 3.0. Use the `debug:twig` command instead. + +### Validator + + * The class `Symfony\Component\Validator\Mapping\Cache\ApcCache` has been removed in favor + of `Symfony\Component\Validator\Mapping\Cache\DoctrineCache`. + + Before: + + ```php + use Symfony\Component\Validator\Mapping\Cache\ApcCache; + + $cache = new ApcCache('symfony.validator'); + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\Cache\DoctrineCache; + use Doctrine\Common\Cache\ApcCache; + + $apcCache = new ApcCache(); + $apcCache->setNamespace('symfony.validator'); + + $cache = new DoctrineCache($apcCache); + ``` + + * The constraints `Optional` and `Required` were moved to the + `Symfony\Component\Validator\Constraints\` namespace. You should adapt + the path wherever you used them. + + Before: + + ```php + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Assert\Collection({ + * "foo" = @Assert\Collection\Required(), + * "bar" = @Assert\Collection\Optional(), + * }) + */ + private $property; + ``` + + After: + + ```php + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Assert\Collection({ + * "foo" = @Assert\Required(), + * "bar" = @Assert\Optional(), + * }) + */ + private $property; + ``` + + * The option "`methods`" of the `Callback` constraint was removed. You should + use the option "`callback`" instead. If you have multiple callbacks, add + multiple callback constraints instead. + + Before (YAML): + + ```yaml + constraints: + - Callback: [firstCallback, secondCallback] + ``` + + After (YAML): + + ```yaml + constraints: + - Callback: firstCallback + - Callback: secondCallback + ``` + + When using annotations, you can now put the `Callback` constraint directly on + the method that should be executed. + + Before (Annotations): + + ```php + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\ExecutionContextInterface; + + /** + * @Assert\Callback({"callback"}) + */ + class MyClass + { + public function callback(ExecutionContextInterface $context) + { + // ... + } + } + ``` + + After (Annotations): + + ```php + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\ExecutionContextInterface; + + class MyClass + { + /** + * @Assert\Callback + */ + public function callback(ExecutionContextInterface $context) + { + // ... + } + } + ``` + + * The interface `ValidatorInterface` was replaced by the more powerful + interface `Validator\ValidatorInterface`. The signature of the `validate()` + method is slightly different in that interface and accepts a value, zero + or more constraints and validation group. It replaces both + `validate()` and `validateValue()` in the previous interface. + + Before: + + ```php + $validator->validate($object, 'Strict'); + + $validator->validateValue($value, new NotNull()); + ``` + + After: + + ```php + $validator->validate($object, null, 'Strict'); + + $validator->validate($value, new NotNull()); + ``` + + Apart from this change, the new methods `startContext()` and `inContext()` + were added. The first of them allows to run multiple validations in the + same context and aggregate their violations: + + ```php + $violations = $validator->startContext() + ->atPath('firstName')->validate($firstName, new NotNull()) + ->atPath('age')->validate($age, new Type('integer')) + ->getViolations(); + ``` + + The second allows to run validation in an existing context. This is + especially useful when calling the validator from within constraint + validators: + + ```php + $validator->inContext($context)->validate($object); + ``` + + Instead of a `Validator`, the validator builder now returns a + `Validator\RecursiveValidator` instead. + + * The interface `ValidationVisitorInterface` and its implementation + `ValidationVisitor` were removed. The implementation of the visitor pattern + was flawed. Fixing that implementation would have drastically slowed down + the validator execution, so the visitor was removed completely instead. + + Along with the visitor, the method `accept()` was removed from + `MetadataInterface`. + + * The interface `MetadataInterface` was moved to the `Mapping` namespace. + + Before: + + ```php + use Symfony\Component\Validator\MetadataInterface; + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\MetadataInterface; + ``` + + The methods `getCascadingStrategy()` and `getTraversalStrategy()` were + added to the interface. The first method should return a bit mask of the + constants in class `CascadingStrategy`. The second should return a bit + mask of the constants in `TraversalStrategy`. + + Example: + + ```php + use Symfony\Component\Validator\Mapping\TraversalStrategy; + + public function getTraversalStrategy() + { + return TraversalStrategy::TRAVERSE; + } + ``` + + * The interface `PropertyMetadataInterface` was moved to the `Mapping` + namespace. + + Before: + + ```php + use Symfony\Component\Validator\PropertyMetadataInterface; + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; + ``` + + * The interface `PropertyMetadataContainerInterface` was moved to the `Mapping` + namespace and renamed to `ClassMetadataInterface`. + + Before: + + ```php + use Symfony\Component\Validator\PropertyMetadataContainerInterface; + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\ClassMetadataInterface; + ``` + + The interface now contains four additional methods: + + * `getConstrainedProperties()` + * `hasGroupSequence()` + * `getGroupSequence()` + * `isGroupSequenceProvider()` + + See the inline documentation of these methods for more information. + + * The interface `ClassBasedInterface` was removed. You should use + `Mapping\ClassMetadataInterface` instead: + + Before: + + ```php + use Symfony\Component\Validator\ClassBasedInterface; + + class MyClassMetadata implements ClassBasedInterface + { + // ... + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\ClassMetadataInterface; + + class MyClassMetadata implements ClassMetadataInterface + { + // ... + } + ``` + + * The class `ElementMetadata` was renamed to `GenericMetadata`. + + Before: + + ```php + use Symfony\Component\Validator\Mapping\ElementMetadata; + + class MyMetadata extends ElementMetadata + { + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\GenericMetadata; + + class MyMetadata extends GenericMetadata + { + } + ``` + + * The interface `ExecutionContextInterface` and its implementation + `ExecutionContext` were moved to the `Context` namespace. + + Before: + + ```php + use Symfony\Component\Validator\ExecutionContextInterface; + ``` + + After: + + ```php + use Symfony\Component\Validator\Context\ExecutionContextInterface; + ``` + + The interface now contains the following additional methods: + + * `getValidator()` + * `getObject()` + * `setNode()` + * `setGroup()` + * `markGroupAsValidated()` + * `isGroupValidated()` + * `markConstraintAsValidated()` + * `isConstraintValidated()` + + See the inline documentation of these methods for more information. + + The method `addViolationAt()` was removed. You should use `buildViolation()` + instead. + + Before: + + ```php + $context->addViolationAt('property', 'The value {{ value }} is invalid.', array( + '{{ value }}' => $invalidValue, + )); + ``` + + After: + + ```php + $context->buildViolation('The value {{ value }} is invalid.') + ->atPath('property') + ->setParameter('{{ value }}', $invalidValue) + ->addViolation(); + ``` + + The methods `validate()` and `validateValue()` were removed. You should use + `getValidator()` together with `inContext()` instead. + + Before: + + ```php + $context->validate($object); + ``` + + After: + + ```php + $context->getValidator() + ->inContext($context) + ->validate($object); + ``` + + The parameters `$invalidValue`, `$plural` and `$code` were removed from + `addViolation()`. You should use `buildViolation()` instead. See above for + an example. + + The method `getMetadataFactory()` was removed. You can use `getValidator()` + instead and use the methods `getMetadataFor()` or `hasMetadataFor()` on the + validator instance. + + Before: + + ```php + $metadata = $context->getMetadataFactory()->getMetadataFor($myClass); + ``` + + After: + + ```php + $metadata = $context->getValidator()->getMetadataFor($myClass); + ``` + + * The interface `GlobalExecutionContextInterface` was removed. Most of the + information provided by that interface can be queried from + `Context\ExecutionContextInterface` instead. + + * The interface `MetadataFactoryInterface` was moved to the `Mapping\Factory` + namespace along with its implementations `BlackholeMetadataFactory` and + `ClassMetadataFactory`. These classes were furthermore renamed to + `BlackHoleMetadataFactory` and `LazyLoadingMetadataFactory`. + + Before: + + ```php + use Symfony\Component\Validator\Mapping\ClassMetadataFactory; + + $factory = new ClassMetadataFactory($loader); + ``` + + After: + + ```php + use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; + + $factory = new LazyLoadingMetadataFactory($loader); + ``` + + * The option `$deep` was removed from the constraint `Valid`. When traversing + arrays, nested arrays are always traversed (same behavior as before). When + traversing nested objects, their traversal strategy is used. + + * The method `ValidatorBuilder::setPropertyAccessor()` was removed. The + validator now functions without a property accessor. + + * The methods `getMessageParameters()` and `getMessagePluralization()` in + `ConstraintViolation` were renamed to `getParameters()` and `getPlural()`. + + Before: + + ```php + $parameters = $violation->getMessageParameters(); + $plural = $violation->getMessagePluralization(); + ``` + + After: + + ```php + $parameters = $violation->getParameters(); + $plural = $violation->getPlural(); + ``` + + * The class `Symfony\Component\Validator\DefaultTranslator` was removed. You + should use `Symfony\Component\Translation\IdentityTranslator` instead. + + Before: + + ```php + $translator = new \Symfony\Component\Validator\DefaultTranslator(); + ``` + + After: + + ```php + $translator = new \Symfony\Component\Translation\IdentityTranslator(); + $translator->setLocale('en'); + ``` + +### Yaml + + * Using a colon in an unquoted mapping value leads to a `ParseException`. + * Starting an unquoted string with `@`, `` ` ``, `|`, or `>` leads to a `ParseException`. + * When surrounding strings with double-quotes, you must now escape `\` characters. Not + escaping those characters (when surrounded by double-quotes) leads to a `ParseException`. + + Before: + + ```yml + class: "Foo\Var" + ``` + + After: + + ```yml + class: "Foo\\Var" + ``` + + + * The ability to pass file names to `Yaml::parse()` has been removed. + + Before: + + ```php + Yaml::parse($fileName); + ``` + + After: + + ```php + Yaml::parse(file_get_contents($fileName)); + ``` + +### Process + + * `Process::setStdin()` and `Process::getStdin()` have been removed. Use + `Process::setInput()` and `Process::getInput()` that works the same way. + * `Process::setInput()` and `ProcessBuilder::setInput()` do not accept non-scalar types. + +### Monolog Bridge + + * `Symfony\Bridge\Monolog\Logger::emerg()` was removed. Use `emergency()` which is PSR-3 compatible. + * `Symfony\Bridge\Monolog\Logger::crit()` was removed. Use `critical()` which is PSR-3 compatible. + * `Symfony\Bridge\Monolog\Logger::err()` was removed. Use `error()` which is PSR-3 compatible. + * `Symfony\Bridge\Monolog\Logger::warn()` was removed. Use `warning()` which is PSR-3 compatible. + +### Swiftmailer Bridge + + * `Symfony\Bridge\Swiftmailer\DataCollector\MessageDataCollector` was removed. Use the `Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector` class instead. + +### HttpFoundation + +* `Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface` no longer implements the `IteratorAggregate` interface. Use the `all()` method instead of iterating over the flash bag. + +### Config + + * `\Symfony\Component\Config\Resource\ResourceInterface::isFresh()` has been removed. Also, + cache validation through this method (which was still supported in 2.8 for BC) does no longer + work because the `\Symfony\Component\Config\Resource\BCResourceInterfaceChecker` helper class + has been removed as well. diff --git a/vendor/symfony/symfony/appveyor.yml b/vendor/symfony/symfony/appveyor.yml new file mode 100644 index 0000000..de3093f --- /dev/null +++ b/vendor/symfony/symfony/appveyor.yml @@ -0,0 +1,61 @@ +build: false +shallow_clone: true +platform: x86 +clone_folder: c:\projects\symfony + +cache: + - c:\php -> appveyor.yml + - .phpunit -> phpunit + +init: + - SET PATH=c:\php;%PATH% + - SET COMPOSER_NO_INTERACTION=1 + - SET SYMFONY_DEPRECATIONS_HELPER=strict + - SET PHP=1 + - SET ANSICON=121x90 (121x90) + - SET SYMFONY_PHPUNIT_SKIPPED_TESTS=phpunit.skipped + +install: + - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) + - cd c:\php + - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.5.9-nts-Win32-VC11-x86.zip + - IF %PHP%==1 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y >nul + - IF %PHP%==1 del /Q *.zip + - IF %PHP%==1 cd ext + - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.10/php_apcu-4.0.10-5.5-nts-vc11-x86.zip + - IF %PHP%==1 7z x php_apcu-4.0.10-5.5-nts-vc11-x86.zip -y >nul + - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/memcache/3.0.8/php_memcache-3.0.8-5.5-nts-vc11-x86.zip + - IF %PHP%==1 7z x php_memcache-3.0.8-5.5-nts-vc11-x86.zip -y >nul + - IF %PHP%==1 del /Q *.zip + - IF %PHP%==1 cd .. + - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat + - IF %PHP%==1 copy /Y php.ini-development php.ini-min + - IF %PHP%==1 echo max_execution_time=1200 >> php.ini-min + - IF %PHP%==1 echo date.timezone="UTC" >> php.ini-min + - IF %PHP%==1 echo extension_dir=ext >> php.ini-min + - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini-min + - IF %PHP%==1 copy /Y php.ini-min php.ini-max + - IF %PHP%==1 echo extension=php_apcu.dll >> php.ini-max + - IF %PHP%==1 echo apc.enable_cli=1 >> php.ini-max + - IF %PHP%==1 echo extension=php_memcache.dll >> php.ini-max + - IF %PHP%==1 echo extension=php_intl.dll >> php.ini-max + - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini-max + - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini-max + - IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini-max + - IF %PHP%==1 echo extension=php_ldap.dll >> php.ini-max + - appveyor DownloadFile https://getcomposer.org/composer.phar + - copy /Y php.ini-max php.ini + - cd c:\projects\symfony + - php phpunit install + - IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev) + - composer update --prefer-source --no-progress --ansi + +test_script: + - cd c:\projects\symfony + - Setlocal EnableDelayedExpansion + - SET X=0 + - copy /Y c:\php\php.ini-min c:\php\php.ini + - php phpunit symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! + - copy /Y c:\php\php.ini-max c:\php\php.ini + - php phpunit symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! + - exit %X% diff --git a/vendor/symfony/symfony/composer.json b/vendor/symfony/symfony/composer.json new file mode 100644 index 0000000..da34246 --- /dev/null +++ b/vendor/symfony/symfony/composer.json @@ -0,0 +1,112 @@ +{ + "name": "symfony/symfony", + "type": "library", + "description": "The Symfony PHP framework", + "keywords": ["framework"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "doctrine/common": "~2.4", + "twig/twig": "~1.23|~2.0", + "psr/log": "~1.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0" + }, + "replace": { + "symfony/asset": "self.version", + "symfony/browser-kit": "self.version", + "symfony/class-loader": "self.version", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/css-selector": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/debug": "self.version", + "symfony/debug-bundle": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/expression-language": "self.version", + "symfony/filesystem": "self.version", + "symfony/finder": "self.version", + "symfony/form": "self.version", + "symfony/framework-bundle": "self.version", + "symfony/http-foundation": "self.version", + "symfony/http-kernel": "self.version", + "symfony/intl": "self.version", + "symfony/ldap": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/property-access": "self.version", + "symfony/property-info": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-core": "self.version", + "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", + "symfony/security-http": "self.version", + "symfony/security-bundle": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/templating": "self.version", + "symfony/translation": "self.version", + "symfony/twig-bridge": "self.version", + "symfony/twig-bundle": "self.version", + "symfony/validator": "self.version", + "symfony/var-dumper": "self.version", + "symfony/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.4", + "doctrine/orm": "~2.4,>=2.4.5", + "doctrine/doctrine-bundle": "~1.4", + "monolog/monolog": "~1.11", + "ocramius/proxy-manager": "~0.4|~1.0", + "egulias/email-validator": "~1.2", + "symfony/security-acl": "~2.8|~3.0", + "phpdocumentor/reflection": "^1.0.7" + }, + "conflict": { + "phpdocumentor/reflection": "<1.0.7" + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", + "Symfony\\Bundle\\": "src/Symfony/Bundle/", + "Symfony\\Component\\": "src/Symfony/Component/" + }, + "classmap": [ + "src/Symfony/Component/Intl/Resources/stubs" + ], + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/phpunit b/vendor/symfony/symfony/phpunit new file mode 100755 index 0000000..146ab9d --- /dev/null +++ b/vendor/symfony/symfony/phpunit @@ -0,0 +1,196 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// Please update when phpunit needs to be reinstalled with fresh deps: +// Cache-Id-Version: 2015-11-28 09:05 UTC + +use Symfony\Component\Process\ProcessUtils; + +error_reporting(-1); +require __DIR__.'/src/Symfony/Component/Process/ProcessUtils.php'; + +// PHPUnit 4.8 does not support PHP 7, while 5.1 requires PHP 5.6+ +$PHPUNIT_VERSION = PHP_VERSION_ID >= 50600 ? '5.1' : '4.8'; +$PHPUNIT_DIR = __DIR__.'/.phpunit'; +$PHP = defined('PHP_BINARY') ? PHP_BINARY : 'php'; +$PHP = ProcessUtils::escapeArgument($PHP); +if ('phpdbg' === PHP_SAPI) { + $PHP .= ' -qrr'; +} + +$COMPOSER = file_exists($COMPOSER = __DIR__.'/composer.phar') || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? `where.exe composer.phar` : `which composer.phar`)) + ? $PHP.' '.ProcessUtils::escapeArgument($COMPOSER) + : 'composer'; + +if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__) !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION.md5")) { + // Build a standalone phpunit without symfony/yaml + + $oldPwd = getcwd(); + @mkdir($PHPUNIT_DIR); + chdir($PHPUNIT_DIR); + if (file_exists("phpunit-$PHPUNIT_VERSION")) { + passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION")); + } + if (extension_loaded('openssl') && ini_get('allow_url_fopen')) { + stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb')); + } else { + @unlink("$PHPUNIT_VERSION.zip"); + passthru("wget https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip"); + } + $zip = new ZipArchive(); + $zip->open("$PHPUNIT_VERSION.zip"); + $zip->extractTo(getcwd()); + $zip->close(); + chdir("phpunit-$PHPUNIT_VERSION"); + passthru("$COMPOSER remove --no-update symfony/yaml"); + passthru("$COMPOSER require --dev --no-update symfony/phpunit-bridge \">=2.8@dev\""); + passthru("$COMPOSER install --prefer-source --no-progress --ansi"); + file_put_contents('phpunit', <<setMaxDepth(3); + + $skippedTests = isset($_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS']) ? $_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS'] : false; + $runningProcs = array(); + + foreach ($finder as $file => $fileInfo) { + if ('phpunit.xml.dist' === $file) { + $component = dirname($fileInfo->getPathname()); + + // Run phpunit tests in parallel + + if ($skippedTests) { + putenv("SYMFONY_PHPUNIT_SKIPPED_TESTS=$component/$skippedTests"); + } + + $c = ProcessUtils::escapeArgument($component); + + if ($proc = proc_open(sprintf($cmd, $c, " > $c/phpunit.stdout 2> $c/phpunit.stderr"), array(), $pipes)) { + $runningProcs[$component] = $proc; + } else { + $exit = 1; + echo "\033[41mKO\033[0m $component\n\n"; + } + } + } + chdir($oldPwd); + + // Fixes for colors support on appveyor + // See https://github.com/appveyor/ci/issues/373 + $colorFixes = array( + array("S\033[0m\033[0m\033[36m\033[1mS", "E\033[0m\033[0m\033[31m\033[1mE", "I\033[0m\033[0m\033[33m\033[1mI", "F\033[0m\033[0m\033[41m\033[37mF"), + array("SS", "EE", "II", "FF"), + ); + $colorFixes[0] = array_merge($colorFixes[0], $colorFixes[0]); + $colorFixes[1] = array_merge($colorFixes[1], $colorFixes[1]); + + while ($runningProcs) { + usleep(300000); + $terminatedProcs = array(); + foreach ($runningProcs as $component => $proc) { + $procStatus = proc_get_status($proc); + if (!$procStatus['running']) { + $terminatedProcs[$component] = $procStatus['exitcode']; + unset($runningProcs[$component]); + proc_close($proc); + } + } + + foreach ($terminatedProcs as $component => $procStatus) { + foreach (array('out', 'err') as $file) { + $file = "$component/phpunit.std$file"; + + if ('\\' === DIRECTORY_SEPARATOR) { + $h = fopen($file, 'rb'); + while (false !== $line = fgets($h)) { + echo str_replace($colorFixes[0], $colorFixes[1], preg_replace( + '/(\033\[[0-9]++);([0-9]++m)(?:(.)(\033\[0m))?/', + "$1m\033[$2$3$4$4", + $line + )); + } + fclose($h); + } else { + readfile($file); + } + unlink($file); + } + + // Fail on any individual component failures but ignore STATUS_STACK_BUFFER_OVERRUN (-1073740791) on Windows when APCu is enabled + if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !ini_get('apc.enable_cli') || -1073740791 !== $procStatus)) { + $exit = $procStatus; + echo "\033[41mKO\033[0m $component\n\n"; + } else { + echo "\033[32mOK\033[0m $component\n\n"; + } + } + } +} elseif (!isset($argv[1]) || 'install' !== $argv[1]) { + // Run regular phpunit in a subprocess + + $errFile = tempnam(sys_get_temp_dir(), 'phpunit.stderr.'); + if ($proc = proc_open(sprintf($cmd, '', ' 2> '.ProcessUtils::escapeArgument($errFile)), array(1 => array('pipe', 'w')), $pipes)) { + stream_copy_to_stream($pipes[1], STDOUT); + fclose($pipes[1]); + $exit = proc_close($proc); + + readfile($errFile); + unlink($errFile); + } + + if (!file_exists($component = array_pop($argv))) { + $component = basename(getcwd()); + } + + if ($exit) { + echo "\033[41mKO\033[0m $component\n\n"; + } else { + echo "\033[32mOK\033[0m $component\n\n"; + } +} + +exit($exit); diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CHANGELOG.md new file mode 100644 index 0000000..c95a194 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -0,0 +1,43 @@ +CHANGELOG +========= + +3.0.0 +----- + + * removed `EntityChoiceList` + * removed `$manager` (2nd) and `$class` (3th) arguments of `ORMQueryBuilderLoader` + * removed passing a query builder closure to `ORMQueryBuilderLoader` + * removed `loader` and `property` options of the `DoctrineType` + +2.7.0 +----- + + * added DoctrineChoiceLoader + * deprecated EntityChoiceList + * deprecated passing a query builder closure to ORMQueryBuilderLoader + * deprecated $manager and $em arguments of ORMQueryBuilderLoader + * added optional arguments $propertyAccessor and $choiceListFactory to DoctrineOrmExtension constructor + * deprecated "loader" and "property" options of DoctrineType + +2.4.0 +----- + + * deprecated DoctrineOrmTestCase class + +2.2.0 +----- + + * added an optional PropertyAccessorInterface parameter to DoctrineType, + EntityType and EntityChoiceList + +2.1.0 +----- + + * added a default implementation of the ManagerRegistry + * added a session storage for Doctrine DBAL + * DoctrineOrmTypeGuesser now guesses "collection" for array Doctrine type + * DoctrineType now caches its choice lists in order to improve performance + * DoctrineType now uses ManagerRegistry::getManagerForClass() if the option "em" is not set + * UniqueEntity validation constraint now accepts a "repositoryMethod" option that will be used to check for uniqueness instead of the default "findBy" + * [BC BREAK] the DbalLogger::log() visibility has been changed from public to + protected diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php new file mode 100644 index 0000000..b1e4f6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\CacheWarmer; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +/** + * The proxy generator cache warmer generates all entity proxies. + * + * In the process of generating proxies the cache for all the metadata is primed also, + * since this information is necessary to build the proxies in the first place. + * + * @author Benjamin Eberlei + */ +class ProxyCacheWarmer implements CacheWarmerInterface +{ + private $registry; + + /** + * Constructor. + * + * @param ManagerRegistry $registry A ManagerRegistry instance + */ + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + /** + * This cache warmer is not optional, without proxies fatal error occurs! + * + * @return false + */ + public function isOptional() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + foreach ($this->registry->getManagers() as $em) { + // we need the directory no matter the proxy cache generation strategy + if (!is_dir($proxyCacheDir = $em->getConfiguration()->getProxyDir())) { + if (false === @mkdir($proxyCacheDir, 0777, true)) { + throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory "%s".', $proxyCacheDir)); + } + } elseif (!is_writable($proxyCacheDir)) { + throw new \RuntimeException(sprintf('The Doctrine Proxy directory "%s" is not writeable for the current system user.', $proxyCacheDir)); + } + + // if proxies are autogenerated we don't need to generate them in the cache warmer + if ($em->getConfiguration()->getAutoGenerateProxyClasses()) { + continue; + } + + $classes = $em->getMetadataFactory()->getAllMetadata(); + + $em->getProxyFactory()->generateProxyClasses($classes); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php new file mode 100644 index 0000000..5d07cee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine; + +use Doctrine\Common\EventArgs; +use Doctrine\Common\EventManager; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Allows lazy loading of listener services. + * + * @author Johannes M. Schmitt + */ +class ContainerAwareEventManager extends EventManager +{ + /** + * Map of registered listeners. + * + * => + * + * @var array + */ + private $listeners = array(); + private $initialized = array(); + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of the event is + * the name of the method that is invoked on listeners. + * @param EventArgs $eventArgs The event arguments to pass to the event handlers/listeners. + * If not supplied, the single empty EventArgs instance is used. + * + * @return bool + */ + public function dispatchEvent($eventName, EventArgs $eventArgs = null) + { + if (isset($this->listeners[$eventName])) { + $eventArgs = null === $eventArgs ? EventArgs::getEmptyInstance() : $eventArgs; + + $initialized = isset($this->initialized[$eventName]); + + foreach ($this->listeners[$eventName] as $hash => $listener) { + if (!$initialized && is_string($listener)) { + $this->listeners[$eventName][$hash] = $listener = $this->container->get($listener); + } + + $listener->$eventName($eventArgs); + } + $this->initialized[$eventName] = true; + } + } + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $event The name of the event. + * + * @return array The event listeners for the specified event, or all event listeners. + */ + public function getListeners($event = null) + { + return $event ? $this->listeners[$event] : $this->listeners; + } + + /** + * Checks whether an event has any registered listeners. + * + * @param string $event + * + * @return bool TRUE if the specified event has any listeners, FALSE otherwise. + */ + public function hasListeners($event) + { + return isset($this->listeners[$event]) && $this->listeners[$event]; + } + + /** + * Adds an event listener that listens on the specified events. + * + * @param string|array $events The event(s) to listen on. + * @param object|string $listener The listener object. + * + * @throws \RuntimeException + */ + public function addEventListener($events, $listener) + { + if (is_string($listener)) { + if ($this->initialized) { + throw new \RuntimeException('Adding lazy-loading listeners after construction is not supported.'); + } + + $hash = '_service_'.$listener; + } else { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + } + + foreach ((array) $events as $event) { + // Overrides listener if a previous one was associated already + // Prevents duplicate listeners on same event (same instance only) + $this->listeners[$event][$hash] = $listener; + } + } + + /** + * Removes an event listener from the specified events. + * + * @param string|array $events + * @param object|string $listener + */ + public function removeEventListener($events, $listener) + { + if (is_string($listener)) { + $hash = '_service_'.$listener; + } else { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + } + + foreach ((array) $events as $event) { + // Check if actually have this listener associated + if (isset($this->listeners[$event][$hash])) { + unset($this->listeners[$event][$hash]); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php new file mode 100644 index 0000000..a57b9ae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DataCollector; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\DBAL\Logging\DebugStack; +use Doctrine\DBAL\Types\Type; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * DoctrineDataCollector. + * + * @author Fabien Potencier + */ +class DoctrineDataCollector extends DataCollector +{ + private $registry; + private $connections; + private $managers; + private $loggers = array(); + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + $this->connections = $registry->getConnectionNames(); + $this->managers = $registry->getManagerNames(); + } + + /** + * Adds the stack logger for a connection. + * + * @param string $name + * @param DebugStack $logger + */ + public function addLogger($name, DebugStack $logger) + { + $this->loggers[$name] = $logger; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $queries = array(); + foreach ($this->loggers as $name => $logger) { + $queries[$name] = $this->sanitizeQueries($name, $logger->queries); + } + + $this->data = array( + 'queries' => $queries, + 'connections' => $this->connections, + 'managers' => $this->managers, + ); + } + + public function getManagers() + { + return $this->data['managers']; + } + + public function getConnections() + { + return $this->data['connections']; + } + + public function getQueryCount() + { + return array_sum(array_map('count', $this->data['queries'])); + } + + public function getQueries() + { + return $this->data['queries']; + } + + public function getTime() + { + $time = 0; + foreach ($this->data['queries'] as $queries) { + foreach ($queries as $query) { + $time += $query['executionMS']; + } + } + + return $time; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'db'; + } + + private function sanitizeQueries($connectionName, $queries) + { + foreach ($queries as $i => $query) { + $queries[$i] = $this->sanitizeQuery($connectionName, $query); + } + + return $queries; + } + + private function sanitizeQuery($connectionName, $query) + { + $query['explainable'] = true; + if (null === $query['params']) { + $query['params'] = array(); + } + if (!is_array($query['params'])) { + $query['params'] = array($query['params']); + } + foreach ($query['params'] as $j => $param) { + if (isset($query['types'][$j])) { + // Transform the param according to the type + $type = $query['types'][$j]; + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $query['types'][$j] = $type->getBindingType(); + $param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform()); + } + } + + list($query['params'][$j], $explainable) = $this->sanitizeParam($param); + if (!$explainable) { + $query['explainable'] = false; + } + } + + return $query; + } + + /** + * Sanitizes a param. + * + * The return value is an array with the sanitized value and a boolean + * indicating if the original value was kept (allowing to use the sanitized + * value to explain the query). + * + * @param mixed $var + * + * @return array + */ + private function sanitizeParam($var) + { + if (is_object($var)) { + return array(sprintf('Object(%s)', get_class($var)), false); + } + + if (is_array($var)) { + $a = array(); + $original = true; + foreach ($var as $k => $v) { + list($value, $orig) = $this->sanitizeParam($v); + $original = $original && $orig; + $a[$k] = $value; + } + + return array($a, $original); + } + + if (is_resource($var)) { + return array(sprintf('Resource(%s)', get_resource_type($var)), false); + } + + return array($var, true); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php new file mode 100644 index 0000000..0b10527 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DataFixtures; + +use Doctrine\Common\DataFixtures\FixtureInterface; +use Doctrine\Common\DataFixtures\Loader; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Doctrine data fixtures loader that injects the service container into + * fixture objects that implement ContainerAwareInterface. + * + * Note: Use of this class requires the Doctrine data fixtures extension, which + * is a suggested dependency for Symfony. + */ +class ContainerAwareLoader extends Loader +{ + /** + * @var ContainerInterface + */ + private $container; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function addFixture(FixtureInterface $fixture) + { + if ($fixture instanceof ContainerAwareInterface) { + $fixture->setContainer($this->container); + } + + parent::addFixture($fixture); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php new file mode 100644 index 0000000..063b935 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -0,0 +1,493 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Config\Resource\FileResource; + +/** + * This abstract classes groups common code that Doctrine Object Manager extensions (ORM, MongoDB, CouchDB) need. + * + * @author Benjamin Eberlei + */ +abstract class AbstractDoctrineExtension extends Extension +{ + /** + * Used inside metadata driver method to simplify aggregation of data. + * + * @var array + */ + protected $aliasMap = array(); + + /** + * Used inside metadata driver method to simplify aggregation of data. + * + * @var array + */ + protected $drivers = array(); + + /** + * @param array $objectManager A configured object manager. + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @throws \InvalidArgumentException + */ + protected function loadMappingInformation(array $objectManager, ContainerBuilder $container) + { + if ($objectManager['auto_mapping']) { + // automatically register bundle mappings + foreach (array_keys($container->getParameter('kernel.bundles')) as $bundle) { + if (!isset($objectManager['mappings'][$bundle])) { + $objectManager['mappings'][$bundle] = array( + 'mapping' => true, + 'is_bundle' => true, + ); + } + } + } + + foreach ($objectManager['mappings'] as $mappingName => $mappingConfig) { + if (null !== $mappingConfig && false === $mappingConfig['mapping']) { + continue; + } + + $mappingConfig = array_replace(array( + 'dir' => false, + 'type' => false, + 'prefix' => false, + ), (array) $mappingConfig); + + $mappingConfig['dir'] = $container->getParameterBag()->resolveValue($mappingConfig['dir']); + // a bundle configuration is detected by realizing that the specified dir is not absolute and existing + if (!isset($mappingConfig['is_bundle'])) { + $mappingConfig['is_bundle'] = !is_dir($mappingConfig['dir']); + } + + if ($mappingConfig['is_bundle']) { + $bundle = null; + foreach ($container->getParameter('kernel.bundles') as $name => $class) { + if ($mappingName === $name) { + $bundle = new \ReflectionClass($class); + + break; + } + } + + if (null === $bundle) { + throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.', $mappingName)); + } + + $mappingConfig = $this->getMappingDriverBundleConfigDefaults($mappingConfig, $bundle, $container); + if (!$mappingConfig) { + continue; + } + } + + $this->assertValidMappingConfiguration($mappingConfig, $objectManager['name']); + $this->setMappingDriverConfig($mappingConfig, $mappingName); + $this->setMappingDriverAlias($mappingConfig, $mappingName); + } + } + + /** + * Register the alias for this mapping driver. + * + * Aliases can be used in the Query languages of all the Doctrine object managers to simplify writing tasks. + * + * @param array $mappingConfig + * @param string $mappingName + */ + protected function setMappingDriverAlias($mappingConfig, $mappingName) + { + if (isset($mappingConfig['alias'])) { + $this->aliasMap[$mappingConfig['alias']] = $mappingConfig['prefix']; + } else { + $this->aliasMap[$mappingName] = $mappingConfig['prefix']; + } + } + + /** + * Register the mapping driver configuration for later use with the object managers metadata driver chain. + * + * @param array $mappingConfig + * @param string $mappingName + * + * @throws \InvalidArgumentException + */ + protected function setMappingDriverConfig(array $mappingConfig, $mappingName) + { + $mappingDirectory = $mappingConfig['dir']; + if (!is_dir($mappingDirectory)) { + throw new \InvalidArgumentException(sprintf('Invalid Doctrine mapping path given. Cannot load Doctrine mapping/bundle named "%s".', $mappingName)); + } + + if (substr($mappingDirectory, 0, 7) !== 'phar://') { + $mappingDirectory = realpath($mappingDirectory); + } + $this->drivers[$mappingConfig['type']][$mappingConfig['prefix']] = $mappingDirectory; + } + + /** + * If this is a bundle controlled mapping all the missing information can be autodetected by this method. + * + * Returns false when autodetection failed, an array of the completed information otherwise. + * + * @param array $bundleConfig + * @param \ReflectionClass $bundle + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @return array|false + */ + protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundle, ContainerBuilder $container) + { + $bundleDir = dirname($bundle->getFilename()); + + if (!$bundleConfig['type']) { + $bundleConfig['type'] = $this->detectMetadataDriver($bundleDir, $container); + } + + if (!$bundleConfig['type']) { + // skip this bundle, no mapping information was found. + return false; + } + + if (!$bundleConfig['dir']) { + if (in_array($bundleConfig['type'], array('annotation', 'staticphp'))) { + $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName(); + } else { + $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory(); + } + } else { + $bundleConfig['dir'] = $bundleDir.'/'.$bundleConfig['dir']; + } + + if (!$bundleConfig['prefix']) { + $bundleConfig['prefix'] = $bundle->getNamespaceName().'\\'.$this->getMappingObjectDefaultName(); + } + + return $bundleConfig; + } + + /** + * Register all the collected mapping information with the object manager by registering the appropriate mapping drivers. + * + * @param array $objectManager + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function registerMappingDrivers($objectManager, ContainerBuilder $container) + { + // configure metadata driver for each bundle based on the type of mapping files found + if ($container->hasDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'))) { + $chainDriverDef = $container->getDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver')); + } else { + $chainDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.driver_chain.class%')); + $chainDriverDef->setPublic(false); + } + + foreach ($this->drivers as $driverType => $driverPaths) { + $mappingService = $this->getObjectManagerElementName($objectManager['name'].'_'.$driverType.'_metadata_driver'); + if ($container->hasDefinition($mappingService)) { + $mappingDriverDef = $container->getDefinition($mappingService); + $args = $mappingDriverDef->getArguments(); + if ($driverType == 'annotation') { + $args[1] = array_merge(array_values($driverPaths), $args[1]); + } else { + $args[0] = array_merge(array_values($driverPaths), $args[0]); + } + $mappingDriverDef->setArguments($args); + } elseif ($driverType == 'annotation') { + $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array( + new Reference($this->getObjectManagerElementName('metadata.annotation_reader')), + array_values($driverPaths), + )); + } else { + $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array( + array_values($driverPaths), + )); + } + $mappingDriverDef->setPublic(false); + if (false !== strpos($mappingDriverDef->getClass(), 'yml') || false !== strpos($mappingDriverDef->getClass(), 'xml')) { + $mappingDriverDef->setArguments(array(array_flip($driverPaths))); + $mappingDriverDef->addMethodCall('setGlobalBasename', array('mapping')); + } + + $container->setDefinition($mappingService, $mappingDriverDef); + + foreach ($driverPaths as $prefix => $driverPath) { + $chainDriverDef->addMethodCall('addDriver', array(new Reference($mappingService), $prefix)); + } + } + + $container->setDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'), $chainDriverDef); + } + + /** + * Assertion if the specified mapping information is valid. + * + * @param array $mappingConfig + * @param string $objectManagerName + * + * @throws \InvalidArgumentException + */ + protected function assertValidMappingConfiguration(array $mappingConfig, $objectManagerName) + { + if (!$mappingConfig['type'] || !$mappingConfig['dir'] || !$mappingConfig['prefix']) { + throw new \InvalidArgumentException(sprintf('Mapping definitions for Doctrine manager "%s" require at least the "type", "dir" and "prefix" options.', $objectManagerName)); + } + + if (!is_dir($mappingConfig['dir'])) { + throw new \InvalidArgumentException(sprintf('Specified non-existing directory "%s" as Doctrine mapping source.', $mappingConfig['dir'])); + } + + if (!in_array($mappingConfig['type'], array('xml', 'yml', 'annotation', 'php', 'staticphp'))) { + throw new \InvalidArgumentException(sprintf('Can only configure "xml", "yml", "annotation", "php" or '. + '"staticphp" through the DoctrineBundle. Use your own bundle to configure other metadata drivers. '. + 'You can register them by adding a new driver to the '. + '"%s" service definition.', $this->getObjectManagerElementName($objectManagerName.'_metadata_driver') + )); + } + } + + /** + * Detects what metadata driver to use for the supplied directory. + * + * @param string $dir A directory path + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @return string|null A metadata driver short name, if one can be detected + */ + protected function detectMetadataDriver($dir, ContainerBuilder $container) + { + // add the closest existing directory as a resource + $configPath = $this->getMappingResourceConfigDirectory(); + $resource = $dir.'/'.$configPath; + while (!is_dir($resource)) { + $resource = dirname($resource); + } + + $container->addResource(new FileResource($resource)); + + $extension = $this->getMappingResourceExtension(); + if (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.xml')) && count($files)) { + return 'xml'; + } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.yml')) && count($files)) { + return 'yml'; + } elseif (($files = glob($dir.'/'.$configPath.'/*.'.$extension.'.php')) && count($files)) { + return 'php'; + } + + // add the directory itself as a resource + $container->addResource(new FileResource($dir)); + + if (is_dir($dir.'/'.$this->getMappingObjectDefaultName())) { + return 'annotation'; + } + } + + /** + * Loads a configured object manager metadata, query or result cache driver. + * + * @param array $objectManager A configured object manager. + * @param ContainerBuilder $container A ContainerBuilder instance. + * @param string $cacheName + * + * @throws \InvalidArgumentException In case of unknown driver type. + */ + protected function loadObjectManagerCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName) + { + $this->loadCacheDriver($cacheName, $objectManager['name'], $objectManager[$cacheName.'_driver'], $container); + } + + /** + * Loads a cache driver. + * + * @param string $cacheDriverServiceId The cache driver name. + * @param string $objectManagerName The object manager name. + * @param array $cacheDriver The cache driver mapping. + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container The ContainerBuilder instance. + * + * @return string + * + * @throws \InvalidArgumentException + */ + protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheDriver, ContainerBuilder $container) + { + $cacheDriverServiceId = $this->getObjectManagerElementName($objectManagerName.'_'.$cacheName); + + switch ($cacheDriver['type']) { + case 'service': + $container->setAlias($cacheDriverServiceId, new Alias($cacheDriver['id'], false)); + + return $cacheDriverServiceId; + case 'memcache': + $memcacheClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcache.class').'%'; + $memcacheInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcache_instance.class').'%'; + $memcacheHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.memcache_host').'%'; + $memcachePort = !empty($cacheDriver['port']) || (isset($cacheDriver['port']) && $cacheDriver['port'] === 0) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.memcache_port').'%'; + $cacheDef = new Definition($memcacheClass); + $memcacheInstance = new Definition($memcacheInstanceClass); + $memcacheInstance->addMethodCall('connect', array( + $memcacheHost, $memcachePort, + )); + $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManagerName)), $memcacheInstance); + $cacheDef->addMethodCall('setMemcache', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcache_instance', $objectManagerName))))); + break; + case 'memcached': + $memcachedClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcached.class').'%'; + $memcachedInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcached_instance.class').'%'; + $memcachedHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.memcached_host').'%'; + $memcachedPort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.memcached_port').'%'; + $cacheDef = new Definition($memcachedClass); + $memcachedInstance = new Definition($memcachedInstanceClass); + $memcachedInstance->addMethodCall('addServer', array( + $memcachedHost, $memcachedPort, + )); + $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName)), $memcachedInstance); + $cacheDef->addMethodCall('setMemcached', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName))))); + break; + case 'redis': + $redisClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.redis.class').'%'; + $redisInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.redis_instance.class').'%'; + $redisHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.redis_host').'%'; + $redisPort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.redis_port').'%'; + $cacheDef = new Definition($redisClass); + $redisInstance = new Definition($redisInstanceClass); + $redisInstance->addMethodCall('connect', array( + $redisHost, $redisPort, + )); + $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName)), $redisInstance); + $cacheDef->addMethodCall('setRedis', array(new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName))))); + break; + case 'apc': + case 'array': + case 'xcache': + case 'wincache': + case 'zenddata': + $cacheDef = new Definition('%'.$this->getObjectManagerElementName(sprintf('cache.%s.class', $cacheDriver['type'])).'%'); + break; + default: + throw new \InvalidArgumentException(sprintf('"%s" is an unrecognized Doctrine cache driver.', $cacheDriver['type'])); + } + + $cacheDef->setPublic(false); + + if (!isset($cacheDriver['namespace'])) { + // generate a unique namespace for the given application + $env = $container->getParameter('kernel.root_dir').$container->getParameter('kernel.environment'); + $hash = hash('sha256', $env); + $namespace = 'sf2'.$this->getMappingResourceExtension().'_'.$objectManagerName.'_'.$hash; + + $cacheDriver['namespace'] = $namespace; + } + + $cacheDef->addMethodCall('setNamespace', array($cacheDriver['namespace'])); + + $container->setDefinition($cacheDriverServiceId, $cacheDef); + + return $cacheDriverServiceId; + } + + /** + * Returns a modified version of $managerConfigs. + * + * The manager called $autoMappedManager will map all bundles that are not mepped by other managers. + * + * @param array $managerConfigs + * @param array $bundles + * + * @return array The modified version of $managerConfigs. + */ + protected function fixManagersAutoMappings(array $managerConfigs, array $bundles) + { + if ($autoMappedManager = $this->validateAutoMapping($managerConfigs)) { + foreach (array_keys($bundles) as $bundle) { + foreach ($managerConfigs as $manager) { + if (isset($manager['mappings'][$bundle])) { + continue 2; + } + } + $managerConfigs[$autoMappedManager]['mappings'][$bundle] = array( + 'mapping' => true, + 'is_bundle' => true, + ); + } + $managerConfigs[$autoMappedManager]['auto_mapping'] = false; + } + + return $managerConfigs; + } + + /** + * Prefixes the relative dependency injection container path with the object manager prefix. + * + * @example $name is 'entity_manager' then the result would be 'doctrine.orm.entity_manager' + * + * @param string $name + * + * @return string + */ + abstract protected function getObjectManagerElementName($name); + + /** + * Noun that describes the mapped objects such as Entity or Document. + * + * Will be used for autodetection of persistent objects directory. + * + * @return string + */ + abstract protected function getMappingObjectDefaultName(); + + /** + * Relative path from the bundle root to the directory where mapping files reside. + * + * @return string + */ + abstract protected function getMappingResourceConfigDirectory(); + + /** + * Extension used by the mapping files. + * + * @return string + */ + abstract protected function getMappingResourceExtension(); + + /** + * Search for a manager that is declared as 'auto_mapping' = true. + * + * @param array $managerConfigs + * + * @return null|string The name of the manager. If no one manager is found, returns null + * + * @throws \LogicException + */ + private function validateAutoMapping(array $managerConfigs) + { + $autoMappedManager = null; + foreach ($managerConfigs as $name => $manager) { + if (!$manager['auto_mapping']) { + continue; + } + + if (null !== $autoMappedManager) { + throw new \LogicException(sprintf('You cannot enable "auto_mapping" on more than one manager at the same time (found in "%s" and %s").', $autoMappedManager, $name)); + } + + $autoMappedManager = $name; + } + + return $autoMappedManager; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php new file mode 100644 index 0000000..de35c55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Config\Resource\FileResource; + +/** + * Registers additional validators. + * + * @author Benjamin Eberlei + */ +class DoctrineValidationPass implements CompilerPassInterface +{ + /** + * @var string + */ + private $managerType; + + public function __construct($managerType) + { + $this->managerType = $managerType; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->updateValidatorMappingFiles($container, 'xml', 'xml'); + $this->updateValidatorMappingFiles($container, 'yaml', 'yml'); + } + + /** + * Gets the validation mapping files for the format and extends them with + * files matching a doctrine search pattern (Resources/config/validation.orm.xml). + * + * @param ContainerBuilder $container + * @param string $mapping + * @param string $extension + */ + private function updateValidatorMappingFiles(ContainerBuilder $container, $mapping, $extension) + { + if (!$container->hasParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files')) { + return; + } + + $files = $container->getParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files'); + $validationPath = 'Resources/config/validation.'.$this->managerType.'.'.$extension; + + foreach ($container->getParameter('kernel.bundles') as $bundle) { + $reflection = new \ReflectionClass($bundle); + if (is_file($file = dirname($reflection->getFilename()).'/'.$validationPath)) { + $files[] = realpath($file); + $container->addResource(new FileResource($file)); + } + } + + $container->setParameter('validator.mapping.loader.'.$mapping.'_files_loader.mapping_files', $files); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php new file mode 100644 index 0000000..94f72fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Registers event listeners and subscribers to the available doctrine connections. + * + * @author Jeremy Mikola + * @author Alexander + */ +class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface +{ + private $connections; + private $container; + private $eventManagers; + private $managerTemplate; + private $tagPrefix; + + /** + * Constructor. + * + * @param string $connections Parameter ID for connections + * @param string $managerTemplate sprintf() template for generating the event + * manager's service ID for a connection name + * @param string $tagPrefix Tag prefix for listeners and subscribers + */ + public function __construct($connections, $managerTemplate, $tagPrefix) + { + $this->connections = $connections; + $this->managerTemplate = $managerTemplate; + $this->tagPrefix = $tagPrefix; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasParameter($this->connections)) { + return; + } + + $taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber'); + $taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener'); + + if (empty($taggedSubscribers) && empty($taggedListeners)) { + return; + } + + $this->container = $container; + $this->connections = $container->getParameter($this->connections); + $sortFunc = function ($a, $b) { + $a = isset($a['priority']) ? $a['priority'] : 0; + $b = isset($b['priority']) ? $b['priority'] : 0; + + return $a > $b ? -1 : 1; + }; + + if (!empty($taggedSubscribers)) { + $subscribersPerCon = $this->groupByConnection($taggedSubscribers); + foreach ($subscribersPerCon as $con => $subscribers) { + $em = $this->getEventManager($con); + + uasort($subscribers, $sortFunc); + foreach ($subscribers as $id => $instance) { + if ($container->getDefinition($id)->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event subscriber.', $id)); + } + + $em->addMethodCall('addEventSubscriber', array(new Reference($id))); + } + } + } + + if (!empty($taggedListeners)) { + $listenersPerCon = $this->groupByConnection($taggedListeners, true); + foreach ($listenersPerCon as $con => $listeners) { + $em = $this->getEventManager($con); + + uasort($listeners, $sortFunc); + foreach ($listeners as $id => $instance) { + if ($container->getDefinition($id)->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event listener.', $id)); + } + + $em->addMethodCall('addEventListener', array( + array_unique($instance['event']), + isset($instance['lazy']) && $instance['lazy'] ? $id : new Reference($id), + )); + } + } + } + } + + private function groupByConnection(array $services, $isListener = false) + { + $grouped = array(); + foreach ($allCons = array_keys($this->connections) as $con) { + $grouped[$con] = array(); + } + + foreach ($services as $id => $instances) { + foreach ($instances as $instance) { + if ($isListener) { + if (!isset($instance['event'])) { + throw new \InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); + } + $instance['event'] = array($instance['event']); + + if (isset($instance['lazy']) && $instance['lazy']) { + $this->container->getDefinition($id)->setPublic(true); + } + } + + $cons = isset($instance['connection']) ? array($instance['connection']) : $allCons; + foreach ($cons as $con) { + if (!isset($grouped[$con])) { + throw new \RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); + } + + if ($isListener && isset($grouped[$con][$id])) { + $grouped[$con][$id]['event'] = array_merge($grouped[$con][$id]['event'], $instance['event']); + } else { + $grouped[$con][$id] = $instance; + } + } + } + } + + return $grouped; + } + + private function getEventManager($name) + { + if (null === $this->eventManagers) { + $this->eventManagers = array(); + foreach ($this->connections as $n => $id) { + $this->eventManagers[$n] = $this->container->getDefinition(sprintf($this->managerTemplate, $n)); + } + } + + return $this->eventManagers[$name]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php new file mode 100644 index 0000000..fd32b8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; + +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Base class for the doctrine bundles to provide a compiler pass class that + * helps to register doctrine mappings. + * + * The compiler pass is meant to register the mappings with the metadata + * chain driver corresponding to one of the object managers. + * + * For concrete implementations that are easy to use, see the + * RegisterXyMappingsPass classes in the DoctrineBundle resp. + * DoctrineMongodbBundle, DoctrineCouchdbBundle and DoctrinePhpcrBundle. + * + * @author David Buchmann + */ +abstract class RegisterMappingsPass implements CompilerPassInterface +{ + /** + * DI object for the driver to use, either a service definition for a + * private service or a reference for a public service. + * + * @var Definition|Reference + */ + protected $driver; + + /** + * List of namespaces handled by the driver. + * + * @var string[] + */ + protected $namespaces; + + /** + * List of potential container parameters that hold the object manager name + * to register the mappings with the correct metadata driver, for example + * array('acme.manager', 'doctrine.default_entity_manager'). + * + * @var string[] + */ + protected $managerParameters; + + /** + * Naming pattern of the metadata chain driver service ids, for example + * 'doctrine.orm.%s_metadata_driver'. + * + * @var string + */ + protected $driverPattern; + + /** + * A name for a parameter in the container. If set, this compiler pass will + * only do anything if the parameter is present. (But regardless of the + * value of that parameter. + * + * @var string + */ + protected $enabledParameter; + + /** + * Naming pattern for the configuration service id, for example + * 'doctrine.orm.%s_configuration'. + * + * @var string + */ + private $configurationPattern; + + /** + * Method name to call on the configuration service. This depends on the + * Doctrine implementation. For example addEntityNamespace. + * + * @var string + */ + private $registerAliasMethodName; + + /** + * Map of alias to namespace. + * + * @var string[] + */ + private $aliasMap; + + /** + * Constructor. + * + * The $managerParameters is an ordered list of container parameters that could provide the + * name of the manager to register these namespaces and alias on. The first non-empty name + * is used, the others skipped. + * + * The $aliasMap parameter can be used to define bundle namespace shortcuts like the + * DoctrineBundle provides automatically for objects in the default Entity/Document folder. + * + * @param Definition|Reference $driver Driver DI definition or reference. + * @param string[] $namespaces List of namespaces handled by $driver. + * @param string[] $managerParameters List of container parameters that could + * hold the manager name. + * @param string $driverPattern Pattern for the metadata driver service name. + * @param string $enabledParameter Service container parameter that must be + * present to enable the mapping. Set to false + * to not do any check, optional. + * @param string $configurationPattern Pattern for the Configuration service name. + * @param string $registerAliasMethodName Name of Configuration class method to + * register alias. + * @param string[] $aliasMap Map of alias to namespace. + * + * @since Support for bundle alias was added in Symfony 2.6 + */ + public function __construct($driver, array $namespaces, array $managerParameters, $driverPattern, $enabledParameter = false, $configurationPattern = '', $registerAliasMethodName = '', array $aliasMap = array()) + { + $this->driver = $driver; + $this->namespaces = $namespaces; + $this->managerParameters = $managerParameters; + $this->driverPattern = $driverPattern; + $this->enabledParameter = $enabledParameter; + if (count($aliasMap) && (!$configurationPattern || !$registerAliasMethodName)) { + throw new \InvalidArgumentException('configurationPattern and registerAliasMethodName are required to register namespace alias'); + } + $this->configurationPattern = $configurationPattern; + $this->registerAliasMethodName = $registerAliasMethodName; + $this->aliasMap = $aliasMap; + } + + /** + * Register mappings and alias with the metadata drivers. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + if (!$this->enabled($container)) { + return; + } + + $mappingDriverDef = $this->getDriver($container); + $chainDriverDefService = $this->getChainDriverServiceName($container); + // Definition for a Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain + $chainDriverDef = $container->getDefinition($chainDriverDefService); + foreach ($this->namespaces as $namespace) { + $chainDriverDef->addMethodCall('addDriver', array($mappingDriverDef, $namespace)); + } + + if (!count($this->aliasMap)) { + return; + } + + $configurationServiceName = $this->getConfigurationServiceName($container); + // Definition of the Doctrine\...\Configuration class specific to the Doctrine flavour. + $configurationServiceDefinition = $container->getDefinition($configurationServiceName); + foreach ($this->aliasMap as $alias => $namespace) { + $configurationServiceDefinition->addMethodCall($this->registerAliasMethodName, array($alias, $namespace)); + } + } + + /** + * Get the service name of the metadata chain driver that the mappings + * should be registered with. + * + * @param ContainerBuilder $container + * + * @return string The name of the chain driver service + * + * @throws ParameterNotFoundException if non of the managerParameters has a + * non-empty value. + */ + protected function getChainDriverServiceName(ContainerBuilder $container) + { + return sprintf($this->driverPattern, $this->getManagerName($container)); + } + + /** + * Create the service definition for the metadata driver. + * + * @param ContainerBuilder $container passed on in case an extending class + * needs access to the container. + * + * @return Definition|Reference the metadata driver to add to all chain drivers + */ + protected function getDriver(ContainerBuilder $container) + { + return $this->driver; + } + + /** + * Get the service name from the pattern and the configured manager name. + * + * @param ContainerBuilder $container + * + * @return string a service definition name + * + * @throws ParameterNotFoundException if none of the managerParameters has a + * non-empty value. + */ + private function getConfigurationServiceName(ContainerBuilder $container) + { + return sprintf($this->configurationPattern, $this->getManagerName($container)); + } + + /** + * Determine the manager name. + * + * The default implementation loops over the managerParameters and returns + * the first non-empty parameter. + * + * @param ContainerBuilder $container + * + * @return string The name of the active manager. + * + * @throws ParameterNotFoundException If none of the managerParameters is found in the container. + */ + private function getManagerName(ContainerBuilder $container) + { + foreach ($this->managerParameters as $param) { + if ($container->hasParameter($param)) { + $name = $container->getParameter($param); + if ($name) { + return $name; + } + } + } + + throw new ParameterNotFoundException('Could not determine the Doctrine manager. Either Doctrine is not configured or a bundle is misconfigured.'); + } + + /** + * Determine whether this mapping should be activated or not. This allows + * to take this decision with the container builder available. + * + * This default implementation checks if the class has the enabledParameter + * configured and if so if that parameter is present in the container. + * + * @param ContainerBuilder $container + * + * @return bool whether this compiler pass really should register the mappings + */ + protected function enabled(ContainerBuilder $container) + { + return !$this->enabledParameter || $container->hasParameter($this->enabledParameter); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php new file mode 100644 index 0000000..ebcdf82 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/DependencyInjection/Security/UserProvider/EntityFactory.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\DependencyInjection\Security\UserProvider; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * EntityFactory creates services for Doctrine user provider. + * + * @author Fabien Potencier + * @author Christophe Coevoet + */ +class EntityFactory implements UserProviderFactoryInterface +{ + private $key; + private $providerId; + + public function __construct($key, $providerId) + { + $this->key = $key; + $this->providerId = $providerId; + } + + public function create(ContainerBuilder $container, $id, $config) + { + $container + ->setDefinition($id, new DefinitionDecorator($this->providerId)) + ->addArgument($config['class']) + ->addArgument($config['property']) + ->addArgument($config['manager_name']) + ; + } + + public function getKey() + { + return $this->key; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('class')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('property')->defaultNull()->end() + ->scalarNode('manager_name')->defaultNull()->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ExpressionLanguage/DoctrineParserCache.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ExpressionLanguage/DoctrineParserCache.php new file mode 100644 index 0000000..1c0e8cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ExpressionLanguage/DoctrineParserCache.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\ExpressionLanguage; + +use Doctrine\Common\Cache\Cache; +use Symfony\Component\ExpressionLanguage\ParsedExpression; +use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; + +/** + * @author Adrien Brault + */ +class DoctrineParserCache implements ParserCacheInterface +{ + /** + * @var Cache + */ + private $cache; + + public function __construct(Cache $cache) + { + $this->cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function fetch($key) + { + if (false === $value = $this->cache->fetch($key)) { + return; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function save($key, ParsedExpression $expression) + { + $this->cache->save($key, $expression); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php new file mode 100644 index 0000000..1a09609 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\ChoiceList; + +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Form\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; +use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; + +/** + * Loads choices using a Doctrine object manager. + * + * @author Bernhard Schussek + */ +class DoctrineChoiceLoader implements ChoiceLoaderInterface +{ + /** + * @var ChoiceListFactoryInterface + */ + private $factory; + + /** + * @var ObjectManager + */ + private $manager; + + /** + * @var string + */ + private $class; + + /** + * @var IdReader + */ + private $idReader; + + /** + * @var null|EntityLoaderInterface + */ + private $objectLoader; + + /** + * @var ChoiceListInterface + */ + private $choiceList; + + /** + * Creates a new choice loader. + * + * Optionally, an implementation of {@link EntityLoaderInterface} can be + * passed which optimizes the object loading for one of the Doctrine + * mapper implementations. + * + * @param ChoiceListFactoryInterface $factory The factory for creating + * the loaded choice list + * @param ObjectManager $manager The object manager + * @param string $class The class name of the + * loaded objects + * @param IdReader $idReader The reader for the object + * IDs. + * @param null|EntityLoaderInterface $objectLoader The objects loader + */ + public function __construct(ChoiceListFactoryInterface $factory, ObjectManager $manager, $class, IdReader $idReader = null, EntityLoaderInterface $objectLoader = null) + { + $classMetadata = $manager->getClassMetadata($class); + + $this->factory = $factory; + $this->manager = $manager; + $this->class = $classMetadata->getName(); + $this->idReader = $idReader ?: new IdReader($manager, $classMetadata); + $this->objectLoader = $objectLoader; + } + + /** + * {@inheritdoc} + */ + public function loadChoiceList($value = null) + { + if ($this->choiceList) { + return $this->choiceList; + } + + $objects = $this->objectLoader + ? $this->objectLoader->getEntities() + : $this->manager->getRepository($this->class)->findAll(); + + $this->choiceList = $this->factory->createListFromChoices($objects, $value); + + return $this->choiceList; + } + + /** + * {@inheritdoc} + */ + public function loadValuesForChoices(array $choices, $value = null) + { + // Performance optimization + if (empty($choices)) { + return array(); + } + + // Optimize performance for single-field identifiers. We already + // know that the IDs are used as values + + // Attention: This optimization does not check choices for existence + if (!$this->choiceList && $this->idReader->isSingleId()) { + $values = array(); + + // Maintain order and indices of the given objects + foreach ($choices as $i => $object) { + if ($object instanceof $this->class) { + // Make sure to convert to the right format + $values[$i] = (string) $this->idReader->getIdValue($object); + } + } + + return $values; + } + + return $this->loadChoiceList($value)->getValuesForChoices($choices); + } + + /** + * {@inheritdoc} + */ + public function loadChoicesForValues(array $values, $value = null) + { + // Performance optimization + // Also prevents the generation of "WHERE id IN ()" queries through the + // object loader. At least with MySQL and on the development machine + // this was tested on, no exception was thrown for such invalid + // statements, consequently no test fails when this code is removed. + // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557 + if (empty($values)) { + return array(); + } + + // Optimize performance in case we have an object loader and + // a single-field identifier + if (!$this->choiceList && $this->objectLoader && $this->idReader->isSingleId()) { + $unorderedObjects = $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values); + $objectsById = array(); + $objects = array(); + + // Maintain order and indices from the given $values + // An alternative approach to the following loop is to add the + // "INDEX BY" clause to the Doctrine query in the loader, + // but I'm not sure whether that's doable in a generic fashion. + foreach ($unorderedObjects as $object) { + $objectsById[(string) $this->idReader->getIdValue($object)] = $object; + } + + foreach ($values as $i => $id) { + if (isset($objectsById[$id])) { + $objects[$i] = $objectsById[$id]; + } + } + + return $objects; + } + + return $this->loadChoiceList($value)->getChoicesForValues($values); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityLoaderInterface.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityLoaderInterface.php new file mode 100644 index 0000000..2de34af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityLoaderInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\ChoiceList; + +/** + * Custom loader for entities in the choice list. + * + * @author Benjamin Eberlei + */ +interface EntityLoaderInterface +{ + /** + * Returns an array of entities that are valid choices in the corresponding choice list. + * + * @return array The entities. + */ + public function getEntities(); + + /** + * Returns an array of entities matching the given identifiers. + * + * @param string $identifier The identifier field of the object. This method + * is not applicable for fields with multiple + * identifiers. + * @param array $values The values of the identifiers. + * + * @return array The entities. + */ + public function getEntitiesByIds($identifier, array $values); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php new file mode 100644 index 0000000..6ae98b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\ChoiceList; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Form\Exception\RuntimeException; + +/** + * A utility for reading object IDs. + * + * @since 1.0 + * + * @author Bernhard Schussek + * + * @internal This class is meant for internal use only. + */ +class IdReader +{ + /** + * @var ObjectManager + */ + private $om; + + /** + * @var ClassMetadata + */ + private $classMetadata; + + /** + * @var bool + */ + private $singleId; + + /** + * @var bool + */ + private $intId; + + /** + * @var string + */ + private $idField; + + /** + * @var IdReader|null + */ + private $associationIdReader; + + public function __construct(ObjectManager $om, ClassMetadata $classMetadata) + { + $ids = $classMetadata->getIdentifierFieldNames(); + $idType = $classMetadata->getTypeOfField(current($ids)); + + $this->om = $om; + $this->classMetadata = $classMetadata; + $this->singleId = 1 === count($ids); + $this->intId = $this->singleId && in_array($idType, array('integer', 'smallint', 'bigint')); + $this->idField = current($ids); + + // single field association are resolved, since the schema column could be an int + if ($this->singleId && $classMetadata->hasAssociation($this->idField)) { + $this->associationIdReader = new self($om, $om->getClassMetadata( + $classMetadata->getAssociationTargetClass($this->idField) + )); + + $this->singleId = $this->associationIdReader->isSingleId(); + $this->intId = $this->associationIdReader->isIntId(); + } + } + + /** + * Returns whether the class has a single-column ID. + * + * @return bool Returns `true` if the class has a single-column ID and + * `false` otherwise. + */ + public function isSingleId() + { + return $this->singleId; + } + + /** + * Returns whether the class has a single-column integer ID. + * + * @return bool Returns `true` if the class has a single-column integer ID + * and `false` otherwise. + */ + public function isIntId() + { + return $this->intId; + } + + /** + * Returns the ID value for an object. + * + * This method assumes that the object has a single-column ID. + * + * @param object $object The object. + * + * @return mixed The ID value. + */ + public function getIdValue($object) + { + if (!$object) { + return; + } + + if (!$this->om->contains($object)) { + throw new RuntimeException( + 'Entities passed to the choice field must be managed. Maybe '. + 'persist them in the entity manager?' + ); + } + + $this->om->initializeObject($object); + + $idValue = current($this->classMetadata->getIdentifierValues($object)); + + if ($this->associationIdReader) { + $idValue = $this->associationIdReader->getIdValue($idValue); + } + + return $idValue; + } + + /** + * Returns the name of the ID field. + * + * This method assumes that the object has a single-column ID. + * + * @return string The name of the ID field. + */ + public function getIdField() + { + return $this->idField; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php new file mode 100644 index 0000000..1b548e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\ChoiceList; + +use Doctrine\ORM\QueryBuilder; +use Doctrine\DBAL\Connection; + +/** + * Loads entities using a {@link QueryBuilder} instance. + * + * @author Benjamin Eberlei + * @author Bernhard Schussek + */ +class ORMQueryBuilderLoader implements EntityLoaderInterface +{ + /** + * Contains the query builder that builds the query for fetching the + * entities. + * + * This property should only be accessed through queryBuilder. + * + * @var QueryBuilder + */ + private $queryBuilder; + + /** + * Construct an ORM Query Builder Loader. + * + * @param QueryBuilder $queryBuilder The query builder for creating the query builder. + */ + public function __construct(QueryBuilder $queryBuilder) + { + $this->queryBuilder = $queryBuilder; + } + + /** + * {@inheritdoc} + */ + public function getEntities() + { + return $this->queryBuilder->getQuery()->execute(); + } + + /** + * {@inheritdoc} + */ + public function getEntitiesByIds($identifier, array $values) + { + $qb = clone $this->queryBuilder; + $alias = current($qb->getRootAliases()); + $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier; + $parameter = str_replace('.', '_', $parameter); + $where = $qb->expr()->in($alias.'.'.$identifier, ':'.$parameter); + + // Guess type + $entity = current($qb->getRootEntities()); + $metadata = $qb->getEntityManager()->getClassMetadata($entity); + if (in_array($metadata->getTypeOfField($identifier), array('integer', 'bigint', 'smallint'))) { + $parameterType = Connection::PARAM_INT_ARRAY; + + // Filter out non-integer values (e.g. ""). If we don't, some + // databases such as PostgreSQL fail. + $values = array_values(array_filter($values, function ($v) { + return (string) $v === (string) (int) $v; + })); + } else { + $parameterType = Connection::PARAM_STR_ARRAY; + } + if (!$values) { + return array(); + } + + return $qb->andWhere($where) + ->getQuery() + ->setParameter($parameter, $values, $parameterType) + ->getResult(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php new file mode 100644 index 0000000..e56674b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\DataTransformer; + +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\DataTransformerInterface; +use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ArrayCollection; + +/** + * @author Bernhard Schussek + */ +class CollectionToArrayTransformer implements DataTransformerInterface +{ + /** + * Transforms a collection into an array. + * + * @param Collection $collection A collection of entities + * + * @return mixed An array of entities + * + * @throws TransformationFailedException + */ + public function transform($collection) + { + if (null === $collection) { + return array(); + } + + // For cases when the collection getter returns $collection->toArray() + // in order to prevent modifications of the returned collection + if (is_array($collection)) { + return $collection; + } + + if (!$collection instanceof Collection) { + throw new TransformationFailedException('Expected a Doctrine\Common\Collections\Collection object.'); + } + + return $collection->toArray(); + } + + /** + * Transforms choice keys into entities. + * + * @param mixed $array An array of entities + * + * @return Collection A collection of entities + */ + public function reverseTransform($array) + { + if ('' === $array || null === $array) { + $array = array(); + } else { + $array = (array) $array; + } + + return new ArrayCollection($array); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php new file mode 100644 index 0000000..ed8e0a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; +use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; +use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; +use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +class DoctrineOrmExtension extends AbstractExtension +{ + protected $registry; + + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + /** + * @var ChoiceListFactoryInterface + */ + private $choiceListFactory; + + public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null) + { + $this->registry = $registry; + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); + $this->choiceListFactory = $choiceListFactory ?: new CachingFactoryDecorator(new PropertyAccessDecorator(new DefaultChoiceListFactory(), $this->propertyAccessor)); + } + + protected function loadTypes() + { + return array( + new EntityType($this->registry, $this->propertyAccessor, $this->choiceListFactory), + ); + } + + protected function loadTypeGuesser() + { + return new DoctrineOrmTypeGuesser($this->registry); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php new file mode 100644 index 0000000..2e6af7a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Persistence\Mapping\MappingException; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\MappingException as LegacyMappingException; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\TypeGuess; +use Symfony\Component\Form\Guess\ValueGuess; +use Doctrine\Common\Util\ClassUtils; + +class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface +{ + protected $registry; + + private $cache = array(); + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + /** + * {@inheritdoc} + */ + public function guessType($class, $property) + { + if (!$ret = $this->getMetadata($class)) { + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', array(), Guess::LOW_CONFIDENCE); + } + + list($metadata, $name) = $ret; + + if ($metadata->hasAssociation($property)) { + $multiple = $metadata->isCollectionValuedAssociation($property); + $mapping = $metadata->getAssociationMapping($property); + + return new TypeGuess('Symfony\Bridge\Doctrine\Form\Type\EntityType', array('em' => $name, 'class' => $mapping['targetEntity'], 'multiple' => $multiple), Guess::HIGH_CONFIDENCE); + } + + switch ($metadata->getTypeOfField($property)) { + case Type::TARRAY: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), Guess::MEDIUM_CONFIDENCE); + case Type::BOOLEAN: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CheckboxType', array(), Guess::HIGH_CONFIDENCE); + case Type::DATETIME: + case Type::DATETIMETZ: + case 'vardatetime': + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array(), Guess::HIGH_CONFIDENCE); + case Type::DATE: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', array(), Guess::HIGH_CONFIDENCE); + case Type::TIME: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', array(), Guess::HIGH_CONFIDENCE); + case Type::DECIMAL: + case Type::FLOAT: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\NumberType', array(), Guess::MEDIUM_CONFIDENCE); + case Type::INTEGER: + case Type::BIGINT: + case Type::SMALLINT: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\IntegerType', array(), Guess::MEDIUM_CONFIDENCE); + case Type::STRING: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', array(), Guess::MEDIUM_CONFIDENCE); + case Type::TEXT: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextareaType', array(), Guess::MEDIUM_CONFIDENCE); + default: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', array(), Guess::LOW_CONFIDENCE); + } + } + + /** + * {@inheritdoc} + */ + public function guessRequired($class, $property) + { + $classMetadatas = $this->getMetadata($class); + + if (!$classMetadatas) { + return; + } + + /** @var ClassMetadataInfo $classMetadata */ + $classMetadata = $classMetadatas[0]; + + // Check whether the field exists and is nullable or not + if ($classMetadata->hasField($property)) { + if (!$classMetadata->isNullable($property) && Type::BOOLEAN !== $classMetadata->getTypeOfField($property)) { + return new ValueGuess(true, Guess::HIGH_CONFIDENCE); + } + + return new ValueGuess(false, Guess::MEDIUM_CONFIDENCE); + } + + // Check whether the association exists, is a to-one association and its + // join column is nullable or not + if ($classMetadata->isAssociationWithSingleJoinColumn($property)) { + $mapping = $classMetadata->getAssociationMapping($property); + + if (!isset($mapping['joinColumns'][0]['nullable'])) { + // The "nullable" option defaults to true, in that case the + // field should not be required. + return new ValueGuess(false, Guess::HIGH_CONFIDENCE); + } + + return new ValueGuess(!$mapping['joinColumns'][0]['nullable'], Guess::HIGH_CONFIDENCE); + } + } + + /** + * {@inheritdoc} + */ + public function guessMaxLength($class, $property) + { + $ret = $this->getMetadata($class); + if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { + $mapping = $ret[0]->getFieldMapping($property); + + if (isset($mapping['length'])) { + return new ValueGuess($mapping['length'], Guess::HIGH_CONFIDENCE); + } + + if (in_array($ret[0]->getTypeOfField($property), array(Type::DECIMAL, Type::FLOAT))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + } + } + + /** + * {@inheritdoc} + */ + public function guessPattern($class, $property) + { + $ret = $this->getMetadata($class); + if ($ret && $ret[0]->hasField($property) && !$ret[0]->hasAssociation($property)) { + if (in_array($ret[0]->getTypeOfField($property), array(Type::DECIMAL, Type::FLOAT))) { + return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); + } + } + } + + protected function getMetadata($class) + { + // normalize class name + $class = ClassUtils::getRealClass(ltrim($class, '\\')); + + if (array_key_exists($class, $this->cache)) { + return $this->cache[$class]; + } + + $this->cache[$class] = null; + foreach ($this->registry->getManagers() as $name => $em) { + try { + return $this->cache[$class] = array($em->getClassMetadata($class), $name); + } catch (MappingException $e) { + // not an entity or mapped super class + } catch (LegacyMappingException $e) { + // not an entity or mapped super class, using Doctrine ORM 2.2 + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php new file mode 100644 index 0000000..4edf104 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\EventListener; + +use Doctrine\Common\Collections\Collection; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Merge changes from the request to a Doctrine\Common\Collections\Collection instance. + * + * This works with ORM, MongoDB and CouchDB instances of the collection interface. + * + * @author Bernhard Schussek + * + * @see Collection + */ +class MergeDoctrineCollectionListener implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + // Higher priority than core MergeCollectionListener so that this one + // is called before + return array(FormEvents::SUBMIT => array('onBind', 10)); + } + + public function onBind(FormEvent $event) + { + $collection = $event->getForm()->getData(); + $data = $event->getData(); + + // If all items were removed, call clear which has a higher + // performance on persistent collections + if ($collection instanceof Collection && count($data) === 0) { + $collection->clear(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php new file mode 100644 index 0000000..353c185 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -0,0 +1,294 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\Type; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader; +use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface; +use Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader; +use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer; +use Symfony\Bridge\Doctrine\Form\EventListener\MergeDoctrineCollectionListener; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; +use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; +use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; +use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; +use Symfony\Component\Form\Exception\RuntimeException; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +abstract class DoctrineType extends AbstractType +{ + /** + * @var ManagerRegistry + */ + protected $registry; + + /** + * @var ChoiceListFactoryInterface + */ + private $choiceListFactory; + + /** + * @var IdReader[] + */ + private $idReaders = array(); + + /** + * @var DoctrineChoiceLoader[] + */ + private $choiceLoaders = array(); + + /** + * Creates the label for a choice. + * + * For backwards compatibility, objects are cast to strings by default. + * + * @param object $choice The object. + * + * @return string The string representation of the object. + * + * @internal This method is public to be usable as callback. It should not + * be used in user code. + */ + public static function createChoiceLabel($choice) + { + return (string) $choice; + } + + /** + * Creates the field name for a choice. + * + * This method is used to generate field names if the underlying object has + * a single-column integer ID. In that case, the value of the field is + * the ID of the object. That ID is also used as field name. + * + * @param object $choice The object. + * @param int|string $key The choice key. + * @param string $value The choice value. Corresponds to the object's + * ID here. + * + * @return string The field name. + * + * @internal This method is public to be usable as callback. It should not + * be used in user code. + */ + public static function createChoiceName($choice, $key, $value) + { + return str_replace('-', '_', (string) $value); + } + + /** + * Gets important parts from QueryBuilder that will allow to cache its results. + * For instance in ORM two query builders with an equal SQL string and + * equal parameters are considered to be equal. + * + * @param object $queryBuilder + * + * @return array|false Array with important QueryBuilder parts or false if + * they can't be determined + * + * @internal This method is public to be usable as callback. It should not + * be used in user code. + */ + public function getQueryBuilderPartsForCachingHash($queryBuilder) + { + return false; + } + + public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null, ChoiceListFactoryInterface $choiceListFactory = null) + { + $this->registry = $registry; + $this->choiceListFactory = $choiceListFactory ?: new PropertyAccessDecorator(new DefaultChoiceListFactory(), $propertyAccessor); + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + if ($options['multiple']) { + $builder + ->addEventSubscriber(new MergeDoctrineCollectionListener()) + ->addViewTransformer(new CollectionToArrayTransformer(), true) + ; + } + } + + public function configureOptions(OptionsResolver $resolver) + { + $choiceLoader = function (Options $options) { + // Unless the choices are given explicitly, load them on demand + if (null === $options['choices']) { + $hash = null; + $qbParts = null; + + // If there is no QueryBuilder we can safely cache DoctrineChoiceLoader, + // also if concrete Type can return important QueryBuilder parts to generate + // hash key we go for it as well + if (!$options['query_builder'] || false !== ($qbParts = $this->getQueryBuilderPartsForCachingHash($options['query_builder']))) { + $hash = CachingFactoryDecorator::generateHash(array( + $options['em'], + $options['class'], + $qbParts, + )); + + if (isset($this->choiceLoaders[$hash])) { + return $this->choiceLoaders[$hash]; + } + } + + if (null !== $options['query_builder']) { + $entityLoader = $this->getLoader($options['em'], $options['query_builder'], $options['class']); + } else { + $queryBuilder = $options['em']->getRepository($options['class'])->createQueryBuilder('e'); + $entityLoader = $this->getLoader($options['em'], $queryBuilder, $options['class']); + } + + $doctrineChoiceLoader = new DoctrineChoiceLoader( + $this->choiceListFactory, + $options['em'], + $options['class'], + $options['id_reader'], + $entityLoader + ); + + if ($hash !== null) { + $this->choiceLoaders[$hash] = $doctrineChoiceLoader; + } + + return $doctrineChoiceLoader; + } + }; + + $choiceName = function (Options $options) { + /** @var IdReader $idReader */ + $idReader = $options['id_reader']; + + // If the object has a single-column, numeric ID, use that ID as + // field name. We can only use numeric IDs as names, as we cannot + // guarantee that a non-numeric ID contains a valid form name + if ($idReader->isIntId()) { + return array(__CLASS__, 'createChoiceName'); + } + + // Otherwise, an incrementing integer is used as name automatically + }; + + // The choices are always indexed by ID (see "choices" normalizer + // and DoctrineChoiceLoader), unless the ID is composite. Then they + // are indexed by an incrementing integer. + // Use the ID/incrementing integer as choice value. + $choiceValue = function (Options $options) { + /** @var IdReader $idReader */ + $idReader = $options['id_reader']; + + // If the entity has a single-column ID, use that ID as value + if ($idReader->isSingleId()) { + return array($idReader, 'getIdValue'); + } + + // Otherwise, an incrementing integer is used as value automatically + }; + + $emNormalizer = function (Options $options, $em) { + /* @var ManagerRegistry $registry */ + if (null !== $em) { + if ($em instanceof ObjectManager) { + return $em; + } + + return $this->registry->getManager($em); + } + + $em = $this->registry->getManagerForClass($options['class']); + + if (null === $em) { + throw new RuntimeException(sprintf( + 'Class "%s" seems not to be a managed Doctrine entity. '. + 'Did you forget to map it?', + $options['class'] + )); + } + + return $em; + }; + + // Invoke the query builder closure so that we can cache choice lists + // for equal query builders + $queryBuilderNormalizer = function (Options $options, $queryBuilder) { + if (is_callable($queryBuilder)) { + $queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); + } + + return $queryBuilder; + }; + + // Set the "id_reader" option via the normalizer. This option is not + // supposed to be set by the user. + $idReaderNormalizer = function (Options $options) { + $hash = CachingFactoryDecorator::generateHash(array( + $options['em'], + $options['class'], + )); + + // The ID reader is a utility that is needed to read the object IDs + // when generating the field values. The callback generating the + // field values has no access to the object manager or the class + // of the field, so we store that information in the reader. + // The reader is cached so that two choice lists for the same class + // (and hence with the same reader) can successfully be cached. + if (!isset($this->idReaders[$hash])) { + $classMetadata = $options['em']->getClassMetadata($options['class']); + $this->idReaders[$hash] = new IdReader($options['em'], $classMetadata); + } + + return $this->idReaders[$hash]; + }; + + $resolver->setDefaults(array( + 'em' => null, + 'query_builder' => null, + 'choices' => null, + 'choice_loader' => $choiceLoader, + 'choice_label' => array(__CLASS__, 'createChoiceLabel'), + 'choice_name' => $choiceName, + 'choice_value' => $choiceValue, + 'id_reader' => null, // internal + 'choice_translation_domain' => false, + )); + + $resolver->setRequired(array('class')); + + $resolver->setNormalizer('em', $emNormalizer); + $resolver->setNormalizer('query_builder', $queryBuilderNormalizer); + $resolver->setNormalizer('id_reader', $idReaderNormalizer); + + $resolver->setAllowedTypes('em', array('null', 'string', 'Doctrine\Common\Persistence\ObjectManager')); + } + + /** + * Return the default loader object. + * + * @param ObjectManager $manager + * @param mixed $queryBuilder + * @param string $class + * + * @return EntityLoaderInterface + */ + abstract public function getLoader(ObjectManager $manager, $queryBuilder, $class); + + public function getParent() + { + return 'Symfony\Component\Form\Extension\Core\Type\ChoiceType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php new file mode 100644 index 0000000..f0c8a9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Form\Type; + +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\ORM\QueryBuilder; +use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class EntityType extends DoctrineType +{ + public function configureOptions(OptionsResolver $resolver) + { + parent::configureOptions($resolver); + + // Invoke the query builder closure so that we can cache choice lists + // for equal query builders + $queryBuilderNormalizer = function (Options $options, $queryBuilder) { + if (is_callable($queryBuilder)) { + $queryBuilder = call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); + + if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) { + throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); + } + } + + return $queryBuilder; + }; + + $resolver->setNormalizer('query_builder', $queryBuilderNormalizer); + $resolver->setAllowedTypes('query_builder', array('null', 'callable', 'Doctrine\ORM\QueryBuilder')); + } + + /** + * Return the default loader object. + * + * @param ObjectManager $manager + * @param QueryBuilder $queryBuilder + * @param string $class + * + * @return ORMQueryBuilderLoader + */ + public function getLoader(ObjectManager $manager, $queryBuilder, $class) + { + return new ORMQueryBuilderLoader($queryBuilder); + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return 'entity'; + } + + /** + * We consider two query builders with an equal SQL string and + * equal parameters to be equal. + * + * @param QueryBuilder $queryBuilder + * + * @return array + * + * @internal This method is public to be usable as callback. It should not + * be used in user code. + */ + public function getQueryBuilderPartsForCachingHash($queryBuilder) + { + return array( + $queryBuilder->getQuery()->getSQL(), + $queryBuilder->getParameters()->toArray(), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php new file mode 100644 index 0000000..fd7dcff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandler.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\HttpFoundation; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\DriverException; +use Doctrine\DBAL\Platforms\SQLServer2008Platform; + +/** + * DBAL based session storage. + * + * This implementation is very similar to Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler + * but uses a Doctrine connection and thus also works with non-PDO-based drivers like mysqli and OCI8. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + * @author Tobias Schultze + */ +class DbalSessionHandler implements \SessionHandlerInterface +{ + /** + * @var Connection + */ + private $con; + + /** + * @var string + */ + private $table; + + /** + * @var string Column for session id + */ + private $idCol = 'sess_id'; + + /** + * @var string Column for session data + */ + private $dataCol = 'sess_data'; + + /** + * @var string Column for timestamp + */ + private $timeCol = 'sess_time'; + + /** + * Constructor. + * + * @param Connection $con A connection + * @param string $tableName Table name + */ + public function __construct(Connection $con, $tableName = 'sessions') + { + $this->con = $con; + $this->table = $tableName; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + // delete the record associated with this id + $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; + + try { + $stmt = $this->con->prepare($sql); + $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $stmt->execute(); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Exception was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // delete the session records that have expired + $sql = "DELETE FROM $this->table WHERE $this->timeCol < :time"; + + try { + $stmt = $this->con->prepare($sql); + $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); + $stmt->execute(); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Exception was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id"; + + try { + $stmt = $this->con->prepare($sql); + $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $stmt->execute(); + + // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed + $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); + + if ($sessionRows) { + return base64_decode($sessionRows[0][0]); + } + + return ''; + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Exception was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + $encoded = base64_encode($data); + + try { + // We use a single MERGE SQL query when supported by the database. + $mergeSql = $this->getMergeSql(); + + if (null !== $mergeSql) { + $mergeStmt = $this->con->prepare($mergeSql); + $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); + + // Oracle has a bug that will intermittently happen if you + // have only 1 bind on a CLOB field for 2 different statements + // (INSERT and UPDATE in this case) + if ('oracle' == $this->con->getDatabasePlatform()->getName()) { + $mergeStmt->bindParam(':data2', $encoded, \PDO::PARAM_STR); + } + + $mergeStmt->execute(); + + return true; + } + + $updateStmt = $this->con->prepare( + "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id" + ); + $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $updateStmt->execute(); + + // When MERGE is not supported, like in Postgres, we have to use this approach that can result in + // duplicate key errors when the same session is written simultaneously. We can just catch such an + // error and re-execute the update. This is similar to a serializable transaction with retry logic + // on serialization failures but without the overhead and without possible false positives due to + // longer gap locking. + if (!$updateStmt->rowCount()) { + try { + $insertStmt = $this->con->prepare( + "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" + ); + $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $insertStmt->execute(); + } catch (\Exception $e) { + $driverException = $e->getPrevious(); + // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys + // DriverException only available since DBAL 2.5 + if ( + ($driverException instanceof DriverException && 0 === strpos($driverException->getSQLState(), '23')) || + ($driverException instanceof \PDOException && 0 === strpos($driverException->getCode(), '23')) + ) { + $updateStmt->execute(); + } else { + throw $e; + } + } + } + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Exception was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database. + * + * @return string|null The SQL string or null when not supported + */ + private function getMergeSql() + { + $platform = $this->con->getDatabasePlatform()->getName(); + + switch ($platform) { + case 'mysql': + return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". + "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)"; + case 'oracle': + // DUAL is Oracle specific dummy table + return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data2, $this->timeCol = :time"; + case $this->con->getDatabasePlatform() instanceof SQLServer2008Platform: + // MERGE is only available since SQL Server 2008 and must be terminated by semicolon + // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx + return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;"; + case 'sqlite': + return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php new file mode 100644 index 0000000..978373d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/HttpFoundation/DbalSessionHandlerSchema.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\HttpFoundation; + +use Doctrine\DBAL\Schema\Schema; + +/** + * DBAL Session Storage Schema. + * + * @author Johannes M. Schmitt + */ +final class DbalSessionHandlerSchema extends Schema +{ + public function __construct($tableName = 'sessions') + { + parent::__construct(); + + $this->addSessionTable($tableName); + } + + public function addToSchema(Schema $schema) + { + foreach ($this->getTables() as $table) { + $schema->_addTable($table); + } + } + + private function addSessionTable($tableName) + { + $table = $this->createTable($tableName); + $table->addColumn('sess_id', 'string'); + $table->addColumn('sess_data', 'text')->setNotNull(true); + $table->addColumn('sess_time', 'integer')->setNotNull(true)->setUnsigned(true); + $table->setPrimaryKey(array('sess_id')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php new file mode 100644 index 0000000..51f9f75 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Logger; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Stopwatch\Stopwatch; +use Doctrine\DBAL\Logging\SQLLogger; + +/** + * DbalLogger. + * + * @author Fabien Potencier + */ +class DbalLogger implements SQLLogger +{ + const MAX_STRING_LENGTH = 32; + const BINARY_DATA_VALUE = '(binary value)'; + + protected $logger; + protected $stopwatch; + + /** + * Constructor. + * + * @param LoggerInterface $logger A LoggerInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + */ + public function __construct(LoggerInterface $logger = null, Stopwatch $stopwatch = null) + { + $this->logger = $logger; + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + if (null !== $this->stopwatch) { + $this->stopwatch->start('doctrine', 'doctrine'); + } + + if (is_array($params)) { + $params = $this->normalizeParams($params); + } + + if (null !== $this->logger) { + $this->log($sql, null === $params ? array() : $params); + } + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + if (null !== $this->stopwatch) { + $this->stopwatch->stop('doctrine'); + } + } + + /** + * Logs a message. + * + * @param string $message A message to log + * @param array $params The context + */ + protected function log($message, array $params) + { + $this->logger->debug($message, $params); + } + + private function normalizeParams(array $params) + { + foreach ($params as $index => $param) { + // normalize recursively + if (is_array($param)) { + $params[$index] = $this->normalizeParams($param); + continue; + } + + if (!is_string($params[$index])) { + continue; + } + + // non utf-8 strings break json encoding + if (!preg_match('//u', $params[$index])) { + $params[$index] = self::BINARY_DATA_VALUE; + continue; + } + + // detect if the too long string must be shorten + if (self::MAX_STRING_LENGTH < iconv_strlen($params[$index], 'UTF-8')) { + $params[$index] = iconv_substr($params[$index], 0, self::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]'; + continue; + } + } + + return $params; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ManagerRegistry.php new file mode 100644 index 0000000..ff2c598 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Doctrine\Common\Persistence\AbstractManagerRegistry; + +/** + * References Doctrine connections and entity/document managers. + * + * @author Lukas Kahwe Smith + */ +abstract class ManagerRegistry extends AbstractManagerRegistry implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * {@inheritdoc} + */ + protected function getService($name) + { + return $this->container->get($name); + } + + /** + * {@inheritdoc} + */ + protected function resetService($name) + { + $this->container->set($name, null); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php new file mode 100644 index 0000000..dc22e23 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\PropertyInfo; + +use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; +use Doctrine\Common\Persistence\Mapping\MappingException; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\PropertyInfo\Type; + +/** + * Extracts data using Doctrine ORM and ODM metadata. + * + * @author Kévin Dunglas + */ +class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface +{ + /** + * @var ClassMetadataFactory + */ + private $classMetadataFactory; + + public function __construct(ClassMetadataFactory $classMetadataFactory) + { + $this->classMetadataFactory = $classMetadataFactory; + } + + /** + * {@inheritdoc} + */ + public function getProperties($class, array $context = array()) + { + try { + $metadata = $this->classMetadataFactory->getMetadataFor($class); + } catch (MappingException $exception) { + return; + } + + return array_merge($metadata->getFieldNames(), $metadata->getAssociationNames()); + } + + /** + * {@inheritdoc} + */ + public function getTypes($class, $property, array $context = array()) + { + try { + $metadata = $this->classMetadataFactory->getMetadataFor($class); + } catch (MappingException $exception) { + return; + } + + if ($metadata->hasAssociation($property)) { + $class = $metadata->getAssociationTargetClass($property); + + if ($metadata->isSingleValuedAssociation($property)) { + if ($metadata instanceof ClassMetadataInfo) { + $nullable = isset($metadata->discriminatorColumn['nullable']) ? $metadata->discriminatorColumn['nullable'] : false; + } else { + $nullable = false; + } + + return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class)); + } + + return array(new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + 'Doctrine\Common\Collections\Collection', + true, + new Type(Type::BUILTIN_TYPE_INT), + new Type(Type::BUILTIN_TYPE_OBJECT, false, $class) + )); + } + + if ($metadata->hasField($property)) { + $typeOfField = $metadata->getTypeOfField($property); + $nullable = $metadata instanceof ClassMetadataInfo && $metadata->isNullable($property); + + switch ($typeOfField) { + case 'date': + case 'datetime': + case 'datetimetz': + case 'time': + return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')); + + case 'array': + return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)); + + case 'simple_array': + return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))); + + case 'json_array': + return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)); + + default: + return array(new Type($this->getPhpType($typeOfField), $nullable)); + } + } + } + + /** + * Gets the corresponding built-in PHP type. + * + * @param string $doctrineType + * + * @return string + */ + private function getPhpType($doctrineType) + { + switch ($doctrineType) { + case 'smallint': + // No break + case 'bigint': + // No break + case 'integer': + return Type::BUILTIN_TYPE_INT; + + case 'decimal': + return Type::BUILTIN_TYPE_FLOAT; + + case 'text': + // No break + case 'guid': + return Type::BUILTIN_TYPE_STRING; + + case 'boolean': + return Type::BUILTIN_TYPE_BOOL; + + case 'blob': + // No break + case 'binary': + return Type::BUILTIN_TYPE_RESOURCE; + + default: + return $doctrineType; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/README.md b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/README.md new file mode 100644 index 0000000..3dabc3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/README.md @@ -0,0 +1,14 @@ +Doctrine Bridge +=============== + +Provides integration for [Doctrine](http://www.doctrine-project.org/) with +various Symfony components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/Doctrine/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/RegistryInterface.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/RegistryInterface.php new file mode 100644 index 0000000..9bc9821 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/RegistryInterface.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine; + +use Doctrine\Common\Persistence\ManagerRegistry as ManagerRegistryInterface; +use Doctrine\ORM\EntityManager; + +/** + * References Doctrine connections and entity managers. + * + * @author Fabien Potencier + */ +interface RegistryInterface extends ManagerRegistryInterface +{ + /** + * Gets the default entity manager name. + * + * @return string The default entity manager name + */ + public function getDefaultEntityManagerName(); + + /** + * Gets a named entity manager. + * + * @param string $name The entity manager name (null for the default one) + * + * @return EntityManager + */ + public function getEntityManager($name = null); + + /** + * Gets an array of all registered entity managers. + * + * @return array An array of EntityManager instances + */ + public function getEntityManagers(); + + /** + * Resets a named entity manager. + * + * This method is useful when an entity manager has been closed + * because of a rollbacked transaction AND when you think that + * it makes sense to get a new one to replace the closed one. + * + * Be warned that you will get a brand new entity manager as + * the existing one is not useable anymore. This means that any + * other object with a dependency on this entity manager will + * hold an obsolete reference. You can inject the registry instead + * to avoid this problem. + * + * @param string $name The entity manager name (null for the default one) + * + * @return EntityManager + */ + public function resetEntityManager($name = null); + + /** + * Resolves a registered namespace alias to the full namespace. + * + * This method looks for the alias in all registered entity managers. + * + * @param string $alias The alias + * + * @return string The full namespace + * + * @see Configuration::getEntityNamespace + */ + public function getEntityNamespace($alias); + + /** + * Gets all connection names. + * + * @return array An array of connection names + */ + public function getEntityManagerNames(); + + /** + * Gets the entity manager associated with a given class. + * + * @param string $class A Doctrine Entity class name + * + * @return EntityManager|null + */ + public function getEntityManagerForClass($class); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php new file mode 100644 index 0000000..b2216b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Security\RememberMe; + +use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; +use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface; +use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; +use Symfony\Component\Security\Core\Exception\TokenNotFoundException; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Type as DoctrineType; + +/** + * This class provides storage for the tokens that is set in "remember me" + * cookies. This way no password secrets will be stored in the cookies on + * the client machine, and thus the security is improved. + * + * This depends only on doctrine in order to get a database connection + * and to do the conversion of the datetime column. + * + * In order to use this class, you need the following table in your database: + * CREATE TABLE `rememberme_token` ( + * `series` char(88) UNIQUE PRIMARY KEY NOT NULL, + * `value` char(88) NOT NULL, + * `lastUsed` datetime NOT NULL, + * `class` varchar(100) NOT NULL, + * `username` varchar(200) NOT NULL + * ); + */ +class DoctrineTokenProvider implements TokenProviderInterface +{ + /** + * Doctrine DBAL database connection + * F.ex. service id: doctrine.dbal.default_connection. + * + * @var Connection + */ + private $conn; + + /** + * new DoctrineTokenProvider for the RememberMe authentication service. + * + * @param Connection $conn + */ + public function __construct(Connection $conn) + { + $this->conn = $conn; + } + + /** + * {@inheritdoc} + */ + public function loadTokenBySeries($series) + { + $sql = 'SELECT class, username, value, lastUsed' + .' FROM rememberme_token WHERE series=:series'; + $paramValues = array('series' => $series); + $paramTypes = array('series' => \PDO::PARAM_STR); + $stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if ($row) { + return new PersistentToken($row['class'], $row['username'], $series, $row['value'], new \DateTime($row['lastUsed'])); + } + + throw new TokenNotFoundException('No token found.'); + } + + /** + * {@inheritdoc} + */ + public function deleteTokenBySeries($series) + { + $sql = 'DELETE FROM rememberme_token WHERE series=:series'; + $paramValues = array('series' => $series); + $paramTypes = array('series' => \PDO::PARAM_STR); + $this->conn->executeUpdate($sql, $paramValues, $paramTypes); + } + + /** + * {@inheritdoc} + */ + public function updateToken($series, $tokenValue, \DateTime $lastUsed) + { + $sql = 'UPDATE rememberme_token SET value=:value, lastUsed=:lastUsed' + .' WHERE series=:series'; + $paramValues = array( + 'value' => $tokenValue, + 'lastUsed' => $lastUsed, + 'series' => $series, + ); + $paramTypes = array( + 'value' => \PDO::PARAM_STR, + 'lastUsed' => DoctrineType::DATETIME, + 'series' => \PDO::PARAM_STR, + ); + $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes); + if ($updated < 1) { + throw new TokenNotFoundException('No token found.'); + } + } + + /** + * {@inheritdoc} + */ + public function createNewToken(PersistentTokenInterface $token) + { + $sql = 'INSERT INTO rememberme_token' + .' (class, username, series, value, lastUsed)' + .' VALUES (:class, :username, :series, :value, :lastUsed)'; + $paramValues = array( + 'class' => $token->getClass(), + 'username' => $token->getUsername(), + 'series' => $token->getSeries(), + 'value' => $token->getTokenValue(), + 'lastUsed' => $token->getLastUsed(), + ); + $paramTypes = array( + 'class' => \PDO::PARAM_STR, + 'username' => \PDO::PARAM_STR, + 'series' => \PDO::PARAM_STR, + 'value' => \PDO::PARAM_STR, + 'lastUsed' => DoctrineType::DATETIME, + ); + $this->conn->executeUpdate($sql, $paramValues, $paramTypes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php new file mode 100644 index 0000000..0c747f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Security\User; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Wrapper around a Doctrine ObjectManager. + * + * Provides easy to use provisioning for Doctrine entity users. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class EntityUserProvider implements UserProviderInterface +{ + private $registry; + private $managerName; + private $classOrAlias; + private $class; + private $property; + + public function __construct(ManagerRegistry $registry, $classOrAlias, $property = null, $managerName = null) + { + $this->registry = $registry; + $this->managerName = $managerName; + $this->classOrAlias = $classOrAlias; + $this->property = $property; + } + + /** + * {@inheritdoc} + */ + public function loadUserByUsername($username) + { + $repository = $this->getRepository(); + if (null !== $this->property) { + $user = $repository->findOneBy(array($this->property => $username)); + } else { + if (!$repository instanceof UserLoaderInterface) { + throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface.', get_class($repository))); + } + + $user = $repository->loadUserByUsername($username); + } + + if (null === $user) { + throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); + } + + return $user; + } + + /** + * {@inheritdoc} + */ + public function refreshUser(UserInterface $user) + { + $class = $this->getClass(); + if (!$user instanceof $class) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); + } + + $repository = $this->getRepository(); + if ($repository instanceof UserProviderInterface) { + $refreshedUser = $repository->refreshUser($user); + } else { + // The user must be reloaded via the primary key as all other data + // might have changed without proper persistence in the database. + // That's the case when the user has been changed by a form with + // validation errors. + if (!$id = $this->getClassMetadata()->getIdentifierValues($user)) { + throw new \InvalidArgumentException('You cannot refresh a user '. + 'from the EntityUserProvider that does not contain an identifier. '. + 'The user object has to be serialized with its own identifier '. + 'mapped by Doctrine.' + ); + } + + $refreshedUser = $repository->find($id); + if (null === $refreshedUser) { + throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($id))); + } + } + + return $refreshedUser; + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return $class === $this->getClass() || is_subclass_of($class, $this->getClass()); + } + + private function getObjectManager() + { + return $this->registry->getManager($this->managerName); + } + + private function getRepository() + { + return $this->getObjectManager()->getRepository($this->classOrAlias); + } + + private function getClass() + { + if (null === $this->class) { + $class = $this->classOrAlias; + + if (false !== strpos($class, ':')) { + $class = $this->getClassMetadata()->getName(); + } + + $this->class = $class; + } + + return $this->class; + } + + private function getClassMetadata() + { + return $this->getObjectManager()->getClassMetadata($this->classOrAlias); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php new file mode 100644 index 0000000..452939f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Security/User/UserLoaderInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Security\User; + +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Represents a class that loads UserInterface objects from Doctrine source for the authentication system. + * + * This interface is meant to facilitate the loading of a User from Doctrine source using a custom method. + * If you want to implement your own logic of retrieving the user from Doctrine your repository should implement this + * interface. + * + * @see UserInterface + * + * @author Michal Trojanowski + */ +interface UserLoaderInterface +{ + /** + * Loads the user for the given username. + * + * This method must return null if the user is not found. + * + * @param string $username The username + * + * @return UserInterface|null + */ + public function loadUserByUsername($username); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php new file mode 100644 index 0000000..962099e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Test; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\EntityManager; + +/** + * Provides utility functions needed in tests. + * + * @author Bernhard Schussek + */ +class DoctrineTestHelper +{ + /** + * Returns an entity manager for testing. + * + * @return EntityManager + */ + public static function createTestEntityManager() + { + if (!extension_loaded('pdo_sqlite')) { + \PHPUnit_Framework_TestCase::markTestSkipped('Extension pdo_sqlite is required.'); + } + + $config = new \Doctrine\ORM\Configuration(); + $config->setEntityNamespaces(array('SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures')); + $config->setAutoGenerateProxyClasses(true); + $config->setProxyDir(\sys_get_temp_dir()); + $config->setProxyNamespace('SymfonyTests\Doctrine'); + $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader())); + $config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache()); + $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache()); + + $params = array( + 'driver' => 'pdo_sqlite', + 'memory' => true, + ); + + return EntityManager::create($params, $config); + } + + /** + * This class cannot be instantiated. + */ + private function __construct() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php new file mode 100644 index 0000000..55991db --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests; + +use Symfony\Bridge\Doctrine\ContainerAwareEventManager; +use Symfony\Component\DependencyInjection\Container; + +class ContainerAwareEventManagerTest extends \PHPUnit_Framework_TestCase +{ + private $container; + private $evm; + + protected function setUp() + { + $this->container = new Container(); + $this->evm = new ContainerAwareEventManager($this->container); + } + + public function testDispatchEvent() + { + $this->container->set('foobar', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'foobar'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->evm->dispatchEvent('foo'); + + $this->assertTrue($listener1->called); + $this->assertTrue($listener2->called); + } + + public function testRemoveEventListener() + { + $this->evm->addEventListener('foo', 'bar'); + $this->evm->addEventListener('foo', $listener = new MyListener()); + + $listeners = array('foo' => array('_service_bar' => 'bar', spl_object_hash($listener) => $listener)); + $this->assertSame($listeners, $this->evm->getListeners()); + $this->assertSame($listeners['foo'], $this->evm->getListeners('foo')); + + $this->evm->removeEventListener('foo', $listener); + $this->assertSame(array('_service_bar' => 'bar'), $this->evm->getListeners('foo')); + + $this->evm->removeEventListener('foo', 'bar'); + $this->assertSame(array(), $this->evm->getListeners('foo')); + } +} + +class MyListener +{ + public $called = false; + + public function foo() + { + $this->called = true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php new file mode 100644 index 0000000..45d0310 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\DataCollector; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class DoctrineDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollectConnections() + { + $c = $this->createCollector(array()); + $c->collect(new Request(), new Response()); + $this->assertEquals(array('default' => 'doctrine.dbal.default_connection'), $c->getConnections()); + } + + public function testCollectManagers() + { + $c = $this->createCollector(array()); + $c->collect(new Request(), new Response()); + $this->assertEquals(array('default' => 'doctrine.orm.default_entity_manager'), $c->getManagers()); + } + + public function testCollectQueryCount() + { + $c = $this->createCollector(array()); + $c->collect(new Request(), new Response()); + $this->assertEquals(0, $c->getQueryCount()); + + $queries = array( + array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 0), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + $this->assertEquals(1, $c->getQueryCount()); + } + + public function testCollectTime() + { + $c = $this->createCollector(array()); + $c->collect(new Request(), new Response()); + $this->assertEquals(0, $c->getTime()); + + $queries = array( + array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + $this->assertEquals(1, $c->getTime()); + + $queries = array( + array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), + array('sql' => 'SELECT * FROM table2', 'params' => array(), 'types' => array(), 'executionMS' => 2), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + $this->assertEquals(3, $c->getTime()); + } + + /** + * @dataProvider paramProvider + */ + public function testCollectQueries($param, $types, $expected, $explainable) + { + $queries = array( + array('sql' => 'SELECT * FROM table1 WHERE field1 = ?1', 'params' => array($param), 'types' => $types, 'executionMS' => 1), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + + $collectedQueries = $c->getQueries(); + $this->assertEquals($expected, $collectedQueries['default'][0]['params'][0]); + $this->assertEquals($explainable, $collectedQueries['default'][0]['explainable']); + } + + public function testCollectQueryWithNoParams() + { + $queries = array( + array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), + array('sql' => 'SELECT * FROM table1', 'params' => null, 'types' => null, 'executionMS' => 1), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + + $collectedQueries = $c->getQueries(); + $this->assertEquals(array(), $collectedQueries['default'][0]['params']); + $this->assertTrue($collectedQueries['default'][0]['explainable']); + $this->assertEquals(array(), $collectedQueries['default'][1]['params']); + $this->assertTrue($collectedQueries['default'][1]['explainable']); + } + + /** + * @dataProvider paramProvider + */ + public function testSerialization($param, $types, $expected, $explainable) + { + $queries = array( + array('sql' => 'SELECT * FROM table1 WHERE field1 = ?1', 'params' => array($param), 'types' => $types, 'executionMS' => 1), + ); + $c = $this->createCollector($queries); + $c->collect(new Request(), new Response()); + $c = unserialize(serialize($c)); + + $collectedQueries = $c->getQueries(); + $this->assertEquals($expected, $collectedQueries['default'][0]['params'][0]); + $this->assertEquals($explainable, $collectedQueries['default'][0]['explainable']); + } + + public function paramProvider() + { + return array( + array('some value', array(), 'some value', true), + array(1, array(), 1, true), + array(true, array(), true, true), + array(null, array(), null, true), + array(new \DateTime('2011-09-11'), array('date'), '2011-09-11', true), + array(fopen(__FILE__, 'r'), array(), 'Resource(stream)', false), + array(new \SplFileInfo(__FILE__), array(), 'Object(SplFileInfo)', false), + ); + } + + private function createCollector($queries) + { + $connection = $this->getMockBuilder('Doctrine\DBAL\Connection') + ->disableOriginalConstructor() + ->getMock(); + $connection->expects($this->any()) + ->method('getDatabasePlatform') + ->will($this->returnValue(new MySqlPlatform())); + + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry + ->expects($this->any()) + ->method('getConnectionNames') + ->will($this->returnValue(array('default' => 'doctrine.dbal.default_connection'))); + $registry + ->expects($this->any()) + ->method('getManagerNames') + ->will($this->returnValue(array('default' => 'doctrine.orm.default_entity_manager'))); + $registry->expects($this->any()) + ->method('getConnection') + ->will($this->returnValue($connection)); + + $logger = $this->getMock('Doctrine\DBAL\Logging\DebugStack'); + $logger->queries = $queries; + + $collector = new DoctrineDataCollector($registry); + $collector->addLogger('default', $logger); + + return $collector; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php new file mode 100644 index 0000000..53ad5a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DataFixtures/ContainerAwareLoaderTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\DataFixtures; + +use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader; +use Symfony\Bridge\Doctrine\Tests\Fixtures\ContainerAwareFixture; + +class ContainerAwareLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testShouldSetContainerOnContainerAwareFixture() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $loader = new ContainerAwareLoader($container); + $fixture = new ContainerAwareFixture(); + + $loader->addFixture($fixture); + + $this->assertSame($container, $fixture->container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php new file mode 100644 index 0000000..11bffb9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection\CompilerPass; + +use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +class RegisterEventListenersAndSubscribersPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testExceptionOnAbstractTaggedSubscriber() + { + $container = $this->createBuilder(); + + $abstractDefinition = new Definition('stdClass'); + $abstractDefinition->setAbstract(true); + $abstractDefinition->addTag('doctrine.event_subscriber'); + + $container->setDefinition('a', $abstractDefinition); + + $this->process($container); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testExceptionOnAbstractTaggedListener() + { + $container = $this->createBuilder(); + + $abstractDefinition = new Definition('stdClass'); + $abstractDefinition->setAbstract(true); + $abstractDefinition->addTag('doctrine.event_listener', array('event' => 'test')); + + $container->setDefinition('a', $abstractDefinition); + + $this->process($container); + } + + public function testProcessEventListenersWithPriorities() + { + $container = $this->createBuilder(); + + $container + ->register('a', 'stdClass') + ->addTag('doctrine.event_listener', array( + 'event' => 'foo', + 'priority' => -5, + )) + ->addTag('doctrine.event_listener', array( + 'event' => 'bar', + )) + ; + $container + ->register('b', 'stdClass') + ->addTag('doctrine.event_listener', array( + 'event' => 'foo', + )) + ; + + $this->process($container); + $this->assertEquals(array('b', 'a'), $this->getServiceOrder($container, 'addEventListener')); + + $calls = $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls(); + $this->assertEquals(array('foo', 'bar'), $calls[1][1][0]); + } + + public function testProcessEventListenersWithMultipleConnections() + { + $container = $this->createBuilder(true); + + $container + ->register('a', 'stdClass') + ->addTag('doctrine.event_listener', array( + 'event' => 'onFlush', + )) + ; + $this->process($container); + + $callsDefault = $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls(); + + $this->assertEquals('addEventListener', $callsDefault[0][0]); + $this->assertEquals(array('onFlush'), $callsDefault[0][1][0]); + + $callsSecond = $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls(); + $this->assertEquals($callsDefault, $callsSecond); + } + + public function testProcessEventSubscribersWithPriorities() + { + $container = $this->createBuilder(); + + $container + ->register('a', 'stdClass') + ->addTag('doctrine.event_subscriber') + ; + $container + ->register('b', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'priority' => 5, + )) + ; + $container + ->register('c', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'priority' => 10, + )) + ; + $container + ->register('d', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'priority' => 10, + )) + ; + $container + ->register('e', 'stdClass') + ->addTag('doctrine.event_subscriber', array( + 'priority' => 10, + )) + ; + + $this->process($container); + $serviceOrder = $this->getServiceOrder($container, 'addEventSubscriber'); + $unordered = array_splice($serviceOrder, 0, 3); + sort($unordered); + $this->assertEquals(array('c', 'd', 'e'), $unordered); + $this->assertEquals(array('b', 'a'), $serviceOrder); + } + + public function testProcessNoTaggedServices() + { + $container = $this->createBuilder(true); + + $this->process($container); + + $this->assertEquals(array(), $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls()); + + $this->assertEquals(array(), $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls()); + } + + private function process(ContainerBuilder $container) + { + $pass = new RegisterEventListenersAndSubscribersPass('doctrine.connections', 'doctrine.dbal.%s_connection.event_manager', 'doctrine'); + $pass->process($container); + } + + private function getServiceOrder(ContainerBuilder $container, $method) + { + $order = array(); + foreach ($container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() as list($name, $arguments)) { + if ($method !== $name) { + continue; + } + + if ('addEventListener' === $name) { + $order[] = (string) $arguments[1]; + continue; + } + + $order[] = (string) $arguments[0]; + } + + return $order; + } + + private function createBuilder($multipleConnections = false) + { + $container = new ContainerBuilder(); + + $connections = array('default' => 'doctrine.dbal.default_connection'); + + $container->register('doctrine.dbal.default_connection.event_manager', 'stdClass'); + $container->register('doctrine.dbal.default_connection', 'stdClass'); + + if ($multipleConnections) { + $container->register('doctrine.dbal.second_connection.event_manager', 'stdClass'); + $container->register('doctrine.dbal.second_connection', 'stdClass'); + $connections['second'] = 'doctrine.dbal.second_connection'; + } + + $container->setParameter('doctrine.connections', $connections); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php new file mode 100644 index 0000000..760c0fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +/** + * @author Fabio B. Silva + */ +class DoctrineExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension + */ + private $extension; + + protected function setUp() + { + parent::setUp(); + + $this->extension = $this + ->getMockBuilder('Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension') + ->setMethods(array( + 'getMappingResourceConfigDirectory', + 'getObjectManagerElementName', + 'getMappingObjectDefaultName', + 'getMappingResourceExtension', + 'load', + )) + ->getMock() + ; + + $this->extension->expects($this->any()) + ->method('getObjectManagerElementName') + ->will($this->returnCallback(function ($name) { + return 'doctrine.orm.'.$name; + })); + } + + /** + * @expectedException \LogicException + */ + public function testFixManagersAutoMappingsWithTwoAutomappings() + { + $emConfigs = array( + 'em1' => array( + 'auto_mapping' => true, + ), + 'em2' => array( + 'auto_mapping' => true, + ), + ); + + $bundles = array( + 'FristBundle' => 'My\FristBundle', + 'SecondBundle' => 'My\SecondBundle', + ); + + $reflection = new \ReflectionClass(get_class($this->extension)); + $method = $reflection->getMethod('fixManagersAutoMappings'); + $method->setAccessible(true); + + $method->invoke($this->extension, $emConfigs, $bundles); + } + + public function getAutomappingData() + { + return array( + array( + array( // no auto mapping on em1 + 'auto_mapping' => false, + ), + array( // no auto mapping on em2 + 'auto_mapping' => false, + ), + array(), + array(), + ), + array( + array( // no auto mapping on em1 + 'auto_mapping' => false, + ), + array( // auto mapping enabled on em2 + 'auto_mapping' => true, + ), + array(), + array( + 'mappings' => array( + 'FristBundle' => array( + 'mapping' => true, + 'is_bundle' => true, + ), + 'SecondBundle' => array( + 'mapping' => true, + 'is_bundle' => true, + ), + ), + ), + ), + array( + array( // no auto mapping on em1, but it defines SecondBundle as own + 'auto_mapping' => false, + 'mappings' => array( + 'SecondBundle' => array( + 'mapping' => true, + 'is_bundle' => true, + ), + ), + ), + array( // auto mapping enabled on em2 + 'auto_mapping' => true, + ), + array( + 'mappings' => array( + 'SecondBundle' => array( + 'mapping' => true, + 'is_bundle' => true, + ), + ), + ), + array( + 'mappings' => array( + 'FristBundle' => array( + 'mapping' => true, + 'is_bundle' => true, + ), + ), + ), + ), + ); + } + + /** + * @dataProvider getAutomappingData + */ + public function testFixManagersAutoMappings(array $originalEm1, array $originalEm2, array $expectedEm1, array $expectedEm2) + { + $emConfigs = array( + 'em1' => $originalEm1, + 'em2' => $originalEm2, + ); + + $bundles = array( + 'FristBundle' => 'My\FristBundle', + 'SecondBundle' => 'My\SecondBundle', + ); + + $reflection = new \ReflectionClass(get_class($this->extension)); + $method = $reflection->getMethod('fixManagersAutoMappings'); + $method->setAccessible(true); + + $newEmConfigs = $method->invoke($this->extension, $emConfigs, $bundles); + + $this->assertEquals($newEmConfigs['em1'], array_merge(array( + 'auto_mapping' => false, + ), $expectedEm1)); + $this->assertEquals($newEmConfigs['em2'], array_merge(array( + 'auto_mapping' => false, + ), $expectedEm2)); + } + + public function providerBasicDrivers() + { + return array( + array('doctrine.orm.cache.apc.class', array('type' => 'apc')), + array('doctrine.orm.cache.array.class', array('type' => 'array')), + array('doctrine.orm.cache.xcache.class', array('type' => 'xcache')), + array('doctrine.orm.cache.wincache.class', array('type' => 'wincache')), + array('doctrine.orm.cache.zenddata.class', array('type' => 'zenddata')), + array('doctrine.orm.cache.redis.class', array('type' => 'redis'), array('setRedis')), + array('doctrine.orm.cache.memcache.class', array('type' => 'memcache'), array('setMemcache')), + array('doctrine.orm.cache.memcached.class', array('type' => 'memcached'), array('setMemcached')), + ); + } + + /** + * @param string $class + * @param array $config + * + * @dataProvider providerBasicDrivers + */ + public function testLoadBasicCacheDriver($class, array $config, array $expectedCalls = array()) + { + $container = $this->createContainer(); + $cacheName = 'metadata_cache'; + $objectManager = array( + 'name' => 'default', + 'metadata_cache_driver' => $config, + ); + + $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); + + $this->assertTrue($container->hasDefinition('doctrine.orm.default_metadata_cache')); + + $definition = $container->getDefinition('doctrine.orm.default_metadata_cache'); + $defCalls = $definition->getMethodCalls(); + $expectedCalls[] = 'setNamespace'; + $actualCalls = array_map(function ($call) { + return $call[0]; + }, $defCalls); + + $this->assertFalse($definition->isPublic()); + $this->assertEquals("%$class%", $definition->getClass()); + + foreach (array_unique($expectedCalls) as $call) { + $this->assertContains($call, $actualCalls); + } + } + + public function testServiceCacheDriver() + { + $cacheName = 'metadata_cache'; + $container = $this->createContainer(); + $definition = new Definition('%doctrine.orm.cache.apc.class%'); + $objectManager = array( + 'name' => 'default', + 'metadata_cache_driver' => array( + 'type' => 'service', + 'id' => 'service_driver', + ), + ); + + $container->setDefinition('service_driver', $definition); + + $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); + + $this->assertTrue($container->hasAlias('doctrine.orm.default_metadata_cache')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage "unrecognized_type" is an unrecognized Doctrine cache driver. + */ + public function testUnrecognizedCacheDriverException() + { + $cacheName = 'metadata_cache'; + $container = $this->createContainer(); + $objectManager = array( + 'name' => 'default', + 'metadata_cache_driver' => array( + 'type' => 'unrecognized_type', + ), + ); + + $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); + } + + protected function invokeLoadCacheDriver(array $objectManager, ContainerBuilder $container, $cacheName) + { + $method = new \ReflectionMethod($this->extension, 'loadObjectManagerCacheDriver'); + + $method->setAccessible(true); + + $method->invokeArgs($this->extension, array($objectManager, $container, $cacheName)); + } + + /** + * @param array $data + * + * @return \Symfony\Component\DependencyInjection\ContainerBuilder + */ + protected function createContainer(array $data = array()) + { + return new ContainerBuilder(new ParameterBag(array_merge(array( + 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), + 'kernel.cache_dir' => __DIR__, + 'kernel.debug' => false, + 'kernel.environment' => 'test', + 'kernel.name' => 'kernel', + 'kernel.root_dir' => __DIR__, + ), $data))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php new file mode 100644 index 0000000..a473b3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/ExpressionLanguage/DoctrineParserCacheTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\ExpressionLanguage; + +use Symfony\Bridge\Doctrine\ExpressionLanguage\DoctrineParserCache; + +class DoctrineParserCacheTest extends \PHPUnit_Framework_TestCase +{ + public function testFetch() + { + $doctrineCacheMock = $this->getMock('Doctrine\Common\Cache\Cache'); + $parserCache = new DoctrineParserCache($doctrineCacheMock); + + $doctrineCacheMock->expects($this->once()) + ->method('fetch') + ->will($this->returnValue('bar')); + + $result = $parserCache->fetch('foo'); + + $this->assertEquals('bar', $result); + } + + public function testFetchUnexisting() + { + $doctrineCacheMock = $this->getMock('Doctrine\Common\Cache\Cache'); + $parserCache = new DoctrineParserCache($doctrineCacheMock); + + $doctrineCacheMock + ->expects($this->once()) + ->method('fetch') + ->will($this->returnValue(false)); + + $this->assertNull($parserCache->fetch('')); + } + + public function testSave() + { + $doctrineCacheMock = $this->getMock('Doctrine\Common\Cache\Cache'); + $parserCache = new DoctrineParserCache($doctrineCacheMock); + + $expression = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParsedExpression') + ->disableOriginalConstructor() + ->getMock(); + + $doctrineCacheMock->expects($this->once()) + ->method('save') + ->with('foo', $expression); + + $parserCache->save('foo', $expression); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php new file mode 100644 index 0000000..c6d689a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class AssociationEntity +{ + /** + * @var int + * @ORM\Id @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; + + /** + * @ORM\ManyToOne(targetEntity="SingleIntIdEntity") + * + * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity + */ + public $single; + + /** + * @ORM\ManyToOne(targetEntity="CompositeIntIdEntity") + * @ORM\JoinColumns({ + * @ORM\JoinColumn(name="composite_id1", referencedColumnName="id1"), + * @ORM\JoinColumn(name="composite_id2", referencedColumnName="id2") + * }) + * + * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity + */ + public $composite; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php new file mode 100644 index 0000000..740a4f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class CompositeIntIdEntity +{ + /** @Id @Column(type="integer") */ + protected $id1; + + /** @Id @Column(type="integer") */ + protected $id2; + + /** @Column(type="string") */ + public $name; + + public function __construct($id1, $id2, $name) + { + $this->id1 = $id1; + $this->id2 = $id2; + $this->name = $name; + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php new file mode 100644 index 0000000..10e083a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class CompositeStringIdEntity +{ + /** @Id @Column(type="string") */ + protected $id1; + + /** @Id @Column(type="string") */ + protected $id2; + + /** @Column(type="string") */ + public $name; + + public function __construct($id1, $id2, $name) + { + $this->id1 = $id1; + $this->id2 = $id2; + $this->name = $name; + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php new file mode 100644 index 0000000..5141e16 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\Common\DataFixtures\FixtureInterface; +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; + +class ContainerAwareFixture implements FixtureInterface, ContainerAwareInterface +{ + public $container; + + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } + + public function load(ObjectManager $manager) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php new file mode 100644 index 0000000..cfb8e8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class DoubleNameEntity +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + /** @Column(type="string", nullable=true) */ + public $name2; + + public function __construct($id, $name, $name2) + { + $this->id = $id; + $this->name = $name; + $this->name2 = $name2; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php new file mode 100644 index 0000000..f8000db --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures\Embeddable; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Embeddable + */ +class Identifier +{ + /** + * @var int + * + * @ORM\Id + * @ORM\Column(type="integer") + */ + protected $value; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php new file mode 100644 index 0000000..6d7b267 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + */ +class EmbeddedIdentifierEntity +{ + /** + * @var Embeddable\Identifier + * + * @ORM\Embedded(class="Symfony\Bridge\Doctrine\Tests\Fixtures\Embeddable\Identifier") + */ + protected $id; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php new file mode 100644 index 0000000..2e36204 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class GroupableEntity +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string", nullable=true) */ + public $name; + + /** @Column(type="string", nullable=true) */ + public $groupName; + + public function __construct($id, $name, $groupName) + { + $this->id = $id; + $this->name = $name; + $this->groupName = $groupName; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php new file mode 100644 index 0000000..954de33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\OneToOne; + +/** @Entity */ +class SingleAssociationToIntIdEntity +{ + /** @Id @OneToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"ALL"}) */ + protected $entity; + + /** @Column(type="string", nullable=true) */ + public $name; + + public function __construct(SingleIntIdNoToStringEntity $entity, $name) + { + $this->entity = $entity; + $this->name = $name; + } + + public function __toString() + { + return (string) $this->name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php new file mode 100644 index 0000000..44630a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class SingleIntIdEntity +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string", nullable=true) */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString() + { + return (string) $this->name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php new file mode 100644 index 0000000..bcbe7a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class SingleIntIdNoToStringEntity +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string", nullable=true) */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php new file mode 100644 index 0000000..e457f69 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\GeneratedValue; +use Doctrine\ORM\Mapping\Id; + +/** @Entity */ +class SingleStringCastableIdEntity +{ + /** + * @Id + * @Column(type="string") + * @GeneratedValue(strategy="NONE") + */ + protected $id; + + /** @Column(type="string", nullable=true) */ + public $name; + + public function __construct($id, $name) + { + $this->id = new StringCastableObjectIdentity($id); + $this->name = $name; + } + + public function __toString() + { + return (string) $this->name; + } +} + +class StringCastableObjectIdentity +{ + protected $id; + + public function __construct($id) + { + $this->id = $id; + } + + public function __toString() + { + return (string) $this->id; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php new file mode 100644 index 0000000..258c5a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class SingleStringIdEntity +{ + /** @Id @Column(type="string") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php new file mode 100644 index 0000000..e59e32c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Symfony\Component\Security\Core\User\UserInterface; + +/** @Entity */ +class User implements UserInterface +{ + /** @Id @Column(type="integer") */ + protected $id1; + + /** @Id @Column(type="integer") */ + protected $id2; + + /** @Column(type="string") */ + public $name; + + public function __construct($id1, $id2, $name) + { + $this->id1 = $id1; + $this->id2 = $id2; + $this->name = $name; + } + + public function getRoles() + { + } + + public function getPassword() + { + } + + public function getSalt() + { + } + + public function getUsername() + { + return $this->name; + } + + public function eraseCredentials() + { + } + + public function equals(UserInterface $user) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php new file mode 100644 index 0000000..4bfb789 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; + +use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; +use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\Version; + +class ORMQueryBuilderLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testIdentifierTypeIsStringArray() + { + $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', Connection::PARAM_STR_ARRAY); + } + + public function testIdentifierTypeIsIntegerArray() + { + $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', Connection::PARAM_INT_ARRAY); + } + + protected function checkIdentifierType($classname, $expectedType) + { + $em = DoctrineTestHelper::createTestEntityManager(); + + $query = $this->getMockBuilder('QueryMock') + ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->getMock(); + + $query->expects($this->once()) + ->method('setParameter') + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array(1, 2), $expectedType) + ->willReturn($query); + + $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') + ->setConstructorArgs(array($em)) + ->setMethods(array('getQuery')) + ->getMock(); + + $qb->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + + $qb->select('e') + ->from($classname, 'e'); + + $loader = new ORMQueryBuilderLoader($qb); + $loader->getEntitiesByIds('id', array(1, 2)); + } + + public function testFilterNonIntegerValues() + { + $em = DoctrineTestHelper::createTestEntityManager(); + + $query = $this->getMockBuilder('QueryMock') + ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->getMock(); + + $query->expects($this->once()) + ->method('setParameter') + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array(1, 2, 3), Connection::PARAM_INT_ARRAY) + ->willReturn($query); + + $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') + ->setConstructorArgs(array($em)) + ->setMethods(array('getQuery')) + ->getMock(); + + $qb->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + + $qb->select('e') + ->from('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', 'e'); + + $loader = new ORMQueryBuilderLoader($qb); + $loader->getEntitiesByIds('id', array(1, '', 2, 3, 'foo')); + } + + public function testEmbeddedIdentifierName() + { + if (Version::compare('2.5.0') > 0) { + $this->markTestSkipped('Applicable only for Doctrine >= 2.5.0'); + + return; + } + + $em = DoctrineTestHelper::createTestEntityManager(); + + $query = $this->getMockBuilder('QueryMock') + ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->getMock(); + + $query->expects($this->once()) + ->method('setParameter') + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', array(1, 2, 3), Connection::PARAM_INT_ARRAY) + ->willReturn($query); + + $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') + ->setConstructorArgs(array($em)) + ->setMethods(array('getQuery')) + ->getMock(); + $qb->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + + $qb->select('e') + ->from('Symfony\Bridge\Doctrine\Tests\Fixtures\EmbeddedIdentifierEntity', 'e'); + + $loader = new ORMQueryBuilderLoader($qb); + $loader->getEntitiesByIds('id.value', array(1, '', 2, 3, 'foo')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php new file mode 100644 index 0000000..50b9bcd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Form\DataTransformer; + +use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer; + +/** + * @author Bernhard Schussek + */ +class CollectionToArrayTransformerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var CollectionToArrayTransformer + */ + private $transformer; + + protected function setUp() + { + $this->transformer = new CollectionToArrayTransformer(); + } + + public function testTransform() + { + $array = array( + 2 => 'foo', + 3 => 'bar', + ); + + $this->assertSame($array, $this->transformer->transform(new ArrayCollection($array))); + } + + /** + * This test is needed for cases when getXxxs() in the entity returns the + * result of $collection->toArray(), in order to prevent modifications of + * the inner collection. + * + * See https://github.com/symfony/symfony/pull/9308 + */ + public function testTransformArray() + { + $array = array( + 2 => 'foo', + 3 => 'bar', + ); + + $this->assertSame($array, $this->transformer->transform($array)); + } + + public function testTransformNull() + { + $this->assertSame(array(), $this->transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformExpectsArrayOrCollection() + { + $this->transformer->transform('Foo'); + } + + public function testReverseTransform() + { + $array = array( + 2 => 'foo', + 3 => 'bar', + ); + + $this->assertEquals(new ArrayCollection($array), $this->transformer->reverseTransform($array)); + } + + public function testReverseTransformEmpty() + { + $this->assertEquals(new ArrayCollection(), $this->transformer->reverseTransform('')); + } + + public function testReverseTransformNull() + { + $this->assertEquals(new ArrayCollection(), $this->transformer->reverseTransform(null)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php new file mode 100644 index 0000000..0cb900f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Form; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\ValueGuess; + +class DoctrineOrmTypeGuesserTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider requiredProvider + */ + public function testRequiredGuesser($classMetadata, $expected) + { + $this->assertEquals($expected, $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); + } + + public function requiredProvider() + { + $return = array(); + + // Simple field, not nullable + $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(true)); + $classMetadata->expects($this->once())->method('isNullable')->with('field')->will($this->returnValue(false)); + + $return[] = array($classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)); + + // Simple field, nullable + $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(true)); + $classMetadata->expects($this->once())->method('isNullable')->with('field')->will($this->returnValue(true)); + + $return[] = array($classMetadata, new ValueGuess(false, Guess::MEDIUM_CONFIDENCE)); + + // One-to-one, nullable (by default) + $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); + $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); + + $mapping = array('joinColumns' => array(array())); + $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->will($this->returnValue($mapping)); + + $return[] = array($classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)); + + // One-to-one, nullable (explicit) + $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); + $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); + + $mapping = array('joinColumns' => array(array('nullable' => true))); + $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->will($this->returnValue($mapping)); + + $return[] = array($classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)); + + // One-to-one, not nullable + $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); + $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); + + $mapping = array('joinColumns' => array(array('nullable' => false))); + $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->will($this->returnValue($mapping)); + + $return[] = array($classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)); + + // One-to-many, no clue + $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $classMetadata->expects($this->once())->method('hasField')->with('field')->will($this->returnValue(false)); + $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(false)); + + $return[] = array($classMetadata, null); + + return $return; + } + + private function getGuesser(ClassMetadata $classMetadata) + { + $em = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + $em->expects($this->once())->method('getClassMetaData')->with('TestEntity')->will($this->returnValue($classMetadata)); + + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry->expects($this->once())->method('getManagers')->will($this->returnValue(array($em))); + + return new DoctrineOrmTypeGuesser($registry); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php new file mode 100644 index 0000000..57df419 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Form\Type; + +use Symfony\Component\Form\Test\FormPerformanceTestCase; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; +use Doctrine\ORM\Tools\SchemaTool; +use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; +use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; + +/** + * @author Bernhard Schussek + */ +class EntityTypePerformanceTest extends FormPerformanceTestCase +{ + const ENTITY_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; + + /** + * @var \Doctrine\ORM\EntityManager + */ + private $em; + + protected function getExtensions() + { + $manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + + $manager->expects($this->any()) + ->method('getManager') + ->will($this->returnValue($this->em)); + + $manager->expects($this->any()) + ->method('getManagerForClass') + ->will($this->returnValue($this->em)); + + return array( + new CoreExtension(), + new DoctrineOrmExtension($manager), + ); + } + + protected function setUp() + { + $this->em = DoctrineTestHelper::createTestEntityManager(); + + parent::setUp(); + + $schemaTool = new SchemaTool($this->em); + $classes = array( + $this->em->getClassMetadata(self::ENTITY_CLASS), + ); + + try { + $schemaTool->dropSchema($classes); + } catch (\Exception $e) { + } + + try { + $schemaTool->createSchema($classes); + } catch (\Exception $e) { + } + + $ids = range(1, 300); + + foreach ($ids as $id) { + $name = 65 + chr($id % 57); + $this->em->persist(new SingleIntIdEntity($id, $name)); + } + + $this->em->flush(); + } + + /** + * This test case is realistic in collection forms where each + * row contains the same entity field. + * + * @group benchmark + */ + public function testCollapsedEntityField() + { + $this->setMaxRunningTime(1); + + for ($i = 0; $i < 40; ++$i) { + $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'class' => self::ENTITY_CLASS, + )); + + // force loading of the choice list + $form->createView(); + } + } + + /** + * @group benchmark + */ + public function testCollapsedEntityFieldWithChoices() + { + $choices = $this->em->createQuery('SELECT c FROM '.self::ENTITY_CLASS.' c')->getResult(); + $this->setMaxRunningTime(1); + + for ($i = 0; $i < 40; ++$i) { + $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'class' => self::ENTITY_CLASS, + 'choices' => $choices, + )); + + // force loading of the choice list + $form->createView(); + } + } + + /** + * @group benchmark + */ + public function testCollapsedEntityFieldWithPreferredChoices() + { + $choices = $this->em->createQuery('SELECT c FROM '.self::ENTITY_CLASS.' c')->getResult(); + $this->setMaxRunningTime(1); + + for ($i = 0; $i < 40; ++$i) { + $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'class' => self::ENTITY_CLASS, + 'preferred_choices' => $choices, + )); + + // force loading of the choice list + $form->createView(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php new file mode 100644 index 0000000..d090fd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -0,0 +1,1140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Form\Type; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Tools\SchemaTool; +use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; +use Symfony\Bridge\Doctrine\Form\DoctrineOrmTypeGuesser; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\GroupableEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringCastableIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity; +use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\Forms; +use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity; + +class EntityTypeTest extends TypeTestCase +{ + const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\GroupableEntity'; + const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'; + const SINGLE_IDENT_NO_TO_STRING_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity'; + const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity'; + const SINGLE_ASSOC_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity'; + const SINGLE_STRING_CASTABLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringCastableIdEntity'; + const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'; + const COMPOSITE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdEntity'; + + /** + * @var EntityManager + */ + private $em; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ManagerRegistry + */ + private $emRegistry; + + protected function setUp() + { + $this->em = DoctrineTestHelper::createTestEntityManager(); + $this->emRegistry = $this->createRegistryMock('default', $this->em); + + parent::setUp(); + + $schemaTool = new SchemaTool($this->em); + $classes = array( + $this->em->getClassMetadata(self::ITEM_GROUP_CLASS), + $this->em->getClassMetadata(self::SINGLE_IDENT_CLASS), + $this->em->getClassMetadata(self::SINGLE_IDENT_NO_TO_STRING_CLASS), + $this->em->getClassMetadata(self::SINGLE_STRING_IDENT_CLASS), + $this->em->getClassMetadata(self::SINGLE_ASSOC_IDENT_CLASS), + $this->em->getClassMetadata(self::SINGLE_STRING_CASTABLE_IDENT_CLASS), + $this->em->getClassMetadata(self::COMPOSITE_IDENT_CLASS), + $this->em->getClassMetadata(self::COMPOSITE_STRING_IDENT_CLASS), + ); + + try { + $schemaTool->dropSchema($classes); + } catch (\Exception $e) { + } + + try { + $schemaTool->createSchema($classes); + } catch (\Exception $e) { + } + } + + protected function tearDown() + { + parent::tearDown(); + + $this->em = null; + $this->emRegistry = null; + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new DoctrineOrmExtension($this->emRegistry), + )); + } + + protected function persist(array $entities) + { + foreach ($entities as $entity) { + $this->em->persist($entity); + } + + $this->em->flush(); + // no clear, because entities managed by the choice field must + // be managed! + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException + */ + public function testClassOptionIsRequired() + { + $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testInvalidClassOption() + { + $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'class' => 'foo', + )); + } + + public function testSetDataToUninitializedEntityWithNonRequired() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + 'choice_label' => 'name', + )); + + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + } + + public function testSetDataToUninitializedEntityWithNonRequiredToString() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + )); + + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + } + + public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + $qb = $this->em->createQueryBuilder()->select('e')->from(self::SINGLE_IDENT_CLASS, 'e'); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + 'choice_label' => 'name', + 'query_builder' => $qb, + )); + + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => new \stdClass(), + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testConfigureQueryBuilderWithClosureReturningNonQueryBuilder() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function () { + return new \stdClass(); + }, + )); + + $field->submit('2'); + } + + public function testConfigureQueryBuilderWithClosureReturningNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function () { + return; + }, + )); + + $this->assertEquals(array(), $field->createView()->vars['choices']); + } + + public function testSetDataSingleNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->setData(null); + + $this->assertNull($field->getData()); + $this->assertSame('', $field->getViewData()); + } + + public function testSetDataMultipleExpandedNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->setData(null); + + $this->assertNull($field->getData()); + $this->assertSame(array(), $field->getViewData()); + } + + public function testSetDataMultipleNonExpandedNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->setData(null); + + $this->assertNull($field->getData()); + $this->assertSame(array(), $field->getViewData()); + } + + public function testSubmitSingleExpandedNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->submit(null); + + $this->assertNull($field->getData()); + $this->assertNull($field->getViewData()); + } + + public function testSubmitSingleNonExpandedNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->submit(null); + + $this->assertNull($field->getData()); + $this->assertSame('', $field->getViewData()); + } + + public function testSubmitMultipleNull() + { + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + )); + $field->submit(null); + + $this->assertEquals(new ArrayCollection(), $field->getData()); + $this->assertSame(array(), $field->getViewData()); + } + + public function testSubmitSingleNonExpandedSingleIdentifier() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertSame('2', $field->getViewData()); + } + + public function testSubmitSingleNonExpandedSingleAssocIdentifier() + { + $innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo'); + $innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar'); + + $entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo'); + $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); + + $this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_ASSOC_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertSame('2', $field->getViewData()); + } + + public function testSubmitSingleNonExpandedCompositeIdentifier() + { + $entity1 = new CompositeIntIdEntity(10, 20, 'Foo'); + $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + // the collection key is used here + $field->submit('1'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertSame('1', $field->getViewData()); + } + + public function testSubmitMultipleNonExpandedSingleIdentifier() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit(array('1', '3')); + + $expected = new ArrayCollection(array($entity1, $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertSame(array('1', '3'), $field->getViewData()); + } + + public function testSubmitMultipleNonExpandedSingleAssocIdentifier() + { + $innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo'); + $innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar'); + $innerEntity3 = new SingleIntIdNoToStringEntity(3, 'InBaz'); + + $entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo'); + $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); + $entity3 = new SingleAssociationToIntIdEntity($innerEntity3, 'Baz'); + + $this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_ASSOC_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit(array('1', '3')); + + $expected = new ArrayCollection(array($entity1, $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertSame(array('1', '3'), $field->getViewData()); + } + + public function testSubmitMultipleNonExpandedSingleIdentifierForExistingData() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $existing = new ArrayCollection(array(0 => $entity2)); + + $field->setData($existing); + $field->submit(array('1', '3')); + + // entry with index 0 ($entity2) was replaced + $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + // same object still, useful if it is a PersistentCollection + $this->assertSame($existing, $field->getData()); + $this->assertSame(array('1', '3'), $field->getViewData()); + } + + public function testSubmitMultipleNonExpandedCompositeIdentifier() + { + $entity1 = new CompositeIntIdEntity(10, 20, 'Foo'); + $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); + $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + // because of the composite key collection keys are used + $field->submit(array('0', '2')); + + $expected = new ArrayCollection(array($entity1, $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertSame(array('0', '2'), $field->getViewData()); + } + + public function testSubmitMultipleNonExpandedCompositeIdentifierExistingData() + { + $entity1 = new CompositeIntIdEntity(10, 20, 'Foo'); + $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); + $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $existing = new ArrayCollection(array(0 => $entity2)); + + $field->setData($existing); + $field->submit(array('0', '2')); + + // entry with index 0 ($entity2) was replaced + $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + // same object still, useful if it is a PersistentCollection + $this->assertSame($existing, $field->getData()); + $this->assertSame(array('0', '2'), $field->getViewData()); + } + + public function testSubmitSingleExpanded() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertFalse($field['1']->getData()); + $this->assertTrue($field['2']->getData()); + $this->assertNull($field['1']->getViewData()); + $this->assertSame('2', $field['2']->getViewData()); + } + + public function testSubmitMultipleExpanded() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Bar'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit(array('1', '3')); + + $expected = new ArrayCollection(array($entity1, $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertTrue($field['1']->getData()); + $this->assertFalse($field['2']->getData()); + $this->assertTrue($field['3']->getData()); + $this->assertSame('1', $field['1']->getViewData()); + $this->assertNull($field['2']->getViewData()); + $this->assertSame('3', $field['3']->getViewData()); + } + + public function testSubmitMultipleExpandedWithNegativeIntegerId() + { + $entity1 = new SingleIntIdEntity(-1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit(array('-1')); + + $expected = new ArrayCollection(array($entity1)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertTrue($field['_1']->getData()); + $this->assertFalse($field['2']->getData()); + } + + public function testSubmitSingleNonExpandedStringCastableIdentifier() + { + $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); + $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertSame('2', $field->getViewData()); + } + + public function testSubmitSingleStringCastableIdentifierExpanded() + { + $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); + $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); + + $this->persist(array($entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertFalse($field['0']->getData()); + $this->assertTrue($field['1']->getData()); + $this->assertNull($field['0']->getViewData()); + $this->assertSame('2', $field['1']->getViewData()); + } + + public function testSubmitMultipleNonExpandedStringCastableIdentifierForExistingData() + { + $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); + $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); + $entity3 = new SingleStringCastableIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $existing = new ArrayCollection(array(0 => $entity2)); + + $field->setData($existing); + $field->submit(array('1', '3')); + + // entry with index 0 ($entity2) was replaced + $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + // same object still, useful if it is a PersistentCollection + $this->assertSame($existing, $field->getData()); + $this->assertSame(array('1', '3'), $field->getViewData()); + } + + public function testSubmitMultipleNonExpandedStringCastableIdentifier() + { + $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); + $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); + $entity3 = new SingleStringCastableIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit(array('1', '3')); + + $expected = new ArrayCollection(array($entity1, $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertSame(array('1', '3'), $field->getViewData()); + } + + public function testSubmitMultipleStringCastableIdentifierExpanded() + { + $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); + $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); + $entity3 = new SingleStringCastableIdEntity(3, 'Bar'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => true, + 'expanded' => true, + 'em' => 'default', + 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit(array('1', '3')); + + $expected = new ArrayCollection(array($entity1, $entity3)); + + $this->assertTrue($field->isSynchronized()); + $this->assertEquals($expected, $field->getData()); + $this->assertTrue($field['0']->getData()); + $this->assertFalse($field['1']->getData()); + $this->assertTrue($field['2']->getData()); + $this->assertSame('1', $field['0']->getViewData()); + $this->assertNull($field['1']->getViewData()); + $this->assertSame('3', $field['2']->getViewData()); + } + + public function testOverrideChoices() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + // not all persisted entities should be displayed + 'choices' => array($entity1, $entity2), + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity2, $field->getData()); + $this->assertSame('2', $field->getViewData()); + } + + public function testGroupByChoices() + { + $item1 = new GroupableEntity(1, 'Foo', 'Group1'); + $item2 = new GroupableEntity(2, 'Bar', 'Group1'); + $item3 = new GroupableEntity(3, 'Baz', 'Group2'); + $item4 = new GroupableEntity(4, 'Boo!', null); + + $this->persist(array($item1, $item2, $item3, $item4)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::ITEM_GROUP_CLASS, + 'choices' => array($item1, $item2, $item3, $item4), + 'choice_label' => 'name', + 'group_by' => 'groupName', + )); + + $field->submit('2'); + + $this->assertSame('2', $field->getViewData()); + $this->assertEquals(array( + 'Group1' => new ChoiceGroupView('Group1', array( + 1 => new ChoiceView($item1, '1', 'Foo'), + 2 => new ChoiceView($item2, '2', 'Bar'), + )), + 'Group2' => new ChoiceGroupView('Group2', array( + 3 => new ChoiceView($item3, '3', 'Baz'), + )), + 4 => new ChoiceView($item4, '4', 'Boo!'), + ), $field->createView()->vars['choices']); + } + + public function testPreferredChoices() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'preferred_choices' => array($entity3, $entity2), + 'choice_label' => 'name', + )); + + $this->assertEquals(array(3 => new ChoiceView($entity3, '3', 'Baz'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['preferred_choices']); + $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo')), $field->createView()->vars['choices']); + } + + public function testOverrideChoicesWithPreferredChoices() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choices' => array($entity2, $entity3), + 'preferred_choices' => array($entity3), + 'choice_label' => 'name', + )); + + $this->assertEquals(array(3 => new ChoiceView($entity3, '3', 'Baz')), $field->createView()->vars['preferred_choices']); + $this->assertEquals(array(2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + } + + public function testDisallowChoicesThatAreNotIncludedChoicesSingleIdentifier() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'choices' => array($entity1, $entity2), + 'choice_label' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedChoicesSingleAssocIdentifier() + { + $innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo'); + $innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar'); + + $entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo'); + $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); + + $this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_ASSOC_IDENT_CLASS, + 'choices' => array($entity1, $entity2), + 'choice_label' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedChoicesCompositeIdentifier() + { + $entity1 = new CompositeIntIdEntity(10, 20, 'Foo'); + $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); + $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'choices' => array($entity1, $entity2), + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifier() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $repository = $this->em->getRepository(self::SINGLE_IDENT_CLASS); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => $repository->createQueryBuilder('e') + ->where('e.id IN (1, 2)'), + 'choice_label' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleAssocIdentifier() + { + $innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo'); + $innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar'); + $innerEntity3 = new SingleIntIdNoToStringEntity(3, 'InBaz'); + + $entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo'); + $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); + $entity3 = new SingleAssociationToIntIdEntity($innerEntity3, 'Baz'); + + $this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3)); + + $repository = $this->em->getRepository(self::SINGLE_ASSOC_IDENT_CLASS); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_ASSOC_IDENT_CLASS, + 'query_builder' => $repository->createQueryBuilder('e') + ->where('e.entity IN (1, 2)'), + 'choice_label' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingleIdentifier() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function ($repository) { + return $repository->createQueryBuilder('e') + ->where('e.id IN (1, 2)'); + }, + 'choice_label' => 'name', + )); + + $field->submit('3'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureCompositeIdentifier() + { + $entity1 = new CompositeIntIdEntity(10, 20, 'Foo'); + $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); + $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => 'default', + 'class' => self::COMPOSITE_IDENT_CLASS, + 'query_builder' => function ($repository) { + return $repository->createQueryBuilder('e') + ->where('e.id1 IN (10, 50)'); + }, + 'choice_label' => 'name', + )); + + $field->submit('2'); + + $this->assertFalse($field->isSynchronized()); + $this->assertNull($field->getData()); + } + + public function testSubmitSingleStringIdentifier() + { + $entity1 = new SingleStringIdEntity('foo', 'Foo'); + + $this->persist(array($entity1)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::SINGLE_STRING_IDENT_CLASS, + 'choice_label' => 'name', + )); + + $field->submit('foo'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity1, $field->getData()); + $this->assertSame('foo', $field->getViewData()); + } + + public function testSubmitCompositeStringIdentifier() + { + $entity1 = new CompositeStringIdEntity('foo1', 'foo2', 'Foo'); + + $this->persist(array($entity1)); + + $field = $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'multiple' => false, + 'expanded' => false, + 'em' => 'default', + 'class' => self::COMPOSITE_STRING_IDENT_CLASS, + 'choice_label' => 'name', + )); + + // the collection key is used here + $field->submit('0'); + + $this->assertTrue($field->isSynchronized()); + $this->assertSame($entity1, $field->getData()); + $this->assertSame('0', $field->getViewData()); + } + + public function testGetManagerForClassIfNoEm() + { + $this->emRegistry->expects($this->never()) + ->method('getManager'); + + $this->emRegistry->expects($this->once()) + ->method('getManagerForClass') + ->with(self::SINGLE_IDENT_CLASS) + ->will($this->returnValue($this->em)); + + $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'class' => self::SINGLE_IDENT_CLASS, + 'required' => false, + 'choice_label' => 'name', + )); + } + + public function testExplicitEm() + { + $this->emRegistry->expects($this->never()) + ->method('getManager'); + + $this->emRegistry->expects($this->never()) + ->method('getManagerForClass'); + + $this->factory->createNamed('name', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + 'em' => $this->em, + 'class' => self::SINGLE_IDENT_CLASS, + 'choice_label' => 'name', + )); + } + + public function testLoaderCaching() + { + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Bar'); + $entity3 = new SingleIntIdEntity(3, 'Baz'); + + $this->persist(array($entity1, $entity2, $entity3)); + + $repo = $this->em->getRepository(self::SINGLE_IDENT_CLASS); + + $entityType = new EntityType( + $this->emRegistry, + PropertyAccess::createPropertyAccessor() + ); + + $entityTypeGuesser = new DoctrineOrmTypeGuesser($this->emRegistry); + + $factory = Forms::createFormFactoryBuilder() + ->addType($entityType) + ->addTypeGuesser($entityTypeGuesser) + ->getFormFactory(); + + $formBuilder = $factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + + $formBuilder->add('property1', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'), + )); + + $formBuilder->add('property2', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function (EntityRepository $repo) { + return $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'); + }, + )); + + $formBuilder->add('property3', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array( + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + 'query_builder' => function (EntityRepository $repo) { + return $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'); + }, + )); + + $form = $formBuilder->getForm(); + + $form->submit(array( + 'property1' => 1, + 'property2' => 1, + 'property3' => 2, + )); + + $choiceLoader1 = $form->get('property1')->getConfig()->getOption('choice_loader'); + $choiceLoader2 = $form->get('property2')->getConfig()->getOption('choice_loader'); + $choiceLoader3 = $form->get('property3')->getConfig()->getOption('choice_loader'); + + $this->assertInstanceOf('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface', $choiceLoader1); + $this->assertSame($choiceLoader1, $choiceLoader2); + $this->assertSame($choiceLoader1, $choiceLoader3); + } + + protected function createRegistryMock($name, $em) + { + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry->expects($this->any()) + ->method('getManager') + ->with($this->equalTo($name)) + ->will($this->returnValue($em)); + + return $registry; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php new file mode 100644 index 0000000..4f82bc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/HttpFoundation/DbalSessionHandlerTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\HttpFoundation; + +use Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler; + +/** + * Test class for DbalSessionHandler. + * + * @author Drak + */ +class DbalSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $connection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock(); + $handler = new DbalSessionHandler($connection); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php new file mode 100644 index 0000000..6acc47f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Logger; + +use Symfony\Bridge\Doctrine\Logger\DbalLogger; + +class DbalLoggerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getLogFixtures + */ + public function testLog($sql, $params, $logParams) + { + $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + + $dbalLogger = $this + ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') + ->setConstructorArgs(array($logger, null)) + ->setMethods(array('log')) + ->getMock() + ; + + $dbalLogger + ->expects($this->once()) + ->method('log') + ->with($sql, $logParams) + ; + + $dbalLogger->startQuery($sql, $params); + } + + public function getLogFixtures() + { + return array( + array('SQL', null, array()), + array('SQL', array(), array()), + array('SQL', array('foo' => 'bar'), array('foo' => 'bar')), + array('SQL', array('foo' => "\x7F\xFF"), array('foo' => DbalLogger::BINARY_DATA_VALUE)), + array('SQL', array('foo' => "bar\x7F\xFF"), array('foo' => DbalLogger::BINARY_DATA_VALUE)), + array('SQL', array('foo' => ''), array('foo' => '')), + ); + } + + public function testLogNonUtf8() + { + $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + + $dbalLogger = $this + ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') + ->setConstructorArgs(array($logger, null)) + ->setMethods(array('log')) + ->getMock() + ; + + $dbalLogger + ->expects($this->once()) + ->method('log') + ->with('SQL', array('utf8' => 'foo', 'nonutf8' => DbalLogger::BINARY_DATA_VALUE)) + ; + + $dbalLogger->startQuery('SQL', array( + 'utf8' => 'foo', + 'nonutf8' => "\x7F\xFF", + )); + } + + public function testLogNonUtf8Array() + { + $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + + $dbalLogger = $this + ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') + ->setConstructorArgs(array($logger, null)) + ->setMethods(array('log')) + ->getMock() + ; + + $dbalLogger + ->expects($this->once()) + ->method('log') + ->with('SQL', array( + 'utf8' => 'foo', + array( + 'nonutf8' => DbalLogger::BINARY_DATA_VALUE, + ), + ) + ) + ; + + $dbalLogger->startQuery('SQL', array( + 'utf8' => 'foo', + array( + 'nonutf8' => "\x7F\xFF", + ), + )); + } + + public function testLogLongString() + { + $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + + $dbalLogger = $this + ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') + ->setConstructorArgs(array($logger, null)) + ->setMethods(array('log')) + ->getMock() + ; + + $testString = 'abc'; + + $shortString = str_pad('', DbalLogger::MAX_STRING_LENGTH, $testString); + $longString = str_pad('', DbalLogger::MAX_STRING_LENGTH + 1, $testString); + + $dbalLogger + ->expects($this->once()) + ->method('log') + ->with('SQL', array('short' => $shortString, 'long' => substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6).' [...]')) + ; + + $dbalLogger->startQuery('SQL', array( + 'short' => $shortString, + 'long' => $longString, + )); + } + + public function testLogUTF8LongString() + { + $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + + $dbalLogger = $this + ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') + ->setConstructorArgs(array($logger, null)) + ->setMethods(array('log')) + ->getMock() + ; + + $testStringArray = array('é', 'á', 'ű', 'ő', 'ú', 'ö', 'ü', 'ó', 'í'); + $testStringCount = count($testStringArray); + + $shortString = ''; + $longString = ''; + for ($i = 1; $i <= DbalLogger::MAX_STRING_LENGTH; ++$i) { + $shortString .= $testStringArray[$i % $testStringCount]; + $longString .= $testStringArray[$i % $testStringCount]; + } + $longString .= $testStringArray[$i % $testStringCount]; + + $dbalLogger + ->expects($this->once()) + ->method('log') + ->with('SQL', array('short' => $shortString, 'long' => mb_substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]')) + ; + + $dbalLogger->startQuery('SQL', array( + 'short' => $shortString, + 'long' => $longString, + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php new file mode 100644 index 0000000..df6c355 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\PropertyInfo\Tests; + +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Tools\Setup; +use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor; +use Symfony\Component\PropertyInfo\Type; + +/** + * @author Kévin Dunglas + */ +class DoctrineExtractorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var DoctrineExtractor + */ + private $extractor; + + public function setUp() + { + $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'), true); + $entityManager = EntityManager::create(array('driver' => 'pdo_sqlite'), $config); + + $this->extractor = new DoctrineExtractor($entityManager->getMetadataFactory()); + } + + public function testGetProperties() + { + $this->assertEquals( + array( + 'id', + 'guid', + 'time', + 'json', + 'simpleArray', + 'bool', + 'binary', + 'foo', + 'bar', + ), + $this->extractor->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy') + ); + } + + /** + * @dataProvider typesProvider + */ + public function testExtract($property, array $type = null) + { + $this->assertEquals($type, $this->extractor->getTypes('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy', $property, array())); + } + + public function typesProvider() + { + return array( + array('id', array(new Type(Type::BUILTIN_TYPE_INT))), + array('guid', array(new Type(Type::BUILTIN_TYPE_STRING))), + array('bool', array(new Type(Type::BUILTIN_TYPE_BOOL))), + array('binary', array(new Type(Type::BUILTIN_TYPE_RESOURCE))), + array('json', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true))), + array('foo', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation'))), + array('bar', array(new Type( + Type::BUILTIN_TYPE_OBJECT, + false, + 'Doctrine\Common\Collections\Collection', + true, + new Type(Type::BUILTIN_TYPE_INT), + new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') + ))), + array('simpleArray', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), + array('notMapped', null), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php new file mode 100644 index 0000000..864bd78 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\ManyToMany; +use Doctrine\ORM\Mapping\ManyToOne; + +/** + * @Entity + * + * @author Kévin Dunglas + */ +class DoctrineDummy +{ + /** + * @Id + * @Column(type="smallint") + */ + public $id; + + /** + * @ManyToOne(targetEntity="DoctrineRelation") + */ + public $foo; + + /** + * @ManyToMany(targetEntity="DoctrineRelation") + */ + public $bar; + + /** + * @Column(type="guid") + */ + protected $guid; + + /** + * @Column(type="time") + */ + private $time; + + /** + * @Column(type="json_array") + */ + private $json; + + /** + * @Column(type="simple_array") + */ + private $simpleArray; + + /** + * @Column(type="boolean") + */ + private $bool; + + /** + * @Column(type="binary") + */ + private $binary; + + public $notMapped; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php new file mode 100644 index 0000000..bfb27e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Id; + +/** + * @Entity + * + * @author Kévin Dunglas + */ +class DoctrineRelation +{ + /** + * @Id + * @Column(type="smallint") + */ + public $id; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php new file mode 100644 index 0000000..c71378b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Security\User; + +use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; +use Symfony\Bridge\Doctrine\Tests\Fixtures\User; +use Symfony\Bridge\Doctrine\Security\User\EntityUserProvider; +use Doctrine\ORM\Tools\SchemaTool; + +class EntityUserProviderTest extends \PHPUnit_Framework_TestCase +{ + public function testRefreshUserGetsUserByPrimaryKey() + { + $em = DoctrineTestHelper::createTestEntityManager(); + $this->createSchema($em); + + $user1 = new User(1, 1, 'user1'); + $user2 = new User(1, 2, 'user2'); + + $em->persist($user1); + $em->persist($user2); + $em->flush(); + + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); + + // try to change the user identity + $user1->name = 'user2'; + + $this->assertSame($user1, $provider->refreshUser($user1)); + } + + public function testRefreshUserRequiresId() + { + $em = DoctrineTestHelper::createTestEntityManager(); + + $user1 = new User(null, null, 'user1'); + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); + + $this->setExpectedException( + 'InvalidArgumentException', + 'You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine' + ); + $provider->refreshUser($user1); + } + + public function testRefreshInvalidUser() + { + $em = DoctrineTestHelper::createTestEntityManager(); + $this->createSchema($em); + + $user1 = new User(1, 1, 'user1'); + + $em->persist($user1); + $em->flush(); + + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); + + $user2 = new User(1, 2, 'user2'); + $this->setExpectedException( + 'Symfony\Component\Security\Core\Exception\UsernameNotFoundException', + 'User with id {"id1":1,"id2":2} not found' + ); + $provider->refreshUser($user2); + } + + public function testSupportProxy() + { + $em = DoctrineTestHelper::createTestEntityManager(); + $this->createSchema($em); + + $user1 = new User(1, 1, 'user1'); + + $em->persist($user1); + $em->flush(); + $em->clear(); + + $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); + + $user2 = $em->getReference('Symfony\Bridge\Doctrine\Tests\Fixtures\User', array('id1' => 1, 'id2' => 1)); + $this->assertTrue($provider->supportsClass(get_class($user2))); + } + + public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided() + { + $repository = $this->getMock('\Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface'); + $repository->expects($this->once()) + ->method('loadUserByUsername') + ->with('name') + ->willReturn( + $this->getMock('\Symfony\Component\Security\Core\User\UserInterface') + ); + + $provider = new EntityUserProvider( + $this->getManager($this->getObjectManager($repository)), + 'Symfony\Bridge\Doctrine\Tests\Fixtures\User' + ); + + $provider->loadUserByUsername('name'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLoadUserByUserNameShouldDeclineInvalidInterface() + { + $repository = $this->getMock('\Symfony\Component\Security\Core\User\AdvancedUserInterface'); + + $provider = new EntityUserProvider( + $this->getManager($this->getObjectManager($repository)), + 'Symfony\Bridge\Doctrine\Tests\Fixtures\User' + ); + + $provider->loadUserByUsername('name'); + } + + private function getManager($em, $name = null) + { + $manager = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $manager->expects($this->any()) + ->method('getManager') + ->with($this->equalTo($name)) + ->will($this->returnValue($em)); + + return $manager; + } + + private function getObjectManager($repository) + { + $em = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager') + ->setMethods(array('getClassMetadata', 'getRepository')) + ->getMockForAbstractClass(); + $em->expects($this->any()) + ->method('getRepository') + ->willReturn($repository); + + return $em; + } + + private function createSchema($em) + { + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema(array( + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\User'), + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php new file mode 100644 index 0000000..8cf6ec0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -0,0 +1,452 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Validator\Constraints; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\Persistence\ObjectRepository; +use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; +use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator; +use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; +use Doctrine\ORM\Tools\SchemaTool; + +/** + * @author Bernhard Schussek + */ +class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest +{ + const EM_NAME = 'foo'; + + /** + * @var ObjectManager + */ + protected $em; + + /** + * @var ManagerRegistry + */ + protected $registry; + + /** + * @var ObjectRepository + */ + protected $repository; + + protected function setUp() + { + $this->em = DoctrineTestHelper::createTestEntityManager(); + $this->registry = $this->createRegistryMock($this->em); + $this->createSchema($this->em); + + parent::setUp(); + } + + protected function createRegistryMock(ObjectManager $em = null) + { + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry->expects($this->any()) + ->method('getManager') + ->with($this->equalTo(self::EM_NAME)) + ->will($this->returnValue($em)); + + return $registry; + } + + protected function createRepositoryMock() + { + $repository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository') + ->setMethods(array('findByCustom', 'find', 'findAll', 'findOneBy', 'findBy', 'getClassName')) + ->getMock() + ; + + return $repository; + } + + protected function createEntityManagerMock($repositoryMock) + { + $em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager') + ->getMock() + ; + $em->expects($this->any()) + ->method('getRepository') + ->will($this->returnValue($repositoryMock)) + ; + + $classMetadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); + $classMetadata + ->expects($this->any()) + ->method('hasField') + ->will($this->returnValue(true)) + ; + $reflParser = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionParser') + ->disableOriginalConstructor() + ->getMock() + ; + $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty') + ->setConstructorArgs(array($reflParser, 'property-name')) + ->setMethods(array('getValue')) + ->getMock() + ; + $refl + ->expects($this->any()) + ->method('getValue') + ->will($this->returnValue(true)) + ; + $classMetadata->reflFields = array('name' => $refl); + $em->expects($this->any()) + ->method('getClassMetadata') + ->will($this->returnValue($classMetadata)) + ; + + return $em; + } + + protected function createValidator() + { + return new UniqueEntityValidator($this->registry); + } + + private function createSchema(ObjectManager $em) + { + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema(array( + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity'), + )); + } + + /** + * This is a functional test as there is a large integration necessary to get the validator working. + */ + public function testValidateUniqueness() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Foo'); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->em->persist($entity1); + $this->em->flush(); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->validator->validate($entity2, $constraint); + + $this->buildViolation('myMessage') + ->atPath('property.path.name') + ->setInvalidValue('Foo') + ->assertRaised(); + } + + public function testValidateCustomErrorPath() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + 'errorPath' => 'bar', + )); + + $entity1 = new SingleIntIdEntity(1, 'Foo'); + $entity2 = new SingleIntIdEntity(2, 'Foo'); + + $this->em->persist($entity1); + $this->em->flush(); + + $this->validator->validate($entity2, $constraint); + + $this->buildViolation('myMessage') + ->atPath('property.path.bar') + ->setInvalidValue('Foo') + ->assertRaised(); + } + + public function testValidateUniquenessWithNull() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $entity1 = new SingleIntIdEntity(1, null); + $entity2 = new SingleIntIdEntity(2, null); + + $this->em->persist($entity1); + $this->em->persist($entity2); + $this->em->flush(); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + } + + public function testValidateUniquenessWithIgnoreNull() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name', 'name2'), + 'em' => self::EM_NAME, + 'ignoreNull' => false, + )); + + $entity1 = new DoubleNameEntity(1, 'Foo', null); + $entity2 = new DoubleNameEntity(2, 'Foo', null); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->em->persist($entity1); + $this->em->flush(); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->validator->validate($entity2, $constraint); + + $this->buildViolation('myMessage') + ->atPath('property.path.name') + ->setInvalidValue('Foo') + ->assertRaised(); + } + + public function testValidateUniquenessWithValidCustomErrorPath() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name', 'name2'), + 'em' => self::EM_NAME, + 'errorPath' => 'name2', + )); + + $entity1 = new DoubleNameEntity(1, 'Foo', 'Bar'); + $entity2 = new DoubleNameEntity(2, 'Foo', 'Bar'); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->em->persist($entity1); + $this->em->flush(); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->validator->validate($entity2, $constraint); + + $this->buildViolation('myMessage') + ->atPath('property.path.name2') + ->setInvalidValue('Bar') + ->assertRaised(); + } + + public function testValidateUniquenessUsingCustomRepositoryMethod() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + 'repositoryMethod' => 'findByCustom', + )); + + $repository = $this->createRepositoryMock(); + $repository->expects($this->once()) + ->method('findByCustom') + ->will($this->returnValue(array())) + ; + $this->em = $this->createEntityManagerMock($repository); + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $entity1 = new SingleIntIdEntity(1, 'foo'); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + } + + public function testValidateUniquenessWithUnrewoundArray() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + 'repositoryMethod' => 'findByCustom', + )); + + $entity = new SingleIntIdEntity(1, 'foo'); + + $repository = $this->createRepositoryMock(); + $repository->expects($this->once()) + ->method('findByCustom') + ->will( + $this->returnCallback(function () use ($entity) { + $returnValue = array( + $entity, + ); + next($returnValue); + + return $returnValue; + }) + ) + ; + $this->em = $this->createEntityManagerMock($repository); + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $this->validator->validate($entity, $constraint); + + $this->assertNoViolation(); + } + + public function testAssociatedEntity() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('single'), + 'em' => self::EM_NAME, + )); + + $entity1 = new SingleIntIdEntity(1, 'foo'); + $associated = new AssociationEntity(); + $associated->single = $entity1; + $associated2 = new AssociationEntity(); + $associated2->single = $entity1; + + $this->em->persist($entity1); + $this->em->persist($associated); + $this->em->flush(); + + $this->validator->validate($associated, $constraint); + + $this->assertNoViolation(); + + $this->em->persist($associated2); + $this->em->flush(); + + $this->validator->validate($associated2, $constraint); + + $this->buildViolation('myMessage') + ->atPath('property.path.single') + ->setInvalidValue(1) + ->assertRaised(); + } + + public function testAssociatedEntityWithNull() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('single'), + 'em' => self::EM_NAME, + 'ignoreNull' => false, + )); + + $associated = new AssociationEntity(); + $associated->single = null; + + $this->em->persist($associated); + $this->em->flush(); + + $this->validator->validate($associated, $constraint); + + $this->assertNoViolation(); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + * @expectedExceptionMessage Associated entities are not allowed to have more than one identifier field + */ + public function testAssociatedCompositeEntity() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('composite'), + 'em' => self::EM_NAME, + )); + + $composite = new CompositeIntIdEntity(1, 1, 'test'); + $associated = new AssociationEntity(); + $associated->composite = $composite; + + $this->em->persist($composite); + $this->em->persist($associated); + $this->em->flush(); + + $this->validator->validate($associated, $constraint); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + * @expectedExceptionMessage Object manager "foo" does not exist. + */ + public function testDedicatedEntityManagerNullObject() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + )); + + $this->em = null; + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $entity = new SingleIntIdEntity(1, null); + + $this->validator->validate($entity, $constraint); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + * @expectedExceptionMessage Unable to find the object manager associated with an entity of class "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity" + */ + public function testEntityManagerNullObject() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + // no "em" option set + )); + + $this->em = null; + $this->registry = $this->createRegistryMock($this->em); + $this->validator = $this->createValidator(); + $this->validator->initialize($this->context); + + $entity = new SingleIntIdEntity(1, null); + + $this->validator->validate($entity, $constraint); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php new file mode 100644 index 0000000..fc6e213 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * Constraint for the Unique Entity validator. + * + * @Annotation + * @Target({"CLASS", "ANNOTATION"}) + * + * @author Benjamin Eberlei + */ +class UniqueEntity extends Constraint +{ + public $message = 'This value is already used.'; + public $service = 'doctrine.orm.validator.unique'; + public $em = null; + public $repositoryMethod = 'findBy'; + public $fields = array(); + public $errorPath = null; + public $ignoreNull = true; + + public function getRequiredOptions() + { + return array('fields'); + } + + /** + * The validator must be defined as a service with this name. + * + * @return string + */ + public function validatedBy() + { + return $this->service; + } + + /** + * {@inheritdoc} + */ + public function getTargets() + { + return self::CLASS_CONSTRAINT; + } + + public function getDefaultOption() + { + return 'fields'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php new file mode 100644 index 0000000..0fbf42c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Validator\Constraints; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; +use Symfony\Component\Validator\ConstraintValidator; + +/** + * Unique Entity Validator checks if one or a set of fields contain unique values. + * + * @author Benjamin Eberlei + */ +class UniqueEntityValidator extends ConstraintValidator +{ + /** + * @var ManagerRegistry + */ + private $registry; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + /** + * @param object $entity + * @param Constraint $constraint + * + * @throws UnexpectedTypeException + * @throws ConstraintDefinitionException + */ + public function validate($entity, Constraint $constraint) + { + if (!$constraint instanceof UniqueEntity) { + throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UniqueEntity'); + } + + if (!is_array($constraint->fields) && !is_string($constraint->fields)) { + throw new UnexpectedTypeException($constraint->fields, 'array'); + } + + if (null !== $constraint->errorPath && !is_string($constraint->errorPath)) { + throw new UnexpectedTypeException($constraint->errorPath, 'string or null'); + } + + $fields = (array) $constraint->fields; + + if (0 === count($fields)) { + throw new ConstraintDefinitionException('At least one field has to be specified.'); + } + + if ($constraint->em) { + $em = $this->registry->getManager($constraint->em); + + if (!$em) { + throw new ConstraintDefinitionException(sprintf('Object manager "%s" does not exist.', $constraint->em)); + } + } else { + $em = $this->registry->getManagerForClass(get_class($entity)); + + if (!$em) { + throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', get_class($entity))); + } + } + + $class = $em->getClassMetadata(get_class($entity)); + /* @var $class \Doctrine\Common\Persistence\Mapping\ClassMetadata */ + + $criteria = array(); + foreach ($fields as $fieldName) { + if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) { + throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $fieldName)); + } + + $criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity); + + if ($constraint->ignoreNull && null === $criteria[$fieldName]) { + return; + } + + if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) { + /* Ensure the Proxy is initialized before using reflection to + * read its identifiers. This is necessary because the wrapped + * getter methods in the Proxy are being bypassed. + */ + $em->initializeObject($criteria[$fieldName]); + + $relatedClass = $em->getClassMetadata($class->getAssociationTargetClass($fieldName)); + $relatedId = $relatedClass->getIdentifierValues($criteria[$fieldName]); + + if (count($relatedId) > 1) { + throw new ConstraintDefinitionException( + 'Associated entities are not allowed to have more than one identifier field to be '. + 'part of a unique constraint in: '.$class->getName().'#'.$fieldName + ); + } + $criteria[$fieldName] = array_pop($relatedId); + } + } + + $repository = $em->getRepository(get_class($entity)); + $result = $repository->{$constraint->repositoryMethod}($criteria); + + /* If the result is a MongoCursor, it must be advanced to the first + * element. Rewinding should have no ill effect if $result is another + * iterator implementation. + */ + if ($result instanceof \Iterator) { + $result->rewind(); + } elseif (is_array($result)) { + reset($result); + } + + /* If no entity matched the query criteria or a single entity matched, + * which is the same as the entity being validated, the criteria is + * unique. + */ + if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) { + return; + } + + $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; + $invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]]; + + if ($this->context instanceof ExecutionContextInterface) { + $this->context->buildViolation($constraint->message) + ->atPath($errorPath) + ->setInvalidValue($invalidValue) + ->addViolation(); + } else { + $this->buildViolation($constraint->message) + ->atPath($errorPath) + ->setInvalidValue($invalidValue) + ->addViolation(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php new file mode 100644 index 0000000..42cafdd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Validator; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\Validator\ObjectInitializerInterface; + +/** + * Automatically loads proxy object before validation. + * + * @author Fabien Potencier + */ +class DoctrineInitializer implements ObjectInitializerInterface +{ + protected $registry; + + public function __construct(ManagerRegistry $registry) + { + $this->registry = $registry; + } + + public function initialize($object) + { + $manager = $this->registry->getManagerForClass(get_class($object)); + if (null !== $manager) { + $manager->initializeObject($object); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/composer.json new file mode 100644 index 0000000..d1443b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/composer.json @@ -0,0 +1,57 @@ +{ + "name": "symfony/doctrine-bridge", + "type": "symfony-bridge", + "description": "Symfony Doctrine Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "doctrine/common": "~2.4" + }, + "require-dev": { + "symfony/stopwatch": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/form": "~3.0,>3.0-BETA1", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/property-access": "~2.8|~3.0", + "symfony/property-info": "~2.8|3.0", + "symfony/security": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/validator": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.4", + "doctrine/orm": "~2.4,>=2.4.5" + }, + "suggest": { + "symfony/form": "", + "symfony/validator": "", + "symfony/property-info": "", + "doctrine/data-fixtures": "", + "doctrine/dbal": "", + "doctrine/orm": "" + }, + "autoload": { + "psr-4": { "Symfony\\Bridge\\Doctrine\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/phpunit.xml.dist new file mode 100644 index 0000000..c006d23 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/CHANGELOG.md new file mode 100644 index 0000000..cb7deea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -0,0 +1,19 @@ +CHANGELOG +========= + +3.0.0 +----- + + * deprecated interface `Symfony\Component\HttpKernel\Log\LoggerInterface` has been removed + * deprecated methods `Logger::crit()`, `Logger::emerg()`, `Logger::err()` and `Logger::warn()` have been removed + +2.4.0 +----- + + * added ConsoleHandler and ConsoleFormatter which can be used to show log messages + in the console output depending on the verbosity settings + +2.1.0 +----- + + * added ChromePhpHandler diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php new file mode 100644 index 0000000..1af93bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Formatter; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Formats incoming records for console output by coloring them depending on log level. + * + * @author Tobias Schultze + */ +class ConsoleFormatter extends LineFormatter +{ + const SIMPLE_FORMAT = "%start_tag%[%datetime%] %channel%.%level_name%:%end_tag% %message% %context% %extra%\n"; + + /** + * {@inheritdoc} + */ + public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = true) + { + parent::__construct($format, $dateFormat, $allowInlineLineBreaks, $ignoreEmptyContextAndExtra); + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + if ($record['level'] >= Logger::ERROR) { + $record['start_tag'] = ''; + $record['end_tag'] = ''; + } elseif ($record['level'] >= Logger::NOTICE) { + $record['start_tag'] = ''; + $record['end_tag'] = ''; + } elseif ($record['level'] >= Logger::INFO) { + $record['start_tag'] = ''; + $record['end_tag'] = ''; + } else { + $record['start_tag'] = ''; + $record['end_tag'] = ''; + } + + return parent::format($record); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php new file mode 100644 index 0000000..267b012 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\ChromePHPHandler as BaseChromePhpHandler; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; + +/** + * ChromePhpHandler. + * + * @author Christophe Coevoet + */ +class ChromePhpHandler extends BaseChromePhpHandler +{ + /** + * @var array + */ + private $headers = array(); + + /** + * @var Response + */ + private $response; + + /** + * Adds the headers to the response once it's created. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if (!preg_match('{\bChrome/\d+[\.\d+]*\b}', $event->getRequest()->headers->get('User-Agent'))) { + $this->sendHeaders = false; + $this->headers = array(); + + return; + } + + $this->response = $event->getResponse(); + foreach ($this->headers as $header => $content) { + $this->response->headers->set($header, $content); + } + $this->headers = array(); + } + + /** + * {@inheritdoc} + */ + protected function sendHeader($header, $content) + { + if (!$this->sendHeaders) { + return; + } + + if ($this->response) { + $this->response->headers->set($header, $content); + } else { + $this->headers[$header] = $content; + } + } + + /** + * Override default behavior since we check it in onKernelResponse. + */ + protected function headersAccepted() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php new file mode 100644 index 0000000..592584f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Logger; +use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Writes logs to the console output depending on its verbosity setting. + * + * It is disabled by default and gets activated as soon as a command is executed. + * Instead of listening to the console events, the output can also be set manually. + * + * The minimum logging level at which this handler will be triggered depends on the + * verbosity setting of the console output. The default mapping is: + * - OutputInterface::VERBOSITY_NORMAL will show all WARNING and higher logs + * - OutputInterface::VERBOSITY_VERBOSE (-v) will show all NOTICE and higher logs + * - OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) will show all INFO and higher logs + * - OutputInterface::VERBOSITY_DEBUG (-vvv) will show all DEBUG and higher logs, i.e. all logs + * + * This mapping can be customized with the $verbosityLevelMap constructor parameter. + * + * @author Tobias Schultze + */ +class ConsoleHandler extends AbstractProcessingHandler implements EventSubscriberInterface +{ + /** + * @var OutputInterface|null + */ + private $output; + + /** + * @var array + */ + private $verbosityLevelMap = array( + OutputInterface::VERBOSITY_NORMAL => Logger::WARNING, + OutputInterface::VERBOSITY_VERBOSE => Logger::NOTICE, + OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO, + OutputInterface::VERBOSITY_DEBUG => Logger::DEBUG, + ); + + /** + * Constructor. + * + * @param OutputInterface|null $output The console output to use (the handler remains disabled when passing null + * until the output is set, e.g. by using console events) + * @param bool $bubble Whether the messages that are handled can bubble up the stack + * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging + * level (leave empty to use the default mapping) + */ + public function __construct(OutputInterface $output = null, $bubble = true, array $verbosityLevelMap = array()) + { + parent::__construct(Logger::DEBUG, $bubble); + $this->output = $output; + + if ($verbosityLevelMap) { + $this->verbosityLevelMap = $verbosityLevelMap; + } + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $this->updateLevel() && parent::isHandling($record); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + // we have to update the logging level each time because the verbosity of the + // console output might have changed in the meantime (it is not immutable) + return $this->updateLevel() && parent::handle($record); + } + + /** + * Sets the console output to use for printing logs. + * + * @param OutputInterface $output The console output to use + */ + public function setOutput(OutputInterface $output) + { + $this->output = $output; + } + + /** + * Disables the output. + */ + public function close() + { + $this->output = null; + + parent::close(); + } + + /** + * Before a command is executed, the handler gets activated and the console output + * is set in order to know where to write the logs. + * + * @param ConsoleCommandEvent $event + */ + public function onCommand(ConsoleCommandEvent $event) + { + $output = $event->getOutput(); + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->setOutput($output); + } + + /** + * After a command has been executed, it disables the output. + * + * @param ConsoleTerminateEvent $event + */ + public function onTerminate(ConsoleTerminateEvent $event) + { + $this->close(); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + ConsoleEvents::COMMAND => array('onCommand', 255), + ConsoleEvents::TERMINATE => array('onTerminate', -255), + ); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->output->write((string) $record['formatted']); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new ConsoleFormatter(); + } + + /** + * Updates the logging level based on the verbosity setting of the console output. + * + * @return bool Whether the handler is enabled and verbosity is not set to quiet. + */ + private function updateLevel() + { + if (null === $this->output || OutputInterface::VERBOSITY_QUIET === $verbosity = $this->output->getVerbosity()) { + return false; + } + + if (isset($this->verbosityLevelMap[$verbosity])) { + $this->setLevel($this->verbosityLevelMap[$verbosity]); + } else { + $this->setLevel(Logger::DEBUG); + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php new file mode 100644 index 0000000..f1046c9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/DebugHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Logger; +use Monolog\Handler\TestHandler; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * DebugLogger. + * + * @author Jordi Boggiano + */ +class DebugHandler extends TestHandler implements DebugLoggerInterface +{ + /** + * {@inheritdoc} + */ + public function getLogs() + { + $records = array(); + foreach ($this->records as $record) { + $records[] = array( + 'timestamp' => $record['datetime']->getTimestamp(), + 'message' => $record['message'], + 'priority' => $record['level'], + 'priorityName' => $record['level_name'], + 'context' => $record['context'], + 'channel' => isset($record['channel']) ? $record['channel'] : '', + ); + } + + return $records; + } + + /** + * {@inheritdoc} + */ + public function countErrors() + { + $cnt = 0; + $levels = array(Logger::ERROR, Logger::CRITICAL, Logger::ALERT, Logger::EMERGENCY); + foreach ($levels as $level) { + if (isset($this->recordsByLevel[$level])) { + $cnt += count($this->recordsByLevel[$level]); + } + } + + return $cnt; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php new file mode 100644 index 0000000..413b476 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler\FingersCrossed; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * Activation strategy that ignores 404s for certain URLs. + * + * @author Jordi Boggiano + * @author Fabien Potencier + */ +class NotFoundActivationStrategy extends ErrorLevelActivationStrategy +{ + private $blacklist; + private $requestStack; + + public function __construct(RequestStack $requestStack, array $excludedUrls, $actionLevel) + { + parent::__construct($actionLevel); + + $this->requestStack = $requestStack; + $this->blacklist = '{('.implode('|', $excludedUrls).')}i'; + } + + public function isHandlerActivated(array $record) + { + $isActivated = parent::isHandlerActivated($record); + + if ( + $isActivated + && isset($record['context']['exception']) + && $record['context']['exception'] instanceof HttpException + && $record['context']['exception']->getStatusCode() == 404 + && ($request = $this->requestStack->getMasterRequest()) + ) { + return !preg_match($this->blacklist, $request->getPathInfo()); + } + + return $isActivated; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..339843c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\FirePHPHandler as BaseFirePHPHandler; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpFoundation\Response; + +/** + * FirePHPHandler. + * + * @author Jordi Boggiano + */ +class FirePHPHandler extends BaseFirePHPHandler +{ + /** + * @var array + */ + private $headers = array(); + + /** + * @var Response + */ + private $response; + + /** + * Adds the headers to the response once it's created. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $event->getRequest()->headers->get('User-Agent')) + && !$event->getRequest()->headers->has('X-FirePHP-Version')) { + $this->sendHeaders = false; + $this->headers = array(); + + return; + } + + $this->response = $event->getResponse(); + foreach ($this->headers as $header => $content) { + $this->response->headers->set($header, $content); + } + $this->headers = array(); + } + + /** + * {@inheritdoc} + */ + protected function sendHeader($header, $content) + { + if (!$this->sendHeaders) { + return; + } + + if ($this->response) { + $this->response->headers->set($header, $content); + } else { + $this->headers[$header] = $content; + } + } + + /** + * Override default behavior since we check the user agent in onKernelResponse. + */ + protected function headersAccepted() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..0412e94 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\SwiftMailerHandler as BaseSwiftMailerHandler; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; + +/** + * Extended SwiftMailerHandler that flushes mail queue if necessary. + * + * @author Philipp Kräutli + */ +class SwiftMailerHandler extends BaseSwiftMailerHandler +{ + protected $transport; + + protected $instantFlush = false; + + /** + * @param \Swift_Transport $transport + */ + public function setTransport(\Swift_Transport $transport) + { + $this->transport = $transport; + } + + /** + * After the kernel has been terminated we will always flush messages. + * + * @param PostResponseEvent $event + */ + public function onKernelTerminate(PostResponseEvent $event) + { + $this->instantFlush = true; + } + + /** + * After the CLI application has been terminated we will always flush messages. + * + * @param ConsoleTerminateEvent $event + */ + public function onCliTerminate(ConsoleTerminateEvent $event) + { + $this->instantFlush = true; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + parent::send($content, $records); + + if ($this->instantFlush) { + $this->flushMemorySpool(); + } + } + + /** + * Flushes the mail queue if a memory spool is used. + */ + private function flushMemorySpool() + { + $mailerTransport = $this->mailer->getTransport(); + if (!$mailerTransport instanceof \Swift_Transport_SpoolTransport) { + return; + } + + $spool = $mailerTransport->getSpool(); + if (!$spool instanceof \Swift_MemorySpool) { + return; + } + + if (null === $this->transport) { + throw new \Exception('No transport available to flush mail queue'); + } + + $spool->flushQueue($this->transport); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Logger.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Logger.php new file mode 100644 index 0000000..f0e1080 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Logger.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog; + +use Monolog\Logger as BaseLogger; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * Logger. + * + * @author Fabien Potencier + */ +class Logger extends BaseLogger implements DebugLoggerInterface +{ + /** + * {@inheritdoc} + */ + public function getLogs() + { + if ($logger = $this->getDebugLogger()) { + return $logger->getLogs(); + } + + return array(); + } + + /** + * {@inheritdoc} + */ + public function countErrors() + { + if ($logger = $this->getDebugLogger()) { + return $logger->countErrors(); + } + + return 0; + } + + /** + * Returns a DebugLoggerInterface instance if one is registered with this logger. + * + * @return DebugLoggerInterface|null A DebugLoggerInterface instance or null if none is registered + */ + private function getDebugLogger() + { + foreach ($this->handlers as $handler) { + if ($handler instanceof DebugLoggerInterface) { + return $handler; + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..2752d03 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Processor; + +use Monolog\Processor\WebProcessor as BaseWebProcessor; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +/** + * WebProcessor override to read from the HttpFoundation's Request. + * + * @author Jordi Boggiano + */ +class WebProcessor extends BaseWebProcessor +{ + public function __construct(array $extraFields = null) + { + // Pass an empty array as the default null value would access $_SERVER + parent::__construct(array(), $extraFields); + } + + public function onKernelRequest(GetResponseEvent $event) + { + if ($event->isMasterRequest()) { + $this->serverData = $event->getRequest()->server->all(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/README.md b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/README.md new file mode 100644 index 0000000..b0d91ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/README.md @@ -0,0 +1,13 @@ +Monolog Bridge +============== + +Provides integration for Monolog with various Symfony components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/Monolog/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php new file mode 100644 index 0000000..6cb3159 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests\Handler; + +use Monolog\Logger; +use Symfony\Bridge\Monolog\Handler\ConsoleHandler; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Console\Command\Command; + +/** + * Tests the ConsoleHandler and also the ConsoleFormatter. + * + * @author Tobias Schultze + */ +class ConsoleHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $handler = new ConsoleHandler(null, false); + $this->assertFalse($handler->getBubble(), 'the bubble parameter gets propagated'); + } + + public function testIsHandling() + { + $handler = new ConsoleHandler(); + $this->assertFalse($handler->isHandling(array()), '->isHandling returns false when no output is set'); + } + + /** + * @dataProvider provideVerbosityMappingTests + */ + public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = array()) + { + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output + ->expects($this->atLeastOnce()) + ->method('getVerbosity') + ->will($this->returnValue($verbosity)) + ; + $handler = new ConsoleHandler($output, true, $map); + $this->assertSame($isHandling, $handler->isHandling(array('level' => $level)), + '->isHandling returns correct value depending on console verbosity and log level' + ); + } + + public function provideVerbosityMappingTests() + { + return array( + array(OutputInterface::VERBOSITY_QUIET, Logger::ERROR, false), + array(OutputInterface::VERBOSITY_NORMAL, Logger::WARNING, true), + array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, false), + array(OutputInterface::VERBOSITY_VERBOSE, Logger::NOTICE, true), + array(OutputInterface::VERBOSITY_VERBOSE, Logger::INFO, false), + array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::INFO, true), + array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::DEBUG, false), + array(OutputInterface::VERBOSITY_DEBUG, Logger::DEBUG, true), + array(OutputInterface::VERBOSITY_DEBUG, Logger::EMERGENCY, true), + array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, true, array( + OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE, + )), + array(OutputInterface::VERBOSITY_DEBUG, Logger::NOTICE, true, array( + OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE, + )), + ); + } + + public function testVerbosityChanged() + { + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output + ->expects($this->at(0)) + ->method('getVerbosity') + ->will($this->returnValue(OutputInterface::VERBOSITY_QUIET)) + ; + $output + ->expects($this->at(1)) + ->method('getVerbosity') + ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG)) + ; + $handler = new ConsoleHandler($output); + $this->assertFalse($handler->isHandling(array('level' => Logger::NOTICE)), + 'when verbosity is set to quiet, the handler does not handle the log' + ); + $this->assertTrue($handler->isHandling(array('level' => Logger::NOTICE)), + 'since the verbosity of the output increased externally, the handler is now handling the log' + ); + } + + public function testGetFormatter() + { + $handler = new ConsoleHandler(); + $this->assertInstanceOf('Symfony\Bridge\Monolog\Formatter\ConsoleFormatter', $handler->getFormatter(), + '-getFormatter returns ConsoleFormatter by default' + ); + } + + public function testWritingAndFormatting() + { + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output + ->expects($this->any()) + ->method('getVerbosity') + ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG)) + ; + $output + ->expects($this->once()) + ->method('write') + ->with('[2013-05-29 16:21:54] app.INFO: My info message '."\n") + ; + + $handler = new ConsoleHandler(null, false); + $handler->setOutput($output); + + $infoRecord = array( + 'message' => 'My info message', + 'context' => array(), + 'level' => Logger::INFO, + 'level_name' => Logger::getLevelName(Logger::INFO), + 'channel' => 'app', + 'datetime' => new \DateTime('2013-05-29 16:21:54'), + 'extra' => array(), + ); + + $this->assertTrue($handler->handle($infoRecord), 'The handler finished handling the log as bubble is false.'); + } + + public function testLogsFromListeners() + { + $output = new BufferedOutput(); + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + + $handler = new ConsoleHandler(null, false); + + $logger = new Logger('app'); + $logger->pushHandler($handler); + + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(ConsoleEvents::COMMAND, function () use ($logger) { + $logger->addInfo('Before command message.'); + }); + $dispatcher->addListener(ConsoleEvents::TERMINATE, function () use ($logger) { + $logger->addInfo('Before terminate message.'); + }); + + $dispatcher->addSubscriber($handler); + + $dispatcher->addListener(ConsoleEvents::COMMAND, function () use ($logger) { + $logger->addInfo('After command message.'); + }); + $dispatcher->addListener(ConsoleEvents::TERMINATE, function () use ($logger) { + $logger->addInfo('After terminate message.'); + }); + + $event = new ConsoleCommandEvent(new Command('foo'), $this->getMock('Symfony\Component\Console\Input\InputInterface'), $output); + $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + $this->assertContains('Before command message.', $out = $output->fetch()); + $this->assertContains('After command message.', $out); + + $event = new ConsoleTerminateEvent(new Command('foo'), $this->getMock('Symfony\Component\Console\Input\InputInterface'), $output, 0); + $dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + $this->assertContains('Before terminate message.', $out = $output->fetch()); + $this->assertContains('After terminate message.', $out); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php new file mode 100644 index 0000000..48bddc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests\Handler\FingersCrossed; + +use Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Monolog\Logger; + +class NotFoundActivationStrategyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider isActivatedProvider + */ + public function testIsActivated($url, $record, $expected) + { + $requestStack = new RequestStack(); + $requestStack->push(Request::create($url)); + + $strategy = new NotFoundActivationStrategy($requestStack, array('^/foo', 'bar'), Logger::WARNING); + + $this->assertEquals($expected, $strategy->isHandlerActivated($record)); + } + + public function isActivatedProvider() + { + return array( + array('/test', array('level' => Logger::DEBUG), false), + array('/foo', array('level' => Logger::DEBUG, 'context' => $this->getContextException(404)), false), + array('/baz/bar', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), + array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), + array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), + + array('/test', array('level' => Logger::ERROR), true), + array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), true), + array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), + ); + } + + protected function getContextException($code) + { + return array('exception' => new HttpException($code)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php new file mode 100644 index 0000000..e685ef0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests; + +use Monolog\Handler\TestHandler; +use Symfony\Bridge\Monolog\Handler\DebugHandler; +use Symfony\Bridge\Monolog\Logger; + +class LoggerTest extends \PHPUnit_Framework_TestCase +{ + public function testGetLogsWithDebugHandler() + { + $handler = new DebugHandler(); + $logger = new Logger(__METHOD__, array($handler)); + + $this->assertTrue($logger->error('error message')); + $this->assertSame(1, count($logger->getLogs())); + } + + public function testGetLogsWithoutDebugHandler() + { + $handler = new TestHandler(); + $logger = new Logger(__METHOD__, array($handler)); + + $this->assertTrue($logger->error('error message')); + $this->assertSame(array(), $logger->getLogs()); + } + + public function testCountErrorsWithDebugHandler() + { + $handler = new DebugHandler(); + $logger = new Logger(__METHOD__, array($handler)); + + $this->assertTrue($logger->debug('test message')); + $this->assertTrue($logger->info('test message')); + $this->assertTrue($logger->notice('test message')); + $this->assertTrue($logger->warning('test message')); + + $this->assertTrue($logger->error('test message')); + $this->assertTrue($logger->critical('test message')); + $this->assertTrue($logger->alert('test message')); + $this->assertTrue($logger->emergency('test message')); + + $this->assertSame(4, $logger->countErrors()); + } + + public function testGetLogs() + { + $logger = new Logger('test'); + $logger->pushHandler(new DebugHandler()); + + $logger->addInfo('test'); + $this->assertCount(1, $logger->getLogs()); + list($record) = $logger->getLogs(); + + $this->assertEquals('test', $record['message']); + $this->assertEquals(Logger::INFO, $record['priority']); + } + + public function testCountErrorsWithoutDebugHandler() + { + $handler = new TestHandler(); + $logger = new Logger(__METHOD__, array($handler)); + + $this->assertTrue($logger->error('error message')); + $this->assertSame(0, $logger->countErrors()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php new file mode 100644 index 0000000..1232bfa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests\Processor; + +use Monolog\Logger; +use Symfony\Bridge\Monolog\Processor\WebProcessor; +use Symfony\Component\HttpFoundation\Request; + +class WebProcessorTest extends \PHPUnit_Framework_TestCase +{ + public function testUsesRequestServerData() + { + list($event, $server) = $this->createRequestEvent(); + + $processor = new WebProcessor(); + $processor->onKernelRequest($event); + $record = $processor($this->getRecord()); + + $this->assertCount(5, $record['extra']); + $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']); + $this->assertEquals($server['REMOTE_ADDR'], $record['extra']['ip']); + $this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']); + $this->assertEquals($server['SERVER_NAME'], $record['extra']['server']); + $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); + } + + public function testCanBeConstructedWithExtraFields() + { + if (!$this->isExtraFieldsSupported()) { + $this->markTestSkipped('WebProcessor of the installed Monolog version does not support $extraFields parameter'); + } + + list($event, $server) = $this->createRequestEvent(); + + $processor = new WebProcessor(array('url', 'referrer')); + $processor->onKernelRequest($event); + $record = $processor($this->getRecord()); + + $this->assertCount(2, $record['extra']); + $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']); + $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); + } + + /** + * @return array + */ + private function createRequestEvent() + { + $server = array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + 'SERVER_NAME' => 'D', + 'HTTP_REFERER' => 'E', + ); + + $request = new Request(); + $request->server->replace($server); + + $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $event->expects($this->any()) + ->method('isMasterRequest') + ->will($this->returnValue(true)); + $event->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)); + + return array($event, $server); + } + + /** + * @param int $level + * @param string $message + * + * @return array Record + */ + private function getRecord($level = Logger::WARNING, $message = 'test') + { + return array( + 'message' => $message, + 'context' => array(), + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + } + + private function isExtraFieldsSupported() + { + $monologWebProcessorClass = new \ReflectionClass('Monolog\Processor\WebProcessor'); + + foreach ($monologWebProcessorClass->getConstructor()->getParameters() as $parameter) { + if ('extraFields' === $parameter->getName()) { + return true; + } + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/composer.json new file mode 100644 index 0000000..9e943ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/monolog-bridge", + "type": "symfony-bridge", + "description": "Symfony Monolog Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "monolog/monolog": "~1.11" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/event-dispatcher": "~2.8|~3.0" + }, + "suggest": { + "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", + "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings. You need version ~2.3 of the console for it.", + "symfony/event-dispatcher": "Needed when using log messages in console commands." + }, + "autoload": { + "psr-4": { "Symfony\\Bridge\\Monolog\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/phpunit.xml.dist new file mode 100644 index 0000000..8a60f06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Monolog/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/ClockMock.php b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/ClockMock.php new file mode 100644 index 0000000..b81fee9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/ClockMock.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +/** + * @author Nicolas Grekas + */ +class ClockMock +{ + private static $now; + + public static function withClockMock($enable = null) + { + if (null === $enable) { + return null !== self::$now; + } + + self::$now = is_numeric($enable) ? (float) $enable : ($enable ? microtime(true) : null); + } + + public static function time() + { + if (null === self::$now) { + return \time(); + } + + return (int) self::$now; + } + + public static function sleep($s) + { + if (null === self::$now) { + return \sleep($s); + } + + self::$now += (int) $s; + + return 0; + } + + public static function usleep($us) + { + if (null === self::$now) { + return \usleep($us); + } + + self::$now += $us / 1000000; + } + + public static function microtime($asFloat = false) + { + if (null === self::$now) { + return \microtime($asFloat); + } + + if ($asFloat) { + return self::$now; + } + + return sprintf("%0.6f %d\n", self::$now - (int) self::$now, (int) self::$now); + } + + public static function register($class) + { + $self = get_called_class(); + + $mockedNs = array(substr($class, 0, strrpos($class, '\\'))); + if (strpos($class, '\\Tests\\')) { + $ns = str_replace('\\Tests\\', '\\', $class); + $mockedNs[] = substr($ns, 0, strrpos($ns, '\\')); + } + foreach ($mockedNs as $ns) { + if (function_exists($ns.'\time')) { + continue; + } + eval(<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +/** + * Catch deprecation notices and print a summary report at the end of the test suite. + * + * @author Nicolas Grekas + */ +class DeprecationErrorHandler +{ + const MODE_WEAK = 'weak'; + + private static $isRegistered = false; + + /** + * Registers and configures the deprecation handler. + * + * The following reporting modes are supported: + * - use "weak" to hide the deprecation report but keep a global count; + * - use "/some-regexp/" to stop the test suite whenever a deprecation + * message matches the given regular expression; + * - use a number to define the upper bound of allowed deprecations, + * making the test suite fail whenever more notices are trigerred. + * + * @param int|string|false $mode The reporting mode. Defaults to not allowing any deprecations. + */ + public static function register($mode = 0) + { + if (self::$isRegistered) { + return; + } + if (self::MODE_WEAK !== $mode && (!isset($mode[0]) || '/' !== $mode[0])) { + $mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0; + } + $deprecations = array( + 'unsilencedCount' => 0, + 'remainingCount' => 0, + 'legacyCount' => 0, + 'otherCount' => 0, + 'unsilenced' => array(), + 'remaining' => array(), + 'legacy' => array(), + 'other' => array(), + ); + $deprecationHandler = function ($type, $msg, $file, $line, $context) use (&$deprecations, $mode) { + if (E_USER_DEPRECATED !== $type) { + return \PHPUnit_Util_ErrorHandler::handleError($type, $msg, $file, $line, $context); + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); + + $i = count($trace); + while (isset($trace[--$i]['class']) && ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_'))) { + // No-op + } + + if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) { + $class = isset($trace[$i]['object']) ? get_class($trace[$i]['object']) : $trace[$i]['class']; + $method = $trace[$i]['function']; + + if (0 !== error_reporting()) { + $group = 'unsilenced'; + } elseif (0 === strpos($method, 'testLegacy') + || 0 === strpos($method, 'provideLegacy') + || 0 === strpos($method, 'getLegacy') + || strpos($class, '\Legacy') + || in_array('legacy', \PHPUnit_Util_Test::getGroups($class, $method), true) + ) { + $group = 'legacy'; + } else { + $group = 'remaining'; + } + + if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) { + $e = new \Exception($msg); + $r = new \ReflectionProperty($e, 'trace'); + $r->setAccessible(true); + $r->setValue($e, array_slice($trace, 1, $i)); + + echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':'; + echo "\n".$msg; + echo "\nStack trace:"; + echo "\n".str_replace(' '.getcwd().DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString()); + echo "\n"; + + exit(1); + } + if ('legacy' !== $group && self::MODE_WEAK !== $mode) { + $ref = &$deprecations[$group][$msg]['count']; + ++$ref; + $ref = &$deprecations[$group][$msg][$class.'::'.$method]; + ++$ref; + } + } else { + $group = 'other'; + $ref = &$deprecations[$group][$msg]['count']; + ++$ref; + } + ++$deprecations[$group.'Count']; + }; + $oldErrorHandler = set_error_handler($deprecationHandler); + + if (null !== $oldErrorHandler) { + restore_error_handler(); + if (array('PHPUnit_Util_ErrorHandler', 'handleError') === $oldErrorHandler) { + restore_error_handler(); + self::register($mode); + } + } elseif (!isset($mode[0]) || '/' !== $mode[0]) { + self::$isRegistered = true; + if (self::hasColorSupport()) { + $colorize = function ($str, $red) { + $color = $red ? '41;37' : '43;30'; + + return "\x1B[{$color}m{$str}\x1B[0m"; + }; + } else { + $colorize = function ($str) {return $str;}; + } + register_shutdown_function(function () use ($mode, &$deprecations, $deprecationHandler, $colorize) { + $currErrorHandler = set_error_handler('var_dump'); + restore_error_handler(); + + if ($currErrorHandler !== $deprecationHandler) { + echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n"; + } + + $cmp = function ($a, $b) { + return $b['count'] - $a['count']; + }; + + foreach (array('unsilenced', 'remaining', 'legacy', 'other') as $group) { + if ($deprecations[$group.'Count']) { + echo "\n", $colorize(sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), 'legacy' !== $group), "\n"; + + uasort($deprecations[$group], $cmp); + + foreach ($deprecations[$group] as $msg => $notices) { + echo "\n", rtrim($msg, '.'), ': ', $notices['count'], "x\n"; + + arsort($notices); + + foreach ($notices as $method => $count) { + if ('count' !== $method) { + echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n"; + } + } + } + } + } + if (!empty($notices)) { + echo "\n"; + } + + if (DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) { + exit(1); + } + }); + } + } + + private static function hasColorSupport() + { + if ('\\' === DIRECTORY_SEPARATOR) { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + } + + return defined('STDOUT') && function_exists('posix_isatty') && @posix_isatty(STDOUT); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/README.md b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/README.md new file mode 100644 index 0000000..ef5f76f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/README.md @@ -0,0 +1,61 @@ +PHPUnit Bridge +============== + +Provides utilities for PHPUnit, especially user deprecation notices management. + +It comes with the following features: + + * disable the garbage collector; + * enforce a consistent `C` locale; + * auto-register `class_exists` to load Doctrine annotations; + * print a user deprecation notices summary at the end of the test suite; + * display the stack trace of a deprecation on-demand. + +By default any non-legacy-tagged or any non-@-silenced deprecation notices will +make tests fail. This can be changed by setting the `SYMFONY_DEPRECATIONS_HELPER` +environment variable to the maximum number of deprecations that are allowed to be +triggered before making the test suite fail. Alternatively, setting it to `weak` +will make the bridge ignore any deprecation notices and is useful to projects +that must use deprecated interfaces for backward compatibility reasons. + +A summary of deprecation notices is displayed at the end of the test suite: + + * **Unsilenced** reports deprecation notices that were triggered without the + recommended @-silencing operator; + * **Legacy** deprecation notices denote tests that explicitly test some legacy + interfaces. There are four ways to mark a test as legacy: + - make its class start with the `Legacy` prefix; + - make its method start with `testLegacy`; + - make its data provider start with `provideLegacy` or `getLegacy`; + - add the `@group legacy` annotation to its class or method. + * **Remaining/Other** deprecation notices are all other (non-legacy) + notices, grouped by message, test class and method. + +Usage +----- + +Add this bridge to the `require-dev` section of your `composer.json` file +(not in `require`) with e.g. `composer require --dev "symfony/phpunit-bridge"`. + +When running `phpunit`, you will see a summary of deprecation notices at the end +of the test suite. + +Deprecation notices in the **Unsilenced** section should just be @-silenced: +`@trigger_error('...', E_USER_DEPRECATED);`. Without the @-silencing operator, +users would need to opt-out from deprecation notices. Silencing by default swaps +this behavior and allows users to opt-in when they are ready to cope with them +(by adding a custom error handler like the one provided by this bridge.) + +Deprecation notices in the **Remaining/Other** section need some thought. +You have to decide either to: + + * update your code to not use deprecated interfaces anymore, thus gaining better + forward compatibility; + * or move them to the **Legacy** section (by using one of the above way). + +In case you need to inspect the stack trace of a particular deprecation triggered +by your unit tests, you can set the `SYMFONY_DEPRECATIONS_HELPER` env var to a +regular expression that matches this deprecation's message, encapsed between `/`. +For example, `SYMFONY_DEPRECATIONS_HELPER=/foobar/ phpunit` will stop your test +suite once a deprecation notice is triggered whose message contains the "foobar" +string. diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php new file mode 100644 index 0000000..4f4dc78 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/SymfonyTestsListener.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit; + +use Doctrine\Common\Annotations\AnnotationRegistry; + +/** + * Collects and replays skipped tests. + * + * @author Nicolas Grekas + */ +class SymfonyTestsListener extends \PHPUnit_Framework_BaseTestListener +{ + private static $globallyEnabled = false; + private $state = -1; + private $skippedFile = false; + private $wasSkipped = array(); + private $isSkipped = array(); + + public function __construct(array $extraClockMockedNamespaces = array()) + { + if ($extraClockMockedNamespaces) { + foreach ($extraClockMockedNamespaces as $ns) { + ClockMock::register($ns.'\DummyClass'); + } + } + if (self::$globallyEnabled) { + $this->state = -2; + } else { + self::$globallyEnabled = true; + } + } + + public function __destruct() + { + if (0 < $this->state) { + file_put_contents($this->skippedFile, 'isSkipped, true).';'); + } + } + + public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) + { + $suiteName = $suite->getName(); + + if (-1 === $this->state) { + echo "Testing $suiteName\n"; + $this->state = 0; + + if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) { + AnnotationRegistry::registerLoader('class_exists'); + } + + if ($this->skippedFile = getenv('SYMFONY_PHPUNIT_SKIPPED_TESTS')) { + $this->state = 1; + + if (file_exists($this->skippedFile)) { + $this->state = 2; + + if (!$this->wasSkipped = require $this->skippedFile) { + echo "All tests already ran successfully.\n"; + $suite->setTests(array()); + } + } + } + $testSuites = array($suite); + for ($i = 0; isset($testSuites[$i]); ++$i) { + foreach ($testSuites[$i]->tests() as $test) { + if ($test instanceof \PHPUnit_Framework_TestSuite) { + if (class_exists($test->getName(), false) && in_array('time-sensitive', \PHPUnit_Util_Test::getGroups($test->getName()), true)) { + ClockMock::register($test->getName()); + } else { + $testSuites[] = $test; + } + } + } + } + } elseif (2 === $this->state) { + $skipped = array(); + foreach ($suite->tests() as $test) { + if (!$test instanceof \PHPUnit_Framework_TestCase + || isset($this->wasSkipped[$suiteName]['*']) + || isset($this->wasSkipped[$suiteName][$test->getName()])) { + $skipped[] = $test; + } + } + $suite->setTests($skipped); + } + } + + public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + if (0 < $this->state) { + if ($test instanceof \PHPUnit_Framework_TestCase) { + $class = get_class($test); + $method = $test->getName(); + } else { + $class = $test->getName(); + $method = '*'; + } + + $this->isSkipped[$class][$method] = 1; + } + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { + $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName()); + + if (in_array('time-sensitive', $groups, true)) { + ClockMock::register(get_class($test)); + ClockMock::withClockMock(true); + } + } + } + + public function endTest(\PHPUnit_Framework_Test $test, $time) + { + if (-2 < $this->state && $test instanceof \PHPUnit_Framework_TestCase) { + $groups = \PHPUnit_Util_Test::getGroups(get_class($test), $test->getName()); + + if (in_array('time-sensitive', $groups, true)) { + ClockMock::withClockMock(false); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/TextUI/Command.php b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/TextUI/Command.php new file mode 100644 index 0000000..203fd16 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/TextUI/Command.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\TextUI; + +/** + * {@inheritdoc} + */ +class Command extends \PHPUnit_TextUI_Command +{ + /** + * {@inheritdoc} + */ + protected function createRunner() + { + return new TestRunner($this->arguments['loader']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php new file mode 100644 index 0000000..94602bb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\TextUI; + +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; + +/** + * {@inheritdoc} + */ +class TestRunner extends \PHPUnit_TextUI_TestRunner +{ + /** + * {@inheritdoc} + */ + protected function handleConfiguration(array &$arguments) + { + $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); + $arguments['listeners'][] = new SymfonyTestsListener(); + + return parent::handleConfiguration($arguments); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/bootstrap.php b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/bootstrap.php new file mode 100644 index 0000000..c0abdaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/bootstrap.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Bridge\PhpUnit\DeprecationErrorHandler; + +// Detect if we're loaded by an actual run of phpunit +if (!defined('PHPUNIT_COMPOSER_INSTALL') && !class_exists('PHPUnit_TextUI_Command', false)) { + return; +} + +// Disabling Zend Garbage Collection to prevent segfaults with PHP5.4+ +// https://bugs.php.net/bug.php?id=53976 +gc_disable(); + +// Enforce a consistent locale +setlocale(LC_ALL, 'C'); + +if (!class_exists('Doctrine\Common\Annotations\AnnotationRegistry', false) && class_exists('Doctrine\Common\Annotations\AnnotationRegistry')) { + AnnotationRegistry::registerLoader('class_exists'); +} + +DeprecationErrorHandler::register(getenv('SYMFONY_DEPRECATIONS_HELPER')); diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/composer.json new file mode 100644 index 0000000..a7b4b59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/phpunit-bridge", + "type": "symfony-bridge", + "description": "Symfony PHPUnit Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "suggest": { + "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" + }, + "autoload": { + "files": [ "bootstrap.php" ], + "psr-4": { "Symfony\\Bridge\\PhpUnit\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist new file mode 100644 index 0000000..7f631b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/PhpUnit/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/CHANGELOG.md new file mode 100644 index 0000000..1f8f60c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +2.3.0 +----- + + * First introduction of `Symfony\Bridge\ProxyManager` diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php new file mode 100644 index 0000000..0101026 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\LazyProxy\Instantiator; + +use ProxyManager\Configuration; +use ProxyManager\Factory\LazyLoadingValueHolderFactory; +use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; +use ProxyManager\Proxy\LazyLoadingInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; + +/** + * Runtime lazy loading proxy generator. + * + * @author Marco Pivetta + */ +class RuntimeInstantiator implements InstantiatorInterface +{ + /** + * @var LazyLoadingValueHolderFactory + */ + private $factory; + + public function __construct() + { + $config = new Configuration(); + $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); + + $this->factory = new LazyLoadingValueHolderFactory($config); + } + + /** + * {@inheritdoc} + */ + public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator) + { + return $this->factory->createProxy( + $definition->getClass(), + function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($realInstantiator) { + $wrappedInstance = call_user_func($realInstantiator); + + $proxy->setProxyInitializer(null); + + return true; + } + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php new file mode 100644 index 0000000..550786d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper; + +use ProxyManager\Generator\ClassGenerator; +use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy; +use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface; + +/** + * Generates dumped PHP code of proxies via reflection. + * + * @author Marco Pivetta + */ +class ProxyDumper implements DumperInterface +{ + /** + * @var string + */ + private $salt; + + /** + * @var LazyLoadingValueHolderGenerator + */ + private $proxyGenerator; + + /** + * @var BaseGeneratorStrategy + */ + private $classGenerator; + + /** + * Constructor. + * + * @param string $salt + */ + public function __construct($salt = '') + { + $this->salt = $salt; + $this->proxyGenerator = new LazyLoadingValueHolderGenerator(); + $this->classGenerator = new BaseGeneratorStrategy(); + } + + /** + * {@inheritdoc} + */ + public function isProxyCandidate(Definition $definition) + { + return $definition->isLazy() && ($class = $definition->getClass()) && class_exists($class); + } + + /** + * {@inheritdoc} + */ + public function getProxyFactoryCode(Definition $definition, $id) + { + $instantiation = 'return'; + + if ($definition->isShared()) { + $instantiation .= " \$this->services['$id'] ="; + } + + $methodName = 'get'.Container::camelize($id).'Service'; + $proxyClass = $this->getProxyClassName($definition); + + return <<$methodName(false); + + \$proxy->setProxyInitializer(null); + + return true; + } + ); + } + + +EOF; + } + + /** + * {@inheritdoc} + */ + public function getProxyCode(Definition $definition) + { + $generatedClass = new ClassGenerator($this->getProxyClassName($definition)); + + $this->proxyGenerator->generate(new \ReflectionClass($definition->getClass()), $generatedClass); + + return $this->classGenerator->generate($generatedClass); + } + + /** + * Produces the proxy class name for the given definition. + * + * @param Definition $definition + * + * @return string + */ + private function getProxyClassName(Definition $definition) + { + return str_replace('\\', '', $definition->getClass()).'_'.spl_object_hash($definition).$this->salt; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/README.md b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/README.md new file mode 100644 index 0000000..35d4199 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/README.md @@ -0,0 +1,15 @@ +ProxyManager Bridge +=================== + +Provides integration for [ProxyManager][1] with various Symfony components. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Bridge/ProxyManager/ + $ composer install + $ phpunit + +[1]: https://github.com/Ocramius/ProxyManager diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php new file mode 100644 index 0000000..ae13d54 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy; + +require_once __DIR__.'/Fixtures/includes/foo.php'; + +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Integration tests for {@see \Symfony\Component\DependencyInjection\ContainerBuilder} combined + * with the ProxyManager bridge. + * + * @author Marco Pivetta + */ +class ContainerBuilderTest extends \PHPUnit_Framework_TestCase +{ + public function testCreateProxyServiceWithRuntimeInstantiator() + { + $builder = new ContainerBuilder(); + + $builder->setProxyInstantiator(new RuntimeInstantiator()); + + $builder->register('foo1', 'ProxyManagerBridgeFooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $builder->getDefinition('foo1')->setLazy(true); + + /* @var $foo1 \ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface */ + $foo1 = $builder->get('foo1'); + + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); + $this->assertInstanceOf('\ProxyManagerBridgeFooClass', $foo1); + $this->assertInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1); + $this->assertFalse($foo1->isProxyInitialized()); + + $foo1->initializeProxy(); + + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved after initialization'); + $this->assertTrue($foo1->isProxyInitialized()); + $this->assertInstanceOf('\ProxyManagerBridgeFooClass', $foo1->getWrappedValueHolderValue()); + $this->assertNotInstanceOf('\ProxyManager\Proxy\LazyLoadingInterface', $foo1->getWrappedValueHolderValue()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php new file mode 100644 index 0000000..412674e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\Dumper; + +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; + +/** + * Integration tests for {@see \Symfony\Component\DependencyInjection\Dumper\PhpDumper} combined + * with the ProxyManager bridge. + * + * @author Marco Pivetta + */ +class PhpDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testDumpContainerWithProxyService() + { + $container = new ContainerBuilder(); + + $container->register('foo', 'stdClass'); + $container->getDefinition('foo')->setLazy(true); + $container->compile(); + + $dumper = new PhpDumper($container); + + $dumper->setProxyDumper(new ProxyDumper()); + + $dumpedString = $dumper->dump(); + + $this->assertStringMatchesFormatFile( + __DIR__.'/../Fixtures/php/lazy_service_structure.txt', + $dumpedString, + '->dump() does generate proxy lazy loading logic.' + ); + } + + /** + * Verifies that the generated container retrieves the same proxy instance on multiple subsequent requests. + */ + public function testDumpContainerWithProxyServiceWillShareProxies() + { + require_once __DIR__.'/../Fixtures/php/lazy_service.php'; + + $container = new \LazyServiceProjectServiceContainer(); + + /* @var $proxy \stdClass_c1d194250ee2e2b7d2eab8b8212368a8 */ + $proxy = $container->get('foo'); + $this->assertInstanceOf('stdClass_c1d194250ee2e2b7d2eab8b8212368a8', $proxy); + $this->assertSame($proxy, $container->get('foo')); + + $this->assertFalse($proxy->isProxyInitialized()); + + $proxy->initializeProxy(); + + $this->assertTrue($proxy->isProxyInitialized()); + $this->assertSame($proxy, $container->get('foo')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php new file mode 100644 index 0000000..1013a8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php @@ -0,0 +1,36 @@ +arguments = $arguments; + } + + public static function getInstance($arguments = array()) + { + $obj = new self($arguments); + $obj->called = true; + + return $obj; + } + + public function initialize() + { + $this->initialized = true; + } + + public function configure() + { + $this->configured = true; + } + + public function setBar($value = null) + { + $this->bar = $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php new file mode 100644 index 0000000..6c30a41 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service.php @@ -0,0 +1,187 @@ +services = array(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @param bool $lazyLoad whether to try lazy-loading the service with a proxy + * + * @return stdClass A stdClass instance. + */ + public function getFooService($lazyLoad = true) + { + if ($lazyLoad) { + return $this->services['foo'] = new stdClass_c1d194250ee2e2b7d2eab8b8212368a8( + function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) { + $wrappedInstance = $this->getFooService(false); + + $proxy->setProxyInitializer(null); + + return true; + } + ); + } + + return new \stdClass(); + } +} + +class stdClass_c1d194250ee2e2b7d2eab8b8212368a8 extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface +{ + /** + * @var \Closure|null initializer responsible for generating the wrapped object + */ + private $valueHolder5157dd96e88c0 = null; + + /** + * @var \Closure|null initializer responsible for generating the wrapped object + */ + private $initializer5157dd96e8924 = null; + + /** + * @override constructor for lazy initialization + * + * @param \Closure|null $initializer + */ + public function __construct($initializer) + { + $this->initializer5157dd96e8924 = $initializer; + } + + /** + * @param string $name + */ + public function __get($name) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__get', array('name' => $name)); + + return $this->valueHolder5157dd96e88c0->$name; + } + + /** + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__set', array('name' => $name, 'value' => $value)); + + $this->valueHolder5157dd96e88c0->$name = $value; + } + + /** + * @param string $name + * + * @return bool + */ + public function __isset($name) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__isset', array('name' => $name)); + + return isset($this->valueHolder5157dd96e88c0->$name); + } + + /** + * @param string $name + */ + public function __unset($name) + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__unset', array('name' => $name)); + + unset($this->valueHolder5157dd96e88c0->$name); + } + + /** + * + */ + public function __clone() + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__clone', array()); + + $this->valueHolder5157dd96e88c0 = clone $this->valueHolder5157dd96e88c0; + } + + /** + * + */ + public function __sleep() + { + $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__sleep', array()); + + return array('valueHolder5157dd96e88c0'); + } + + /** + * + */ + public function __wakeup() + { + } + + /** + * {@inheritdoc} + */ + public function setProxyInitializer(\Closure $initializer = null) + { + $this->initializer5157dd96e8924 = $initializer; + } + + /** + * {@inheritdoc} + */ + public function getProxyInitializer() + { + return $this->initializer5157dd96e8924; + } + + /** + * {@inheritdoc} + */ + public function initializeProxy() + { + return $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, 'initializeProxy', array()); + } + + /** + * {@inheritdoc} + */ + public function isProxyInitialized() + { + return null !== $this->valueHolder5157dd96e88c0; + } + + /** + * {@inheritdoc} + */ + public function getWrappedValueHolderValue() + { + return $this->valueHolder5157dd96e88c0; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt new file mode 100644 index 0000000..7bd36c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/php/lazy_service_structure.txt @@ -0,0 +1,26 @@ +services['foo'] = new stdClass_%s( + function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) { + $wrappedInstance = $this->getFooService(false); + + $proxy->setProxyInitializer(null); + + return true; + } + ); + } + + return new \stdClass(); + } +} + +class stdClass_%s extends \stdClass implements \ProxyManager\Proxy\VirtualProxyInterface +{%a}%A diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php new file mode 100644 index 0000000..8b2402b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\Instantiator; + +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Tests for {@see \Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator}. + * + * @author Marco Pivetta + */ +class RuntimeInstantiatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var RuntimeInstantiator + */ + protected $instantiator; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->instantiator = new RuntimeInstantiator(); + } + + public function testInstantiateProxy() + { + $instance = new \stdClass(); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $definition = new Definition('stdClass'); + $instantiator = function () use ($instance) { + return $instance; + }; + + /* @var $proxy \ProxyManager\Proxy\LazyLoadingInterface|\ProxyManager\Proxy\ValueHolderInterface */ + $proxy = $this->instantiator->instantiateProxy($container, $definition, 'foo', $instantiator); + + $this->assertInstanceOf('ProxyManager\Proxy\LazyLoadingInterface', $proxy); + $this->assertInstanceOf('ProxyManager\Proxy\ValueHolderInterface', $proxy); + $this->assertFalse($proxy->isProxyInitialized()); + + $proxy->initializeProxy(); + + $this->assertSame($instance, $proxy->getWrappedValueHolderValue()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php new file mode 100644 index 0000000..52f83e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper; + +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Tests for {@see \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper}. + * + * @author Marco Pivetta + */ +class ProxyDumperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ProxyDumper + */ + protected $dumper; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->dumper = new ProxyDumper(); + } + + /** + * @dataProvider getProxyCandidates + * + * @param Definition $definition + * @param bool $expected + */ + public function testIsProxyCandidate(Definition $definition, $expected) + { + $this->assertSame($expected, $this->dumper->isProxyCandidate($definition)); + } + + public function testGetProxyCode() + { + $definition = new Definition(__CLASS__); + + $definition->setLazy(true); + + $code = $this->dumper->getProxyCode($definition); + + $this->assertStringMatchesFormat( + '%Aclass SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest%aextends%w' + .'\Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper\ProxyDumperTest%a', + $code + ); + } + + public function testGetProxyFactoryCode() + { + $definition = new Definition(__CLASS__); + + $definition->setLazy(true); + + $code = $this->dumper->getProxyFactoryCode($definition, 'foo'); + + $this->assertStringMatchesFormat( + '%wif ($lazyLoad) {%wreturn $this->services[\'foo\'] = new ' + .'SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest_%s(%wfunction ' + .'(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {' + .'%w$wrappedInstance = $this->getFooService(false);%w$proxy->setProxyInitializer(null);' + .'%wreturn true;%w}%w);%w}%w', + $code + ); + } + + /** + * @return array + */ + public function getProxyCandidates() + { + $definitions = array( + array(new Definition(__CLASS__), true), + array(new Definition('stdClass'), true), + array(new Definition('foo'.uniqid()), false), + array(new Definition(), false), + ); + + array_map( + function ($definition) { + $definition[0]->setLazy(true); + }, + $definitions + ); + + return $definitions; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/composer.json new file mode 100644 index 0000000..e91f237 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/proxy-manager-bridge", + "type": "symfony-bridge", + "description": "Symfony ProxyManager Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/dependency-injection": "~2.8|~3.0", + "ocramius/proxy-manager": "~0.4|~1.0" + }, + "require-dev": { + "symfony/config": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Bridge\\ProxyManager\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist new file mode 100644 index 0000000..60980be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/ProxyManager/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/AppVariable.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/AppVariable.php new file mode 100644 index 0000000..6657abf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/AppVariable.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + +/** + * Exposes some Symfony parameters and services as an "app" global variable. + * + * @author Fabien Potencier + */ +class AppVariable +{ + private $tokenStorage; + private $requestStack; + private $environment; + private $debug; + + public function setTokenStorage(TokenStorageInterface $tokenStorage) + { + $this->tokenStorage = $tokenStorage; + } + + public function setRequestStack(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + public function setEnvironment($environment) + { + $this->environment = $environment; + } + + public function setDebug($debug) + { + $this->debug = (bool) $debug; + } + + /** + * Returns the current user. + * + * @return mixed + * + * @see TokenInterface::getUser() + */ + public function getUser() + { + if (null !== $this->tokenStorage) { + $tokenStorage = $this->tokenStorage; + } else { + throw new \RuntimeException('The "app.user" variable is not available.'); + } + + if (!$token = $tokenStorage->getToken()) { + return; + } + + $user = $token->getUser(); + if (is_object($user)) { + return $user; + } + } + + /** + * Returns the current request. + * + * @return Request|null The HTTP request object + */ + public function getRequest() + { + if (null === $this->requestStack) { + throw new \RuntimeException('The "app.request" variable is not available.'); + } + + return $this->requestStack->getCurrentRequest(); + } + + /** + * Returns the current session. + * + * @return Session|null The session + */ + public function getSession() + { + if (null === $this->requestStack) { + throw new \RuntimeException('The "app.session" variable is not available.'); + } + + if ($request = $this->getRequest()) { + return $request->getSession(); + } + } + + /** + * Returns the current app environment. + * + * @return string The current environment string (e.g 'dev') + */ + public function getEnvironment() + { + if (null === $this->environment) { + throw new \RuntimeException('The "app.environment" variable is not available.'); + } + + return $this->environment; + } + + /** + * Returns the current app debug mode. + * + * @return bool The current debug mode + */ + public function getDebug() + { + if (null === $this->debug) { + throw new \RuntimeException('The "app.debug" variable is not available.'); + } + + return $this->debug; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/CHANGELOG.md new file mode 100644 index 0000000..b4df596 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -0,0 +1,47 @@ +CHANGELOG +========= + +2.7.0 +----- + + * added LogoutUrlExtension (provides `logout_url` and `logout_path`) + * added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions) + * added AssetExtension (provides the `asset` and `asset_version` functions) + * Added possibility to extract translation messages from a file or files besides extracting from a directory + +2.5.0 +----- + + * moved command `twig:lint` from `TwigBundle` + +2.4.0 +----- + + * added stopwatch tag to time templates with the WebProfilerBundle + +2.3.0 +----- + + * added helpers form(), form_start() and form_end() + * deprecated form_enctype() in favor of form_start() + +2.2.0 +----- + + * added a `controller` function to help generating controller references + * added a `render_esi` and a `render_hinclude` function + * [BC BREAK] restricted the `render` tag to only accept URIs or ControllerReference instances (the signature changed) + * added a `render` function to render a request + * The `app` global variable is now injected even when using the twig service directly. + * Added an optional parameter to the `path` and `url` function which allows to generate + relative paths (e.g. "../parent-file") and scheme-relative URLs (e.g. "//example.com/dir/file"). + +2.1.0 +----- + + * added global variables access in a form theme + * added TwigEngine + * added TwigExtractor + * added a csrf_token function + * added a way to specify a default domain for a Twig template (via the + 'trans_default_domain' tag) diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Command/DebugCommand.php new file mode 100644 index 0000000..861de86 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -0,0 +1,220 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Lists twig functions, filters, globals and tests present in the current project. + * + * @author Jordi Boggiano + */ +class DebugCommand extends Command +{ + private $twig; + + /** + * {@inheritdoc} + */ + public function __construct($name = 'debug:twig') + { + parent::__construct($name); + } + + /** + * Sets the twig environment. + * + * @param \Twig_Environment $twig + */ + public function setTwigEnvironment(\Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * @return \Twig_Environment $twig + */ + protected function getTwigEnvironment() + { + return $this->twig; + } + + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('filter', InputArgument::OPTIONAL, 'Show details for all entries matching this filter'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (text or json)', 'text'), + )) + ->setDescription('Shows a list of twig functions, filters, globals and tests') + ->setHelp(<<%command.name%
    command outputs a list of twig functions, +filters, globals and tests. Output can be filtered with an optional argument. + + php %command.full_name% + +The command lists all functions, filters, etc. + + php %command.full_name% date + +The command lists everything that contains the word date. + + php %command.full_name% --format=json + +The command lists everything in a machine readable json format. +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $twig = $this->getTwigEnvironment(); + + if (null === $twig) { + $io->error('The Twig environment needs to be set.'); + + return 1; + } + + $types = array('functions', 'filters', 'tests', 'globals'); + + if ($input->getOption('format') === 'json') { + $data = array(); + foreach ($types as $type) { + foreach ($twig->{'get'.ucfirst($type)}() as $name => $entity) { + $data[$type][$name] = $this->getMetadata($type, $entity); + } + } + $data['tests'] = array_keys($data['tests']); + $io->writeln(json_encode($data)); + + return 0; + } + + $filter = $input->getArgument('filter'); + + foreach ($types as $index => $type) { + $items = array(); + foreach ($twig->{'get'.ucfirst($type)}() as $name => $entity) { + if (!$filter || false !== strpos($name, $filter)) { + $items[$name] = $name.$this->getPrettyMetadata($type, $entity); + } + } + + if (!$items) { + continue; + } + + $io->section(ucfirst($type)); + + ksort($items); + $io->listing($items); + } + + return 0; + } + + private function getMetadata($type, $entity) + { + if ($type === 'globals') { + return $entity; + } + if ($type === 'tests') { + return; + } + if ($type === 'functions' || $type === 'filters') { + $args = array(); + $cb = $entity->getCallable(); + if (is_null($cb)) { + return; + } + if (is_array($cb)) { + if (!method_exists($cb[0], $cb[1])) { + return; + } + $refl = new \ReflectionMethod($cb[0], $cb[1]); + } elseif (is_object($cb) && method_exists($cb, '__invoke')) { + $refl = new \ReflectionMethod($cb, '__invoke'); + } elseif (function_exists($cb)) { + $refl = new \ReflectionFunction($cb); + } elseif (is_string($cb) && preg_match('{^(.+)::(.+)$}', $cb, $m) && method_exists($m[1], $m[2])) { + $refl = new \ReflectionMethod($m[1], $m[2]); + } else { + throw new \UnexpectedValueException('Unsupported callback type'); + } + + // filter out context/environment args + $args = array_filter($refl->getParameters(), function ($param) use ($entity) { + if ($entity->needsContext() && $param->getName() === 'context') { + return false; + } + + return !$param->getClass() || $param->getClass()->getName() !== 'Twig_Environment'; + }); + + // format args + $args = array_map(function ($param) { + if ($param->isDefaultValueAvailable()) { + return $param->getName().' = '.json_encode($param->getDefaultValue()); + } + + return $param->getName(); + }, $args); + + if ($type === 'filters') { + // remove the value the filter is applied on + array_shift($args); + } + + return $args; + } + } + + private function getPrettyMetadata($type, $entity) + { + if ($type === 'tests') { + return ''; + } + + try { + $meta = $this->getMetadata($type, $entity); + if ($meta === null) { + return '(unknown?)'; + } + } catch (\UnexpectedValueException $e) { + return ' '.$e->getMessage().''; + } + + if ($type === 'globals') { + if (is_object($meta)) { + return ' = object('.get_class($meta).')'; + } + + return ' = '.substr(@json_encode($meta), 0, 50); + } + + if ($type === 'functions') { + return '('.implode(', ', $meta).')'; + } + + if ($type === 'filters') { + return $meta ? '('.implode(', ', $meta).')' : ''; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Command/LintCommand.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Command/LintCommand.php new file mode 100644 index 0000000..1299e45 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Finder\Finder; + +/** + * Command that will validate your template syntax and output encountered errors. + * + * @author Marc Weistroff + * @author Jérôme Tamarelle + */ +class LintCommand extends Command +{ + private $twig; + + /** + * {@inheritdoc} + */ + public function __construct($name = 'lint:twig') + { + parent::__construct($name); + } + + /** + * Sets the twig environment. + * + * @param \Twig_Environment $twig + */ + public function setTwigEnvironment(\Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * @return \Twig_Environment $twig + */ + protected function getTwigEnvironment() + { + return $this->twig; + } + + protected function configure() + { + $this + ->setDescription('Lints a template and outputs encountered errors') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt') + ->addArgument('filename', InputArgument::IS_ARRAY) + ->setHelp(<<%command.name% command lints a template and outputs to STDOUT +the first encountered syntax error. + +You can validate the syntax of a file: + +php %command.full_name% filename + +Or of a whole directory: + +php %command.full_name% dirname +php %command.full_name% dirname --format=json + +You can also pass the template contents from STDIN: + +cat filename | php %command.full_name% +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + if (null === $twig = $this->getTwigEnvironment()) { + $io->error('The Twig environment needs to be set.'); + + return 1; + } + + $filenames = $input->getArgument('filename'); + + if (0 === count($filenames)) { + if (0 !== ftell(STDIN)) { + throw new \RuntimeException('Please provide a filename or pipe template content to STDIN.'); + } + + $template = ''; + while (!feof(STDIN)) { + $template .= fread(STDIN, 1024); + } + + return $this->display($input, $output, $io, array($this->validate($twig, $template, uniqid('sf_')))); + } + + $filesInfo = $this->getFilesInfo($twig, $filenames); + + return $this->display($input, $output, $io, $filesInfo); + } + + private function getFilesInfo(\Twig_Environment $twig, array $filenames) + { + $filesInfo = array(); + foreach ($filenames as $filename) { + foreach ($this->findFiles($filename) as $file) { + $filesInfo[] = $this->validate($twig, file_get_contents($file), $file); + } + } + + return $filesInfo; + } + + protected function findFiles($filename) + { + if (is_file($filename)) { + return array($filename); + } elseif (is_dir($filename)) { + return Finder::create()->files()->in($filename)->name('*.twig'); + } + + throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename)); + } + + private function validate(\Twig_Environment $twig, $template, $file) + { + $realLoader = $twig->getLoader(); + try { + $temporaryLoader = new \Twig_Loader_Array(array((string) $file => $template)); + $twig->setLoader($temporaryLoader); + $nodeTree = $twig->parse($twig->tokenize($template, (string) $file)); + $twig->compile($nodeTree); + $twig->setLoader($realLoader); + } catch (\Twig_Error $e) { + $twig->setLoader($realLoader); + + return array('template' => $template, 'file' => $file, 'valid' => false, 'exception' => $e); + } + + return array('template' => $template, 'file' => $file, 'valid' => true); + } + + private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, $files) + { + switch ($input->getOption('format')) { + case 'txt': + return $this->displayTxt($output, $io, $files); + case 'json': + return $this->displayJson($output, $files); + default: + throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format'))); + } + } + + private function displayTxt(OutputInterface $output, SymfonyStyle $io, $filesInfo) + { + $errors = 0; + + foreach ($filesInfo as $info) { + if ($info['valid'] && $output->isVerbose()) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$errors; + $this->renderException($io, $info['template'], $info['exception'], $info['file']); + } + } + + if ($errors === 0) { + $io->success(sprintf('All %d Twig files contain valid syntax.', count($filesInfo))); + } else { + $io->warning(sprintf('%d Twig files have valid syntax and %d contain errors.', count($filesInfo) - $errors, $errors)); + } + + return min($errors, 1); + } + + private function displayJson(OutputInterface $output, $filesInfo) + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + unset($v['template']); + if (!$v['valid']) { + $v['message'] = $v['exception']->getMessage(); + unset($v['exception']); + ++$errors; + } + }); + + $output->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT)); + + return min($errors, 1); + } + + private function renderException(OutputInterface $output, $template, \Twig_Error $exception, $file = null) + { + $line = $exception->getTemplateLine(); + + if ($file) { + $output->text(sprintf(' ERROR in %s (line %s)', $file, $line)); + } else { + $output->text(sprintf(' ERROR (line %s)', $line)); + } + + foreach ($this->getContext($template, $line) as $lineNumber => $code) { + $output->text(sprintf( + '%s %-6s %s', + $lineNumber === $line ? ' >> ' : ' ', + $lineNumber, + $code + )); + if ($lineNumber === $line) { + $output->text(sprintf(' >> %s ', $exception->getRawMessage())); + } + } + } + + private function getContext($template, $line, $context = 3) + { + $lines = explode("\n", $template); + + $position = max(0, $line - $context); + $max = min(count($lines), $line - 1 + $context); + + $result = array(); + while ($position < $max) { + $result[$position + 1] = $lines[$position]; + ++$position; + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php new file mode 100644 index 0000000..90b21d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * TwigDataCollector. + * + * @author Fabien Potencier + */ +class TwigDataCollector extends DataCollector implements LateDataCollectorInterface +{ + private $profile; + private $computed; + + public function __construct(\Twig_Profiler_Profile $profile) + { + $this->profile = $profile; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + $this->data['profile'] = serialize($this->profile); + } + + public function getTime() + { + return $this->getProfile()->getDuration() * 1000; + } + + public function getTemplateCount() + { + return $this->getComputedData('template_count'); + } + + public function getTemplates() + { + return $this->getComputedData('templates'); + } + + public function getBlockCount() + { + return $this->getComputedData('block_count'); + } + + public function getMacroCount() + { + return $this->getComputedData('macro_count'); + } + + public function getHtmlCallGraph() + { + $dumper = new \Twig_Profiler_Dumper_Html(); + $dump = $dumper->dump($this->getProfile()); + + // needed to remove the hardcoded CSS styles + $dump = str_replace(array( + '', + '', + '', + ), array( + '', + '', + '', + ), $dump); + + return new \Twig_Markup($dump, 'UTF-8'); + } + + public function getProfile() + { + if (null === $this->profile) { + $this->profile = unserialize($this->data['profile']); + } + + return $this->profile; + } + + private function getComputedData($index) + { + if (null === $this->computed) { + $this->computed = $this->computeData($this->getProfile()); + } + + return $this->computed[$index]; + } + + private function computeData(\Twig_Profiler_Profile $profile) + { + $data = array( + 'template_count' => 0, + 'block_count' => 0, + 'macro_count' => 0, + ); + + $templates = array(); + foreach ($profile as $p) { + $d = $this->computeData($p); + + $data['template_count'] += ($p->isTemplate() ? 1 : 0) + $d['template_count']; + $data['block_count'] += ($p->isBlock() ? 1 : 0) + $d['block_count']; + $data['macro_count'] += ($p->isMacro() ? 1 : 0) + $d['macro_count']; + + if ($p->isTemplate()) { + if (!isset($templates[$p->getTemplate()])) { + $templates[$p->getTemplate()] = 1; + } else { + ++$templates[$p->getTemplate()]; + } + } + + foreach ($d['templates'] as $template => $count) { + if (!isset($templates[$template])) { + $templates[$template] = $count; + } else { + $templates[$template] += $count; + } + } + } + $data['templates'] = $templates; + + return $data; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'twig'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/AssetExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/AssetExtension.php new file mode 100644 index 0000000..f599a9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/AssetExtension.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Asset\Packages; + +/** + * Twig extension for the Symfony Asset component. + * + * @author Fabien Potencier + */ +class AssetExtension extends \Twig_Extension +{ + private $packages; + + public function __construct(Packages $packages) + { + $this->packages = $packages; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('asset', array($this, 'getAssetUrl')), + new \Twig_SimpleFunction('asset_version', array($this, 'getAssetVersion')), + ); + } + + /** + * Returns the public url/path of an asset. + * + * If the package used to generate the path is an instance of + * UrlPackage, you will always get a URL and not a path. + * + * @param string $path A public path + * @param string $packageName The name of the asset package to use + * + * @return string The public path of the asset + */ + public function getAssetUrl($path, $packageName = null) + { + return $this->packages->getUrl($path, $packageName); + } + + /** + * Returns the version of an asset. + * + * @param string $path A public path + * @param string $packageName The name of the asset package to use + * + * @return string The asset version + */ + public function getAssetVersion($path, $packageName = null) + { + return $this->packages->getVersion($path, $packageName); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'asset'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/CodeExtension.php new file mode 100644 index 0000000..5ec74b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -0,0 +1,235 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +/** + * Twig extension relate to PHP code and used by the profiler and the default exception templates. + * + * @author Fabien Potencier + */ +class CodeExtension extends \Twig_Extension +{ + private $fileLinkFormat; + private $rootDir; + private $charset; + + /** + * Constructor. + * + * @param string $fileLinkFormat The format for links to source files + * @param string $rootDir The project root directory + * @param string $charset The charset + */ + public function __construct($fileLinkFormat, $rootDir, $charset) + { + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->rootDir = str_replace('/', DIRECTORY_SEPARATOR, dirname($rootDir)).DIRECTORY_SEPARATOR; + $this->charset = $charset; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + new \Twig_SimpleFilter('abbr_class', array($this, 'abbrClass'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('abbr_method', array($this, 'abbrMethod'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('format_args', array($this, 'formatArgs'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('format_args_as_text', array($this, 'formatArgsAsText')), + new \Twig_SimpleFilter('file_excerpt', array($this, 'fileExcerpt'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('format_file', array($this, 'formatFile'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('format_file_from_text', array($this, 'formatFileFromText'), array('is_safe' => array('html'))), + new \Twig_SimpleFilter('file_link', array($this, 'getFileLink')), + ); + } + + public function abbrClass($class) + { + $parts = explode('\\', $class); + $short = array_pop($parts); + + return sprintf('%s', $class, $short); + } + + public function abbrMethod($method) + { + if (false !== strpos($method, '::')) { + list($class, $method) = explode('::', $method, 2); + $result = sprintf('%s::%s()', $this->abbrClass($class), $method); + } elseif ('Closure' === $method) { + $result = sprintf('%s', $method, $method); + } else { + $result = sprintf('%s()', $method, $method); + } + + return $result; + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + public function formatArgs($args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $parts = explode('\\', $item[1]); + $short = array_pop($parts); + $formattedValue = sprintf('object(%s)', $item[1], $short); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('string' === $item[0]) { + $formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->charset)); + } elseif ('null' === $item[0]) { + $formattedValue = 'null'; + } elseif ('boolean' === $item[0]) { + $formattedValue = ''.strtolower(var_export($item[1], true)).''; + } elseif ('resource' === $item[0]) { + $formattedValue = 'resource'; + } else { + $formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->charset), true)); + } + + $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); + } + + return implode(', ', $result); + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + public function formatArgsAsText($args) + { + return strip_tags($this->formatArgs($args)); + } + + /** + * Returns an excerpt of a code file around the given line number. + * + * @param string $file A file path + * @param int $line The selected line number + * + * @return string An HTML string + */ + public function fileExcerpt($file, $line) + { + if (is_readable($file)) { + // highlight_file could throw warnings + // see https://bugs.php.net/bug.php?id=25725 + $code = @highlight_file($file, true); + // remove main code/span tags + $code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code); + $content = preg_split('#
    #', $code); + + $lines = array(); + for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; ++$i) { + $lines[] = ''.self::fixCodeMarkup($content[$i - 1]).''; + } + + return '
      '.implode("\n", $lines).'
    '; + } + } + + /** + * Formats a file path. + * + * @param string $file An absolute file path + * @param int $line The line number + * @param string $text Use this text for the link rather than the file path + * + * @return string + */ + public function formatFile($file, $line, $text = null) + { + $file = trim($file); + + if (null === $text) { + $text = str_replace('/', DIRECTORY_SEPARATOR, $file); + if (0 === strpos($text, $this->rootDir)) { + $text = substr($text, strlen($this->rootDir)); + $text = explode(DIRECTORY_SEPARATOR, $text, 2); + $text = sprintf('%s%s', $this->rootDir, $text[0], isset($text[1]) ? DIRECTORY_SEPARATOR.$text[1] : ''); + } + } + + $text = "$text at line $line"; + + if (false !== $link = $this->getFileLink($file, $line)) { + $flags = ENT_QUOTES | ENT_SUBSTITUTE; + + return sprintf('%s', htmlspecialchars($link, $flags, $this->charset), $text); + } + + return $text; + } + + /** + * Returns the link for a given file/line pair. + * + * @param string $file An absolute file path + * @param int $line The line number + * + * @return string A link of false + */ + public function getFileLink($file, $line) + { + if ($this->fileLinkFormat && is_file($file)) { + return strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line)); + } + + return false; + } + + public function formatFileFromText($text) + { + return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) { + return 'in '.$this->formatFile($match[2], $match[3]); + }, $text); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'code'; + } + + protected static function fixCodeMarkup($line) + { + //
    ending tag from previous line + $opening = strpos($line, ''); + if (false !== $closing && (false === $opening || $closing < $opening)) { + $line = substr_replace($line, '', $closing, 7); + } + + // missing
    tag at the end of line + $opening = strpos($line, ''); + if (false !== $opening && (false === $closing || $closing > $opening)) { + $line .= '
    '; + } + + return $line; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/DumpExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/DumpExtension.php new file mode 100644 index 0000000..30318ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/DumpExtension.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Bridge\Twig\TokenParser\DumpTokenParser; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; + +/** + * Provides integration of the dump() function with Twig. + * + * @author Nicolas Grekas + */ +class DumpExtension extends \Twig_Extension +{ + private $cloner; + + public function __construct(ClonerInterface $cloner) + { + $this->cloner = $cloner; + } + + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('dump', array($this, 'dump'), array('is_safe' => array('html'), 'needs_context' => true, 'needs_environment' => true)), + ); + } + + public function getTokenParsers() + { + return array(new DumpTokenParser()); + } + + public function getName() + { + return 'dump'; + } + + public function dump(\Twig_Environment $env, $context) + { + if (!$env->isDebug()) { + return; + } + + if (2 === func_num_args()) { + $vars = array(); + foreach ($context as $key => $value) { + if (!$value instanceof \Twig_Template) { + $vars[$key] = $value; + } + } + + $vars = array($vars); + } else { + $vars = func_get_args(); + unset($vars[0], $vars[1]); + } + + $dump = fopen('php://memory', 'r+b'); + $dumper = new HtmlDumper($dump); + + foreach ($vars as $value) { + $dumper->dump($this->cloner->cloneVar($value)); + } + rewind($dump); + + return stream_get_contents($dump); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php new file mode 100644 index 0000000..6b30a27 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.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\Bridge\Twig\Extension; + +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * ExpressionExtension gives a way to create Expressions from a template. + * + * @author Fabien Potencier + */ +class ExpressionExtension extends \Twig_Extension +{ + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('expression', array($this, 'createExpression')), + ); + } + + public function createExpression($expression) + { + return new Expression($expression); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'expression'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/FormExtension.php new file mode 100644 index 0000000..dab1a6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; +use Symfony\Bridge\Twig\Form\TwigRendererInterface; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; + +/** + * FormExtension extends Twig with form capabilities. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class FormExtension extends \Twig_Extension implements \Twig_Extension_InitRuntimeInterface +{ + /** + * This property is public so that it can be accessed directly from compiled + * templates without having to call a getter, which slightly decreases performance. + * + * @var TwigRendererInterface + */ + public $renderer; + + public function __construct(TwigRendererInterface $renderer) + { + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public function initRuntime(\Twig_Environment $environment) + { + $this->renderer->setEnvironment($environment); + } + + /** + * {@inheritdoc} + */ + public function getTokenParsers() + { + return array( + // {% form_theme form "SomeBundle::widgets.twig" %} + new FormThemeTokenParser(), + ); + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('form_widget', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form_errors', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form_label', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form_row', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form_rest', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form_start', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('form_end', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), + new \Twig_SimpleFunction('csrf_token', array($this, 'renderCsrfToken')), + ); + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + new \Twig_SimpleFilter('humanize', array($this, 'humanize')), + ); + } + + /** + * {@inheritdoc} + */ + public function getTests() + { + return array( + new \Twig_SimpleTest('selectedchoice', array($this, 'isSelectedChoice')), + ); + } + + /** + * {@inheritdoc} + */ + public function renderCsrfToken($tokenId) + { + return $this->renderer->renderCsrfToken($tokenId); + } + + /** + * Makes a technical name human readable. + * + * @param string $text The text to humanize. + * + * @return string The humanized text. + */ + public function humanize($text) + { + return $this->renderer->humanize($text); + } + + /** + * Returns whether a choice is selected for a given form value. + * + * Unfortunately Twig does not support an efficient way to execute the + * "is_selected" closure passed to the template by ChoiceType. It is faster + * to implement the logic here (around 65ms for a specific form). + * + * Directly implementing the logic here is also faster than doing so in + * ChoiceView (around 30ms). + * + * The worst option tested so far is to implement the logic in ChoiceView + * and access the ChoiceView method directly in the template. Doing so is + * around 220ms slower than doing the method call here in the filter. Twig + * seems to be much more efficient at executing filters than at executing + * methods of an object. + * + * @param ChoiceView $choice The choice to check. + * @param string|array $selectedValue The selected value to compare. + * + * @return bool Whether the choice is selected. + * + * @see ChoiceView::isSelected() + */ + public function isSelectedChoice(ChoiceView $choice, $selectedValue) + { + if (is_array($selectedValue)) { + return in_array($choice->value, $selectedValue, true); + } + + return $choice->value === $selectedValue; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'form'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php new file mode 100644 index 0000000..ad7949d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; + +/** + * Twig extension for the Symfony HttpFoundation component. + * + * @author Fabien Potencier + */ +class HttpFoundationExtension extends \Twig_Extension +{ + private $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('absolute_url', array($this, 'generateAbsoluteUrl')), + new \Twig_SimpleFunction('relative_path', array($this, 'generateRelativePath')), + ); + } + + /** + * Returns the absolute URL for the given absolute or relative path. + * + * This method returns the path unchanged if no request is available. + * + * @param string $path The path + * + * @return string The absolute URL + * + * @see Request::getUriForPath() + */ + public function generateAbsoluteUrl($path) + { + if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) { + return $path; + } + + if (!$request = $this->requestStack->getMasterRequest()) { + return $path; + } + + if (!$path || '/' !== $path[0]) { + $prefix = $request->getPathInfo(); + $last = strlen($prefix) - 1; + if ($last !== $pos = strrpos($prefix, '/')) { + $prefix = substr($prefix, 0, $pos).'/'; + } + + return $request->getUriForPath($prefix.$path); + } + + return $request->getSchemeAndHttpHost().$path; + } + + /** + * Returns a relative path based on the current Request. + * + * This method returns the path unchanged if no request is available. + * + * @param string $path The path + * + * @return string The relative path + * + * @see Request::getRelativeUriForPath() + */ + public function generateRelativePath($path) + { + if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) { + return $path; + } + + if (!$request = $this->requestStack->getMasterRequest()) { + return $path; + } + + return $request->getRelativeUriForPath($path); + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return 'request'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php new file mode 100644 index 0000000..1da12aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +/** + * Provides integration with the HttpKernel component. + * + * @author Fabien Potencier + */ +class HttpKernelExtension extends \Twig_Extension +{ + private $handler; + + /** + * Constructor. + * + * @param FragmentHandler $handler A FragmentHandler instance + */ + public function __construct(FragmentHandler $handler) + { + $this->handler = $handler; + } + + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('render', array($this, 'renderFragment'), array('is_safe' => array('html'))), + new \Twig_SimpleFunction('render_*', array($this, 'renderFragmentStrategy'), array('is_safe' => array('html'))), + new \Twig_SimpleFunction('controller', array($this, 'controller')), + ); + } + + /** + * Renders a fragment. + * + * @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 + * + * @see FragmentHandler::render() + */ + public function renderFragment($uri, $options = array()) + { + $strategy = isset($options['strategy']) ? $options['strategy'] : 'inline'; + unset($options['strategy']); + + return $this->handler->render($uri, $strategy, $options); + } + + /** + * Renders a fragment. + * + * @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 + * + * @see FragmentHandler::render() + */ + public function renderFragmentStrategy($strategy, $uri, $options = array()) + { + return $this->handler->render($uri, $strategy, $options); + } + + public function controller($controller, $attributes = array(), $query = array()) + { + return new ControllerReference($controller, $attributes, $query); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'http_kernel'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php new file mode 100644 index 0000000..7fc2787 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; + +/** + * LogoutUrlHelper provides generator functions for the logout URL to Twig. + * + * @author Jeremy Mikola + */ +class LogoutUrlExtension extends \Twig_Extension +{ + private $generator; + + public function __construct(LogoutUrlGenerator $generator) + { + $this->generator = $generator; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('logout_url', array($this, 'getLogoutUrl')), + new \Twig_SimpleFunction('logout_path', array($this, 'getLogoutPath')), + ); + } + + /** + * Generates the relative logout URL for the firewall. + * + * @param string|null $key The firewall key or null to use the current firewall key + * + * @return string The relative logout URL + */ + public function getLogoutPath($key = null) + { + return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * Generates the absolute logout URL for the firewall. + * + * @param string|null $key The firewall key or null to use the current firewall key + * + * @return string The absolute logout URL + */ + public function getLogoutUrl($key = null) + { + return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'logout_url'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php new file mode 100644 index 0000000..648a6c8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * @author Fabien Potencier + */ +class ProfilerExtension extends \Twig_Extension_Profiler +{ + private $stopwatch; + private $events; + + public function __construct(\Twig_Profiler_Profile $profile, Stopwatch $stopwatch = null) + { + parent::__construct($profile); + + $this->stopwatch = $stopwatch; + $this->events = new \SplObjectStorage(); + } + + public function enter(\Twig_Profiler_Profile $profile) + { + if ($this->stopwatch && $profile->isTemplate()) { + $this->events[$profile] = $this->stopwatch->start($profile->getName(), 'template'); + } + + parent::enter($profile); + } + + public function leave(\Twig_Profiler_Profile $profile) + { + parent::leave($profile); + + if ($this->stopwatch && $profile->isTemplate()) { + $this->events[$profile]->stop(); + unset($this->events[$profile]); + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'native_profiler'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php new file mode 100644 index 0000000..7469183 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Provides integration of the Routing component with Twig. + * + * @author Fabien Potencier + */ +class RoutingExtension extends \Twig_Extension +{ + private $generator; + + public function __construct(UrlGeneratorInterface $generator) + { + $this->generator = $generator; + } + + /** + * Returns a list of functions to add to the existing list. + * + * @return array An array of functions + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('url', array($this, 'getUrl'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), + new \Twig_SimpleFunction('path', array($this, 'getPath'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), + ); + } + + public function getPath($name, $parameters = array(), $relative = false) + { + return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); + } + + public function getUrl($name, $parameters = array(), $schemeRelative = false) + { + return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * Determines at compile time whether the generated URL will be safe and thus + * saving the unneeded automatic escaping for performance reasons. + * + * The URL generation process percent encodes non-alphanumeric characters. So there is no risk + * that malicious/invalid characters are part of the URL. The only character within an URL that + * must be escaped in html is the ampersand ("&") which separates query params. So we cannot mark + * the URL generation as always safe, but only when we are sure there won't be multiple query + * params. This is the case when there are none or only one constant parameter given. + * E.g. we know beforehand this will be safe: + * - path('route') + * - path('route', {'param': 'value'}) + * But the following may not: + * - path('route', var) + * - path('route', {'param': ['val1', 'val2'] }) // a sub-array + * - path('route', {'param1': 'value1', 'param2': 'value2'}) + * If param1 and param2 reference placeholder in the route, it would still be safe. But we don't know. + * + * @param \Twig_Node $argsNode The arguments of the path/url function + * + * @return array An array with the contexts the URL is safe + */ + public function isUrlGenerationSafe(\Twig_Node $argsNode) + { + // support named arguments + $paramsNode = $argsNode->hasNode('parameters') ? $argsNode->getNode('parameters') : ( + $argsNode->hasNode(1) ? $argsNode->getNode(1) : null + ); + + if (null === $paramsNode || $paramsNode instanceof \Twig_Node_Expression_Array && count($paramsNode) <= 2 && + (!$paramsNode->hasNode(1) || $paramsNode->getNode(1) instanceof \Twig_Node_Expression_Constant) + ) { + return array('html'); + } + + return array(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'routing'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php new file mode 100644 index 0000000..fa5fae2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Security\Acl\Voter\FieldVote; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; + +/** + * SecurityExtension exposes security context features. + * + * @author Fabien Potencier + */ +class SecurityExtension extends \Twig_Extension +{ + private $securityChecker; + + public function __construct(AuthorizationCheckerInterface $securityChecker = null) + { + $this->securityChecker = $securityChecker; + } + + public function isGranted($role, $object = null, $field = null) + { + if (null === $this->securityChecker) { + return false; + } + + if (null !== $field) { + $object = new FieldVote($object, $field); + } + + try { + return $this->securityChecker->isGranted($role, $object); + } catch (AuthenticationCredentialsNotFoundException $e) { + return false; + } + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('is_granted', array($this, 'isGranted')), + ); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'security'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php new file mode 100644 index 0000000..52af923 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Bridge\Twig\TokenParser\StopwatchTokenParser; + +/** + * Twig extension for the stopwatch helper. + * + * @author Wouter J + */ +class StopwatchExtension extends \Twig_Extension +{ + private $stopwatch; + + /** + * @var bool + */ + private $enabled; + + public function __construct(Stopwatch $stopwatch = null, $enabled = true) + { + $this->stopwatch = $stopwatch; + $this->enabled = $enabled; + } + + public function getStopwatch() + { + return $this->stopwatch; + } + + public function getTokenParsers() + { + return array( + /* + * {% stopwatch foo %} + * Some stuff which will be recorded on the timeline + * {% endstopwatch %} + */ + new StopwatchTokenParser($this->stopwatch !== null && $this->enabled), + ); + } + + public function getName() + { + return 'stopwatch'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php new file mode 100644 index 0000000..f1f2fbd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Bridge\Twig\TokenParser\TransTokenParser; +use Symfony\Bridge\Twig\TokenParser\TransChoiceTokenParser; +use Symfony\Bridge\Twig\TokenParser\TransDefaultDomainTokenParser; +use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; +use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor; + +/** + * Provides integration of the Translation component with Twig. + * + * @author Fabien Potencier + */ +class TranslationExtension extends \Twig_Extension +{ + private $translator; + private $translationNodeVisitor; + + public function __construct(TranslatorInterface $translator, \Twig_NodeVisitorInterface $translationNodeVisitor = null) + { + if (!$translationNodeVisitor) { + $translationNodeVisitor = new TranslationNodeVisitor(); + } + + $this->translator = $translator; + $this->translationNodeVisitor = $translationNodeVisitor; + } + + public function getTranslator() + { + return $this->translator; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + new \Twig_SimpleFilter('trans', array($this, 'trans')), + new \Twig_SimpleFilter('transchoice', array($this, 'transchoice')), + ); + } + + /** + * Returns the token parser instance to add to the existing list. + * + * @return array An array of Twig_TokenParser instances + */ + public function getTokenParsers() + { + return array( + // {% trans %}Symfony is great!{% endtrans %} + new TransTokenParser(), + + // {% transchoice count %} + // {0} There is no apples|{1} There is one apple|]1,Inf] There is {{ count }} apples + // {% endtranschoice %} + new TransChoiceTokenParser(), + + // {% trans_default_domain "foobar" %} + new TransDefaultDomainTokenParser(), + ); + } + + /** + * {@inheritdoc} + */ + public function getNodeVisitors() + { + return array($this->translationNodeVisitor, new TranslationDefaultDomainNodeVisitor()); + } + + public function getTranslationNodeVisitor() + { + return $this->translationNodeVisitor; + } + + public function trans($message, array $arguments = array(), $domain = null, $locale = null) + { + return $this->translator->trans($message, $arguments, $domain, $locale); + } + + public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null) + { + return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'translator'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/YamlExtension.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/YamlExtension.php new file mode 100644 index 0000000..fc9bf0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Extension/YamlExtension.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Extension; + +use Symfony\Component\Yaml\Dumper as YamlDumper; + +/** + * Provides integration of the Yaml component with Twig. + * + * @author Fabien Potencier + */ +class YamlExtension extends \Twig_Extension +{ + /** + * {@inheritdoc} + */ + public function getFilters() + { + return array( + new \Twig_SimpleFilter('yaml_encode', array($this, 'encode')), + new \Twig_SimpleFilter('yaml_dump', array($this, 'dump')), + ); + } + + public function encode($input, $inline = 0, $dumpObjects = false) + { + static $dumper; + + if (null === $dumper) { + $dumper = new YamlDumper(); + } + + return $dumper->dump($input, $inline, 0, false, $dumpObjects); + } + + public function dump($value, $inline = 0, $dumpObjects = false) + { + if (is_resource($value)) { + return '%Resource%'; + } + + if (is_array($value) || is_object($value)) { + return '%'.gettype($value).'% '.$this->encode($value, $inline, $dumpObjects); + } + + return $this->encode($value, $inline, $dumpObjects); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'yaml'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRenderer.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRenderer.php new file mode 100644 index 0000000..ac139e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRenderer.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Form; + +use Symfony\Component\Form\FormRenderer; + +/** + * @author Bernhard Schussek + */ +class TwigRenderer extends FormRenderer implements TwigRendererInterface +{ + /** + * @var TwigRendererEngineInterface + */ + private $engine; + + public function __construct(TwigRendererEngineInterface $engine, $csrfTokenManager = null) + { + parent::__construct($engine, $csrfTokenManager); + + $this->engine = $engine; + } + + /** + * {@inheritdoc} + */ + public function setEnvironment(\Twig_Environment $environment) + { + $this->engine->setEnvironment($environment); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php new file mode 100644 index 0000000..f9dfea3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Form; + +use Symfony\Component\Form\AbstractRendererEngine; +use Symfony\Component\Form\FormView; + +/** + * @author Bernhard Schussek + */ +class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererEngineInterface +{ + /** + * @var \Twig_Environment + */ + private $environment; + + /** + * @var \Twig_Template + */ + private $template; + + /** + * {@inheritdoc} + */ + public function setEnvironment(\Twig_Environment $environment) + { + $this->environment = $environment; + } + + /** + * {@inheritdoc} + */ + public function renderBlock(FormView $view, $resource, $blockName, array $variables = array()) + { + $cacheKey = $view->vars[self::CACHE_KEY_VAR]; + + $context = $this->environment->mergeGlobals($variables); + + ob_start(); + + // By contract,This method can only be called after getting the resource + // (which is passed to the method). Getting a resource for the first time + // (with an empty cache) is guaranteed to invoke loadResourcesFromTheme(), + // where the property $template is initialized. + + // We do not call renderBlock here to avoid too many nested level calls + // (XDebug limits the level to 100 by default) + $this->template->displayBlock($blockName, $context, $this->resources[$cacheKey]); + + return ob_get_clean(); + } + + /** + * Loads the cache with the resource for a given block name. + * + * This implementation eagerly loads all blocks of the themes assigned to the given view + * and all of its ancestors views. This is necessary, because Twig receives the + * list of blocks later. At that point, all blocks must already be loaded, for the + * case that the function "block()" is used in the Twig template. + * + * @see getResourceForBlock() + * + * @param string $cacheKey The cache key of the form view. + * @param FormView $view The form view for finding the applying themes. + * @param string $blockName The name of the block to load. + * + * @return bool True if the resource could be loaded, false otherwise. + */ + protected function loadResourceForBlockName($cacheKey, FormView $view, $blockName) + { + // The caller guarantees that $this->resources[$cacheKey][$block] is + // not set, but it doesn't have to check whether $this->resources[$cacheKey] + // is set. If $this->resources[$cacheKey] is set, all themes for this + // $cacheKey are already loaded (due to the eager population, see doc comment). + if (isset($this->resources[$cacheKey])) { + // As said in the previous, the caller guarantees that + // $this->resources[$cacheKey][$block] is not set. Since the themes are + // already loaded, it can only be a non-existing block. + $this->resources[$cacheKey][$blockName] = false; + + return false; + } + + // Recursively try to find the block in the themes assigned to $view, + // then of its parent view, then of the parent view of the parent and so on. + // When the root view is reached in this recursion, also the default + // themes are taken into account. + + // Check each theme whether it contains the searched block + if (isset($this->themes[$cacheKey])) { + for ($i = count($this->themes[$cacheKey]) - 1; $i >= 0; --$i) { + $this->loadResourcesFromTheme($cacheKey, $this->themes[$cacheKey][$i]); + // CONTINUE LOADING (see doc comment) + } + } + + // Check the default themes once we reach the root view without success + if (!$view->parent) { + for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) { + $this->loadResourcesFromTheme($cacheKey, $this->defaultThemes[$i]); + // CONTINUE LOADING (see doc comment) + } + } + + // Proceed with the themes of the parent view + if ($view->parent) { + $parentCacheKey = $view->parent->vars[self::CACHE_KEY_VAR]; + + if (!isset($this->resources[$parentCacheKey])) { + $this->loadResourceForBlockName($parentCacheKey, $view->parent, $blockName); + } + + // EAGER CACHE POPULATION (see doc comment) + foreach ($this->resources[$parentCacheKey] as $nestedBlockName => $resource) { + if (!isset($this->resources[$cacheKey][$nestedBlockName])) { + $this->resources[$cacheKey][$nestedBlockName] = $resource; + } + } + } + + // Even though we loaded the themes, it can happen that none of them + // contains the searched block + if (!isset($this->resources[$cacheKey][$blockName])) { + // Cache that we didn't find anything to speed up further accesses + $this->resources[$cacheKey][$blockName] = false; + } + + return false !== $this->resources[$cacheKey][$blockName]; + } + + /** + * Loads the resources for all blocks in a theme. + * + * @param string $cacheKey The cache key for storing the resource. + * @param mixed $theme The theme to load the block from. This parameter + * is passed by reference, because it might be necessary + * to initialize the theme first. Any changes made to + * this variable will be kept and be available upon + * further calls to this method using the same theme. + */ + protected function loadResourcesFromTheme($cacheKey, &$theme) + { + if (!$theme instanceof \Twig_Template) { + /* @var \Twig_Template $theme */ + $theme = $this->environment->loadTemplate($theme); + } + + if (null === $this->template) { + // Store the first \Twig_Template instance that we find so that + // we can call displayBlock() later on. It doesn't matter *which* + // template we use for that, since we pass the used blocks manually + // anyway. + $this->template = $theme; + } + + // Use a separate variable for the inheritance traversal, because + // theme is a reference and we don't want to change it. + $currentTheme = $theme; + + $context = $this->environment->mergeGlobals(array()); + + // The do loop takes care of template inheritance. + // Add blocks from all templates in the inheritance tree, but avoid + // overriding blocks already set. + do { + foreach ($currentTheme->getBlocks() as $block => $blockData) { + if (!isset($this->resources[$cacheKey][$block])) { + // The resource given back is the key to the bucket that + // contains this block. + $this->resources[$cacheKey][$block] = $blockData; + } + } + } while (false !== $currentTheme = $currentTheme->getParent($context)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php new file mode 100644 index 0000000..ef764a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererEngineInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Form; + +use Symfony\Component\Form\FormRendererEngineInterface; + +/** + * @author Bernhard Schussek + */ +interface TwigRendererEngineInterface extends FormRendererEngineInterface +{ + /** + * Sets Twig's environment. + * + * @param \Twig_Environment $environment + */ + public function setEnvironment(\Twig_Environment $environment); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php new file mode 100644 index 0000000..4682f52 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Form/TwigRendererInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Form; + +use Symfony\Component\Form\FormRendererInterface; + +/** + * @author Bernhard Schussek + */ +interface TwigRendererInterface extends FormRendererInterface +{ + /** + * Sets Twig's environment. + * + * @param \Twig_Environment $environment + */ + public function setEnvironment(\Twig_Environment $environment); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/LICENSE b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/DumpNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/DumpNode.php new file mode 100644 index 0000000..522497b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/DumpNode.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * @author Julien Galenski + */ +class DumpNode extends \Twig_Node +{ + private $varPrefix; + + public function __construct($varPrefix, \Twig_Node $values = null, $lineno, $tag = null) + { + parent::__construct(array('values' => $values), array(), $lineno, $tag); + $this->varPrefix = $varPrefix; + } + + /** + * {@inheritdoc} + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler + ->write("if (\$this->env->isDebug()) {\n") + ->indent(); + + $values = $this->getNode('values'); + + if (null === $values) { + // remove embedded templates (macros) from the context + $compiler + ->write(sprintf('$%svars = array();'."\n", $this->varPrefix)) + ->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $this->varPrefix)) + ->indent() + ->write(sprintf('if (!$%sval instanceof \Twig_Template) {'."\n", $this->varPrefix)) + ->indent() + ->write(sprintf('$%1$svars[$%1$skey] = $%1$sval;'."\n", $this->varPrefix)) + ->outdent() + ->write("}\n") + ->outdent() + ->write("}\n") + ->addDebugInfo($this) + ->write(sprintf('\Symfony\Component\VarDumper\VarDumper::dump($%svars);'."\n", $this->varPrefix)); + } elseif (1 === $values->count()) { + $compiler + ->addDebugInfo($this) + ->write('\Symfony\Component\VarDumper\VarDumper::dump(') + ->subcompile($values->getNode(0)) + ->raw(");\n"); + } else { + $compiler + ->addDebugInfo($this) + ->write('\Symfony\Component\VarDumper\VarDumper::dump(array('."\n") + ->indent(); + foreach ($values as $node) { + $compiler->addIndentation(); + if ($node->hasAttribute('name')) { + $compiler + ->string($node->getAttribute('name')) + ->raw(' => '); + } + $compiler + ->subcompile($node) + ->raw(",\n"); + } + $compiler + ->outdent() + ->write("));\n"); + } + + $compiler + ->outdent() + ->write("}\n"); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormThemeNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormThemeNode.php new file mode 100644 index 0000000..e598ae1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/FormThemeNode.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * @author Fabien Potencier + */ +class FormThemeNode extends \Twig_Node +{ + public function __construct(\Twig_Node $form, \Twig_Node $resources, $lineno, $tag = null) + { + parent::__construct(array('form' => $form, 'resources' => $resources), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler $compiler A Twig_Compiler instance + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('$this->env->getExtension(\'form\')->renderer->setTheme(') + ->subcompile($this->getNode('form')) + ->raw(', ') + ->subcompile($this->getNode('resources')) + ->raw(");\n"); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/RenderBlockNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/RenderBlockNode.php new file mode 100644 index 0000000..35dcb40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/RenderBlockNode.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * Compiles a call to {@link \Symfony\Component\Form\FormRendererInterface::renderBlock()}. + * + * The function name is used as block name. For example, if the function name + * is "foo", the block "foo" will be rendered. + * + * @author Bernhard Schussek + */ +class RenderBlockNode extends \Twig_Node_Expression_Function +{ + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + $arguments = iterator_to_array($this->getNode('arguments')); + $compiler->write('$this->env->getExtension(\'form\')->renderer->renderBlock('); + + if (isset($arguments[0])) { + $compiler->subcompile($arguments[0]); + $compiler->raw(', \''.$this->getAttribute('name').'\''); + + if (isset($arguments[1])) { + $compiler->raw(', '); + $compiler->subcompile($arguments[1]); + } + } + + $compiler->raw(')'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php new file mode 100644 index 0000000..93bba43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * @author Bernhard Schussek + */ +class SearchAndRenderBlockNode extends \Twig_Node_Expression_Function +{ + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + $compiler->raw('$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock('); + + preg_match('/_([^_]+)$/', $this->getAttribute('name'), $matches); + + $label = null; + $arguments = iterator_to_array($this->getNode('arguments')); + $blockNameSuffix = $matches[1]; + + if (isset($arguments[0])) { + $compiler->subcompile($arguments[0]); + $compiler->raw(', \''.$blockNameSuffix.'\''); + + if (isset($arguments[1])) { + if ('label' === $blockNameSuffix) { + // The "label" function expects the label in the second and + // the variables in the third argument + $label = $arguments[1]; + $variables = isset($arguments[2]) ? $arguments[2] : null; + $lineno = $label->getLine(); + + if ($label instanceof \Twig_Node_Expression_Constant) { + // If the label argument is given as a constant, we can either + // strip it away if it is empty, or integrate it into the array + // of variables at compile time. + $labelIsExpression = false; + + // Only insert the label into the array if it is not empty + if (!twig_test_empty($label->getAttribute('value'))) { + $originalVariables = $variables; + $variables = new \Twig_Node_Expression_Array(array(), $lineno); + $labelKey = new \Twig_Node_Expression_Constant('label', $lineno); + + if (null !== $originalVariables) { + foreach ($originalVariables->getKeyValuePairs() as $pair) { + // Don't copy the original label attribute over if it exists + if ((string) $labelKey !== (string) $pair['key']) { + $variables->addElement($pair['value'], $pair['key']); + } + } + } + + // Insert the label argument into the array + $variables->addElement($label, $labelKey); + } + } else { + // The label argument is not a constant, but some kind of + // expression. This expression needs to be evaluated at runtime. + // Depending on the result (whether it is null or not), the + // label in the arguments should take precedence over the label + // in the attributes or not. + $labelIsExpression = true; + } + } else { + // All other functions than "label" expect the variables + // in the second argument + $label = null; + $variables = $arguments[1]; + $labelIsExpression = false; + } + + if (null !== $variables || $labelIsExpression) { + $compiler->raw(', '); + + if (null !== $variables) { + $compiler->subcompile($variables); + } + + if ($labelIsExpression) { + if (null !== $variables) { + $compiler->raw(' + '); + } + + // Check at runtime whether the label is empty. + // If not, add it to the array at runtime. + $compiler->raw('(twig_test_empty($_label_ = '); + $compiler->subcompile($label); + $compiler->raw(') ? array() : array("label" => $_label_))'); + } + } + } + } + + $compiler->raw(')'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/StopwatchNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/StopwatchNode.php new file mode 100644 index 0000000..06eeb49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/StopwatchNode.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * Represents a stopwatch node. + * + * @author Wouter J + */ +class StopwatchNode extends \Twig_Node +{ + public function __construct(\Twig_Node $name, $body, \Twig_Node_Expression_AssignName $var, $lineno = 0, $tag = null) + { + parent::__construct(array('body' => $body, 'name' => $name, 'var' => $var), array(), $lineno, $tag); + } + + public function compile(\Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('') + ->subcompile($this->getNode('var')) + ->raw(' = ') + ->subcompile($this->getNode('name')) + ->write(";\n") + ->write("\$this->env->getExtension('stopwatch')->getStopwatch()->start(") + ->subcompile($this->getNode('var')) + ->raw(", 'template');\n") + ->subcompile($this->getNode('body')) + ->write("\$this->env->getExtension('stopwatch')->getStopwatch()->stop(") + ->subcompile($this->getNode('var')) + ->raw(");\n") + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php new file mode 100644 index 0000000..adee71f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * @author Fabien Potencier + */ +class TransDefaultDomainNode extends \Twig_Node +{ + public function __construct(\Twig_Node_Expression $expr, $lineno = 0, $tag = null) + { + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler $compiler A Twig_Compiler instance + */ + public function compile(\Twig_Compiler $compiler) + { + // noop as this node is just a marker for TranslationDefaultDomainNodeVisitor + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransNode.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransNode.php new file mode 100644 index 0000000..af85d8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Node; + +/** + * @author Fabien Potencier + */ +class TransNode extends \Twig_Node +{ + public function __construct(\Twig_Node $body, \Twig_Node $domain = null, \Twig_Node_Expression $count = null, \Twig_Node_Expression $vars = null, \Twig_Node_Expression $locale = null, $lineno = 0, $tag = null) + { + parent::__construct(array('count' => $count, 'body' => $body, 'domain' => $domain, 'vars' => $vars, 'locale' => $locale), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler $compiler A Twig_Compiler instance + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + $vars = $this->getNode('vars'); + $defaults = new \Twig_Node_Expression_Array(array(), -1); + if ($vars instanceof \Twig_Node_Expression_Array) { + $defaults = $this->getNode('vars'); + $vars = null; + } + list($msg, $defaults) = $this->compileString($this->getNode('body'), $defaults, (bool) $vars); + + $method = null === $this->getNode('count') ? 'trans' : 'transChoice'; + + $compiler + ->write('echo $this->env->getExtension(\'translator\')->getTranslator()->'.$method.'(') + ->subcompile($msg) + ; + + $compiler->raw(', '); + + if (null !== $this->getNode('count')) { + $compiler + ->subcompile($this->getNode('count')) + ->raw(', ') + ; + } + + if (null !== $vars) { + $compiler + ->raw('array_merge(') + ->subcompile($defaults) + ->raw(', ') + ->subcompile($this->getNode('vars')) + ->raw(')') + ; + } else { + $compiler->subcompile($defaults); + } + + $compiler->raw(', '); + + if (null === $this->getNode('domain')) { + $compiler->repr('messages'); + } else { + $compiler->subcompile($this->getNode('domain')); + } + + if (null !== $this->getNode('locale')) { + $compiler + ->raw(', ') + ->subcompile($this->getNode('locale')) + ; + } + $compiler->raw(");\n"); + } + + protected function compileString(\Twig_Node $body, \Twig_Node_Expression_Array $vars, $ignoreStrictCheck = false) + { + if ($body instanceof \Twig_Node_Expression_Constant) { + $msg = $body->getAttribute('value'); + } elseif ($body instanceof \Twig_Node_Text) { + $msg = $body->getAttribute('data'); + } else { + return array($body, $vars); + } + + preg_match_all('/(?getLine()); + if (!$vars->hasElement($key)) { + if ('count' === $var && null !== $this->getNode('count')) { + $vars->addElement($this->getNode('count'), $key); + } else { + $varExpr = new \Twig_Node_Expression_Name($var, $body->getLine()); + $varExpr->setAttribute('ignore_strict_check', $ignoreStrictCheck); + $vars->addElement($varExpr, $key); + } + } + } + + return array(new \Twig_Node_Expression_Constant(str_replace('%%', '%', trim($msg)), $body->getLine()), $vars); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php new file mode 100644 index 0000000..1712bd5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\NodeVisitor; + +/** + * @author Jean-François Simon + */ +class Scope +{ + /** + * @var Scope|null + */ + private $parent; + + /** + * @var array + */ + private $data = array(); + + /** + * @var bool + */ + private $left = false; + + /** + * @param Scope $parent + */ + public function __construct(Scope $parent = null) + { + $this->parent = $parent; + } + + /** + * Opens a new child scope. + * + * @return Scope + */ + public function enter() + { + return new self($this); + } + + /** + * Closes current scope and returns parent one. + * + * @return Scope|null + */ + public function leave() + { + $this->left = true; + + return $this->parent; + } + + /** + * Stores data into current scope. + * + * @param string $key + * @param mixed $value + * + * @throws \LogicException + * + * @return Scope Current scope + */ + public function set($key, $value) + { + if ($this->left) { + throw new \LogicException('Left scope is not mutable.'); + } + + $this->data[$key] = $value; + + return $this; + } + + /** + * Tests if a data is visible from current scope. + * + * @param string $key + * + * @return bool + */ + public function has($key) + { + if (array_key_exists($key, $this->data)) { + return true; + } + + if (null === $this->parent) { + return false; + } + + return $this->parent->has($key); + } + + /** + * Returns data visible from current scope. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function get($key, $default = null) + { + if (array_key_exists($key, $this->data)) { + return $this->data[$key]; + } + + if (null === $this->parent) { + return $default; + } + + return $this->parent->get($key, $default); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php new file mode 100644 index 0000000..c923df9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\NodeVisitor; + +use Symfony\Bridge\Twig\Node\TransNode; +use Symfony\Bridge\Twig\Node\TransDefaultDomainNode; + +/** + * TranslationDefaultDomainNodeVisitor. + * + * @author Fabien Potencier + */ +class TranslationDefaultDomainNodeVisitor extends \Twig_BaseNodeVisitor +{ + /** + * @var Scope + */ + private $scope; + + /** + * Constructor. + */ + public function __construct() + { + $this->scope = new Scope(); + } + + /** + * {@inheritdoc} + */ + protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) + { + if ($node instanceof \Twig_Node_Block || $node instanceof \Twig_Node_Module) { + $this->scope = $this->scope->enter(); + } + + if ($node instanceof TransDefaultDomainNode) { + if ($node->getNode('expr') instanceof \Twig_Node_Expression_Constant) { + $this->scope->set('domain', $node->getNode('expr')); + + return $node; + } else { + $var = $env->getParser()->getVarName(); + $name = new \Twig_Node_Expression_AssignName($var, $node->getLine()); + $this->scope->set('domain', new \Twig_Node_Expression_Name($var, $node->getLine())); + + return new \Twig_Node_Set(false, new \Twig_Node(array($name)), new \Twig_Node(array($node->getNode('expr'))), $node->getLine()); + } + } + + if (!$this->scope->has('domain')) { + return $node; + } + + if ($node instanceof \Twig_Node_Expression_Filter && in_array($node->getNode('filter')->getAttribute('value'), array('trans', 'transchoice'))) { + $arguments = $node->getNode('arguments'); + $ind = 'trans' === $node->getNode('filter')->getAttribute('value') ? 1 : 2; + if ($this->isNamedArguments($arguments)) { + if (!$arguments->hasNode('domain') && !$arguments->hasNode($ind)) { + $arguments->setNode('domain', $this->scope->get('domain')); + } + } else { + if (!$arguments->hasNode($ind)) { + if (!$arguments->hasNode($ind - 1)) { + $arguments->setNode($ind - 1, new \Twig_Node_Expression_Array(array(), $node->getLine())); + } + + $arguments->setNode($ind, $this->scope->get('domain')); + } + } + } elseif ($node instanceof TransNode) { + if (null === $node->getNode('domain')) { + $node->setNode('domain', $this->scope->get('domain')); + } + } + + return $node; + } + + /** + * {@inheritdoc} + */ + protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) + { + if ($node instanceof TransDefaultDomainNode) { + return false; + } + + if ($node instanceof \Twig_Node_Block || $node instanceof \Twig_Node_Module) { + $this->scope = $this->scope->leave(); + } + + return $node; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return -10; + } + + /** + * @return bool + */ + private function isNamedArguments($arguments) + { + foreach ($arguments as $name => $node) { + if (!is_int($name)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php new file mode 100644 index 0000000..4426a9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\NodeVisitor; + +use Symfony\Bridge\Twig\Node\TransNode; + +/** + * TranslationNodeVisitor extracts translation messages. + * + * @author Fabien Potencier + */ +class TranslationNodeVisitor extends \Twig_BaseNodeVisitor +{ + const UNDEFINED_DOMAIN = '_undefined'; + + private $enabled = false; + private $messages = array(); + + public function enable() + { + $this->enabled = true; + $this->messages = array(); + } + + public function disable() + { + $this->enabled = false; + $this->messages = array(); + } + + public function getMessages() + { + return $this->messages; + } + + /** + * {@inheritdoc} + */ + protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) + { + if (!$this->enabled) { + return $node; + } + + if ( + $node instanceof \Twig_Node_Expression_Filter && + 'trans' === $node->getNode('filter')->getAttribute('value') && + $node->getNode('node') instanceof \Twig_Node_Expression_Constant + ) { + // extract constant nodes with a trans filter + $this->messages[] = array( + $node->getNode('node')->getAttribute('value'), + $this->getReadDomainFromArguments($node->getNode('arguments'), 1), + ); + } elseif ( + $node instanceof \Twig_Node_Expression_Filter && + 'transchoice' === $node->getNode('filter')->getAttribute('value') && + $node->getNode('node') instanceof \Twig_Node_Expression_Constant + ) { + // extract constant nodes with a trans filter + $this->messages[] = array( + $node->getNode('node')->getAttribute('value'), + $this->getReadDomainFromArguments($node->getNode('arguments'), 2), + ); + } elseif ($node instanceof TransNode) { + // extract trans nodes + $this->messages[] = array( + $node->getNode('body')->getAttribute('data'), + $this->getReadDomainFromNode($node->getNode('domain')), + ); + } + + return $node; + } + + /** + * {@inheritdoc} + */ + protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) + { + return $node; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 0; + } + + /** + * @param \Twig_Node $arguments + * @param int $index + * + * @return string|null + */ + private function getReadDomainFromArguments(\Twig_Node $arguments, $index) + { + if ($arguments->hasNode('domain')) { + $argument = $arguments->getNode('domain'); + } elseif ($arguments->hasNode($index)) { + $argument = $arguments->getNode($index); + } else { + return; + } + + return $this->getReadDomainFromNode($argument); + } + + /** + * @param \Twig_Node $node + * + * @return string|null + */ + private function getReadDomainFromNode(\Twig_Node $node = null) + { + if (null === $node) { + return; + } + + if ($node instanceof \Twig_Node_Expression_Constant) { + return $node->getAttribute('value'); + } + + return self::UNDEFINED_DOMAIN; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/README.md b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/README.md new file mode 100644 index 0000000..1f92af9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/README.md @@ -0,0 +1,15 @@ +Twig Bridge +=========== + +Provides integration for [Twig](http://twig.sensiolabs.org/) with various +Symfony components. + +Resources +--------- + +If you want to run the unit tests, install dev dependencies before +running PHPUnit: + + $ cd path/to/Symfony/Bridge/Twig/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig new file mode 100644 index 0000000..e997615 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig @@ -0,0 +1,70 @@ +{% use "bootstrap_3_layout.html.twig" %} + +{% block form_start -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-horizontal')|trim}) %} + {{- parent() -}} +{%- endblock form_start %} + +{# Labels #} + +{% block form_label -%} +{% spaceless %} + {% if label is same as(false) %} +
    + {% else %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ block('form_label_class'))|trim}) %} + {{- parent() -}} + {% endif %} +{% endspaceless %} +{%- endblock form_label %} + +{% block form_label_class -%} +col-sm-2 +{%- endblock form_label_class %} + +{# Rows #} + +{% block form_row -%} +
    + {{- form_label(form) -}} +
    + {{- form_widget(form) -}} + {{- form_errors(form) -}} +
    +{##}
    +{%- endblock form_row %} + +{% block checkbox_row -%} + {{- block('checkbox_radio_row') -}} +{%- endblock checkbox_row %} + +{% block radio_row -%} + {{- block('checkbox_radio_row') -}} +{%- endblock radio_row %} + +{% block checkbox_radio_row -%} +{% spaceless %} +
    +
    +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +
    +{% endspaceless %} +{%- endblock checkbox_radio_row %} + +{% block submit_row -%} +{% spaceless %} +
    +
    +
    + {{ form_widget(form) }} +
    +
    +{% endspaceless %} +{% endblock submit_row %} + +{% block form_group_class -%} +col-sm-10 +{%- endblock form_group_class %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig new file mode 100644 index 0000000..165236e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -0,0 +1,240 @@ +{% use "form_div_layout.html.twig" %} + +{# Widgets #} + +{% block form_widget_simple -%} + {% if type is not defined or 'file' != type %} + {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) -%} + {% endif %} + {{- parent() -}} +{%- endblock form_widget_simple %} + +{% block textarea_widget -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %} + {{- parent() -}} +{%- endblock textarea_widget %} + +{% block button_widget -%} + {% set attr = attr|merge({class: (attr.class|default('btn-default') ~ ' btn')|trim}) %} + {{- parent() -}} +{%- endblock %} + +{% block money_widget -%} +
    + {% set prepend = '{{' == money_pattern[0:2] %} + {% if not prepend %} + {{ money_pattern|replace({ '{{ widget }}':''}) }} + {% endif %} + {{- block('form_widget_simple') -}} + {% if prepend %} + {{ money_pattern|replace({ '{{ widget }}':''}) }} + {% endif %} +
    +{%- endblock money_widget %} + +{% block percent_widget -%} +
    + {{- block('form_widget_simple') -}} + % +
    +{%- endblock percent_widget %} + +{% block datetime_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%} +
    + {{- form_errors(form.date) -}} + {{- form_errors(form.time) -}} + {{- form_widget(form.date, { datetime: true } ) -}} + {{- form_widget(form.time, { datetime: true } ) -}} +
    + {%- endif %} +{%- endblock datetime_widget %} + +{% block date_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%} + {% if datetime is not defined or not datetime -%} +
    + {%- endif %} + {{- date_pattern|replace({ + '{{ year }}': form_widget(form.year), + '{{ month }}': form_widget(form.month), + '{{ day }}': form_widget(form.day), + })|raw -}} + {% if datetime is not defined or not datetime -%} +
    + {%- endif -%} + {% endif %} +{%- endblock date_widget %} + +{% block time_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-inline')|trim}) -%} + {% if datetime is not defined or false == datetime -%} +
    + {%- endif -%} + {{- form_widget(form.hour) }}{% if with_minutes %}:{{ form_widget(form.minute) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second) }}{% endif %} + {% if datetime is not defined or false == datetime -%} +
    + {%- endif -%} + {% endif %} +{%- endblock time_widget %} + +{% block choice_widget_collapsed -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %} + {{- parent() -}} +{%- endblock %} + +{% block choice_widget_expanded -%} + {% if '-inline' in label_attr.class|default('') -%} +
    + {%- for child in form %} + {{- form_widget(child, { + parent_label_class: label_attr.class|default(''), + translation_domain: choice_translation_domain, + }) -}} + {% endfor -%} +
    + {%- else -%} +
    + {%- for child in form %} + {{- form_widget(child, { + parent_label_class: label_attr.class|default(''), + translation_domain: choice_translation_domain, + }) -}} + {% endfor -%} +
    + {%- endif %} +{%- endblock choice_widget_expanded %} + +{% block checkbox_widget -%} + {% set parent_label_class = parent_label_class|default('') -%} + {% if 'checkbox-inline' in parent_label_class %} + {{- form_label(form, null, { widget: parent() }) -}} + {% else -%} +
    + {{- form_label(form, null, { widget: parent() }) -}} +
    + {%- endif %} +{%- endblock checkbox_widget %} + +{% block radio_widget -%} + {%- set parent_label_class = parent_label_class|default('') -%} + {% if 'radio-inline' in parent_label_class %} + {{- form_label(form, null, { widget: parent() }) -}} + {% else -%} +
    + {{- form_label(form, null, { widget: parent() }) -}} +
    + {%- endif %} +{%- endblock radio_widget %} + +{# Labels #} + +{% block form_label -%} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' control-label')|trim}) -%} + {{- parent() -}} +{%- endblock form_label %} + +{% block choice_label -%} + {# remove the checkbox-inline and radio-inline class, it's only useful for embed labels #} + {%- set label_attr = label_attr|merge({class: label_attr.class|default('')|replace({'checkbox-inline': '', 'radio-inline': ''})|trim}) -%} + {{- block('form_label') -}} +{% endblock %} + +{% block checkbox_label -%} + {{- block('checkbox_radio_label') -}} +{%- endblock checkbox_label %} + +{% block radio_label -%} + {{- block('checkbox_radio_label') -}} +{%- endblock radio_label %} + +{% block checkbox_radio_label %} + {# Do not display the label if widget is not defined in order to prevent double label rendering #} + {% if widget is defined %} + {% if required %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %} + {% endif %} + {% if parent_label_class is defined %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %} + {% endif %} + {% if label is not same as(false) and label is empty %} + {% set label = name|humanize %} + {% endif %} + + {{- widget|raw }} {{ label is not same as(false) ? (translation_domain is same as(false) ? label : label|trans({}, translation_domain)) -}} + + {% endif %} +{% endblock checkbox_radio_label %} + +{# Rows #} + +{% block form_row -%} +
    + {{- form_label(form) -}} + {{- form_widget(form) -}} + {{- form_errors(form) -}} +
    +{%- endblock form_row %} + +{% block button_row -%} +
    + {{- form_widget(form) -}} +
    +{%- endblock button_row %} + +{% block choice_row -%} + {% set force_error = true %} + {{- block('form_row') }} +{%- endblock choice_row %} + +{% block date_row -%} + {% set force_error = true %} + {{- block('form_row') }} +{%- endblock date_row %} + +{% block time_row -%} + {% set force_error = true %} + {{- block('form_row') }} +{%- endblock time_row %} + +{% block datetime_row -%} + {% set force_error = true %} + {{- block('form_row') }} +{%- endblock datetime_row %} + +{% block checkbox_row -%} +
    + {{- form_widget(form) -}} + {{- form_errors(form) -}} +
    +{%- endblock checkbox_row %} + +{% block radio_row -%} +
    + {{- form_widget(form) -}} + {{- form_errors(form) -}} +
    +{%- endblock radio_row %} + +{# Errors #} + +{% block form_errors -%} + {% if errors|length > 0 -%} + {% if form.parent %}{% else %}
    {% endif %} +
      + {%- for error in errors -%} +
    • {{ error.message }}
    • + {%- endfor -%} +
    + {% if form.parent %}{% else %}
    {% endif %} + {%- endif %} +{%- endblock form_errors %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig new file mode 100644 index 0000000..20c6e02 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -0,0 +1,371 @@ +{# Widgets #} + +{%- block form_widget -%} + {% if compound %} + {{- block('form_widget_compound') -}} + {% else %} + {{- block('form_widget_simple') -}} + {% endif %} +{%- endblock form_widget -%} + +{%- block form_widget_simple -%} + {%- set type = type|default('text') -%} + +{%- endblock form_widget_simple -%} + +{%- block form_widget_compound -%} +
    + {%- if form.parent is empty -%} + {{ form_errors(form) }} + {%- endif -%} + {{- block('form_rows') -}} + {{- form_rest(form) -}} +
    +{%- endblock form_widget_compound -%} + +{%- block collection_widget -%} + {% if prototype is defined %} + {%- set attr = attr|merge({'data-prototype': form_row(prototype) }) -%} + {% endif %} + {{- block('form_widget') -}} +{%- endblock collection_widget -%} + +{%- block textarea_widget -%} + +{%- endblock textarea_widget -%} + +{%- block choice_widget -%} + {% if expanded %} + {{- block('choice_widget_expanded') -}} + {% else %} + {{- block('choice_widget_collapsed') -}} + {% endif %} +{%- endblock choice_widget -%} + +{%- block choice_widget_expanded -%} +
    + {%- for child in form %} + {{- form_widget(child) -}} + {{- form_label(child, null, {translation_domain: choice_translation_domain}) -}} + {% endfor -%} +
    +{%- endblock choice_widget_expanded -%} + +{%- block choice_widget_collapsed -%} + {%- if required and placeholder is none and not placeholder_in_choices and not multiple -%} + {% set required = false %} + {%- endif -%} + +{%- endblock choice_widget_collapsed -%} + +{%- block choice_widget_options -%} + {% for group_label, choice in options %} + {%- if choice is iterable -%} + + {% set options = choice %} + {{- block('choice_widget_options') -}} + + {%- else -%} + {% set attr = choice.attr %} + + {%- endif -%} + {% endfor %} +{%- endblock choice_widget_options -%} + +{%- block checkbox_widget -%} + +{%- endblock checkbox_widget -%} + +{%- block radio_widget -%} + +{%- endblock radio_widget -%} + +{%- block datetime_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {%- else -%} +
    + {{- form_errors(form.date) -}} + {{- form_errors(form.time) -}} + {{- form_widget(form.date) -}} + {{- form_widget(form.time) -}} +
    + {%- endif -%} +{%- endblock datetime_widget -%} + +{%- block date_widget -%} + {%- if widget == 'single_text' -%} + {{ block('form_widget_simple') }} + {%- else -%} +
    + {{- date_pattern|replace({ + '{{ year }}': form_widget(form.year), + '{{ month }}': form_widget(form.month), + '{{ day }}': form_widget(form.day), + })|raw -}} +
    + {%- endif -%} +{%- endblock date_widget -%} + +{%- block time_widget -%} + {%- if widget == 'single_text' -%} + {{ block('form_widget_simple') }} + {%- else -%} + {%- set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} -%} +
    + {{ form_widget(form.hour, vars) }}{% if with_minutes %}:{{ form_widget(form.minute, vars) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second, vars) }}{% endif %} +
    + {%- endif -%} +{%- endblock time_widget -%} + +{%- block number_widget -%} + {# type="number" doesn't work with floats #} + {%- set type = type|default('text') -%} + {{ block('form_widget_simple') }} +{%- endblock number_widget -%} + +{%- block integer_widget -%} + {%- set type = type|default('number') -%} + {{ block('form_widget_simple') }} +{%- endblock integer_widget -%} + +{%- block money_widget -%} + {{ money_pattern|replace({ '{{ widget }}': block('form_widget_simple') })|raw }} +{%- endblock money_widget -%} + +{%- block url_widget -%} + {%- set type = type|default('url') -%} + {{ block('form_widget_simple') }} +{%- endblock url_widget -%} + +{%- block search_widget -%} + {%- set type = type|default('search') -%} + {{ block('form_widget_simple') }} +{%- endblock search_widget -%} + +{%- block percent_widget -%} + {%- set type = type|default('text') -%} + {{ block('form_widget_simple') }} % +{%- endblock percent_widget -%} + +{%- block password_widget -%} + {%- set type = type|default('password') -%} + {{ block('form_widget_simple') }} +{%- endblock password_widget -%} + +{%- block hidden_widget -%} + {%- set type = type|default('hidden') -%} + {{ block('form_widget_simple') }} +{%- endblock hidden_widget -%} + +{%- block email_widget -%} + {%- set type = type|default('email') -%} + {{ block('form_widget_simple') }} +{%- endblock email_widget -%} + +{%- block range_widget -%} + {% set type = type|default('range') %} + {{- block('form_widget_simple') -}} +{%- endblock range_widget %} + +{%- block button_widget -%} + {%- if label is empty -%} + {%- if label_format is not empty -%} + {% set label = label_format|replace({ + '%name%': name, + '%id%': id, + }) %} + {%- else -%} + {% set label = name|humanize %} + {%- endif -%} + {%- endif -%} + +{%- endblock button_widget -%} + +{%- block submit_widget -%} + {%- set type = type|default('submit') -%} + {{ block('button_widget') }} +{%- endblock submit_widget -%} + +{%- block reset_widget -%} + {%- set type = type|default('reset') -%} + {{ block('button_widget') }} +{%- endblock reset_widget -%} + +{# Labels #} + +{%- block form_label -%} + {% if label is not same as(false) -%} + {% if not compound -%} + {% set label_attr = label_attr|merge({'for': id}) %} + {%- endif -%} + {% if required -%} + {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %} + {%- endif -%} + {% if label is empty -%} + {%- if label_format is not empty -%} + {% set label = label_format|replace({ + '%name%': name, + '%id%': id, + }) %} + {%- else -%} + {% set label = name|humanize %} + {%- endif -%} + {%- endif -%} + {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }} + {%- endif -%} +{%- endblock form_label -%} + +{%- block button_label -%}{%- endblock -%} + +{# Rows #} + +{%- block repeated_row -%} + {# + No need to render the errors here, as all errors are mapped + to the first child (see RepeatedTypeValidatorExtension). + #} + {{- block('form_rows') -}} +{%- endblock repeated_row -%} + +{%- block form_row -%} +
    + {{- form_label(form) -}} + {{- form_errors(form) -}} + {{- form_widget(form) -}} +
    +{%- endblock form_row -%} + +{%- block button_row -%} +
    + {{- form_widget(form) -}} +
    +{%- endblock button_row -%} + +{%- block hidden_row -%} + {{ form_widget(form) }} +{%- endblock hidden_row -%} + +{# Misc #} + +{%- block form -%} + {{ form_start(form) }} + {{- form_widget(form) -}} + {{ form_end(form) }} +{%- endblock form -%} + +{%- block form_start -%} + {% set method = method|upper %} + {%- if method in ["GET", "POST"] -%} + {% set form_method = method %} + {%- else -%} + {% set form_method = "POST" %} + {%- endif -%} +
    + {%- if form_method != method -%} + + {%- endif -%} +{%- endblock form_start -%} + +{%- block form_end -%} + {%- if not render_rest is defined or render_rest -%} + {{ form_rest(form) }} + {%- endif -%} +
    +{%- endblock form_end -%} + +{%- block form_errors -%} + {%- if errors|length > 0 -%} +
      + {%- for error in errors -%} +
    • {{ error.message }}
    • + {%- endfor -%} +
    + {%- endif -%} +{%- endblock form_errors -%} + +{%- block form_rest -%} + {% for child in form -%} + {% if not child.rendered %} + {{- form_row(child) -}} + {% endif %} + {%- endfor %} +{% endblock form_rest %} + +{# Support #} + +{%- block form_rows -%} + {% for child in form %} + {{- form_row(child) -}} + {% endfor %} +{%- endblock form_rows -%} + +{%- block widget_attributes -%} + id="{{ id }}" name="{{ full_name }}" + {%- if disabled %} disabled="disabled"{% endif -%} + {%- if required %} required="required"{% endif -%} + {%- for attrname, attrvalue in attr -%} + {{- " " -}} + {%- if attrname in ['placeholder', 'title'] -%} + {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" + {%- elseif attrvalue is same as(true) -%} + {{- attrname }}="{{ attrname }}" + {%- elseif attrvalue is not same as(false) -%} + {{- attrname }}="{{ attrvalue }}" + {%- endif -%} + {%- endfor -%} +{%- endblock widget_attributes -%} + +{%- block widget_container_attributes -%} + {%- if id is not empty %}id="{{ id }}"{% endif -%} + {%- for attrname, attrvalue in attr -%} + {{- " " -}} + {%- if attrname in ['placeholder', 'title'] -%} + {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" + {%- elseif attrvalue is same as(true) -%} + {{- attrname }}="{{ attrname }}" + {%- elseif attrvalue is not same as(false) -%} + {{- attrname }}="{{ attrvalue }}" + {%- endif -%} + {%- endfor -%} +{%- endblock widget_container_attributes -%} + +{%- block button_attributes -%} + id="{{ id }}" name="{{ full_name }}"{% if disabled %} disabled="disabled"{% endif -%} + {%- for attrname, attrvalue in attr -%} + {{- " " -}} + {%- if attrname in ['placeholder', 'title'] -%} + {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" + {%- elseif attrvalue is same as(true) -%} + {{- attrname }}="{{ attrname }}" + {%- elseif attrvalue is not same as(false) -%} + {{- attrname }}="{{ attrvalue }}" + {%- endif -%} + {%- endfor -%} +{%- endblock button_attributes -%} + +{% block attributes -%} + {%- for attrname, attrvalue in attr -%} + {{- " " -}} + {%- if attrname in ['placeholder', 'title'] -%} + {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}" + {%- elseif attrvalue is same as(true) -%} + {{- attrname }}="{{ attrname }}" + {%- elseif attrvalue is not same as(false) -%} + {{- attrname }}="{{ attrvalue }}" + {%- endif -%} + {%- endfor -%} +{%- endblock attributes -%} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig new file mode 100644 index 0000000..c7b3a43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -0,0 +1,44 @@ +{% use "form_div_layout.html.twig" %} + +{%- block form_row -%} + + + {{- form_label(form) -}} + + + {{- form_errors(form) -}} + {{- form_widget(form) -}} + + +{%- endblock form_row -%} + +{%- block button_row -%} + + + + {{- form_widget(form) -}} + + +{%- endblock button_row -%} + +{%- block hidden_row -%} + + + {{- form_widget(form) -}} + + +{%- endblock hidden_row -%} + +{%- block form_widget_compound -%} + + {%- if form.parent is empty and errors|length > 0 -%} + + + + {%- endif -%} + {{- block('form_rows') -}} + {{- form_rest(form) -}} +
    + {{- form_errors(form) -}} +
    +{%- endblock form_widget_compound -%} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig new file mode 100644 index 0000000..77e37da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig @@ -0,0 +1,321 @@ +{% extends "form_div_layout.html.twig" %} + +{# Based on Foundation 5 Doc #} +{# Widgets #} + +{% block form_widget_simple -%} + {% if errors|length > 0 -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + {{- parent() -}} +{%- endblock form_widget_simple %} + +{% block textarea_widget -%} + {% if errors|length > 0 -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + {{- parent() -}} +{%- endblock textarea_widget %} + +{% block button_widget -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' button')|trim}) %} + {{- parent() -}} +{%- endblock %} + +{% block money_widget -%} +
    + {% set prepend = '{{' == money_pattern[0:2] %} + {% if not prepend %} +
    + {{ money_pattern|replace({ '{{ widget }}':''}) }} +
    + {% endif %} +
    + {{- block('form_widget_simple') -}} +
    + {% if prepend %} +
    + {{ money_pattern|replace({ '{{ widget }}':''}) }} +
    + {% endif %} +
    +{%- endblock money_widget %} + +{% block percent_widget -%} +
    +
    + {{- block('form_widget_simple') -}} +
    +
    + % +
    +
    +{%- endblock percent_widget %} + +{% block datetime_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' row')|trim}) %} +
    +
    {{ form_errors(form.date) }}
    +
    {{ form_errors(form.time) }}
    +
    +
    +
    {{ form_widget(form.date, { datetime: true } ) }}
    +
    {{ form_widget(form.time, { datetime: true } ) }}
    +
    + {% endif %} +{%- endblock datetime_widget %} + +{% block date_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' row')|trim}) %} + {% if datetime is not defined or not datetime %} +
    + {% endif %} + {{- date_pattern|replace({ + '{{ year }}': '
    ' ~ form_widget(form.year) ~ '
    ', + '{{ month }}': '
    ' ~ form_widget(form.month) ~ '
    ', + '{{ day }}': '
    ' ~ form_widget(form.day) ~ '
    ', + })|raw -}} + {% if datetime is not defined or not datetime %} +
    + {% endif %} + {% endif %} +{%- endblock date_widget %} + +{% block time_widget -%} + {% if widget == 'single_text' %} + {{- block('form_widget_simple') -}} + {% else %} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' row')|trim}) %} + {% if datetime is not defined or false == datetime %} +
    + {% endif %} + {% if with_seconds %} +
    {{ form_widget(form.hour) }}
    +
    +
    +
    + : +
    +
    + {{ form_widget(form.minute) }} +
    +
    +
    +
    +
    +
    + : +
    +
    + {{ form_widget(form.second) }} +
    +
    +
    + {% else %} +
    {{ form_widget(form.hour) }}
    +
    +
    +
    + : +
    +
    + {{ form_widget(form.minute) }} +
    +
    +
    + {% endif %} + {% if datetime is not defined or false == datetime %} +
    + {% endif %} + {% endif %} +{%- endblock time_widget %} + +{% block choice_widget_collapsed -%} + {% if errors|length > 0 -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + + {% if multiple -%} + {% set attr = attr|merge({style: (attr.style|default('') ~ ' height: auto; background-image: none;')|trim}) %} + {% endif %} + + {% if required and placeholder is none and not placeholder_in_choices and not multiple -%} + {% set required = false %} + {%- endif -%} + +{%- endblock choice_widget_collapsed %} + +{% block choice_widget_expanded -%} + {% if '-inline' in label_attr.class|default('') %} +
      + {% for child in form %} +
    • {{ form_widget(child, { + parent_label_class: label_attr.class|default(''), + }) }}
    • + {% endfor %} +
    + {% else %} +
    + {% for child in form %} + {{ form_widget(child, { + parent_label_class: label_attr.class|default(''), + }) }} + {% endfor %} +
    + {% endif %} +{%- endblock choice_widget_expanded %} + +{% block checkbox_widget -%} + {% set parent_label_class = parent_label_class|default('') %} + {% if errors|length > 0 -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + {% if 'checkbox-inline' in parent_label_class %} + {{ form_label(form, null, { widget: parent() }) }} + {% else %} +
    + {{ form_label(form, null, { widget: parent() }) }} +
    + {% endif %} +{%- endblock checkbox_widget %} + +{% block radio_widget -%} + {% set parent_label_class = parent_label_class|default('') %} + {% if 'radio-inline' in parent_label_class %} + {{ form_label(form, null, { widget: parent() }) }} + {% else %} + {% if errors|length > 0 -%} + {% set attr = attr|merge({class: (attr.class|default('') ~ ' error')|trim}) %} + {% endif %} +
    + {{ form_label(form, null, { widget: parent() }) }} +
    + {% endif %} +{%- endblock radio_widget %} + +{# Labels #} + +{% block form_label -%} + {% if errors|length > 0 -%} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + {{- parent() -}} +{%- endblock form_label %} + +{% block choice_label -%} + {% if errors|length > 0 -%} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + {# remove the checkbox-inline and radio-inline class, it's only useful for embed labels #} + {% set label_attr = label_attr|merge({class: label_attr.class|default('')|replace({'checkbox-inline': '', 'radio-inline': ''})|trim}) %} + {{- block('form_label') -}} +{%- endblock %} + +{% block checkbox_label -%} + {{- block('checkbox_radio_label') -}} +{%- endblock checkbox_label %} + +{% block radio_label -%} + {{- block('checkbox_radio_label') -}} +{%- endblock radio_label %} + +{% block checkbox_radio_label -%} + {% if required %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) %} + {% endif %} + {% if errors|length > 0 -%} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' error')|trim}) %} + {% endif %} + {% if parent_label_class is defined %} + {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ parent_label_class)|trim}) %} + {% endif %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + + {{ widget|raw }} + {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }} + +{%- endblock checkbox_radio_label %} + +{# Rows #} + +{% block form_row -%} +
    +
    + {{ form_label(form) }} + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +
    +{%- endblock form_row %} + +{% block choice_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock choice_row %} + +{% block date_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock date_row %} + +{% block time_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock time_row %} + +{% block datetime_row -%} + {% set force_error = true %} + {{ block('form_row') }} +{%- endblock datetime_row %} + +{% block checkbox_row -%} +
    +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +
    +{%- endblock checkbox_row %} + +{% block radio_row -%} +
    +
    + {{ form_widget(form) }} + {{ form_errors(form) }} +
    +
    +{%- endblock radio_row %} + +{# Errors #} + +{% block form_errors -%} + {% if errors|length > 0 -%} + {% if form.parent %}{% else %}
    {% endif %} + {%- for error in errors -%} + {{ error.message }} + {% if not loop.last %}, {% endif %} + {%- endfor -%} + {% if form.parent %}{% else %}
    {% endif %} + {%- endif %} +{%- endblock form_errors %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php new file mode 100644 index 0000000..be15eaf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Command; + +use Symfony\Bridge\Twig\Command\LintCommand; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; + +class LintCommandTest extends \PHPUnit_Framework_TestCase +{ + private $files; + + public function testLintCorrectFile() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile('{{ foo }}'); + + $ret = $tester->execute(array('filename' => array($filename)), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertRegExp('/^\/\/ OK in /', trim($tester->getDisplay())); + } + + public function testLintIncorrectFile() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile('{{ foo'); + + $ret = $tester->execute(array('filename' => array($filename)), array('decorated' => false)); + + $this->assertEquals(1, $ret, 'Returns 1 in case of error'); + $this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay())); + } + + /** + * @expectedException \RuntimeException + */ + public function testLintFileNotReadable() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile(''); + unlink($filename); + + $ret = $tester->execute(array('filename' => array($filename)), array('decorated' => false)); + } + + public function testLintFileCompileTimeException() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile("{{ 2|number_format(2, decimal_point='.', ',') }}"); + + $ret = $tester->execute(array('filename' => array($filename)), array('decorated' => false)); + + $this->assertEquals(1, $ret, 'Returns 1 in case of error'); + $this->assertRegExp('/ERROR in \S+ \(line /', trim($tester->getDisplay())); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $twig = new \Twig_Environment(new \Twig_Loader_Filesystem()); + + $command = new LintCommand(); + $command->setTwigEnvironment($twig); + + $application = new Application(); + $application->add($command); + $command = $application->find('lint:twig'); + + return new CommandTester($command); + } + + /** + * @return string Path to the new file + */ + private function createFile($content) + { + $filename = tempnam(sys_get_temp_dir(), 'sf-'); + file_put_contents($filename, $content); + + $this->files[] = $filename; + + return $filename; + } + + protected function setUp() + { + $this->files = array(); + } + + protected function tearDown() + { + foreach ($this->files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php new file mode 100644 index 0000000..5e4a9a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\CodeExtension; + +class CodeExtensionTest extends \PHPUnit_Framework_TestCase +{ + protected $helper; + + public function testFormatFile() + { + $expected = sprintf('%s at line 25', __FILE__, __FILE__); + $this->assertEquals($expected, $this->getExtension()->formatFile(__FILE__, 25)); + } + + /** + * @dataProvider getClassNameProvider + */ + public function testGettingClassAbbreviation($class, $abbr) + { + $this->assertEquals($this->getExtension()->abbrClass($class), $abbr); + } + + /** + * @dataProvider getMethodNameProvider + */ + public function testGettingMethodAbbreviation($method, $abbr) + { + $this->assertEquals($this->getExtension()->abbrMethod($method), $abbr); + } + + public function getClassNameProvider() + { + return array( + array('F\Q\N\Foo', 'Foo'), + array('Bare', 'Bare'), + ); + } + + public function getMethodNameProvider() + { + return array( + array('F\Q\N\Foo::Method', 'Foo::Method()'), + array('Bare::Method', 'Bare::Method()'), + array('Closure', 'Closure'), + array('Method', 'Method()'), + ); + } + + public function testGetName() + { + $this->assertEquals('code', $this->getExtension()->getName()); + } + + protected function getExtension() + { + return new CodeExtension('txmt://open?url=file://%f&line=%l', '/root', 'UTF-8'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php new file mode 100644 index 0000000..af93114 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\DumpExtension; +use Symfony\Component\VarDumper\VarDumper; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +class DumpExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getDumpTags + */ + public function testDumpTag($template, $debug, $expectedOutput, $expectedDumped) + { + $extension = new DumpExtension(new VarCloner()); + $twig = new \Twig_Environment(new \Twig_Loader_Array(array('template' => $template)), array( + 'debug' => $debug, + 'cache' => false, + 'optimizations' => 0, + )); + $twig->addExtension($extension); + + $dumped = null; + $exception = null; + $prevDumper = VarDumper::setHandler(function ($var) use (&$dumped) {$dumped = $var;}); + + try { + $this->assertEquals($expectedOutput, $twig->render('template')); + } catch (\Exception $exception) { + } + + VarDumper::setHandler($prevDumper); + + if (null !== $exception) { + throw $exception; + } + + $this->assertSame($expectedDumped, $dumped); + } + + public function getDumpTags() + { + return array( + array('A{% dump %}B', true, 'AB', array()), + array('A{% set foo="bar"%}B{% dump %}C', true, 'ABC', array('foo' => 'bar')), + array('A{% dump %}B', false, 'AB', null), + ); + } + + /** + * @dataProvider getDumpArgs + */ + public function testDump($context, $args, $expectedOutput, $debug = true) + { + $extension = new DumpExtension(new VarCloner()); + $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array( + 'debug' => $debug, + 'cache' => false, + 'optimizations' => 0, + )); + + array_unshift($args, $context); + array_unshift($args, $twig); + + $dump = call_user_func_array(array($extension, 'dump'), $args); + + if ($debug) { + $this->assertStringStartsWith('\n"), + array( + array(), + array(123, 456), + "
    123\n
    \n" + ."
    456\n
    \n", + ), + array( + array('foo' => 'bar'), + array(), + "
    array:1 [\n"
    +                ."  \"foo\" => \"bar\"\n"
    +                ."]\n"
    +                ."
    \n", + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php new file mode 100644 index 0000000..0c7cc1b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/ExpressionExtensionTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\ExpressionExtension; +use Symfony\Component\ExpressionLanguage\Expression; + +class ExpressionExtensionTest extends \PHPUnit_Framework_TestCase +{ + protected $helper; + + public function testExpressionCreation() + { + $template = "{{ expression('1 == 1') }}"; + $twig = new \Twig_Environment(new \Twig_Loader_Array(array('template' => $template)), array('debug' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0)); + $twig->addExtension(new ExpressionExtension()); + + $output = $twig->render('template'); + $this->assertEquals('1 == 1', $output); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php new file mode 100644 index 0000000..1c8e5dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubFilesystemLoader.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension\Fixtures; + +class StubFilesystemLoader extends \Twig_Loader_Filesystem +{ + protected function findTemplate($name, $throw = true) + { + // strip away bundle name + $parts = explode(':', $name); + + return parent::findTemplate(end($parts), $throw); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php new file mode 100644 index 0000000..b7d011b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/StubTranslator.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension\Fixtures; + +use Symfony\Component\Translation\TranslatorInterface; + +class StubTranslator implements TranslatorInterface +{ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + return '[trans]'.$id.'[/trans]'; + } + + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + return '[trans]'.$id.'[/trans]'; + } + + public function setLocale($locale) + { + } + + public function getLocale() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/child_label.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/child_label.html.twig new file mode 100644 index 0000000..8c7c248 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/child_label.html.twig @@ -0,0 +1,3 @@ +{% block form_label %} + +{% endblock form_label %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/custom_widgets.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/custom_widgets.html.twig new file mode 100644 index 0000000..4eda8d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/custom_widgets.html.twig @@ -0,0 +1,25 @@ +{% block _text_id_widget %} +{% spaceless %} +
    + {{ form_widget(form) }} +
    +{% endspaceless %} +{% endblock _text_id_widget %} + +{% block _names_entry_label %} +{% spaceless %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endspaceless %} +{% endblock _names_entry_label %} + +{% block _name_c_entry_label %} +{% spaceless %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endspaceless %} +{% endblock _name_c_entry_label %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/page_dynamic_extends.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/page_dynamic_extends.html.twig new file mode 100644 index 0000000..ac166b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/page_dynamic_extends.html.twig @@ -0,0 +1 @@ +{% extends dynamic_template_name ~ '.html.twig' %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/parent_label.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/parent_label.html.twig new file mode 100644 index 0000000..e96278b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/parent_label.html.twig @@ -0,0 +1,3 @@ +{% block form_label %} + +{% endblock form_label %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme.html.twig new file mode 100644 index 0000000..da1c1b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme.html.twig @@ -0,0 +1,6 @@ +{% block form_widget_simple %} +{% spaceless %} + {% set type = type|default('text') %} + +{% endspaceless %} +{% endblock form_widget_simple %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme_extends.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme_extends.html.twig new file mode 100644 index 0000000..8c71986 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme_extends.html.twig @@ -0,0 +1,8 @@ +{% extends 'form_div_layout.html.twig' %} + +{% block form_widget_simple %} +{% spaceless %} + {% set type = type|default('text') %} + +{% endspaceless %} +{% endblock form_widget_simple %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme_use.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme_use.html.twig new file mode 100644 index 0000000..d485b8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/Fixtures/templates/form/theme_use.html.twig @@ -0,0 +1,8 @@ +{% use 'form_div_layout.html.twig' %} + +{% block form_widget_simple %} +{% spaceless %} + {% set type = type|default('text') %} + +{% endspaceless %} +{% endblock form_widget_simple %} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php new file mode 100644 index 0000000..833829f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\FormExtension; +use Symfony\Bridge\Twig\Form\TwigRenderer; +use Symfony\Bridge\Twig\Form\TwigRendererEngine; +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\AbstractBootstrap3HorizontalLayoutTest; + +class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest +{ + /** + * @var FormExtension + */ + protected $extension; + + protected $testableFeatures = array( + 'choice_attr', + ); + + protected function setUp() + { + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'bootstrap_3_horizontal_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__.'/Fixtures/templates/form', + )); + + $environment = new \Twig_Environment($loader, array('strict_variables' => true)); + $environment->addExtension(new TranslationExtension(new StubTranslator())); + $environment->addExtension($this->extension); + + $this->extension->initRuntime($environment); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->extension = null; + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + if ($label !== null) { + $vars += array('label' => $label); + } + + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors'); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->extension->renderer->setTheme($view, $themes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php new file mode 100644 index 0000000..45cddd4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\FormExtension; +use Symfony\Bridge\Twig\Form\TwigRenderer; +use Symfony\Bridge\Twig\Form\TwigRendererEngine; +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\AbstractBootstrap3LayoutTest; + +class FormExtensionBootstrap3LayoutTest extends AbstractBootstrap3LayoutTest +{ + /** + * @var FormExtension + */ + protected $extension; + + protected function setUp() + { + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'bootstrap_3_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__.'/Fixtures/templates/form', + )); + + $environment = new \Twig_Environment($loader, array('strict_variables' => true)); + $environment->addExtension(new TranslationExtension(new StubTranslator())); + $environment->addExtension($this->extension); + + $this->extension->initRuntime($environment); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->extension = null; + } + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('
    ', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + if ($label !== null) { + $vars += array('label' => $label); + } + + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors'); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->extension->renderer->setTheme($view, $themes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php new file mode 100644 index 0000000..1f51618 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\FormExtension; +use Symfony\Bridge\Twig\Form\TwigRenderer; +use Symfony\Bridge\Twig\Form\TwigRendererEngine; +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\AbstractDivLayoutTest; + +class FormExtensionDivLayoutTest extends AbstractDivLayoutTest +{ + /** + * @var FormExtension + */ + protected $extension; + + protected function setUp() + { + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'form_div_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__.'/Fixtures/templates/form', + )); + + $environment = new \Twig_Environment($loader, array('strict_variables' => true)); + $environment->addExtension(new TranslationExtension(new StubTranslator())); + $environment->addGlobal('global', ''); + // the value can be any template that exists + $environment->addGlobal('dynamic_template_name', 'child_label'); + $environment->addExtension($this->extension); + + $this->extension->initRuntime($environment); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->extension = null; + } + + public function testThemeBlockInheritanceUsingUse() + { + $view = $this->factory + ->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType') + ->createView() + ; + + $this->setTheme($view, array('theme_use.html.twig')); + + $this->assertMatchesXpath( + $this->renderWidget($view), + '/input[@type="email"][@rel="theme"]' + ); + } + + public function testThemeBlockInheritanceUsingExtend() + { + $view = $this->factory + ->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType') + ->createView() + ; + + $this->setTheme($view, array('theme_extends.html.twig')); + + $this->assertMatchesXpath( + $this->renderWidget($view), + '/input[@type="email"][@rel="theme"]' + ); + } + + public function testThemeBlockInheritanceUsingDynamicExtend() + { + $view = $this->factory + ->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType') + ->createView() + ; + + $renderer = $this->extension->renderer; + $renderer->setTheme($view, array('page_dynamic_extends.html.twig')); + $renderer->searchAndRenderBlock($view, 'row'); + } + + public function isSelectedChoiceProvider() + { + return array( + array(true, '0', '0'), + array(true, '1', '1'), + array(true, '', ''), + array(true, '1.23', '1.23'), + array(true, 'foo', 'foo'), + array(true, 'foo10', 'foo10'), + array(true, 'foo', array(1, 'foo', 'foo10')), + + array(false, 10, array(1, 'foo', 'foo10')), + array(false, 0, array(1, 'foo', 'foo10')), + ); + } + + /** + * @dataProvider isSelectedChoiceProvider + */ + public function testIsChoiceSelected($expected, $choice, $value) + { + $choice = new ChoiceView($choice, $choice, $choice.' label'); + + $this->assertSame($expected, $this->extension->isSelectedChoice($choice, $value)); + } + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + if ($label !== null) { + $vars += array('label' => $label); + } + + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors'); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->extension->renderer->setTheme($view, $themes); + } + + public static function themeBlockInheritanceProvider() + { + return array( + array(array('theme.html.twig')), + ); + } + + public static function themeInheritanceProvider() + { + return array( + array(array('parent_label.html.twig'), array('child_label.html.twig')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php new file mode 100644 index 0000000..38a3ba3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Component\Form\FormView; +use Symfony\Bridge\Twig\Form\TwigRenderer; +use Symfony\Bridge\Twig\Form\TwigRendererEngine; +use Symfony\Bridge\Twig\Extension\FormExtension; +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Component\Form\Tests\AbstractTableLayoutTest; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator; +use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; + +class FormExtensionTableLayoutTest extends AbstractTableLayoutTest +{ + /** + * @var FormExtension + */ + protected $extension; + + protected function setUp() + { + parent::setUp(); + + $rendererEngine = new TwigRendererEngine(array( + 'form_table_layout.html.twig', + 'custom_widgets.html.twig', + )); + $renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')); + + $this->extension = new FormExtension($renderer); + + $loader = new StubFilesystemLoader(array( + __DIR__.'/../../Resources/views/Form', + __DIR__.'/Fixtures/templates/form', + )); + + $environment = new \Twig_Environment($loader, array('strict_variables' => true)); + $environment->addExtension(new TranslationExtension(new StubTranslator())); + $environment->addGlobal('global', ''); + $environment->addExtension($this->extension); + + $this->extension->initRuntime($environment); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->extension = null; + } + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form', $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + if ($label !== null) { + $vars += array('label' => $label); + } + + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'label', $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'errors'); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'widget', $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'row', $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->searchAndRenderBlock($view, 'rest', $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_start', $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->extension->renderer->renderBlock($view, 'form_end', $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->extension->renderer->setTheme($view, $themes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php new file mode 100644 index 0000000..91f978a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\HttpFoundationExtension; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; + +class HttpFoundationExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getGenerateAbsoluteUrlData() + */ + public function testGenerateAbsoluteUrl($expected, $path, $pathinfo) + { + $stack = new RequestStack(); + $stack->push(Request::create($pathinfo)); + $extension = new HttpFoundationExtension($stack); + + $this->assertEquals($expected, $extension->generateAbsoluteUrl($path)); + } + + public function getGenerateAbsoluteUrlData() + { + return array( + array('http://localhost/foo.png', '/foo.png', '/foo/bar.html'), + array('http://localhost/foo/foo.png', 'foo.png', '/foo/bar.html'), + array('http://localhost/foo/foo.png', 'foo.png', '/foo/bar'), + array('http://localhost/foo/bar/foo.png', 'foo.png', '/foo/bar/'), + + array('http://example.com/baz', 'http://example.com/baz', '/'), + array('https://example.com/baz', 'https://example.com/baz', '/'), + array('//example.com/baz', '//example.com/baz', '/'), + ); + } + + public function testGenerateAbsoluteUrlWithScriptFileName() + { + $request = Request::create('http://localhost/app/web/app_dev.php'); + $request->server->set('SCRIPT_FILENAME', '/var/www/app/web/app_dev.php'); + + $stack = new RequestStack(); + $stack->push($request); + $extension = new HttpFoundationExtension($stack); + + $this->assertEquals( + 'http://localhost/app/web/bundles/framework/css/structure.css', + $extension->generateAbsoluteUrl('/app/web/bundles/framework/css/structure.css') + ); + } + + /** + * @dataProvider getGenerateRelativePathData() + */ + public function testGenerateRelativePath($expected, $path, $pathinfo) + { + if (!method_exists('Symfony\Component\HttpFoundation\Request', 'getRelativeUriForPath')) { + $this->markTestSkipped('Your version of Symfony HttpFoundation is too old.'); + } + + $stack = new RequestStack(); + $stack->push(Request::create($pathinfo)); + $extension = new HttpFoundationExtension($stack); + + $this->assertEquals($expected, $extension->generateRelativePath($path)); + } + + public function getGenerateRelativePathData() + { + return array( + array('../foo.png', '/foo.png', '/foo/bar.html'), + array('../baz/foo.png', '/baz/foo.png', '/foo/bar.html'), + array('baz/foo.png', 'baz/foo.png', '/foo/bar.html'), + + array('http://example.com/baz', 'http://example.com/baz', '/'), + array('https://example.com/baz', 'https://example.com/baz', '/'), + array('//example.com/baz', '//example.com/baz', '/'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php new file mode 100644 index 0000000..403ed32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\HttpKernelExtension; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; + +class HttpKernelExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Twig_Error_Runtime + */ + public function testFragmentWithError() + { + $renderer = $this->getFragmentHandler($this->throwException(new \Exception('foo'))); + + $this->renderTemplate($renderer); + } + + public function testRenderFragment() + { + $renderer = $this->getFragmentHandler($this->returnValue(new Response('html'))); + + $response = $this->renderTemplate($renderer); + + $this->assertEquals('html', $response); + } + + public function testUnknownFragmentRenderer() + { + $context = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') + ->disableOriginalConstructor() + ->getMock() + ; + $renderer = new FragmentHandler($context); + + $this->setExpectedException('InvalidArgumentException', 'The "inline" renderer does not exist.'); + $renderer->render('/foo'); + } + + protected function getFragmentHandler($return) + { + $strategy = $this->getMock('Symfony\\Component\\HttpKernel\\Fragment\\FragmentRendererInterface'); + $strategy->expects($this->once())->method('getName')->will($this->returnValue('inline')); + $strategy->expects($this->once())->method('render')->will($return); + + $context = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') + ->disableOriginalConstructor() + ->getMock() + ; + + $context->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); + + $renderer = new FragmentHandler($context, array($strategy), false); + + return $renderer; + } + + protected function renderTemplate(FragmentHandler $renderer, $template = '{{ render("foo") }}') + { + $loader = new \Twig_Loader_Array(array('index' => $template)); + $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); + $twig->addExtension(new HttpKernelExtension($renderer)); + + return $twig->render('index'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php new file mode 100644 index 0000000..1c1ac64 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\RoutingExtension; + +class RoutingExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getEscapingTemplates + */ + public function testEscaping($template, $mustBeEscaped) + { + $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('debug' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0)); + $twig->addExtension(new RoutingExtension($this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'))); + + $nodes = $twig->parse($twig->tokenize($template)); + + $this->assertSame($mustBeEscaped, $nodes->getNode('body')->getNode(0)->getNode('expr') instanceof \Twig_Node_Expression_Filter); + } + + public function getEscapingTemplates() + { + return array( + array('{{ path("foo") }}', false), + array('{{ path("foo", {}) }}', false), + array('{{ path("foo", { foo: "foo" }) }}', false), + array('{{ path("foo", foo) }}', true), + array('{{ path("foo", { foo: foo }) }}', true), + array('{{ path("foo", { foo: ["foo", "bar"] }) }}', true), + array('{{ path("foo", { foo: "foo", bar: "bar" }) }}', true), + + array('{{ path(name = "foo", parameters = {}) }}', false), + array('{{ path(name = "foo", parameters = { foo: "foo" }) }}', false), + array('{{ path(name = "foo", parameters = foo) }}', true), + array('{{ path(name = "foo", parameters = { foo: ["foo", "bar"] }) }}', true), + array('{{ path(name = "foo", parameters = { foo: foo }) }}', true), + array('{{ path(name = "foo", parameters = { foo: "foo", bar: "bar" }) }}', true), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php new file mode 100644 index 0000000..8c0cdfe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\StopwatchExtension; + +class StopwatchExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Twig_Error_Syntax + */ + public function testFailIfStoppingWrongEvent() + { + $this->testTiming('{% stopwatch "foo" %}{% endstopwatch "bar" %}', array()); + } + + /** + * @dataProvider getTimingTemplates + */ + public function testTiming($template, $events) + { + $twig = new \Twig_Environment(new \Twig_Loader_Array(array('template' => $template)), array('debug' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0)); + $twig->addExtension(new StopwatchExtension($this->getStopwatch($events))); + + try { + $nodes = $twig->render('template'); + } catch (\Twig_Error_Runtime $e) { + throw $e->getPrevious(); + } + } + + public function getTimingTemplates() + { + return array( + array('{% stopwatch "foo" %}something{% endstopwatch %}', 'foo'), + array('{% stopwatch "foo" %}symfony2 is fun{% endstopwatch %}{% stopwatch "bar" %}something{% endstopwatch %}', array('foo', 'bar')), + array('{% set foo = "foo" %}{% stopwatch foo %}something{% endstopwatch %}', 'foo'), + array('{% set foo = "foo" %}{% stopwatch foo %}something {% set foo = "bar" %}{% endstopwatch %}', 'foo'), + array('{% stopwatch "foo.bar" %}something{% endstopwatch %}', 'foo.bar'), + array('{% stopwatch "foo" %}something{% endstopwatch %}{% stopwatch "foo" %}something else{% endstopwatch %}', array('foo', 'foo')), + ); + } + + protected function getStopwatch($events = array()) + { + $events = is_array($events) ? $events : array($events); + $stopwatch = $this->getMock('Symfony\Component\Stopwatch\Stopwatch'); + + $i = -1; + foreach ($events as $eventName) { + $stopwatch->expects($this->at(++$i)) + ->method('start') + ->with($this->equalTo($eventName), 'template') + ; + $stopwatch->expects($this->at(++$i)) + ->method('stop') + ->with($this->equalTo($eventName)) + ; + } + + return $stopwatch; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php new file mode 100644 index 0000000..f4a6dbf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Extension; + +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Component\Translation\Translator; +use Symfony\Component\Translation\MessageSelector; +use Symfony\Component\Translation\Loader\ArrayLoader; + +class TranslationExtensionTest extends \PHPUnit_Framework_TestCase +{ + public function testEscaping() + { + $output = $this->getTemplate('{% trans %}Percent: %value%%% (%msg%){% endtrans %}')->render(array('value' => 12, 'msg' => 'approx.')); + + $this->assertEquals('Percent: 12% (approx.)', $output); + } + + /** + * @dataProvider getTransTests + */ + public function testTrans($template, $expected, array $variables = array()) + { + if ($expected != $this->getTemplate($template)->render($variables)) { + echo $template."\n"; + $loader = new \Twig_Loader_Array(array('index' => $template)); + $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); + $twig->addExtension(new TranslationExtension(new Translator('en', new MessageSelector()))); + + echo $twig->compile($twig->parse($twig->tokenize($twig->getLoader()->getSource('index'), 'index')))."\n\n"; + $this->assertEquals($expected, $this->getTemplate($template)->render($variables)); + } + + $this->assertEquals($expected, $this->getTemplate($template)->render($variables)); + } + + /** + * @expectedException \Twig_Error_Syntax + * @expectedExceptionMessage Unexpected token. Twig was looking for the "with", "from", or "into" keyword in "index" at line 3. + */ + public function testTransUnknownKeyword() + { + $output = $this->getTemplate("{% trans \n\nfoo %}{% endtrans %}")->render(); + } + + /** + * @expectedException \Twig_Error_Syntax + * @expectedExceptionMessage A message inside a trans tag must be a simple text in "index" at line 2. + */ + public function testTransComplexBody() + { + $output = $this->getTemplate("{% trans %}\n{{ 1 + 2 }}{% endtrans %}")->render(); + } + + /** + * @expectedException \Twig_Error_Syntax + * @expectedExceptionMessage A message inside a transchoice tag must be a simple text in "index" at line 2. + */ + public function testTransChoiceComplexBody() + { + $output = $this->getTemplate("{% transchoice count %}\n{{ 1 + 2 }}{% endtranschoice %}")->render(); + } + + public function getTransTests() + { + return array( + // trans tag + array('{% trans %}Hello{% endtrans %}', 'Hello'), + array('{% trans %}%name%{% endtrans %}', 'Symfony', array('name' => 'Symfony')), + + array('{% trans from elsewhere %}Hello{% endtrans %}', 'Hello'), + + array('{% trans %}Hello %name%{% endtrans %}', 'Hello Symfony', array('name' => 'Symfony')), + array('{% trans with { \'%name%\': \'Symfony\' } %}Hello %name%{% endtrans %}', 'Hello Symfony'), + array('{% set vars = { \'%name%\': \'Symfony\' } %}{% trans with vars %}Hello %name%{% endtrans %}', 'Hello Symfony'), + + array('{% trans into "fr"%}Hello{% endtrans %}', 'Hello'), + + // transchoice + array('{% transchoice count from "messages" %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', + 'There is no apples', array('count' => 0),), + array('{% transchoice count %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', + 'There is 5 apples', array('count' => 5),), + array('{% transchoice count %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtranschoice %}', + 'There is 5 apples (Symfony)', array('count' => 5, 'name' => 'Symfony'),), + array('{% transchoice count with { \'%name%\': \'Symfony\' } %}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%){% endtranschoice %}', + 'There is 5 apples (Symfony)', array('count' => 5),), + array('{% transchoice count into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', + 'There is no apples', array('count' => 0),), + array('{% transchoice 5 into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}', + 'There is 5 apples',), + + // trans filter + array('{{ "Hello"|trans }}', 'Hello'), + array('{{ name|trans }}', 'Symfony', array('name' => 'Symfony')), + array('{{ hello|trans({ \'%name%\': \'Symfony\' }) }}', 'Hello Symfony', array('hello' => 'Hello %name%')), + array('{% set vars = { \'%name%\': \'Symfony\' } %}{{ hello|trans(vars) }}', 'Hello Symfony', array('hello' => 'Hello %name%')), + array('{{ "Hello"|trans({}, "messages", "fr") }}', 'Hello'), + + // transchoice filter + array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|transchoice(count) }}', 'There is 5 apples', array('count' => 5)), + array('{{ text|transchoice(5, {\'%name%\': \'Symfony\'}) }}', 'There is 5 apples (Symfony)', array('text' => '{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples (%name%)')), + array('{{ "{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples"|transchoice(count, {}, "messages", "fr") }}', 'There is 5 apples', array('count' => 5)), + ); + } + + public function testDefaultTranslationDomain() + { + $templates = array( + 'index' => ' + {%- extends "base" %} + + {%- trans_default_domain "foo" %} + + {%- block content %} + {%- trans %}foo{% endtrans %} + {%- trans from "custom" %}foo{% endtrans %} + {{- "foo"|trans }} + {{- "foo"|trans({}, "custom") }} + {{- "foo"|transchoice(1) }} + {{- "foo"|transchoice(1, {}, "custom") }} + {% endblock %} + ', + + 'base' => ' + {%- block content "" %} + ', + ); + + $translator = new Translator('en', new MessageSelector()); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (messages)'), 'en'); + $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom'); + $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo'); + + $template = $this->getTemplate($templates, $translator); + + $this->assertEquals('foo (foo)foo (custom)foo (foo)foo (custom)foo (foo)foo (custom)', trim($template->render(array()))); + } + + public function testDefaultTranslationDomainWithNamedArguments() + { + $templates = array( + 'index' => ' + {%- trans_default_domain "foo" %} + + {%- block content %} + {{- "foo"|trans(arguments = {}, domain = "custom") }} + {{- "foo"|transchoice(count = 1) }} + {{- "foo"|transchoice(count = 1, arguments = {}, domain = "custom") }} + {{- "foo"|trans({}, domain = "custom") }} + {{- "foo"|trans({}, "custom", locale = "fr") }} + {{- "foo"|transchoice(1, arguments = {}, domain = "custom") }} + {{- "foo"|transchoice(1, {}, "custom", locale = "fr") }} + {% endblock %} + ', + + 'base' => ' + {%- block content "" %} + ', + ); + + $translator = new Translator('en', new MessageSelector()); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array('foo' => 'foo (messages)'), 'en'); + $translator->addResource('array', array('foo' => 'foo (custom)'), 'en', 'custom'); + $translator->addResource('array', array('foo' => 'foo (foo)'), 'en', 'foo'); + $translator->addResource('array', array('foo' => 'foo (fr)'), 'fr', 'custom'); + + $template = $this->getTemplate($templates, $translator); + + $this->assertEquals('foo (custom)foo (foo)foo (custom)foo (custom)foo (fr)foo (custom)foo (fr)', trim($template->render(array()))); + } + + protected function getTemplate($template, $translator = null) + { + if (null === $translator) { + $translator = new Translator('en', new MessageSelector()); + } + + if (is_array($template)) { + $loader = new \Twig_Loader_Array($template); + } else { + $loader = new \Twig_Loader_Array(array('index' => $template)); + } + $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); + $twig->addExtension(new TranslationExtension($translator)); + + return $twig->loadTemplate('index'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/syntax_error.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/syntax_error.twig new file mode 100644 index 0000000..b3e0698 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/syntax_error.twig @@ -0,0 +1 @@ +{% syntax error diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/with_translations.html.twig b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/with_translations.html.twig new file mode 100644 index 0000000..8cdb42d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Fixtures/extractor/with_translations.html.twig @@ -0,0 +1 @@ +

    {{ 'Hi!'|trans }}

    diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php new file mode 100644 index 0000000..8e5b933 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/DumpNodeTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Node; + +use Symfony\Bridge\Twig\Node\DumpNode; + +class DumpNodeTest extends \PHPUnit_Framework_TestCase +{ + public function testNoVar() + { + $node = new DumpNode('bar', null, 7); + + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $compiler = new \Twig_Compiler($env); + + $expected = <<<'EOTXT' +if ($this->env->isDebug()) { + $barvars = array(); + foreach ($context as $barkey => $barval) { + if (!$barval instanceof \Twig_Template) { + $barvars[$barkey] = $barval; + } + } + // line 7 + \Symfony\Component\VarDumper\VarDumper::dump($barvars); +} + +EOTXT; + + $this->assertSame($expected, $compiler->compile($node)->getSource()); + } + + public function testIndented() + { + $node = new DumpNode('bar', null, 7); + + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $compiler = new \Twig_Compiler($env); + + $expected = <<<'EOTXT' + if ($this->env->isDebug()) { + $barvars = array(); + foreach ($context as $barkey => $barval) { + if (!$barval instanceof \Twig_Template) { + $barvars[$barkey] = $barval; + } + } + // line 7 + \Symfony\Component\VarDumper\VarDumper::dump($barvars); + } + +EOTXT; + + $this->assertSame($expected, $compiler->compile($node, 1)->getSource()); + } + + public function testOneVar() + { + $vars = new \Twig_Node(array( + new \Twig_Node_Expression_Name('foo', 7), + )); + $node = new DumpNode('bar', $vars, 7); + + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $compiler = new \Twig_Compiler($env); + + $expected = <<<'EOTXT' +if ($this->env->isDebug()) { + // line 7 + \Symfony\Component\VarDumper\VarDumper::dump(%foo%); +} + +EOTXT; + $expected = preg_replace('/%(.*?)%/', '(isset($context["$1"]) ? $context["$1"] : null)', $expected); + + $this->assertSame($expected, $compiler->compile($node)->getSource()); + } + + public function testMultiVars() + { + $vars = new \Twig_Node(array( + new \Twig_Node_Expression_Name('foo', 7), + new \Twig_Node_Expression_Name('bar', 7), + )); + $node = new DumpNode('bar', $vars, 7); + + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $compiler = new \Twig_Compiler($env); + + $expected = <<<'EOTXT' +if ($this->env->isDebug()) { + // line 7 + \Symfony\Component\VarDumper\VarDumper::dump(array( + "foo" => %foo%, + "bar" => %bar%, + )); +} + +EOTXT; + $expected = preg_replace('/%(.*?)%/', '(isset($context["$1"]) ? $context["$1"] : null)', $expected); + + $this->assertSame($expected, $compiler->compile($node)->getSource()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php new file mode 100644 index 0000000..3d62ce6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Node; + +use Symfony\Bridge\Twig\Node\FormThemeNode; + +class FormThemeTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $form = new \Twig_Node_Expression_Name('form', 0); + $resources = new \Twig_Node(array( + new \Twig_Node_Expression_Constant('tpl1', 0), + new \Twig_Node_Expression_Constant('tpl2', 0), + )); + + $node = new FormThemeNode($form, $resources, 0); + + $this->assertEquals($form, $node->getNode('form')); + $this->assertEquals($resources, $node->getNode('resources')); + } + + public function testCompile() + { + $form = new \Twig_Node_Expression_Name('form', 0); + $resources = new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant(0, 0), + new \Twig_Node_Expression_Constant('tpl1', 0), + new \Twig_Node_Expression_Constant(1, 0), + new \Twig_Node_Expression_Constant('tpl2', 0), + ), 0); + + $node = new FormThemeNode($form, $resources, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->setTheme(%s, array(0 => "tpl1", 1 => "tpl2"));', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + + $resources = new \Twig_Node_Expression_Constant('tpl1', 0); + + $node = new FormThemeNode($form, $resources, 0); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->setTheme(%s, "tpl1");', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + protected function getVariableGetter($name) + { + return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php new file mode 100644 index 0000000..2aea02a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/SearchAndRenderBlockNodeTest.php @@ -0,0 +1,268 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Node; + +use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode; + +class SearchAndRenderBlockNodeTest extends \PHPUnit_Framework_TestCase +{ + public function testCompileWidget() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + )); + + $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'widget\')', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileWidgetWithVariables() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant('foo', 0), + new \Twig_Node_Expression_Constant('bar', 0), + ), 0), + )); + + $node = new SearchAndRenderBlockNode('form_widget', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'widget\', array("foo" => "bar"))', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithLabel() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Constant('my label', 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("label" => "my label"))', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithNullLabel() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Constant(null, 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + // "label" => null must not be included in the output! + // Otherwise the default label is overwritten with null. + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithEmptyStringLabel() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Constant('', 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + // "label" => null must not be included in the output! + // Otherwise the default label is overwritten with null. + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithDefaultLabel() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\')', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithAttributes() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Constant(null, 0), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant('foo', 0), + new \Twig_Node_Expression_Constant('bar', 0), + ), 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + // "label" => null must not be included in the output! + // Otherwise the default label is overwritten with null. + // https://github.com/symfony/symfony/issues/5029 + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar"))', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithLabelAndAttributes() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Constant('value in argument', 0), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant('foo', 0), + new \Twig_Node_Expression_Constant('bar', 0), + new \Twig_Node_Expression_Constant('label', 0), + new \Twig_Node_Expression_Constant('value in attributes', 0), + ), 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar", "label" => "value in argument"))', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithLabelThatEvaluatesToNull() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Conditional( + // if + new \Twig_Node_Expression_Constant(true, 0), + // then + new \Twig_Node_Expression_Constant(null, 0), + // else + new \Twig_Node_Expression_Constant(null, 0), + 0 + ), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + // "label" => null must not be included in the output! + // Otherwise the default label is overwritten with null. + // https://github.com/symfony/symfony/issues/5029 + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', (twig_test_empty($_label_ = ((true) ? (null) : (null))) ? array() : array("label" => $_label_)))', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + public function testCompileLabelWithLabelThatEvaluatesToNullAndAttributes() + { + $arguments = new \Twig_Node(array( + new \Twig_Node_Expression_Name('form', 0), + new \Twig_Node_Expression_Conditional( + // if + new \Twig_Node_Expression_Constant(true, 0), + // then + new \Twig_Node_Expression_Constant(null, 0), + // else + new \Twig_Node_Expression_Constant(null, 0), + 0 + ), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant('foo', 0), + new \Twig_Node_Expression_Constant('bar', 0), + new \Twig_Node_Expression_Constant('label', 0), + new \Twig_Node_Expression_Constant('value in attributes', 0), + ), 0), + )); + + $node = new SearchAndRenderBlockNode('form_label', $arguments, 0); + + $compiler = new \Twig_Compiler(new \Twig_Environment($this->getMock('Twig_LoaderInterface'))); + + // "label" => null must not be included in the output! + // Otherwise the default label is overwritten with null. + // https://github.com/symfony/symfony/issues/5029 + $this->assertEquals( + sprintf( + '$this->env->getExtension(\'form\')->renderer->searchAndRenderBlock(%s, \'label\', array("foo" => "bar", "label" => "value in attributes") + (twig_test_empty($_label_ = ((true) ? (null) : (null))) ? array() : array("label" => $_label_)))', + $this->getVariableGetter('form') + ), + trim($compiler->compile($node)->getSource()) + ); + } + + protected function getVariableGetter($name) + { + return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php new file mode 100644 index 0000000..2281444 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Node/TransNodeTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Node; + +use Symfony\Bridge\Twig\Node\TransNode; + +/** + * @author Asmir Mustafic + */ +class TransNodeTest extends \PHPUnit_Framework_TestCase +{ + public function testCompileStrict() + { + $body = new \Twig_Node_Text('trans %var%', 0); + $vars = new \Twig_Node_Expression_Name('foo', 0); + $node = new TransNode($body, null, null, $vars); + + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('strict_variables' => true)); + $compiler = new \Twig_Compiler($env); + + $this->assertEquals( + sprintf( + 'echo $this->env->getExtension(\'translator\')->getTranslator()->trans("trans %%var%%", array_merge(array("%%var%%" => %s), %s), "messages");', + $this->getVariableGetterWithoutStrictCheck('var'), + $this->getVariableGetterWithStrictCheck('foo') + ), + trim($compiler->compile($node)->getSource()) + ); + } + protected function getVariableGetterWithoutStrictCheck($name) + { + return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name); + } + + protected function getVariableGetterWithStrictCheck($name) + { + if (version_compare(\Twig_Environment::VERSION, '2.0.0-DEV', '>=')) { + return sprintf('(isset($context["%s"]) || array_key_exists("%s", $context) ? $context["%s"] : $this->notFound("%s", 0))', $name, $name, $name, $name); + } + + return sprintf('(isset($context["%1$s"]) ? $context["%1$s"] : $this->getContext($context, "%1$s"))', $name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php new file mode 100644 index 0000000..aa9d204 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/ScopeTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\NodeVisitor\Scope; + +class ScopeTest extends \PHPUnit_Framework_TestCase +{ + public function testScopeInitiation() + { + $scope = new Scope(); + $scope->enter(); + $this->assertNull($scope->get('test')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php new file mode 100644 index 0000000..f9cf08b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\NodeVisitor\TranslationDefaultDomainNodeVisitor; +use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; + +class TranslationDefaultDomainNodeVisitorTest extends \PHPUnit_Framework_TestCase +{ + private static $message = 'message'; + private static $domain = 'domain'; + + /** @dataProvider getDefaultDomainAssignmentTestData */ + public function testDefaultDomainAssignment(\Twig_Node $node) + { + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $visitor = new TranslationDefaultDomainNodeVisitor(); + + // visit trans_default_domain tag + $defaultDomain = TwigNodeProvider::getTransDefaultDomainTag(self::$domain); + $visitor->enterNode($defaultDomain, $env); + $visitor->leaveNode($defaultDomain, $env); + + // visit tested node + $enteredNode = $visitor->enterNode($node, $env); + $leavedNode = $visitor->leaveNode($node, $env); + $this->assertSame($node, $enteredNode); + $this->assertSame($node, $leavedNode); + + // extracting tested node messages + $visitor = new TranslationNodeVisitor(); + $visitor->enable(); + $visitor->enterNode($node, $env); + $visitor->leaveNode($node, $env); + + $this->assertEquals(array(array(self::$message, self::$domain)), $visitor->getMessages()); + } + + /** @dataProvider getDefaultDomainAssignmentTestData */ + public function testNewModuleWithoutDefaultDomainTag(\Twig_Node $node) + { + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $visitor = new TranslationDefaultDomainNodeVisitor(); + + // visit trans_default_domain tag + $newModule = TwigNodeProvider::getModule('test'); + $visitor->enterNode($newModule, $env); + $visitor->leaveNode($newModule, $env); + + // visit tested node + $enteredNode = $visitor->enterNode($node, $env); + $leavedNode = $visitor->leaveNode($node, $env); + $this->assertSame($node, $enteredNode); + $this->assertSame($node, $leavedNode); + + // extracting tested node messages + $visitor = new TranslationNodeVisitor(); + $visitor->enable(); + $visitor->enterNode($node, $env); + $visitor->leaveNode($node, $env); + + $this->assertEquals(array(array(self::$message, null)), $visitor->getMessages()); + } + + public function getDefaultDomainAssignmentTestData() + { + return array( + array(TwigNodeProvider::getTransFilter(self::$message)), + array(TwigNodeProvider::getTransChoiceFilter(self::$message)), + array(TwigNodeProvider::getTransTag(self::$message)), + // with named arguments + array(TwigNodeProvider::getTransFilter(self::$message, null, array( + 'arguments' => new \Twig_Node_Expression_Array(array(), 0), + ))), + array(TwigNodeProvider::getTransChoiceFilter(self::$message), null, array( + 'arguments' => new \Twig_Node_Expression_Array(array(), 0), + )), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php new file mode 100644 index 0000000..1673603 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\NodeVisitor\TranslationNodeVisitor; + +class TranslationNodeVisitorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getMessagesExtractionTestData */ + public function testMessagesExtraction(\Twig_Node $node, array $expectedMessages) + { + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $visitor = new TranslationNodeVisitor(); + $visitor->enable(); + $visitor->enterNode($node, $env); + $visitor->leaveNode($node, $env); + $this->assertEquals($expectedMessages, $visitor->getMessages()); + } + + public function testMessageExtractionWithInvalidDomainNode() + { + $message = 'new key'; + + $node = new \Twig_Node_Expression_Filter( + new \Twig_Node_Expression_Constant($message, 0), + new \Twig_Node_Expression_Constant('trans', 0), + new \Twig_Node(array( + new \Twig_Node_Expression_Array(array(), 0), + new \Twig_Node_Expression_Name('variable', 0), + )), + 0 + ); + + $this->testMessagesExtraction($node, array(array($message, TranslationNodeVisitor::UNDEFINED_DOMAIN))); + } + + public function getMessagesExtractionTestData() + { + $message = 'new key'; + $domain = 'domain'; + + return array( + array(TwigNodeProvider::getTransFilter($message), array(array($message, null))), + array(TwigNodeProvider::getTransChoiceFilter($message), array(array($message, null))), + array(TwigNodeProvider::getTransTag($message), array(array($message, null))), + array(TwigNodeProvider::getTransFilter($message, $domain), array(array($message, $domain))), + array(TwigNodeProvider::getTransChoiceFilter($message, $domain), array(array($message, $domain))), + array(TwigNodeProvider::getTransTag($message, $domain), array(array($message, $domain))), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php new file mode 100644 index 0000000..0e401f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TwigNodeProvider.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\NodeVisitor; + +use Symfony\Bridge\Twig\Node\TransDefaultDomainNode; +use Symfony\Bridge\Twig\Node\TransNode; + +class TwigNodeProvider +{ + public static function getModule($content) + { + return new \Twig_Node_Module( + new \Twig_Node_Expression_Constant($content, 0), + null, + new \Twig_Node_Expression_Array(array(), 0), + new \Twig_Node_Expression_Array(array(), 0), + new \Twig_Node_Expression_Array(array(), 0), + null, + null + ); + } + + public static function getTransFilter($message, $domain = null, $arguments = null) + { + if (!$arguments) { + $arguments = $domain ? array( + new \Twig_Node_Expression_Array(array(), 0), + new \Twig_Node_Expression_Constant($domain, 0), + ) : array(); + } + + return new \Twig_Node_Expression_Filter( + new \Twig_Node_Expression_Constant($message, 0), + new \Twig_Node_Expression_Constant('trans', 0), + new \Twig_Node($arguments), + 0 + ); + } + + public static function getTransChoiceFilter($message, $domain = null, $arguments = null) + { + if (!$arguments) { + $arguments = $domain ? array( + new \Twig_Node_Expression_Constant(0, 0), + new \Twig_Node_Expression_Array(array(), 0), + new \Twig_Node_Expression_Constant($domain, 0), + ) : array(); + } + + return new \Twig_Node_Expression_Filter( + new \Twig_Node_Expression_Constant($message, 0), + new \Twig_Node_Expression_Constant('transchoice', 0), + new \Twig_Node($arguments), + 0 + ); + } + + public static function getTransTag($message, $domain = null) + { + return new TransNode( + new \Twig_Node_Body(array(), array('data' => $message)), + $domain ? new \Twig_Node_Expression_Constant($domain, 0) : null + ); + } + + public static function getTransDefaultDomainTag($domain) + { + return new TransDefaultDomainNode( + new \Twig_Node_Expression_Constant($domain, 0) + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php new file mode 100644 index 0000000..2986cd1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\TokenParser; + +use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser; +use Symfony\Bridge\Twig\Node\FormThemeNode; + +class FormThemeTokenParserTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTestsForFormTheme + */ + public function testCompile($source, $expected) + { + $env = new \Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $env->addTokenParser(new FormThemeTokenParser()); + $stream = $env->tokenize($source); + $parser = new \Twig_Parser($env); + + $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)); + } + + public function getTestsForFormTheme() + { + return array( + array( + '{% form_theme form "tpl1" %}', + new FormThemeNode( + new \Twig_Node_Expression_Name('form', 1), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant(0, 1), + new \Twig_Node_Expression_Constant('tpl1', 1), + ), 1), + 1, + 'form_theme' + ), + ), + array( + '{% form_theme form "tpl1" "tpl2" %}', + new FormThemeNode( + new \Twig_Node_Expression_Name('form', 1), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant(0, 1), + new \Twig_Node_Expression_Constant('tpl1', 1), + new \Twig_Node_Expression_Constant(1, 1), + new \Twig_Node_Expression_Constant('tpl2', 1), + ), 1), + 1, + 'form_theme' + ), + ), + array( + '{% form_theme form with "tpl1" %}', + new FormThemeNode( + new \Twig_Node_Expression_Name('form', 1), + new \Twig_Node_Expression_Constant('tpl1', 1), + 1, + 'form_theme' + ), + ), + array( + '{% form_theme form with ["tpl1"] %}', + new FormThemeNode( + new \Twig_Node_Expression_Name('form', 1), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant(0, 1), + new \Twig_Node_Expression_Constant('tpl1', 1), + ), 1), + 1, + 'form_theme' + ), + ), + array( + '{% form_theme form with ["tpl1", "tpl2"] %}', + new FormThemeNode( + new \Twig_Node_Expression_Name('form', 1), + new \Twig_Node_Expression_Array(array( + new \Twig_Node_Expression_Constant(0, 1), + new \Twig_Node_Expression_Constant('tpl1', 1), + new \Twig_Node_Expression_Constant(1, 1), + new \Twig_Node_Expression_Constant('tpl2', 1), + ), 1), + 1, + 'form_theme' + ), + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php new file mode 100644 index 0000000..5e7c4ce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests\Translation; + +use Symfony\Bridge\Twig\Extension\TranslationExtension; +use Symfony\Bridge\Twig\Translation\TwigExtractor; +use Symfony\Component\Translation\MessageCatalogue; + +class TwigExtractorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getExtractData + */ + public function testExtract($template, $messages) + { + $loader = $this->getMock('Twig_LoaderInterface'); + $twig = new \Twig_Environment($loader, array( + 'strict_variables' => true, + 'debug' => true, + 'cache' => false, + 'autoescape' => false, + )); + $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + + $extractor = new TwigExtractor($twig); + $extractor->setPrefix('prefix'); + $catalogue = new MessageCatalogue('en'); + + $m = new \ReflectionMethod($extractor, 'extractTemplate'); + $m->setAccessible(true); + $m->invoke($extractor, $template, $catalogue); + + foreach ($messages as $key => $domain) { + $this->assertTrue($catalogue->has($key, $domain)); + $this->assertEquals('prefix'.$key, $catalogue->get($key, $domain)); + } + } + + public function getExtractData() + { + return array( + array('{{ "new key" | trans() }}', array('new key' => 'messages')), + array('{{ "new key" | trans() | upper }}', array('new key' => 'messages')), + array('{{ "new key" | trans({}, "domain") }}', array('new key' => 'domain')), + array('{{ "new key" | transchoice(1) }}', array('new key' => 'messages')), + array('{{ "new key" | transchoice(1) | upper }}', array('new key' => 'messages')), + array('{{ "new key" | transchoice(1, {}, "domain") }}', array('new key' => 'domain')), + array('{% trans %}new key{% endtrans %}', array('new key' => 'messages')), + array('{% trans %} new key {% endtrans %}', array('new key' => 'messages')), + array('{% trans from "domain" %}new key{% endtrans %}', array('new key' => 'domain')), + array('{% set foo = "new key" | trans %}', array('new key' => 'messages')), + array('{{ 1 ? "new key" | trans : "another key" | trans }}', array('new key' => 'messages', 'another key' => 'messages')), + + // make sure 'trans_default_domain' tag is supported + array('{% trans_default_domain "domain" %}{{ "new key"|trans }}', array('new key' => 'domain')), + array('{% trans_default_domain "domain" %}{{ "new key"|transchoice }}', array('new key' => 'domain')), + array('{% trans_default_domain "domain" %}{% trans %}new key{% endtrans %}', array('new key' => 'domain')), + + // make sure this works with twig's named arguments + array('{{ "new key" | trans(domain="domain") }}', array('new key' => 'domain')), + array('{{ "new key" | transchoice(domain="domain", count=1) }}', array('new key' => 'domain')), + ); + } + + /** + * @expectedException \Twig_Error + * @expectedExceptionMessageRegExp /Unclosed "block" in ".*extractor(\/|\\)syntax_error\.twig" at line 1/ + * @dataProvider resourcesWithSyntaxErrorsProvider + */ + public function testExtractSyntaxError($resources) + { + $twig = new \Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + + $extractor = new TwigExtractor($twig); + $extractor->extract($resources, new MessageCatalogue('en')); + } + + /** + * @return array + */ + public function resourcesWithSyntaxErrorsProvider() + { + return array( + array(__DIR__.'/../Fixtures'), + array(__DIR__.'/../Fixtures/extractor/syntax_error.twig'), + array(new \SplFileInfo(__DIR__.'/../Fixtures/extractor/syntax_error.twig')), + ); + } + + /** + * @dataProvider resourceProvider + */ + public function testExtractWithFiles($resource) + { + $loader = new \Twig_Loader_Array(array()); + $twig = new \Twig_Environment($loader, array( + 'strict_variables' => true, + 'debug' => true, + 'cache' => false, + 'autoescape' => false, + )); + $twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface'))); + + $extractor = new TwigExtractor($twig); + $catalogue = new MessageCatalogue('en'); + $extractor->extract($resource, $catalogue); + + $this->assertTrue($catalogue->has('Hi!', 'messages')); + $this->assertEquals('Hi!', $catalogue->get('Hi!', 'messages')); + } + + /** + * @return array + */ + public function resourceProvider() + { + $directory = __DIR__.'/../Fixtures/extractor/'; + + return array( + array($directory.'with_translations.html.twig'), + array(array($directory.'with_translations.html.twig')), + array(array(new \SplFileInfo($directory.'with_translations.html.twig'))), + array(new \ArrayObject(array($directory.'with_translations.html.twig'))), + array(new \ArrayObject(array(new \SplFileInfo($directory.'with_translations.html.twig')))), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php new file mode 100644 index 0000000..e7047c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Tests/TwigEngineTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Tests; + +use Symfony\Bridge\Twig\TwigEngine; +use Symfony\Component\Templating\TemplateReference; + +class TwigEngineTest extends \PHPUnit_Framework_TestCase +{ + public function testExistsWithTemplateInstances() + { + $engine = $this->getTwig(); + + $this->assertTrue($engine->exists($this->getMockForAbstractClass('Twig_Template', array(), '', false))); + } + + public function testExistsWithNonExistentTemplates() + { + $engine = $this->getTwig(); + + $this->assertFalse($engine->exists('foobar')); + $this->assertFalse($engine->exists(new TemplateReference('foorbar'))); + } + + public function testExistsWithTemplateWithSyntaxErrors() + { + $engine = $this->getTwig(); + + $this->assertTrue($engine->exists('error')); + $this->assertTrue($engine->exists(new TemplateReference('error'))); + } + + public function testExists() + { + $engine = $this->getTwig(); + + $this->assertTrue($engine->exists('index')); + $this->assertTrue($engine->exists(new TemplateReference('index'))); + } + + public function testRender() + { + $engine = $this->getTwig(); + + $this->assertSame('foo', $engine->render('index')); + $this->assertSame('foo', $engine->render(new TemplateReference('index'))); + } + + /** + * @expectedException \Twig_Error_Syntax + */ + public function testRenderWithError() + { + $engine = $this->getTwig(); + + $engine->render(new TemplateReference('error')); + } + + protected function getTwig() + { + $twig = new \Twig_Environment(new \Twig_Loader_Array(array( + 'index' => 'foo', + 'error' => '{{ foo }', + ))); + $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + + return new TwigEngine($twig, $parser); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php new file mode 100644 index 0000000..269ead6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\TokenParser; + +use Symfony\Bridge\Twig\Node\DumpNode; + +/** + * Token Parser for the 'dump' tag. + * + * Dump variables with: + *
    + *  {% dump %}
    + *  {% dump foo %}
    + *  {% dump foo, bar %}
    + * 
    + * + * @author Julien Galenski + */ +class DumpTokenParser extends \Twig_TokenParser +{ + /** + * {@inheritdoc} + */ + public function parse(\Twig_Token $token) + { + $values = null; + if (!$this->parser->getStream()->test(\Twig_Token::BLOCK_END_TYPE)) { + $values = $this->parser->getExpressionParser()->parseMultitargetExpression(); + } + $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE); + + return new DumpNode($this->parser->getVarName(), $values, $token->getLine(), $this->getTag()); + } + + /** + * {@inheritdoc} + */ + public function getTag() + { + return 'dump'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php new file mode 100644 index 0000000..daf8782 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\TokenParser; + +use Symfony\Bridge\Twig\Node\FormThemeNode; + +/** + * Token Parser for the 'form_theme' tag. + * + * @author Fabien Potencier + */ +class FormThemeTokenParser extends \Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_Node A Twig_Node instance + */ + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + $form = $this->parser->getExpressionParser()->parseExpression(); + + if ($this->parser->getStream()->test(\Twig_Token::NAME_TYPE, 'with')) { + $this->parser->getStream()->next(); + $resources = $this->parser->getExpressionParser()->parseExpression(); + } else { + $resources = new \Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); + do { + $resources->addElement($this->parser->getExpressionParser()->parseExpression()); + } while (!$stream->test(\Twig_Token::BLOCK_END_TYPE)); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return new FormThemeNode($form, $resources, $lineno, $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'form_theme'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php new file mode 100644 index 0000000..2983e4c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/StopwatchTokenParser.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\TokenParser; + +use Symfony\Bridge\Twig\Node\StopwatchNode; + +/** + * Token Parser for the stopwatch tag. + * + * @author Wouter J + */ +class StopwatchTokenParser extends \Twig_TokenParser +{ + protected $stopwatchIsAvailable; + + public function __construct($stopwatchIsAvailable) + { + $this->stopwatchIsAvailable = $stopwatchIsAvailable; + } + + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + // {% stopwatch 'bar' %} + $name = $this->parser->getExpressionParser()->parseExpression(); + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + // {% endstopwatch %} + $body = $this->parser->subparse(array($this, 'decideStopwatchEnd'), true); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + if ($this->stopwatchIsAvailable) { + return new StopwatchNode($name, $body, new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $lineno, $this->getTag()); + } + + return $body; + } + + public function decideStopwatchEnd(\Twig_Token $token) + { + return $token->test('endstopwatch'); + } + + public function getTag() + { + return 'stopwatch'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php new file mode 100644 index 0000000..7ea1a2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransChoiceTokenParser.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\TokenParser; + +use Symfony\Bridge\Twig\Node\TransNode; + +/** + * Token Parser for the 'transchoice' tag. + * + * @author Fabien Potencier + */ +class TransChoiceTokenParser extends TransTokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_Node A Twig_Node instance + * + * @throws \Twig_Error_Syntax + */ + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + $vars = new \Twig_Node_Expression_Array(array(), $lineno); + + $count = $this->parser->getExpressionParser()->parseExpression(); + + $domain = null; + $locale = null; + + if ($stream->test('with')) { + // {% transchoice count with vars %} + $stream->next(); + $vars = $this->parser->getExpressionParser()->parseExpression(); + } + + if ($stream->test('from')) { + // {% transchoice count from "messages" %} + $stream->next(); + $domain = $this->parser->getExpressionParser()->parseExpression(); + } + + if ($stream->test('into')) { + // {% transchoice count into "fr" %} + $stream->next(); + $locale = $this->parser->getExpressionParser()->parseExpression(); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + $body = $this->parser->subparse(array($this, 'decideTransChoiceFork'), true); + + if (!$body instanceof \Twig_Node_Text && !$body instanceof \Twig_Node_Expression) { + throw new \Twig_Error_Syntax('A message inside a transchoice tag must be a simple text.', $body->getLine(), $stream->getFilename()); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return new TransNode($body, $domain, $count, $vars, $locale, $lineno, $this->getTag()); + } + + public function decideTransChoiceFork($token) + { + return $token->test(array('endtranschoice')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'transchoice'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php new file mode 100644 index 0000000..09832ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\TokenParser; + +use Symfony\Bridge\Twig\Node\TransDefaultDomainNode; + +/** + * Token Parser for the 'trans_default_domain' tag. + * + * @author Fabien Potencier + */ +class TransDefaultDomainTokenParser extends \Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_Node A Twig_Node instance + */ + public function parse(\Twig_Token $token) + { + $expr = $this->parser->getExpressionParser()->parseExpression(); + + $this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE); + + return new TransDefaultDomainNode($expr, $token->getLine(), $this->getTag()); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'trans_default_domain'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php new file mode 100644 index 0000000..06472d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\TokenParser; + +use Symfony\Bridge\Twig\Node\TransNode; + +/** + * Token Parser for the 'trans' tag. + * + * @author Fabien Potencier + */ +class TransTokenParser extends \Twig_TokenParser +{ + /** + * Parses a token and returns a node. + * + * @param \Twig_Token $token A Twig_Token instance + * + * @return \Twig_Node A Twig_Node instance + * + * @throws \Twig_Error_Syntax + */ + public function parse(\Twig_Token $token) + { + $lineno = $token->getLine(); + $stream = $this->parser->getStream(); + + $vars = new \Twig_Node_Expression_Array(array(), $lineno); + $domain = null; + $locale = null; + if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + if ($stream->test('with')) { + // {% trans with vars %} + $stream->next(); + $vars = $this->parser->getExpressionParser()->parseExpression(); + } + + if ($stream->test('from')) { + // {% trans from "messages" %} + $stream->next(); + $domain = $this->parser->getExpressionParser()->parseExpression(); + } + + if ($stream->test('into')) { + // {% trans into "fr" %} + $stream->next(); + $locale = $this->parser->getExpressionParser()->parseExpression(); + } elseif (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) { + throw new \Twig_Error_Syntax('Unexpected token. Twig was looking for the "with", "from", or "into" keyword.', $stream->getCurrent()->getLine(), $stream->getFilename()); + } + } + + // {% trans %}message{% endtrans %} + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideTransFork'), true); + + if (!$body instanceof \Twig_Node_Text && !$body instanceof \Twig_Node_Expression) { + throw new \Twig_Error_Syntax('A message inside a trans tag must be a simple text.', $body->getLine(), $stream->getFilename()); + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return new TransNode($body, $domain, null, $vars, $locale, $lineno, $this->getTag()); + } + + public function decideTransFork($token) + { + return $token->test(array('endtrans')); + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag() + { + return 'trans'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php new file mode 100644 index 0000000..d892fe7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Translation; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; +use Symfony\Component\Translation\Extractor\AbstractFileExtractor; +use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * TwigExtractor extracts translation messages from a twig template. + * + * @author Michel Salib + * @author Fabien Potencier + */ +class TwigExtractor extends AbstractFileExtractor implements ExtractorInterface +{ + /** + * Default domain for found messages. + * + * @var string + */ + private $defaultDomain = 'messages'; + + /** + * Prefix for found message. + * + * @var string + */ + private $prefix = ''; + + /** + * The twig environment. + * + * @var \Twig_Environment + */ + private $twig; + + public function __construct(\Twig_Environment $twig) + { + $this->twig = $twig; + } + + /** + * {@inheritdoc} + */ + public function extract($resource, MessageCatalogue $catalogue) + { + $files = $this->extractFiles($resource); + foreach ($files as $file) { + try { + $this->extractTemplate(file_get_contents($file->getPathname()), $catalogue); + } catch (\Twig_Error $e) { + if ($file instanceof SplFileInfo) { + $e->setTemplateFile($file->getRelativePathname()); + } elseif ($file instanceof \SplFileInfo) { + $e->setTemplateFile($file->getRealPath()); + } + + throw $e; + } + } + } + + /** + * {@inheritdoc} + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + protected function extractTemplate($template, MessageCatalogue $catalogue) + { + $visitor = $this->twig->getExtension('translator')->getTranslationNodeVisitor(); + $visitor->enable(); + + $this->twig->parse($this->twig->tokenize($template)); + + foreach ($visitor->getMessages() as $message) { + $catalogue->set(trim($message[0]), $this->prefix.trim($message[0]), $message[1] ?: $this->defaultDomain); + } + + $visitor->disable(); + } + + /** + * @param string $file + * + * @return bool + */ + protected function canBeExtracted($file) + { + return $this->isFile($file) && 'twig' === pathinfo($file, PATHINFO_EXTENSION); + } + + /** + * @param string|array $directory + * + * @return array + */ + protected function extractFromDirectory($directory) + { + $finder = new Finder(); + + return $finder->files()->name('*.twig')->in($directory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TwigEngine.php b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TwigEngine.php new file mode 100644 index 0000000..3e3257e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/TwigEngine.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig; + +use Symfony\Component\Templating\EngineInterface; +use Symfony\Component\Templating\StreamingEngineInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * This engine knows how to render Twig templates. + * + * @author Fabien Potencier + */ +class TwigEngine implements EngineInterface, StreamingEngineInterface +{ + protected $environment; + protected $parser; + + /** + * Constructor. + * + * @param \Twig_Environment $environment A \Twig_Environment instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + */ + public function __construct(\Twig_Environment $environment, TemplateNameParserInterface $parser) + { + $this->environment = $environment; + $this->parser = $parser; + } + + /** + * {@inheritdoc} + * + * It also supports \Twig_Template as name parameter. + * + * @throws \Twig_Error if something went wrong like a thrown exception while rendering the template + */ + public function render($name, array $parameters = array()) + { + return $this->load($name)->render($parameters); + } + + /** + * {@inheritdoc} + * + * It also supports \Twig_Template as name parameter. + * + * @throws \Twig_Error if something went wrong like a thrown exception while rendering the template + */ + public function stream($name, array $parameters = array()) + { + $this->load($name)->display($parameters); + } + + /** + * {@inheritdoc} + * + * It also supports \Twig_Template as name parameter. + */ + public function exists($name) + { + if ($name instanceof \Twig_Template) { + return true; + } + + $loader = $this->environment->getLoader(); + + if ($loader instanceof \Twig_ExistsLoaderInterface) { + return $loader->exists((string) $name); + } + + try { + // cast possible TemplateReferenceInterface to string because the + // EngineInterface supports them but Twig_LoaderInterface does not + $loader->getSource((string) $name); + } catch (\Twig_Error_Loader $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + * + * It also supports \Twig_Template as name parameter. + */ + public function supports($name) + { + if ($name instanceof \Twig_Template) { + return true; + } + + $template = $this->parser->parse($name); + + return 'twig' === $template->get('engine'); + } + + /** + * Loads the given template. + * + * @param string|TemplateReferenceInterface|\Twig_Template $name A template name or an instance of + * TemplateReferenceInterface or \Twig_Template + * + * @return \Twig_TemplateInterface A \Twig_TemplateInterface instance + * + * @throws \InvalidArgumentException if the template does not exist + */ + protected function load($name) + { + if ($name instanceof \Twig_Template) { + return $name; + } + + try { + return $this->environment->loadTemplate((string) $name); + } catch (\Twig_Error_Loader $e) { + throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/composer.json b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/composer.json new file mode 100644 index 0000000..ba90af0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/composer.json @@ -0,0 +1,65 @@ +{ + "name": "symfony/twig-bridge", + "type": "symfony-bridge", + "description": "Symfony Twig Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "twig/twig": "~1.23|~2.0" + }, + "require-dev": { + "symfony/asset": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/form": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/routing": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0", + "symfony/security": "~2.8|~3.0", + "symfony/security-acl": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/var-dumper": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0" + }, + "suggest": { + "symfony/finder": "", + "symfony/asset": "For using the AssetExtension", + "symfony/form": "For using the FormExtension", + "symfony/http-kernel": "For using the HttpKernelExtension", + "symfony/routing": "For using the RoutingExtension", + "symfony/templating": "For using the TwigEngine", + "symfony/translation": "For using the TranslationExtension", + "symfony/yaml": "For using the YamlExtension", + "symfony/security": "For using the SecurityExtension", + "symfony/stopwatch": "For using the StopwatchExtension", + "symfony/var-dumper": "For using the DumpExtension", + "symfony/expression-language": "For using the ExpressionExtension" + }, + "autoload": { + "psr-4": { "Symfony\\Bridge\\Twig\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bridge/Twig/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/phpunit.xml.dist new file mode 100644 index 0000000..10c0be1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bridge/Twig/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DebugBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DebugBundle.php new file mode 100644 index 0000000..3aa536d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DebugBundle.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DebugBundle; + +use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\DumpDataCollectorPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\VarDumper\VarDumper; + +/** + * @author Nicolas Grekas + */ +class DebugBundle extends Bundle +{ + public function boot() + { + if ($this->container->getParameter('kernel.debug')) { + $container = $this->container; + + // This code is here to lazy load the dump stack. This default + // configuration for CLI mode is overridden in HTTP mode on + // 'kernel.request' event + VarDumper::setHandler(function ($var) use ($container) { + $dumper = $container->get('var_dumper.cli_dumper'); + $cloner = $container->get('var_dumper.cloner'); + $handler = function ($var) use ($dumper, $cloner) { + $dumper->dump($cloner->cloneVar($var)); + }; + VarDumper::setHandler($handler); + $handler($var); + }); + } + } + + /** + * {@inheritdoc} + */ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new DumpDataCollectorPass()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php new file mode 100644 index 0000000..4dcb390 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DebugBundle\DependencyInjection\Compiler; + +use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Registers the file link format for the {@link \Symfony\Component\HttpKernel\DataCollector\DumpDataCollector}. + * + * @author Christian Flothmann + */ +class DumpDataCollectorPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('data_collector.dump')) { + return; + } + + $definition = $container->getDefinition('data_collector.dump'); + + if ($container->hasParameter('templating.helper.code.file_link_format')) { + $definition->replaceArgument(1, $container->getParameter('templating.helper.code.file_link_format')); + } + + if (!$container->hasParameter('web_profiler.debug_toolbar.mode') || WebDebugToolbarListener::DISABLED === $container->getParameter('web_profiler.debug_toolbar.mode')) { + $definition->replaceArgument(3, null); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..5761e62 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DebugBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * DebugExtension configuration structure. + * + * @author Nicolas Grekas + */ +class Configuration implements ConfigurationInterface +{ + /** + * {@inheritdoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('debug'); + + $rootNode + ->children() + ->integerNode('max_items') + ->info('Max number of displayed items past the first level, -1 means no limit') + ->min(-1) + ->defaultValue(2500) + ->end() + ->integerNode('max_string_length') + ->info('Max length of displayed strings, -1 means no limit') + ->min(-1) + ->defaultValue(-1) + ->end() + ->scalarNode('dump_destination') + ->info('A stream URL where dumps should be written to') + ->example('php://stderr') + ->defaultNull() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php new file mode 100644 index 0000000..a616988 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DebugBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +/** + * DebugExtension. + * + * @author Nicolas Grekas + */ +class DebugExtension extends Extension +{ + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + + $container->getDefinition('var_dumper.cloner') + ->addMethodCall('setMaxItems', array($config['max_items'])) + ->addMethodCall('setMaxString', array($config['max_string_length'])); + + if (null !== $config['dump_destination']) { + $container->getDefinition('var_dumper.cli_dumper') + ->replaceArgument(0, $config['dump_destination']) + ; + $container->getDefinition('data_collector.dump') + ->replaceArgument(4, new Reference('var_dumper.cli_dumper')) + ; + } + } + + /** + * {@inheritdoc} + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + /** + * {@inheritdoc} + */ + public function getNamespace() + { + return 'http://symfony.com/schema/dic/debug'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/schema/debug-1.0.xsd b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/schema/debug-1.0.xsd new file mode 100644 index 0000000..a582ff8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/schema/debug-1.0.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml new file mode 100644 index 0000000..001b987 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/config/services.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + null + %kernel.charset% + + null + + + + + + + + + + + null + %kernel.charset% + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/meta/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..ef1cde9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig new file mode 100644 index 0000000..1163d28 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig @@ -0,0 +1,78 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% if collector.dumpsCount %} + {% set icon %} + {{ include('@Debug/Profiler/icon.svg') }} + {{ collector.dumpsCount }} + {% endset %} + + {% set text %} + {% for dump in collector.getDumps('html') %} +
    + + {% if dump.file %} + {% set link = dump.file|file_link(dump.line) %} + {% if link %} + {{ dump.name }} + {% else %} + {{ dump.name }} + {% endif %} + {% else %} + {{ dump.name }} + {% endif %} + + line {{ dump.line }} + + {{ dump.data|raw }} +
    + {% endfor %} + + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }} + {% endif %} +{% endblock %} + +{% block menu %} + + {{ include('@Debug/Profiler/icon.svg') }} + Debug + +{% endblock %} + +{% block panel %} +

    Dumped Contents

    + + {% for dump in collector.getDumps('html') %} +
    +

    In + {% if dump.line %} + {% set link = dump.file|file_link(dump.line) %} + {% if link %} + {{ dump.name }} + {% else %} + {{ dump.name }} + {% endif %} + {% else %} + {{ dump.name }} + {% endif %} + line {{ dump.line }} + + Show code +

    + + + + {{ dump.data|raw }} +
    + {% else %} +
    +

    No content was dumped.

    +
    + {% endfor %} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg new file mode 100644 index 0000000..2f7e708 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php new file mode 100644 index 0000000..6500a85 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/Compiler/DumpDataCollectorPassTest.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DebugBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\DumpDataCollectorPass; +use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\HttpFoundation\RequestStack; + +class DumpDataCollectorPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcessWithFileLinkFormatParameter() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new DumpDataCollectorPass()); + $container->setParameter('templating.helper.code.file_link_format', 'file-link-format'); + + $definition = new Definition('Symfony\Component\HttpKernel\DataCollector\DumpDataCollector', array(null, null, null, null)); + $container->setDefinition('data_collector.dump', $definition); + + $container->compile(); + + $this->assertSame('file-link-format', $definition->getArgument(1)); + } + + public function testProcessWithoutFileLinkFormatParameter() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new DumpDataCollectorPass()); + + $definition = new Definition('Symfony\Component\HttpKernel\DataCollector\DumpDataCollector', array(null, null, null, null)); + $container->setDefinition('data_collector.dump', $definition); + + $container->compile(); + + $this->assertNull($definition->getArgument(1)); + } + + public function testProcessWithToolbarEnabled() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new DumpDataCollectorPass()); + $requestStack = new RequestStack(); + + $definition = new Definition('Symfony\Component\HttpKernel\DataCollector\DumpDataCollector', array(null, null, null, $requestStack)); + $container->setDefinition('data_collector.dump', $definition); + $container->setParameter('web_profiler.debug_toolbar.mode', WebDebugToolbarListener::ENABLED); + + $container->compile(); + + $this->assertSame($requestStack, $definition->getArgument(3)); + } + + public function testProcessWithToolbarDisabled() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new DumpDataCollectorPass()); + + $definition = new Definition('Symfony\Component\HttpKernel\DataCollector\DumpDataCollector', array(null, null, null, new RequestStack())); + $container->setDefinition('data_collector.dump', $definition); + $container->setParameter('web_profiler.debug_toolbar.mode', WebDebugToolbarListener::DISABLED); + + $container->compile(); + + $this->assertNull($definition->getArgument(3)); + } + + public function testProcessWithoutToolbar() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new DumpDataCollectorPass()); + + $definition = new Definition('Symfony\Component\HttpKernel\DataCollector\DumpDataCollector', array(null, null, null, new RequestStack())); + $container->setDefinition('data_collector.dump', $definition); + + $container->compile(); + + $this->assertNull($definition->getArgument(3)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php new file mode 100644 index 0000000..fc4e620 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\DebugBundle\Tests\DependencyInjection; + +use Symfony\Bundle\DebugBundle\DependencyInjection\DebugExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class DebugExtensionTest extends \PHPUnit_Framework_TestCase +{ + public function testLoadWithoutConfiguration() + { + $container = $this->createContainer(); + $container->registerExtension(new DebugExtension()); + $container->loadFromExtension('debug', array()); + $this->compileContainer($container); + + $expectedTags = array( + array( + 'id' => 'dump', + 'template' => '@Debug/Profiler/dump.html.twig', + 'priority' => 240, + ), + ); + + $this->assertSame($expectedTags, $container->getDefinition('data_collector.dump')->getTag('data_collector')); + } + + private function createContainer() + { + $container = new ContainerBuilder(new ParameterBag(array( + 'kernel.cache_dir' => __DIR__, + 'kernel.root_dir' => __DIR__.'/Fixtures', + 'kernel.charset' => 'UTF-8', + 'kernel.debug' => true, + 'kernel.bundles' => array('DebugBundle' => 'Symfony\\Bundle\\DebugBundle\\DebugBundle'), + ))); + + return $container; + } + + private function compileContainer(ContainerBuilder $container) + { + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/composer.json b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/composer.json new file mode 100644 index 0000000..e3338e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/composer.json @@ -0,0 +1,45 @@ +{ + "name": "symfony/debug-bundle", + "type": "symfony-bundle", + "description": "Symfony DebugBundle", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/twig-bridge": "~2.8|~3.0", + "symfony/var-dumper": "~2.8|~3.0" + }, + "require-dev": { + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/web-profiler-bundle": "~2.8|~3.0" + }, + "suggest": { + "symfony/config": "For service container configuration", + "symfony/dependency-injection": "For using as a service from the container" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\DebugBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist new file mode 100644 index 0000000..90ec0a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/DebugBundle/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md new file mode 100644 index 0000000..f15ffc7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -0,0 +1,133 @@ +CHANGELOG +========= + +3.0.0 +----- + + * removed `validator.api` parameter + * removed `alias` option of the `form.type` tag + +2.8.0 +----- + + * Deprecated the `alias` option of the `form.type_extension` tag in favor of the + `extended_type`/`extended-type` option + * Deprecated the `alias` option of the `form.type` tag + * Deprecated the Shell + +2.7.0 +----- + + * Added possibility to extract translation messages from a file or files besides extracting from a directory + * Added `TranslationsCacheWarmer` to create catalogues at warmup + +2.6.0 +----- + + * Added helper commands (`server:start`, `server:stop` and `server:status`) to control the built-in web + server in the background + * Added `Controller::isCsrfTokenValid` helper + * Added configuration for the PropertyAccess component + * Added `Controller::redirectToRoute` helper + * Added `Controller::addFlash` helper + * Added `Controller::isGranted` helper + * Added `Controller::denyAccessUnlessGranted` helper + * Deprecated `app.security` in twig as `app.user` and `is_granted()` are already available + +2.5.0 +----- + + * Added `translation:debug` command + * Added `--no-backup` option to `translation:update` command + * Added `config:debug` command + * Added `yaml:lint` command + * Deprecated the `RouterApacheDumperCommand` which will be removed in Symfony 3.0. + +2.4.0 +----- + + * allowed multiple IP addresses in profiler matcher settings + * added stopwatch helper to time templates with the WebProfilerBundle + * added service definition for "security.secure_random" service + * added service definitions for the new Security CSRF sub-component + +2.3.0 +----- + + * [BC BREAK] added a way to disable the profiler (when disabling the profiler, it is now completely removed) + To get the same "disabled" behavior as before, set `enabled` to `true` and `collect` to `false` + * [BC BREAK] the `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass` was moved + to `Component\HttpKernel\DependencyInjection\RegisterListenersPass` + * added ControllerNameParser::build() which converts a controller short notation (a:b:c) to a class::method notation + * added possibility to run PHP built-in server in production environment + * added possibility to load the serializer component in the service container + * added route debug information when using the `router:match` command + * added `TimedPhpEngine` + * added `--clean` option to the `translation:update` command + * added `http_method_override` option + * added support for default templates per render tag + * added FormHelper::form(), FormHelper::start() and FormHelper::end() + * deprecated FormHelper::enctype() in favor of FormHelper::start() + * RedirectController actions now receive the Request instance via the method signature. + +2.2.0 +----- + + * added a new `uri_signer` service to help sign URIs + * deprecated `Symfony\Bundle\FrameworkBundle\HttpKernel::render()` and `Symfony\Bundle\FrameworkBundle\HttpKernel::forward()` + * deprecated the `Symfony\Bundle\FrameworkBundle\HttpKernel` class in favor of `Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel` + * added support for adding new HTTP content rendering strategies (like ESI and Hinclude) + in the DIC via the `kernel.fragment_renderer` tag + * [BC BREAK] restricted the `Symfony\Bundle\FrameworkBundle\HttpKernel::render()` method to only accept URIs or ControllerReference instances + * `Symfony\Bundle\FrameworkBundle\HttpKernel::render()` method signature changed and the first argument + must now be a URI or a ControllerReference instance (the `generateInternalUri()` method was removed) + * The internal routes (`Resources/config/routing/internal.xml`) have been removed and replaced with a listener (`Symfony\Component\HttpKernel\EventListener\FragmentListener`) + * The `render` method of the `actions` templating helper signature and arguments changed + * replaced Symfony\Bundle\FrameworkBundle\Controller\TraceableControllerResolver by Symfony\Component\HttpKernel\Controller\TraceableControllerResolver + * replaced Symfony\Component\HttpKernel\Debug\ContainerAwareTraceableEventDispatcher by Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher + * added Client::enableProfiler() + * a new parameter has been added to the DIC: `router.request_context.base_url` + You can customize it for your functional tests or for generating URLs with + the right base URL when your are in the CLI context. + * added support for default templates per render tag + +2.1.0 +----- + + * moved the translation files to the Form and Validator components + * changed the default extension for XLIFF files from .xliff to .xlf + * moved Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher to Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher + * moved Symfony\Bundle\FrameworkBundle\Debug\TraceableEventDispatcher to Symfony\Component\EventDispatcher\ContainerAwareTraceableEventDispatcher + * added a router:match command + * added a config:dump-reference command + * added a server:run command + * added kernel.event_subscriber tag + * added a way to create relative symlinks when running assets:install command (--relative option) + * added Controller::getUser() + * [BC BREAK] assets_base_urls and base_urls merging strategy has changed + * changed the default profiler storage to use the filesystem instead of SQLite + * added support for placeholders in route defaults and requirements (replaced + by the value set in the service container) + * added Filesystem component as a dependency + * added support for hinclude (use ``standalone: 'js'`` in render tag) + * session options: lifetime, path, domain, secure, httponly were deprecated. + Prefixed versions should now be used instead: cookie_lifetime, cookie_path, + cookie_domain, cookie_secure, cookie_httponly + * [BC BREAK] following session options: 'lifetime', 'path', 'domain', 'secure', + 'httponly' are now prefixed with cookie_ when dumped to the container + * Added `handler_id` configuration under `session` key to represent `session.handler` + service, defaults to `session.handler.native_file`. + * Added `gc_maxlifetime`, `gc_probability`, and `gc_divisor` to session + configuration. This means session garbage collection has a + `gc_probability`/`gc_divisor` chance of being run. The `gc_maxlifetime` defines + how long a session can idle for. It is different from cookie lifetime which + declares how long a cookie can be stored on the remote client. + * Removed 'auto_start' configuration parameter from session config. The session will + start on demand. + * [BC BREAK] TemplateNameParser::parseFromFilename() has been moved to a dedicated + parser: TemplateFilenameParser::parse(). + * [BC BREAK] Kernel parameters are replaced by their value wherever they appear + in Route patterns, requirements and defaults. Use '%%' as the escaped value for '%'. + * [BC BREAK] Switched behavior of flash messages to expire flash messages on retrieval + using Symfony\Component\HttpFoundation\Session\Flash\FlashBag as opposed to on + next pageload regardless of whether they are displayed or not. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php new file mode 100644 index 0000000..54b9b33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ClassCacheCacheWarmer.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +/** + * Generates the Class Cache (classes.php) file. + * + * @author Tugdual Saunier + */ +class ClassCacheCacheWarmer implements CacheWarmerInterface +{ + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + $classmap = $cacheDir.'/classes.map'; + + if (!is_file($classmap)) { + return; + } + + if (file_exists($cacheDir.'/classes.php')) { + return; + } + + ClassCollectionLoader::load(include($classmap), $cacheDir, 'classes', false); + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always true + */ + public function isOptional() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php new file mode 100644 index 0000000..256bebe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Routing\RouterInterface; + +/** + * Generates the router matcher and generator classes. + * + * @author Fabien Potencier + */ +class RouterCacheWarmer implements CacheWarmerInterface +{ + protected $router; + + /** + * Constructor. + * + * @param RouterInterface $router A Router instance + */ + public function __construct(RouterInterface $router) + { + $this->router = $router; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + if ($this->router instanceof WarmableInterface) { + $this->router->warmUp($cacheDir); + } + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always true + */ + public function isOptional() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php new file mode 100644 index 0000000..1e22984 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinder.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * Finds all the templates. + * + * @author Victor Berchet + */ +class TemplateFinder implements TemplateFinderInterface +{ + private $kernel; + private $parser; + private $rootDir; + private $templates; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param string $rootDir The directory where global templates can be stored + */ + public function __construct(KernelInterface $kernel, TemplateNameParserInterface $parser, $rootDir) + { + $this->kernel = $kernel; + $this->parser = $parser; + $this->rootDir = $rootDir; + } + + /** + * Find all the templates in the bundle and in the kernel Resources folder. + * + * @return TemplateReferenceInterface[] + */ + public function findAllTemplates() + { + if (null !== $this->templates) { + return $this->templates; + } + + $templates = array(); + + foreach ($this->kernel->getBundles() as $bundle) { + $templates = array_merge($templates, $this->findTemplatesInBundle($bundle)); + } + + $templates = array_merge($templates, $this->findTemplatesInFolder($this->rootDir.'/views')); + + return $this->templates = $templates; + } + + /** + * Find templates in the given directory. + * + * @param string $dir The folder where to look for templates + * + * @return TemplateReferenceInterface[] + */ + private function findTemplatesInFolder($dir) + { + $templates = array(); + + if (is_dir($dir)) { + $finder = new Finder(); + foreach ($finder->files()->followLinks()->in($dir) as $file) { + $template = $this->parser->parse($file->getRelativePathname()); + if (false !== $template) { + $templates[] = $template; + } + } + } + + return $templates; + } + + /** + * Find templates in the given bundle. + * + * @param BundleInterface $bundle The bundle where to look for templates + * + * @return TemplateReferenceInterface[] + */ + private function findTemplatesInBundle(BundleInterface $bundle) + { + $name = $bundle->getName(); + $templates = array_merge( + $this->findTemplatesInFolder($bundle->getPath().'/Resources/views'), + $this->findTemplatesInFolder($this->rootDir.'/'.$name.'/views') + ); + $templates = array_unique($templates); + + foreach ($templates as $i => $template) { + $templates[$i] = $template->set('bundle', $name); + } + + return $templates; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinderInterface.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinderInterface.php new file mode 100644 index 0000000..433ed8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplateFinderInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +/** + * Interface for finding all the templates. + * + * @author Victor Berchet + */ +interface TemplateFinderInterface +{ + /** + * Find all the templates. + * + * @return array An array of templates of type TemplateReferenceInterface + */ + public function findAllTemplates(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php new file mode 100644 index 0000000..829218b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; +use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator; + +/** + * Computes the association between template names and their paths on the disk. + * + * @author Fabien Potencier + */ +class TemplatePathsCacheWarmer extends CacheWarmer +{ + protected $finder; + protected $locator; + + /** + * Constructor. + * + * @param TemplateFinderInterface $finder A template finder + * @param TemplateLocator $locator The template locator + */ + public function __construct(TemplateFinderInterface $finder, TemplateLocator $locator) + { + $this->finder = $finder; + $this->locator = $locator; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + $templates = array(); + + foreach ($this->finder->findAllTemplates() as $template) { + $templates[$template->getLogicalName()] = $this->locator->locate($template); + } + + $this->writeCacheFile($cacheDir.'/templates.php', sprintf(' + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * Generates the catalogues for translations. + * + * @author Xavier Leune + */ +class TranslationsCacheWarmer implements CacheWarmerInterface +{ + private $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + if ($this->translator instanceof WarmableInterface) { + $this->translator->warmUp($cacheDir); + } + } + + /** + * {@inheritdoc} + */ + public function isOptional() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php new file mode 100644 index 0000000..d9b064e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Client as BaseClient; +use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\CookieJar; + +/** + * Client simulates a browser and makes requests to a Kernel object. + * + * @author Fabien Potencier + */ +class Client extends BaseClient +{ + private $hasPerformedRequest = false; + private $profiler = false; + private $reboot = true; + + /** + * {@inheritdoc} + */ + public function __construct(KernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + parent::__construct($kernel, $server, $history, $cookieJar); + } + + /** + * Returns the container. + * + * @return ContainerInterface + */ + public function getContainer() + { + return $this->kernel->getContainer(); + } + + /** + * Returns the kernel. + * + * @return KernelInterface + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Gets the profile associated with the current Response. + * + * @return HttpProfile A Profile instance + */ + public function getProfile() + { + if (!$this->kernel->getContainer()->has('profiler')) { + return false; + } + + return $this->kernel->getContainer()->get('profiler')->loadProfileFromResponse($this->response); + } + + /** + * Enables the profiler for the very next request. + * + * If the profiler is not enabled, the call to this method does nothing. + */ + public function enableProfiler() + { + if ($this->kernel->getContainer()->has('profiler')) { + $this->profiler = true; + } + } + + /** + * Disables kernel reboot between requests. + * + * By default, the Client reboots the Kernel for each request. This method + * allows to keep the same kernel across requests. + */ + public function disableReboot() + { + $this->reboot = false; + } + + /** + * Enables kernel reboot between requests. + */ + public function enableReboot() + { + $this->reboot = true; + } + + /** + * {@inheritdoc} + * + * @param Request $request A Request instance + * + * @return Response A Response instance + */ + protected function doRequest($request) + { + // avoid shutting down the Kernel if no request has been performed yet + // WebTestCase::createClient() boots the Kernel but do not handle a request + if ($this->hasPerformedRequest && $this->reboot) { + $this->kernel->shutdown(); + } else { + $this->hasPerformedRequest = true; + } + + if ($this->profiler) { + $this->profiler = false; + + $this->kernel->boot(); + $this->kernel->getContainer()->get('profiler')->enable(); + } + + return parent::doRequest($request); + } + + /** + * {@inheritdoc} + * + * @param Request $request A Request instance + * + * @return Response A Response instance + */ + protected function doRequestInProcess($request) + { + $response = parent::doRequestInProcess($request); + + $this->profiler = false; + + return $response; + } + + /** + * Returns the script to execute when the request must be insulated. + * + * It assumes that the autoloader is named 'autoload.php' and that it is + * stored in the same directory as the kernel (this is the case for the + * Symfony Standard Edition). If this is not your case, create your own + * client and override this method. + * + * @param Request $request A Request instance + * + * @return string The script content + */ + protected function getScript($request) + { + $kernel = str_replace("'", "\\'", serialize($this->kernel)); + $request = str_replace("'", "\\'", serialize($request)); + + $r = new \ReflectionObject($this->kernel); + + $autoloader = dirname($r->getFileName()).'/autoload.php'; + if (is_file($autoloader)) { + $autoloader = str_replace("'", "\\'", $autoloader); + } else { + $autoloader = ''; + } + + $path = str_replace("'", "\\'", $r->getFileName()); + + $profilerCode = ''; + if ($this->profiler) { + $profilerCode = '$kernel->getContainer()->get(\'profiler\')->enable();'; + } + + $errorReporting = error_reporting(); + + $code = <<boot(); +$profilerCode + +\$request = unserialize('$request'); +EOF; + + return $code.$this->getHandleScript(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php new file mode 100644 index 0000000..6b20a42 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Style\StyleInterface; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * A console command for dumping available configuration reference. + * + * @author Kevin Bond + * @author Wouter J + * @author Grégoire Pineau + */ +abstract class AbstractConfigCommand extends ContainerDebugCommand +{ + protected function listBundles($output) + { + $headers = array('Bundle name', 'Extension alias'); + $rows = array(); + foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) { + $extension = $bundle->getContainerExtension(); + $rows[] = array($bundle->getName(), $extension ? $extension->getAlias() : ''); + } + + if ($output instanceof StyleInterface) { + $output->table($headers, $rows); + } else { + $output->writeln('Available registered bundles with their extension alias if available:'); + $table = new Table($output); + $table->setHeaders($headers)->setRows($rows)->render($output); + } + } + + protected function findExtension($name) + { + $extension = null; + $bundles = $this->initializeBundles(); + foreach ($bundles as $bundle) { + $extension = $bundle->getContainerExtension(); + + if ($extension && ($name === $extension->getAlias() || $name === $bundle->getName())) { + break; + } + } + + if (!$extension) { + $message = sprintf('No extension with alias "%s" is enabled', $name); + if (preg_match('/Bundle$/', $name)) { + $message = sprintf('No extensions with configuration available for "%s"', $name); + } + + throw new \LogicException($message); + } + + return $extension; + } + + public function validateConfiguration(ExtensionInterface $extension, $configuration) + { + if (!$configuration) { + throw new \LogicException(sprintf('The extension with alias "%s" does not have its getConfiguration() method setup', $extension->getAlias())); + } + + if (!$configuration instanceof ConfigurationInterface) { + throw new \LogicException(sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable', get_class($configuration))); + } + } + + private function initializeBundles() + { + // Re-build bundle manually to initialize DI extensions that can be extended by other bundles in their build() method + // as this method is not called when the container is loaded from the cache. + $container = $this->getContainerBuilder(); + $bundles = $this->getContainer()->get('kernel')->registerBundles(); + foreach ($bundles as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $container->registerExtension($extension); + } + } + + foreach ($bundles as $bundle) { + $bundle->build($container); + } + + return $bundles; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php new file mode 100644 index 0000000..647dc0f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * Command that places bundle web assets into a given directory. + * + * @author Fabien Potencier + * @author Gábor Egyed + */ +class AssetsInstallCommand extends ContainerAwareCommand +{ + const METHOD_COPY = 'copy'; + const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink'; + const METHOD_RELATIVE_SYMLINK = 'relative symlink'; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('assets:install') + ->setDefinition(array( + new InputArgument('target', InputArgument::OPTIONAL, 'The target directory', 'web'), + )) + ->addOption('symlink', null, InputOption::VALUE_NONE, 'Symlinks the assets instead of copying it') + ->addOption('relative', null, InputOption::VALUE_NONE, 'Make relative symlinks') + ->setDescription('Installs bundles web assets under a public web directory') + ->setHelp(<<%command.name% command installs bundle assets into a given +directory (e.g. the web directory). + + php %command.full_name% web + +A "bundles" directory will be created inside the target directory and the +"Resources/public" directory of each bundle will be copied into it. + +To create a symlink to each bundle instead of copying its assets, use the +--symlink option (will fall back to hard copies when symbolic links aren't possible: + + php %command.full_name% web --symlink + +To make symlink relative, add the --relative option: + + php %command.full_name% web --symlink --relative + +EOT + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $targetArg = rtrim($input->getArgument('target'), '/'); + + if (!is_dir($targetArg)) { + throw new \InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); + } + + $this->filesystem = $this->getContainer()->get('filesystem'); + + // Create the bundles directory otherwise symlink will fail. + $bundlesDir = $targetArg.'/bundles/'; + $this->filesystem->mkdir($bundlesDir, 0777); + + $io = new SymfonyStyle($input, $output); + $io->newLine(); + + if ($input->getOption('relative')) { + $expectedMethod = self::METHOD_RELATIVE_SYMLINK; + $io->text('Trying to install assets as relative symbolic links.'); + } elseif ($input->getOption('symlink')) { + $expectedMethod = self::METHOD_ABSOLUTE_SYMLINK; + $io->text('Trying to install assets as absolute symbolic links.'); + } else { + $expectedMethod = self::METHOD_COPY; + $io->text('Installing assets as hard copies.'); + } + + $io->newLine(); + + $rows = array(); + $copyUsed = false; + $exitCode = 0; + /** @var BundleInterface $bundle */ + foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) { + if (!is_dir($originDir = $bundle->getPath().'/Resources/public')) { + continue; + } + + $targetDir = $bundlesDir.preg_replace('/bundle$/', '', strtolower($bundle->getName())); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $message = sprintf("%s\n-> %s", $bundle->getName(), $targetDir); + } else { + $message = $bundle->getName(); + } + + try { + $this->filesystem->remove($targetDir); + + if (self::METHOD_RELATIVE_SYMLINK === $expectedMethod) { + $method = $this->relativeSymlinkWithFallback($originDir, $targetDir); + } elseif (self::METHOD_ABSOLUTE_SYMLINK === $expectedMethod) { + $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir); + } else { + $method = $this->hardCopy($originDir, $targetDir); + } + + if (self::METHOD_COPY === $method) { + $copyUsed = true; + } + + if ($method === $expectedMethod) { + $rows[] = array(sprintf('%s', '\\' === DIRECTORY_SEPARATOR ? 'OK' : "\xE2\x9C\x94" /* HEAVY CHECK MARK (U+2714) */), $message, $method); + } else { + $rows[] = array(sprintf('%s', '\\' === DIRECTORY_SEPARATOR ? 'WARNING' : '!'), $message, $method); + } + } catch (\Exception $e) { + $exitCode = 1; + $rows[] = array(sprintf('%s', '\\' === DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage()); + } + } + + $io->table(array('', 'Bundle', 'Method / Error'), $rows); + + if (0 !== $exitCode) { + $io->error('Some errors occurred while installing assets.'); + } else { + if ($copyUsed) { + $io->note('Some assets were installed via copy. If you make changes to these assets you have to run this command again.'); + } + $io->success('All assets were successfully installed.'); + } + + return $exitCode; + } + + /** + * Try to create relative symlink. + * + * Falling back to absolute symlink and finally hard copy. + * + * @param string $originDir + * @param string $targetDir + * + * @return string + */ + private function relativeSymlinkWithFallback($originDir, $targetDir) + { + try { + $this->symlink($originDir, $targetDir, true); + $method = self::METHOD_RELATIVE_SYMLINK; + } catch (IOException $e) { + $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir); + } + + return $method; + } + + /** + * Try to create absolute symlink. + * + * Falling back to hard copy. + * + * @param string $originDir + * @param string $targetDir + * + * @return string + */ + private function absoluteSymlinkWithFallback($originDir, $targetDir) + { + try { + $this->symlink($originDir, $targetDir); + $method = self::METHOD_ABSOLUTE_SYMLINK; + } catch (IOException $e) { + // fall back to copy + $method = $this->hardCopy($originDir, $targetDir); + } + + return $method; + } + + /** + * Creates symbolic link. + * + * @param string $originDir + * @param string $targetDir + * @param bool $relative + * + * @throws IOException If link can not be created. + */ + private function symlink($originDir, $targetDir, $relative = false) + { + if ($relative) { + $originDir = $this->filesystem->makePathRelative($originDir, realpath(dirname($targetDir))); + } + $this->filesystem->symlink($originDir, $targetDir); + if (!file_exists($targetDir)) { + throw new IOException(sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir); + } + } + + /** + * Copies origin to target. + * + * @param string $originDir + * @param string $targetDir + * + * @return string + */ + private function hardCopy($originDir, $targetDir) + { + $this->filesystem->mkdir($targetDir, 0777); + // We use a custom iterator to ignore VCS files + $this->filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir)); + + return self::METHOD_COPY; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php new file mode 100644 index 0000000..71208b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Finder\Finder; + +/** + * Clear and Warmup the cache. + * + * @author Francis Besset + * @author Fabien Potencier + */ +class CacheClearCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('cache:clear') + ->setDefinition(array( + new InputOption('no-warmup', '', InputOption::VALUE_NONE, 'Do not warm up the cache'), + new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'), + )) + ->setDescription('Clears the cache') + ->setHelp(<<%command.name% command clears the application cache for a given environment +and debug mode: + + php %command.full_name% --env=dev + php %command.full_name% --env=prod --no-debug +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $outputIsVerbose = $output->isVerbose(); + $io = new SymfonyStyle($input, $output); + + $realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); + // the old cache dir name must not be longer than the real one to avoid exceeding + // the maximum length of a directory or file path within it (esp. Windows MAX_PATH) + $oldCacheDir = substr($realCacheDir, 0, -1).('~' === substr($realCacheDir, -1) ? '+' : '~'); + $filesystem = $this->getContainer()->get('filesystem'); + + if (!is_writable($realCacheDir)) { + throw new \RuntimeException(sprintf('Unable to write in the "%s" directory', $realCacheDir)); + } + + if ($filesystem->exists($oldCacheDir)) { + $filesystem->remove($oldCacheDir); + } + + $kernel = $this->getContainer()->get('kernel'); + $io->comment(sprintf('Clearing the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $this->getContainer()->get('cache_clearer')->clear($realCacheDir); + + if ($input->getOption('no-warmup')) { + $filesystem->rename($realCacheDir, $oldCacheDir); + } else { + // the warmup cache dir name must have the same length than the real one + // to avoid the many problems in serialized resources files + $realCacheDir = realpath($realCacheDir); + $warmupDir = substr($realCacheDir, 0, -1).('_' === substr($realCacheDir, -1) ? '-' : '_'); + + if ($filesystem->exists($warmupDir)) { + if ($outputIsVerbose) { + $io->comment('Clearing outdated warmup directory...'); + } + $filesystem->remove($warmupDir); + } + + if ($outputIsVerbose) { + $io->comment('Warming up cache...'); + } + $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); + + $filesystem->rename($realCacheDir, $oldCacheDir); + if ('\\' === DIRECTORY_SEPARATOR) { + sleep(1); // workaround for Windows PHP rename bug + } + $filesystem->rename($warmupDir, $realCacheDir); + } + + if ($outputIsVerbose) { + $io->comment('Removing old cache directory...'); + } + + $filesystem->remove($oldCacheDir); + + if ($outputIsVerbose) { + $io->comment('Finished'); + } + + $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + } + + /** + * @param string $warmupDir + * @param string $realCacheDir + * @param bool $enableOptionalWarmers + */ + protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true) + { + // create a temporary kernel + $realKernel = $this->getContainer()->get('kernel'); + $realKernelClass = get_class($realKernel); + $namespace = ''; + if (false !== $pos = strrpos($realKernelClass, '\\')) { + $namespace = substr($realKernelClass, 0, $pos); + $realKernelClass = substr($realKernelClass, $pos + 1); + } + $tempKernel = $this->getTempKernel($realKernel, $namespace, $realKernelClass, $warmupDir); + $tempKernel->boot(); + + $tempKernelReflection = new \ReflectionObject($tempKernel); + $tempKernelFile = $tempKernelReflection->getFileName(); + + // warmup temporary dir + $warmer = $tempKernel->getContainer()->get('cache_warmer'); + if ($enableOptionalWarmers) { + $warmer->enableOptionalWarmers(); + } + $warmer->warmUp($warmupDir); + + // fix references to the Kernel in .meta files + $safeTempKernel = str_replace('\\', '\\\\', get_class($tempKernel)); + $realKernelFQN = get_class($realKernel); + + foreach (Finder::create()->files()->name('*.meta')->in($warmupDir) as $file) { + file_put_contents($file, preg_replace( + '/(C\:\d+\:)"'.$safeTempKernel.'"/', + sprintf('$1"%s"', $realKernelFQN), + file_get_contents($file) + )); + } + + // fix references to cached files with the real cache directory name + $search = array($warmupDir, str_replace('\\', '\\\\', $warmupDir)); + $replace = str_replace('\\', '/', $realCacheDir); + foreach (Finder::create()->files()->in($warmupDir) as $file) { + $content = str_replace($search, $replace, file_get_contents($file)); + file_put_contents($file, $content); + } + + // fix references to kernel/container related classes + $search = $tempKernel->getName().ucfirst($tempKernel->getEnvironment()); + $replace = $realKernel->getName().ucfirst($realKernel->getEnvironment()); + foreach (Finder::create()->files()->name($search.'*')->in($warmupDir) as $file) { + $content = str_replace($search, $replace, file_get_contents($file)); + file_put_contents(str_replace($search, $replace, $file), $content); + unlink($file); + } + + // remove temp kernel file after cache warmed up + @unlink($tempKernelFile); + } + + /** + * @param KernelInterface $parent + * @param string $namespace + * @param string $parentClass + * @param string $warmupDir + * + * @return KernelInterface + */ + protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) + { + $cacheDir = var_export($warmupDir, true); + $rootDir = var_export(realpath($parent->getRootDir()), true); + $logDir = var_export(realpath($parent->getLogDir()), true); + // the temp kernel class name must have the same length than the real one + // to avoid the many problems in serialized resources files + $class = substr($parentClass, 0, -1).'_'; + // the temp kernel name must be changed too + $name = var_export(substr($parent->getName(), 0, -1).'_', true); + $code = <<getResources(); + \$filteredResources = array(); + foreach (\$resources as \$resource) { + if ((string) \$resource !== __FILE__) { + \$filteredResources[] = \$resource; + } + } + + \$container->setResources(\$filteredResources); + + return \$container; + } + } +} +EOF; + $this->getContainer()->get('filesystem')->mkdir($warmupDir); + file_put_contents($file = $warmupDir.'/kernel.tmp', $code); + require_once $file; + $class = "$namespace\\$class"; + + return new $class($parent->getEnvironment(), $parent->isDebug()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php new file mode 100644 index 0000000..fdbf9d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Warmup the cache. + * + * @author Fabien Potencier + */ +class CacheWarmupCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('cache:warmup') + ->setDefinition(array( + new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'), + )) + ->setDescription('Warms up an empty cache') + ->setHelp(<<%command.name% command warms up the cache. + +Before running this command, the cache must be empty. + +This command does not generate the classes cache (as when executing this +command, too many classes that should be part of the cache are already loaded +in memory). Use curl or any other similar tool to warm up +the classes cache if you want. + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $kernel = $this->getContainer()->get('kernel'); + $io->comment(sprintf('Warming up the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + + $warmer = $this->getContainer()->get('cache_warmer'); + + if (!$input->getOption('no-optional-warmers')) { + $warmer->enableOptionalWarmers(); + } + + $warmer->warmUp($this->getContainer()->getParameter('kernel.cache_dir')); + + $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php new file mode 100644 index 0000000..93c46bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Yaml\Yaml; + +/** + * A console command for dumping available configuration reference. + * + * @author Grégoire Pineau + */ +class ConfigDebugCommand extends AbstractConfigCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('debug:config') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'The bundle name or the extension alias'), + )) + ->setDescription('Dumps the current configuration for an extension') + ->setHelp(<<%command.name% command dumps the current configuration for an +extension/bundle. + +Either the extension alias or bundle name can be used: + + php %command.full_name% framework + php %command.full_name% FrameworkBundle + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $name = $input->getArgument('name'); + + if (empty($name)) { + $io->comment('Provide the name of a bundle as the first argument of this command to dump its configuration.'); + $io->newLine(); + $this->listBundles($output); + + return; + } + + $extension = $this->findExtension($name); + $container = $this->compileContainer(); + + $configs = $container->getExtensionConfig($extension->getAlias()); + $configuration = $extension->getConfiguration($configs, $container); + + $this->validateConfiguration($extension, $configuration); + + $configs = $container->getParameterBag()->resolveValue($configs); + + $processor = new Processor(); + $config = $processor->processConfiguration($configuration, $configs); + + if ($name === $extension->getAlias()) { + $io->title(sprintf('Current configuration for extension with alias "%s"', $name)); + } else { + $io->title(sprintf('Current configuration for "%s"', $name)); + } + + $io->writeln(Yaml::dump(array($extension->getAlias() => $config), 3)); + } + + private function compileContainer() + { + $kernel = clone $this->getContainer()->get('kernel'); + $kernel->boot(); + + $method = new \ReflectionMethod($kernel, 'buildContainer'); + $method->setAccessible(true); + $container = $method->invoke($kernel); + $container->getCompiler()->compile($container); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php new file mode 100644 index 0000000..6b1d9ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; +use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * A console command for dumping available configuration reference. + * + * @author Kevin Bond + * @author Wouter J + * @author Grégoire Pineau + */ +class ConfigDumpReferenceCommand extends AbstractConfigCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('config:dump-reference') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (yaml or xml)', 'yaml'), + )) + ->setDescription('Dumps the default configuration for an extension') + ->setHelp(<<%command.name% command dumps the default configuration for an +extension/bundle. + +Either the extension alias or bundle name can be used: + + php %command.full_name% framework + php %command.full_name% FrameworkBundle + +With the --format option specifies the format of the configuration, +this is either yaml or xml. +When the option is not provided, yaml is used. + + php %command.full_name% FrameworkBundle --format=xml + +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $name = $input->getArgument('name'); + + if (empty($name)) { + $io->comment('Provide the name of a bundle as the first argument of this command to dump its default configuration.'); + $io->newLine(); + $this->listBundles($output); + + return; + } + + $extension = $this->findExtension($name); + + $configuration = $extension->getConfiguration(array(), $this->getContainerBuilder()); + + $this->validateConfiguration($extension, $configuration); + + if ($name === $extension->getAlias()) { + $message = sprintf('Default configuration for extension with alias: "%s"', $name); + } else { + $message = sprintf('Default configuration for "%s"', $name); + } + + switch ($input->getOption('format')) { + case 'yaml': + $io->writeln(sprintf('# %s', $message)); + $dumper = new YamlReferenceDumper(); + break; + case 'xml': + $io->writeln(sprintf('', $message)); + $dumper = new XmlReferenceDumper(); + break; + default: + $io->writeln($message); + throw new \InvalidArgumentException('Only the yaml and xml formats are supported.'); + } + + $io->writeln($dumper->dump($configuration, $extension->getNamespace())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php new file mode 100644 index 0000000..218f816 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; + +/** + * Command. + * + * @author Fabien Potencier + */ +abstract class ContainerAwareCommand extends Command implements ContainerAwareInterface +{ + /** + * @var ContainerInterface|null + */ + private $container; + + /** + * @return ContainerInterface + * + * @throws \LogicException + */ + protected function getContainer() + { + if (null === $this->container) { + $application = $this->getApplication(); + if (null === $application) { + throw new \LogicException('The container cannot be retrieved as the application instance is not yet set.'); + } + + $this->container = $application->getKernel()->getContainer(); + } + + return $this->container; + } + + /** + * {@inheritdoc} + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php new file mode 100644 index 0000000..b8413a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +/** + * A console command for retrieving information about services. + * + * @author Ryan Weaver + */ +class ContainerDebugCommand extends ContainerAwareCommand +{ + /** + * @var ContainerBuilder|null + */ + protected $containerBuilder; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('debug:container') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo)'), + new InputOption('show-private', null, InputOption::VALUE_NONE, 'Used to show public *and* private services'), + new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Shows all services with a specific tag'), + new InputOption('tags', null, InputOption::VALUE_NONE, 'Displays tagged services for an application'), + new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Displays a specific parameter for an application'), + new InputOption('parameters', null, InputOption::VALUE_NONE, 'Displays parameters for an application'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), + )) + ->setDescription('Displays current services for an application') + ->setHelp(<<%command.name% command displays all configured public services: + + php %command.full_name% + +To get specific information about a service, specify its name: + + php %command.full_name% validator + +By default, private services are hidden. You can display all services by +using the --show-private flag: + + php %command.full_name% --show-private + +Use the --tags option to display tagged public services grouped by tag: + + php %command.full_name% --tags + +Find all services with a specific tag by specifying the tag name with the --tag option: + + php %command.full_name% --tag=form.type + +Use the --parameters option to display all parameters: + + php %command.full_name% --parameters + +Display a specific parameter by specifying his name with the --parameter option: + + php %command.full_name% --parameter=kernel.debug + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $this->validateInput($input); + + if ($input->getOption('parameters')) { + $object = $this->getContainerBuilder()->getParameterBag(); + $options = array(); + } elseif ($parameter = $input->getOption('parameter')) { + $object = $this->getContainerBuilder(); + $options = array('parameter' => $parameter); + } elseif ($input->getOption('tags')) { + $object = $this->getContainerBuilder(); + $options = array('group_by' => 'tags', 'show_private' => $input->getOption('show-private')); + } elseif ($tag = $input->getOption('tag')) { + $object = $this->getContainerBuilder(); + $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private')); + } elseif ($name = $input->getArgument('name')) { + $object = $this->getContainerBuilder(); + $name = $this->findProperServiceName($input, $io, $object, $name); + $options = array('id' => $name); + } else { + $object = $this->getContainerBuilder(); + $options = array('show_private' => $input->getOption('show-private')); + } + + $helper = new DescriptorHelper(); + $options['format'] = $input->getOption('format'); + $options['raw_text'] = $input->getOption('raw'); + $options['output'] = $io; + $helper->describe($output, $object, $options); + + if (!$input->getArgument('name') && $input->isInteractive()) { + $io->comment('To search for a specific service, re-run this command with a search term. (e.g. debug:container log)'); + } + } + + /** + * Validates input arguments and options. + * + * @param InputInterface $input + * + * @throws \InvalidArgumentException + */ + protected function validateInput(InputInterface $input) + { + $options = array('tags', 'tag', 'parameters', 'parameter'); + + $optionsCount = 0; + foreach ($options as $option) { + if ($input->getOption($option)) { + ++$optionsCount; + } + } + + $name = $input->getArgument('name'); + if ((null !== $name) && ($optionsCount > 0)) { + throw new \InvalidArgumentException('The options tags, tag, parameters & parameter can not be combined with the service name argument.'); + } elseif ((null === $name) && $optionsCount > 1) { + throw new \InvalidArgumentException('The options tags, tag, parameters & parameter can not be combined together.'); + } + } + + /** + * Loads the ContainerBuilder from the cache. + * + * @return ContainerBuilder + * + * @throws \LogicException + */ + protected function getContainerBuilder() + { + if ($this->containerBuilder) { + return $this->containerBuilder; + } + + if (!$this->getApplication()->getKernel()->isDebug()) { + throw new \LogicException(sprintf('Debug information about the container is only available in debug mode.')); + } + + if (!is_file($cachedFile = $this->getContainer()->getParameter('debug.container.dump'))) { + throw new \LogicException(sprintf('Debug information about the container could not be found. Please clear the cache and try again.')); + } + + $container = new ContainerBuilder(); + + $loader = new XmlFileLoader($container, new FileLocator()); + $loader->load($cachedFile); + + return $this->containerBuilder = $container; + } + + private function findProperServiceName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $builder, $name) + { + if ($builder->has($name) || !$input->isInteractive()) { + return $name; + } + + $matchingServices = $this->findServiceIdsContaining($builder, $name); + if (empty($matchingServices)) { + throw new \InvalidArgumentException(sprintf('No services found that match "%s".', $name)); + } + + return $io->choice('Select one of the following services to display its information', $matchingServices); + } + + private function findServiceIdsContaining(ContainerBuilder $builder, $name) + { + $serviceIds = $builder->getServiceIds(); + $foundServiceIds = array(); + $name = strtolower($name); + foreach ($serviceIds as $serviceId) { + if (false === strpos($serviceId, $name)) { + continue; + } + $foundServiceIds[] = $serviceId; + } + + return $foundServiceIds; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php new file mode 100644 index 0000000..9732537 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * A console command for retrieving information about event dispatcher. + * + * @author Matthieu Auger + */ +class EventDispatcherDebugCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('debug:event-dispatcher') + ->setDefinition(array( + new InputArgument('event', InputArgument::OPTIONAL, 'An event name'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), + )) + ->setDescription('Displays configured listeners for an application') + ->setHelp(<<%command.name% command displays all configured listeners: + + php %command.full_name% + +To get specific listeners for an event, specify its name: + + php %command.full_name% kernel.request +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $dispatcher = $this->getEventDispatcher(); + + $options = array(); + if ($event = $input->getArgument('event')) { + if (!$dispatcher->hasListeners($event)) { + $io->warning(sprintf('The event "%s" does not have any registered listeners.', $event)); + + return; + } + + $options = array('event' => $event); + } + + $helper = new DescriptorHelper(); + $options['format'] = $input->getOption('format'); + $options['raw_text'] = $input->getOption('raw'); + $options['output'] = $io; + $helper->describe($io, $dispatcher, $options); + } + + /** + * Loads the Event Dispatcher from the container. + * + * @return EventDispatcherInterface + */ + protected function getEventDispatcher() + { + return $this->getContainer()->get('event_dispatcher'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php new file mode 100644 index 0000000..d8c97a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Routing\Route; + +/** + * A console command for retrieving information about routes. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class RouterDebugCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (!$this->getContainer()->has('router')) { + return false; + } + $router = $this->getContainer()->get('router'); + if (!$router instanceof RouterInterface) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('debug:router') + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A route name'), + new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'), + )) + ->setDescription('Displays current routes for an application') + ->setHelp(<<%command.name% displays the configured routes: + + php %command.full_name% + +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException When route does not exist + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $name = $input->getArgument('name'); + $helper = new DescriptorHelper(); + + if ($name) { + $route = $this->getContainer()->get('router')->getRouteCollection()->get($name); + if (!$route) { + throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name)); + } + + $this->convertController($route); + + $helper->describe($io, $route, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'name' => $name, + 'output' => $io, + )); + } else { + $routes = $this->getContainer()->get('router')->getRouteCollection(); + + foreach ($routes as $route) { + $this->convertController($route); + } + + $helper->describe($io, $routes, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'show_controllers' => $input->getOption('show-controllers'), + 'output' => $io, + )); + } + } + + private function convertController(Route $route) + { + $nameParser = $this->getContainer()->get('controller_name_converter'); + if ($route->hasDefault('_controller')) { + try { + $route->setDefault('_controller', $nameParser->build($route->getDefault('_controller'))); + } catch (\InvalidArgumentException $e) { + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php new file mode 100644 index 0000000..7b17e25 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; + +/** + * A console command to test route matching. + * + * @author Fabien Potencier + */ +class RouterMatchCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (!$this->getContainer()->has('router')) { + return false; + } + $router = $this->getContainer()->get('router'); + if (!$router instanceof RouterInterface) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('router:match') + ->setDefinition(array( + new InputArgument('path_info', InputArgument::REQUIRED, 'A path info'), + new InputOption('method', null, InputOption::VALUE_REQUIRED, 'Sets the HTTP method'), + new InputOption('scheme', null, InputOption::VALUE_REQUIRED, 'Sets the URI scheme (usually http or https)'), + new InputOption('host', null, InputOption::VALUE_REQUIRED, 'Sets the URI host'), + )) + ->setDescription('Helps debug routes by simulating a path info match') + ->setHelp(<<%command.name% shows which routes match a given request and which don't and for what reason: + + php %command.full_name% /foo + +or + + php %command.full_name% /foo --method POST --scheme https --host symfony.com --verbose + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $router = $this->getContainer()->get('router'); + $context = $router->getContext(); + if (null !== $method = $input->getOption('method')) { + $context->setMethod($method); + } + if (null !== $scheme = $input->getOption('scheme')) { + $context->setScheme($scheme); + } + if (null !== $host = $input->getOption('host')) { + $context->setHost($host); + } + + $matcher = new TraceableUrlMatcher($router->getRouteCollection(), $context); + + $traces = $matcher->getTraces($input->getArgument('path_info')); + + $io->newLine(); + + $matches = false; + foreach ($traces as $trace) { + if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) { + $io->text(sprintf('Route "%s" almost matches but %s', $trace['name'], lcfirst($trace['log']))); + } elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) { + $io->success(sprintf('Route "%s" matches', $trace['name'])); + + $routerDebugCommand = $this->getApplication()->find('debug:router'); + $routerDebugCommand->run(new ArrayInput(array('name' => $trace['name'])), $output); + + $matches = true; + } elseif ($input->getOption('verbose')) { + $io->text(sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log'])); + } + } + + if (!$matches) { + $io->error(sprintf('None of the routes match the path "%s"', $input->getArgument('path_info'))); + + return 1; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php new file mode 100644 index 0000000..ccfa5dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +/** + * Base methods for commands related to PHP's built-in web server. + * + * @author Christian Flothmann + */ +abstract class ServerCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (defined('HHVM_VERSION')) { + return false; + } + + if (!class_exists('Symfony\Component\Process\Process')) { + return false; + } + + return parent::isEnabled(); + } + + /** + * Determines the name of the lock file for a particular PHP web server process. + * + * @param string $address An address/port tuple + * + * @return string The filename + */ + protected function getLockFile($address) + { + return sys_get_temp_dir().'/'.strtr($address, '.:', '--').'.pid'; + } + + protected function isOtherServerProcessRunning($address) + { + $lockFile = $this->getLockFile($address); + + if (file_exists($lockFile)) { + return true; + } + + list($hostname, $port) = explode(':', $address); + + $fp = @fsockopen($hostname, $port, $errno, $errstr, 5); + + if (false !== $fp) { + fclose($fp); + + return true; + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php new file mode 100644 index 0000000..9be88e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerRunCommand.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Process\PhpExecutableFinder; +use Symfony\Component\Process\ProcessBuilder; + +/** + * Runs Symfony application using PHP built-in web server. + * + * @author Michał Pipa + */ +class ServerRunCommand extends ServerCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), + new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), + new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), + )) + ->setName('server:run') + ->setDescription('Runs PHP built-in web server') + ->setHelp(<<%command.name% runs PHP built-in web server: + + %command.full_name% + +To change default bind address and port use the address argument: + + %command.full_name% 127.0.0.1:8080 + +To change default docroot directory use the --docroot option: + + %command.full_name% --docroot=htdocs/ + +If you have custom docroot directory layout, you can specify your own +router script using --router option: + + %command.full_name% --router=app/config/router.php + +Specifing a router script is required when the used environment is not "dev", +"prod", or "test". + +See also: http://www.php.net/manual/en/features.commandline.webserver.php + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $documentRoot = $input->getOption('docroot'); + + if (null === $documentRoot) { + $documentRoot = $this->getContainer()->getParameter('kernel.root_dir').'/../web'; + } + + if (!is_dir($documentRoot)) { + $io->error(sprintf('The given document root directory "%s" does not exist', $documentRoot)); + + return 1; + } + + $env = $this->getContainer()->getParameter('kernel.environment'); + $address = $input->getArgument('address'); + + if (false === strpos($address, ':')) { + $address = $address.':'.$input->getOption('port'); + } + + if ($this->isOtherServerProcessRunning($address)) { + $io->error(sprintf('A process is already listening on http://%s.', $address)); + + return 1; + } + + if ('prod' === $env) { + $io->error('Running PHP built-in server in production environment is NOT recommended!'); + } + + $io->success(sprintf('Server running on http://%s', $address)); + $io->comment('Quit the server with CONTROL-C.'); + + if (null === $builder = $this->createPhpProcessBuilder($io, $address, $input->getOption('router'), $env)) { + return 1; + } + + $builder->setWorkingDirectory($documentRoot); + $builder->setTimeout(null); + $process = $builder->getProcess(); + + if (OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) { + $process->disableOutput(); + } + + $this + ->getHelper('process') + ->run($output, $process, null, null, OutputInterface::VERBOSITY_VERBOSE); + + if (!$process->isSuccessful()) { + $errorMessages = array('Built-in server terminated unexpectedly.'); + + if ($process->isOutputDisabled()) { + $errorMessages[] = 'Run the command again with -v option for more details.'; + } + + $io->error($errorMessages); + } + + return $process->getExitCode(); + } + + private function createPhpProcessBuilder(SymfonyStyle $io, $address, $router, $env) + { + $router = $router ?: $this + ->getContainer() + ->get('kernel') + ->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env)) + ; + + if (!file_exists($router)) { + $io->error(sprintf('The given router script "%s" does not exist.', $router)); + + return; + } + + $router = realpath($router); + $finder = new PhpExecutableFinder(); + + if (false === $binary = $finder->find()) { + $io->error('Unable to find PHP binary to run server.'); + + return; + } + + return new ProcessBuilder(array($binary, '-S', $address, $router)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php new file mode 100644 index 0000000..fdec777 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStartCommand.php @@ -0,0 +1,234 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Process\PhpExecutableFinder; +use Symfony\Component\Process\Process; + +/** + * Runs PHP's built-in web server in a background process. + * + * @author Christian Flothmann + */ +class ServerStartCommand extends ServerCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), + new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null), + new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'), + new InputOption('force', 'f', InputOption::VALUE_NONE, 'Force web server startup'), + )) + ->setName('server:start') + ->setDescription('Starts PHP built-in web server in the background') + ->setHelp(<<%command.name% runs PHP's built-in web server: + + php %command.full_name% + +To change the default bind address and the default port use the address argument: + + php %command.full_name% 127.0.0.1:8080 + +To change the default document root directory use the --docroot option: + + php %command.full_name% --docroot=htdocs/ + +If you have a custom document root directory layout, you can specify your own +router script using the --router option: + + php %command.full_name% --router=app/config/router.php + +Specifying a router script is required when the used environment is not "dev" or +"prod". + +See also: http://www.php.net/manual/en/features.commandline.webserver.php + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $cliOutput = $output); + + if (!extension_loaded('pcntl')) { + $io->error(array( + 'This command needs the pcntl extension to run.', + 'You can either install it or use the "server:run" command instead to run the built-in web server.', + )); + + if ($io->ask('Do you want to execute server:run immediately? [Yn] ', true)) { + $command = $this->getApplication()->find('server:run'); + + return $command->run($input, $cliOutput); + } + + return 1; + } + + $documentRoot = $input->getOption('docroot'); + + if (null === $documentRoot) { + $documentRoot = $this->getContainer()->getParameter('kernel.root_dir').'/../web'; + } + + if (!is_dir($documentRoot)) { + $io->error(sprintf('The given document root directory "%s" does not exist.', $documentRoot)); + + return 1; + } + + $env = $this->getContainer()->getParameter('kernel.environment'); + + if (false === $router = $this->determineRouterScript($input->getOption('router'), $env, $io)) { + return 1; + } + + $address = $input->getArgument('address'); + + if (false === strpos($address, ':')) { + $address = $address.':'.$input->getOption('port'); + } + + if (!$input->getOption('force') && $this->isOtherServerProcessRunning($address)) { + $io->error(array( + sprintf('A process is already listening on http://%s.', $address), + 'Use the --force option if the server process terminated unexpectedly to start a new web server process.', + )); + + return 1; + } + + if ('prod' === $env) { + $io->error('Running PHP built-in server in production environment is NOT recommended!'); + } + + $pid = pcntl_fork(); + + if ($pid < 0) { + $io->error('Unable to start the server process.'); + + return 1; + } + + if ($pid > 0) { + $io->success(sprintf('Web server listening on http://%s', $address)); + + return; + } + + if (posix_setsid() < 0) { + $io->error('Unable to set the child process as session leader'); + + return 1; + } + + if (null === $process = $this->createServerProcess($io, $address, $documentRoot, $router)) { + return 1; + } + + $process->disableOutput(); + $process->start(); + $lockFile = $this->getLockFile($address); + touch($lockFile); + + if (!$process->isRunning()) { + $io->error('Unable to start the server process'); + unlink($lockFile); + + return 1; + } + + // stop the web server when the lock file is removed + while ($process->isRunning()) { + if (!file_exists($lockFile)) { + $process->stop(); + } + + sleep(1); + } + } + + /** + * Determine the absolute file path for the router script, using the environment to choose a standard script + * if no custom router script is specified. + * + * @param string|null $router File path of the custom router script, if set by the user; otherwise null + * @param string $env The application environment + * @param SymfonyStyle $io An SymfonyStyle instance + * + * @return string|bool The absolute file path of the router script, or false on failure + */ + private function determineRouterScript($router, $env, SymfonyStyle $io) + { + if (null === $router) { + $router = $this + ->getContainer() + ->get('kernel') + ->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env)) + ; + } + + if (false === $path = realpath($router)) { + $io->error(sprintf('The given router script "%s" does not exist.', $router)); + + return false; + } + + return $path; + } + + /** + * Creates a process to start PHP's built-in web server. + * + * @param SymfonyStyle $io A SymfonyStyle instance + * @param string $address IP address and port to listen to + * @param string $documentRoot The application's document root + * @param string $router The router filename + * + * @return Process The process + */ + private function createServerProcess(SymfonyStyle $io, $address, $documentRoot, $router) + { + $finder = new PhpExecutableFinder(); + if (false === $binary = $finder->find()) { + $io->error('Unable to find PHP binary to start server.'); + + return; + } + + $script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), array( + $binary, + '-S', + $address, + $router, + ))); + + return new Process('exec '.$script, $documentRoot, null, null, null); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php new file mode 100644 index 0000000..b6ec558 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStatusCommand.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Shows the status of a process that is running PHP's built-in web server in + * the background. + * + * @author Christian Flothmann + */ +class ServerStatusCommand extends ServerCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'), + )) + ->setName('server:status') + ->setDescription('Outputs the status of the built-in web server for the given address') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $address = $input->getArgument('address'); + + // remove an orphaned lock file + if (file_exists($this->getLockFile($address)) && !$this->isServerRunning($address)) { + unlink($this->getLockFile($address)); + } + + if (file_exists($this->getLockFile($address))) { + $io->success(sprintf('Web server still listening on http://%s', $address)); + } else { + $io->warning(sprintf('No web server is listening on http://%s', $address)); + } + } + + private function isServerRunning($address) + { + list($hostname, $port) = explode(':', $address); + + if (false !== $fp = @fsockopen($hostname, $port, $errno, $errstr, 1)) { + fclose($fp); + + return true; + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php new file mode 100644 index 0000000..9ce6c3b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ServerStopCommand.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Stops a background process running PHP's built-in web server. + * + * @author Christian Flothmann + */ +class ServerStopCommand extends ServerCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1'), + new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'), + )) + ->setName('server:stop') + ->setDescription('Stops PHP\'s built-in web server that was started with the server:start command') + ->setHelp(<<%command.name% stops PHP's built-in web server: + + php %command.full_name% + +To change the default bind address and the default port use the address argument: + + php %command.full_name% 127.0.0.1:8080 + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $address = $input->getArgument('address'); + if (false === strpos($address, ':')) { + $address = $address.':'.$input->getOption('port'); + } + + $lockFile = $this->getLockFile($address); + + if (!file_exists($lockFile)) { + $io->error(sprintf('No web server is listening on http://%s', $address)); + + return 1; + } + + unlink($lockFile); + $io->success(sprintf('Stopped the web server listening on http://%s', $address)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php new file mode 100644 index 0000000..138964e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -0,0 +1,317 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Translator; + +/** + * Helps finding unused or missing translation messages in a given locale + * and comparing them with the fallback ones. + * + * @author Florian Voutzinos + */ +class TranslationDebugCommand extends ContainerAwareCommand +{ + const MESSAGE_MISSING = 0; + const MESSAGE_UNUSED = 1; + const MESSAGE_EQUALS_FALLBACK = 2; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('debug:translation') + ->setDefinition(array( + new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), + new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages, defaults to app/Resources folder'), + new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'), + new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Displays only missing messages'), + new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages'), + new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'), + )) + ->setDescription('Displays translation messages information') + ->setHelp(<<%command.name% command helps finding unused or missing translation +messages and comparing them with the fallback ones by inspecting the +templates and translation files of a given bundle or the app folder. + +You can display information about bundle translations in a specific locale: + + php %command.full_name% en AcmeDemoBundle + +You can also specify a translation domain for the search: + + php %command.full_name% --domain=messages en AcmeDemoBundle + +You can only display missing messages: + + php %command.full_name% --only-missing en AcmeDemoBundle + +You can only display unused messages: + + php %command.full_name% --only-unused en AcmeDemoBundle + +You can display information about app translations in a specific locale: + + php %command.full_name% en + +You can display information about translations in all registered bundles in a specific locale: + + php %command.full_name% --all en + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $locale = $input->getArgument('locale'); + $domain = $input->getOption('domain'); + /** @var TranslationLoader $loader */ + $loader = $this->getContainer()->get('translation.loader'); + /** @var Kernel $kernel */ + $kernel = $this->getContainer()->get('kernel'); + + // Define Root Path to App folder + $transPaths = array($kernel->getRootDir().'/Resources/'); + + // Override with provided Bundle info + if (null !== $input->getArgument('bundle')) { + try { + $bundle = $kernel->getBundle($input->getArgument('bundle')); + $transPaths = array( + $bundle->getPath().'/Resources/', + sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName()), + ); + } catch (\InvalidArgumentException $e) { + // such a bundle does not exist, so treat the argument as path + $transPaths = array($input->getArgument('bundle').'/Resources/'); + + if (!is_dir($transPaths[0])) { + throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + } + } + } elseif ($input->getOption('all')) { + foreach ($kernel->getBundles() as $bundle) { + $transPaths[] = $bundle->getPath().'/Resources/'; + $transPaths[] = sprintf('%s/Resources/%s/', $kernel->getRootDir(), $bundle->getName()); + } + } + + // Extract used messages + $extractedCatalogue = $this->extractMessages($locale, $transPaths); + + // Load defined messages + $currentCatalogue = $this->loadCurrentMessages($locale, $transPaths, $loader); + + // Merge defined and extracted messages to get all message ids + $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); + $allMessages = $mergeOperation->getResult()->all($domain); + if (null !== $domain) { + $allMessages = array($domain => $allMessages); + } + + // No defined or extracted messages + if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { + $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); + + if (null !== $domain) { + $outputMessage .= sprintf(' and domain "%s"', $domain); + } + + $io->warning($outputMessage); + + return; + } + + // Load the fallback catalogues + $fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths, $loader); + + // Display header line + $headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)); + foreach ($fallbackCatalogues as $fallbackCatalogue) { + $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); + } + $rows = array(); + // Iterate all message ids and determine their state + foreach ($allMessages as $domain => $messages) { + foreach (array_keys($messages) as $messageId) { + $value = $currentCatalogue->get($messageId, $domain); + $states = array(); + + if ($extractedCatalogue->defines($messageId, $domain)) { + if (!$currentCatalogue->defines($messageId, $domain)) { + $states[] = self::MESSAGE_MISSING; + } + } elseif ($currentCatalogue->defines($messageId, $domain)) { + $states[] = self::MESSAGE_UNUSED; + } + + if (!in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') + || !in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { + continue; + } + + foreach ($fallbackCatalogues as $fallbackCatalogue) { + if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) { + $states[] = self::MESSAGE_EQUALS_FALLBACK; + + break; + } + } + + $row = array($this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value)); + foreach ($fallbackCatalogues as $fallbackCatalogue) { + $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); + } + + $rows[] = $row; + } + } + + $io->table($headers, $rows); + } + + private function formatState($state) + { + if (self::MESSAGE_MISSING === $state) { + return ' missing '; + } + + if (self::MESSAGE_UNUSED === $state) { + return ' unused '; + } + + if (self::MESSAGE_EQUALS_FALLBACK === $state) { + return ' fallback '; + } + + return $state; + } + + private function formatStates(array $states) + { + $result = array(); + foreach ($states as $state) { + $result[] = $this->formatState($state); + } + + return implode(' ', $result); + } + + private function formatId($id) + { + return sprintf('%s', $id); + } + + private function sanitizeString($string, $length = 40) + { + $string = trim(preg_replace('/\s+/', ' ', $string)); + + if (false !== $encoding = mb_detect_encoding($string, null, true)) { + if (mb_strlen($string, $encoding) > $length) { + return mb_substr($string, 0, $length - 3, $encoding).'...'; + } + } elseif (strlen($string) > $length) { + return substr($string, 0, $length - 3).'...'; + } + + return $string; + } + + /** + * @param string $locale + * @param array $transPaths + * + * @return MessageCatalogue + */ + private function extractMessages($locale, $transPaths) + { + $extractedCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + $path = $path.'views'; + if (is_dir($path)) { + $this->getContainer()->get('translation.extractor')->extract($path, $extractedCatalogue); + } + } + + return $extractedCatalogue; + } + + /** + * @param string $locale + * @param array $transPaths + * @param TranslationLoader $loader + * + * @return MessageCatalogue + */ + private function loadCurrentMessages($locale, $transPaths, TranslationLoader $loader) + { + $currentCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + $path = $path.'translations'; + if (is_dir($path)) { + $loader->loadMessages($path, $currentCatalogue); + } + } + + return $currentCatalogue; + } + + /** + * @param string $locale + * @param array $transPaths + * @param TranslationLoader $loader + * + * @return MessageCatalogue[] + */ + private function loadFallbackCatalogues($locale, $transPaths, TranslationLoader $loader) + { + $fallbackCatalogues = array(); + $translator = $this->getContainer()->get('translator'); + if ($translator instanceof Translator) { + foreach ($translator->getFallbackLocales() as $fallbackLocale) { + if ($fallbackLocale === $locale) { + continue; + } + + $fallbackCatalogue = new MessageCatalogue($fallbackLocale); + foreach ($transPaths as $path) { + $path = $path.'translations'; + if (is_dir($path)) { + $loader->loadMessages($path, $fallbackCatalogue); + } + } + $fallbackCatalogues[] = $fallbackCatalogue; + } + } + + return $fallbackCatalogues; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php new file mode 100644 index 0000000..01678dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -0,0 +1,215 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Translation\Catalogue\TargetOperation; +use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * A command that parses templates to extract translation messages and adds them + * into the translation files. + * + * @author Michel Salib + */ +class TranslationUpdateCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('translation:update') + ->setDefinition(array( + new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), + new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages, defaults to app/Resources folder'), + new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'), + new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'yml'), + new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'), + new InputOption('force', null, InputOption::VALUE_NONE, 'Should the update be done'), + new InputOption('no-backup', null, InputOption::VALUE_NONE, 'Should backup be disabled'), + new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'), + )) + ->setDescription('Updates the translation file') + ->setHelp(<<%command.name% command extract translation strings from templates +of a given bundle or the app folder. It can display them or merge the new ones into the translation files. +When new translation strings are found it can automatically add a prefix to the translation +message. + +Example running against a Bundle (AcmeBundle) + php %command.full_name% --dump-messages en AcmeBundle + php %command.full_name% --force --prefix="new_" fr AcmeBundle + +Example running against app messages (app/Resources folder) + php %command.full_name% --dump-messages en + php %command.full_name% --force --prefix="new_" fr +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + // check presence of force or dump-message + if ($input->getOption('force') !== true && $input->getOption('dump-messages') !== true) { + $io->error('You must choose one of --force or --dump-messages'); + + return 1; + } + + // check format + $writer = $this->getContainer()->get('translation.writer'); + $supportedFormats = $writer->getFormats(); + if (!in_array($input->getOption('output-format'), $supportedFormats)) { + $io->error(array('Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.')); + + return 1; + } + $kernel = $this->getContainer()->get('kernel'); + + // Define Root Path to App folder + $transPaths = array($kernel->getRootDir().'/Resources/'); + $currentName = 'app folder'; + + // Override with provided Bundle info + if (null !== $input->getArgument('bundle')) { + try { + $foundBundle = $kernel->getBundle($input->getArgument('bundle')); + $transPaths = array( + $foundBundle->getPath().'/Resources/', + sprintf('%s/Resources/%s/', $kernel->getRootDir(), $foundBundle->getName()), + ); + $currentName = $foundBundle->getName(); + } catch (\InvalidArgumentException $e) { + // such a bundle does not exist, so treat the argument as path + $transPaths = array($input->getArgument('bundle').'/Resources/'); + $currentName = $transPaths[0]; + + if (!is_dir($transPaths[0])) { + throw new \InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + } + } + } + + $io->title('Translation Messages Extractor and Dumper'); + $io->comment(sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); + + // load any messages from templates + $extractedCatalogue = new MessageCatalogue($input->getArgument('locale')); + $io->comment('Parsing templates...'); + $extractor = $this->getContainer()->get('translation.extractor'); + $extractor->setPrefix($input->getOption('prefix')); + foreach ($transPaths as $path) { + $path .= 'views'; + if (is_dir($path)) { + $extractor->extract($path, $extractedCatalogue); + } + } + + // load any existing messages from the translation files + $currentCatalogue = new MessageCatalogue($input->getArgument('locale')); + $io->comment('Loading translation files...'); + $loader = $this->getContainer()->get('translation.loader'); + foreach ($transPaths as $path) { + $path .= 'translations'; + if (is_dir($path)) { + $loader->loadMessages($path, $currentCatalogue); + } + } + + // process catalogues + $operation = $input->getOption('clean') + ? new TargetOperation($currentCatalogue, $extractedCatalogue) + : new MergeOperation($currentCatalogue, $extractedCatalogue); + + // Exit if no messages found. + if (!count($operation->getDomains())) { + $io->warning('No translation messages were found.'); + + return; + } + + // show compiled list of messages + if (true === $input->getOption('dump-messages')) { + $extractedMessagesCount = 0; + $io->newLine(); + foreach ($operation->getDomains() as $domain) { + $newKeys = array_keys($operation->getNewMessages($domain)); + $allKeys = array_keys($operation->getMessages($domain)); + $domainMessagesCount = count($newKeys) + count($allKeys); + + $io->section(sprintf('Messages extracted for domain "%s" (%d messages)', $domain, $domainMessagesCount)); + + $io->listing(array_merge( + array_diff($allKeys, $newKeys), + array_map(function ($id) { + return sprintf('%s', $id); + }, $newKeys), + array_map(function ($id) { + return sprintf('%s', $id); + }, array_keys($operation->getObsoleteMessages($domain))) + )); + + $extractedMessagesCount += $domainMessagesCount; + } + + if ($input->getOption('output-format') == 'xlf') { + $io->comment('Xliff output version is 1.2'); + } + + $resultMessage = sprintf('%d messages were successfully extracted', $extractedMessagesCount); + } + + if ($input->getOption('no-backup') === true) { + $writer->disableBackup(); + } + + // save the files + if ($input->getOption('force') === true) { + $io->comment('Writing files...'); + + $bundleTransPath = false; + foreach ($transPaths as $path) { + $path .= 'translations'; + if (is_dir($path)) { + $bundleTransPath = $path; + } + } + + if (!$bundleTransPath) { + $bundleTransPath = end($transPaths).'translations'; + } + + $writer->writeTranslations($operation->getResult(), $input->getOption('output-format'), array('path' => $bundleTransPath, 'default_locale' => $this->getContainer()->getParameter('kernel.default_locale'))); + + if (true === $input->getOption('dump-messages')) { + $resultMessage .= ' and translation files were updated.'; + } else { + $resultMessage = 'Translation files were successfully updated.'; + } + } + + $io->success($resultMessage); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php new file mode 100644 index 0000000..eb35084 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser; + +/** + * Validates YAML files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + */ +class YamlLintCommand extends Command +{ + protected function configure() + { + $this + ->setName('lint:yaml') + ->setDescription('Lints a file and outputs encountered errors') + ->addArgument('filename', null, 'A file or a directory or STDIN') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt') + ->setHelp(<<%command.name% command lints a YAML file and outputs to STDOUT +the first encountered syntax error. + +You can validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +Or all YAML files in a bundle: + + php %command.full_name% @AcmeDemoBundle + +You can also pass the YAML contents from STDIN: + + cat filename | php %command.full_name% + +EOF + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $filename = $input->getArgument('filename'); + + if (!$filename) { + if (0 !== ftell(STDIN)) { + throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.'); + } + + $content = ''; + while (!feof(STDIN)) { + $content .= fread(STDIN, 1024); + } + + return $this->display($input, $output, $io, array($this->validate($content))); + } + + if (0 !== strpos($filename, '@') && !is_readable($filename)) { + throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename)); + } + + $files = array(); + if (is_file($filename)) { + $files = array($filename); + } elseif (is_dir($filename)) { + $files = Finder::create()->files()->in($filename)->name('*.yml'); + } else { + $dir = $this->getApplication()->getKernel()->locateResource($filename); + $files = Finder::create()->files()->in($dir)->name('*.yml'); + } + + $filesInfo = array(); + foreach ($files as $file) { + $filesInfo[] = $this->validate(file_get_contents($file), $file); + } + + return $this->display($input, $output, $io, $filesInfo); + } + + private function validate($content, $file = null) + { + $parser = new Parser(); + try { + $parser->parse($content); + } catch (ParseException $e) { + return array('file' => $file, 'valid' => false, 'message' => $e->getMessage()); + } + + return array('file' => $file, 'valid' => true); + } + + private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, $files) + { + switch ($input->getOption('format')) { + case 'txt': + return $this->displayTxt($output, $io, $files); + case 'json': + return $this->displayJson($io, $files); + default: + throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format'))); + } + } + + private function displayTxt(OutputInterface $output, SymfonyStyle $io, $filesInfo) + { + $errors = 0; + + foreach ($filesInfo as $info) { + if ($info['valid'] && $output->isVerbose()) { + $io->comment('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + ++$errors; + $io->text(sprintf(' ERROR in %s', $info['file'])); + $io->text(sprintf(' >> %s', $info['message'])); + } + } + + if ($errors === 0) { + $io->success(sprintf('All %d YAML files contain valid syntax.', count($filesInfo))); + } else { + $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', count($filesInfo) - $errors, $errors)); + } + + return min($errors, 1); + } + + private function displayJson(OutputInterface $output, $filesInfo) + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + if (!$v['valid']) { + ++$errors; + } + }); + + $output->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT)); + + return min($errors, 1); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php new file mode 100644 index 0000000..3ff574b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\Console\Application as BaseApplication; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Application. + * + * @author Fabien Potencier + */ +class Application extends BaseApplication +{ + private $kernel; + private $commandsRegistered = false; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + + parent::__construct('Symfony', Kernel::VERSION.' - '.$kernel->getName().'/'.$kernel->getEnvironment().($kernel->isDebug() ? '/debug' : '')); + + $this->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', $kernel->getEnvironment())); + $this->getDefinition()->addOption(new InputOption('--no-debug', null, InputOption::VALUE_NONE, 'Switches off debug mode.')); + } + + /** + * Gets the Kernel associated with this Console. + * + * @return KernelInterface A KernelInterface instance + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + $this->kernel->boot(); + + if (!$this->commandsRegistered) { + $this->registerCommands(); + + $this->commandsRegistered = true; + } + + $container = $this->kernel->getContainer(); + + foreach ($this->all() as $command) { + if ($command instanceof ContainerAwareInterface) { + $command->setContainer($container); + } + } + + $this->setDispatcher($container->get('event_dispatcher')); + + return parent::doRun($input, $output); + } + + protected function registerCommands() + { + $container = $this->kernel->getContainer(); + + foreach ($this->kernel->getBundles() as $bundle) { + if ($bundle instanceof Bundle) { + $bundle->registerCommands($this); + } + } + + if ($container->hasParameter('console.command.ids')) { + foreach ($container->getParameter('console.command.ids') as $id) { + $this->add($container->get($id)); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php new file mode 100644 index 0000000..dde5c14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -0,0 +1,308 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof RouteCollection: + $this->describeRouteCollection($object, $options); + break; + case $object instanceof Route: + $this->describeRoute($object, $options); + break; + case $object instanceof ParameterBag: + $this->describeContainerParameters($object, $options); + break; + case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']: + $this->describeContainerTags($object, $options); + break; + case $object instanceof ContainerBuilder && isset($options['id']): + $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options); + break; + case $object instanceof ContainerBuilder && isset($options['parameter']): + $this->describeContainerParameter($object->getParameter($options['parameter']), $options); + break; + case $object instanceof ContainerBuilder: + $this->describeContainerServices($object, $options); + break; + case $object instanceof Definition: + $this->describeContainerDefinition($object, $options); + break; + case $object instanceof Alias: + $this->describeContainerAlias($object, $options); + break; + case $object instanceof EventDispatcherInterface: + $this->describeEventDispatcherListeners($object, $options); + break; + case is_callable($object): + $this->describeCallable($object, $options); + break; + default: + throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * Returns the output. + * + * @return OutputInterface The output + */ + protected function getOutput() + { + return $this->output; + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @param RouteCollection $routes + * @param array $options + */ + abstract protected function describeRouteCollection(RouteCollection $routes, array $options = array()); + + /** + * Describes an InputOption instance. + * + * @param Route $route + * @param array $options + */ + abstract protected function describeRoute(Route $route, array $options = array()); + + /** + * Describes container parameters. + * + * @param ParameterBag $parameters + * @param array $options + */ + abstract protected function describeContainerParameters(ParameterBag $parameters, array $options = array()); + + /** + * Describes container tags. + * + * @param ContainerBuilder $builder + * @param array $options + */ + abstract protected function describeContainerTags(ContainerBuilder $builder, array $options = array()); + + /** + * Describes a container service by its name. + * + * Common options are: + * * name: name of described service + * + * @param Definition|Alias|object $service + * @param array $options + */ + abstract protected function describeContainerService($service, array $options = array()); + + /** + * Describes container services. + * + * Common options are: + * * tag: filters described services by given tag + * + * @param ContainerBuilder $builder + * @param array $options + */ + abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = array()); + + /** + * Describes a service definition. + * + * @param Definition $definition + * @param array $options + */ + abstract protected function describeContainerDefinition(Definition $definition, array $options = array()); + + /** + * Describes a service alias. + * + * @param Alias $alias + * @param array $options + */ + abstract protected function describeContainerAlias(Alias $alias, array $options = array()); + + /** + * Describes a container parameter. + * + * @param string $parameter + * @param array $options + */ + abstract protected function describeContainerParameter($parameter, array $options = array()); + + /** + * Describes event dispatcher listeners. + * + * Common options are: + * * name: name of listened event + * + * @param EventDispatcherInterface $eventDispatcher + * @param array $options + */ + abstract protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()); + + /** + * Describes a callable. + * + * @param callable $callable + * @param array $options + */ + abstract protected function describeCallable($callable, array $options = array()); + + /** + * Formats a value as string. + * + * @param mixed $value + * + * @return string + */ + protected function formatValue($value) + { + if (is_object($value)) { + return sprintf('object(%s)', get_class($value)); + } + + if (is_string($value)) { + return $value; + } + + return preg_replace("/\n\s*/s", '', var_export($value, true)); + } + + /** + * Formats a parameter. + * + * @param mixed $value + * + * @return string + */ + protected function formatParameter($value) + { + if (is_bool($value) || is_array($value) || (null === $value)) { + $jsonString = json_encode($value); + + if (preg_match('/^(.{60})./us', $jsonString, $matches)) { + return $matches[1].'...'; + } + + return $jsonString; + } + + return (string) $value; + } + + /** + * @param ContainerBuilder $builder + * @param string $serviceId + * + * @return mixed + */ + protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceId) + { + if ($builder->hasDefinition($serviceId)) { + return $builder->getDefinition($serviceId); + } + + // Some service IDs don't have a Definition, they're simply an Alias + if ($builder->hasAlias($serviceId)) { + return $builder->getAlias($serviceId); + } + + // the service has been injected in some special way, just return the service + return $builder->get($serviceId); + } + + /** + * @param ContainerBuilder $builder + * @param bool $showPrivate + * + * @return array + */ + protected function findDefinitionsByTag(ContainerBuilder $builder, $showPrivate) + { + $definitions = array(); + $tags = $builder->findTags(); + asort($tags); + + foreach ($tags as $tag) { + foreach ($builder->findTaggedServiceIds($tag) as $serviceId => $attributes) { + $definition = $this->resolveServiceDefinition($builder, $serviceId); + + if (!$definition instanceof Definition || !$showPrivate && !$definition->isPublic()) { + continue; + } + + if (!isset($definitions[$tag])) { + $definitions[$tag] = array(); + } + + $definitions[$tag][$serviceId] = $definition; + } + } + + return $definitions; + } + + protected function sortParameters(ParameterBag $parameters) + { + $parameters = $parameters->all(); + ksort($parameters); + + return $parameters; + } + + protected function sortServiceIds(array $serviceIds) + { + asort($serviceIds); + + return $serviceIds; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php new file mode 100644 index 0000000..5febe72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -0,0 +1,367 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $data = array(); + foreach ($routes->all() as $name => $route) { + $data[$name] = $this->getRouteData($route); + } + + $this->writeData($data, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $this->writeData($this->getRouteData($route), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $this->writeData($this->sortParameters($parameters), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + $data = array(); + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $data[$tag] = array(); + foreach ($definitions as $definition) { + $data[$tag][] = $this->getContainerDefinitionData($definition, true); + } + } + + $this->writeData($data, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array()) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + if ($service instanceof Alias) { + $this->writeData($this->getContainerAliasData($service), $options); + } elseif ($service instanceof Definition) { + $this->writeData($this->getContainerDefinitionData($service), $options); + } else { + $this->writeData(get_class($service), $options); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $showPrivate = isset($options['show_private']) && $options['show_private']; + $data = array('definitions' => array(), 'aliases' => array(), 'services' => array()); + + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $service = $this->resolveServiceDefinition($builder, $serviceId); + + if ($service instanceof Alias) { + $data['aliases'][$serviceId] = $this->getContainerAliasData($service); + } elseif ($service instanceof Definition) { + if (($showPrivate || $service->isPublic())) { + $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service); + } + } else { + $data['services'][$serviceId] = get_class($service); + } + } + + $this->writeData($data, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags']), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array()) + { + $this->writeData($this->getContainerAliasData($alias), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $this->writeData($this->getEventDispatcherListenersData($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $this->writeData($this->getCallableData($callable, $options), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $key = isset($options['parameter']) ? $options['parameter'] : ''; + + $this->writeData(array($key => $parameter), $options); + } + + /** + * Writes data as json. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0; + $this->write(json_encode($data, $flags | JSON_PRETTY_PRINT)."\n"); + } + + /** + * @param Route $route + * + * @return array + */ + protected function getRouteData(Route $route) + { + return array( + 'path' => $route->getPath(), + 'pathRegex' => $route->compile()->getRegex(), + 'host' => '' !== $route->getHost() ? $route->getHost() : 'ANY', + 'hostRegex' => '' !== $route->getHost() ? $route->compile()->getHostRegex() : '', + 'scheme' => $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY', + 'method' => $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY', + 'class' => get_class($route), + 'defaults' => $route->getDefaults(), + 'requirements' => $route->getRequirements() ?: 'NO CUSTOM', + 'options' => $route->getOptions(), + ); + } + + /** + * @param Definition $definition + * @param bool $omitTags + * + * @return array + */ + private function getContainerDefinitionData(Definition $definition, $omitTags = false) + { + $data = array( + 'class' => (string) $definition->getClass(), + 'public' => $definition->isPublic(), + 'synthetic' => $definition->isSynthetic(), + 'lazy' => $definition->isLazy(), + ); + + if (method_exists($definition, 'isShared')) { + $data['shared'] = $definition->isShared(); + } + + $data['abstract'] = $definition->isAbstract(); + + if (method_exists($definition, 'isAutowired')) { + $data['autowire'] = $definition->isAutowired(); + + $data['autowiring_types'] = array(); + foreach ($definition->getAutowiringTypes() as $autowiringType) { + $data['autowiring_types'][] = $autowiringType; + } + } + + $data['file'] = $definition->getFile(); + + if ($factory = $definition->getFactory()) { + if (is_array($factory)) { + if ($factory[0] instanceof Reference) { + $data['factory_service'] = (string) $factory[0]; + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $data['factory_class'] = $factory[0]; + } + $data['factory_method'] = $factory[1]; + } else { + $data['factory_function'] = $factory; + } + } + + if (!$omitTags) { + $data['tags'] = array(); + if (count($definition->getTags())) { + foreach ($definition->getTags() as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $data['tags'][] = array('name' => $tagName, 'parameters' => $parameters); + } + } + } + } + + return $data; + } + + /** + * @param Alias $alias + * + * @return array + */ + private function getContainerAliasData(Alias $alias) + { + return array( + 'service' => (string) $alias, + 'public' => $alias->isPublic(), + ); + } + + /** + * @param EventDispatcherInterface $eventDispatcher + * @param string|null $event + * + * @return array + */ + private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, $event = null) + { + $data = array(); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + foreach ($registeredListeners as $listener) { + $l = $this->getCallableData($listener); + $l['priority'] = $eventDispatcher->getListenerPriority($event, $listener); + $data[] = $l; + } + } else { + ksort($registeredListeners); + + foreach ($registeredListeners as $eventListened => $eventListeners) { + foreach ($eventListeners as $eventListener) { + $l = $this->getCallableData($eventListener); + $l['priority'] = $eventDispatcher->getListenerPriority($eventListened, $eventListener); + $data[$eventListened][] = $l; + } + } + } + + return $data; + } + + /** + * @param callable $callable + * @param array $options + * + * @return array + */ + private function getCallableData($callable, array $options = array()) + { + $data = array(); + + if (is_array($callable)) { + $data['type'] = 'function'; + + if (is_object($callable[0])) { + $data['name'] = $callable[1]; + $data['class'] = get_class($callable[0]); + } else { + if (0 !== strpos($callable[1], 'parent::')) { + $data['name'] = $callable[1]; + $data['class'] = $callable[0]; + $data['static'] = true; + } else { + $data['name'] = substr($callable[1], 8); + $data['class'] = $callable[0]; + $data['static'] = true; + $data['parent'] = true; + } + } + + return $data; + } + + if (is_string($callable)) { + $data['type'] = 'function'; + + if (false === strpos($callable, '::')) { + $data['name'] = $callable; + } else { + $callableParts = explode('::', $callable); + + $data['name'] = $callableParts[1]; + $data['class'] = $callableParts[0]; + $data['static'] = true; + } + + return $data; + } + + if ($callable instanceof \Closure) { + $data['type'] = 'closure'; + + return $data; + } + + if (method_exists($callable, '__invoke')) { + $data['type'] = 'object'; + $data['name'] = get_class($callable); + + return $data; + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 0000000..c09c894 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,366 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $first = true; + foreach ($routes->all() as $name => $route) { + if ($first) { + $first = false; + } else { + $this->write("\n\n"); + } + $this->describeRoute($route, array('name' => $name)); + } + $this->write("\n"); + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $output = '- Path: '.$route->getPath() + ."\n".'- Path Regex: '.$route->compile()->getRegex() + ."\n".'- Host: '.('' !== $route->getHost() ? $route->getHost() : 'ANY') + ."\n".'- Host Regex: '.('' !== $route->getHost() ? $route->compile()->getHostRegex() : '') + ."\n".'- Scheme: '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY') + ."\n".'- Method: '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY') + ."\n".'- Class: '.get_class($route) + ."\n".'- Defaults: '.$this->formatRouterConfig($route->getDefaults()) + ."\n".'- Requirements: '.($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM') + ."\n".'- Options: '.$this->formatRouterConfig($route->getOptions()); + + $this->write(isset($options['name']) + ? $options['name']."\n".str_repeat('-', strlen($options['name']))."\n\n".$output + : $output); + $this->write("\n"); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $this->write("Container parameters\n====================\n"); + foreach ($this->sortParameters($parameters) as $key => $value) { + $this->write(sprintf("\n- `%s`: `%s`", $key, $this->formatParameter($value))); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + $this->write("Container tags\n=============="); + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $this->write("\n\n".$tag."\n".str_repeat('-', strlen($tag))); + foreach ($definitions as $serviceId => $definition) { + $this->write("\n\n"); + $this->describeContainerDefinition($definition, array('omit_tags' => true, 'id' => $serviceId)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array()) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + $childOptions = array('id' => $options['id'], 'as_array' => true); + + if ($service instanceof Alias) { + $this->describeContainerAlias($service, $childOptions); + } elseif ($service instanceof Definition) { + $this->describeContainerDefinition($service, $childOptions); + } else { + $this->write(sprintf('**`%s`:** `%s`', $options['id'], get_class($service))); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + + $title = $showPrivate ? 'Public and private services' : 'Public services'; + if (isset($options['tag'])) { + $title .= ' with tag `'.$options['tag'].'`'; + } + $this->write($title."\n".str_repeat('=', strlen($title))); + + $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $showPrivate = isset($options['show_private']) && $options['show_private']; + $services = array('definitions' => array(), 'aliases' => array(), 'services' => array()); + + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $service = $this->resolveServiceDefinition($builder, $serviceId); + + if ($service instanceof Alias) { + $services['aliases'][$serviceId] = $service; + } elseif ($service instanceof Definition) { + if (($showPrivate || $service->isPublic())) { + $services['definitions'][$serviceId] = $service; + } + } else { + $services['services'][$serviceId] = $service; + } + } + + if (!empty($services['definitions'])) { + $this->write("\n\nDefinitions\n-----------\n"); + foreach ($services['definitions'] as $id => $service) { + $this->write("\n"); + $this->describeContainerDefinition($service, array('id' => $id)); + } + } + + if (!empty($services['aliases'])) { + $this->write("\n\nAliases\n-------\n"); + foreach ($services['aliases'] as $id => $service) { + $this->write("\n"); + $this->describeContainerAlias($service, array('id' => $id)); + } + } + + if (!empty($services['services'])) { + $this->write("\n\nServices\n--------\n"); + foreach ($services['services'] as $id => $service) { + $this->write("\n"); + $this->write(sprintf('- `%s`: `%s`', $id, get_class($service))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + $output = '- Class: `'.$definition->getClass().'`' + ."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no') + ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no') + ."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no') + ; + + if (method_exists($definition, 'isShared')) { + $output .= "\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no'); + } + + $output .= "\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no'); + + if (method_exists($definition, 'isAutowired')) { + $output .= "\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no'); + + foreach ($definition->getAutowiringTypes() as $autowiringType) { + $output .= "\n".'- Autowiring Type: `'.$autowiringType.'`'; + } + } + + if ($definition->getFile()) { + $output .= "\n".'- File: `'.$definition->getFile().'`'; + } + + if ($factory = $definition->getFactory()) { + if (is_array($factory)) { + if ($factory[0] instanceof Reference) { + $output .= "\n".'- Factory Service: `'.$factory[0].'`'; + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $output .= "\n".'- Factory Class: `'.$factory[0].'`'; + } + $output .= "\n".'- Factory Method: `'.$factory[1].'`'; + } else { + $output .= "\n".'- Factory Function: `'.$factory.'`'; + } + } + + if (!(isset($options['omit_tags']) && $options['omit_tags'])) { + foreach ($definition->getTags() as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $output .= "\n".'- Tag: `'.$tagName.'`'; + foreach ($parameters as $name => $value) { + $output .= "\n".' - '.ucfirst($name).': '.$value; + } + } + } + } + + $this->write(isset($options['id']) ? sprintf("%s\n%s\n\n%s\n", $options['id'], str_repeat('~', strlen($options['id'])), $output) : $output); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array()) + { + $output = '- Service: `'.$alias.'`' + ."\n".'- Public: '.($alias->isPublic() ? 'yes' : 'no'); + + $this->write(isset($options['id']) ? sprintf("%s\n%s\n\n%s\n", $options['id'], str_repeat('~', strlen($options['id'])), $output) : $output); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', strlen($options['parameter'])), $this->formatParameter($parameter)) : $parameter); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $event = array_key_exists('event', $options) ? $options['event'] : null; + + $title = 'Registered listeners'; + if (null !== $event) { + $title .= sprintf(' for event `%s` ordered by descending priority', $event); + } + + $this->write(sprintf('# %s', $title)."\n"); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + foreach ($registeredListeners as $order => $listener) { + $this->write("\n".sprintf('## Listener %d', $order + 1)."\n"); + $this->describeCallable($listener); + $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($event, $listener))."\n"); + } + } else { + ksort($registeredListeners); + + foreach ($registeredListeners as $eventListened => $eventListeners) { + $this->write("\n".sprintf('## %s', $eventListened)."\n"); + + foreach ($eventListeners as $order => $eventListener) { + $this->write("\n".sprintf('### Listener %d', $order + 1)."\n"); + $this->describeCallable($eventListener); + $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($eventListened, $eventListener))."\n"); + } + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $string = ''; + + if (is_array($callable)) { + $string .= "\n- Type: `function`"; + + if (is_object($callable[0])) { + $string .= "\n".sprintf('- Name: `%s`', $callable[1]); + $string .= "\n".sprintf('- Class: `%s`', get_class($callable[0])); + } else { + if (0 !== strpos($callable[1], 'parent::')) { + $string .= "\n".sprintf('- Name: `%s`', $callable[1]); + $string .= "\n".sprintf('- Class: `%s`', $callable[0]); + $string .= "\n- Static: yes"; + } else { + $string .= "\n".sprintf('- Name: `%s`', substr($callable[1], 8)); + $string .= "\n".sprintf('- Class: `%s`', $callable[0]); + $string .= "\n- Static: yes"; + $string .= "\n- Parent: yes"; + } + } + + return $this->write($string."\n"); + } + + if (is_string($callable)) { + $string .= "\n- Type: `function`"; + + if (false === strpos($callable, '::')) { + $string .= "\n".sprintf('- Name: `%s`', $callable); + } else { + $callableParts = explode('::', $callable); + + $string .= "\n".sprintf('- Name: `%s`', $callableParts[1]); + $string .= "\n".sprintf('- Class: `%s`', $callableParts[0]); + $string .= "\n- Static: yes"; + } + + return $this->write($string."\n"); + } + + if ($callable instanceof \Closure) { + $string .= "\n- Type: `closure`"; + + return $this->write($string."\n"); + } + + if (method_exists($callable, '__invoke')) { + $string .= "\n- Type: `object`"; + $string .= "\n".sprintf('- Name: `%s`', get_class($callable)); + + return $this->write($string."\n"); + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } + + /** + * @param array $array + * + * @return string + */ + private function formatRouterConfig(array $array) + { + if (!count($array)) { + return 'NONE'; + } + + $string = ''; + ksort($array); + foreach ($array as $name => $value) { + $string .= "\n".' - `'.$name.'`: '.$this->formatValue($value); + } + + return $string; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php new file mode 100644 index 0000000..1a93459 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -0,0 +1,466 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $showControllers = isset($options['show_controllers']) && $options['show_controllers']; + + $tableHeaders = array('Name', 'Method', 'Scheme', 'Host', 'Path'); + if ($showControllers) { + $tableHeaders[] = 'Controller'; + } + + $tableRows = array(); + foreach ($routes->all() as $name => $route) { + $row = array( + $name, + $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY', + $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY', + '' !== $route->getHost() ? $route->getHost() : 'ANY', + $route->getPath(), + ); + + if ($showControllers) { + $controller = $route->getDefault('_controller'); + if ($controller instanceof \Closure) { + $controller = 'Closure'; + } elseif (is_object($controller)) { + $controller = get_class($controller); + } + $row[] = $controller; + } + + $tableRows[] = $row; + } + + if (isset($options['output'])) { + $options['output']->table($tableHeaders, $tableRows); + } else { + $table = new Table($this->getOutput()); + $table->setHeaders($tableHeaders)->setRows($tableRows); + $table->render(); + } + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $tableHeaders = array('Property', 'Value'); + $tableRows = array( + array('Route Name', isset($options['name']) ? $options['name'] : ''), + array('Path', $route->getPath()), + array('Path Regex', $route->compile()->getRegex()), + array('Host', ('' !== $route->getHost() ? $route->getHost() : 'ANY')), + array('Host Regex', ('' !== $route->getHost() ? $route->compile()->getHostRegex() : '')), + array('Scheme', ($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY')), + array('Method', ($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY')), + array('Requirements', ($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM')), + array('Class', get_class($route)), + array('Defaults', $this->formatRouterConfig($route->getDefaults())), + array('Options', $this->formatRouterConfig($route->getOptions())), + ); + + $table = new Table($this->getOutput()); + $table->setHeaders($tableHeaders)->setRows($tableRows); + $table->render(); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $tableHeaders = array('Parameter', 'Value'); + + $tableRows = array(); + foreach ($this->sortParameters($parameters) as $parameter => $value) { + $tableRows[] = array($parameter, $this->formatParameter($value)); + } + + $options['output']->title('Symfony Container Parameters'); + $options['output']->table($tableHeaders, $tableRows); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + + if ($showPrivate) { + $options['output']->title('Symfony Container Public and Private Tags'); + } else { + $options['output']->title('Symfony Container Public Tags'); + } + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $options['output']->section(sprintf('"%s" tag', $tag)); + $options['output']->listing(array_keys($definitions)); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array()) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + if ($service instanceof Alias) { + $this->describeContainerAlias($service, $options); + } elseif ($service instanceof Definition) { + $this->describeContainerDefinition($service, $options); + } else { + $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); + $options['output']->table( + array('Service ID', 'Class'), + array( + array(isset($options['id']) ? $options['id'] : '-', get_class($service)), + ) + ); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + $showTag = isset($options['tag']) ? $options['tag'] : null; + + if ($showPrivate) { + $title = 'Symfony Container Public and Private Services'; + } else { + $title = 'Symfony Container Public Services'; + } + + if ($showTag) { + $title .= sprintf(' Tagged with "%s" Tag', $options['tag']); + } + + $options['output']->title($title); + + $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $maxTags = array(); + + foreach ($serviceIds as $key => $serviceId) { + $definition = $this->resolveServiceDefinition($builder, $serviceId); + if ($definition instanceof Definition) { + // filter out private services unless shown explicitly + if (!$showPrivate && !$definition->isPublic()) { + unset($serviceIds[$key]); + continue; + } + if ($showTag) { + $tags = $definition->getTag($showTag); + foreach ($tags as $tag) { + foreach ($tag as $key => $value) { + if (!isset($maxTags[$key])) { + $maxTags[$key] = strlen($key); + } + if (strlen($value) > $maxTags[$key]) { + $maxTags[$key] = strlen($value); + } + } + } + } + } + } + + $tagsCount = count($maxTags); + $tagsNames = array_keys($maxTags); + + $tableHeaders = array_merge(array('Service ID'), $tagsNames, array('Class name')); + $tableRows = array(); + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $definition = $this->resolveServiceDefinition($builder, $serviceId); + if ($definition instanceof Definition) { + if ($showTag) { + foreach ($definition->getTag($showTag) as $key => $tag) { + $tagValues = array(); + foreach ($tagsNames as $tagName) { + $tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : ''; + } + if (0 === $key) { + $tableRows[] = array_merge(array($serviceId), $tagValues, array($definition->getClass())); + } else { + $tableRows[] = array_merge(array(' "'), $tagValues, array('')); + } + } + } else { + $tableRows[] = array($serviceId, $definition->getClass()); + } + } elseif ($definition instanceof Alias) { + $alias = $definition; + $tableRows[] = array_merge(array($serviceId, sprintf('alias for "%s"', $alias)), $tagsCount ? array_fill(0, $tagsCount, '') : array()); + } else { + // we have no information (happens with "service_container") + $tableRows[] = array_merge(array($serviceId, get_class($definition)), $tagsCount ? array_fill(0, $tagsCount, '') : array()); + } + } + + $options['output']->table($tableHeaders, $tableRows); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + if (isset($options['id'])) { + $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); + } + + $tableHeaders = array('Option', 'Value'); + + $tableRows[] = array('Service ID', isset($options['id']) ? $options['id'] : '-'); + $tableRows[] = array('Class', $definition->getClass() ?: '-'); + + $tags = $definition->getTags(); + if (count($tags)) { + $tagInformation = ''; + foreach ($tags as $tagName => $tagData) { + foreach ($tagData as $tagParameters) { + $parameters = array_map(function ($key, $value) { + return sprintf('%s: %s', $key, $value); + }, array_keys($tagParameters), array_values($tagParameters)); + $parameters = implode(', ', $parameters); + + if ('' === $parameters) { + $tagInformation .= sprintf('%s', $tagName); + } else { + $tagInformation .= sprintf('%s (%s)', $tagName, $parameters); + } + } + } + } else { + $tagInformation = '-'; + } + $tableRows[] = array('Tags', $tagInformation); + + $tableRows[] = array('Public', $definition->isPublic() ? 'yes' : 'no'); + $tableRows[] = array('Synthetic', $definition->isSynthetic() ? 'yes' : 'no'); + $tableRows[] = array('Lazy', $definition->isLazy() ? 'yes' : 'no'); + if (method_exists($definition, 'isShared')) { + $tableRows[] = array('Shared', $definition->isShared() ? 'yes' : 'no'); + } + $tableRows[] = array('Abstract', $definition->isAbstract() ? 'yes' : 'no'); + + if (method_exists($definition, 'isAutowired')) { + $tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : 'no'); + + $autowiringTypes = $definition->getAutowiringTypes(); + if (count($autowiringTypes)) { + $autowiringTypesInformation = implode(', ', $autowiringTypes); + } else { + $autowiringTypesInformation = '-'; + } + + $tableRows[] = array('Autowiring Types', $autowiringTypesInformation); + } + + if ($definition->getFile()) { + $tableRows[] = array('Required File', $definition->getFile() ? $definition->getFile() : '-'); + } + + if ($factory = $definition->getFactory()) { + if (is_array($factory)) { + if ($factory[0] instanceof Reference) { + $tableRows[] = array('Factory Service', $factory[0]); + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $tableRows[] = array('Factory Class', $factory[0]); + } + $tableRows[] = array('Factory Method', $factory[1]); + } else { + $tableRows[] = array('Factory Function', $factory); + } + } + + $options['output']->table($tableHeaders, $tableRows); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array()) + { + $options['output']->comment(sprintf("This service is an alias for the service %s\n", (string) $alias)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $options['output']->table( + array('Parameter', 'Value'), + array( + array($options['parameter'], $this->formatParameter($parameter), + ), + )); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $event = array_key_exists('event', $options) ? $options['event'] : null; + + if (null !== $event) { + $title = sprintf('Registered Listeners for "%s" Event', $event); + } else { + $title = 'Registered Listeners Grouped by Event'; + } + + $options['output']->title($title); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + $this->renderEventListenerTable($eventDispatcher, $event, $registeredListeners, $options['output']); + } else { + ksort($registeredListeners); + foreach ($registeredListeners as $eventListened => $eventListeners) { + $options['output']->section(sprintf('"%s" event', $eventListened)); + $this->renderEventListenerTable($eventDispatcher, $eventListened, $eventListeners, $options['output']); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $this->writeText($this->formatCallable($callable), $options); + } + + /** + * @param array $array + */ + private function renderEventListenerTable(EventDispatcherInterface $eventDispatcher, $event, array $eventListeners, SymfonyStyle $io) + { + $tableHeaders = array('Order', 'Callable', 'Priority'); + $tableRows = array(); + + $order = 1; + foreach ($eventListeners as $order => $listener) { + $tableRows[] = array(sprintf('#%d', $order + 1), $this->formatCallable($listener), $eventDispatcher->getListenerPriority($event, $listener)); + } + + $io->table($tableHeaders, $tableRows); + } + + /** + * @param array $config + * + * @return string + */ + private function formatRouterConfig(array $config) + { + if (empty($config)) { + return 'NONE'; + } + + ksort($config); + + $configAsString = ''; + foreach ($config as $key => $value) { + $configAsString .= sprintf("\n%s: %s", $key, $this->formatValue($value)); + } + + return trim($configAsString); + } + + /** + * @param string $section + * @param string $message + * + * @return string + */ + private function formatSection($section, $message) + { + return sprintf('[%s] %s', $section, $message); + } + + /** + * @param callable $callable + * + * @return string + */ + private function formatCallable($callable) + { + if (is_array($callable)) { + if (is_object($callable[0])) { + return sprintf('%s::%s()', get_class($callable[0]), $callable[1]); + } + + return sprintf('%s::%s()', $callable[0], $callable[1]); + } + + if (is_string($callable)) { + return sprintf('%s()', $callable); + } + + if ($callable instanceof \Closure) { + return '\Closure()'; + } + + if (method_exists($callable, '__invoke')) { + return sprintf('%s::__invoke()', get_class($callable)); + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } + + /** + * @param string $content + * @param array $options + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php new file mode 100644 index 0000000..bb315be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -0,0 +1,531 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $this->writeDocument($this->getRouteCollectionDocument($routes)); + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $this->writeDocument($this->getRouteDocument($route, isset($options['name']) ? $options['name'] : null)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $this->writeDocument($this->getContainerParametersDocument($parameters)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_private']) && $options['show_private'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array()) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + $this->writeDocument($this->getContainerServiceDocument($service, $options['id'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + $this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array()) + { + $this->writeDocument($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null)); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null)); + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $this->writeDocument($this->getCallableDocument($callable)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $this->writeDocument($this->getContainerParameterDocument($parameter, $options)); + } + + /** + * Writes DOM document. + * + * @param \DOMDocument $dom + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @param RouteCollection $routes + * + * @return \DOMDocument + */ + private function getRouteCollectionDocument(RouteCollection $routes) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($routesXML = $dom->createElement('routes')); + + foreach ($routes->all() as $name => $route) { + $routeXML = $this->getRouteDocument($route, $name); + $routesXML->appendChild($routesXML->ownerDocument->importNode($routeXML->childNodes->item(0), true)); + } + + return $dom; + } + + /** + * @param Route $route + * @param string|null $name + * + * @return \DOMDocument + */ + private function getRouteDocument(Route $route, $name = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($routeXML = $dom->createElement('route')); + + if ($name) { + $routeXML->setAttribute('name', $name); + } + + $routeXML->setAttribute('class', get_class($route)); + + $routeXML->appendChild($pathXML = $dom->createElement('path')); + $pathXML->setAttribute('regex', $route->compile()->getRegex()); + $pathXML->appendChild(new \DOMText($route->getPath())); + + if ('' !== $route->getHost()) { + $routeXML->appendChild($hostXML = $dom->createElement('host')); + $hostXML->setAttribute('regex', $route->compile()->getHostRegex()); + $hostXML->appendChild(new \DOMText($route->getHost())); + } + + foreach ($route->getSchemes() as $scheme) { + $routeXML->appendChild($schemeXML = $dom->createElement('scheme')); + $schemeXML->appendChild(new \DOMText($scheme)); + } + + foreach ($route->getMethods() as $method) { + $routeXML->appendChild($methodXML = $dom->createElement('method')); + $methodXML->appendChild(new \DOMText($method)); + } + + if (count($route->getDefaults())) { + $routeXML->appendChild($defaultsXML = $dom->createElement('defaults')); + foreach ($route->getDefaults() as $attribute => $value) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->setAttribute('key', $attribute); + $defaultXML->appendChild(new \DOMText($this->formatValue($value))); + } + } + + if (count($route->getRequirements())) { + $routeXML->appendChild($requirementsXML = $dom->createElement('requirements')); + foreach ($route->getRequirements() as $attribute => $pattern) { + $requirementsXML->appendChild($requirementXML = $dom->createElement('requirement')); + $requirementXML->setAttribute('key', $attribute); + $requirementXML->appendChild(new \DOMText($pattern)); + } + } + + if (count($route->getOptions())) { + $routeXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($route->getOptions() as $name => $value) { + $optionsXML->appendChild($optionXML = $dom->createElement('option')); + $optionXML->setAttribute('key', $name); + $optionXML->appendChild(new \DOMText($this->formatValue($value))); + } + } + + return $dom; + } + + /** + * @param ParameterBag $parameters + * + * @return \DOMDocument + */ + private function getContainerParametersDocument(ParameterBag $parameters) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($parametersXML = $dom->createElement('parameters')); + + foreach ($this->sortParameters($parameters) as $key => $value) { + $parametersXML->appendChild($parameterXML = $dom->createElement('parameter')); + $parameterXML->setAttribute('key', $key); + $parameterXML->appendChild(new \DOMText($this->formatParameter($value))); + } + + return $dom; + } + + /** + * @param ContainerBuilder $builder + * @param bool $showPrivate + * + * @return \DOMDocument + */ + private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivate = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($containerXML = $dom->createElement('container')); + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $containerXML->appendChild($tagXML = $dom->createElement('tag')); + $tagXML->setAttribute('name', $tag); + + foreach ($definitions as $serviceId => $definition) { + $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true); + $tagXML->appendChild($dom->importNode($definitionXML->childNodes->item(0), true)); + } + } + + return $dom; + } + + /** + * @param mixed $service + * @param string $id + * + * @return \DOMDocument + */ + private function getContainerServiceDocument($service, $id) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + if ($service instanceof Alias) { + $dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true)); + } elseif ($service instanceof Definition) { + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id)->childNodes->item(0), true)); + } else { + $dom->appendChild($serviceXML = $dom->createElement('service')); + $serviceXML->setAttribute('id', $id); + $serviceXML->setAttribute('class', get_class($service)); + } + + return $dom; + } + + /** + * @param ContainerBuilder $builder + * @param string|null $tag + * @param bool $showPrivate + * + * @return \DOMDocument + */ + private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($containerXML = $dom->createElement('container')); + + $serviceIds = $tag ? array_keys($builder->findTaggedServiceIds($tag)) : $builder->getServiceIds(); + + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $service = $this->resolveServiceDefinition($builder, $serviceId); + + if ($service instanceof Definition && !($showPrivate || $service->isPublic())) { + continue; + } + + $serviceXML = $this->getContainerServiceDocument($service, $serviceId); + $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true)); + } + + return $dom; + } + + /** + * @param Definition $definition + * @param string|null $id + * @param bool $omitTags + * + * @return \DOMDocument + */ + private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($serviceXML = $dom->createElement('definition')); + + if ($id) { + $serviceXML->setAttribute('id', $id); + } + + $serviceXML->setAttribute('class', $definition->getClass()); + + if ($factory = $definition->getFactory()) { + $serviceXML->appendChild($factoryXML = $dom->createElement('factory')); + + if (is_array($factory)) { + if ($factory[0] instanceof Reference) { + $factoryXML->setAttribute('service', (string) $factory[0]); + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $factoryXML->setAttribute('class', $factory[0]); + } + $factoryXML->setAttribute('method', $factory[1]); + } else { + $factoryXML->setAttribute('function', $factory); + } + } + + $serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false'); + $serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false'); + $serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false'); + if (method_exists($definition, 'isShared')) { + $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); + } + $serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false'); + + if (method_exists($definition, 'isAutowired')) { + $serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false'); + } + + $serviceXML->setAttribute('file', $definition->getFile()); + + if (!$omitTags) { + $tags = $definition->getTags(); + + if (count($tags) > 0) { + $serviceXML->appendChild($tagsXML = $dom->createElement('tags')); + foreach ($tags as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $tagsXML->appendChild($tagXML = $dom->createElement('tag')); + $tagXML->setAttribute('name', $tagName); + foreach ($parameters as $name => $value) { + $tagXML->appendChild($parameterXML = $dom->createElement('parameter')); + $parameterXML->setAttribute('name', $name); + $parameterXML->appendChild(new \DOMText($this->formatParameter($value))); + } + } + } + } + } + + return $dom; + } + + /** + * @param Alias $alias + * @param string|null $id + * + * @return \DOMDocument + */ + private function getContainerAliasDocument(Alias $alias, $id = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($aliasXML = $dom->createElement('alias')); + + if ($id) { + $aliasXML->setAttribute('id', $id); + } + + $aliasXML->setAttribute('service', (string) $alias); + $aliasXML->setAttribute('public', $alias->isPublic() ? 'true' : 'false'); + + return $dom; + } + + /** + * @param string $parameter + * @param array $options + * + * @return \DOMDocument + */ + private function getContainerParameterDocument($parameter, $options = array()) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($parameterXML = $dom->createElement('parameter')); + + if (isset($options['parameter'])) { + $parameterXML->setAttribute('key', $options['parameter']); + } + + $parameterXML->appendChild(new \DOMText($this->formatParameter($parameter))); + + return $dom; + } + + /** + * @param EventDispatcherInterface $eventDispatcher + * @param string|null $event + * + * @return \DOMDocument + */ + private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, $event = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher')); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + $this->appendEventListenerDocument($eventDispatcher, $event, $eventDispatcherXML, $registeredListeners); + } else { + ksort($registeredListeners); + + foreach ($registeredListeners as $eventListened => $eventListeners) { + $eventDispatcherXML->appendChild($eventXML = $dom->createElement('event')); + $eventXML->setAttribute('name', $eventListened); + + $this->appendEventListenerDocument($eventDispatcher, $eventListened, $eventXML, $eventListeners); + } + } + + return $dom; + } + + /** + * @param \DOMElement $element + * @param array $eventListeners + */ + private function appendEventListenerDocument(EventDispatcherInterface $eventDispatcher, $event, \DOMElement $element, array $eventListeners) + { + foreach ($eventListeners as $listener) { + $callableXML = $this->getCallableDocument($listener); + $callableXML->childNodes->item(0)->setAttribute('priority', $eventDispatcher->getListenerPriority($event, $listener)); + + $element->appendChild($element->ownerDocument->importNode($callableXML->childNodes->item(0), true)); + } + } + + /** + * @param callable $callable + * + * @return \DOMDocument + */ + private function getCallableDocument($callable) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($callableXML = $dom->createElement('callable')); + + if (is_array($callable)) { + $callableXML->setAttribute('type', 'function'); + + if (is_object($callable[0])) { + $callableXML->setAttribute('name', $callable[1]); + $callableXML->setAttribute('class', get_class($callable[0])); + } else { + if (0 !== strpos($callable[1], 'parent::')) { + $callableXML->setAttribute('name', $callable[1]); + $callableXML->setAttribute('class', $callable[0]); + $callableXML->setAttribute('static', 'true'); + } else { + $callableXML->setAttribute('name', substr($callable[1], 8)); + $callableXML->setAttribute('class', $callable[0]); + $callableXML->setAttribute('static', 'true'); + $callableXML->setAttribute('parent', 'true'); + } + } + + return $dom; + } + + if (is_string($callable)) { + $callableXML->setAttribute('type', 'function'); + + if (false === strpos($callable, '::')) { + $callableXML->setAttribute('name', $callable); + } else { + $callableParts = explode('::', $callable); + + $callableXML->setAttribute('name', $callableParts[1]); + $callableXML->setAttribute('class', $callableParts[0]); + $callableXML->setAttribute('static', 'true'); + } + + return $dom; + } + + if ($callable instanceof \Closure) { + $callableXML->setAttribute('type', 'closure'); + + return $dom; + } + + if (method_exists($callable, '__invoke')) { + $callableXML->setAttribute('type', 'object'); + $callableXML->setAttribute('name', get_class($callable)); + + return $dom; + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php new file mode 100644 index 0000000..2599f00 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Helper; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor; +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor; +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor; +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper; + +/** + * @author Jean-François Simon + * + * @internal + */ +class DescriptorHelper extends BaseDescriptorHelper +{ + /** + * Constructor. + */ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php new file mode 100644 index 0000000..502398b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -0,0 +1,396 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Csrf\CsrfToken; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Doctrine\Bundle\DoctrineBundle\Registry; + +/** + * Controller is a simple implementation of a Controller. + * + * It provides methods to common features needed in controllers. + * + * @author Fabien Potencier + */ +abstract class Controller implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * Generates a URL from the given parameters. + * + * @param string $route The name of the route + * @param mixed $parameters An array of parameters + * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + * + * @see UrlGeneratorInterface + */ + protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + return $this->container->get('router')->generate($route, $parameters, $referenceType); + } + + /** + * Forwards the request to another controller. + * + * @param string $controller The controller name (a string like BlogBundle:Post:index) + * @param array $path An array of path parameters + * @param array $query An array of query parameters + * + * @return Response A Response instance + */ + protected function forward($controller, array $path = array(), array $query = array()) + { + $path['_controller'] = $controller; + $subRequest = $this->container->get('request_stack')->getCurrentRequest()->duplicate($query, null, $path); + + return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } + + /** + * Returns a RedirectResponse to the given URL. + * + * @param string $url The URL to redirect to + * @param int $status The status code to use for the Response + * + * @return RedirectResponse + */ + protected function redirect($url, $status = 302) + { + return new RedirectResponse($url, $status); + } + + /** + * Returns a RedirectResponse to the given route with the given parameters. + * + * @param string $route The name of the route + * @param array $parameters An array of parameters + * @param int $status The status code to use for the Response + * + * @return RedirectResponse + */ + protected function redirectToRoute($route, array $parameters = array(), $status = 302) + { + return $this->redirect($this->generateUrl($route, $parameters), $status); + } + + /** + * Adds a flash message to the current session for type. + * + * @param string $type The type + * @param string $message The message + * + * @throws \LogicException + */ + protected function addFlash($type, $message) + { + if (!$this->container->has('session')) { + throw new \LogicException('You can not use the addFlash method if sessions are disabled.'); + } + + $this->container->get('session')->getFlashBag()->add($type, $message); + } + + /** + * Checks if the attributes are granted against the current authentication token and optionally supplied object. + * + * @param mixed $attributes The attributes + * @param mixed $object The object + * + * @return bool + * + * @throws \LogicException + */ + protected function isGranted($attributes, $object = null) + { + if (!$this->container->has('security.authorization_checker')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + return $this->container->get('security.authorization_checker')->isGranted($attributes, $object); + } + + /** + * Throws an exception unless the attributes are granted against the current authentication token and optionally + * supplied object. + * + * @param mixed $attributes The attributes + * @param mixed $object The object + * @param string $message The message passed to the exception + * + * @throws AccessDeniedException + */ + protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') + { + if (!$this->isGranted($attributes, $object)) { + throw $this->createAccessDeniedException($message); + } + } + + /** + * Returns a rendered view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * + * @return string The rendered view + */ + protected function renderView($view, array $parameters = array()) + { + if ($this->container->has('templating')) { + return $this->container->get('templating')->render($view, $parameters); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available.'); + } + + return $this->container->get('twig')->render($view, $parameters); + } + + /** + * Renders a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param Response $response A response instance + * + * @return Response A Response instance + */ + protected function render($view, array $parameters = array(), Response $response = null) + { + if ($this->container->has('templating')) { + return $this->container->get('templating')->renderResponse($view, $parameters, $response); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available.'); + } + + if (null === $response) { + $response = new Response(); + } + + $response->setContent($this->container->get('twig')->render($view, $parameters)); + + return $response; + } + + /** + * Streams a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param StreamedResponse $response A response instance + * + * @return StreamedResponse A StreamedResponse instance + */ + protected function stream($view, array $parameters = array(), StreamedResponse $response = null) + { + if ($this->container->has('templating')) { + $templating = $this->container->get('templating'); + + $callback = function () use ($templating, $view, $parameters) { + $templating->stream($view, $parameters); + }; + } elseif ($this->container->has('twig')) { + $twig = $this->container->get('twig'); + + $callback = function () use ($twig, $view, $parameters) { + $twig->display($view, $parameters); + }; + } else { + throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available.'); + } + + if (null === $response) { + return new StreamedResponse($callback); + } + + $response->setCallback($callback); + + return $response; + } + + /** + * Returns a NotFoundHttpException. + * + * This will result in a 404 response code. Usage example: + * + * throw $this->createNotFoundException('Page not found!'); + * + * @param string $message A message + * @param \Exception|null $previous The previous exception + * + * @return NotFoundHttpException + */ + protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) + { + return new NotFoundHttpException($message, $previous); + } + + /** + * Returns an AccessDeniedException. + * + * This will result in a 403 response code. Usage example: + * + * throw $this->createAccessDeniedException('Unable to access this page!'); + * + * @param string $message A message + * @param \Exception|null $previous The previous exception + * + * @return AccessDeniedException + */ + protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) + { + return new AccessDeniedException($message, $previous); + } + + /** + * Creates and returns a Form instance from the type of the form. + * + * @param string $type The fully qualified class name of the form type + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return Form + */ + protected function createForm($type, $data = null, array $options = array()) + { + return $this->container->get('form.factory')->create($type, $data, $options); + } + + /** + * Creates and returns a form builder instance. + * + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return FormBuilder + */ + protected function createFormBuilder($data = null, array $options = array()) + { + return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); + } + + /** + * Shortcut to return the Doctrine Registry service. + * + * @return Registry + * + * @throws \LogicException If DoctrineBundle is not available + */ + protected function getDoctrine() + { + if (!$this->container->has('doctrine')) { + throw new \LogicException('The DoctrineBundle is not registered in your application.'); + } + + return $this->container->get('doctrine'); + } + + /** + * Get a user from the Security Token Storage. + * + * @return mixed + * + * @throws \LogicException If SecurityBundle is not available + * + * @see TokenInterface::getUser() + */ + protected function getUser() + { + if (!$this->container->has('security.token_storage')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + if (null === $token = $this->container->get('security.token_storage')->getToken()) { + return; + } + + if (!is_object($user = $token->getUser())) { + // e.g. anonymous authentication + return; + } + + return $user; + } + + /** + * Returns true if the service id is defined. + * + * @param string $id The service id + * + * @return bool true if the service id is defined, false otherwise + */ + protected function has($id) + { + return $this->container->has($id); + } + + /** + * Gets a container service by its id. + * + * @param string $id The service id + * + * @return object The service + */ + protected function get($id) + { + return $this->container->get($id); + } + + /** + * Gets a container configuration parameter by its name. + * + * @param string $name The parameter name + * + * @return mixed + */ + protected function getParameter($name) + { + return $this->container->getParameter($name); + } + + /** + * Checks the validity of a CSRF token. + * + * @param string $id The id used when generating the token + * @param string $token The actual token sent with the request that should be validated + * + * @return bool + */ + protected function isCsrfTokenValid($id, $token) + { + if (!$this->container->has('security.csrf.token_manager')) { + throw new \LogicException('CSRF protection is not enabled in your application.'); + } + + return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php new file mode 100644 index 0000000..873c736 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerNameParser.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * ControllerNameParser converts controller from the short notation a:b:c + * (BlogBundle:Post:index) to a fully-qualified class::method string + * (Bundle\BlogBundle\Controller\PostController::indexAction). + * + * @author Fabien Potencier + */ +class ControllerNameParser +{ + protected $kernel; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + /** + * Converts a short notation a:b:c to a class::method. + * + * @param string $controller A short notation controller (a:b:c) + * + * @return string A string in the class::method notation + * + * @throws \InvalidArgumentException when the specified bundle is not enabled + * or the controller cannot be found + */ + public function parse($controller) + { + $originalController = $controller; + if (3 !== count($parts = explode(':', $controller))) { + throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid "a:b:c" controller string.', $controller)); + } + + list($bundle, $controller, $action) = $parts; + $controller = str_replace('/', '\\', $controller); + $bundles = array(); + + try { + // this throws an exception if there is no such bundle + $allBundles = $this->kernel->getBundle($bundle, false); + } catch (\InvalidArgumentException $e) { + $message = sprintf( + 'The "%s" (from the _controller value "%s") does not exist or is not enabled in your kernel!', + $bundle, + $originalController + ); + + if ($alternative = $this->findAlternative($bundle)) { + $message .= sprintf(' Did you mean "%s:%s:%s"?', $alternative, $controller, $action); + } + + throw new \InvalidArgumentException($message, 0, $e); + } + + foreach ($allBundles as $b) { + $try = $b->getNamespace().'\\Controller\\'.$controller.'Controller'; + if (class_exists($try)) { + return $try.'::'.$action.'Action'; + } + + $bundles[] = $b->getName(); + $msg = sprintf('The _controller value "%s:%s:%s" maps to a "%s" class, but this class was not found. Create this class or check the spelling of the class and its namespace.', $bundle, $controller, $action, $try); + } + + if (count($bundles) > 1) { + $msg = sprintf('Unable to find controller "%s:%s" in bundles %s.', $bundle, $controller, implode(', ', $bundles)); + } + + throw new \InvalidArgumentException($msg); + } + + /** + * Converts a class::method notation to a short one (a:b:c). + * + * @param string $controller A string in the class::method notation + * + * @return string A short notation controller (a:b:c) + * + * @throws \InvalidArgumentException when the controller is not valid or cannot be found in any bundle + */ + public function build($controller) + { + if (0 === preg_match('#^(.*?\\\\Controller\\\\(.+)Controller)::(.+)Action$#', $controller, $match)) { + throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid "class::method" string.', $controller)); + } + + $className = $match[1]; + $controllerName = $match[2]; + $actionName = $match[3]; + foreach ($this->kernel->getBundles() as $name => $bundle) { + if (0 !== strpos($className, $bundle->getNamespace())) { + continue; + } + + return sprintf('%s:%s:%s', $name, $controllerName, $actionName); + } + + throw new \InvalidArgumentException(sprintf('Unable to find a bundle that defines controller "%s".', $controller)); + } + + /** + * Attempts to find a bundle that is *similar* to the given bundle name. + * + * @param string $nonExistentBundleName + * + * @return string + */ + private function findAlternative($nonExistentBundleName) + { + $bundleNames = array_map(function ($b) { + return $b->getName(); + }, $this->kernel->getBundles()); + + $alternative = null; + $shortest = null; + foreach ($bundleNames as $bundleName) { + // if there's a partial match, return it immediately + if (false !== strpos($bundleName, $nonExistentBundleName)) { + return $bundleName; + } + + $lev = levenshtein($nonExistentBundleName, $bundleName); + if ($lev <= strlen($nonExistentBundleName) / 3 && ($alternative === null || $lev < $shortest)) { + $alternative = $bundleName; + } + } + + return $alternative; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php new file mode 100644 index 0000000..6804ded --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; + +/** + * ControllerResolver. + * + * @author Fabien Potencier + */ +class ControllerResolver extends BaseControllerResolver +{ + protected $container; + protected $parser; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param ControllerNameParser $parser A ControllerNameParser instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(ContainerInterface $container, ControllerNameParser $parser, LoggerInterface $logger = null) + { + $this->container = $container; + $this->parser = $parser; + + parent::__construct($logger); + } + + /** + * Returns a callable for the given controller. + * + * @param string $controller A Controller string + * + * @return mixed A PHP callable + * + * @throws \LogicException When the name could not be parsed + * @throws \InvalidArgumentException When the controller class does not exist + */ + protected function createController($controller) + { + if (false === strpos($controller, '::')) { + $count = substr_count($controller, ':'); + if (2 == $count) { + // controller in the a:b:c notation then + $controller = $this->parser->parse($controller); + } elseif (1 == $count) { + // controller in the service:method notation + list($service, $method) = explode(':', $controller, 2); + + return array($this->container->get($service), $method); + } elseif ($this->container->has($controller) && method_exists($service = $this->container->get($controller), '__invoke')) { + return $service; + } else { + throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller)); + } + } + + return parent::createController($controller); + } + + /** + * {@inheritdoc} + */ + protected function instantiateController($class) + { + $controller = parent::instantiateController($class); + + if ($controller instanceof ContainerAwareInterface) { + $controller->setContainer($this->container); + } + + return $controller; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php new file mode 100644 index 0000000..a1f1b04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Redirects a request to another URL. + * + * @author Fabien Potencier + */ +class RedirectController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * Redirects to another route with the given name. + * + * The response status code is 302 if the permanent parameter is false (default), + * and 301 if the redirection is permanent. + * + * In case the route name is empty, the status code will be 404 when permanent is false + * and 410 otherwise. + * + * @param Request $request The request instance + * @param string $route The route name to redirect to + * @param bool $permanent Whether the redirection is permanent + * @param bool|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore + * + * @return Response A Response instance + * + * @throws HttpException In case the route name is empty + */ + public function redirectAction(Request $request, $route, $permanent = false, $ignoreAttributes = false) + { + if ('' == $route) { + throw new HttpException($permanent ? 410 : 404); + } + + $attributes = array(); + if (false === $ignoreAttributes || is_array($ignoreAttributes)) { + $attributes = $request->attributes->get('_route_params'); + unset($attributes['route'], $attributes['permanent'], $attributes['ignoreAttributes']); + if ($ignoreAttributes) { + $attributes = array_diff_key($attributes, array_flip($ignoreAttributes)); + } + } + + return new RedirectResponse($this->container->get('router')->generate($route, $attributes, UrlGeneratorInterface::ABSOLUTE_URL), $permanent ? 301 : 302); + } + + /** + * Redirects to a URL. + * + * The response status code is 302 if the permanent parameter is false (default), + * and 301 if the redirection is permanent. + * + * In case the path is empty, the status code will be 404 when permanent is false + * and 410 otherwise. + * + * @param Request $request The request instance + * @param string $path The absolute path or URL to redirect to + * @param bool $permanent Whether the redirect is permanent or not + * @param string|null $scheme The URL scheme (null to keep the current one) + * @param int|null $httpPort The HTTP port (null to keep the current one for the same scheme or the configured port in the container) + * @param int|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the configured port in the container) + * + * @return Response A Response instance + * + * @throws HttpException In case the path is empty + */ + public function urlRedirectAction(Request $request, $path, $permanent = false, $scheme = null, $httpPort = null, $httpsPort = null) + { + if ('' == $path) { + throw new HttpException($permanent ? 410 : 404); + } + + $statusCode = $permanent ? 301 : 302; + + // redirect if the path is a full URL + if (parse_url($path, PHP_URL_SCHEME)) { + return new RedirectResponse($path, $statusCode); + } + + if (null === $scheme) { + $scheme = $request->getScheme(); + } + + $qs = $request->getQueryString(); + if ($qs) { + if (strpos($path, '?') === false) { + $qs = '?'.$qs; + } else { + $qs = '&'.$qs; + } + } + + $port = ''; + if ('http' === $scheme) { + if (null === $httpPort) { + if ('http' === $request->getScheme()) { + $httpPort = $request->getPort(); + } elseif ($this->container->hasParameter('request_listener.http_port')) { + $httpPort = $this->container->getParameter('request_listener.http_port'); + } + } + + if (null !== $httpPort && 80 != $httpPort) { + $port = ":$httpPort"; + } + } elseif ('https' === $scheme) { + if (null === $httpsPort) { + if ('https' === $request->getScheme()) { + $httpsPort = $request->getPort(); + } elseif ($this->container->hasParameter('request_listener.https_port')) { + $httpsPort = $this->container->getParameter('request_listener.https_port'); + } + } + + if (null !== $httpsPort && 443 != $httpsPort) { + $port = ":$httpsPort"; + } + } + + $url = $scheme.'://'.$request->getHost().$port.$request->getBaseUrl().$path.$qs; + + return new RedirectResponse($url, $statusCode); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php new file mode 100644 index 0000000..d222bc7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Response; + +/** + * TemplateController. + * + * @author Fabien Potencier + */ +class TemplateController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * Renders a template. + * + * @param string $template The template name + * @param int|null $maxAge Max age for client caching + * @param int|null $sharedAge Max age for shared (proxy) caching + * @param bool|null $private Whether or not caching should apply for client caches only + * + * @return Response A Response instance + */ + public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null) + { + /** @var $response \Symfony\Component\HttpFoundation\Response */ + $response = $this->container->get('templating')->renderResponse($template); + + if ($maxAge) { + $response->setMaxAge($maxAge); + } + + if ($sharedAge) { + $response->setSharedMaxAge($sharedAge); + } + + if ($private) { + $response->setPrivate(); + } elseif ($private === false || (null === $private && ($maxAge || $sharedAge))) { + $response->setPublic(); + } + + return $response; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DataCollector/RouterDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DataCollector/RouterDataCollector.php new file mode 100644 index 0000000..b7289e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DataCollector/RouterDataCollector.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\RouterDataCollector as BaseRouterDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; + +/** + * RouterDataCollector. + * + * @author Fabien Potencier + */ +class RouterDataCollector extends BaseRouterDataCollector +{ + public function guessRoute(Request $request, $controller) + { + if (is_array($controller)) { + $controller = $controller[0]; + } + + if ($controller instanceof RedirectController) { + return $request->attributes->get('_route'); + } + + return parent::guessRoute($request, $controller); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php new file mode 100644 index 0000000..27b9b46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheClearerPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers the cache clearers. + * + * @author Dustin Dobervich + */ +class AddCacheClearerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('cache_clearer')) { + return; + } + + $clearers = array(); + foreach ($container->findTaggedServiceIds('kernel.cache_clearer') as $id => $attributes) { + $clearers[] = new Reference($id); + } + + $container->getDefinition('cache_clearer')->replaceArgument(0, $clearers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php new file mode 100644 index 0000000..5fe2f62 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddCacheWarmerPass.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers the cache warmers. + * + * @author Fabien Potencier + */ +class AddCacheWarmerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('cache_warmer')) { + return; + } + + $warmers = array(); + foreach ($container->findTaggedServiceIds('kernel.cache_warmer') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $warmers[$priority][] = new Reference($id); + } + + if (empty($warmers)) { + return; + } + + // sort by priority and flatten + krsort($warmers); + $warmers = call_user_func_array('array_merge', $warmers); + + $container->getDefinition('cache_warmer')->replaceArgument(0, $warmers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php new file mode 100644 index 0000000..3c96761 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConsoleCommandPass.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * AddConsoleCommandPass. + * + * @author Grégoire Pineau + */ +class AddConsoleCommandPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $commandServices = $container->findTaggedServiceIds('console.command'); + + foreach ($commandServices as $id => $tags) { + $definition = $container->getDefinition($id); + + if (!$definition->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be public.', $id)); + } + + if ($definition->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must not be abstract.', $id)); + } + + $class = $container->getParameterBag()->resolveValue($definition->getClass()); + $r = new \ReflectionClass($class); + if (!$r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command')) { + throw new \InvalidArgumentException(sprintf('The service "%s" tagged "console.command" must be a subclass of "Symfony\\Component\\Console\\Command\\Command".', $id)); + } + $container->setAlias('console.command.'.strtolower(str_replace('\\', '_', $class)), $id); + } + + $container->setParameter('console.command.ids', array_keys($commandServices)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php new file mode 100644 index 0000000..757c98b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class AddConstraintValidatorsPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('validator.validator_factory')) { + return; + } + + $validators = array(); + foreach ($container->findTaggedServiceIds('validator.constraint_validator') as $id => $attributes) { + if (isset($attributes[0]['alias'])) { + $validators[$attributes[0]['alias']] = $id; + } + } + + $container->getDefinition('validator.validator_factory')->replaceArgument(1, $validators); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php new file mode 100644 index 0000000..6510d02 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers the expression language providers. + * + * @author Fabien Potencier + */ +class AddExpressionLanguageProvidersPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + // routing + if ($container->has('router')) { + $definition = $container->findDefinition('router'); + foreach ($container->findTaggedServiceIds('routing.expression_language_provider') as $id => $attributes) { + $definition->addMethodCall('addExpressionLanguageProvider', array(new Reference($id))); + } + } + + // security + if ($container->has('security.access.expression_voter')) { + $definition = $container->findDefinition('security.access.expression_voter'); + foreach ($container->findTaggedServiceIds('security.expression_language_provider') as $id => $attributes) { + $definition->addMethodCall('addExpressionLanguageProvider', array(new Reference($id))); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php new file mode 100644 index 0000000..6f58fc2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class AddValidatorInitializersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('validator.builder')) { + return; + } + + $validatorBuilder = $container->getDefinition('validator.builder'); + + $initializers = array(); + foreach ($container->findTaggedServiceIds('validator.initializer') as $id => $attributes) { + $initializers[] = new Reference($id); + } + + $validatorBuilder->addMethodCall('addObjectInitializers', array($initializers)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php new file mode 100644 index 0000000..f152ce8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +class CompilerDebugDumpPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $filename = self::getCompilerLogFilename($container); + + $filesystem = new Filesystem(); + $filesystem->dumpFile($filename, implode("\n", $container->getCompiler()->getLog()), null); + try { + $filesystem->chmod($filename, 0666, umask()); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + + public static function getCompilerLogFilename(ContainerInterface $container) + { + $class = $container->getParameter('kernel.container_class'); + + return $container->getParameter('kernel.cache_dir').'/'.$class.'Compiler.log'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php new file mode 100644 index 0000000..a8e1c54 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ConfigCachePass.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +class ConfigCachePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $resourceCheckers = array(); + + foreach ($container->findTaggedServiceIds('config_cache.resource_checker') as $id => $tags) { + $priority = isset($tags[0]['priority']) ? $tags[0]['priority'] : 0; + $resourceCheckers[$priority][] = new Reference($id); + } + + if (empty($resourceCheckers)) { + return; + } + + // sort by priority and flatten + krsort($resourceCheckers); + $resourceCheckers = call_user_func_array('array_merge', $resourceCheckers); + + $container->getDefinition('config_cache_factory')->replaceArgument(0, $resourceCheckers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php new file mode 100644 index 0000000..98fa5c9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\XmlDumper; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * Dumps the ContainerBuilder to a cache file so that it can be used by + * debugging tools such as the debug:container console command. + * + * @author Ryan Weaver + * @author Fabien Potencier + */ +class ContainerBuilderDebugDumpPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $dumper = new XmlDumper($container); + $filename = $container->getParameter('debug.container.dump'); + $filesystem = new Filesystem(); + $filesystem->dumpFile($filename, $dumper->dump(), null); + try { + $filesystem->chmod($filename, 0666, umask()); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php new file mode 100644 index 0000000..6843e55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds all services with the tags "form.type" and "form.type_guesser" as + * arguments of the "form.extension" service. + * + * @author Bernhard Schussek + */ +class FormPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('form.extension')) { + return; + } + + $definition = $container->getDefinition('form.extension'); + + // Builds an array with fully-qualified type class names as keys and service IDs as values + $types = array(); + + foreach ($container->findTaggedServiceIds('form.type') as $serviceId => $tag) { + $serviceDefinition = $container->getDefinition($serviceId); + if (!$serviceDefinition->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as form types are lazy-loaded.', $serviceId)); + } + + // Support type access by FQCN + $types[$serviceDefinition->getClass()] = $serviceId; + } + + $definition->replaceArgument(1, $types); + + $typeExtensions = array(); + + foreach ($container->findTaggedServiceIds('form.type_extension') as $serviceId => $tag) { + $serviceDefinition = $container->getDefinition($serviceId); + if (!$serviceDefinition->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as form type extensions are lazy-loaded.', $serviceId)); + } + + if (isset($tag[0]['extended_type'])) { + $extendedType = $tag[0]['extended_type']; + } else { + throw new \InvalidArgumentException(sprintf('Tagged form type extension must have the extended type configured using the extended_type/extended-type attribute, none was configured for the "%s" service.', $serviceId)); + } + + $typeExtensions[$extendedType][] = $serviceId; + } + + $definition->replaceArgument(2, $typeExtensions); + + // Find all services annotated with "form.type_guesser" + $guessers = array_keys($container->findTaggedServiceIds('form.type_guesser')); + foreach ($guessers as $serviceId) { + $serviceDefinition = $container->getDefinition($serviceId); + if (!$serviceDefinition->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as form type guessers are lazy-loaded.', $serviceId)); + } + } + + $definition->replaceArgument(3, $guessers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php new file mode 100644 index 0000000..3b9fd96 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Abdellatif Ait boudad + */ +class LoggingTranslatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasAlias('logger') || !$container->hasAlias('translator')) { + return; + } + + // skip if the symfony/translation version is lower than 2.6 + if (!interface_exists('Symfony\Component\Translation\TranslatorBagInterface')) { + return; + } + + if ($container->hasParameter('translator.logging') && $container->getParameter('translator.logging')) { + $translatorAlias = $container->getAlias('translator'); + $definition = $container->getDefinition((string) $translatorAlias); + $class = $container->getParameterBag()->resolveValue($definition->getClass()); + + $refClass = new \ReflectionClass($class); + if ($refClass->implementsInterface('Symfony\Component\Translation\TranslatorInterface') && $refClass->implementsInterface('Symfony\Component\Translation\TranslatorBagInterface')) { + $container->getDefinition('translator.logging')->setDecoratedService('translator'); + $container->getDefinition('translation.warmer')->replaceArgument(0, new Reference('translator.logging.inner')); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php new file mode 100644 index 0000000..78ccb79 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ProfilerPass.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged data_collector services to profiler service. + * + * @author Fabien Potencier + */ +class ProfilerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('profiler')) { + return; + } + + $definition = $container->getDefinition('profiler'); + + $collectors = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + foreach ($container->findTaggedServiceIds('data_collector') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $template = null; + + if (isset($attributes[0]['template'])) { + if (!isset($attributes[0]['id'])) { + throw new \InvalidArgumentException(sprintf('Data collector service "%s" must have an id attribute in order to specify a template', $id)); + } + $template = array($attributes[0]['id'], $attributes[0]['template']); + } + + $collectors->insert(array($id, $template), array($priority, --$order)); + } + + $templates = array(); + foreach ($collectors as $collector) { + $definition->addMethodCall('add', array(new Reference($collector[0]))); + $templates[$collector[0]] = $collector[1]; + } + + $container->setParameter('data_collector.templates', $templates); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php new file mode 100644 index 0000000..819827a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PropertyInfoPass.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds extractors to the property_info service. + * + * @author Kévin Dunglas + */ +class PropertyInfoPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('property_info')) { + return; + } + + $listExtractors = $this->findAndSortTaggedServices('property_info.list_extractor', $container); + $container->getDefinition('property_info')->replaceArgument(0, $listExtractors); + + $typeExtractors = $this->findAndSortTaggedServices('property_info.type_extractor', $container); + $container->getDefinition('property_info')->replaceArgument(1, $typeExtractors); + + $descriptionExtractors = $this->findAndSortTaggedServices('property_info.description_extractor', $container); + $container->getDefinition('property_info')->replaceArgument(2, $descriptionExtractors); + + $accessExtractors = $this->findAndSortTaggedServices('property_info.access_extractor', $container); + $container->getDefinition('property_info')->replaceArgument(3, $accessExtractors); + } + + /** + * Finds all services with the given tag name and order them by their priority. + * + * @param string $tagName + * @param ContainerBuilder $container + * + * @return array + */ + private function findAndSortTaggedServices($tagName, ContainerBuilder $container) + { + $services = $container->findTaggedServiceIds($tagName); + + $sortedServices = array(); + foreach ($services as $serviceId => $tags) { + foreach ($tags as $attributes) { + $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; + $sortedServices[$priority][] = new Reference($serviceId); + } + } + + if (empty($sortedServices)) { + return array(); + } + + krsort($sortedServices); + + // Flatten the array + return call_user_func_array('array_merge', $sortedServices); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php new file mode 100644 index 0000000..6ff7c12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/RoutingResolverPass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged routing.loader services to routing.resolver service. + * + * @author Fabien Potencier + */ +class RoutingResolverPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('routing.resolver')) { + return; + } + + $definition = $container->getDefinition('routing.resolver'); + + foreach ($container->findTaggedServiceIds('routing.loader') as $id => $attributes) { + $definition->addMethodCall('addLoader', array(new Reference($id))); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php new file mode 100644 index 0000000..6188fea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerPass.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds all services with the tags "serializer.encoder" and "serializer.normalizer" as + * encoders and normalizers to the Serializer service. + * + * @author Javier Lopez + */ +class SerializerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('serializer')) { + return; + } + + // Looks for all the services tagged "serializer.normalizer" and adds them to the Serializer service + $normalizers = $this->findAndSortTaggedServices('serializer.normalizer', $container); + $container->getDefinition('serializer')->replaceArgument(0, $normalizers); + + // Looks for all the services tagged "serializer.encoders" and adds them to the Serializer service + $encoders = $this->findAndSortTaggedServices('serializer.encoder', $container); + $container->getDefinition('serializer')->replaceArgument(1, $encoders); + } + + /** + * Finds all services with the given tag name and order them by their priority. + * + * @param string $tagName + * @param ContainerBuilder $container + * + * @return array + * + * @throws \RuntimeException + */ + private function findAndSortTaggedServices($tagName, ContainerBuilder $container) + { + $services = $container->findTaggedServiceIds($tagName); + + if (empty($services)) { + throw new \RuntimeException(sprintf('You must tag at least one service as "%s" to use the Serializer service', $tagName)); + } + + $sortedServices = array(); + foreach ($services as $serviceId => $tags) { + foreach ($tags as $attributes) { + $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; + $sortedServices[$priority][] = new Reference($serviceId); + } + } + + krsort($sortedServices); + + // Flatten the array + return call_user_func_array('array_merge', $sortedServices); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php new file mode 100644 index 0000000..4ab3a9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TemplatingPass.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class TemplatingPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->hasDefinition('templating')) { + return; + } + + if ($container->hasDefinition('templating.engine.php')) { + $helpers = array(); + foreach ($container->findTaggedServiceIds('templating.helper') as $id => $attributes) { + if (isset($attributes[0]['alias'])) { + $helpers[$attributes[0]['alias']] = $id; + } + } + + if (count($helpers) > 0) { + $definition = $container->getDefinition('templating.engine.php'); + $definition->addMethodCall('setHelpers', array($helpers)); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php new file mode 100644 index 0000000..f7c0974 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationDumperPass.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged translation.formatter services to translation writer. + */ +class TranslationDumperPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translation.writer')) { + return; + } + + $definition = $container->getDefinition('translation.writer'); + + foreach ($container->findTaggedServiceIds('translation.dumper') as $id => $attributes) { + $definition->addMethodCall('addDumper', array($attributes[0]['alias'], new Reference($id))); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php new file mode 100644 index 0000000..af88585 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslationExtractorPass.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged translation.extractor services to translation extractor. + */ +class TranslationExtractorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translation.extractor')) { + return; + } + + $definition = $container->getDefinition('translation.extractor'); + + foreach ($container->findTaggedServiceIds('translation.extractor') as $id => $attributes) { + if (!isset($attributes[0]['alias'])) { + throw new \RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id)); + } + + $definition->addMethodCall('addExtractor', array($attributes[0]['alias'], new Reference($id))); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php new file mode 100644 index 0000000..4e45016 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TranslatorPass.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +class TranslatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translator.default')) { + return; + } + + $loaders = array(); + foreach ($container->findTaggedServiceIds('translation.loader') as $id => $attributes) { + $loaders[$id][] = $attributes[0]['alias']; + if (isset($attributes[0]['legacy-alias'])) { + $loaders[$id][] = $attributes[0]['legacy-alias']; + } + } + + if ($container->hasDefinition('translation.loader')) { + $definition = $container->getDefinition('translation.loader'); + foreach ($loaders as $id => $formats) { + foreach ($formats as $format) { + $definition->addMethodCall('addLoader', array($format, new Reference($id))); + } + } + } + + $container->findDefinition('translator.default')->replaceArgument(2, $loaders); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php new file mode 100644 index 0000000..ed852fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Find all service tags which are defined, but not used and yield a warning log message. + * + * @author Florian Pfitzer + */ +class UnusedTagsPass implements CompilerPassInterface +{ + private $whitelist = array( + 'console.command', + 'config_cache.resource_checker', + 'data_collector', + 'form.type', + 'form.type_extension', + 'form.type_guesser', + 'kernel.cache_clearer', + 'kernel.cache_warmer', + 'kernel.event_listener', + 'kernel.event_subscriber', + 'kernel.fragment_renderer', + 'monolog.logger', + 'routing.expression_language_provider', + 'routing.loader', + 'security.expression_language_provider', + 'security.remember_me_aware', + 'security.voter', + 'serializer.encoder', + 'serializer.normalizer', + 'templating.helper', + 'translation.dumper', + 'translation.extractor', + 'translation.loader', + 'twig.extension', + 'twig.loader', + 'validator.constraint_validator', + 'validator.initializer', + ); + + public function process(ContainerBuilder $container) + { + $compiler = $container->getCompiler(); + $formatter = $compiler->getLoggingFormatter(); + $tags = array_unique(array_merge($container->findTags(), $this->whitelist)); + + foreach ($container->findUnusedTags() as $tag) { + // skip whitelisted tags + if (in_array($tag, $this->whitelist)) { + continue; + } + + // check for typos + $candidates = array(); + foreach ($tags as $definedTag) { + if ($definedTag === $tag) { + continue; + } + + if (false !== strpos($definedTag, $tag) || levenshtein($tag, $definedTag) <= strlen($tag) / 3) { + $candidates[] = $definedTag; + } + } + + $services = array_keys($container->findTaggedServiceIds($tag)); + $message = sprintf('Tag "%s" was defined on service(s) "%s", but was never used.', $tag, implode('", "', $services)); + if (!empty($candidates)) { + $message .= sprintf(' Did you mean "%s"?', implode('", "', $candidates)); + } + + $compiler->addLogMessage($formatter->format($this, $message)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..a6c9587 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -0,0 +1,524 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * FrameworkExtension configuration structure. + * + * @author Jeremy Mikola + */ +class Configuration implements ConfigurationInterface +{ + private $debug; + + /** + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($debug) + { + $this->debug = (bool) $debug; + } + + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('framework'); + + $rootNode + ->children() + ->scalarNode('secret')->end() + ->scalarNode('http_method_override') + ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") + ->defaultTrue() + ->end() + ->arrayNode('trusted_proxies') + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v) && null !== $v; }) + ->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); }) + ->end() + ->prototype('scalar') + ->validate() + ->ifTrue(function ($v) { + if (empty($v)) { + return false; + } + + if (false !== strpos($v, '/')) { + list($v, $mask) = explode('/', $v, 2); + + if (strcmp($mask, (int) $mask) || $mask < 1 || $mask > (false !== strpos($v, ':') ? 128 : 32)) { + return true; + } + } + + return !filter_var($v, FILTER_VALIDATE_IP); + }) + ->thenInvalid('Invalid proxy IP "%s"') + ->end() + ->end() + ->end() + ->scalarNode('ide')->defaultNull()->end() + ->booleanNode('test')->end() + ->scalarNode('default_locale')->defaultValue('en')->end() + ->arrayNode('trusted_hosts') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ; + + $this->addCsrfSection($rootNode); + $this->addFormSection($rootNode); + $this->addEsiSection($rootNode); + $this->addSsiSection($rootNode); + $this->addFragmentsSection($rootNode); + $this->addProfilerSection($rootNode); + $this->addRouterSection($rootNode); + $this->addSessionSection($rootNode); + $this->addRequestSection($rootNode); + $this->addTemplatingSection($rootNode); + $this->addAssetsSection($rootNode); + $this->addTranslatorSection($rootNode); + $this->addValidationSection($rootNode); + $this->addAnnotationsSection($rootNode); + $this->addSerializerSection($rootNode); + $this->addPropertyAccessSection($rootNode); + $this->addPropertyInfoSection($rootNode); + + return $treeBuilder; + } + + private function addCsrfSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('csrf_protection') + ->canBeEnabled() + ->end() + ->end() + ; + } + + private function addFormSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('form') + ->info('form configuration') + ->canBeEnabled() + ->children() + ->arrayNode('csrf_protection') + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled + ->scalarNode('field_name')->defaultValue('_token')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addEsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('esi') + ->info('esi configuration') + ->canBeEnabled() + ->end() + ->end() + ; + } + + private function addSsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('ssi') + ->info('ssi configuration') + ->canBeEnabled() + ->end() + ->end(); + } + + private function addFragmentsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('fragments') + ->info('fragments configuration') + ->canBeEnabled() + ->children() + ->scalarNode('path')->defaultValue('/_fragment')->end() + ->end() + ->end() + ->end() + ; + } + + private function addProfilerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('profiler') + ->info('profiler configuration') + ->canBeEnabled() + ->children() + ->booleanNode('collect')->defaultTrue()->end() + ->booleanNode('only_exceptions')->defaultFalse()->end() + ->booleanNode('only_master_requests')->defaultFalse()->end() + ->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end() + ->arrayNode('matcher') + ->canBeUnset() + ->performNoDeepMerging() + ->fixXmlConfig('ip') + ->children() + ->scalarNode('path') + ->info('use the urldecoded format') + ->example('^/path to resource/') + ->end() + ->scalarNode('service')->end() + ->arrayNode('ips') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRouterSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('router') + ->info('router configuration') + ->canBeUnset() + ->children() + ->scalarNode('resource')->isRequired()->end() + ->scalarNode('type')->end() + ->scalarNode('http_port')->defaultValue(80)->end() + ->scalarNode('https_port')->defaultValue(443)->end() + ->scalarNode('strict_requirements') + ->info( + "set to true to throw an exception when a parameter does not match the requirements\n". + "set to false to disable exceptions when a parameter does not match the requirements (and return null instead)\n". + "set to null to disable parameter checks against requirements\n". + "'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production" + ) + ->defaultTrue() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addSessionSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('session') + ->info('session configuration') + ->canBeUnset() + ->children() + ->scalarNode('storage_id')->defaultValue('session.storage.native')->end() + ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end() + ->scalarNode('name')->end() + ->scalarNode('cookie_lifetime')->end() + ->scalarNode('cookie_path')->end() + ->scalarNode('cookie_domain')->end() + ->booleanNode('cookie_secure')->end() + ->booleanNode('cookie_httponly')->defaultTrue()->end() + ->booleanNode('use_cookies')->end() + ->scalarNode('gc_divisor')->end() + ->scalarNode('gc_probability')->defaultValue(1)->end() + ->scalarNode('gc_maxlifetime')->end() + ->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end() + ->integerNode('metadata_update_threshold') + ->defaultValue('0') + ->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed') + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRequestSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('request') + ->info('request configuration') + ->canBeUnset() + ->fixXmlConfig('format') + ->children() + ->arrayNode('formats') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && isset($v['mime_type']); }) + ->then(function ($v) { return $v['mime_type']; }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTemplatingSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('templating') + ->info('templating configuration') + ->canBeUnset() + ->children() + ->scalarNode('hinclude_default_template')->defaultNull()->end() + ->scalarNode('cache')->end() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->fixXmlConfig('resource') + ->children() + ->arrayNode('resources') + ->addDefaultChildrenIfNoneSet() + ->prototype('scalar')->defaultValue('FrameworkBundle:Form')->end() + ->validate() + ->ifTrue(function ($v) {return !in_array('FrameworkBundle:Form', $v); }) + ->then(function ($v) { + return array_merge(array('FrameworkBundle:Form'), $v); + }) + ->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('engine') + ->children() + ->arrayNode('engines') + ->example(array('twig')) + ->isRequired() + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('loader') + ->children() + ->arrayNode('loaders') + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addAssetsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('assets') + ->info('assets configuration') + ->canBeUnset() + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultValue('%%s?%%s')->end() + ->scalarNode('base_path')->defaultValue('')->end() + ->arrayNode('base_urls') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('package') + ->children() + ->arrayNode('packages') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultNull()->end() + ->scalarNode('base_path')->defaultValue('')->end() + ->arrayNode('base_urls') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTranslatorSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('translator') + ->info('translator configuration') + ->canBeEnabled() + ->fixXmlConfig('fallback') + ->fixXmlConfig('path') + ->children() + ->arrayNode('fallbacks') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->defaultValue(array('en')) + ->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addValidationSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('validation') + ->info('validation configuration') + ->canBeEnabled() + ->children() + ->scalarNode('cache') + ->beforeNormalization() + // Can be removed in 3.0, once ApcCache support is dropped + ->ifString()->then(function ($v) { return 'apc' === $v ? 'validator.mapping.cache.apc' : $v; }) + ->end() + ->end() + ->booleanNode('enable_annotations')->defaultFalse()->end() + ->arrayNode('static_method') + ->defaultValue(array('loadValidatorMetadata')) + ->prototype('scalar')->end() + ->treatFalseLike(array()) + ->validate() + ->ifTrue(function ($v) { return !is_array($v); }) + ->then(function ($v) { return (array) $v; }) + ->end() + ->end() + ->scalarNode('translation_domain')->defaultValue('validators')->end() + ->booleanNode('strict_email')->defaultFalse()->end() + ->end() + ->end() + ->end() + ; + } + + private function addAnnotationsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('annotations') + ->info('annotation configuration') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('cache')->defaultValue('file')->end() + ->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end() + ->booleanNode('debug')->defaultValue('%kernel.debug%')->end() + ->end() + ->end() + ->end() + ; + } + + private function addSerializerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('serializer') + ->info('serializer configuration') + ->canBeEnabled() + ->children() + ->booleanNode('enable_annotations')->defaultFalse()->end() + ->scalarNode('cache')->end() + ->scalarNode('name_converter')->end() + ->end() + ->end() + ->end() + ; + } + + private function addPropertyAccessSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('property_access') + ->addDefaultsIfNotSet() + ->info('Property access configuration') + ->children() + ->booleanNode('magic_call')->defaultFalse()->end() + ->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end() + ->end() + ->end() + ->end() + ; + } + + private function addPropertyInfoSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('property_info') + ->info('Property info configuration') + ->canBeEnabled() + ->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php new file mode 100644 index 0000000..5fb6b7a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -0,0 +1,991 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Validator\Validation; + +/** + * FrameworkExtension. + * + * @author Fabien Potencier + * @author Jeremy Mikola + * @author Kévin Dunglas + */ +class FrameworkExtension extends Extension +{ + private $formConfigEnabled = false; + private $translationConfigEnabled = false; + private $sessionConfigEnabled = false; + + /** + * Responds to the app.config configuration parameter. + * + * @param array $configs + * @param ContainerBuilder $container + * + * @throws LogicException + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + + $loader->load('web.xml'); + $loader->load('services.xml'); + $loader->load('fragment_renderer.xml'); + + // A translator must always be registered (as support is included by + // default in the Form component). If disabled, an identity translator + // will be used and everything will still work as expected. + $loader->load('translation.xml'); + + // Property access is used by both the Form and the Validator component + $loader->load('property_access.xml'); + + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (isset($config['secret'])) { + $container->setParameter('kernel.secret', $config['secret']); + } + + $container->setParameter('kernel.http_method_override', $config['http_method_override']); + $container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']); + $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']); + $container->setParameter('kernel.default_locale', $config['default_locale']); + + if (!empty($config['test'])) { + $loader->load('test.xml'); + } + + if (isset($config['session'])) { + $this->sessionConfigEnabled = true; + $this->registerSessionConfiguration($config['session'], $container, $loader); + } + + if (isset($config['request'])) { + $this->registerRequestConfiguration($config['request'], $container, $loader); + } + + if ($this->isConfigEnabled($container, $config['form'])) { + $this->formConfigEnabled = true; + $this->registerFormConfiguration($config, $container, $loader); + $config['validation']['enabled'] = true; + + if (!class_exists('Symfony\Component\Validator\Validation')) { + throw new LogicException('The Validator component is required to use the Form component.'); + } + } + + $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); + + if (isset($config['assets'])) { + $this->registerAssetsConfiguration($config['assets'], $container, $loader); + } + + if (isset($config['templating'])) { + $this->registerTemplatingConfiguration($config['templating'], $config['ide'], $container, $loader); + } + + $this->registerValidationConfiguration($config['validation'], $container, $loader); + $this->registerEsiConfiguration($config['esi'], $container, $loader); + $this->registerSsiConfiguration($config['ssi'], $container, $loader); + $this->registerFragmentsConfiguration($config['fragments'], $container, $loader); + $this->registerTranslatorConfiguration($config['translator'], $container); + $this->registerProfilerConfiguration($config['profiler'], $container, $loader); + + if (isset($config['router'])) { + $this->registerRouterConfiguration($config['router'], $container, $loader); + } + + $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); + $this->registerPropertyAccessConfiguration($config['property_access'], $container); + + if (isset($config['serializer'])) { + $this->registerSerializerConfiguration($config['serializer'], $container, $loader); + } + + if (isset($config['property_info'])) { + $this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader); + } + + $loader->load('debug_prod.xml'); + $definition = $container->findDefinition('debug.debug_handlers_listener'); + + if ($container->hasParameter('templating.helper.code.file_link_format')) { + $definition->replaceArgument(5, '%templating.helper.code.file_link_format%'); + } + + if ($container->getParameter('kernel.debug')) { + $loader->load('debug.xml'); + + $definition = $container->findDefinition('http_kernel'); + $definition->replaceArgument(1, new Reference('debug.controller_resolver')); + + // replace the regular event_dispatcher service with the debug one + $definition = $container->findDefinition('event_dispatcher'); + $definition->setPublic(false); + $container->setDefinition('debug.event_dispatcher.parent', $definition); + $container->setAlias('event_dispatcher', 'debug.event_dispatcher'); + } else { + $definition->replaceArgument(2, E_COMPILE_ERROR | E_PARSE | E_ERROR | E_CORE_ERROR); + } + + $this->addClassesToCompile(array( + 'Symfony\\Component\\Config\\FileLocator', + + 'Symfony\\Component\\Debug\\ErrorHandler', + + 'Symfony\\Component\\EventDispatcher\\Event', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', + + 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', + 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', + 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', + 'Symfony\\Component\\HttpKernel\\KernelEvents', + 'Symfony\\Component\\HttpKernel\\Config\\FileLocator', + + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', + // Cannot be included because annotations will parse the big compiled class file + // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', + )); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($container->getParameter('kernel.debug')); + } + + /** + * Loads Form configuration. + * + * @param array $config A configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + * + * @throws \LogicException + */ + private function registerFormConfiguration($config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('form.xml'); + if (null === $config['form']['csrf_protection']['enabled']) { + $config['form']['csrf_protection']['enabled'] = $config['csrf_protection']['enabled']; + } + + if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) { + $loader->load('form_csrf.xml'); + + $container->setParameter('form.type_extension.csrf.enabled', true); + $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']); + } else { + $container->setParameter('form.type_extension.csrf.enabled', false); + } + } + + /** + * Loads the ESI configuration. + * + * @param array $config An ESI configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerEsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + $loader->load('esi.xml'); + } + + /** + * Loads the SSI configuration. + * + * @param array $config An SSI configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerSsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + $loader->load('ssi.xml'); + } + + /** + * Loads the fragments configuration. + * + * @param array $config A fragments configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerFragmentsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + $loader->load('fragment_listener.xml'); + $container->setParameter('fragment.path', $config['path']); + } + + /** + * Loads the profiler configuration. + * + * @param array $config A profiler configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + * + * @throws \LogicException + */ + private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + // this is needed for the WebProfiler to work even if the profiler is disabled + $container->setParameter('data_collector.templates', array()); + + return; + } + + $loader->load('profiling.xml'); + $loader->load('collectors.xml'); + + if ($this->formConfigEnabled) { + $loader->load('form_debug.xml'); + } + + if ($this->translationConfigEnabled) { + $loader->load('translation_debug.xml'); + $container->getDefinition('translator.data_collector')->setDecoratedService('translator'); + } + + $container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']); + $container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']); + + // Choose storage class based on the DSN + list($class) = explode(':', $config['dsn'], 2); + if ('file' !== $class) { + throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class)); + } + + $container->setParameter('profiler.storage.dsn', $config['dsn']); + + if (isset($config['matcher'])) { + if (isset($config['matcher']['service'])) { + $container->setAlias('profiler.request_matcher', $config['matcher']['service']); + } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path']) || isset($config['matcher']['ips'])) { + $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher'); + $definition->setPublic(false); + + if (isset($config['matcher']['ip'])) { + $definition->addMethodCall('matchIp', array($config['matcher']['ip'])); + } + + if (isset($config['matcher']['ips'])) { + $definition->addMethodCall('matchIps', array($config['matcher']['ips'])); + } + + if (isset($config['matcher']['path'])) { + $definition->addMethodCall('matchPath', array($config['matcher']['path'])); + } + } + } + + if (!$config['collect']) { + $container->getDefinition('profiler')->addMethodCall('disable', array()); + } + } + + /** + * Loads the router configuration. + * + * @param array $config A router configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('routing.xml'); + + $container->setParameter('router.resource', $config['resource']); + $container->setParameter('router.cache_class_prefix', $container->getParameter('kernel.name').ucfirst($container->getParameter('kernel.environment'))); + $router = $container->findDefinition('router.default'); + $argument = $router->getArgument(2); + $argument['strict_requirements'] = $config['strict_requirements']; + if (isset($config['type'])) { + $argument['resource_type'] = $config['type']; + } + $router->replaceArgument(2, $argument); + + $container->setParameter('request_listener.http_port', $config['http_port']); + $container->setParameter('request_listener.https_port', $config['https_port']); + + $this->addClassesToCompile(array( + 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'Symfony\\Component\\Routing\\RequestContext', + 'Symfony\\Component\\Routing\\Router', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', + $container->findDefinition('router.default')->getClass(), + )); + } + + /** + * Loads the session configuration. + * + * @param array $config A session configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerSessionConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('session.xml'); + + // session storage + $container->setAlias('session.storage', $config['storage_id']); + $options = array(); + foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor') as $key) { + if (isset($config[$key])) { + $options[$key] = $config[$key]; + } + } + + $container->setParameter('session.storage.options', $options); + + // session handler (the internal callback registered with PHP session management) + if (null === $config['handler_id']) { + // Set the handler class to be null + $container->getDefinition('session.storage.native')->replaceArgument(1, null); + $container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null); + } else { + $handlerId = $config['handler_id']; + + if ($config['metadata_update_threshold'] > 0) { + $container->getDefinition('session.handler.write_check')->addArgument(new Reference($handlerId)); + $handlerId = 'session.handler.write_check'; + } + + $container->setAlias('session.handler', $handlerId); + } + + $container->setParameter('session.save_path', $config['save_path']); + + $this->addClassesToCompile(array( + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy', + $container->getDefinition('session')->getClass(), + )); + + if ($container->hasDefinition($config['storage_id'])) { + $this->addClassesToCompile(array( + $container->findDefinition('session.storage')->getClass(), + )); + } + + $container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']); + } + + /** + * Loads the request configuration. + * + * @param array $config A request configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerRequestConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if ($config['formats']) { + $loader->load('request.xml'); + $container + ->getDefinition('request.add_request_formats_listener') + ->replaceArgument(0, $config['formats']) + ; + } + } + + /** + * Loads the templating configuration. + * + * @param array $config A templating configuration array + * @param string $ide + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerTemplatingConfiguration(array $config, $ide, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('templating.xml'); + + if (!$container->hasParameter('templating.helper.code.file_link_format')) { + $links = array( + 'textmate' => 'txmt://open?url=file://%%f&line=%%l', + 'macvim' => 'mvim://open?url=file://%%f&line=%%l', + 'emacs' => 'emacs://open?url=file://%%f&line=%%l', + 'sublime' => 'subl://open?url=file://%%f&line=%%l', + ); + + $container->setParameter('templating.helper.code.file_link_format', str_replace('%', '%%', ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format')) ?: (isset($links[$ide]) ? $links[$ide] : $ide)); + } + + $container->setParameter('fragment.renderer.hinclude.global_template', $config['hinclude_default_template']); + + if ($container->getParameter('kernel.debug')) { + $logger = new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE); + + $container->getDefinition('templating.loader.cache') + ->addTag('monolog.logger', array('channel' => 'templating')) + ->addMethodCall('setLogger', array($logger)); + $container->getDefinition('templating.loader.chain') + ->addTag('monolog.logger', array('channel' => 'templating')) + ->addMethodCall('setLogger', array($logger)); + } + + if (!empty($config['loaders'])) { + $loaders = array_map(function ($loader) { return new Reference($loader); }, $config['loaders']); + + // Use a delegation unless only a single loader was registered + if (1 === count($loaders)) { + $container->setAlias('templating.loader', (string) reset($loaders)); + } else { + $container->getDefinition('templating.loader.chain')->addArgument($loaders); + $container->setAlias('templating.loader', 'templating.loader.chain'); + } + } + + $container->setParameter('templating.loader.cache.path', null); + if (isset($config['cache'])) { + // Wrap the existing loader with cache (must happen after loaders are registered) + $container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader')); + $loaderCache = $container->getDefinition('templating.loader.cache'); + $container->setParameter('templating.loader.cache.path', $config['cache']); + + $container->setDefinition('templating.loader', $loaderCache); + } + + $this->addClassesToCompile(array( + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', + $container->findDefinition('templating.locator')->getClass(), + )); + + $container->setParameter('templating.engines', $config['engines']); + $engines = array_map(function ($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']); + + // Use a delegation unless only a single engine was registered + if (1 === count($engines)) { + $container->setAlias('templating', (string) reset($engines)); + } else { + foreach ($engines as $engine) { + $container->getDefinition('templating.engine.delegating')->addMethodCall('addEngine', array($engine)); + } + $container->setAlias('templating', 'templating.engine.delegating'); + } + + $container->getDefinition('fragment.renderer.hinclude') + ->addTag('kernel.fragment_renderer', array('alias' => 'hinclude')) + ->replaceArgument(0, new Reference('templating')) + ; + + // configure the PHP engine if needed + if (in_array('php', $config['engines'], true)) { + $loader->load('templating_php.xml'); + + $container->setParameter('templating.helper.form.resources', $config['form']['resources']); + + if ($container->getParameter('kernel.debug')) { + $loader->load('templating_debug.xml'); + + $container->setDefinition('templating.engine.php', $container->findDefinition('debug.templating.engine.php')); + $container->setAlias('debug.templating.engine.php', 'templating.engine.php'); + } + + $this->addClassesToCompile(array( + 'Symfony\\Component\\Templating\\Storage\\FileStorage', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', + )); + + if ($container->hasDefinition('assets.packages')) { + $container->getDefinition('templating.helper.assets')->replaceArgument(0, new Reference('assets.packages')); + } else { + $container->removeDefinition('templating.helper.assets'); + } + } + } + + /** + * Loads the assets configuration. + * + * @param array $config A assets configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerAssetsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('assets.xml'); + + $defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], '_default'); + + $defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion); + $container->setDefinition('assets._default_package', $defaultPackage); + + $namedPackages = array(); + foreach ($config['packages'] as $name => $package) { + if (null === $package['version']) { + $version = $defaultVersion; + } else { + $format = $package['version_format'] ?: $config['version_format']; + $version = $this->createVersion($container, $package['version'], $format, $name); + } + + $container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version)); + $namedPackages[$name] = new Reference('assets._package_'.$name); + } + + $container->getDefinition('assets.packages') + ->replaceArgument(0, new Reference('assets._default_package')) + ->replaceArgument(1, $namedPackages) + ; + } + + /** + * Returns a definition for an asset package. + */ + private function createPackageDefinition($basePath, array $baseUrls, Reference $version) + { + if ($basePath && $baseUrls) { + throw new \LogicException('An asset package cannot have base URLs and base paths.'); + } + + if (!$baseUrls) { + $package = new DefinitionDecorator('assets.path_package'); + + return $package + ->setPublic(false) + ->replaceArgument(0, $basePath) + ->replaceArgument(1, $version) + ; + } + + $package = new DefinitionDecorator('assets.url_package'); + + return $package + ->setPublic(false) + ->replaceArgument(0, $baseUrls) + ->replaceArgument(1, $version) + ; + } + + private function createVersion(ContainerBuilder $container, $version, $format, $name) + { + if (null === $version) { + return new Reference('assets.empty_version_strategy'); + } + + $def = new DefinitionDecorator('assets.static_version_strategy'); + $def + ->replaceArgument(0, $version) + ->replaceArgument(1, $format) + ; + $container->setDefinition('assets._version_'.$name, $def); + + return new Reference('assets._version_'.$name); + } + + /** + * Loads the translator configuration. + * + * @param array $config A translator configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + */ + private function registerTranslatorConfiguration(array $config, ContainerBuilder $container) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + $this->translationConfigEnabled = true; + + // Use the "real" translator instead of the identity default + $container->setAlias('translator', 'translator.default'); + $translator = $container->findDefinition('translator.default'); + $translator->addMethodCall('setFallbackLocales', array($config['fallbacks'])); + + $container->setParameter('translator.logging', $config['logging']); + + // Discover translation directories + $dirs = array(); + if (class_exists('Symfony\Component\Validator\Validation')) { + $r = new \ReflectionClass('Symfony\Component\Validator\Validation'); + + $dirs[] = dirname($r->getFileName()).'/Resources/translations'; + } + if (class_exists('Symfony\Component\Form\Form')) { + $r = new \ReflectionClass('Symfony\Component\Form\Form'); + + $dirs[] = dirname($r->getFileName()).'/Resources/translations'; + } + if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) { + $r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException'); + + $dirs[] = dirname($r->getFileName()).'/../Resources/translations'; + } + $overridePath = $container->getParameter('kernel.root_dir').'/Resources/%s/translations'; + foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { + $reflection = new \ReflectionClass($class); + if (is_dir($dir = dirname($reflection->getFileName()).'/Resources/translations')) { + $dirs[] = $dir; + } + if (is_dir($dir = sprintf($overridePath, $bundle))) { + $dirs[] = $dir; + } + } + foreach ($config['paths'] as $dir) { + if (is_dir($dir)) { + $dirs[] = $dir; + } else { + throw new \UnexpectedValueException(sprintf('%s defined in translator.paths does not exist or is not a directory', $dir)); + } + } + if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/translations')) { + $dirs[] = $dir; + } + + // Register translation resources + if ($dirs) { + foreach ($dirs as $dir) { + $container->addResource(new DirectoryResource($dir)); + } + + $files = array(); + $finder = Finder::create() + ->files() + ->filter(function (\SplFileInfo $file) { + return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); + }) + ->in($dirs) + ; + + $locales = array(); + foreach ($finder as $file) { + list($domain, $locale, $format) = explode('.', $file->getBasename(), 3); + if (!isset($files[$locale])) { + $files[$locale] = array(); + } + + $files[$locale][] = (string) $file; + } + + $options = array_merge( + $translator->getArgument(3), + array('resource_files' => $files) + ); + + $translator->replaceArgument(3, $options); + } + } + + /** + * Loads the validator configuration. + * + * @param array $config A validation configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + $loader->load('validator.xml'); + + $validatorBuilder = $container->getDefinition('validator.builder'); + + $container->setParameter('validator.translation_domain', $config['translation_domain']); + + list($xmlMappings, $yamlMappings) = $this->getValidatorMappingFiles($container); + if (count($xmlMappings) > 0) { + $validatorBuilder->addMethodCall('addXmlMappings', array($xmlMappings)); + } + + if (count($yamlMappings) > 0) { + $validatorBuilder->addMethodCall('addYamlMappings', array($yamlMappings)); + } + + $definition = $container->findDefinition('validator.email'); + $definition->replaceArgument(0, $config['strict_email']); + + if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) { + $validatorBuilder->addMethodCall('enableAnnotationMapping', array(new Reference('annotation_reader'))); + } + + if (array_key_exists('static_method', $config) && $config['static_method']) { + foreach ($config['static_method'] as $methodName) { + $validatorBuilder->addMethodCall('addMethodMapping', array($methodName)); + } + } + + if (isset($config['cache'])) { + $container->setParameter( + 'validator.mapping.cache.prefix', + 'validator_'.hash('sha256', $container->getParameter('kernel.root_dir')) + ); + + $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference($config['cache']))); + } + } + + private function getValidatorMappingFiles(ContainerBuilder $container) + { + $files = array(array(), array()); + + if (interface_exists('Symfony\Component\Form\FormInterface')) { + $reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface'); + $files[0][] = dirname($reflClass->getFileName()).'/Resources/config/validation.xml'; + $container->addResource(new FileResource($files[0][0])); + } + + $bundles = $container->getParameter('kernel.bundles'); + foreach ($bundles as $bundle) { + $reflection = new \ReflectionClass($bundle); + $dirname = dirname($reflection->getFileName()); + + if (is_file($file = $dirname.'/Resources/config/validation.xml')) { + $files[0][] = realpath($file); + $container->addResource(new FileResource($file)); + } + + if (is_file($file = $dirname.'/Resources/config/validation.yml')) { + $files[1][] = realpath($file); + $container->addResource(new FileResource($file)); + } + + if (is_dir($dir = $dirname.'/Resources/config/validation')) { + foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { + $files[0][] = $file->getRealpath(); + } + foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { + $files[1][] = $file->getRealpath(); + } + + $container->addResource(new DirectoryResource($dir)); + } + } + + return $files; + } + + private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, $loader) + { + $loader->load('annotations.xml'); + + if ('none' !== $config['cache']) { + if ('file' === $config['cache']) { + $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']); + if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { + throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir)); + } + + $container + ->getDefinition('annotations.filesystem_cache') + ->replaceArgument(0, $cacheDir) + ; + } + + $container + ->getDefinition('annotations.cached_reader') + ->replaceArgument(1, new Reference('file' !== $config['cache'] ? $config['cache'] : 'annotations.filesystem_cache')) + ->replaceArgument(2, $config['debug']) + ; + $container->setAlias('annotation_reader', 'annotations.cached_reader'); + } + } + + private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container) + { + $container + ->getDefinition('property_accessor') + ->replaceArgument(0, $config['magic_call']) + ->replaceArgument(1, $config['throw_exception_on_invalid_index']) + ; + } + + /** + * Loads the security configuration. + * + * @param array $config A CSRF configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + * + * @throws \LogicException + */ + private function registerSecurityCsrfConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + if (!$this->sessionConfigEnabled) { + throw new \LogicException('CSRF protection needs sessions to be enabled.'); + } + + // Enable services for CSRF protection (even without forms) + $loader->load('security_csrf.xml'); + } + + /** + * Loads the serializer configuration. + * + * @param array $config A serializer configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$config['enabled']) { + return; + } + + $loader->load('serializer.xml'); + $chainLoader = $container->getDefinition('serializer.mapping.chain_loader'); + + $serializerLoaders = array(); + if (isset($config['enable_annotations']) && $config['enable_annotations']) { + $annotationLoader = new Definition( + 'Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', + array(new Reference('annotation_reader')) + ); + $annotationLoader->setPublic(false); + + $serializerLoaders[] = $annotationLoader; + } + + $bundles = $container->getParameter('kernel.bundles'); + foreach ($bundles as $bundle) { + $reflection = new \ReflectionClass($bundle); + $dirname = dirname($reflection->getFileName()); + + if (is_file($file = $dirname.'/Resources/config/serialization.xml')) { + $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array(realpath($file))); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + $container->addResource(new FileResource($file)); + } + + if (is_file($file = $dirname.'/Resources/config/serialization.yml')) { + $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array(realpath($file))); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + $container->addResource(new FileResource($file)); + } + + if (is_dir($dir = $dirname.'/Resources/config/serialization')) { + foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { + $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($file->getRealpath())); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + } + foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { + $definition = new Definition('Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader', array($file->getRealpath())); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + } + + $container->addResource(new DirectoryResource($dir)); + } + } + + $chainLoader->replaceArgument(0, $serializerLoaders); + + if (isset($config['cache']) && $config['cache']) { + $container->setParameter( + 'serializer.mapping.cache.prefix', + 'serializer_'.hash('sha256', $container->getParameter('kernel.root_dir')) + ); + + $container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument( + 1, new Reference($config['cache']) + ); + } + + if (isset($config['name_converter']) && $config['name_converter']) { + $container->getDefinition('serializer.normalizer.object')->replaceArgument(1, new Reference($config['name_converter'])); + } + } + + /** + * Loads property info configuration. + * + * @param array $config + * @param ContainerBuilder $container + * @param XmlFileLoader $loader + */ + private function registerPropertyInfoConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$config['enabled']) { + return; + } + + $loader->load('property_info.xml'); + + if (class_exists('phpDocumentor\Reflection\ClassReflector')) { + $definition = $container->register('property_info.php_doc_extractor', 'Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor'); + $definition->addTag('property_info.description_extractor', array('priority' => -1000)); + $definition->addTag('property_info.type_extractor', array('priority' => -1001)); + } + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/symfony'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php new file mode 100644 index 0000000..6c248d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\EventListener; + +use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Sets the session in the request. + * + * @author Fabien Potencier + */ +class SessionListener extends BaseSessionListener +{ + /** + * @var ContainerInterface + */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + protected function getSession() + { + if (!$this->container->has('session')) { + return; + } + + return $this->container->get('session'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php new file mode 100644 index 0000000..b32faa2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\EventListener; + +use Symfony\Component\HttpKernel\EventListener\TestSessionListener as BaseTestSessionListener; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * TestSessionListener. + * + * @author Fabien Potencier + */ +class TestSessionListener extends BaseTestSessionListener +{ + protected $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + protected function getSession() + { + if (!$this->container->has('session')) { + return; + } + + return $this->container->get('session'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php new file mode 100644 index 0000000..94062da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheClearerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDumpPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; +use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FrameworkBundle extends Bundle +{ + public function boot() + { + ErrorHandler::register(null, false)->throwAt($this->container->getParameter('debug.error_handler.throw_at'), true); + + if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { + Request::setTrustedProxies($trustedProxies); + } + + if ($this->container->getParameter('kernel.http_method_override')) { + Request::enableHttpMethodParameterOverride(); + } + + if ($trustedHosts = $this->container->getParameter('kernel.trusted_hosts')) { + Request::setTrustedHosts($trustedHosts); + } + } + + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new RoutingResolverPass()); + $container->addCompilerPass(new ProfilerPass()); + // must be registered before removing private services as some might be listeners/subscribers + // but as late as possible to get resolved parameters + $container->addCompilerPass(new RegisterListenersPass(), PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new TemplatingPass()); + $container->addCompilerPass(new AddConstraintValidatorsPass()); + $container->addCompilerPass(new AddValidatorInitializersPass()); + $container->addCompilerPass(new AddConsoleCommandPass()); + $container->addCompilerPass(new FormPass()); + $container->addCompilerPass(new TranslatorPass()); + $container->addCompilerPass(new LoggingTranslatorPass()); + $container->addCompilerPass(new AddCacheWarmerPass()); + $container->addCompilerPass(new AddCacheClearerPass()); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + $container->addCompilerPass(new TranslationExtractorPass()); + $container->addCompilerPass(new TranslationDumperPass()); + $container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new SerializerPass()); + $container->addCompilerPass(new PropertyInfoPass()); + + if ($container->getParameter('kernel.debug')) { + $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new ConfigCachePass()); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php new file mode 100644 index 0000000..fad4adc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\HttpCache; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\HttpCache\HttpCache as BaseHttpCache; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpCache\Store; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Manages HTTP cache objects in a Container. + * + * @author Fabien Potencier + */ +abstract class HttpCache extends BaseHttpCache +{ + protected $cacheDir; + protected $kernel; + + /** + * Constructor. + * + * @param HttpKernelInterface $kernel An HttpKernelInterface instance + * @param string $cacheDir The cache directory (default used if null) + */ + public function __construct(HttpKernelInterface $kernel, $cacheDir = null) + { + $this->kernel = $kernel; + $this->cacheDir = $cacheDir; + + parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions())); + } + + /** + * Forwards the Request to the backend and returns the Response. + * + * @param Request $request A Request instance + * @param bool $raw Whether to catch exceptions or not + * @param Response $entry A Response instance (the stale entry if present, null otherwise) + * + * @return Response A Response instance + */ + protected function forward(Request $request, $raw = false, Response $entry = null) + { + $this->getKernel()->boot(); + $this->getKernel()->getContainer()->set('cache', $this); + $this->getKernel()->getContainer()->set($this->getSurrogate()->getName(), $this->getSurrogate()); + + return parent::forward($request, $raw, $entry); + } + + /** + * Returns an array of options to customize the Cache configuration. + * + * @return array An array of options + */ + protected function getOptions() + { + return array(); + } + + protected function createSurrogate() + { + return new Esi(); + } + + protected function createStore() + { + return new Store($this->cacheDir ?: $this->kernel->getCacheDir().'/http_cache'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php new file mode 100644 index 0000000..fec5b72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Kernel; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Routing\RouteCollectionBuilder; + +/** + * A Kernel that provides configuration hooks. + * + * @author Ryan Weaver + * @author Fabien Potencier + */ +trait MicroKernelTrait +{ + /** + * Add or import routes into your application. + * + * $routes->import('config/routing.yml'); + * $routes->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); + * + * @param RouteCollectionBuilder $routes + */ + abstract protected function configureRoutes(RouteCollectionBuilder $routes); + + /** + * Configures the container. + * + * You can register extensions: + * + * $c->loadFromExtension('framework', array( + * 'secret' => '%secret%' + * )); + * + * Or services: + * + * $c->register('halloween', 'FooBundle\HalloweenProvider'); + * + * Or parameters: + * + * $c->setParameter('halloween', 'lot of fun'); + * + * @param ContainerBuilder $c + * @param LoaderInterface $loader + */ + abstract protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader); + + /** + * {@inheritdoc} + */ + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(function (ContainerBuilder $container) use ($loader) { + $container->loadFromExtension('framework', array( + 'router' => array( + 'resource' => 'kernel:loadRoutes', + 'type' => 'service', + ), + )); + + $this->configureContainer($container, $loader); + + $container->addObjectResource($this); + }); + } + + /** + * @internal + */ + public function loadRoutes(LoaderInterface $loader) + { + $routes = new RouteCollectionBuilder($loader); + $this->configureRoutes($routes); + + return $routes->build(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml new file mode 100644 index 0000000..ad469d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml new file mode 100644 index 0000000..4f2e1fb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml new file mode 100644 index 0000000..b3b80e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml new file mode 100644 index 0000000..d5d7855 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml @@ -0,0 +1,25 @@ + + + + + + %kernel.cache_dir%/%kernel.container_class%.xml + -1 + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml new file mode 100644 index 0000000..3ef3ec9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.xml @@ -0,0 +1,25 @@ + + + + + + 0 + + + + + + + null + + null + null + true + null + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml new file mode 100644 index 0000000..86600a8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/esi.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml new file mode 100644 index 0000000..5d74391 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml new file mode 100644 index 0000000..edc7bd4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -0,0 +1,17 @@ + + + + + + + + + %form.type_extension.csrf.enabled% + %form.type_extension.csrf.field_name% + + %validator.translation_domain% + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml new file mode 100644 index 0000000..9c2dfad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_debug.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_listener.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_listener.xml new file mode 100644 index 0000000..332e767 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_listener.xml @@ -0,0 +1,14 @@ + + + + + + + + + %fragment.path% + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml new file mode 100644 index 0000000..3f0d319 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -0,0 +1,49 @@ + + + + + + + /_fragment + + + + + + + %kernel.debug% + + + + + + + %fragment.path% + + + + + + %fragment.renderer.hinclude.global_template% + %fragment.path% + + + + + + + + %fragment.path% + + + + + + + + %fragment.path% + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml new file mode 100644 index 0000000..fdfd8e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/profiling.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + %profiler.storage.dsn% + + + + + + + + %profiler_listener.only_exceptions% + %profiler_listener.only_master_requests% + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml new file mode 100644 index 0000000..1ebee06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml new file mode 100644 index 0000000..33a0a66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_info.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/request.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/request.xml new file mode 100644 index 0000000..323744e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/request.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php new file mode 100644 index 0000000..432ccff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_dev.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * This file implements rewrite rules for PHP built-in web server. + * + * See: http://www.php.net/manual/en/features.commandline.webserver.php + * + * If you have custom directory layout, then you have to write your own router + * and pass it as a value to 'router' option of server:run command. + * + * @author: Michał Pipa + * @author: Albert Jessurum + */ + +// Workaround https://bugs.php.net/64566 +if (ini_get('auto_prepend_file') && !in_array(realpath(ini_get('auto_prepend_file')), get_included_files(), true)) { + require ini_get('auto_prepend_file'); +} + +if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) { + return false; +} + +$_SERVER = array_merge($_SERVER, $_ENV); +$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app_dev.php'; + +// Since we are rewriting to app_dev.php, adjust SCRIPT_NAME and PHP_SELF accordingly +$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR.'app_dev.php'; +$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR.'app_dev.php'; + +require 'app_dev.php'; + +error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php new file mode 100644 index 0000000..97613a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_prod.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * This file implements rewrite rules for PHP built-in web server. + * + * See: http://www.php.net/manual/en/features.commandline.webserver.php + * + * If you have custom directory layout, then you have to write your own router + * and pass it as a value to 'router' option of server:run command. + * + * @author: Michał Pipa + * @author: Albert Jessurum + */ + +// Workaround https://bugs.php.net/64566 +if (ini_get('auto_prepend_file') && !in_array(realpath(ini_get('auto_prepend_file')), get_included_files(), true)) { + require ini_get('auto_prepend_file'); +} + +if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) { + return false; +} + +$_SERVER = array_merge($_SERVER, $_ENV); +$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app.php'; + +// Since we are rewriting to app.php, adjust SCRIPT_NAME and PHP_SELF accordingly +$_SERVER['SCRIPT_NAME'] = DIRECTORY_SEPARATOR.'app.php'; +$_SERVER['PHP_SELF'] = DIRECTORY_SEPARATOR.'app.php'; + +require 'app.php'; + +error_log(sprintf('%s:%d [%d]: %s', $_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_PORT'], http_response_code(), $_SERVER['REQUEST_URI']), 4); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_test.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_test.php new file mode 100644 index 0000000..5b020d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/router_test.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * This file implements rewrite rules for PHP built-in web server. + * + * See: http://www.php.net/manual/en/features.commandline.webserver.php + * + * If you have custom directory layout, then you have to write your own router + * and pass it as a value to 'router' option of server:run command. + * + * @author: Michał Pipa + * @author: Albert Jessurum + */ + +if (is_file($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) { + return false; +} + +$_SERVER = array_merge($_SERVER, $_ENV); +$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'app_test.php'; + +require 'app_test.php'; diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml new file mode 100644 index 0000000..6050686 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -0,0 +1,102 @@ + + + + + + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper + %router.cache_class_prefix%UrlMatcher + %router.cache_class_prefix%UrlGenerator + localhost + http + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %router.resource% + + %kernel.cache_dir% + %kernel.debug% + %router.options.generator_class% + %router.options.generator_base_class% + %router.options.generator_dumper_class% + %router.options.generator.cache_class% + %router.options.matcher_class% + %router.options.matcher_base_class% + %router.options.matcher_dumper_class% + %router.options.matcher.cache_class% + + + + + + + + + + + + %router.request_context.base_url% + GET + %router.request_context.host% + %router.request_context.scheme% + %request_listener.http_port% + %request_listener.https_port% + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd new file mode 100644 index 0000000..5e26bd1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml new file mode 100644 index 0000000..354b515 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml new file mode 100644 index 0000000..d514a36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + null + + + + + + + + + + + + + + + null + + + + + + %serializer.mapping.cache.prefix% + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml new file mode 100644 index 0000000..acb465b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.root_dir%/Resources + + + + %kernel.secret% + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml new file mode 100644 index 0000000..2a4816a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml @@ -0,0 +1,62 @@ + + + + + + _sf2_meta + + + + + + + + + + + %session.metadata.storage_key% + %session.metadata.update_threshold% + + + + %session.storage.options% + + + + + + + + + + + + + + + %kernel.cache_dir%/sessions + MOCKSESSID + + + + + %session.save_path% + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml new file mode 100644 index 0000000..fe320e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml new file mode 100644 index 0000000..d4d0053 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + %kernel.cache_dir% + + + + + + %kernel.root_dir%/Resources + + + + + + + + + + + + + + + %templating.loader.cache.path% + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml new file mode 100644 index 0000000..f2bdc45 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_debug.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + %kernel.charset% + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml new file mode 100644 index 0000000..8014814 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating_php.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + %kernel.charset% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %templating.helper.code.file_link_format% + %kernel.root_dir% + %kernel.charset% + + + + + + + + + + + + + + + + + + + + %templating.helper.form.resources% + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml new file mode 100644 index 0000000..ebb311c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + %test.client.parameters% + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml new file mode 100644 index 0000000..c31bcbf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + %kernel.cache_dir%/translations + %kernel.debug% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation_debug.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation_debug.xml new file mode 100644 index 0000000..fc7d2fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation_debug.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml new file mode 100644 index 0000000..ac153f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + %validator.translation_domain% + + + + + + + %validator.mapping.cache.prefix% + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml new file mode 100644 index 0000000..7787e1d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + %kernel.charset% + + + + + + + + + + %kernel.default_locale% + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/meta/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css new file mode 100644 index 0000000..6b81755 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/body.css @@ -0,0 +1,138 @@ +/* +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 3.1.2 +build: 56 +*/ +.sf-reset div,.sf-reset dl,.sf-reset dt,.sf-reset dd,.sf-reset ul,.sf-reset ol,.sf-reset li,.sf-reset h1,.sf-reset h2,.sf-reset h3,.sf-reset h4,.sf-reset h5,.sf-reset h6,.sf-reset pre,.sf-reset code,.sf-reset form,.sf-reset fieldset,.sf-reset legend,.sf-reset input,.sf-reset textarea,.sf-reset p,.sf-reset blockquote,.sf-reset th,.sf-reset td{margin:0;padding:0;}.sf-reset table{border-collapse:collapse;border-spacing:0;}.sf-reset fieldset,.sf-reset img{border:0;}.sf-reset address,.sf-reset caption,.sf-reset cite,.sf-reset code,.sf-reset dfn,.sf-reset em,.sf-reset strong,.sf-reset th,.sf-reset var{font-style:normal;font-weight:normal;}.sf-reset li{list-style:none;}.sf-reset caption,.sf-reset th{text-align:left;}.sf-reset h1,.sf-reset h2,.sf-reset h3,.sf-reset h4,.sf-reset h5,.sf-reset h6{font-size:100%;font-weight:normal;}.sf-reset q:before,.sf-reset q:after{content:'';}.sf-reset abbr,.sf-reset acronym{border:0;font-variant:normal;}.sf-reset sup{vertical-align:text-top;}.sf-reset sub{vertical-align:text-bottom;}.sf-reset input,.sf-reset textarea,.sf-reset select{font-family:inherit;font-size:inherit;font-weight:inherit;}.sf-reset input,.sf-reset textarea,.sf-reset select{font-size:100%;}.sf-reset legend{color:#000;} +.sf-reset abbr { + border-bottom: 1px dotted #000; + cursor: help; +} +.sf-reset p { + font-size: 14px; + line-height: 20px; + padding-bottom: 20px; +} +.sf-reset strong { + color: #313131; + font-weight: bold; +} +.sf-reset a { + color: #6c6159; +} +.sf-reset a img { + border: none; +} +.sf-reset a:hover { + text-decoration: underline; +} +.sf-reset em { + font-style: italic; +} +.sf-reset h2, +.sf-reset h3 { + font-weight: bold; +} +.sf-reset h1 { + font-family: Georgia, "Times New Roman", Times, serif; + font-size: 20px; + color: #313131; + word-break: break-all; +} +.sf-reset li { + padding-bottom: 10px; +} +.sf-reset .block { + -moz-border-radius: 16px; + -webkit-border-radius: 16px; + border-radius: 16px; + margin-bottom: 20px; + background-color: #FFFFFF; + border: 1px solid #dfdfdf; + padding: 40px 50px; + word-break: break-all; +} +.sf-reset h2 { + font-size: 16px; + font-family: Arial, Helvetica, sans-serif; +} +.sf-reset li a { + background: none; + color: #868686; + text-decoration: none; +} +.sf-reset li a:hover { + background: none; + color: #313131; + text-decoration: underline; +} +.sf-reset ol { + padding: 10px 0; +} +.sf-reset ol li { + list-style: decimal; + margin-left: 20px; + padding: 2px; + padding-bottom: 20px; +} +.sf-reset ol ol li { + list-style-position: inside; + margin-left: 0; + white-space: nowrap; + font-size: 12px; + padding-bottom: 0; +} +.sf-reset li .selected { + background-color: #ffd; +} +.sf-button { + display: -moz-inline-box; + display: inline-block; + text-align: center; + vertical-align: middle; + border: 0; + background: transparent none; + text-transform: uppercase; + cursor: pointer; + font: bold 11px Arial, Helvetica, sans-serif; +} +.sf-button span { + text-decoration: none; + display: block; + height: 28px; + float: left; +} +.sf-button .border-l { + text-decoration: none; + display: block; + height: 28px; + float: left; + padding: 0 0 0 7px; + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQtJREFUeNpiPHnyJAMakARiByDWYEGT8ADiYGVlZStubm5xlv///4MEQYoKZGRkQkRERLRYWVl5wYJQyXBZWdkwCQkJUxAHKgaWlAHSLqKiosb//v1DsYMFKGCvoqJiDmQzwXTAJYECulxcXNLoumCSoszMzDzoumDGghQwYZUECWIzkrAkSIIGOmlkLI10AiX//P379x8jIyMTNmPf/v79+ysLCwsvuiQoNi5//fr1Kch4dAyS3P/gwYMTQBP+wxwHw0xA4gkQ73v9+vUZdJ2w1Lf82bNn4iCHCQoKasHsZw4ODgbRIL8c+/Lly5M3b978Y2dn5wC6npkFLXnsAOKLjx49AmUHLYAAAwBoQubG016R5wAAAABJRU5ErkJggg==) no-repeat top left; +} +.sf-button .border-r { + padding: 0 7px 0 0; + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAR1JREFUeNpiPHnyZCMDA8MNID5gZmb2nAEJMH7//v3N169fX969e/cYkL8WqGAHXPLv37//QYzfv39/fvPmzbUnT56sAXInmJub/2H5/x8sx8DCwsIrISFhDmQyPX78+CmQXs70798/BmQsKipqBNTgdvz4cWkmkE5kDATMioqKZkCFdiwg1eiAi4tLGqhQF24nMmBmZuYEigth1QkEbEBxTlySYPvJkwSJ00AnjYylgU6gxB8g/oFVEphkvgLF32KNMmCCewYUv4qhEyj47+HDhyeBzIMYOoEp8CxQw56wsLAncJ1//vz5/P79+2svX74EJc2V4BT58+fPd8CE/QKYHMGJOiIiAp6oWW7evDkNSF8DZYfIyEiU7AAQYACJ2vxVdJW4eQAAAABJRU5ErkJggg==) right top no-repeat; +} +.sf-button .btn-bg { + padding: 0px 14px; + color: #636363; + line-height: 28px; + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAYAAACgXdXMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpiPnny5EKGf//+/Wf6//8/A4QAcrGzKCZwGc9sa2urBBBgAIbDUoYVp9lmAAAAAElFTkSuQmCC) repeat-x top left; +} +.sf-button:hover .border-l, +.sf-button-selected .border-l { + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAR9JREFUeNpi/P//PwMyOHfunDqQSgNiexZkibNnzxYBqZa3HOs5v7PcYQBLnjlzhg1IbfzIdsTjA/t+ht9Mr8GKwZL//v3r+sB+0OMN+zqIEf8gFMvJkyd1gXTOa9YNDP//otrPAtSV/Jp9HfPff78Z0AEL0LUeXxivMfxD0wXTqfjj/2ugkf+wSrL9/YtpJEyS4S8WI5Ek/+GR/POPFjr//cenE6/kP9q4Fo/kr39/mdj+M/zFkGQCSj5i+ccPjLJ/GBgkuYOHQR1sNDpmAkb2LBmWwL///zKCIxwZM0VHR18G6p4uxeLLAA4tJMwEshiou1iMxXaHLGswA+t/YbhORuQUv2DBAnCifvxzI+enP3dQJUFg/vz5sOzgBBBgAPxX9j0YnH4JAAAAAElFTkSuQmCC) no-repeat top left; +} +.sf-button:hover .border-r, +.sf-button-selected .border-r { + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT5JREFUeNpiPHv27BkGBoaDQDzLyMjoJgMSYHrM3WX8hn1d0f///88DFRYhSzIuv2X5H8Rg/SfKIPDTkYH/l80OINffxMTkF9O/f/8ZQPgnwyuGl+wrGd6x7vf49+9fO9jYf3+Bkkj4NesmBqAV+SdPntQC6vzHgIz//gOawbqOGchOxtAJwp8Zr4F0e7D8/fuPAR38/P8eZIo0yz8skv8YvoIk+YE6/zNgAyD7sRqLkPzzjxY6/+HS+R+fTkZ8djLh08lCUCcuSWawJGbwMTGwg7zyBatX2Bj5QZKPsBrLzaICktzN8g/NWEYGZgYZjoC/wMiei5FMpFh8QPSU6Ojoy3Cd7EwiDBJsDgxiLNY7gLrKQGIsHAxSDHxAO2TZ/b8D+TVxcXF9MCtYtLiKLgDpfUDVsxITE1GyA0CAAQA2E/N8VuHyAAAAAABJRU5ErkJggg==) right top no-repeat; +} +.sf-button:hover .btn-bg, +.sf-button-selected .btn-bg { + color: #FFFFFF; + text-shadow:0 1px 1px #6b9311; + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAIAAAAvP0KbAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEFJREFUeNpiPnv2LNMdvlymf///M/37B8R/QfQ/MP33L4j+B6Qh7L9//sHpf2h8MA1V+w/KRjYLaDaLCU8vQIABAFO3TxZriO4yAAAAAElFTkSuQmCC) repeat-x top left; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/exception.css b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/exception.css new file mode 100644 index 0000000..7426d44 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/exception.css @@ -0,0 +1,113 @@ +.sf-reset .traces { + padding-bottom: 14px; +} +.sf-reset .traces li { + font-size: 12px; + color: #868686; + padding: 5px 4px; + list-style-type: decimal; + margin-left: 20px; + white-space: break-word; +} +.sf-reset #logs .traces li.error { + font-style: normal; + color: #AA3333; + background: #f9ecec; +} +.sf-reset #logs .traces li.warning { + font-style: normal; + background: #ffcc00; +} +/* fix for Opera not liking empty
  • */ +.sf-reset .traces li:after { + content: "\00A0"; +} +.sf-reset .trace { + border: 1px solid #D3D3D3; + padding: 10px; + overflow: auto; + margin: 10px 0 20px; +} +.sf-reset .block-exception { + -moz-border-radius: 16px; + -webkit-border-radius: 16px; + border-radius: 16px; + margin-bottom: 20px; + background-color: #f6f6f6; + border: 1px solid #dfdfdf; + padding: 30px 28px; + word-wrap: break-word; + overflow: hidden; +} +.sf-reset .block-exception div { + color: #313131; + font-size: 10px; +} +.sf-reset .block-exception-detected .illustration-exception, +.sf-reset .block-exception-detected .text-exception { + float: left; +} +.sf-reset .block-exception-detected .illustration-exception { + width: 152px; +} +.sf-reset .block-exception-detected .text-exception { + width: 670px; + padding: 30px 44px 24px 46px; + position: relative; +} +.sf-reset .text-exception .open-quote, +.sf-reset .text-exception .close-quote { + position: absolute; +} +.sf-reset .open-quote { + top: 0; + left: 0; +} +.sf-reset .close-quote { + bottom: 0; + right: 50px; +} +.sf-reset .block-exception p { + font-family: Arial, Helvetica, sans-serif; +} +.sf-reset .block-exception p a, +.sf-reset .block-exception p a:hover { + color: #565656; +} +.sf-reset .logs h2 { + float: left; + width: 654px; +} +.sf-reset .error-count { + float: right; + width: 170px; + text-align: right; +} +.sf-reset .error-count span { + display: inline-block; + background-color: #aacd4e; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + padding: 4px; + color: white; + margin-right: 2px; + font-size: 11px; + font-weight: bold; +} +.sf-reset .toggle { + vertical-align: middle; +} +.sf-reset .linked ul, +.sf-reset .linked li { + display: inline; +} +.sf-reset #output-content { + color: #000; + font-size: 12px; +} +.sf-reset #traces-text pre { + white-space: pre; + font-size: 12px; + font-family: monospace; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/structure.css b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/structure.css new file mode 100644 index 0000000..87499d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/css/structure.css @@ -0,0 +1,68 @@ +html { + background: #eee; +} +body { + font: 11px Verdana, Arial, sans-serif; + color: #333; +} +.sf-reset, .sf-reset .block, .sf-reset #message { + margin: auto; +} +img { + border: 0; +} +.clear { + clear: both; + height: 0; + font-size: 0; + line-height: 0; +} +.clear-fix:after { + content: "\0020"; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +.clear-fix { + display: inline-block; +} +* html .clear-fix { + height: 1%; +} +.clear-fix { + display: block; +} +.header { + padding: 30px 30px 20px 30px; +} +.header-logo { + float: left; +} +.search { + float: right; + padding-top: 20px; +} +.search label { + line-height: 28px; + vertical-align: middle; +} +.search input { + width: 195px; + font-size: 12px; + border: 1px solid #dadada; + background: #fff url(data:image/gif;base64,R0lGODlhAQAFAKIAAPX19e/v7/39/fr6+urq6gAAAAAAAAAAACH5BAAAAAAALAAAAAABAAUAAAMESAEjCQA7) repeat-x left top; + padding: 5px 6px; + color: #565656; +} +.search input[type="search"] { + -webkit-appearance: textfield; +} +#content { + width: 970px; + margin: 0 auto; +} +#content pre { + white-space: normal; + font-family: Arial, Helvetica, sans-serif; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/blue_picto_less.gif b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/blue_picto_less.gif new file mode 100644 index 0000000..dc70f6a Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/blue_picto_less.gif differ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/blue_picto_more.gif b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/blue_picto_more.gif new file mode 100644 index 0000000..098e388 Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/blue_picto_more.gif differ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/grey_magnifier.png b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/grey_magnifier.png new file mode 100644 index 0000000..cf44e63 Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/grey_magnifier.png differ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/logo_symfony.png b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/logo_symfony.png new file mode 100644 index 0000000..1ce063d Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/public/images/logo_symfony.png differ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php new file mode 100644 index 0000000..eb421be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php @@ -0,0 +1 @@ +block($form, 'widget_attributes') ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php new file mode 100644 index 0000000..2be960d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_attributes.html.php @@ -0,0 +1,10 @@ +id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" + $v): ?> + +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> + +escape($k), $view->escape($k)) ?> + +escape($k), $view->escape($v)) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_label.html.php new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_row.html.php new file mode 100644 index 0000000..b52e929 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_row.html.php @@ -0,0 +1,3 @@ +
    + widget($form) ?> +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php new file mode 100644 index 0000000..4b63876 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/button_widget.html.php @@ -0,0 +1,4 @@ + $name, '%id%' => $id)) + : $view['form']->humanize($name); } ?> + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/checkbox_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/checkbox_widget.html.php new file mode 100644 index 0000000..143557d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/checkbox_widget.html.php @@ -0,0 +1,5 @@ +block($form, 'widget_attributes') ?> + 0): ?> value="escape($value) ?>" + checked="checked" +/> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_options.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_options.html.php new file mode 100644 index 0000000..211ae73 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_options.html.php @@ -0,0 +1 @@ +block($form, 'choice_widget_options') ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget.html.php new file mode 100644 index 0000000..13593a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget.html.php @@ -0,0 +1,5 @@ + +block($form, 'choice_widget_expanded') ?> + +block($form, 'choice_widget_collapsed') ?> + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php new file mode 100644 index 0000000..29ea388 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_collapsed.html.php @@ -0,0 +1,18 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_expanded.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_expanded.html.php new file mode 100644 index 0000000..2c42687 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_expanded.html.php @@ -0,0 +1,6 @@ +
    block($form, 'widget_container_attributes') ?>> + + widget($child) ?> + label($child, null, array('translation_domain' => $choice_translation_domain)) ?> + +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php new file mode 100644 index 0000000..a0363cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/choice_widget_options.html.php @@ -0,0 +1,13 @@ + + + $choice): ?> + + + block($form, 'choice_widget_options', array('choices' => $choice)) ?> + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/collection_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/collection_widget.html.php new file mode 100644 index 0000000..9f6a94b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/collection_widget.html.php @@ -0,0 +1,4 @@ + + escape($view['form']->row($prototype)) ?> + +widget($form, array('attr' => $attr)) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/container_attributes.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/container_attributes.html.php new file mode 100644 index 0000000..302bbfc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/container_attributes.html.php @@ -0,0 +1 @@ +block($form, 'widget_container_attributes') ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php new file mode 100644 index 0000000..22e51d2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php @@ -0,0 +1,11 @@ + + block($form, 'form_widget_simple'); ?> + +
    block($form, 'widget_container_attributes') ?>> + widget($form['year']), + $view['form']->widget($form['month']), + $view['form']->widget($form['day']), + ), $date_pattern) ?> +
    + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/datetime_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/datetime_widget.html.php new file mode 100644 index 0000000..aef2a32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/datetime_widget.html.php @@ -0,0 +1,7 @@ + + block($form, 'form_widget_simple'); ?> + +
    block($form, 'widget_container_attributes') ?>> + widget($form['date']).' '.$view['form']->widget($form['time']) ?> +
    + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php new file mode 100644 index 0000000..0b30c5b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'email')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form.html.php new file mode 100644 index 0000000..fb789fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form.html.php @@ -0,0 +1,3 @@ +start($form) ?> + widget($form) ?> +end($form) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php new file mode 100644 index 0000000..36eba3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php @@ -0,0 +1 @@ +vars['multipart']): ?>enctype="multipart/form-data" diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_end.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_end.html.php new file mode 100644 index 0000000..fe68439 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_end.html.php @@ -0,0 +1,4 @@ + +rest($form) ?> + +
  • diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php new file mode 100644 index 0000000..77c60d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php @@ -0,0 +1,7 @@ + 0): ?> +
      + +
    • getMessage() ?>
    • + +
    + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php new file mode 100644 index 0000000..7a51b2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php @@ -0,0 +1,8 @@ + + + + $name, '%id%' => $id)) + : $view['form']->humanize($name); } ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php new file mode 100644 index 0000000..89041c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php @@ -0,0 +1,5 @@ + + isRendered()): ?> + row($child) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php new file mode 100644 index 0000000..a4f86d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php @@ -0,0 +1,5 @@ +
    + label($form) ?> + errors($form) ?> + widget($form) ?> +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php new file mode 100644 index 0000000..8c3ba86 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php @@ -0,0 +1,3 @@ + + row($child) ?> + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php new file mode 100644 index 0000000..ba2f3a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_start.html.php @@ -0,0 +1,6 @@ + + +
    action="" $v) { printf(' %s="%s"', $view->escape($k), $view->escape($v)); } ?> enctype="multipart/form-data"> + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php new file mode 100644 index 0000000..c5af39a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php @@ -0,0 +1,5 @@ + +block($form, 'form_widget_compound')?> + +block($form, 'form_widget_simple')?> + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php new file mode 100644 index 0000000..7a4f7cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_compound.html.php @@ -0,0 +1,7 @@ +
    block($form, 'widget_container_attributes') ?>> + parent && $errors): ?> + errors($form) ?> + + block($form, 'form_rows') ?> + rest($form) ?> +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_simple.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_simple.html.php new file mode 100644 index 0000000..5d7654f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget_simple.html.php @@ -0,0 +1 @@ +block($form, 'widget_attributes') ?> value="escape($value) ?>" /> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_row.html.php new file mode 100644 index 0000000..3239d8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_row.html.php @@ -0,0 +1 @@ +widget($form) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php new file mode 100644 index 0000000..a43f7de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'hidden')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php new file mode 100644 index 0000000..5fceb49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'number')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php new file mode 100644 index 0000000..644d284 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple'), $money_pattern) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php new file mode 100644 index 0000000..324eb47 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'text')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php new file mode 100644 index 0000000..4390687 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'password')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php new file mode 100644 index 0000000..59b29f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'text')) ?> % diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/radio_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/radio_widget.html.php new file mode 100644 index 0000000..ddc8c52 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/radio_widget.html.php @@ -0,0 +1,5 @@ +block($form, 'widget_attributes') ?> + value="escape($value) ?>" + checked="checked" +/> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/range_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/range_widget.html.php new file mode 100644 index 0000000..4c628f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/range_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'range')); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php new file mode 100644 index 0000000..d4af23d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php @@ -0,0 +1 @@ +block($form, 'form_rows') ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/reset_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/reset_widget.html.php new file mode 100644 index 0000000..1575e82 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/reset_widget.html.php @@ -0,0 +1 @@ +block($form, 'button_widget', array('type' => isset($type) ? $type : 'reset')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php new file mode 100644 index 0000000..4e442f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'search')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/submit_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/submit_widget.html.php new file mode 100644 index 0000000..d42bb2a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/submit_widget.html.php @@ -0,0 +1 @@ +block($form, 'button_widget', array('type' => isset($type) ? $type : 'submit')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/textarea_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/textarea_widget.html.php new file mode 100644 index 0000000..c989ce5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/textarea_widget.html.php @@ -0,0 +1 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php new file mode 100644 index 0000000..e24411c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php @@ -0,0 +1,22 @@ + + block($form, 'form_widget_simple'); ?> + + array('size' => 1)) : array() ?> +
    block($form, 'widget_container_attributes') ?>> + widget($form['hour'], $vars); + + if ($with_minutes) { + echo ':'; + echo $view['form']->widget($form['minute'], $vars); + } + + if ($with_seconds) { + echo ':'; + echo $view['form']->widget($form['second'], $vars); + } + ?> +
    + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_widget.html.php new file mode 100644 index 0000000..0ce4ed2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/url_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'url')) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php new file mode 100644 index 0000000..3fefa47 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_attributes.html.php @@ -0,0 +1,11 @@ +id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" + required="required" + $v): ?> + +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> + +escape($k), $view->escape($k)) ?> + +escape($k), $view->escape($v)) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php new file mode 100644 index 0000000..dc2e5eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/widget_container_attributes.html.php @@ -0,0 +1,10 @@ +id="escape($id) ?>" + $v): ?> + +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> + +escape($k), $view->escape($k)) ?> + +escape($k), $view->escape($v)) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/button_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/button_row.html.php new file mode 100644 index 0000000..ef4d22b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/button_row.html.php @@ -0,0 +1,6 @@ + + + + widget($form) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php new file mode 100644 index 0000000..7e1f2f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php @@ -0,0 +1,9 @@ + + + label($form) ?> + + + errors($form) ?> + widget($form) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php new file mode 100644 index 0000000..20b9668 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget_compound.html.php @@ -0,0 +1,11 @@ +block($form, 'widget_container_attributes') ?>> + parent && $errors): ?> + + + + + block($form, 'form_rows') ?> + rest($form) ?> +
    + errors($form) ?> +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/hidden_row.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/hidden_row.html.php new file mode 100644 index 0000000..491ece3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/hidden_row.html.php @@ -0,0 +1,5 @@ + + + widget($form) ?> + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php new file mode 100644 index 0000000..ce06c61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Routing; + +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\Config\Exception\FileLoaderLoadException; +use Symfony\Component\Config\Loader\DelegatingLoader as BaseDelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolverInterface; + +/** + * DelegatingLoader delegates route loading to other loaders using a loader resolver. + * + * This implementation resolves the _controller attribute from the short notation + * to the fully-qualified form (from a:b:c to class:method). + * + * @author Fabien Potencier + */ +class DelegatingLoader extends BaseDelegatingLoader +{ + protected $parser; + private $loading = false; + + /** + * Constructor. + * + * @param ControllerNameParser $parser A ControllerNameParser instance + * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + */ + public function __construct(ControllerNameParser $parser, LoaderResolverInterface $resolver) + { + $this->parser = $parser; + + parent::__construct($resolver); + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + if ($this->loading) { + // This can happen if a fatal error occurs in parent::load(). + // Here is the scenario: + // - while routes are being loaded by parent::load() below, a fatal error + // occurs (e.g. parse error in a controller while loading annotations); + // - PHP abruptly empties the stack trace, bypassing all catch/finally blocks; + // it then calls the registered shutdown functions; + // - the ErrorHandler catches the fatal error and re-injects it for rendering + // thanks to HttpKernel->terminateWithException() (that calls handleException()); + // - at this stage, if we try to load the routes again, we must prevent + // the fatal error from occurring a second time, + // otherwise the PHP process would be killed immediately; + // - while rendering the exception page, the router can be required + // (by e.g. the web profiler that needs to generate an URL); + // - this handles the case and prevents the second fatal error + // by triggering an exception beforehand. + + throw new FileLoaderLoadException($resource); + } + $this->loading = true; + + try { + $collection = parent::load($resource, $type); + } finally { + $this->loading = false; + } + + foreach ($collection->all() as $route) { + if ($controller = $route->getDefault('_controller')) { + try { + $controller = $this->parser->parse($controller); + } catch (\Exception $e) { + // unable to optimize unknown notation + } + + $route->setDefault('_controller', $controller); + } + } + + return $collection; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableUrlMatcher.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableUrlMatcher.php new file mode 100644 index 0000000..76f8536 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableUrlMatcher.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Routing; + +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcher as BaseMatcher; + +/** + * @author Fabien Potencier + */ +class RedirectableUrlMatcher extends BaseMatcher +{ + /** + * Redirects the user to another URL. + * + * @param string $path The path info to redirect to. + * @param string $route The route that matched + * @param string $scheme The URL scheme (null to keep the current one) + * + * @return array An array of parameters + */ + public function redirect($path, $route, $scheme = null) + { + return array( + '_controller' => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction', + 'path' => $path, + 'permanent' => true, + 'scheme' => $scheme, + 'httpPort' => $this->context->getHttpPort(), + 'httpsPort' => $this->context->getHttpsPort(), + '_route' => $route, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php new file mode 100644 index 0000000..76bf546 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Routing; + +use Symfony\Component\Routing\Router as BaseRouter; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * This Router creates the Loader only when the cache is empty. + * + * @author Fabien Potencier + */ +class Router extends BaseRouter implements WarmableInterface +{ + private $container; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param mixed $resource The main resource to load + * @param array $options An array of options + * @param RequestContext $context The context + */ + public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null) + { + $this->container = $container; + + $this->resource = $resource; + $this->context = $context ?: new RequestContext(); + $this->setOptions($options); + } + + /** + * {@inheritdoc} + */ + public function getRouteCollection() + { + if (null === $this->collection) { + $this->collection = $this->container->get('routing.loader')->load($this->resource, $this->options['resource_type']); + $this->resolveParameters($this->collection); + } + + return $this->collection; + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + $currentDir = $this->getOption('cache_dir'); + + // force cache generation + $this->setOption('cache_dir', $cacheDir); + $this->getMatcher(); + $this->getGenerator(); + + $this->setOption('cache_dir', $currentDir); + } + + /** + * Replaces placeholders with service container parameter values in: + * - the route defaults, + * - the route requirements, + * - the route path, + * - the route host, + * - the route schemes, + * - the route methods. + * + * @param RouteCollection $collection + */ + private function resolveParameters(RouteCollection $collection) + { + foreach ($collection as $route) { + foreach ($route->getDefaults() as $name => $value) { + $route->setDefault($name, $this->resolve($value)); + } + + foreach ($route->getRequirements() as $name => $value) { + $route->setRequirement($name, $this->resolve($value)); + } + + $route->setPath($this->resolve($route->getPath())); + $route->setHost($this->resolve($route->getHost())); + + $schemes = array(); + foreach ($route->getSchemes() as $scheme) { + $schemes = array_merge($schemes, explode('|', $this->resolve($scheme))); + } + $route->setSchemes($schemes); + + $methods = array(); + foreach ($route->getMethods() as $method) { + $methods = array_merge($methods, explode('|', $this->resolve($method))); + } + $route->setMethods($methods); + $route->setCondition($this->resolve($route->getCondition())); + } + } + + /** + * Recursively replaces placeholders with the service container parameters. + * + * @param mixed $value The source which might contain "%placeholders%" + * + * @return mixed The source with the placeholders replaced by the container + * parameters. Arrays are resolved recursively. + * + * @throws ParameterNotFoundException When a placeholder does not exist as a container parameter + * @throws RuntimeException When a container value is not a string or a numeric value + */ + private function resolve($value) + { + if (is_array($value)) { + foreach ($value as $key => $val) { + $value[$key] = $this->resolve($val); + } + + return $value; + } + + if (!is_string($value)) { + return $value; + } + + $container = $this->container; + + $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($container, $value) { + // skip %% + if (!isset($match[1])) { + return '%%'; + } + + $resolved = $container->getParameter($match[1]); + + if (is_string($resolved) || is_numeric($resolved)) { + return (string) $resolved; + } + + throw new RuntimeException(sprintf( + 'The container parameter "%s", used in the route configuration value "%s", '. + 'must be a string or numeric, but it is of type %s.', + $match[1], + $value, + gettype($resolved) + ) + ); + + }, $value); + + return str_replace('%%', '%', $escapedValue); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php new file mode 100644 index 0000000..c204ffa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/DelegatingEngine.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\DelegatingEngine as BaseDelegatingEngine; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; + +/** + * DelegatingEngine selects an engine for a given template. + * + * @author Fabien Potencier + */ +class DelegatingEngine extends BaseDelegatingEngine implements EngineInterface +{ + protected $container; + + /** + * Constructor. + * + * @param ContainerInterface $container The DI container + * @param array $engineIds An array of engine Ids + */ + public function __construct(ContainerInterface $container, array $engineIds) + { + $this->container = $container; + $this->engines = $engineIds; + } + + /** + * {@inheritdoc} + */ + public function getEngine($name) + { + $this->resolveEngines(); + + return parent::getEngine($name); + } + + /** + * {@inheritdoc} + */ + public function renderResponse($view, array $parameters = array(), Response $response = null) + { + $engine = $this->getEngine($view); + + if ($engine instanceof EngineInterface) { + return $engine->renderResponse($view, $parameters, $response); + } + + if (null === $response) { + $response = new Response(); + } + + $response->setContent($engine->render($view, $parameters)); + + return $response; + } + + /** + * Resolved engine ids to their real engine instances from the container. + */ + private function resolveEngines() + { + foreach ($this->engines as $i => $engine) { + if (is_string($engine)) { + $this->engines[$i] = $this->container->get($engine); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/EngineInterface.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/EngineInterface.php new file mode 100644 index 0000000..dcf5897 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/EngineInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\EngineInterface as BaseEngineInterface; +use Symfony\Component\HttpFoundation\Response; + +/** + * EngineInterface is the interface each engine must implement. + * + * @author Fabien Potencier + */ +interface EngineInterface extends BaseEngineInterface +{ + /** + * Renders a view and returns a Response. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param Response $response A Response instance + * + * @return Response A Response instance + * + * @throws \RuntimeException if the template cannot be rendered + */ + public function renderResponse($view, array $parameters = array(), Response $response = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php new file mode 100644 index 0000000..606e886 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; + +/** + * GlobalVariables is the entry point for Symfony global variables in PHP templates. + * + * @author Fabien Potencier + */ +class GlobalVariables +{ + protected $container; + + /** + * @param ContainerInterface $container The DI container + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Returns the current user. + * + * @return mixed + * + * @see TokenInterface::getUser() + */ + public function getUser() + { + if (!$this->container->has('security.token_storage')) { + return; + } + + $tokenStorage = $this->container->get('security.token_storage'); + + if (!$token = $tokenStorage->getToken()) { + return; + } + + $user = $token->getUser(); + if (!is_object($user)) { + return; + } + + return $user; + } + + /** + * Returns the current request. + * + * @return Request|null The HTTP request object + */ + public function getRequest() + { + if ($this->container->has('request_stack')) { + return $this->container->get('request_stack')->getCurrentRequest(); + } + } + + /** + * Returns the current session. + * + * @return Session|null The session + */ + public function getSession() + { + if ($request = $this->getRequest()) { + return $request->getSession(); + } + } + + /** + * Returns the current app environment. + * + * @return string The current environment string (e.g 'dev') + */ + public function getEnvironment() + { + return $this->container->getParameter('kernel.environment'); + } + + /** + * Returns the current app debug mode. + * + * @return bool The current debug mode + */ + public function getDebug() + { + return (bool) $this->container->getParameter('kernel.debug'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/ActionsHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/ActionsHelper.php new file mode 100644 index 0000000..8f0d54e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/ActionsHelper.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +/** + * ActionsHelper manages action inclusions. + * + * @author Fabien Potencier + */ +class ActionsHelper extends Helper +{ + private $handler; + + /** + * Constructor. + * + * @param FragmentHandler $handler A FragmentHandler instance + */ + public function __construct(FragmentHandler $handler) + { + $this->handler = $handler; + } + + /** + * Returns the fragment content for a given URI. + * + * @param string $uri A URI + * @param array $options An array of options + * + * @return string The fragment content + * + * @see FragmentHandler::render() + */ + public function render($uri, array $options = array()) + { + $strategy = isset($options['strategy']) ? $options['strategy'] : 'inline'; + unset($options['strategy']); + + return $this->handler->render($uri, $strategy, $options); + } + + public function controller($controller, $attributes = array(), $query = array()) + { + return new ControllerReference($controller, $attributes, $query); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'actions'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php new file mode 100644 index 0000000..080103d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/CodeHelper.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; + +/** + * CodeHelper. + * + * @author Fabien Potencier + */ +class CodeHelper extends Helper +{ + protected $fileLinkFormat; + protected $rootDir; + protected $charset; + + /** + * Constructor. + * + * @param string $fileLinkFormat The format for links to source files + * @param string $rootDir The project root directory + * @param string $charset The charset + */ + public function __construct($fileLinkFormat, $rootDir, $charset) + { + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->rootDir = str_replace('\\', '/', $rootDir).'/'; + $this->charset = $charset; + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + public function formatArgsAsText(array $args) + { + return strip_tags($this->formatArgs($args)); + } + + public function abbrClass($class) + { + $parts = explode('\\', $class); + $short = array_pop($parts); + + return sprintf('%s', $class, $short); + } + + public function abbrMethod($method) + { + if (false !== strpos($method, '::')) { + list($class, $method) = explode('::', $method, 2); + $result = sprintf('%s::%s()', $this->abbrClass($class), $method); + } elseif ('Closure' === $method) { + $result = sprintf('%s', $method, $method); + } else { + $result = sprintf('%s()', $method, $method); + } + + return $result; + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + public function formatArgs(array $args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $parts = explode('\\', $item[1]); + $short = array_pop($parts); + $formattedValue = sprintf('object(%s)', $item[1], $short); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('string' === $item[0]) { + $formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->getCharset())); + } elseif ('null' === $item[0]) { + $formattedValue = 'null'; + } elseif ('boolean' === $item[0]) { + $formattedValue = ''.strtolower(var_export($item[1], true)).''; + } elseif ('resource' === $item[0]) { + $formattedValue = 'resource'; + } else { + $formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->getCharset()), true)); + } + + $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); + } + + return implode(', ', $result); + } + + /** + * Returns an excerpt of a code file around the given line number. + * + * @param string $file A file path + * @param int $line The selected line number + * + * @return string An HTML string + */ + public function fileExcerpt($file, $line) + { + if (is_readable($file)) { + if (extension_loaded('fileinfo')) { + $finfo = new \Finfo(); + + // Check if the file is an application/octet-stream (eg. Phar file) because highlight_file cannot parse these files + if ('application/octet-stream' === $finfo->file($file, FILEINFO_MIME_TYPE)) { + return; + } + } + + // highlight_file could throw warnings + // see https://bugs.php.net/bug.php?id=25725 + $code = @highlight_file($file, true); + // remove main code/span tags + $code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code); + $content = preg_split('#
    #', $code); + + $lines = array(); + for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; ++$i) { + $lines[] = ''.self::fixCodeMarkup($content[$i - 1]).''; + } + + return '
      '.implode("\n", $lines).'
    '; + } + } + + /** + * Formats a file path. + * + * @param string $file An absolute file path + * @param int $line The line number + * @param string $text Use this text for the link rather than the file path + * + * @return string + */ + public function formatFile($file, $line, $text = null) + { + $flags = ENT_QUOTES | ENT_SUBSTITUTE; + + if (null === $text) { + $file = trim($file); + $fileStr = $file; + if (0 === strpos($fileStr, $this->rootDir)) { + $fileStr = str_replace($this->rootDir, '', str_replace('\\', '/', $fileStr)); + $fileStr = htmlspecialchars($fileStr, $flags, $this->charset); + $fileStr = sprintf('kernel.root_dir/%s', htmlspecialchars($this->rootDir, $flags, $this->charset), $fileStr); + } + + $text = sprintf('%s at line %d', $fileStr, $line); + } + + if (false !== $link = $this->getFileLink($file, $line)) { + return sprintf('%s', htmlspecialchars($link, $flags, $this->charset), $text); + } + + return $text; + } + + /** + * Returns the link for a given file/line pair. + * + * @param string $file An absolute file path + * @param int $line The line number + * + * @return string A link of false + */ + public function getFileLink($file, $line) + { + if ($this->fileLinkFormat && is_file($file)) { + return strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line)); + } + + return false; + } + + public function formatFileFromText($text) + { + return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) { + return 'in '.$this->formatFile($match[2], $match[3]); + }, $text); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'code'; + } + + protected static function fixCodeMarkup($line) + { + // ending tag from previous line + $opening = strpos($line, ''); + if (false !== $closing && (false === $opening || $closing < $opening)) { + $line = substr_replace($line, '', $closing, 7); + } + + // missing tag at the end of line + $opening = strpos($line, ''); + if (false !== $opening && (false === $closing || $closing > $opening)) { + $line .= ''; + } + + return $line; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php new file mode 100644 index 0000000..067bca3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Form\FormRendererInterface; +use Symfony\Component\Form\FormView; + +/** + * FormHelper provides helpers to help display forms. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class FormHelper extends Helper +{ + /** + * @var FormRendererInterface + */ + private $renderer; + + /** + * @param FormRendererInterface $renderer + */ + public function __construct(FormRendererInterface $renderer) + { + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'form'; + } + + /** + * Sets a theme for a given view. + * + * The theme format is ":". + * + * @param FormView $view A FormView instance + * @param string|array $themes A theme or an array of theme + */ + public function setTheme(FormView $view, $themes) + { + $this->renderer->setTheme($view, $themes); + } + + /** + * Renders the HTML for a form. + * + * Example usage: + * + * form($form) ?> + * + * You can pass options during the call: + * + * form($form, array('attr' => array('class' => 'foo'))) ?> + * + * form($form, array('separator' => '+++++')) ?> + * + * This method is mainly intended for prototyping purposes. If you want to + * control the layout of a form in a more fine-grained manner, you are + * advised to use the other helper methods for rendering the parts of the + * form individually. You can also create a custom form theme to adapt + * the look of the form. + * + * @param FormView $view The view for which to render the form + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function form(FormView $view, array $variables = array()) + { + return $this->renderer->renderBlock($view, 'form', $variables); + } + + /** + * Renders the form start tag. + * + * Example usage templates: + * + * start($form) ?>> + * + * @param FormView $view The view for which to render the start tag + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function start(FormView $view, array $variables = array()) + { + return $this->renderer->renderBlock($view, 'form_start', $variables); + } + + /** + * Renders the form end tag. + * + * Example usage templates: + * + * end($form) ?>> + * + * @param FormView $view The view for which to render the end tag + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function end(FormView $view, array $variables = array()) + { + return $this->renderer->renderBlock($view, 'form_end', $variables); + } + + /** + * Renders the HTML for a given view. + * + * Example usage: + * + * widget($form) ?> + * + * You can pass options during the call: + * + * widget($form, array('attr' => array('class' => 'foo'))) ?> + * + * widget($form, array('separator' => '+++++')) ?> + * + * @param FormView $view The view for which to render the widget + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function widget(FormView $view, array $variables = array()) + { + return $this->renderer->searchAndRenderBlock($view, 'widget', $variables); + } + + /** + * Renders the entire form field "row". + * + * @param FormView $view The view for which to render the row + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function row(FormView $view, array $variables = array()) + { + return $this->renderer->searchAndRenderBlock($view, 'row', $variables); + } + + /** + * Renders the label of the given view. + * + * @param FormView $view The view for which to render the label + * @param string $label The label + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function label(FormView $view, $label = null, array $variables = array()) + { + if (null !== $label) { + $variables += array('label' => $label); + } + + return $this->renderer->searchAndRenderBlock($view, 'label', $variables); + } + + /** + * Renders the errors of the given view. + * + * @param FormView $view The view to render the errors for + * + * @return string The HTML markup + */ + public function errors(FormView $view) + { + return $this->renderer->searchAndRenderBlock($view, 'errors'); + } + + /** + * Renders views which have not already been rendered. + * + * @param FormView $view The parent view + * @param array $variables An array of variables + * + * @return string The HTML markup + */ + public function rest(FormView $view, array $variables = array()) + { + return $this->renderer->searchAndRenderBlock($view, 'rest', $variables); + } + + /** + * Renders a block of the template. + * + * @param FormView $view The view for determining the used themes. + * @param string $blockName The name of the block to render. + * @param array $variables The variable to pass to the template. + * + * @return string The HTML markup + */ + public function block(FormView $view, $blockName, array $variables = array()) + { + return $this->renderer->renderBlock($view, $blockName, $variables); + } + + /** + * Returns a CSRF token. + * + * Use this helper for CSRF protection without the overhead of creating a + * form. + * + * + * echo $view['form']->csrfToken('rm_user_'.$user->getId()); + * + * + * Check the token in your action using the same CSRF token id. + * + * + * $csrfProvider = $this->get('security.csrf.token_generator'); + * if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) { + * throw new \RuntimeException('CSRF attack detected.'); + * } + * + * + * @param string $tokenId The CSRF token id of the protected action + * + * @return string A CSRF token + * + * @throws \BadMethodCallException When no CSRF provider was injected in the constructor. + */ + public function csrfToken($tokenId) + { + return $this->renderer->renderCsrfToken($tokenId); + } + + public function humanize($text) + { + return $this->renderer->humanize($text); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RequestHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RequestHelper.php new file mode 100644 index 0000000..694e375 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RequestHelper.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * RequestHelper provides access to the current request parameters. + * + * @author Fabien Potencier + */ +class RequestHelper extends Helper +{ + protected $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * Returns a parameter from the current request object. + * + * @param string $key The name of the parameter + * @param string $default A default value + * + * @return mixed + * + * @see Request::get() + */ + public function getParameter($key, $default = null) + { + return $this->getRequest()->get($key, $default); + } + + /** + * Returns the locale. + * + * @return string + */ + public function getLocale() + { + return $this->getRequest()->getLocale(); + } + + private function getRequest() + { + if (!$this->requestStack->getCurrentRequest()) { + throw new \LogicException('A Request must be available.'); + } + + return $this->requestStack->getCurrentRequest(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'request'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php new file mode 100644 index 0000000..4399104 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * RouterHelper manages links between pages in a template context. + * + * @author Fabien Potencier + */ +class RouterHelper extends Helper +{ + protected $generator; + + /** + * Constructor. + * + * @param UrlGeneratorInterface $router A Router instance + */ + public function __construct(UrlGeneratorInterface $router) + { + $this->generator = $router; + } + + /** + * Generates a URL reference (as an absolute or relative path) to the route with the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param bool $relative Whether to generate a relative or absolute path + * + * @return string The generated URL reference + * + * @see UrlGeneratorInterface + */ + public function path($name, $parameters = array(), $relative = false) + { + return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * Generates a URL reference (as an absolute URL or network path) to the route with the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param bool $schemeRelative Whether to omit the scheme in the generated URL reference + * + * @return string The generated URL reference + * + * @see UrlGeneratorInterface + */ + public function url($name, $parameters = array(), $schemeRelative = false) + { + return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'router'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php new file mode 100644 index 0000000..15ec1a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/SessionHelper.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * SessionHelper provides read-only access to the session attributes. + * + * @author Fabien Potencier + */ +class SessionHelper extends Helper +{ + protected $session; + protected $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value + * + * @return mixed + */ + public function get($name, $default = null) + { + return $this->getSession()->get($name, $default); + } + + public function getFlash($name, array $default = array()) + { + return $this->getSession()->getFlashBag()->get($name, $default); + } + + public function getFlashes() + { + return $this->getSession()->getFlashBag()->all(); + } + + public function hasFlash($name) + { + return $this->getSession()->getFlashBag()->has($name); + } + + private function getSession() + { + if (null === $this->session) { + if (!$this->requestStack->getMasterRequest()) { + throw new \LogicException('A Request must be available.'); + } + + $this->session = $this->requestStack->getMasterRequest()->getSession(); + } + + return $this->session; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'session'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/StopwatchHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/StopwatchHelper.php new file mode 100644 index 0000000..47a9cf8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/StopwatchHelper.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Templating\Helper\Helper; + +/** + * StopwatchHelper provides methods time your PHP templates. + * + * @author Wouter J + */ +class StopwatchHelper extends Helper +{ + private $stopwatch; + + public function __construct(Stopwatch $stopwatch = null) + { + $this->stopwatch = $stopwatch; + } + + public function getName() + { + return 'stopwatch'; + } + + public function __call($method, $arguments = array()) + { + if (null !== $this->stopwatch) { + if (method_exists($this->stopwatch, $method)) { + return call_user_func_array(array($this->stopwatch, $method), $arguments); + } + + throw new \BadMethodCallException(sprintf('Method "%s" of Stopwatch does not exist', $method)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php new file mode 100644 index 0000000..2c2641a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/TranslatorHelper.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * TranslatorHelper. + * + * @author Fabien Potencier + */ +class TranslatorHelper extends Helper +{ + protected $translator; + + /** + * Constructor. + * + * @param TranslatorInterface $translator A TranslatorInterface instance + */ + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + /** + * @see TranslatorInterface::trans() + */ + public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null) + { + return $this->translator->trans($id, $parameters, $domain, $locale); + } + + /** + * @see TranslatorInterface::transChoice() + */ + public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null) + { + return $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'translator'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/FilesystemLoader.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/FilesystemLoader.php new file mode 100644 index 0000000..4f6cffc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/FilesystemLoader.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Loader; + +use Symfony\Component\Templating\Storage\FileStorage; +use Symfony\Component\Templating\Loader\LoaderInterface; +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * FilesystemLoader is a loader that read templates from the filesystem. + * + * @author Fabien Potencier + */ +class FilesystemLoader implements LoaderInterface +{ + protected $locator; + + /** + * Constructor. + * + * @param FileLocatorInterface $locator A FileLocatorInterface instance + */ + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * {@inheritdoc} + */ + public function load(TemplateReferenceInterface $template) + { + try { + $file = $this->locator->locate($template); + } catch (\InvalidArgumentException $e) { + return false; + } + + return new FileStorage($file); + } + + /** + * {@inheritdoc} + */ + public function isFresh(TemplateReferenceInterface $template, $time) + { + if (false === $storage = $this->load($template)) { + return false; + } + + if (!is_readable((string) $storage)) { + return false; + } + + return filemtime((string) $storage) < $time; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php new file mode 100644 index 0000000..286b7c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * TemplateLocator locates templates in bundles. + * + * @author Fabien Potencier + */ +class TemplateLocator implements FileLocatorInterface +{ + protected $locator; + protected $cache; + + /** + * Constructor. + * + * @param FileLocatorInterface $locator A FileLocatorInterface instance + * @param string $cacheDir The cache path + */ + public function __construct(FileLocatorInterface $locator, $cacheDir = null) + { + if (null !== $cacheDir && is_file($cache = $cacheDir.'/templates.php')) { + $this->cache = require $cache; + } + + $this->locator = $locator; + } + + /** + * Returns a full path for a given file. + * + * @param TemplateReferenceInterface $template A template + * + * @return string The full path for the file + */ + protected function getCacheKey($template) + { + return $template->getLogicalName(); + } + + /** + * Returns a full path for a given file. + * + * @param TemplateReferenceInterface $template A template + * @param string $currentPath Unused + * @param bool $first Unused + * + * @return string The full path for the file + * + * @throws \InvalidArgumentException When the template is not an instance of TemplateReferenceInterface + * @throws \InvalidArgumentException When the template file can not be found + */ + public function locate($template, $currentPath = null, $first = true) + { + if (!$template instanceof TemplateReferenceInterface) { + throw new \InvalidArgumentException('The template must be an instance of TemplateReferenceInterface.'); + } + + $key = $this->getCacheKey($template); + + if (isset($this->cache[$key])) { + return $this->cache[$key]; + } + + try { + return $this->cache[$key] = $this->locator->locate($template->getPath(), $currentPath); + } catch (\InvalidArgumentException $e) { + throw new \InvalidArgumentException(sprintf('Unable to find template "%s" : "%s".', $template, $e->getMessage()), 0, $e); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php new file mode 100644 index 0000000..41382f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/PhpEngine.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\PhpEngine as BasePhpEngine; +use Symfony\Component\Templating\Loader\LoaderInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; + +/** + * This engine knows how to render Symfony templates. + * + * @author Fabien Potencier + */ +class PhpEngine extends BasePhpEngine implements EngineInterface +{ + protected $container; + + /** + * Constructor. + * + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param ContainerInterface $container The DI container + * @param LoaderInterface $loader A loader instance + * @param GlobalVariables|null $globals A GlobalVariables instance or null + */ + public function __construct(TemplateNameParserInterface $parser, ContainerInterface $container, LoaderInterface $loader, GlobalVariables $globals = null) + { + $this->container = $container; + + parent::__construct($parser, $loader); + + if (null !== $globals) { + $this->addGlobal('app', $globals); + } + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (!isset($this->helpers[$name])) { + throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + if (is_string($this->helpers[$name])) { + $this->helpers[$name] = $this->container->get($this->helpers[$name]); + $this->helpers[$name]->setCharset($this->charset); + } + + return $this->helpers[$name]; + } + + /** + * {@inheritdoc} + */ + public function setHelpers(array $helpers) + { + $this->helpers = $helpers; + } + + /** + * {@inheritdoc} + */ + public function renderResponse($view, array $parameters = array(), Response $response = null) + { + if (null === $response) { + $response = new Response(); + } + + $response->setContent($this->render($view, $parameters)); + + return $response; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateFilenameParser.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateFilenameParser.php new file mode 100644 index 0000000..df3ab19 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateFilenameParser.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * TemplateFilenameParser converts template filenames to + * TemplateReferenceInterface instances. + * + * @author Fabien Potencier + */ +class TemplateFilenameParser implements TemplateNameParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($name) + { + if ($name instanceof TemplateReferenceInterface) { + return $name; + } + + $parts = explode('/', str_replace('\\', '/', $name)); + + $elements = explode('.', array_pop($parts)); + if (3 > count($elements)) { + return false; + } + $engine = array_pop($elements); + $format = array_pop($elements); + + return new TemplateReference('', implode('/', $parts), implode('.', $elements), $format, $engine); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php new file mode 100644 index 0000000..e4a7053 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateNameParser.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\TemplateReferenceInterface; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Templating\TemplateNameParser as BaseTemplateNameParser; + +/** + * TemplateNameParser converts template names from the short notation + * "bundle:section:template.format.engine" to TemplateReferenceInterface + * instances. + * + * @author Fabien Potencier + */ +class TemplateNameParser extends BaseTemplateNameParser +{ + protected $kernel; + protected $cache = array(); + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function parse($name) + { + if ($name instanceof TemplateReferenceInterface) { + return $name; + } elseif (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + // normalize name + $name = str_replace(':/', ':', preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name))); + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name)); + } + + if (!preg_match('/^([^:]*):([^:]*):(.+)\.([^\.]+)\.([^\.]+)$/', $name, $matches)) { + return parent::parse($name); + } + + $template = new TemplateReference($matches[1], $matches[2], $matches[3], $matches[4], $matches[5]); + + if ($template->get('bundle')) { + try { + $this->kernel->getBundle($template->get('bundle')); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid.', $name), 0, $e); + } + } + + return $this->cache[$name] = $template; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php new file mode 100644 index 0000000..d9bc982 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TemplateReference.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\TemplateReference as BaseTemplateReference; + +/** + * Internal representation of a template. + * + * @author Victor Berchet + */ +class TemplateReference extends BaseTemplateReference +{ + public function __construct($bundle = null, $controller = null, $name = null, $format = null, $engine = null) + { + $this->parameters = array( + 'bundle' => $bundle, + 'controller' => $controller, + 'name' => $name, + 'format' => $format, + 'engine' => $engine, + ); + } + + /** + * Returns the path to the template + * - as a path when the template is not part of a bundle + * - as a resource when the template is part of a bundle. + * + * @return string A path to the template or a resource + */ + public function getPath() + { + $controller = str_replace('\\', '/', $this->get('controller')); + + $path = (empty($controller) ? '' : $controller.'/').$this->get('name').'.'.$this->get('format').'.'.$this->get('engine'); + + return empty($this->parameters['bundle']) ? 'views/'.$path : '@'.$this->get('bundle').'/Resources/views/'.$path; + } + + /** + * {@inheritdoc} + */ + public function getLogicalName() + { + return sprintf('%s:%s:%s.%s.%s', $this->parameters['bundle'], $this->parameters['controller'], $this->parameters['name'], $this->parameters['format'], $this->parameters['engine']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php new file mode 100644 index 0000000..3295cc7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Templating/TimedPhpEngine.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Templating\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Times the time spent to render a template. + * + * @author Fabien Potencier + */ +class TimedPhpEngine extends PhpEngine +{ + protected $stopwatch; + + /** + * Constructor. + * + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param ContainerInterface $container A ContainerInterface instance + * @param LoaderInterface $loader A LoaderInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + * @param GlobalVariables $globals A GlobalVariables instance + */ + public function __construct(TemplateNameParserInterface $parser, ContainerInterface $container, LoaderInterface $loader, Stopwatch $stopwatch, GlobalVariables $globals = null) + { + parent::__construct($parser, $container, $loader, $globals); + + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function render($name, array $parameters = array()) + { + $e = $this->stopwatch->start(sprintf('template.php (%s)', $name), 'template'); + + $ret = parent::render($name, $parameters); + + $e->stop(); + + return $ret; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php new file mode 100644 index 0000000..aa8b669 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Symfony\Component\DependencyInjection\ResettableContainerInterface; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * KernelTestCase is the base class for tests needing a Kernel. + * + * @author Fabien Potencier + */ +abstract class KernelTestCase extends \PHPUnit_Framework_TestCase +{ + protected static $class; + + /** + * @var KernelInterface + */ + protected static $kernel; + + /** + * Finds the directory where the phpunit.xml(.dist) is stored. + * + * If you run tests with the PHPUnit CLI tool, everything will work as expected. + * If not, override this method in your test classes. + * + * @return string The directory where phpunit.xml(.dist) is stored + * + * @throws \RuntimeException + */ + protected static function getPhpUnitXmlDir() + { + if (!isset($_SERVER['argv']) || false === strpos($_SERVER['argv'][0], 'phpunit')) { + throw new \RuntimeException('You must override the KernelTestCase::createKernel() method.'); + } + + $dir = static::getPhpUnitCliConfigArgument(); + if (null === $dir && + (is_file(getcwd().DIRECTORY_SEPARATOR.'phpunit.xml') || + is_file(getcwd().DIRECTORY_SEPARATOR.'phpunit.xml.dist'))) { + $dir = getcwd(); + } + + // Can't continue + if (null === $dir) { + throw new \RuntimeException('Unable to guess the Kernel directory.'); + } + + if (!is_dir($dir)) { + $dir = dirname($dir); + } + + return $dir; + } + + /** + * 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. + * + * @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 (preg_match('/^-[^ \-]*c$/', $testArg) || $testArg === '--configuration') { + $dir = realpath($reversedArgs[$argIndex - 1]); + break; + } elseif (strpos($testArg, '--configuration=') === 0) { + $argPath = substr($testArg, strlen('--configuration=')); + $dir = realpath($argPath); + break; + } + } + + return $dir; + } + + /** + * Attempts to guess the kernel location. + * + * When the Kernel is located, the file is required. + * + * @return string The Kernel class name + * + * @throws \RuntimeException + */ + protected static function getKernelClass() + { + if (isset($_SERVER['KERNEL_DIR'])) { + $dir = $_SERVER['KERNEL_DIR']; + + if (!is_dir($dir)) { + $phpUnitDir = static::getPhpUnitXmlDir(); + if (is_dir("$phpUnitDir/$dir")) { + $dir = "$phpUnitDir/$dir"; + } + } + } else { + $dir = static::getPhpUnitXmlDir(); + } + + $finder = new Finder(); + $finder->name('*Kernel.php')->depth(0)->in($dir); + $results = iterator_to_array($finder); + if (!count($results)) { + throw new \RuntimeException('Either set KERNEL_DIR in your phpunit.xml according to https://symfony.com/doc/current/book/testing.html#your-first-functional-test or override the WebTestCase::createKernel() method.'); + } + + $file = current($results); + $class = $file->getBasename('.php'); + + require_once $file; + + return $class; + } + + /** + * Boots the Kernel for this test. + * + * @param array $options + */ + protected static function bootKernel(array $options = array()) + { + static::ensureKernelShutdown(); + + static::$kernel = static::createKernel($options); + static::$kernel->boot(); + } + + /** + * Creates a Kernel. + * + * Available options: + * + * * environment + * * debug + * + * @param array $options An array of options + * + * @return KernelInterface A KernelInterface instance + */ + protected static function createKernel(array $options = array()) + { + if (null === static::$class) { + static::$class = static::getKernelClass(); + } + + return new static::$class( + isset($options['environment']) ? $options['environment'] : 'test', + isset($options['debug']) ? $options['debug'] : true + ); + } + + /** + * Shuts the kernel down if it was used in the test. + */ + protected static function ensureKernelShutdown() + { + if (null !== static::$kernel) { + $container = static::$kernel->getContainer(); + static::$kernel->shutdown(); + if ($container instanceof ResettableContainerInterface) { + $container->reset(); + } + } + } + + /** + * Clean up Kernel usage in this test. + */ + protected function tearDown() + { + static::ensureKernelShutdown(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php new file mode 100644 index 0000000..5708ef7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use Symfony\Bundle\FrameworkBundle\Client; + +/** + * WebTestCase is the base class for functional tests. + * + * @author Fabien Potencier + */ +abstract class WebTestCase extends KernelTestCase +{ + /** + * Creates a Client. + * + * @param array $options An array of options to pass to the createKernel class + * @param array $server An array of server parameters + * + * @return Client A Client instance + */ + protected static function createClient(array $options = array(), array $server = array()) + { + static::bootKernel($options); + + $client = static::$kernel->getContainer()->get('test.client'); + $client->setServerParameters($server); + + return $client; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplateFinderTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplateFinderTest.php new file mode 100644 index 0000000..a417cda --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/TemplateFinderTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser; +use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinder; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BaseBundle\BaseBundle; + +class TemplateFinderTest extends TestCase +{ + public function testFindAllTemplates() + { + $kernel = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Kernel') + ->disableOriginalConstructor() + ->getMock() + ; + + $kernel + ->expects($this->any()) + ->method('getBundle') + ; + + $kernel + ->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array('BaseBundle' => new BaseBundle()))) + ; + + $parser = new TemplateFilenameParser(); + + $finder = new TemplateFinder($kernel, $parser, __DIR__.'/../Fixtures/Resources'); + + $templates = array_map( + function ($template) { return $template->getLogicalName(); }, + $finder->findAllTemplates() + ); + + $this->assertCount(7, $templates, '->findAllTemplates() find all templates in the bundles and global folders'); + $this->assertContains('BaseBundle::base.format.engine', $templates); + $this->assertContains('BaseBundle::this.is.a.template.format.engine', $templates); + $this->assertContains('BaseBundle:controller:base.format.engine', $templates); + $this->assertContains('BaseBundle:controller:custom.format.engine', $templates); + $this->assertContains('::this.is.a.template.format.engine', $templates); + $this->assertContains('::resource.format.engine', $templates); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/ClientTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/ClientTest.php new file mode 100644 index 0000000..d021b0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/ClientTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests; + +use Symfony\Bundle\FrameworkBundle\Client; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\WebTestCase; +use Symfony\Component\HttpFoundation\Response; + +class ClientTest extends WebTestCase +{ + public function testRebootKernelBetweenRequests() + { + $mock = $this->getKernelMock(); + $mock->expects($this->once())->method('shutdown'); + + $client = new Client($mock); + $client->request('GET', '/'); + $client->request('GET', '/'); + } + + public function testDisabledRebootKernel() + { + $mock = $this->getKernelMock(); + $mock->expects($this->never())->method('shutdown'); + + $client = new Client($mock); + $client->disableReboot(); + $client->request('GET', '/'); + $client->request('GET', '/'); + } + + public function testEnableRebootKernel() + { + $mock = $this->getKernelMock(); + $mock->expects($this->once())->method('shutdown'); + + $client = new Client($mock); + $client->disableReboot(); + $client->request('GET', '/'); + $client->request('GET', '/'); + $client->enableReboot(); + $client->request('GET', '/'); + } + + private function getKernelMock() + { + $mock = $this->getMockBuilder($this->getKernelClass()) + ->setMethods(array('shutdown', 'boot', 'handle')) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects($this->any())->method('handle')->willReturn(new Response('foo')); + + return $mock; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php new file mode 100644 index 0000000..9830897 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture\TestAppKernel; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; + +class CacheClearCommandTest extends TestCase +{ + /** @var TestAppKernel */ + private $kernel; + /** @var Filesystem */ + private $fs; + private $rootDir; + + protected function setUp() + { + $this->fs = new Filesystem(); + $this->kernel = new TestAppKernel('test', true); + $this->rootDir = sys_get_temp_dir().DIRECTORY_SEPARATOR.uniqid('sf2_cache_', true); + $this->kernel->setRootDir($this->rootDir); + $this->fs->mkdir($this->rootDir); + } + + protected function tearDown() + { + $this->fs->remove($this->rootDir); + } + + public function testCacheIsFreshAfterCacheClearedWithWarmup() + { + $input = new ArrayInput(array('cache:clear')); + $application = new Application($this->kernel); + $application->setCatchExceptions(false); + + $application->doRun($input, new NullOutput()); + + // Ensure that all *.meta files are fresh + $finder = new Finder(); + $metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta'); + // simply check that cache is warmed up + $this->assertGreaterThanOrEqual(1, count($metaFiles)); + $configCacheFactory = new ConfigCacheFactory(true); + $that = $this; + + foreach ($metaFiles as $file) { + $configCacheFactory->cache(substr($file, 0, -5), function () use ($that, $file) { + $that->fail(sprintf('Meta file "%s" is not fresh', (string) $file)); + }); + } + + // check that app kernel file present in meta file of container's cache + $containerRef = new \ReflectionObject($this->kernel->getContainer()); + $containerFile = $containerRef->getFileName(); + $containerMetaFile = $containerFile.'.meta'; + $kernelRef = new \ReflectionObject($this->kernel); + $kernelFile = $kernelRef->getFileName(); + /** @var ResourceInterface[] $meta */ + $meta = unserialize(file_get_contents($containerMetaFile)); + $found = false; + foreach ($meta as $resource) { + if ((string) $resource === $kernelFile) { + $found = true; + break; + } + } + $this->assertTrue($found, 'Kernel file should present as resource'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.php new file mode 100644 index 0000000..8a61d96 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture; + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\HttpKernel\Kernel; + +class TestAppKernel extends Kernel +{ + public function registerBundles() + { + return array( + new FrameworkBundle(), + ); + } + + public function setRootDir($rootDir) + { + $this->rootDir = $rootDir; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.DIRECTORY_SEPARATOR.'config.yml'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/Fixture/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/Fixture/config.yml new file mode 100644 index 0000000..68f8d04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/Fixture/config.yml @@ -0,0 +1,2 @@ +framework: + secret: test diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php new file mode 100644 index 0000000..e42babd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterDebugCommandTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RouterDebugCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testDebugAllRoutes() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => null), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Name Method Scheme Host Path', $tester->getDisplay()); + } + + public function testDebugSingleRoute() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'foo'), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Route Name | foo', $tester->getDisplay()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDebugInvalidRoute() + { + $this->createCommandTester()->execute(array('name' => 'test')); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $application = new Application(); + + $command = new RouterDebugCommand(); + $command->setContainer($this->getContainer()); + $application->add($command); + + return new CommandTester($application->find('debug:router')); + } + + private function getContainer() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('foo', new Route('foo')); + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->any()) + ->method('getRouteCollection') + ->will($this->returnValue($routeCollection)) + ; + + $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader') + ->disableOriginalConstructor() + ->getMock(); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('has') + ->with('router') + ->will($this->returnValue(true)) + ; + + $container + ->method('get') + ->will($this->returnValueMap(array( + array('router', 1, $router), + array('controller_name_converter', 1, $loader), + ))); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php new file mode 100644 index 0000000..3c17285 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/RouterMatchCommandTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand; +use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; + +class RouterMatchCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testWithMatchPath() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('path_info' => '/foo', 'foo'), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Route Name | foo', $tester->getDisplay()); + } + + public function testWithNotMatchPath() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('path_info' => '/test', 'foo'), array('decorated' => false)); + + $this->assertEquals(1, $ret, 'Returns 1 in case of failure'); + $this->assertContains('None of the routes match the path "/test"', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $application = new Application(); + + $command = new RouterMatchCommand(); + $command->setContainer($this->getContainer()); + $application->add($command); + + $command = new RouterDebugCommand(); + $command->setContainer($this->getContainer()); + $application->add($command); + + return new CommandTester($application->find('router:match')); + } + + private function getContainer() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('foo', new Route('foo')); + $requestContext = new RequestContext(); + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->any()) + ->method('getRouteCollection') + ->will($this->returnValue($routeCollection)) + ; + $router + ->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($requestContext)) + ; + + $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader') + ->disableOriginalConstructor() + ->getMock(); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('has') + ->with('router') + ->will($this->returnValue(true)); + $container->method('get') + ->will($this->returnValueMap(array( + array('router', 1, $router), + array('controller_name_converter', 1, $loader), + + ))); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php new file mode 100644 index 0000000..c99c0ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; +use Symfony\Component\Filesystem\Filesystem; + +class TranslationDebugCommandTest extends \PHPUnit_Framework_TestCase +{ + private $fs; + private $translationDir; + + public function testDebugMissingMessages() + { + $tester = $this->createCommandTester($this->getContainer(array('foo' => 'foo'))); + $tester->execute(array('locale' => 'en', 'bundle' => 'foo')); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + } + + public function testDebugUnusedMessages() + { + $tester = $this->createCommandTester($this->getContainer(array(), array('foo' => 'foo'))); + $tester->execute(array('locale' => 'en', 'bundle' => 'foo')); + + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + public function testDebugFallbackMessages() + { + $tester = $this->createCommandTester($this->getContainer(array(), array('foo' => 'foo'))); + $tester->execute(array('locale' => 'fr', 'bundle' => 'foo')); + + $this->assertRegExp('/fallback/', $tester->getDisplay()); + } + + public function testNoDefinedMessages() + { + $tester = $this->createCommandTester($this->getContainer()); + $tester->execute(array('locale' => 'fr', 'bundle' => 'test')); + + $this->assertRegExp('/No defined or extracted messages for locale "fr"/', $tester->getDisplay()); + } + + public function testDebugDefaultDirectory() + { + $tester = $this->createCommandTester($this->getContainer(array('foo' => 'foo'), array('bar' => 'bar'))); + $tester->execute(array('locale' => 'en')); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + public function testDebugCustomDirectory() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel->expects($this->once()) + ->method('getBundle') + ->with($this->equalTo($this->translationDir)) + ->willThrowException(new \InvalidArgumentException()); + + $tester = $this->createCommandTester($this->getContainer(array('foo' => 'foo'), array('bar' => 'bar'), $kernel)); + $tester->execute(array('locale' => 'en', 'bundle' => $this->translationDir)); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDebugInvalidDirectory() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel->expects($this->once()) + ->method('getBundle') + ->with($this->equalTo('dir')) + ->will($this->throwException(new \InvalidArgumentException())); + + $tester = $this->createCommandTester($this->getContainer(array(), array(), $kernel)); + $tester->execute(array('locale' => 'en', 'bundle' => 'dir')); + } + + protected function setUp() + { + $this->fs = new Filesystem(); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/Resources/translations'); + $this->fs->mkdir($this->translationDir.'/Resources/views'); + } + + protected function tearDown() + { + $this->fs->remove($this->translationDir); + } + + /** + * @return CommandTester + */ + private function createCommandTester($container) + { + $command = new TranslationDebugCommand(); + $command->setContainer($container); + + $application = new Application(); + $application->add($command); + + return new CommandTester($application->find('debug:translation')); + } + + private function getContainer($extractedMessages = array(), $loadedMessages = array(), $kernel = null) + { + $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + + $translator + ->expects($this->any()) + ->method('getFallbackLocales') + ->will($this->returnValue(array('en'))); + + $extractor = $this->getMock('Symfony\Component\Translation\Extractor\ExtractorInterface'); + $extractor + ->expects($this->any()) + ->method('extract') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($extractedMessages) { + $catalogue->add($extractedMessages); + }) + ); + + $loader = $this->getMock('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader'); + $loader + ->expects($this->any()) + ->method('loadMessages') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($loadedMessages) { + $catalogue->add($loadedMessages); + }) + ); + + if (null === $kernel) { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnValueMap(array( + array('foo', true, $this->getBundle($this->translationDir)), + array('test', true, $this->getBundle('test')), + ))); + } + + $kernel + ->expects($this->any()) + ->method('getRootDir') + ->will($this->returnValue($this->translationDir)); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->any()) + ->method('get') + ->will($this->returnValueMap(array( + array('translation.extractor', 1, $extractor), + array('translation.loader', 1, $loader), + array('translator', 1, $translator), + array('kernel', 1, $kernel), + ))); + + return $container; + } + + private function getBundle($path) + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($path)) + ; + + return $bundle; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php new file mode 100644 index 0000000..a04a0cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\DependencyInjection; +use Symfony\Component\HttpKernel; + +class TranslationUpdateCommandTest extends \PHPUnit_Framework_TestCase +{ + private $fs; + private $translationDir; + + public function testDumpMessagesAndClean() + { + $tester = $this->createCommandTester($this->getContainer(array('foo' => 'foo'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true)); + $this->assertRegExp('/foo/', $tester->getDisplay()); + } + + protected function setUp() + { + $this->fs = new Filesystem(); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/Resources/translations'); + $this->fs->mkdir($this->translationDir.'/Resources/views'); + } + + protected function tearDown() + { + $this->fs->remove($this->translationDir); + } + + /** + * @return CommandTester + */ + private function createCommandTester(DependencyInjection\ContainerInterface $container) + { + $command = new TranslationUpdateCommand(); + $command->setContainer($container); + + $application = new Application(); + $application->add($command); + + return new CommandTester($application->find('translation:update')); + } + + private function getContainer($extractedMessages = array(), $loadedMessages = array(), HttpKernel\KernelInterface $kernel = null) + { + $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + + $translator + ->expects($this->any()) + ->method('getFallbackLocales') + ->will($this->returnValue(array('en'))); + + $extractor = $this->getMock('Symfony\Component\Translation\Extractor\ExtractorInterface'); + $extractor + ->expects($this->any()) + ->method('extract') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($extractedMessages) { + $catalogue->add($extractedMessages); + }) + ); + + $loader = $this->getMock('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader'); + $loader + ->expects($this->any()) + ->method('loadMessages') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($loadedMessages) { + $catalogue->add($loadedMessages); + }) + ); + + $writer = $this->getMock('Symfony\Component\Translation\Writer\TranslationWriter'); + $writer + ->expects($this->any()) + ->method('getFormats') + ->will( + $this->returnValue(array('xlf', 'yml')) + ); + + if (null === $kernel) { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnValueMap(array( + array('foo', true, $this->getBundle($this->translationDir)), + array('test', true, $this->getBundle('test')), + ))); + } + + $kernel + ->expects($this->any()) + ->method('getRootDir') + ->will($this->returnValue($this->translationDir)); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->any()) + ->method('get') + ->will($this->returnValueMap(array( + array('translation.extractor', 1, $extractor), + array('translation.loader', 1, $loader), + array('translation.writer', 1, $writer), + array('translator', 1, $translator), + array('kernel', 1, $kernel), + ))); + + return $container; + } + + private function getBundle($path) + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($path)) + ; + + return $bundle; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php new file mode 100644 index 0000000..612d632 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTest extends TestCase +{ + public function testBundleInterfaceImplementation() + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + $application->doRun(new ArrayInput(array('list')), new NullOutput()); + } + + public function testBundleCommandsAreRegistered() + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); + $bundle->expects($this->once())->method('registerCommands'); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + $application->doRun(new ArrayInput(array('list')), new NullOutput()); + } + + public function testBundleCommandsHaveRightContainer() + { + $command = $this->getMockForAbstractClass('Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand', array('foo'), '', true, true, true, array('setContainer')); + $command->setCode(function () {}); + $command->expects($this->exactly(2))->method('setContainer'); + + $application = new Application($this->getKernel(array())); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command); + $tester = new ApplicationTester($application); + + // set container is called here + $tester->run(array('command' => 'foo')); + + // as the container might have change between two runs, setContainer must called again + $tester->run(array('command' => 'foo')); + } + + private function getKernel(array $bundles) + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $dispatcher + ->expects($this->atLeastOnce()) + ->method('dispatch') + ; + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->atLeastOnce()) + ->method('get') + ->with($this->equalTo('event_dispatcher')) + ->will($this->returnValue($dispatcher)) + ; + $container + ->expects($this->once()) + ->method('hasParameter') + ->with($this->equalTo('console.command.ids')) + ->will($this->returnValue(true)) + ; + $container + ->expects($this->once()) + ->method('getParameter') + ->with($this->equalTo('console.command.ids')) + ->will($this->returnValue(array())) + ; + + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue($bundles)) + ; + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($container)) + ; + + return $kernel; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 0000000..6644bb2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeRouteCollectionTestData */ + public function testDescribeRouteCollection(RouteCollection $routes, $expectedDescription) + { + $this->assertDescription($expectedDescription, $routes); + } + + public function getDescribeRouteCollectionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getRouteCollections()); + } + + /** @dataProvider getDescribeRouteTestData */ + public function testDescribeRoute(Route $route, $expectedDescription) + { + $this->assertDescription($expectedDescription, $route); + } + + public function getDescribeRouteTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getRoutes()); + } + + /** @dataProvider getDescribeContainerParametersTestData */ + public function testDescribeContainerParameters(ParameterBag $parameters, $expectedDescription) + { + $this->assertDescription($expectedDescription, $parameters); + } + + public function getDescribeContainerParametersTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getContainerParameters()); + } + + /** @dataProvider getDescribeContainerBuilderTestData */ + public function testDescribeContainerBuilder(ContainerBuilder $builder, $expectedDescription, array $options) + { + $this->assertDescription($expectedDescription, $builder, $options); + } + + public function getDescribeContainerBuilderTestData() + { + return $this->getContainerBuilderDescriptionTestData(ObjectsProvider::getContainerBuilders()); + } + + /** @dataProvider getDescribeContainerDefinitionTestData */ + public function testDescribeContainerDefinition(Definition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + public function getDescribeContainerDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getContainerDefinitions()); + } + + /** @dataProvider getDescribeContainerAliasTestData */ + public function testDescribeContainerAlias(Alias $alias, $expectedDescription) + { + $this->assertDescription($expectedDescription, $alias); + } + + public function getDescribeContainerAliasTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getContainerAliases()); + } + + /** @dataProvider getDescribeContainerParameterTestData */ + public function testDescribeContainerParameter($parameter, $expectedDescription, array $options) + { + $this->assertDescription($expectedDescription, $parameter, $options); + } + + public function getDescribeContainerParameterTestData() + { + $data = $this->getDescriptionTestData(ObjectsProvider::getContainerParameter()); + + $data[0][] = array('parameter' => 'database_name'); + $data[1][] = array('parameter' => 'twig.form.resources'); + + return $data; + } + + /** @dataProvider getDescribeEventDispatcherTestData */ + public function testDescribeEventDispatcher(EventDispatcher $eventDispatcher, $expectedDescription, array $options) + { + $this->assertDescription($expectedDescription, $eventDispatcher, $options); + } + + public function getDescribeEventDispatcherTestData() + { + return $this->getEventDispatcherDescriptionTestData(ObjectsProvider::getEventDispatchers()); + } + + /** @dataProvider getDescribeCallableTestData */ + public function testDescribeCallable($callable, $expectedDescription) + { + $this->assertDescription($expectedDescription, $callable); + } + + public function getDescribeCallableTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCallables()); + } + + abstract protected function getDescriptor(); + abstract protected function getFormat(); + + private function assertDescription($expectedDescription, $describedObject, array $options = array()) + { + $options['raw_output'] = true; + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + + if ('txt' === $this->getFormat()) { + $options['output'] = new SymfonyStyle(new ArrayInput(array()), $output); + } + + $this->getDescriptor()->describe($output, $describedObject, $options); + + if ('json' === $this->getFormat()) { + $this->assertEquals(json_decode($expectedDescription), json_decode($output->fetch())); + } else { + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } + } + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + private function getContainerBuilderDescriptionTestData(array $objects) + { + $variations = array( + 'services' => array('show_private' => true), + 'public' => array('show_private' => false), + 'tag1' => array('show_private' => true, 'tag' => 'tag1'), + 'tags' => array('group_by' => 'tags', 'show_private' => true), + ); + + $data = array(); + foreach ($objects as $name => $object) { + foreach ($variations as $suffix => $options) { + $description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s_%s.%s', __DIR__, $name, $suffix, $this->getFormat())); + $data[] = array($object, $description, $options); + } + } + + return $data; + } + + private function getEventDispatcherDescriptionTestData(array $objects) + { + $variations = array( + 'events' => array(), + 'event1' => array('event' => 'event1'), + ); + + $data = array(); + foreach ($objects as $name => $object) { + foreach ($variations as $suffix => $options) { + $description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s_%s.%s', __DIR__, $name, $suffix, $this->getFormat())); + $data[] = array($object, $description, $options); + } + } + + return $data; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php new file mode 100644 index 0000000..ee03f65 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 0000000..fbb5aaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php new file mode 100644 index 0000000..71a02ae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class ObjectsProvider +{ + public static function getRouteCollections() + { + $collection1 = new RouteCollection(); + foreach (self::getRoutes() as $name => $route) { + $collection1->add($name, $route); + } + + return array('route_collection_1' => $collection1); + } + + public static function getRoutes() + { + return array( + 'route_1' => new Route( + '/hello/{name}', + array('name' => 'Joseph'), + array('name' => '[a-z]+'), + array('opt1' => 'val1', 'opt2' => 'val2'), + 'localhost', + array('http', 'https'), + array('get', 'head') + ), + 'route_2' => new Route( + '/name/add', + array(), + array(), + array('opt1' => 'val1', 'opt2' => 'val2'), + 'localhost', + array('http', 'https'), + array('put', 'post') + ), + ); + } + + public static function getContainerParameters() + { + return array( + 'parameters_1' => new ParameterBag(array( + 'integer' => 12, + 'string' => 'Hello world!', + 'boolean' => true, + 'array' => array(12, 'Hello world!', true), + )), + ); + } + + public static function getContainerParameter() + { + $builder = new ContainerBuilder(); + $builder->setParameter('database_name', 'symfony'); + $builder->setParameter('twig.form.resources', array( + 'bootstrap_3_horizontal_layout.html.twig', + 'bootstrap_3_layout.html.twig', + 'form_div_layout.html.twig', + 'form_table_layout.html.twig', + )); + + return array( + 'parameter' => $builder, + 'array_parameter' => $builder, + ); + } + + public static function getContainerBuilders() + { + $builder1 = new ContainerBuilder(); + $builder1->setDefinitions(self::getContainerDefinitions()); + $builder1->setAliases(self::getContainerAliases()); + + return array('builder_1' => $builder1); + } + + public static function getContainerDefinitions() + { + $definition1 = new Definition('Full\\Qualified\\Class1'); + $definition2 = new Definition('Full\\Qualified\\Class2'); + + return array( + 'definition_1' => $definition1 + ->setPublic(true) + ->setSynthetic(false) + ->setLazy(true) + ->setAbstract(true) + ->setFactory(array('Full\\Qualified\\FactoryClass', 'get')), + 'definition_2' => $definition2 + ->setPublic(false) + ->setSynthetic(true) + ->setFile('/path/to/file') + ->setLazy(false) + ->setAbstract(false) + ->addTag('tag1', array('attr1' => 'val1', 'attr2' => 'val2')) + ->addTag('tag1', array('attr3' => 'val3')) + ->addTag('tag2') + ->setFactory(array(new Reference('factory.service'), 'get')), + ); + } + + public static function getContainerAliases() + { + return array( + 'alias_1' => new Alias('service_1', true), + 'alias_2' => new Alias('service_2', false), + ); + } + + public static function getEventDispatchers() + { + $eventDispatcher = new EventDispatcher(); + + $eventDispatcher->addListener('event1', 'global_function', 255); + $eventDispatcher->addListener('event1', function () { return 'Closure'; }, -1); + $eventDispatcher->addListener('event2', new CallableClass()); + + return array('event_dispatcher_1' => $eventDispatcher); + } + + public static function getCallables() + { + return array( + 'callable_1' => 'array_key_exists', + 'callable_2' => array('Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass', 'staticMethod'), + 'callable_3' => array(new CallableClass(), 'method'), + 'callable_4' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass::staticMethod', + 'callable_5' => array('Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\ExtendedCallableClass', 'parent::staticMethod'), + 'callable_6' => function () { return 'Closure'; }, + 'callable_7' => new CallableClass(), + ); + } +} + +class CallableClass +{ + public function __invoke() + { + } + public static function staticMethod() + { + } + public function method() + { + } +} + +class ExtendedCallableClass extends CallableClass +{ + public static function staticMethod() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php new file mode 100644 index 0000000..ce4f377 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php new file mode 100644 index 0000000..8cb9a71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php new file mode 100644 index 0000000..4718c1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerNameParserTest.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\ClassLoader\ClassLoader; + +class ControllerNameParserTest extends TestCase +{ + protected $loader; + + protected function setUp() + { + $this->loader = new ClassLoader(); + $this->loader->addPrefixes(array( + 'TestBundle' => __DIR__.'/../Fixtures', + 'TestApplication' => __DIR__.'/../Fixtures', + )); + $this->loader->register(); + } + + protected function tearDown() + { + spl_autoload_unregister(array($this->loader, 'loadClass')); + + $this->loader = null; + } + + public function testParse() + { + $parser = $this->createParser(); + + $this->assertEquals('TestBundle\FooBundle\Controller\DefaultController::indexAction', $parser->parse('FooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction', $parser->parse('FooBundle:Sub\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\Fabpot\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\Sensio\Cms\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioCmsFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\FooBundle\Controller\Test\DefaultController::indexAction', $parser->parse('FooBundle:Test\\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\FooBundle\Controller\Test\DefaultController::indexAction', $parser->parse('FooBundle:Test/Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + + try { + $parser->parse('foo:'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an a:b:c string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an a:b:c string'); + } + } + + public function testBuild() + { + $parser = $this->createParser(); + + $this->assertEquals('FooBundle:Default:index', $parser->build('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + $this->assertEquals('FooBundle:Sub\Default:index', $parser->build('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + + try { + $parser->build('TestBundle\FooBundle\Controller\DefaultController::index'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } + + try { + $parser->build('TestBundle\FooBundle\Controller\Default::indexAction'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } + + try { + $parser->build('Foo\Controller\DefaultController::indexAction'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } + } + + /** + * @dataProvider getMissingControllersTest + */ + public function testMissingControllers($name) + { + $parser = $this->createParser(); + + try { + $parser->parse($name); + $this->fail('->parse() throws a \InvalidArgumentException if the string is in the valid format, but not matching class can be found'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws a \InvalidArgumentException if the class is found but does not exist'); + } + } + + public function getMissingControllersTest() + { + return array( + array('FooBundle:Fake:index'), // a normal bundle + array('SensioFooBundle:Fake:index'), // a bundle with children + ); + } + + /** + * @expectedException + * @dataProvider getInvalidBundleNameTests + */ + public function testInvalidBundleName($bundleName, $suggestedBundleName) + { + $parser = $this->createParser(); + + try { + $parser->parse($bundleName); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws a \InvalidArgumentException if the bundle does not exist'); + + if (false === $suggestedBundleName) { + // make sure we don't have a suggestion + $this->assertNotContains('Did you mean', $e->getMessage()); + } else { + $this->assertContains(sprintf('Did you mean "%s"', $suggestedBundleName), $e->getMessage()); + } + } + } + + public function getInvalidBundleNameTests() + { + return array( + array('FoodBundle:Default:index', 'FooBundle:Default:index'), + array('CrazyBundle:Default:index', false), + ); + } + + private function createParser() + { + $bundles = array( + 'SensioFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), + 'SensioCmsFooBundle' => array($this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle')), + 'FooBundle' => array($this->getBundle('TestBundle\FooBundle', 'FooBundle')), + 'FabpotFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), + ); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnCallback(function ($bundle) use ($bundles) { + if (!isset($bundles[$bundle])) { + throw new \InvalidArgumentException(sprintf('Invalid bundle name "%s"', $bundle)); + } + + return $bundles[$bundle]; + })) + ; + + $bundles = array( + 'SensioFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), + 'SensioCmsFooBundle' => $this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle'), + 'FooBundle' => $this->getBundle('TestBundle\FooBundle', 'FooBundle'), + 'FabpotFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), + ); + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue($bundles)) + ; + + return new ControllerNameParser($kernel); + } + + private function getBundle($namespace, $name) + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle->expects($this->any())->method('getName')->will($this->returnValue($name)); + $bundle->expects($this->any())->method('getNamespace')->will($this->returnValue($namespace)); + + return $bundle; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php new file mode 100644 index 0000000..d0b7040 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest as BaseControllerResolverTest; + +class ControllerResolverTest extends BaseControllerResolverTest +{ + public function testGetControllerOnContainerAware() + { + $resolver = $this->createControllerResolver(); + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerInterface', $controller[0]->getContainer()); + $this->assertSame('testAction', $controller[1]); + } + + public function testGetControllerOnContainerAwareInvokable() + { + $resolver = $this->createControllerResolver(); + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController', $controller); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerInterface', $controller->getContainer()); + } + + public function testGetControllerWithBundleNotation() + { + $shortName = 'FooBundle:Default:test'; + $parser = $this->createMockParser(); + $parser->expects($this->once()) + ->method('parse') + ->with($shortName) + ->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction')) + ; + + $resolver = $this->createControllerResolver(null, $parser); + $request = Request::create('/'); + $request->attributes->set('_controller', $shortName); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController', $controller[0]); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerInterface', $controller[0]->getContainer()); + $this->assertSame('testAction', $controller[1]); + } + + public function testGetControllerService() + { + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($this)) + ; + + $resolver = $this->createControllerResolver(null, null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', 'foo:controllerMethod1'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf(get_class($this), $controller[0]); + $this->assertSame('controllerMethod1', $controller[1]); + } + + public function testGetControllerInvokableService() + { + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with('foo') + ->will($this->returnValue(true)) + ; + $container->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($this)) + ; + + $resolver = $this->createControllerResolver(null, null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', 'foo'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf(get_class($this), $controller); + } + + /** + * @dataProvider getUndefinedControllers + */ + public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null) + { + // All this logic needs to be duplicated, since calling parent::testGetControllerOnNonUndefinedFunction will override the expected excetion and not use the regex + $resolver = $this->createControllerResolver(); + $this->setExpectedExceptionRegExp($exceptionName, $exceptionMessage); + + $request = Request::create('/'); + $request->attributes->set('_controller', $controller); + $resolver->getController($request); + } + + public function getUndefinedControllers() + { + return array( + array('foo', '\LogicException', '/Unable to parse the controller name "foo"\./'), + array('foo::bar', '\InvalidArgumentException', '/Class "foo" does not exist\./'), + array('stdClass', '\LogicException', '/Unable to parse the controller name "stdClass"\./'), + array( + 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar', + '\InvalidArgumentException', + '/.?[cC]ontroller(.*?) for URI "\/" is not callable\.( Expected method(.*) Available methods)?/', + ), + ); + } + + protected function createControllerResolver(LoggerInterface $logger = null, ControllerNameParser $parser = null, ContainerInterface $container = null) + { + if (!$parser) { + $parser = $this->createMockParser(); + } + + if (!$container) { + $container = $this->createMockContainer(); + } + + return new ControllerResolver($container, $parser, $logger); + } + + protected function createMockParser() + { + return $this->getMock('Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser', array(), array(), '', false); + } + + protected function createMockContainer() + { + return $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + } +} + +class ContainerAwareController implements ContainerAwareInterface +{ + private $container; + + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } + + public function getContainer() + { + return $this->container; + } + + public function testAction() + { + } + + public function __invoke() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php new file mode 100644 index 0000000..2bb59d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\User\User; + +class ControllerTest extends TestCase +{ + public function testForward() + { + $request = Request::create('/'); + $request->setLocale('fr'); + $request->setRequestFormat('xml'); + + $requestStack = new RequestStack(); + $requestStack->push($request); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + return new Response($request->getRequestFormat().'--'.$request->getLocale()); + })); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->at(0))->method('get')->will($this->returnValue($requestStack)); + $container->expects($this->at(1))->method('get')->will($this->returnValue($kernel)); + + $controller = new TestController(); + $controller->setContainer($container); + + $response = $controller->forward('a_controller'); + $this->assertEquals('xml--fr', $response->getContent()); + } + + public function testGetUser() + { + $user = new User('user', 'pass'); + $token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')); + + $controller = new TestController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); + + $this->assertSame($controller->getUser(), $user); + } + + public function testGetUserAnonymousUserConvertedToNull() + { + $token = new AnonymousToken('default', 'anon.'); + + $controller = new TestController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); + + $this->assertNull($controller->getUser()); + } + + public function testGetUserWithEmptyTokenStorage() + { + $controller = new TestController(); + $controller->setContainer($this->getContainerWithTokenStorage(null)); + + $this->assertNull($controller->getUser()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The SecurityBundle is not registered in your application. + */ + public function testGetUserWithEmptyContainer() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('has') + ->with('security.token_storage') + ->will($this->returnValue(false)); + + $controller = new TestController(); + $controller->setContainer($container); + + $controller->getUser(); + } + + /** + * @param $token + * + * @return ContainerInterface + */ + private function getContainerWithTokenStorage($token = null) + { + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage'); + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token)); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('has') + ->with('security.token_storage') + ->will($this->returnValue(true)); + + $container + ->expects($this->once()) + ->method('get') + ->with('security.token_storage') + ->will($this->returnValue($tokenStorage)); + + return $container; + } +} + +class TestController extends Controller +{ + public function forward($controller, array $path = array(), array $query = array()) + { + return parent::forward($controller, $path, $query); + } + + public function getUser() + { + return parent::getUser(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php new file mode 100644 index 0000000..eaca189 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -0,0 +1,297 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +/** + * @author Marcin Sikon + */ +class RedirectControllerTest extends TestCase +{ + public function testEmptyRoute() + { + $request = new Request(); + $controller = new RedirectController(); + + try { + $controller->redirectAction($request, '', true); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(410, $e->getStatusCode()); + } + + try { + $controller->redirectAction($request, '', false); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(404, $e->getStatusCode()); + } + } + + /** + * @dataProvider provider + */ + public function testRoute($permanent, $ignoreAttributes, $expectedCode, $expectedAttributes) + { + $request = new Request(); + + $route = 'new-route'; + $url = '/redirect-url'; + $attributes = array( + 'route' => $route, + 'permanent' => $permanent, + '_route' => 'current-route', + '_route_params' => array( + 'route' => $route, + 'permanent' => $permanent, + 'additional-parameter' => 'value', + 'ignoreAttributes' => $ignoreAttributes, + ), + ); + + $request->attributes = new ParameterBag($attributes); + + $router = $this->getMock('Symfony\Component\Routing\RouterInterface'); + $router + ->expects($this->once()) + ->method('generate') + ->with($this->equalTo($route), $this->equalTo($expectedAttributes)) + ->will($this->returnValue($url)); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $container + ->expects($this->once()) + ->method('get') + ->with($this->equalTo('router')) + ->will($this->returnValue($router)); + + $controller = new RedirectController(); + $controller->setContainer($container); + + $returnResponse = $controller->redirectAction($request, $route, $permanent, $ignoreAttributes); + + $this->assertRedirectUrl($returnResponse, $url); + $this->assertEquals($expectedCode, $returnResponse->getStatusCode()); + } + + public function provider() + { + return array( + array(true, false, 301, array('additional-parameter' => 'value')), + array(false, false, 302, array('additional-parameter' => 'value')), + array(false, true, 302, array()), + array(false, array('additional-parameter'), 302, array()), + ); + } + + public function testEmptyPath() + { + $request = new Request(); + $controller = new RedirectController(); + + try { + $controller->urlRedirectAction($request, '', true); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(410, $e->getStatusCode()); + } + + try { + $controller->urlRedirectAction($request, '', false); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(404, $e->getStatusCode()); + } + } + + public function testFullURL() + { + $request = new Request(); + $controller = new RedirectController(); + $returnResponse = $controller->urlRedirectAction($request, 'http://foo.bar/'); + + $this->assertRedirectUrl($returnResponse, 'http://foo.bar/'); + $this->assertEquals(302, $returnResponse->getStatusCode()); + } + + public function testUrlRedirectDefaultPortParameters() + { + $host = 'www.example.com'; + $baseUrl = '/base'; + $path = '/redirect-path'; + $httpPort = 1080; + $httpsPort = 1443; + + $expectedUrl = "https://$host:$httpsPort$baseUrl$path"; + $request = $this->createRequestObject('http', $host, $httpPort, $baseUrl); + $controller = $this->createRedirectController(null, $httpsPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'https'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + + $expectedUrl = "http://$host:$httpPort$baseUrl$path"; + $request = $this->createRequestObject('https', $host, $httpPort, $baseUrl); + $controller = $this->createRedirectController($httpPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'http'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + public function urlRedirectProvider() + { + return array( + // Standard ports + array('http', null, null, 'http', 80, ''), + array('http', 80, null, 'http', 80, ''), + array('https', null, null, 'http', 80, ''), + array('https', 80, null, 'http', 80, ''), + + array('http', null, null, 'https', 443, ''), + array('http', null, 443, 'https', 443, ''), + array('https', null, null, 'https', 443, ''), + array('https', null, 443, 'https', 443, ''), + + // Non-standard ports + array('http', null, null, 'http', 8080, ':8080'), + array('http', 4080, null, 'http', 8080, ':4080'), + array('http', 80, null, 'http', 8080, ''), + array('https', null, null, 'http', 8080, ''), + array('https', null, 8443, 'http', 8080, ':8443'), + array('https', null, 443, 'http', 8080, ''), + + array('https', null, null, 'https', 8443, ':8443'), + array('https', null, 4443, 'https', 8443, ':4443'), + array('https', null, 443, 'https', 8443, ''), + array('http', null, null, 'https', 8443, ''), + array('http', 8080, 4443, 'https', 8443, ':8080'), + array('http', 80, 4443, 'https', 8443, ''), + ); + } + + /** + * @dataProvider urlRedirectProvider + */ + public function testUrlRedirect($scheme, $httpPort, $httpsPort, $requestScheme, $requestPort, $expectedPort) + { + $host = 'www.example.com'; + $baseUrl = '/base'; + $path = '/redirect-path'; + $expectedUrl = "$scheme://$host$expectedPort$baseUrl$path"; + + $request = $this->createRequestObject($requestScheme, $host, $requestPort, $baseUrl); + $controller = $this->createRedirectController(); + + $returnValue = $controller->urlRedirectAction($request, $path, false, $scheme, $httpPort, $httpsPort); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + public function pathQueryParamsProvider() + { + return array( + array('http://www.example.com/base/redirect-path', '/redirect-path', ''), + array('http://www.example.com/base/redirect-path?foo=bar', '/redirect-path?foo=bar', ''), + array('http://www.example.com/base/redirect-path?foo=bar', '/redirect-path', 'foo=bar'), + array('http://www.example.com/base/redirect-path?foo=bar&abc=example', '/redirect-path?foo=bar', 'abc=example'), + array('http://www.example.com/base/redirect-path?foo=bar&abc=example&baz=def', '/redirect-path?foo=bar', 'abc=example&baz=def'), + ); + } + + /** + * @dataProvider pathQueryParamsProvider + */ + public function testPathQueryParams($expectedUrl, $path, $queryString) + { + $scheme = 'http'; + $host = 'www.example.com'; + $baseUrl = '/base'; + $port = 80; + + $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, $queryString); + + $controller = $this->createRedirectController(); + + $returnValue = $controller->urlRedirectAction($request, $path, false, $scheme, $port, null); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + private function createRequestObject($scheme, $host, $port, $baseUrl, $queryString = '') + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request + ->expects($this->any()) + ->method('getScheme') + ->will($this->returnValue($scheme)); + $request + ->expects($this->any()) + ->method('getHost') + ->will($this->returnValue($host)); + $request + ->expects($this->any()) + ->method('getPort') + ->will($this->returnValue($port)); + $request + ->expects($this->any()) + ->method('getBaseUrl') + ->will($this->returnValue($baseUrl)); + $request + ->expects($this->any()) + ->method('getQueryString') + ->will($this->returnValue($queryString)); + + return $request; + } + + private function createRedirectController($httpPort = null, $httpsPort = null) + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + if (null !== $httpPort) { + $container + ->expects($this->once()) + ->method('hasParameter') + ->with($this->equalTo('request_listener.http_port')) + ->will($this->returnValue(true)); + $container + ->expects($this->once()) + ->method('getParameter') + ->with($this->equalTo('request_listener.http_port')) + ->will($this->returnValue($httpPort)); + } + if (null !== $httpsPort) { + $container + ->expects($this->once()) + ->method('hasParameter') + ->with($this->equalTo('request_listener.https_port')) + ->will($this->returnValue(true)); + $container + ->expects($this->once()) + ->method('getParameter') + ->with($this->equalTo('request_listener.https_port')) + ->will($this->returnValue($httpsPort)); + } + + $controller = new RedirectController(); + $controller->setContainer($container); + + return $controller; + } + + public function assertRedirectUrl(Response $returnResponse, $expectedUrl) + { + $this->assertTrue($returnResponse->isRedirect($expectedUrl), "Expected: $expectedUrl\nGot: ".$returnResponse->headers->get('Location')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php new file mode 100644 index 0000000..61d31cb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass; + +class AddCacheWarmerPassTest extends \PHPUnit_Framework_TestCase +{ + public function testThatCacheWarmersAreProcessedInPriorityOrder() + { + $services = array( + 'my_cache_warmer_service1' => array(0 => array('priority' => 100)), + 'my_cache_warmer_service2' => array(0 => array('priority' => 200)), + 'my_cache_warmer_service3' => array(), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('findTaggedServiceIds', 'getDefinition', 'hasDefinition') + ); + + $container->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + $container->expects($this->atLeastOnce()) + ->method('getDefinition') + ->with('cache_warmer') + ->will($this->returnValue($definition)); + $container->expects($this->atLeastOnce()) + ->method('hasDefinition') + ->with('cache_warmer') + ->will($this->returnValue(true)); + + $definition->expects($this->once()) + ->method('replaceArgument') + ->with(0, array( + new Reference('my_cache_warmer_service2'), + new Reference('my_cache_warmer_service1'), + new Reference('my_cache_warmer_service3'), + )); + + $addCacheWarmerPass = new AddCacheWarmerPass(); + $addCacheWarmerPass->process($container); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNoCacheWarmerDefinition() + { + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + + $container->expects($this->never())->method('findTaggedServiceIds'); + $container->expects($this->never())->method('getDefinition'); + $container->expects($this->atLeastOnce()) + ->method('hasDefinition') + ->with('cache_warmer') + ->will($this->returnValue(false)); + $definition->expects($this->never())->method('replaceArgument'); + + $addCacheWarmerPass = new AddCacheWarmerPass(); + $addCacheWarmerPass->process($container); + } + + public function testThatCacheWarmersMightBeNotDefined() + { + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + + $container->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue(array())); + $container->expects($this->never())->method('getDefinition'); + $container->expects($this->atLeastOnce()) + ->method('hasDefinition') + ->with('cache_warmer') + ->will($this->returnValue(true)); + + $definition->expects($this->never())->method('replaceArgument'); + + $addCacheWarmerPass = new AddCacheWarmerPass(); + $addCacheWarmerPass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php new file mode 100644 index 0000000..9750744 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AddConsoleCommandPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + $container->setParameter('my-command.class', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); + + $definition = new Definition('%my-command.class%'); + $definition->addTag('console.command'); + $container->setDefinition('my-command', $definition); + + $container->compile(); + + $alias = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; + $this->assertTrue($container->hasAlias($alias)); + $this->assertSame('my-command', (string) $container->getAlias($alias)); + + $this->assertTrue($container->hasParameter('console.command.ids')); + $this->assertSame(array('my-command'), $container->getParameter('console.command.ids')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my-command" tagged "console.command" must be public. + */ + public function testProcessThrowAnExceptionIfTheServiceIsNotPublic() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); + $definition->addTag('console.command'); + $definition->setPublic(false); + $container->setDefinition('my-command', $definition); + + $container->compile(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my-command" tagged "console.command" must not be abstract. + */ + public function testProcessThrowAnExceptionIfTheServiceIsAbstract() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); + $definition->addTag('console.command'); + $definition->setAbstract(true); + $container->setDefinition('my-command', $definition); + + $container->compile(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my-command" tagged "console.command" must be a subclass of "Symfony\Component\Console\Command\Command". + */ + public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + + $definition = new Definition('SplObjectStorage'); + $definition->addTag('console.command'); + $container->setDefinition('my-command', $definition); + + $container->compile(); + } + + public function testHttpKernelRegisterCommandsIngoreCommandAsAService() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); + $definition->addTag('console.command'); + $container->setDefinition('my-command', $definition); + $container->compile(); + + $application = $this->getMock('Symfony\Component\Console\Application'); + // Never called, because it's the + // Symfony\Bundle\FrameworkBundle\Console\Application that register + // commands as a service + $application->expects($this->never())->method('add'); + + $bundle = new ExtensionPresentBundle(); + $bundle->setContainer($container); + $bundle->registerCommands($application); + } +} + +class MyCommand extends Command +{ +} + +class ExtensionPresentBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php new file mode 100644 index 0000000..9bc4acb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; + +class AddExpressionLanguageProvidersPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcessForRouter() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TestProvider'); + $definition->addTag('routing.expression_language_provider'); + $container->setDefinition('some_routing_provider', $definition); + + $container->register('router', '\stdClass'); + $container->compile(); + + $router = $container->getDefinition('router'); + $calls = $router->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_routing_provider'), $calls[0][1][0]); + } + + public function testProcessForRouterAlias() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TestProvider'); + $definition->addTag('routing.expression_language_provider'); + $container->setDefinition('some_routing_provider', $definition); + + $container->register('my_router', '\stdClass'); + $container->setAlias('router', 'my_router'); + $container->compile(); + + $router = $container->getDefinition('my_router'); + $calls = $router->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_routing_provider'), $calls[0][1][0]); + } + + public function testProcessForSecurity() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TestProvider'); + $definition->addTag('security.expression_language_provider'); + $container->setDefinition('some_security_provider', $definition); + + $container->register('security.access.expression_voter', '\stdClass'); + $container->compile(); + + $router = $container->getDefinition('security.access.expression_voter'); + $calls = $router->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_security_provider'), $calls[0][1][0]); + } + + public function testProcessForSecurityAlias() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TestProvider'); + $definition->addTag('security.expression_language_provider'); + $container->setDefinition('some_security_provider', $definition); + + $container->register('my_security.access.expression_voter', '\stdClass'); + $container->setAlias('security.access.expression_voter', 'my_security.access.expression_voter'); + $container->compile(); + + $router = $container->getDefinition('my_security.access.expression_voter'); + $calls = $router->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_security_provider'), $calls[0][1][0]); + } +} + +class TestProvider +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php new file mode 100644 index 0000000..c0eef3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass; + +class ConfigCachePassTest extends \PHPUnit_Framework_TestCase +{ + public function testThatCheckersAreProcessedInPriorityOrder() + { + $services = array( + 'checker_2' => array(0 => array('priority' => 100)), + 'checker_1' => array(0 => array('priority' => 200)), + 'checker_3' => array(), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('findTaggedServiceIds', 'getDefinition', 'hasDefinition') + ); + + $container->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + $container->expects($this->atLeastOnce()) + ->method('getDefinition') + ->with('config_cache_factory') + ->will($this->returnValue($definition)); + + $definition->expects($this->once()) + ->method('replaceArgument') + ->with(0, array( + new Reference('checker_1'), + new Reference('checker_2'), + new Reference('checker_3'), + )); + + $pass = new ConfigCachePass(); + $pass->process($container); + } + + public function testThatCheckersCanBeMissing() + { + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('findTaggedServiceIds') + ); + + $container->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue(array())); + + $pass = new ConfigCachePass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php new file mode 100644 index 0000000..4cd21ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Form\AbstractType; + +/** + * @author Bernhard Schussek + */ +class FormPassTest extends \PHPUnit_Framework_TestCase +{ + public function testDoNothingIfFormExtensionNotLoaded() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $container->compile(); + + $this->assertFalse($container->hasDefinition('form.extension')); + } + + public function testAddTaggedTypes() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $extDefinition->setArguments(array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $container->setDefinition('form.extension', $extDefinition); + $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type'); + $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type'); + + $container->compile(); + + $extDefinition = $container->getDefinition('form.extension'); + + $this->assertEquals(array( + __CLASS__.'_Type1' => 'my.type1', + __CLASS__.'_Type2' => 'my.type2', + ), $extDefinition->getArgument(1)); + } + + public function testAddTaggedTypeExtensions() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $container->setDefinition('form.extension', $extDefinition); + $container->register('my.type_extension1', 'stdClass') + ->addTag('form.type_extension', array('extended_type' => 'type1')); + $container->register('my.type_extension2', 'stdClass') + ->addTag('form.type_extension', array('extended_type' => 'type1')); + $container->register('my.type_extension3', 'stdClass') + ->addTag('form.type_extension', array('extended_type' => 'type2')); + + $container->compile(); + + $extDefinition = $container->getDefinition('form.extension'); + + $this->assertSame(array( + 'type1' => array( + 'my.type_extension1', + 'my.type_extension2', + ), + 'type2' => array( + 'my.type_extension3', + ), + ), $extDefinition->getArgument(2)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage extended-type attribute, none was configured for the "my.type_extension" service + */ + public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $container->setDefinition('form.extension', $extDefinition); + $container->register('my.type_extension', 'stdClass') + ->addTag('form.type_extension'); + + $container->compile(); + } + + public function testAddTaggedGuessers() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $extDefinition->setArguments(array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $definition1 = new Definition('stdClass'); + $definition1->addTag('form.type_guesser'); + $definition2 = new Definition('stdClass'); + $definition2->addTag('form.type_guesser'); + + $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('my.guesser1', $definition1); + $container->setDefinition('my.guesser2', $definition2); + + $container->compile(); + + $extDefinition = $container->getDefinition('form.extension'); + + $this->assertSame(array( + 'my.guesser1', + 'my.guesser2', + ), $extDefinition->getArgument(3)); + } + + /** + * @dataProvider privateTaggedServicesProvider + */ + public function testPrivateTaggedServices($id, $tagName, $expectedExceptionMessage) + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $extDefinition->setArguments(array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $container->setDefinition('form.extension', $extDefinition); + $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName); + + $this->setExpectedException('\InvalidArgumentException', $expectedExceptionMessage); + + $container->compile(); + } + + public function privateTaggedServicesProvider() + { + return array( + array('my.type', 'form.type', 'The service "my.type" must be public as form types are lazy-loaded'), + array('my.type_extension', 'form.type_extension', 'The service "my.type_extension" must be public as form type extensions are lazy-loaded'), + array('my.guesser', 'form.type_guesser', 'The service "my.guesser" must be public as form type guessers are lazy-loaded'), + ); + } +} + +class FormPassTest_Type1 extends AbstractType +{ +} + +class FormPassTest_Type2 extends AbstractType +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php new file mode 100644 index 0000000..77f894f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; + +class LoggingTranslatorPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $parameterBag = $this->getMock('Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface'); + + $container->expects($this->exactly(2)) + ->method('hasAlias') + ->will($this->returnValue(true)); + + $container->expects($this->once()) + ->method('getParameter') + ->will($this->returnValue(true)); + + $container->expects($this->once()) + ->method('getAlias') + ->will($this->returnValue('translation.default')); + + $container->expects($this->exactly(3)) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $container->expects($this->once()) + ->method('hasParameter') + ->with('translator.logging') + ->will($this->returnValue(true)); + + $definition->expects($this->once()) + ->method('getClass') + ->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Translation\Translator')); + + $parameterBag->expects($this->once()) + ->method('resolveValue') + ->will($this->returnValue("Symfony\Bundle\FrameworkBundle\Translation\Translator")); + + $container->expects($this->once()) + ->method('getParameterBag') + ->will($this->returnValue($parameterBag)); + + $pass = new LoggingTranslatorPass(); + $pass->process($container); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNotLoggerDefinition() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $container->expects($this->once()) + ->method('hasAlias') + ->will($this->returnValue(false)); + + $pass = new LoggingTranslatorPass(); + $pass->process($container); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNotTranslatorDefinition() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $container->expects($this->at(0)) + ->method('hasAlias') + ->will($this->returnValue(true)); + + $container->expects($this->at(0)) + ->method('hasAlias') + ->will($this->returnValue(false)); + + $pass = new LoggingTranslatorPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php new file mode 100644 index 0000000..f2af872 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; + +class ProfilerPassTest extends \PHPUnit_Framework_TestCase +{ + private $profilerDefinition; + + protected function setUp() + { + $this->profilerDefinition = new Definition('ProfilerClass'); + } + + /** + * Tests that collectors that specify a template but no "id" will throw + * an exception (both are needed if the template is specified). + * + * Thus, a fully-valid tag looks something like this: + * + * + */ + public function testTemplateNoIdThrowsException() + { + // one service, with a template key, but no id + $services = array( + 'my_collector_service' => array(0 => array('template' => 'foo')), + ); + + $builder = $this->createContainerMock($services); + + $this->setExpectedException('InvalidArgumentException'); + + $profilerPass = new ProfilerPass(); + $profilerPass->process($builder); + } + + public function testValidCollector() + { + // one service, with a template key, but no id + $services = array( + 'my_collector_service' => array(0 => array('template' => 'foo', 'id' => 'my_collector')), + ); + + $container = $this->createContainerMock($services); + + // fake the getDefinition() to return a Profiler definition + $container->expects($this->atLeastOnce()) + ->method('getDefinition'); + + // assert that the data_collector.templates parameter should be set + $container->expects($this->once()) + ->method('setParameter') + ->with('data_collector.templates', array('my_collector_service' => array('my_collector', 'foo'))); + + $profilerPass = new ProfilerPass(); + $profilerPass->process($container); + + // grab the method calls off of the "profiler" definition + $methodCalls = $this->profilerDefinition->getMethodCalls(); + $this->assertCount(1, $methodCalls); + $this->assertEquals('add', $methodCalls[0][0]); // grab the method part of the first call + } + + private function createContainerMock($services) + { + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'getDefinition', 'findTaggedServiceIds', 'setParameter') + ); + $container->expects($this->any()) + ->method('hasDefinition') + ->with($this->equalTo('profiler')) + ->will($this->returnValue(true)); + $container->expects($this->any()) + ->method('getDefinition') + ->will($this->returnValue($this->profilerDefinition)); + $container->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php new file mode 100644 index 0000000..3d41fb1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; +use Symfony\Component\DependencyInjection\Reference; + +class PropertyInfoPassTest extends \PHPUnit_Framework_TestCase +{ + public function testServicesAreOrderedAccordingToPriority() + { + $services = array( + 'n3' => array('tag' => array()), + 'n1' => array('tag' => array('priority' => 200)), + 'n2' => array('tag' => array('priority' => 100)), + ); + + $expected = array( + new Reference('n1'), + new Reference('n2'), + new Reference('n3'), + ); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds')); + + $container->expects($this->any()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + $propertyInfoPass = new PropertyInfoPass(); + + $method = new \ReflectionMethod( + 'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass', + 'findAndSortTaggedServices' + ); + $method->setAccessible(true); + + $actual = $method->invoke($propertyInfoPass, 'tag', $container); + + $this->assertEquals($expected, $actual); + } + + public function testReturningEmptyArrayWhenNoService() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds')); + + $container->expects($this->any()) + ->method('findTaggedServiceIds') + ->will($this->returnValue(array())); + + $propertyInfoPass = new PropertyInfoPass(); + + $method = new \ReflectionMethod( + 'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass', + 'findAndSortTaggedServices' + ); + $method->setAccessible(true); + + $actual = $method->invoke($propertyInfoPass, 'tag', $container); + + $this->assertEquals(array(), $actual); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php new file mode 100644 index 0000000..27f1636 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; + +/** + * Tests for the SerializerPass class. + * + * @author Javier Lopez + */ +class SerializerPassTest extends \PHPUnit_Framework_TestCase +{ + public function testThrowExceptionWhenNoNormalizers() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('hasDefinition', 'findTaggedServiceIds')); + + $container->expects($this->once()) + ->method('hasDefinition') + ->with('serializer') + ->will($this->returnValue(true)); + + $container->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('serializer.normalizer') + ->will($this->returnValue(array())); + + $this->setExpectedException('RuntimeException'); + + $serializerPass = new SerializerPass(); + $serializerPass->process($container); + } + + public function testThrowExceptionWhenNoEncoders() + { + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + + $container->expects($this->once()) + ->method('hasDefinition') + ->with('serializer') + ->will($this->returnValue(true)); + + $container->expects($this->any()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls( + array('n' => array('serializer.normalizer')), + array() + )); + + $container->expects($this->once()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $this->setExpectedException('RuntimeException'); + + $serializerPass = new SerializerPass(); + $serializerPass->process($container); + } + + public function testServicesAreOrderedAccordingToPriority() + { + $services = array( + 'n3' => array('tag' => array()), + 'n1' => array('tag' => array('priority' => 200)), + 'n2' => array('tag' => array('priority' => 100)), + ); + + $expected = array( + new Reference('n1'), + new Reference('n2'), + new Reference('n3'), + ); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', array('findTaggedServiceIds')); + + $container->expects($this->any()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + $serializerPass = new SerializerPass(); + + $method = new \ReflectionMethod( + 'Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass', + 'findAndSortTaggedServices' + ); + $method->setAccessible(true); + + $actual = $method->invoke($serializerPass, 'tag', $container); + + $this->assertEquals($expected, $actual); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php new file mode 100644 index 0000000..83f7051 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; + +class TranslatorPassTest extends \PHPUnit_Framework_TestCase +{ + public function testValidCollector() + { + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->at(0)) + ->method('addMethodCall') + ->with('addLoader', array('xliff', new Reference('xliff'))); + $definition->expects($this->at(1)) + ->method('addMethodCall') + ->with('addLoader', array('xlf', new Reference('xliff'))); + + $container = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'getDefinition', 'findTaggedServiceIds', 'findDefinition') + ); + $container->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + $container->expects($this->once()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + $container->expects($this->once()) + ->method('findTaggedServiceIds') + ->will($this->returnValue(array('xliff' => array(array('alias' => 'xliff', 'legacy-alias' => 'xlf'))))); + $container->expects($this->once()) + ->method('findDefinition') + ->will($this->returnValue($this->getMock('Symfony\Component\DependencyInjection\Definition'))); + $pass = new TranslatorPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php new file mode 100644 index 0000000..f354007 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; + +class UnusedTagsPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $pass = new UnusedTagsPass(); + + $formatter = $this->getMock('Symfony\Component\DependencyInjection\Compiler\LoggingFormatter'); + $formatter + ->expects($this->at(0)) + ->method('format') + ->with($pass, 'Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?') + ; + + $compiler = $this->getMock('Symfony\Component\DependencyInjection\Compiler\Compiler'); + $compiler->expects($this->once())->method('getLoggingFormatter')->will($this->returnValue($formatter)); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder', + array('findTaggedServiceIds', 'getCompiler', 'findUnusedTags', 'findTags') + ); + $container->expects($this->once())->method('getCompiler')->will($this->returnValue($compiler)); + $container->expects($this->once()) + ->method('findTags') + ->will($this->returnValue(array('kenrel.event_subscriber'))); + $container->expects($this->once()) + ->method('findUnusedTags') + ->will($this->returnValue(array('kenrel.event_subscriber', 'form.type'))); + $container->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('kenrel.event_subscriber') + ->will($this->returnValue(array( + 'foo' => array(), + 'bar' => array(), + ))); + + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..d0dba2b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration; +use Symfony\Component\Config\Definition\Processor; + +class ConfigurationTest extends \PHPUnit_Framework_TestCase +{ + public function testDefaultConfig() + { + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(true), array(array('secret' => 's3cr3t'))); + + $this->assertEquals( + array_merge(array('secret' => 's3cr3t', 'trusted_hosts' => array()), self::getBundleDefaultConfig()), + $config + ); + } + + public function testDoNoDuplicateDefaultFormResources() + { + $input = array('templating' => array( + 'form' => array('resources' => array('FrameworkBundle:Form')), + 'engines' => array('php'), + )); + + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(true), array($input)); + + $this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']); + } + + /** + * @dataProvider getTestValidTrustedProxiesData + */ + public function testValidTrustedProxies($trustedProxies, $processedProxies) + { + $processor = new Processor(); + $configuration = new Configuration(true); + $config = $processor->processConfiguration($configuration, array(array( + 'secret' => 's3cr3t', + 'trusted_proxies' => $trustedProxies, + ))); + + $this->assertEquals($processedProxies, $config['trusted_proxies']); + } + + public function getTestValidTrustedProxiesData() + { + return array( + array(array('127.0.0.1'), array('127.0.0.1')), + array(array('::1'), array('::1')), + array(array('127.0.0.1', '::1'), array('127.0.0.1', '::1')), + array(null, array()), + array(false, array()), + array(array(), array()), + array(array('10.0.0.0/8'), array('10.0.0.0/8')), + array(array('::ffff:0:0/96'), array('::ffff:0:0/96')), + ); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidTypeTrustedProxies() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => 'Not an IP address', + ), + )); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidValueTrustedProxies() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => array('Not an IP address'), + ), + )); + } + + public function testAssetsCanBeEnabled() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $config = $processor->processConfiguration($configuration, array(array('assets' => null))); + + $defaultConfig = array( + 'version' => null, + 'version_format' => '%%s?%%s', + 'base_path' => '', + 'base_urls' => array(), + 'packages' => array(), + ); + + $this->assertEquals($defaultConfig, $config['assets']); + } + + protected static function getBundleDefaultConfig() + { + return array( + 'http_method_override' => true, + 'trusted_proxies' => array(), + 'ide' => null, + 'default_locale' => 'en', + 'csrf_protection' => array( + 'enabled' => false, + ), + 'form' => array( + 'enabled' => false, + 'csrf_protection' => array( + 'enabled' => null, // defaults to csrf_protection.enabled + 'field_name' => '_token', + ), + ), + 'esi' => array('enabled' => false), + 'ssi' => array('enabled' => false), + 'fragments' => array( + 'enabled' => false, + 'path' => '/_fragment', + ), + 'profiler' => array( + 'enabled' => false, + 'only_exceptions' => false, + 'only_master_requests' => false, + 'dsn' => 'file:%kernel.cache_dir%/profiler', + 'collect' => true, + ), + 'translator' => array( + 'enabled' => false, + 'fallbacks' => array('en'), + 'logging' => true, + 'paths' => array(), + ), + 'validation' => array( + 'enabled' => false, + 'enable_annotations' => false, + 'static_method' => array('loadValidatorMetadata'), + 'translation_domain' => 'validators', + 'strict_email' => false, + ), + 'annotations' => array( + 'cache' => 'file', + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + 'debug' => '%kernel.debug%', + ), + 'serializer' => array( + 'enabled' => false, + 'enable_annotations' => false, + ), + 'property_access' => array( + 'magic_call' => false, + 'throw_exception_on_invalid_index' => false, + ), + 'property_info' => array( + 'enabled' => false, + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php new file mode 100644 index 0000000..b0c4548 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests; + +class TestBundle extends \Symfony\Component\HttpKernel\Bundle\Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php new file mode 100644 index 0000000..e3532a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets.php @@ -0,0 +1,25 @@ +loadFromExtension('framework', array( + 'assets' => array( + 'version' => 'SomeVersionScheme', + 'base_urls' => 'http://cdn.example.com', + 'version_format' => '%%s?version=%%s', + 'packages' => array( + 'images_path' => array( + 'base_path' => '/foo', + ), + 'images' => array( + 'version' => '1.0.0', + 'base_urls' => array('http://images1.example.com', 'http://images2.example.com'), + ), + 'foo' => array( + 'version' => '1.0.0', + 'version_format' => '%%s-%%s', + ), + 'bar' => array( + 'base_urls' => array('https://bar2.example.com'), + ), + ), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php new file mode 100644 index 0000000..bf12a8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'templating' => array( + 'engines' => array('php', 'twig'), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/csrf.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/csrf.php new file mode 100644 index 0000000..497ceb2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/csrf.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'csrf_protection' => true, + 'form' => true, + 'session' => array( + 'handler_id' => null, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php new file mode 100644 index 0000000..d1df1c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'csrf_protection' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/default_config.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/default_config.php new file mode 100644 index 0000000..cd2e56b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/default_config.php @@ -0,0 +1,3 @@ +loadFromExtension('framework', array()); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php new file mode 100644 index 0000000..7360c49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'form' => array( + 'csrf_protection' => array( + 'enabled' => false, + ), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php new file mode 100644 index 0000000..b6780cf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -0,0 +1,82 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'default_locale' => 'fr', + 'csrf_protection' => true, + 'form' => array( + 'csrf_protection' => array( + 'field_name' => '_csrf', + ), + ), + 'http_method_override' => false, + 'trusted_proxies' => array('127.0.0.1', '10.0.0.1'), + 'esi' => array( + 'enabled' => true, + ), + 'profiler' => array( + 'only_exceptions' => true, + 'enabled' => false, + ), + 'router' => array( + 'resource' => '%kernel.root_dir%/config/routing.xml', + 'type' => 'xml', + ), + 'session' => array( + 'storage_id' => 'session.storage.native', + 'handler_id' => 'session.handler.native_file', + 'name' => '_SYMFONY', + 'cookie_lifetime' => 86400, + 'cookie_path' => '/', + 'cookie_domain' => 'example.com', + 'cookie_secure' => true, + 'cookie_httponly' => false, + 'use_cookies' => true, + 'gc_maxlifetime' => 90000, + 'gc_divisor' => 108, + 'gc_probability' => 1, + 'save_path' => '/path/to/sessions', + ), + 'templating' => array( + 'cache' => '/path/to/cache', + 'engines' => array('php', 'twig'), + 'loader' => array('loader.foo', 'loader.bar'), + 'form' => array( + 'resources' => array('theme1', 'theme2'), + ), + 'hinclude_default_template' => 'global_hinclude_template', + ), + 'assets' => array( + 'version' => 'v1', + ), + 'translator' => array( + 'enabled' => true, + 'fallback' => 'fr', + 'paths' => array('%kernel.root_dir%/Fixtures/translations'), + ), + 'validation' => array( + 'enabled' => true, + 'cache' => 'apc', + ), + 'annotations' => array( + 'cache' => 'file', + 'debug' => true, + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + ), + 'serializer' => array( + 'enabled' => true, + 'enable_annotations' => true, + 'cache' => 'serializer.mapping.cache.apc', + 'name_converter' => 'serializer.name_converter.camel_case_to_snake_case', + ), + 'ide' => 'file%%link%%format', + 'request' => array( + 'formats' => array( + 'csv' => array( + 'text/csv', + 'text/plain', + ), + 'pdf' => 'application/pdf', + ), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/profiler.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/profiler.php new file mode 100644 index 0000000..6615aa7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/profiler.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'profiler' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php new file mode 100644 index 0000000..4340e61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'property_access' => array( + 'magic_call' => true, + 'throw_exception_on_invalid_index' => true, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_info.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_info.php new file mode 100644 index 0000000..847e15f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_info.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'property_info' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/request.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/request.php new file mode 100644 index 0000000..1e7cb29 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/request.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'request' => array( + 'formats' => array(), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php new file mode 100644 index 0000000..dedd090 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'serializer' => array( + 'enabled' => false, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php new file mode 100644 index 0000000..eadad57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'serializer' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/session.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/session.php new file mode 100644 index 0000000..1041837 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/session.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'session' => array( + 'handler_id' => null, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php new file mode 100644 index 0000000..0abe3a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'translator' => array( + 'fallbacks' => array('en', 'fr'), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php new file mode 100644 index 0000000..35a0b3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'validation' => array( + 'enabled' => true, + 'enable_annotations' => true, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php new file mode 100644 index 0000000..476da79 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'validation' => array( + 'enabled' => true, + 'static_method' => array('loadFoo', 'loadBar'), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php new file mode 100644 index 0000000..b428e06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'validation' => array( + 'enabled' => true, + 'static_method' => false, + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml new file mode 100644 index 0000000..d4e682c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml @@ -0,0 +1,2 @@ +custom: + paths: test diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml new file mode 100644 index 0000000..e39e15f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets.xml @@ -0,0 +1,23 @@ + + + + + + + http://cdn.example.com + + + http://images1.example.com + http://images2.example.com + + + + https://bar2.example.com + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml new file mode 100644 index 0000000..d579ed4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml @@ -0,0 +1,14 @@ + + + + + + php + twig + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml new file mode 100644 index 0000000..586d741 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml new file mode 100644 index 0000000..635557f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml new file mode 100644 index 0000000..0a08d23 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml new file mode 100644 index 0000000..af727ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml new file mode 100644 index 0000000..589ec78 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml new file mode 100644 index 0000000..c896acb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml new file mode 100644 index 0000000..fdeb60a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml new file mode 100644 index 0000000..a7a42d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + text/csv + text/plain + + + application/pdf + + + + loader.foo + loader.bar + php + twig + + theme1 + theme2 + + + + + %kernel.root_dir%/Fixtures/translations + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml new file mode 100644 index 0000000..f3b3095 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml new file mode 100644 index 0000000..d7db78a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml new file mode 100644 index 0000000..825bc46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/request.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/request.xml new file mode 100644 index 0000000..320d1b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/request.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml new file mode 100644 index 0000000..73f1dcc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml new file mode 100644 index 0000000..e202fc1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/session.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/session.xml new file mode 100644 index 0000000..118a08c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/session.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml new file mode 100644 index 0000000..a0e4f4e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml @@ -0,0 +1,15 @@ + + + + + + + en + fr + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml new file mode 100644 index 0000000..22f1536 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml new file mode 100644 index 0000000..053f574 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml @@ -0,0 +1,15 @@ + + + + + + + loadFoo + loadBar + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml new file mode 100644 index 0000000..d26c7a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml new file mode 100644 index 0000000..193dc59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets.yml @@ -0,0 +1,16 @@ +framework: + assets: + version: SomeVersionScheme + version_format: %%s?version=%%s + base_urls: http://cdn.example.com + packages: + images_path: + base_path: '/foo' + images: + version: 1.0.0 + base_urls: ["http://images1.example.com", "http://images2.example.com"] + foo: + version: 1.0.0 + version_format: %%s-%%s + bar: + base_urls: ["https://bar2.example.com"] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml new file mode 100644 index 0000000..66ffdf7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml @@ -0,0 +1,3 @@ +framework: + templating: + engines: [php, twig] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml new file mode 100644 index 0000000..dbdd495 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + csrf_protection: ~ + form: ~ + session: ~ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml new file mode 100644 index 0000000..b8065b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml @@ -0,0 +1,2 @@ +framework: + csrf_protection: ~ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml new file mode 100644 index 0000000..00874fb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml @@ -0,0 +1 @@ +framework: ~ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml new file mode 100644 index 0000000..e3ac7e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml @@ -0,0 +1,4 @@ +framework: + form: + csrf_protection: + enabled: false diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml new file mode 100644 index 0000000..572c5be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -0,0 +1,61 @@ +framework: + secret: s3cr3t + default_locale: fr + csrf_protection: true + form: + csrf_protection: + field_name: _csrf + http_method_override: false + trusted_proxies: ['127.0.0.1', '10.0.0.1'] + esi: + enabled: true + profiler: + only_exceptions: true + enabled: false + router: + resource: %kernel.root_dir%/config/routing.xml + type: xml + session: + storage_id: session.storage.native + handler_id: session.handler.native_file + name: _SYMFONY + cookie_lifetime: 86400 + cookie_path: / + cookie_domain: example.com + cookie_secure: true + cookie_httponly: false + use_cookies: true + gc_probability: 1 + gc_divisor: 108 + gc_maxlifetime: 90000 + save_path: /path/to/sessions + templating: + engines: [php, twig] + loader: [loader.foo, loader.bar] + cache: /path/to/cache + form: + resources: [theme1, theme2] + hinclude_default_template: global_hinclude_template + assets: + version: v1 + translator: + enabled: true + fallback: fr + paths: ['%kernel.root_dir%/Fixtures/translations'] + validation: + enabled: true + cache: apc + annotations: + cache: file + debug: true + file_cache_dir: %kernel.cache_dir%/annotations + serializer: + enabled: true + enable_annotations: true + cache: serializer.mapping.cache.apc + name_converter: serializer.name_converter.camel_case_to_snake_case + ide: file%%link%%format + request: + formats: + csv: ['text/csv', 'text/plain'] + pdf: 'application/pdf' diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml new file mode 100644 index 0000000..9052a2b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml @@ -0,0 +1,3 @@ +framework: + profiler: + enabled: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml new file mode 100644 index 0000000..b5fd271 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml @@ -0,0 +1,4 @@ +framework: + property_access: + magic_call: true + throw_exception_on_invalid_index: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml new file mode 100644 index 0000000..fbdf7a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml @@ -0,0 +1,3 @@ +framework: + property_info: + enabled: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/request.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/request.yml new file mode 100644 index 0000000..9beae1d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/request.yml @@ -0,0 +1,3 @@ +framework: + request: + formats: ~ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml new file mode 100644 index 0000000..330e19a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml @@ -0,0 +1,3 @@ +framework: + serializer: + enabled: false diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml new file mode 100644 index 0000000..40a1ff7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml @@ -0,0 +1,3 @@ +framework: + serializer: + enabled: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/session.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/session.yml new file mode 100644 index 0000000..d91b0c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/session.yml @@ -0,0 +1,3 @@ +framework: + session: + handler_id: null diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml new file mode 100644 index 0000000..271d781 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml @@ -0,0 +1,3 @@ +framework: + translator: + fallbacks: [en, fr] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml new file mode 100644 index 0000000..41f1796 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + validation: + enabled: true + enable_annotations: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml new file mode 100644 index 0000000..6ca3433 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + validation: + enabled: true + static_method: [loadFoo, loadBar] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml new file mode 100644 index 0000000..ca52149 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + validation: + enabled: true + static_method: false diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php new file mode 100644 index 0000000..59bdbca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -0,0 +1,535 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Validator\Validation; + +abstract class FrameworkExtensionTest extends TestCase +{ + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testFormCsrfProtection() + { + $container = $this->createContainerFromFile('full'); + + $def = $container->getDefinition('form.type_extension.csrf'); + + $this->assertTrue($container->getParameter('form.type_extension.csrf.enabled')); + $this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(1)); + $this->assertEquals('_csrf', $container->getParameter('form.type_extension.csrf.field_name')); + $this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(2)); + } + + public function testPropertyAccessWithDefaultValue() + { + $container = $this->createContainerFromFile('full'); + + $def = $container->getDefinition('property_accessor'); + $this->assertFalse($def->getArgument(0)); + $this->assertFalse($def->getArgument(1)); + } + + public function testPropertyAccessWithOverriddenValues() + { + $container = $this->createContainerFromFile('property_accessor'); + $def = $container->getDefinition('property_accessor'); + $this->assertTrue($def->getArgument(0)); + $this->assertTrue($def->getArgument(1)); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage CSRF protection needs sessions to be enabled. + */ + public function testCsrfProtectionNeedsSessionToBeEnabled() + { + $this->createContainerFromFile('csrf_needs_session'); + } + + public function testCsrfProtectionForFormsEnablesCsrfProtectionAutomatically() + { + $container = $this->createContainerFromFile('csrf'); + + $this->assertTrue($container->hasDefinition('security.csrf.token_manager')); + } + + public function testProxies() + { + $container = $this->createContainerFromFile('full'); + + $this->assertEquals(array('127.0.0.1', '10.0.0.1'), $container->getParameter('kernel.trusted_proxies')); + } + + public function testHttpMethodOverride() + { + $container = $this->createContainerFromFile('full'); + + $this->assertFalse($container->getParameter('kernel.http_method_override')); + } + + public function testEsi() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('esi'), '->registerEsiConfiguration() loads esi.xml'); + } + + public function testEnabledProfiler() + { + $container = $this->createContainerFromFile('profiler'); + + $this->assertTrue($container->hasDefinition('profiler'), '->registerProfilerConfiguration() loads profiling.xml'); + $this->assertTrue($container->hasDefinition('data_collector.config'), '->registerProfilerConfiguration() loads collectors.xml'); + } + + public function testDisabledProfiler() + { + $container = $this->createContainerFromFile('full'); + + $this->assertFalse($container->hasDefinition('profiler'), '->registerProfilerConfiguration() does not load profiling.xml'); + $this->assertFalse($container->hasDefinition('data_collector.config'), '->registerProfilerConfiguration() does not load collectors.xml'); + } + + public function testRouter() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->has('router'), '->registerRouterConfiguration() loads routing.xml'); + $arguments = $container->findDefinition('router')->getArguments(); + $this->assertEquals($container->getParameter('kernel.root_dir').'/config/routing.xml', $container->getParameter('router.resource'), '->registerRouterConfiguration() sets routing resource'); + $this->assertEquals('%router.resource%', $arguments[1], '->registerRouterConfiguration() sets routing resource'); + $this->assertEquals('xml', $arguments[2]['resource_type'], '->registerRouterConfiguration() sets routing resource type'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testRouterRequiresResourceOption() + { + $container = $this->createContainer(); + $loader = new FrameworkExtension(); + $loader->load(array(array('router' => true)), $container); + } + + public function testSession() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml'); + $this->assertEquals('fr', $container->getParameter('kernel.default_locale')); + $this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage')); + $this->assertEquals('session.handler.native_file', (string) $container->getAlias('session.handler')); + + $options = $container->getParameter('session.storage.options'); + $this->assertEquals('_SYMFONY', $options['name']); + $this->assertEquals(86400, $options['cookie_lifetime']); + $this->assertEquals('/', $options['cookie_path']); + $this->assertEquals('example.com', $options['cookie_domain']); + $this->assertTrue($options['cookie_secure']); + $this->assertFalse($options['cookie_httponly']); + $this->assertTrue($options['use_cookies']); + $this->assertEquals(108, $options['gc_divisor']); + $this->assertEquals(1, $options['gc_probability']); + $this->assertEquals(90000, $options['gc_maxlifetime']); + + $this->assertEquals('/path/to/sessions', $container->getParameter('session.save_path')); + } + + public function testNullSessionHandler() + { + $container = $this->createContainerFromFile('session'); + + $this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml'); + $this->assertNull($container->getDefinition('session.storage.native')->getArgument(1)); + $this->assertNull($container->getDefinition('session.storage.php_bridge')->getArgument(0)); + } + + public function testRequest() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('request.add_request_formats_listener'), '->registerRequestConfiguration() loads request.xml'); + $listenerDef = $container->getDefinition('request.add_request_formats_listener'); + $this->assertEquals(array('csv' => array('text/csv', 'text/plain'), 'pdf' => array('application/pdf')), $listenerDef->getArgument(0)); + } + + public function testEmptyRequestFormats() + { + $container = $this->createContainerFromFile('request'); + + $this->assertFalse($container->hasDefinition('request.add_request_formats_listener'), '->registerRequestConfiguration() does not load request.xml when no request formats are defined'); + } + + public function testTemplating() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('templating.name_parser'), '->registerTemplatingConfiguration() loads templating.xml'); + + $this->assertEquals('templating.engine.delegating', (string) $container->getAlias('templating'), '->registerTemplatingConfiguration() configures delegating loader if multiple engines are provided'); + + $this->assertEquals($container->getDefinition('templating.loader.chain'), $container->getDefinition('templating.loader.wrapped'), '->registerTemplatingConfiguration() configures loader chain if multiple loaders are provided'); + + $this->assertEquals($container->getDefinition('templating.loader'), $container->getDefinition('templating.loader.cache'), '->registerTemplatingConfiguration() configures the loader to use cache'); + + $this->assertEquals('%templating.loader.cache.path%', $container->getDefinition('templating.loader.cache')->getArgument(1)); + $this->assertEquals('/path/to/cache', $container->getParameter('templating.loader.cache.path')); + + $this->assertEquals(array('php', 'twig'), $container->getParameter('templating.engines'), '->registerTemplatingConfiguration() sets a templating.engines parameter'); + + $this->assertEquals(array('FrameworkBundle:Form', 'theme1', 'theme2'), $container->getParameter('templating.helper.form.resources'), '->registerTemplatingConfiguration() registers the theme and adds the base theme'); + $this->assertEquals('global_hinclude_template', $container->getParameter('fragment.renderer.hinclude.global_template'), '->registerTemplatingConfiguration() registers the global hinclude.js template'); + } + + public function testAssets() + { + $container = $this->createContainerFromFile('assets'); + $packages = $container->getDefinition('assets.packages'); + + // default package + $defaultPackage = $container->getDefinition($packages->getArgument(0)); + $this->assertUrlPackage($container, $defaultPackage, array('http://cdn.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); + + // packages + $packages = $packages->getArgument(1); + $this->assertCount(4, $packages); + + $package = $container->getDefinition($packages['images_path']); + $this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s'); + + $package = $container->getDefinition($packages['images']); + $this->assertUrlPackage($container, $package, array('http://images1.example.com', 'http://images2.example.com'), '1.0.0', '%%s?version=%%s'); + + $package = $container->getDefinition($packages['foo']); + $this->assertPathPackage($container, $package, '', '1.0.0', '%%s-%%s'); + + $package = $container->getDefinition($packages['bar']); + $this->assertUrlPackage($container, $package, array('https://bar2.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); + } + + public function testTranslator() + { + $container = $this->createContainerFromFile('full'); + $this->assertTrue($container->hasDefinition('translator.default'), '->registerTranslatorConfiguration() loads translation.xml'); + $this->assertEquals('translator.default', (string) $container->getAlias('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator'); + $options = $container->getDefinition('translator.default')->getArgument(3); + + $files = array_map(function ($resource) { return realpath($resource); }, $options['resource_files']['en']); + $ref = new \ReflectionClass('Symfony\Component\Validator\Validation'); + $this->assertContains( + strtr(dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Validator translation resources' + ); + $ref = new \ReflectionClass('Symfony\Component\Form\Form'); + $this->assertContains( + strtr(dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Form translation resources' + ); + $ref = new \ReflectionClass('Symfony\Component\Security\Core\Security'); + $this->assertContains( + strtr(dirname($ref->getFileName()).'/Resources/translations/security.en.xlf', '/', DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Security translation resources' + ); + $this->assertContains( + strtr(__DIR__.'/Fixtures/translations/test_paths.en.yml', '/', DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds translation resources in custom paths' + ); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + $this->assertEquals(array('fr'), $calls[1][1][0]); + } + + public function testTranslatorMultipleFallbacks() + { + $container = $this->createContainerFromFile('translator_fallbacks'); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + $this->assertEquals(array('en', 'fr'), $calls[1][1][0]); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testTemplatingRequiresAtLeastOneEngine() + { + $container = $this->createContainer(); + $loader = new FrameworkExtension(); + $loader->load(array(array('templating' => null)), $container); + } + + public function testValidation() + { + $container = $this->createContainerFromFile('full'); + + $ref = new \ReflectionClass('Symfony\Component\Form\Form'); + $xmlMappings = array(dirname($ref->getFileName()).'/Resources/config/validation.xml'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertCount(6, $calls); + $this->assertSame('setConstraintValidatorFactory', $calls[0][0]); + $this->assertEquals(array(new Reference('validator.validator_factory')), $calls[0][1]); + $this->assertSame('setTranslator', $calls[1][0]); + $this->assertEquals(array(new Reference('translator')), $calls[1][1]); + $this->assertSame('setTranslationDomain', $calls[2][0]); + $this->assertSame(array('%validator.translation_domain%'), $calls[2][1]); + $this->assertSame('addXmlMappings', $calls[3][0]); + $this->assertSame(array($xmlMappings), $calls[3][1]); + $this->assertSame('addMethodMapping', $calls[4][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[4][1]); + $this->assertSame('setMetadataCache', $calls[5][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.apc')), $calls[5][1]); + } + + public function testValidationService() + { + $container = $this->createContainerFromFile('validation_annotations'); + + $this->assertInstanceOf('Symfony\Component\Validator\Validator\ValidatorInterface', $container->get('validator')); + } + + public function testAnnotations() + { + $container = $this->createContainerFromFile('full'); + + $this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.filesystem_cache')->getArgument(0)); + $this->assertSame('annotations.cached_reader', (string) $container->getAlias('annotation_reader')); + $this->assertSame('annotations.filesystem_cache', (string) $container->getDefinition('annotations.cached_reader')->getArgument(1)); + } + + public function testFileLinkFormat() + { + $container = $this->createContainerFromFile('full'); + + $this->assertEquals('file%link%format', $container->getParameter('templating.helper.code.file_link_format')); + } + + public function testValidationAnnotations() + { + $container = $this->createContainerFromFile('validation_annotations'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertCount(6, $calls); + $this->assertSame('enableAnnotationMapping', $calls[4][0]); + $this->assertEquals(array(new Reference('annotation_reader')), $calls[4][1]); + $this->assertSame('addMethodMapping', $calls[5][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[5][1]); + // no cache this time + } + + public function testValidationPaths() + { + require_once __DIR__.'/Fixtures/TestBundle/TestBundle.php'; + + $container = $this->createContainerFromFile('validation_annotations', array( + 'kernel.bundles' => array('TestBundle' => 'Symfony\Bundle\FrameworkBundle\Tests\TestBundle'), + )); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertCount(7, $calls); + $this->assertSame('addXmlMappings', $calls[3][0]); + $this->assertSame('addYamlMappings', $calls[4][0]); + $this->assertSame('enableAnnotationMapping', $calls[5][0]); + $this->assertSame('addMethodMapping', $calls[6][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[6][1]); + + $xmlMappings = $calls[3][1][0]; + $this->assertCount(2, $xmlMappings); + try { + // Testing symfony/symfony + $this->assertStringEndsWith('Component'.DIRECTORY_SEPARATOR.'Form/Resources/config/validation.xml', $xmlMappings[0]); + } catch (\Exception $e) { + // Testing symfony/framework-bundle with deps=high + $this->assertStringEndsWith('symfony'.DIRECTORY_SEPARATOR.'form/Resources/config/validation.xml', $xmlMappings[0]); + } + $this->assertStringEndsWith('TestBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'validation.xml', $xmlMappings[1]); + + $yamlMappings = $calls[4][1][0]; + $this->assertCount(1, $yamlMappings); + $this->assertStringEndsWith('TestBundle'.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'validation.yml', $yamlMappings[0]); + } + + public function testValidationNoStaticMethod() + { + $container = $this->createContainerFromFile('validation_no_static_method'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertCount(4, $calls); + $this->assertSame('addXmlMappings', $calls[3][0]); + // no cache, no annotations, no static methods + } + + public function testFormsCanBeEnabledWithoutCsrfProtection() + { + $container = $this->createContainerFromFile('form_no_csrf'); + + $this->assertFalse($container->getParameter('form.type_extension.csrf.enabled')); + } + + public function testStopwatchEnabledWithDebugModeEnabled() + { + $container = $this->createContainerFromFile('default_config', array( + 'kernel.container_class' => 'foo', + 'kernel.debug' => true, + )); + + $this->assertTrue($container->has('debug.stopwatch')); + } + + public function testStopwatchEnabledWithDebugModeDisabled() + { + $container = $this->createContainerFromFile('default_config', array( + 'kernel.container_class' => 'foo', + )); + + $this->assertTrue($container->has('debug.stopwatch')); + } + + public function testSerializerDisabled() + { + $container = $this->createContainerFromFile('default_config'); + $this->assertFalse($container->has('serializer')); + } + + public function testSerializerEnabled() + { + $container = $this->createContainerFromFile('full'); + $this->assertTrue($container->has('serializer')); + + $argument = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0); + + $this->assertCount(1, $argument); + $this->assertEquals('Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', $argument[0]->getClass()); + $this->assertEquals(new Reference('serializer.mapping.cache.apc'), $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1)); + $this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.normalizer.object')->getArgument(1)); + } + + public function testAssetHelperWhenAssetsAreEnabled() + { + $container = $this->createContainerFromFile('full'); + $packages = $container->getDefinition('templating.helper.assets')->getArgument(0); + + $this->assertSame('assets.packages', (string) $packages); + } + + public function testAssetHelperWhenTemplatesAreEnabledAndAssetsAreDisabled() + { + $container = $this->createContainerFromFile('assets_disabled'); + + $this->assertFalse($container->hasDefinition('templating.helper.assets')); + } + + public function testSerializerServiceIsRegisteredWhenEnabled() + { + $container = $this->createContainerFromFile('serializer_enabled'); + + $this->assertTrue($container->hasDefinition('serializer')); + } + + public function testSerializerServiceIsNotRegisteredWhenDisabled() + { + $container = $this->createContainerFromFile('serializer_disabled'); + + $this->assertFalse($container->hasDefinition('serializer')); + } + + public function testPropertyInfoDisabled() + { + $container = $this->createContainerFromFile('default_config'); + $this->assertFalse($container->has('property_info')); + } + + public function testPropertyInfoEnabled() + { + $container = $this->createContainerFromFile('property_info'); + $this->assertTrue($container->has('property_info')); + } + + protected function createContainer(array $data = array()) + { + return new ContainerBuilder(new ParameterBag(array_merge(array( + 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), + 'kernel.cache_dir' => __DIR__, + 'kernel.debug' => false, + 'kernel.environment' => 'test', + 'kernel.name' => 'kernel', + 'kernel.root_dir' => __DIR__, + ), $data))); + } + + protected function createContainerFromFile($file, $data = array()) + { + $container = $this->createContainer($data); + $container->registerExtension(new FrameworkExtension()); + $this->loadFromFile($container, $file); + + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + + return $container; + } + + protected function createContainerFromClosure($closure, $data = array()) + { + $container = $this->createContainer($data); + $container->registerExtension(new FrameworkExtension()); + $loader = new ClosureLoader($container); + $loader->load($closure); + + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + + return $container; + } + + private function assertPathPackage(ContainerBuilder $container, DefinitionDecorator $package, $basePath, $version, $format) + { + $this->assertEquals('assets.path_package', $package->getParent()); + $this->assertEquals($basePath, $package->getArgument(0)); + $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); + } + + private function assertUrlPackage(ContainerBuilder $container, DefinitionDecorator $package, $baseUrls, $version, $format) + { + $this->assertEquals('assets.url_package', $package->getParent()); + $this->assertEquals($baseUrls, $package->getArgument(0)); + $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); + } + + private function assertVersionStrategy(ContainerBuilder $container, Reference $reference, $version, $format) + { + $versionStrategy = $container->getDefinition($reference); + if (null === $version) { + $this->assertEquals('assets.empty_version_strategy', (string) $reference); + } else { + $this->assertEquals('assets.static_version_strategy', $versionStrategy->getParent()); + $this->assertEquals($version, $versionStrategy->getArgument(0)); + $this->assertEquals($format, $versionStrategy->getArgument(1)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php new file mode 100644 index 0000000..01c7393 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Config\FileLocator; + +class PhpFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/Fixtures/php')); + $loader->load($file.'.php'); + } + + /** + * @expectedException \LogicException + */ + public function testAssetsCannotHavePathAndUrl() + { + $container = $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', array( + 'assets' => array( + 'base_urls' => 'http://cdn.example.com', + 'base_path' => '/foo', + ), + )); + }); + } + + /** + * @expectedException \LogicException + */ + public function testAssetPackageCannotHavePathAndUrl() + { + $container = $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', array( + 'assets' => array( + 'packages' => array( + 'impossible' => array( + 'base_urls' => 'http://cdn.example.com', + 'base_path' => '/foo', + ), + ), + ), + )); + }); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php new file mode 100644 index 0000000..2c27eb0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\Config\FileLocator; + +class XmlFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')); + $loader->load($file.'.xml'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php new file mode 100644 index 0000000..43070c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\Config\FileLocator; + +class YamlFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml')); + $loader->load($file.'.yml'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/BaseBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/BaseBundle.php new file mode 100644 index 0000000..494a18d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/BaseBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BaseBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class BaseBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/Resources/views/base.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/Resources/views/base.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/Resources/views/controller/base.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/Resources/views/controller/base.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/Resources/views/this.is.a.template.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/BaseBundle/Resources/views/this.is.a.template.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.json new file mode 100644 index 0000000..283ed13 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.json @@ -0,0 +1,4 @@ +{ + "service": "service_1", + "public": true +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.md new file mode 100644 index 0000000..3220d5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.md @@ -0,0 +1,2 @@ +- Service: `service_1` +- Public: yes \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.txt new file mode 100644 index 0000000..c47e8b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.txt @@ -0,0 +1,3 @@ + + // This service is an alias for the service service_1 + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.xml new file mode 100644 index 0000000..759f8a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_1.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.json new file mode 100644 index 0000000..6998dae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.json @@ -0,0 +1,4 @@ +{ + "service": "service_2", + "public": false +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.md new file mode 100644 index 0000000..73a4101 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.md @@ -0,0 +1,2 @@ +- Service: `service_2` +- Public: no \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.txt new file mode 100644 index 0000000..41654e6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.txt @@ -0,0 +1,3 @@ + + // This service is an alias for the service service_2 + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.xml new file mode 100644 index 0000000..847050b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/alias_2.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.json new file mode 100644 index 0000000..cb68091 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.json @@ -0,0 +1,3 @@ +{ + "twig.form.resources": ["bootstrap_3_horizontal_layout.html.twig", "bootstrap_3_layout.html.twig", "form_div_layout.html.twig", "form_table_layout.html.twig"] +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.md new file mode 100644 index 0000000..593be0c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.md @@ -0,0 +1,4 @@ +twig.form.resources +=================== + +["bootstrap_3_horizontal_layout.html.twig","bootstrap_3_layo... diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.txt new file mode 100644 index 0000000..6ba6c9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.txt @@ -0,0 +1,5 @@ + --------------------- ----------------------------------------------------------------- + Parameter Value + --------------------- ----------------------------------------------------------------- + twig.form.resources ["bootstrap_3_horizontal_layout.html.twig","bootstrap_3_layo... + --------------------- ----------------------------------------------------------------- diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.xml new file mode 100644 index 0000000..0e16f57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/array_parameter.xml @@ -0,0 +1,2 @@ + +["bootstrap_3_horizontal_layout.html.twig","bootstrap_3_layo... diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json new file mode 100644 index 0000000..170dfa2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.json @@ -0,0 +1,33 @@ +{ + "definitions": { + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [ + + ], + "autowire": false, + "autowiring_types": [] + } + }, + "aliases": { + "alias_1": { + "service": "service_1", + "public": true + }, + "alias_2": { + "service": "service_2", + "public": false + } + }, + "services": { + "service_container": "Symfony\\Component\\DependencyInjection\\ContainerBuilder" + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md new file mode 100644 index 0000000..2799586 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.md @@ -0,0 +1,40 @@ +Public services +=============== + +Definitions +----------- + +definition_1 +~~~~~~~~~~~~ + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` + + +Aliases +------- + +alias_1 +~~~~~~~ + +- Service: `service_1` +- Public: yes + +alias_2 +~~~~~~~ + +- Service: `service_2` +- Public: no + + +Services +-------- + +- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.txt new file mode 100644 index 0000000..e2f344e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.txt @@ -0,0 +1,13 @@ + +Symfony Container Public Services +================================= + + ------------------- -------------------------------------------------------- + Service ID Class name + ------------------- -------------------------------------------------------- + alias_1 alias for "service_1" + alias_2 alias for "service_2" + definition_1 Full\Qualified\Class1 + service_container Symfony\Component\DependencyInjection\ContainerBuilder + ------------------- -------------------------------------------------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml new file mode 100644 index 0000000..52031e5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_public.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json new file mode 100644 index 0000000..f5b6b4a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.json @@ -0,0 +1,67 @@ +{ + "definitions": { + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [ + + ], + "autowire": false, + "autowiring_types": [] + }, + "definition_2": { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [ + + ] + } + ], + "autowire": false, + "autowiring_types": [] + } + }, + "aliases": { + "alias_1": { + "service": "service_1", + "public": true + }, + "alias_2": { + "service": "service_2", + "public": false + } + }, + "services": { + "service_container": "Symfony\\Component\\DependencyInjection\\ContainerBuilder" + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md new file mode 100644 index 0000000..c1ebd9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.md @@ -0,0 +1,60 @@ +Public and private services +=========================== + +Definitions +----------- + +definition_1 +~~~~~~~~~~~~ + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` + +definition_2 +~~~~~~~~~~~~ + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` + + +Aliases +------- + +alias_1 +~~~~~~~ + +- Service: `service_1` +- Public: yes + +alias_2 +~~~~~~~ + +- Service: `service_2` +- Public: no + + +Services +-------- + +- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt new file mode 100644 index 0000000..ce0e034 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.txt @@ -0,0 +1,14 @@ + +Symfony Container Public and Private Services +============================================= + + ------------------- -------------------------------------------------------- + Service ID Class name + ------------------- -------------------------------------------------------- + alias_1 alias for "service_1" + alias_2 alias for "service_2" + definition_1 Full\Qualified\Class1 + definition_2 Full\Qualified\Class2 + service_container Symfony\Component\DependencyInjection\ContainerBuilder + ------------------- -------------------------------------------------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml new file mode 100644 index 0000000..464b838 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_services.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + val1 + val2 + + + val3 + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json new file mode 100644 index 0000000..5a3c527 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.json @@ -0,0 +1,44 @@ +{ + "definitions": { + "definition_2": { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [ + + ] + } + ], + "autowire": false, + "autowiring_types": [] + } + }, + "aliases": [ + + ], + "services": [ + + ] +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md new file mode 100644 index 0000000..391f426 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.md @@ -0,0 +1,25 @@ +Public and private services with tag `tag1` +=========================================== + +Definitions +----------- + +definition_2 +~~~~~~~~~~~~ + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt new file mode 100644 index 0000000..9940309 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt @@ -0,0 +1,11 @@ + +Symfony Container Public and Private Services Tagged with "tag1" Tag +==================================================================== + + -------------- ------- ------- ------- ----------------------- + Service ID attr1 attr2 attr3 Class name + -------------- ------- ------- ------- ----------------------- + definition_2 val1 val2 Full\Qualified\Class2 + " val3 + -------------- ------- ------- ------- ----------------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml new file mode 100644 index 0000000..dc67b7c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml @@ -0,0 +1,16 @@ + + + + + + + val1 + val2 + + + val3 + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json new file mode 100644 index 0000000..4f1386b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.json @@ -0,0 +1,32 @@ +{ + "tag1": [ + { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "autowire": false, + "autowiring_types": [] + } + ], + "tag2": [ + { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "autowire": false, + "autowiring_types": [] + } + ] +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md new file mode 100644 index 0000000..640076f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.md @@ -0,0 +1,37 @@ +Container tags +============== + +tag1 +---- + +definition_2 +~~~~~~~~~~~~ + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` + + +tag2 +---- + +definition_2 +~~~~~~~~~~~~ + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.txt new file mode 100644 index 0000000..45523dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.txt @@ -0,0 +1,14 @@ + +Symfony Container Public and Private Tags +========================================= + +"tag1" tag +---------- + + * definition_2 + +"tag2" tag +---------- + + * definition_2 + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml new file mode 100644 index 0000000..863bb33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/builder_1_tags.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.json new file mode 100644 index 0000000..10eccf3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.json @@ -0,0 +1,4 @@ +{ + "type": "function", + "name": "array_key_exists" +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.md new file mode 100644 index 0000000..9cf3135 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.md @@ -0,0 +1,3 @@ + +- Type: `function` +- Name: `array_key_exists` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.txt new file mode 100644 index 0000000..09901c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.txt @@ -0,0 +1 @@ +array_key_exists() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.xml new file mode 100644 index 0000000..28d100d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_1.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.json new file mode 100644 index 0000000..6397254 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.json @@ -0,0 +1,6 @@ +{ + "type": "function", + "name": "staticMethod", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "static": true +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.md new file mode 100644 index 0000000..c041ee8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.md @@ -0,0 +1,5 @@ + +- Type: `function` +- Name: `staticMethod` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Static: yes diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.txt new file mode 100644 index 0000000..5e3101e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::staticMethod() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.xml new file mode 100644 index 0000000..df49310 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_2.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.json new file mode 100644 index 0000000..f77f13a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.json @@ -0,0 +1,5 @@ +{ + "type": "function", + "name": "method", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass" +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.md new file mode 100644 index 0000000..3b61c0d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.md @@ -0,0 +1,4 @@ + +- Type: `function` +- Name: `method` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.txt new file mode 100644 index 0000000..dde0dbb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::method() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.xml new file mode 100644 index 0000000..367e8b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_3.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.json new file mode 100644 index 0000000..6397254 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.json @@ -0,0 +1,6 @@ +{ + "type": "function", + "name": "staticMethod", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "static": true +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.md new file mode 100644 index 0000000..c041ee8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.md @@ -0,0 +1,5 @@ + +- Type: `function` +- Name: `staticMethod` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Static: yes diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.txt new file mode 100644 index 0000000..5e3101e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::staticMethod() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.xml new file mode 100644 index 0000000..df49310 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_4.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.json new file mode 100644 index 0000000..249b59f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.json @@ -0,0 +1,7 @@ +{ + "type": "function", + "name": "staticMethod", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\ExtendedCallableClass", + "static": true, + "parent": true +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.md new file mode 100644 index 0000000..fc69e9b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.md @@ -0,0 +1,6 @@ + +- Type: `function` +- Name: `staticMethod` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass` +- Static: yes +- Parent: yes diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.txt new file mode 100644 index 0000000..1c06a7e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass::parent::staticMethod() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.xml new file mode 100644 index 0000000..05fbb3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_5.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.json new file mode 100644 index 0000000..1798fdd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.json @@ -0,0 +1,3 @@ +{ + "type": "closure" +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.md new file mode 100644 index 0000000..474a9dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.md @@ -0,0 +1,2 @@ + +- Type: `closure` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.txt new file mode 100644 index 0000000..9b030ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.txt @@ -0,0 +1 @@ +\Closure() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.xml new file mode 100644 index 0000000..b0f2ab5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_6.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.json new file mode 100644 index 0000000..0d4d1e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass" +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.md new file mode 100644 index 0000000..c9ad7af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.md @@ -0,0 +1,3 @@ + +- Type: `object` +- Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.txt new file mode 100644 index 0000000..78ef6a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.xml new file mode 100644 index 0000000..7a5d812 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/callable_7.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json new file mode 100644 index 0000000..d11d6e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.json @@ -0,0 +1,16 @@ +{ + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [ + + ], + "autowire": false, + "autowiring_types": [] +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md new file mode 100644 index 0000000..b1d46b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.md @@ -0,0 +1,9 @@ +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt new file mode 100644 index 0000000..86f2c00 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.txt @@ -0,0 +1,17 @@ + ------------------ ----------------------------- + Option Value + ------------------ ----------------------------- + Service ID - + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Autowiring Types - + Factory Class Full\Qualified\FactoryClass + Factory Method get + ------------------ ----------------------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml new file mode 100644 index 0000000..67d1029 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_1.xml @@ -0,0 +1,4 @@ + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json new file mode 100644 index 0000000..6c4207e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.json @@ -0,0 +1,34 @@ +{ + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [ + + ] + } + ], + "autowire": false, + "autowiring_types": [] +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md new file mode 100644 index 0000000..c5a5030 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.md @@ -0,0 +1,16 @@ +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt new file mode 100644 index 0000000..148ca1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.txt @@ -0,0 +1,18 @@ + ------------------ ------------------------------------------------------- + Option Value + ------------------ ------------------------------------------------------- + Service ID - + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)tag1 (attr3: val3)tag2 + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Autowiring Types - + Required File /path/to/file + Factory Service factory.service + Factory Method get + ------------------ ------------------------------------------------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml new file mode 100644 index 0000000..f0b2374 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_2.xml @@ -0,0 +1,14 @@ + + + + + + val1 + val2 + + + val3 + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json new file mode 100644 index 0000000..4b68f0c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json @@ -0,0 +1,11 @@ +[ + { + "type": "function", + "name": "global_function", + "priority": 255 + }, + { + "type": "closure", + "priority": -1 + } +] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md new file mode 100644 index 0000000..98b81ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md @@ -0,0 +1,12 @@ +# Registered listeners for event `event1` ordered by descending priority + +## Listener 1 + +- Type: `function` +- Name: `global_function` +- Priority: `255` + +## Listener 2 + +- Type: `closure` +- Priority: `-1` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt new file mode 100644 index 0000000..c64b97a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt @@ -0,0 +1,11 @@ + +Registered Listeners for "event1" Event +======================================= + + ------- ------------------- ---------- + Order Callable Priority + ------- ------------------- ---------- + #1 global_function() 255 + #2 \Closure() -1 + ------- ------------------- ---------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml new file mode 100644 index 0000000..bc03189 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json new file mode 100644 index 0000000..30772d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json @@ -0,0 +1,20 @@ +{ + "event1": [ + { + "type": "function", + "name": "global_function", + "priority": 255 + }, + { + "type": "closure", + "priority": -1 + } + ], + "event2": [ + { + "type": "object", + "name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "priority": 0 + } + ] +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md new file mode 100644 index 0000000..eb80978 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md @@ -0,0 +1,22 @@ +# Registered listeners + +## event1 + +### Listener 1 + +- Type: `function` +- Name: `global_function` +- Priority: `255` + +### Listener 2 + +- Type: `closure` +- Priority: `-1` + +## event2 + +### Listener 1 + +- Type: `object` +- Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Priority: `0` diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt new file mode 100644 index 0000000..884c1ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt @@ -0,0 +1,23 @@ + +Registered Listeners Grouped by Event +===================================== + +"event1" event +-------------- + + ------- ------------------- ---------- + Order Callable Priority + ------- ------------------- ---------- + #1 global_function() 255 + #2 \Closure() -1 + ------- ------------------- ---------- + +"event2" event +-------------- + + ------- ----------------------------------------------------------------------------------- ---------- + Order Callable Priority + ------- ----------------------------------------------------------------------------------- ---------- + #1 Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() 0 + ------- ----------------------------------------------------------------------------------- ---------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml new file mode 100644 index 0000000..d7443f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.json new file mode 100644 index 0000000..069fdbc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.json @@ -0,0 +1,3 @@ +{ + "database_name": "symfony" +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.md new file mode 100644 index 0000000..239e98d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.md @@ -0,0 +1,4 @@ +database_name +============= + +symfony \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.txt new file mode 100644 index 0000000..ad0b605 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.txt @@ -0,0 +1,6 @@ + --------------- --------- + Parameter Value + --------------- --------- + database_name symfony + --------------- --------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.xml new file mode 100644 index 0000000..8465522 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameter.xml @@ -0,0 +1,2 @@ + +symfony diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.json new file mode 100644 index 0000000..686cbc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.json @@ -0,0 +1,10 @@ +{ + "array": [ + 12, + "Hello world!", + true + ], + "boolean": true, + "integer": 12, + "string": "Hello world!" +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.md new file mode 100644 index 0000000..c1eb4dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.md @@ -0,0 +1,7 @@ +Container parameters +==================== + +- `array`: `[12,"Hello world!",true]` +- `boolean`: `true` +- `integer`: `12` +- `string`: `Hello world!` \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.txt new file mode 100644 index 0000000..abc471c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.txt @@ -0,0 +1,13 @@ + +Symfony Container Parameters +============================ + + ----------- -------------------------- + Parameter Value + ----------- -------------------------- + array [12,"Hello world!",true] + boolean true + integer 12 + string Hello world! + ----------- -------------------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.xml new file mode 100644 index 0000000..e97f895 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/parameters_1.xml @@ -0,0 +1,7 @@ + + + [12,"Hello world!",true] + true + 12 + Hello world! + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.json new file mode 100644 index 0000000..beac79f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.json @@ -0,0 +1,20 @@ +{ + "path": "\/hello\/{name}", + "pathRegex": "#^\/hello(?:\/(?P[a-z]+))?$#s", + "host": "localhost", + "hostRegex": "#^localhost$#si", + "scheme": "http|https", + "method": "GET|HEAD", + "class": "Symfony\\Component\\Routing\\Route", + "defaults": { + "name": "Joseph" + }, + "requirements": { + "name": "[a-z]+" + }, + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.md new file mode 100644 index 0000000..5bfba18 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.md @@ -0,0 +1,15 @@ +- Path: /hello/{name} +- Path Regex: #^/hello(?:/(?P[a-z]+))?$#s +- Host: localhost +- Host Regex: #^localhost$#si +- Scheme: http|https +- Method: GET|HEAD +- Class: Symfony\Component\Routing\Route +- Defaults: + - `name`: Joseph +- Requirements: + - `name`: [a-z]+ +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt new file mode 100644 index 0000000..587543f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.txt @@ -0,0 +1,17 @@ ++--------------+---------------------------------------------------------+ +| Property | Value | ++--------------+---------------------------------------------------------+ +| Route Name | | +| Path | /hello/{name} | +| Path Regex | #^/hello(?:/(?P[a-z]+))?$#s | +| Host | localhost | +| Host Regex | #^localhost$#si | +| Scheme | http|https | +| Method | GET|HEAD | +| Requirements | name: [a-z]+ | +| Class | Symfony\Component\Routing\Route | +| Defaults | name: Joseph | +| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | +| | opt1: val1 | +| | opt2: val2 | ++--------------+---------------------------------------------------------+ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.xml new file mode 100644 index 0000000..b6040bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_1.xml @@ -0,0 +1,20 @@ + + + /hello/{name} + localhost + http + https + GET + HEAD + + Joseph + + + [a-z]+ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json new file mode 100644 index 0000000..d34b3a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.json @@ -0,0 +1,18 @@ +{ + "path": "\/name\/add", + "pathRegex": "#^\/name\/add$#s", + "host": "localhost", + "hostRegex": "#^localhost$#si", + "scheme": "http|https", + "method": "PUT|POST", + "class": "Symfony\\Component\\Routing\\Route", + "defaults": [ + + ], + "requirements": "NO CUSTOM", + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.md new file mode 100644 index 0000000..0a3f84b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.md @@ -0,0 +1,13 @@ +- Path: /name/add +- Path Regex: #^/name/add$#s +- Host: localhost +- Host Regex: #^localhost$#si +- Scheme: http|https +- Method: PUT|POST +- Class: Symfony\Component\Routing\Route +- Defaults: NONE +- Requirements: NO CUSTOM +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt new file mode 100644 index 0000000..e75c19e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.txt @@ -0,0 +1,17 @@ ++--------------+---------------------------------------------------------+ +| Property | Value | ++--------------+---------------------------------------------------------+ +| Route Name | | +| Path | /name/add | +| Path Regex | #^/name/add$#s | +| Host | localhost | +| Host Regex | #^localhost$#si | +| Scheme | http|https | +| Method | PUT|POST | +| Requirements | NO CUSTOM | +| Class | Symfony\Component\Routing\Route | +| Defaults | NONE | +| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | +| | opt1: val1 | +| | opt2: val2 | ++--------------+---------------------------------------------------------+ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.xml new file mode 100644 index 0000000..0f94cf7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_2.xml @@ -0,0 +1,14 @@ + + + /name/add + localhost + http + https + PUT + POST + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json new file mode 100644 index 0000000..7170953 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.json @@ -0,0 +1,40 @@ +{ + "route_1": { + "path": "\/hello\/{name}", + "pathRegex": "#^\/hello(?:\/(?P[a-z]+))?$#s", + "host": "localhost", + "hostRegex": "#^localhost$#si", + "scheme": "http|https", + "method": "GET|HEAD", + "class": "Symfony\\Component\\Routing\\Route", + "defaults": { + "name": "Joseph" + }, + "requirements": { + "name": "[a-z]+" + }, + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } + }, + "route_2": { + "path": "\/name\/add", + "pathRegex": "#^\/name\/add$#s", + "host": "localhost", + "hostRegex": "#^localhost$#si", + "scheme": "http|https", + "method": "PUT|POST", + "class": "Symfony\\Component\\Routing\\Route", + "defaults": [ + + ], + "requirements": "NO CUSTOM", + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.md b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.md new file mode 100644 index 0000000..815f36e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.md @@ -0,0 +1,37 @@ +route_1 +------- + +- Path: /hello/{name} +- Path Regex: #^/hello(?:/(?P[a-z]+))?$#s +- Host: localhost +- Host Regex: #^localhost$#si +- Scheme: http|https +- Method: GET|HEAD +- Class: Symfony\Component\Routing\Route +- Defaults: + - `name`: Joseph +- Requirements: + - `name`: [a-z]+ +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 + + +route_2 +------- + +- Path: /name/add +- Path Regex: #^/name/add$#s +- Host: localhost +- Host Regex: #^localhost$#si +- Scheme: http|https +- Method: PUT|POST +- Class: Symfony\Component\Routing\Route +- Defaults: NONE +- Requirements: NO CUSTOM +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.txt b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.txt new file mode 100644 index 0000000..35ec054 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.txt @@ -0,0 +1,7 @@ + --------- ---------- ------------ ----------- --------------- + Name Method Scheme Host Path + --------- ---------- ------------ ----------- --------------- + route_1 GET|HEAD http|https localhost /hello/{name} + route_2 PUT|POST http|https localhost /name/add + --------- ---------- ------------ ----------- --------------- + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.xml new file mode 100644 index 0000000..6d17820 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/route_collection_1.xml @@ -0,0 +1,35 @@ + + + + /hello/{name} + localhost + http + https + GET + HEAD + + Joseph + + + [a-z]+ + + + + + + + + + /name/add + localhost + http + https + PUT + POST + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/BaseBundle/views/base.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/BaseBundle/views/base.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/BaseBundle/views/controller/custom.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/BaseBundle/views/controller/custom.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations/messages.fr.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations/messages.fr.yml new file mode 100644 index 0000000..767141d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/translations/messages.fr.yml @@ -0,0 +1 @@ +folder: répertoire diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/resource.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/resource.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/this.is.a.template.format.engine b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/this.is.a.template.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php new file mode 100644 index 0000000..04df261 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php @@ -0,0 +1,33 @@ +This template is used for translation message extraction tests +trans('single-quoted key') ?> +trans('double-quoted key') ?> +trans(<< +trans(<<<'EOF' +nowdoc key +EOF +) ?> +trans( + "double-quoted key with whitespace and escaped \$\n\" sequences" +) ?> +trans( + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' +) ?> +trans(<< +trans(<<<'EOF' +nowdoc key with whitespace and nonescaped \$\n sequences +EOF +) ?> + +trans('single-quoted key with "quote mark at the end"') ?> + +transChoice( + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + 10, + array('%count%' => 10) +) ?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..c4bee6c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\Fabpot\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php new file mode 100644 index 0000000..17894ba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\Fabpot\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FabpotFooBundle extends Bundle +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'SensioFooBundle'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..ddda38c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.php new file mode 100644 index 0000000..3c889e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\FooBundle\Controller\Sub; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.php new file mode 100644 index 0000000..1bffc7f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\FooBundle\Controller\Test; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php new file mode 100644 index 0000000..656f17c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FooBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..1bb8038 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\Sensio\Cms\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php new file mode 100644 index 0000000..58967d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\Sensio\Cms\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class SensioCmsFooBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..86486f0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\Sensio\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php new file mode 100644 index 0000000..d1bc5dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace TestBundle\Sensio\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class SensioFooBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.php new file mode 100644 index 0000000..88bd102 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class FragmentController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction(Request $request) + { + return $this->container->get('templating')->renderResponse('fragment.html.php', array('bar' => new Bar())); + } + + public function inlinedAction($options, $_format) + { + return new Response($options['bar']->getBar().' '.$_format); + } + + public function customFormatAction($_format) + { + return new Response($_format); + } + + public function customLocaleAction(Request $request) + { + return new Response($request->getLocale()); + } + + public function forwardLocaleAction(Request $request) + { + return new Response($request->getLocale()); + } +} + +class Bar +{ + private $bar = 'bar'; + + public function getBar() + { + return $this->bar; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.php new file mode 100644 index 0000000..dd518a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Response; + +class ProfilerController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction() + { + return new Response('Hello'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php new file mode 100644 index 0000000..7614637 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; + +class SessionController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function welcomeAction(Request $request, $name = null) + { + $session = $request->getSession(); + + // new session case + if (!$session->has('name')) { + if (!$name) { + return new Response('You are new here and gave no name.'); + } + + // remember name + $session->set('name', $name); + + return new Response(sprintf('Hello %s, nice to meet you.', $name)); + } + + // existing session + $name = $session->get('name'); + + return new Response(sprintf('Welcome back %s, nice to meet you.', $name)); + } + + public function logoutAction(Request $request) + { + $request->getSession('session')->invalidate(); + + return new Response('Session cleared.'); + } + + public function setFlashAction(Request $request, $message) + { + $session = $request->getSession(); + $session->getFlashBag()->set('notice', $message); + + return new RedirectResponse($this->container->get('router')->generate('session_showflash')); + } + + public function showFlashAction(Request $request) + { + $session = $request->getSession(); + + if ($session->getFlashBag()->has('notice')) { + list($output) = $session->getFlashBag()->get('notice'); + } else { + $output = 'No flash was set.'; + } + + return new Response($output); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.php new file mode 100644 index 0000000..5df6e29 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +class SubRequestController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction() + { + $handler = $this->container->get('fragment.handler'); + + $errorUrl = $this->generateUrl('subrequest_fragment_error', array('_locale' => 'fr', '_format' => 'json')); + $altUrl = $this->generateUrl('subrequest_fragment', array('_locale' => 'fr', '_format' => 'json')); + + // simulates a failure during the rendering of a fragment... + // should render fr/json + $content = $handler->render($errorUrl, 'inline', array('alt' => $altUrl)); + + // ...to check that the FragmentListener still references the right Request + // when rendering another fragment after the error occurred + // should render en/html instead of fr/json + $content .= $handler->render(new ControllerReference('TestBundle:SubRequest:fragment')); + + // forces the LocaleListener to set fr for the locale... + // should render fr/json + $content .= $handler->render($altUrl); + + // ...and check that after the rendering, the original Request is back + // and en is used as a locale + // should use en/html instead of fr/json + $content .= '--'.$this->generateUrl('subrequest_fragment'); + + // The RouterListener is also tested as if it does not keep the right + // Request in the context, a 301 would be generated + return new Response($content); + } + + public function fragmentAction(Request $request) + { + return new Response('--'.$request->getLocale().'/'.$request->getRequestFormat()); + } + + public function fragmentErrorAction() + { + throw new \RuntimeException('error'); + } + + protected function generateUrl($name, $arguments = array()) + { + return $this->container->get('router')->generate($name, $arguments); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.php new file mode 100644 index 0000000..8b4abb7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config; + +class CustomConfig +{ + public function addConfiguration($rootNode) + { + $rootNode + ->children() + ->scalarNode('custom')->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..2f45cd9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + private $customConfig; + + public function __construct($customConfig = null) + { + $this->customConfig = $customConfig; + } + + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('test'); + + if ($this->customConfig) { + $this->customConfig->addConfiguration($rootNode); + } + + return $treeBuilder; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php new file mode 100644 index 0000000..e7f170d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; + +class TestExtension extends Extension implements PrependExtensionInterface +{ + private $customConfig; + + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + } + + /** + * {@inheritdoc} + */ + public function prepend(ContainerBuilder $container) + { + $container->prependExtensionConfig('test', array('custom' => 'foo')); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($this->customConfig); + } + + public function setCustomConfig($customConfig) + { + $this->customConfig = $customConfig; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml new file mode 100644 index 0000000..57d83eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml @@ -0,0 +1,46 @@ +session_welcome: + path: /session + defaults: { _controller: TestBundle:Session:welcome } + +session_welcome_name: + path: /session/{name} + defaults: { _controller: TestBundle:Session:welcome } + +session_logout: + path: /session_logout + defaults: { _controller: TestBundle:Session:logout} + +session_setflash: + path: /session_setflash/{message} + defaults: { _controller: TestBundle:Session:setFlash} + +session_showflash: + path: /session_showflash + defaults: { _controller: TestBundle:Session:showFlash} + +profiler: + path: /profiler + defaults: { _controller: TestBundle:Profiler:index } + +subrequest_index: + path: /subrequest/{_locale}.{_format} + defaults: { _controller: TestBundle:SubRequest:index, _format: "html" } + schemes: [https] + +subrequest_fragment_error: + path: /subrequest/fragment/error/{_locale}.{_format} + defaults: { _controller: TestBundle:SubRequest:fragmentError, _format: "html" } + schemes: [http] + +subrequest_fragment: + path: /subrequest/fragment/{_locale}.{_format} + defaults: { _controller: TestBundle:SubRequest:fragment, _format: "html" } + schemes: [http] + +fragment_home: + path: /fragment_home + defaults: { _controller: TestBundle:Fragment:index, _format: txt } + +fragment_inlined: + path: /fragment_inlined + defaults: { _controller: TestBundle:Fragment:inlined } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php new file mode 100644 index 0000000..e7982cf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig; + +class TestBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + /** @var $extension DependencyInjection\TestExtension */ + $extension = $container->getExtension('test'); + + $extension->setCustomConfig(new CustomConfig()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php new file mode 100644 index 0000000..dcffe78 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * @group functional + */ +class ConfigDebugCommandTest extends WebTestCase +{ + private $application; + + protected function setUp() + { + $kernel = static::createKernel(array('test_case' => 'ConfigDump', 'root_config' => 'config.yml')); + $this->application = new Application($kernel); + $this->application->doRun(new ArrayInput(array()), new NullOutput()); + } + + public function testDumpBundleName() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'TestBundle')); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('custom: foo', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $command = $this->application->find('debug:config'); + + return new CommandTester($command); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php new file mode 100644 index 0000000..b0ac90c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * @group functional + */ +class ConfigDumpReferenceCommandTest extends WebTestCase +{ + private $application; + + protected function setUp() + { + $kernel = static::createKernel(array('test_case' => 'ConfigDump', 'root_config' => 'config.yml')); + $this->application = new Application($kernel); + $this->application->doRun(new ArrayInput(array()), new NullOutput()); + } + + public function testDumpBundleName() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'TestBundle')); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('test:', $tester->getDisplay()); + $this->assertContains(' custom:', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $command = $this->application->find('config:dump-reference'); + + return new CommandTester($command); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php new file mode 100644 index 0000000..dff65d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +class FragmentTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testFragment($insulate) + { + $client = $this->createClient(array('test_case' => 'Fragment', 'root_config' => 'config.yml')); + if ($insulate) { + $client->insulate(); + } + + $client->request('GET', '/fragment_home'); + + $this->assertEquals('bar txt--html--es--fr', $client->getResponse()->getContent()); + } + + public function getConfigs() + { + return array( + array(false), + array(true), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php new file mode 100644 index 0000000..a874112 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +class ProfilerTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testProfilerIsDisabled($insulate) + { + $client = $this->createClient(array('test_case' => 'Profiler', 'root_config' => 'config.yml')); + if ($insulate) { + $client->insulate(); + } + + $client->request('GET', '/profiler'); + $this->assertFalse($client->getProfile()); + + // enable the profiler for the next request + $client->enableProfiler(); + $crawler = $client->request('GET', '/profiler'); + $profile = $client->getProfile(); + $this->assertTrue(is_object($profile)); + + $client->request('GET', '/profiler'); + $this->assertFalse($client->getProfile()); + } + + public function getConfigs() + { + return array( + array(false), + array(true), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php new file mode 100644 index 0000000..415e031 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +class SessionTest extends WebTestCase +{ + /** + * Tests session attributes persist. + * + * @dataProvider getConfigs + */ + public function testWelcome($config, $insulate) + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client->insulate(); + } + + // no session + $crawler = $client->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler->text()); + + // remember name + $crawler = $client->request('GET', '/session/drak'); + $this->assertContains('Hello drak, nice to meet you.', $crawler->text()); + + // prove remembered name + $crawler = $client->request('GET', '/session'); + $this->assertContains('Welcome back drak, nice to meet you.', $crawler->text()); + + // clear session + $crawler = $client->request('GET', '/session_logout'); + $this->assertContains('Session cleared.', $crawler->text()); + + // prove cleared session + $crawler = $client->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler->text()); + } + + /** + * Tests flash messages work in practice. + * + * @dataProvider getConfigs + */ + public function testFlash($config, $insulate) + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client->insulate(); + } + + // set flash + $crawler = $client->request('GET', '/session_setflash/Hello%20world.'); + + // check flash displays on redirect + $this->assertContains('Hello world.', $client->followRedirect()->text()); + + // check flash is gone + $crawler = $client->request('GET', '/session_showflash'); + $this->assertContains('No flash was set.', $crawler->text()); + } + + /** + * See if two separate insulated clients can run without + * polluting eachother's session data. + * + * @dataProvider getConfigs + */ + public function testTwoClients($config, $insulate) + { + // start first client + $client1 = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client1->insulate(); + } + + // start second client + $client2 = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client2->insulate(); + } + + // new session, so no name set. + $crawler1 = $client1->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler1->text()); + + // set name of client1 + $crawler1 = $client1->request('GET', '/session/client1'); + $this->assertContains('Hello client1, nice to meet you.', $crawler1->text()); + + // no session for client2 + $crawler2 = $client2->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler2->text()); + + // remember name client2 + $crawler2 = $client2->request('GET', '/session/client2'); + $this->assertContains('Hello client2, nice to meet you.', $crawler2->text()); + + // prove remembered name of client1 + $crawler1 = $client1->request('GET', '/session'); + $this->assertContains('Welcome back client1, nice to meet you.', $crawler1->text()); + + // prove remembered name of client2 + $crawler2 = $client2->request('GET', '/session'); + $this->assertContains('Welcome back client2, nice to meet you.', $crawler2->text()); + + // clear client1 + $crawler1 = $client1->request('GET', '/session_logout'); + $this->assertContains('Session cleared.', $crawler1->text()); + + // prove client1 data is cleared + $crawler1 = $client1->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler1->text()); + + // prove remembered name of client2 remains untouched. + $crawler2 = $client2->request('GET', '/session'); + $this->assertContains('Welcome back client2, nice to meet you.', $crawler2->text()); + } + + public function getConfigs() + { + return array( + // configfile, insulate + array('config.yml', true), + array('config.yml', false), + ); + } + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('SessionTest'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->deleteTmpDir('SessionTest'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SubRequestsTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SubRequestsTest.php new file mode 100644 index 0000000..2aaeb22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SubRequestsTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +class SubRequestsTest extends WebTestCase +{ + public function testStateAfterSubRequest() + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => 'config.yml')); + $client->request('GET', 'https://localhost/subrequest/en'); + + $this->assertEquals('--fr/json--en/html--fr/json--http://localhost/subrequest/fragment/en', $client->getResponse()->getContent()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php new file mode 100644 index 0000000..50d4cfa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/WebTestCase.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Kernel; + +class WebTestCase extends BaseWebTestCase +{ + public static function assertRedirect($response, $location) + { + self::assertTrue($response->isRedirect(), 'Response is not a redirect, got status code: '.$response->getStatusCode()); + self::assertEquals('http://localhost'.$location, $response->headers->get('Location')); + } + + protected function deleteTmpDir($testCase) + { + if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$testCase)) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } + + protected static function getKernelClass() + { + require_once __DIR__.'/app/AppKernel.php'; + + return 'Symfony\Bundle\FrameworkBundle\Tests\Functional\app\AppKernel'; + } + + protected static function createKernel(array $options = array()) + { + $class = self::getKernelClass(); + + if (!isset($options['test_case'])) { + throw new \InvalidArgumentException('The option "test_case" must be set.'); + } + + return new $class( + $options['test_case'], + isset($options['root_config']) ? $options['root_config'] : 'config.yml', + isset($options['environment']) ? $options['environment'] : 'frameworkbundletest'.strtolower($options['test_case']), + isset($options['debug']) ? $options['debug'] : true + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php new file mode 100644 index 0000000..b07d44f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app; + +// get the autoload file +$dir = __DIR__; +$lastDir = null; +while ($dir !== $lastDir) { + $lastDir = $dir; + + if (file_exists($dir.'/autoload.php')) { + require_once $dir.'/autoload.php'; + break; + } + + if (file_exists($dir.'/autoload.php.dist')) { + require_once $dir.'/autoload.php.dist'; + break; + } + + if (file_exists($dir.'/vendor/autoload.php')) { + require_once $dir.'/vendor/autoload.php'; + break; + } + + $dir = dirname($dir); +} + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Kernel; + +/** + * App Test Kernel for functional tests. + * + * @author Johannes M. Schmitt + */ +class AppKernel extends Kernel +{ + private $testCase; + private $rootConfig; + + public function __construct($testCase, $rootConfig, $environment, $debug) + { + if (!is_dir(__DIR__.'/'.$testCase)) { + throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); + } + $this->testCase = $testCase; + + $fs = new Filesystem(); + if (!$fs->isAbsolutePath($rootConfig) && !file_exists($rootConfig = __DIR__.'/'.$testCase.'/'.$rootConfig)) { + throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig)); + } + $this->rootConfig = $rootConfig; + + parent::__construct($environment, $debug); + } + + public function registerBundles() + { + if (!file_exists($filename = $this->getRootDir().'/'.$this->testCase.'/bundles.php')) { + throw new \RuntimeException(sprintf('The bundles file "%s" does not exist.', $filename)); + } + + return include $filename; + } + + public function getRootDir() + { + return __DIR__; + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/logs'; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load($this->rootConfig); + } + + public function serialize() + { + return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); + } + + public function unserialize($str) + { + $a = unserialize($str); + $this->__construct($a[0], $a[1], $a[2], $a[3]); + } + + protected function getKernelParameters() + { + $parameters = parent::getKernelParameters(); + $parameters['kernel.test_case'] = $this->testCase; + + return $parameters; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ConfigDump/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ConfigDump/bundles.php new file mode 100644 index 0000000..a73987b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ConfigDump/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ConfigDump/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ConfigDump/config.yml new file mode 100644 index 0000000..377d3e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ConfigDump/config.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../config/default.yml } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/bundles.php new file mode 100644 index 0000000..a73987b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/config.yml new file mode 100644 index 0000000..f7ea83d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/config.yml @@ -0,0 +1,7 @@ +imports: + - { resource: ../config/default.yml } + +framework: + fragments: ~ + templating: + engines: ['php'] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/routing.yml new file mode 100644 index 0000000..8a9bd84 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Fragment/routing.yml @@ -0,0 +1,2 @@ +_fragmenttest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/bundles.php new file mode 100644 index 0000000..a73987b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/config.yml new file mode 100644 index 0000000..12ce67e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/config.yml @@ -0,0 +1,7 @@ +imports: + - { resource: ../config/default.yml } + +framework: + profiler: + enabled: true + collect: false diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/routing.yml new file mode 100644 index 0000000..d4b77c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Profiler/routing.yml @@ -0,0 +1,2 @@ +_sessiontest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Resources/fragment.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Resources/fragment.html.php new file mode 100644 index 0000000..8ea3c36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Resources/fragment.html.php @@ -0,0 +1,14 @@ +get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:inlined', array( + 'options' => array( + 'bar' => $bar, + 'eleven' => 11, + ), + ))); +?>--get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:customformat', array('_format' => 'html'))); +?>--get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:customlocale', array('_locale' => 'es'))); +?>--getRequest()->setLocale('fr'); + echo $this->get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:forwardlocale')); +?> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/bundles.php new file mode 100644 index 0000000..a73987b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/config.yml new file mode 100644 index 0000000..65dd6c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/config.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ./../config/default.yml } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/routing.yml new file mode 100644 index 0000000..d4b77c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Session/routing.yml @@ -0,0 +1,2 @@ +_sessiontest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/default.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/default.yml new file mode 100644 index 0000000..8ddd7ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/default.yml @@ -0,0 +1,2 @@ +imports: + - { resource: framework.yml } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml new file mode 100644 index 0000000..a313b33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml @@ -0,0 +1,13 @@ +framework: + secret: test + router: { resource: "%kernel.root_dir%/%kernel.test_case%/routing.yml" } + validation: { enabled: true, enable_annotations: true } + csrf_protection: true + form: true + test: ~ + default_locale: en + session: + storage_id: session.storage.mock_file + +services: + logger: { class: Psr\Log\NullLogger } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php new file mode 100644 index 0000000..02bed82 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Routing\RouteCollectionBuilder; + +class ConcreteMicroKernel extends Kernel +{ + use MicroKernelTrait; + + private $cacheDir; + + public function halloweenAction() + { + return new Response('halloween'); + } + + public function registerBundles() + { + return array( + new FrameworkBundle(), + ); + } + + public function getCacheDir() + { + return $this->cacheDir = sys_get_temp_dir().'/sf_micro_kernel'; + } + + public function getLogDir() + { + return $this->cacheDir; + } + + public function __destruct() + { + $fs = new Filesystem(); + $fs->remove($this->cacheDir); + } + + protected function configureRoutes(RouteCollectionBuilder $routes) + { + $routes->add('/', 'kernel:halloweenAction'); + } + + protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader) + { + $c->loadFromExtension('framework', array( + 'secret' => '$ecret', + )); + + $c->setParameter('halloween', 'Have a great day!'); + $c->register('halloween', 'stdClass'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php new file mode 100644 index 0000000..63d5b2d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Symfony\Component\HttpFoundation\Request; + +class MicroKernelTraitTest extends \PHPUnit_Framework_TestCase +{ + public function test() + { + $kernel = new ConcreteMicroKernel('test', true); + $kernel->boot(); + + $request = Request::create('/'); + $response = $kernel->handle($request); + + $this->assertEquals('halloween', $response->getContent()); + $this->assertEquals('Have a great day!', $kernel->getContainer()->getParameter('halloween')); + $this->assertInstanceOf('stdClass', $kernel->getContainer()->get('halloween')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php new file mode 100644 index 0000000..3c8670d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/DelegatingLoaderTest.php @@ -0,0 +1,19 @@ +getMockBuilder(ControllerNameParser::class) + ->disableOriginalConstructor() + ->getMock(); + new DelegatingLoader($controllerNameParser, new LoaderResolver()); + $this->assertTrue(true, '__construct() takes a ControllerNameParser and LoaderResolverInterface respectively as its first and second argument.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php new file mode 100644 index 0000000..057aa6c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Routing; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher; +use Symfony\Component\Routing\RequestContext; + +class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testRedirectWhenNoSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $matcher = new RedirectableUrlMatcher($coll, $context = new RequestContext()); + + $this->assertEquals(array( + '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', + 'path' => '/foo/', + 'permanent' => true, + 'scheme' => null, + 'httpPort' => $context->getHttpPort(), + 'httpsPort' => $context->getHttpsPort(), + '_route' => null, + ), + $matcher->match('/foo') + ); + } + + public function testSchemeRedirect() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + + $matcher = new RedirectableUrlMatcher($coll, $context = new RequestContext()); + + $this->assertEquals(array( + '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', + 'path' => '/foo', + 'permanent' => true, + 'scheme' => 'https', + 'httpPort' => $context->getHttpPort(), + 'httpsPort' => $context->getHttpsPort(), + '_route' => 'foo', + ), + $matcher->match('/foo') + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php new file mode 100644 index 0000000..330e065 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php @@ -0,0 +1,235 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Routing; + +use Symfony\Bundle\FrameworkBundle\Routing\Router; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RouterTest extends \PHPUnit_Framework_TestCase +{ + public function testGenerateWithServiceParam() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + ' /{_locale}', + array( + '_locale' => '%locale%', + ), + array( + '_locale' => 'en|es', + ), array(), '', array(), array(), '"%foo%" == "bar"' + )); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('locale', 'es'); + $sc->setParameter('foo', 'bar'); + + $router = new Router($sc, 'foo'); + + $this->assertSame('/en', $router->generate('foo', array('_locale' => 'en'))); + $this->assertSame('/', $router->generate('foo', array('_locale' => 'es'))); + $this->assertSame('"bar" == "bar"', $router->getRouteCollection()->get('foo')->getCondition()); + } + + public function testDefaultsPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%escaped%%', + 'boo' => array('%parameter%', '%%escaped_parameter%%', array('%bee_parameter%', 'bee')), + 'bee' => array('bee', 'bee'), + ), + array( + ) + )); + + $sc = $this->getServiceContainer($routes); + + $sc->setParameter('parameter.foo', 'foo'); + $sc->setParameter('parameter.bar', 'bar'); + $sc->setParameter('parameter', 'boo'); + $sc->setParameter('bee_parameter', 'foo_bee'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%escaped%', + 'boo' => array('boo', '%escaped_parameter%', array('foo_bee', 'bee')), + 'bee' => array('bee', 'bee'), + ), + $route->getDefaults() + ); + } + + public function testRequirementsPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + ), + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%escaped%%', + ) + )); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('parameter.foo', 'foo'); + $sc->setParameter('parameter.bar', 'bar'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%escaped%', + ), + $route->getRequirements() + ); + } + + public function testPatternPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/before/%parameter.foo%/after/%%escaped%%')); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('parameter.foo', 'foo'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%escaped%', + $route->getPath() + ); + } + + public function testHostPlaceholders() + { + $routes = new RouteCollection(); + + $route = new Route('foo'); + $route->setHost('/before/%parameter.foo%/after/%%escaped%%'); + + $routes->add('foo', $route); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('parameter.foo', 'foo'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%escaped%', + $route->getHost() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException + * @expectedExceptionMessage You have requested a non-existent parameter "nope". + */ + public function testExceptionOnNonExistentParameter() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%nope%')); + + $sc = $this->getServiceContainer($routes); + + $router = new Router($sc, 'foo'); + $router->getRouteCollection()->get('foo'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The container parameter "object", used in the route configuration value "/%object%", must be a string or numeric, but it is of type object. + */ + public function testExceptionOnNonStringParameter() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%object%')); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('object', new \stdClass()); + + $router = new Router($sc, 'foo'); + $router->getRouteCollection()->get('foo'); + } + + /** + * @dataProvider getNonStringValues + */ + public function testDefaultValuesAsNonStrings($value) + { + $routes = new RouteCollection(); + $routes->add('foo', new Route('foo', array('foo' => $value), array('foo' => '\d+'))); + + $sc = $this->getServiceContainer($routes); + + $router = new Router($sc, 'foo'); + + $route = $router->getRouteCollection()->get('foo'); + + $this->assertSame($value, $route->getDefault('foo')); + } + + public function getNonStringValues() + { + return array(array(null), array(false), array(true), array(new \stdClass()), array(array('foo', 'bar')), array(array(array()))); + } + + /** + * @param RouteCollection $routes + * + * @return \Symfony\Component\DependencyInjection\Container + */ + private function getServiceContainer(RouteCollection $routes) + { + $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + + $loader + ->expects($this->any()) + ->method('load') + ->will($this->returnValue($routes)) + ; + + $sc = $this->getMock('Symfony\\Component\\DependencyInjection\\Container', array('get')); + + $sc + ->expects($this->once()) + ->method('get') + ->will($this->returnValue($loader)) + ; + + return $sc; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php new file mode 100644 index 0000000..c5d2d8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/DelegatingEngineTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine; + +class DelegatingEngineTest extends \PHPUnit_Framework_TestCase +{ + public function testSupportsRetrievesEngineFromTheContainer() + { + $container = $this->getContainerMock(array( + 'engine.first' => $this->getEngineMock('template.php', false), + 'engine.second' => $this->getEngineMock('template.php', true), + )); + + $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); + + $this->assertTrue($delegatingEngine->supports('template.php')); + } + + public function testGetExistingEngine() + { + $firstEngine = $this->getEngineMock('template.php', false); + $secondEngine = $this->getEngineMock('template.php', true); + $container = $this->getContainerMock(array( + 'engine.first' => $firstEngine, + 'engine.second' => $secondEngine, + )); + + $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); + + $this->assertSame($secondEngine, $delegatingEngine->getEngine('template.php', array('foo' => 'bar'))); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage No engine is able to work with the template "template.php" + */ + public function testGetInvalidEngine() + { + $firstEngine = $this->getEngineMock('template.php', false); + $secondEngine = $this->getEngineMock('template.php', false); + $container = $this->getContainerMock(array( + 'engine.first' => $firstEngine, + 'engine.second' => $secondEngine, + )); + + $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); + $delegatingEngine->getEngine('template.php', array('foo' => 'bar')); + } + + public function testRenderResponseWithFrameworkEngine() + { + $response = $this->getMock('Symfony\Component\HttpFoundation\Response'); + $engine = $this->getFrameworkEngineMock('template.php', true); + $engine->expects($this->once()) + ->method('renderResponse') + ->with('template.php', array('foo' => 'bar')) + ->will($this->returnValue($response)); + $container = $this->getContainerMock(array('engine' => $engine)); + + $delegatingEngine = new DelegatingEngine($container, array('engine')); + + $this->assertSame($response, $delegatingEngine->renderResponse('template.php', array('foo' => 'bar'))); + } + + public function testRenderResponseWithTemplatingEngine() + { + $engine = $this->getEngineMock('template.php', true); + $container = $this->getContainerMock(array('engine' => $engine)); + $delegatingEngine = new DelegatingEngine($container, array('engine')); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $delegatingEngine->renderResponse('template.php', array('foo' => 'bar'))); + } + + private function getEngineMock($template, $supports) + { + $engine = $this->getMock('Symfony\Component\Templating\EngineInterface'); + + $engine->expects($this->once()) + ->method('supports') + ->with($template) + ->will($this->returnValue($supports)); + + return $engine; + } + + private function getFrameworkEngineMock($template, $supports) + { + $engine = $this->getMock('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface'); + + $engine->expects($this->once()) + ->method('supports') + ->with($template) + ->will($this->returnValue($supports)); + + return $engine; + } + + private function getContainerMock($services) + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $i = 0; + foreach ($services as $id => $service) { + $container->expects($this->at($i++)) + ->method('get') + ->with($id) + ->will($this->returnValue($service)); + } + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/GlobalVariablesTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/GlobalVariablesTest.php new file mode 100644 index 0000000..5c00c62 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/GlobalVariablesTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; + +class GlobalVariablesTest extends TestCase +{ + private $container; + private $globals; + + protected function setUp() + { + $this->container = new Container(); + $this->globals = new GlobalVariables($this->container); + } + + public function testGetUserNoTokenStorage() + { + $this->assertNull($this->globals->getUser()); + } + + public function testGetUserNoToken() + { + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $this->container->set('security.token_storage', $tokenStorage); + $this->assertNull($this->globals->getUser()); + } + + /** + * @dataProvider getUserProvider + */ + public function testGetUser($user, $expectedUser) + { + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $this->container->set('security.token_storage', $tokenStorage); + + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token)); + + $this->assertSame($expectedUser, $this->globals->getUser()); + } + + public function getUserProvider() + { + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $std = new \stdClass(); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + return array( + array($user, $user), + array($std, $std), + array($token, $token), + array('Anon.', null), + array(null, null), + array(10, null), + array(true, null), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.php new file mode 100644 index 0000000..3a66454 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReference; + +class StubTemplateNameParser implements TemplateNameParserInterface +{ + private $root; + + private $rootTheme; + + public function __construct($root, $rootTheme) + { + $this->root = $root; + $this->rootTheme = $rootTheme; + } + + public function parse($name) + { + list($bundle, $controller, $template) = explode(':', $name, 3); + + if ($template[0] == '_') { + $path = $this->rootTheme.'/Custom/'.$template; + } elseif ($bundle === 'TestBundle') { + $path = $this->rootTheme.'/'.$controller.'/'.$template; + } else { + $path = $this->root.'/'.$controller.'/'.$template; + } + + return new TemplateReference($path, 'php'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php new file mode 100644 index 0000000..17d1fd4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Fixtures/StubTranslator.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures; + +use Symfony\Component\Translation\TranslatorInterface; + +class StubTranslator implements TranslatorInterface +{ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + return '[trans]'.$id.'[/trans]'; + } + + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + return '[trans]'.$id.'[/trans]'; + } + + public function setLocale($locale) + { + } + + public function getLocale() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php new file mode 100644 index 0000000..74bdd81 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Templating\TemplatingExtension; +use Symfony\Component\Form\Tests\AbstractDivLayoutTest; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator; +use Symfony\Component\Templating\PhpEngine; +use Symfony\Component\Templating\Loader\FilesystemLoader; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; + +class FormHelperDivLayoutTest extends AbstractDivLayoutTest +{ + /** + * @var PhpEngine + */ + protected $engine; + + protected function getExtensions() + { + // should be moved to the Form component once absolute file paths are supported + // by the default name parser in the Templating component + $reflClass = new \ReflectionClass('Symfony\Bundle\FrameworkBundle\FrameworkBundle'); + $root = realpath(dirname($reflClass->getFileName()).'/Resources/views'); + $rootTheme = realpath(__DIR__.'/Resources'); + $templateNameParser = new StubTemplateNameParser($root, $rootTheme); + $loader = new FilesystemLoader(array()); + + $this->engine = new PhpEngine($templateNameParser, $loader); + $this->engine->addGlobal('global', ''); + $this->engine->setHelpers(array( + new TranslatorHelper(new StubTranslator()), + )); + + return array_merge(parent::getExtensions(), array( + new TemplatingExtension($this->engine, $this->csrfTokenManager, array( + 'FrameworkBundle:Form', + )), + )); + } + + protected function tearDown() + { + $this->engine = null; + + parent::tearDown(); + } + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->form($view, $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + return (string) $this->engine->get('form')->label($view, $label, $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->engine->get('form')->errors($view); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->widget($view, $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->row($view, $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->rest($view, $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->start($view, $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->end($view, $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->engine->get('form')->setTheme($view, $themes); + } + + public static function themeBlockInheritanceProvider() + { + return array( + array(array('TestBundle:Parent')), + ); + } + + public static function themeInheritanceProvider() + { + return array( + array(array('TestBundle:Parent'), array('TestBundle:Child')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php new file mode 100644 index 0000000..1e67134 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Templating\TemplatingExtension; +use Symfony\Component\Form\Tests\AbstractTableLayoutTest; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator; +use Symfony\Component\Templating\PhpEngine; +use Symfony\Component\Templating\Loader\FilesystemLoader; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; + +class FormHelperTableLayoutTest extends AbstractTableLayoutTest +{ + /** + * @var PhpEngine + */ + protected $engine; + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + protected function getExtensions() + { + // should be moved to the Form component once absolute file paths are supported + // by the default name parser in the Templating component + $reflClass = new \ReflectionClass('Symfony\Bundle\FrameworkBundle\FrameworkBundle'); + $root = realpath(dirname($reflClass->getFileName()).'/Resources/views'); + $rootTheme = realpath(__DIR__.'/Resources'); + $templateNameParser = new StubTemplateNameParser($root, $rootTheme); + $loader = new FilesystemLoader(array()); + + $this->engine = new PhpEngine($templateNameParser, $loader); + $this->engine->addGlobal('global', ''); + $this->engine->setHelpers(array( + new TranslatorHelper(new StubTranslator()), + )); + + return array_merge(parent::getExtensions(), array( + new TemplatingExtension($this->engine, $this->csrfTokenManager, array( + 'FrameworkBundle:Form', + 'FrameworkBundle:FormTable', + )), + )); + } + + protected function tearDown() + { + $this->engine = null; + + parent::tearDown(); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->form($view, $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + return (string) $this->engine->get('form')->label($view, $label, $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->engine->get('form')->errors($view); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->widget($view, $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->row($view, $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->rest($view, $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->start($view, $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->end($view, $vars); + } + + protected function setTheme(FormView $view, array $themes) + { + $this->engine->get('form')->setTheme($view, $themes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/RequestHelperTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/RequestHelperTest.php new file mode 100644 index 0000000..d626e63 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/RequestHelperTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\RequestHelper; + +class RequestHelperTest extends \PHPUnit_Framework_TestCase +{ + protected $requestStack; + + protected function setUp() + { + $this->requestStack = new RequestStack(); + $request = new Request(); + $request->initialize(array('foobar' => 'bar')); + $this->requestStack->push($request); + } + + public function testGetParameter() + { + $helper = new RequestHelper($this->requestStack); + + $this->assertEquals('bar', $helper->getParameter('foobar')); + $this->assertEquals('foo', $helper->getParameter('bar', 'foo')); + + $this->assertNull($helper->getParameter('foo')); + } + + public function testGetLocale() + { + $helper = new RequestHelper($this->requestStack); + + $this->assertEquals('en', $helper->getLocale()); + } + + public function testGetName() + { + $helper = new RequestHelper($this->requestStack); + + $this->assertEquals('request', $helper->getName()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php new file mode 100644 index 0000000..0c1af40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php @@ -0,0 +1 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php new file mode 100644 index 0000000..0524003 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php @@ -0,0 +1,2 @@ +humanize($name); } ?> + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php new file mode 100644 index 0000000..e165a42 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php @@ -0,0 +1,4 @@ +humanize($name); +} ?> + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php new file mode 100644 index 0000000..078fe57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php @@ -0,0 +1,3 @@ +
    + widget($form) ?> +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php new file mode 100644 index 0000000..068c5de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php @@ -0,0 +1 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php new file mode 100644 index 0000000..3c6c158 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php @@ -0,0 +1,2 @@ + +block($form, 'widget_attributes') ?> value="" rel="theme" /> diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php new file mode 100644 index 0000000..5517afd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/SessionHelperTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper; + +class SessionHelperTest extends \PHPUnit_Framework_TestCase +{ + protected $requestStack; + + protected function setUp() + { + $request = new Request(); + + $session = new Session(new MockArraySessionStorage()); + $session->set('foobar', 'bar'); + $session->getFlashBag()->set('notice', 'bar'); + + $request->setSession($session); + + $this->requestStack = new RequestStack(); + $this->requestStack->push($request); + } + + protected function tearDown() + { + $this->requestStack = null; + } + + public function testFlash() + { + $helper = new SessionHelper($this->requestStack); + + $this->assertTrue($helper->hasFlash('notice')); + + $this->assertEquals(array('bar'), $helper->getFlash('notice')); + } + + public function testGetFlashes() + { + $helper = new SessionHelper($this->requestStack); + $this->assertEquals(array('notice' => array('bar')), $helper->getFlashes()); + } + + public function testGet() + { + $helper = new SessionHelper($this->requestStack); + + $this->assertEquals('bar', $helper->get('foobar')); + $this->assertEquals('foo', $helper->get('bar', 'foo')); + + $this->assertNull($helper->get('foo')); + } + + public function testGetName() + { + $helper = new SessionHelper($this->requestStack); + + $this->assertEquals('session', $helper->getName()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php new file mode 100644 index 0000000..1a0ff5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/StopwatchHelperTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Bundle\FrameworkBundle\Templating\Helper\StopwatchHelper; + +class StopwatchHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testDevEnvironment() + { + $stopwatch = $this->getMock('Symfony\Component\Stopwatch\Stopwatch'); + $stopwatch->expects($this->once()) + ->method('start') + ->with('foo'); + + $helper = new StopwatchHelper($stopwatch); + $helper->start('foo'); + } + + public function testProdEnvironment() + { + $helper = new StopwatchHelper(null); + + try { + $helper->start('foo'); + } catch (\BadMethodCallException $e) { + $this->fail('Assumed stopwatch is not called when not provided'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Loader/TemplateLocatorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Loader/TemplateLocatorTest.php new file mode 100644 index 0000000..1b28ec4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Loader/TemplateLocatorTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Loader; + +use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TemplateLocatorTest extends TestCase +{ + public function testLocateATemplate() + { + $template = new TemplateReference('bundle', 'controller', 'name', 'format', 'engine'); + + $fileLocator = $this->getFileLocator(); + + $fileLocator + ->expects($this->once()) + ->method('locate') + ->with($template->getPath()) + ->will($this->returnValue('/path/to/template')) + ; + + $locator = new TemplateLocator($fileLocator); + + $this->assertEquals('/path/to/template', $locator->locate($template)); + } + + public function testThrowsExceptionWhenTemplateNotFound() + { + $template = new TemplateReference('bundle', 'controller', 'name', 'format', 'engine'); + + $fileLocator = $this->getFileLocator(); + + $errorMessage = 'FileLocator exception message'; + + $fileLocator + ->expects($this->once()) + ->method('locate') + ->will($this->throwException(new \InvalidArgumentException($errorMessage))) + ; + + $locator = new TemplateLocator($fileLocator); + + try { + $locator->locate($template); + $this->fail('->locate() should throw an exception when the file is not found.'); + } catch (\InvalidArgumentException $e) { + $this->assertContains( + $errorMessage, + $e->getMessage(), + 'TemplateLocator exception should propagate the FileLocator exception message' + ); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsAnExceptionWhenTemplateIsNotATemplateReferenceInterface() + { + $locator = new TemplateLocator($this->getFileLocator()); + $locator->locate('template'); + } + + protected function getFileLocator() + { + return $this + ->getMockBuilder('Symfony\Component\Config\FileLocator') + ->setMethods(array('locate')) + ->setConstructorArgs(array('/path/to/fallback')) + ->getMock() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php new file mode 100644 index 0000000..b5eeb90 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/PhpEngineTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\PhpEngine; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\Templating\TemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class PhpEngineTest extends TestCase +{ + public function testEvaluateAddsAppGlobal() + { + $container = $this->getContainer(); + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $engine = new PhpEngine(new TemplateNameParser(), $container, $loader, $app = new GlobalVariables($container)); + $globals = $engine->getGlobals(); + $this->assertSame($app, $globals['app']); + } + + public function testEvaluateWithoutAvailableRequest() + { + $container = new Container(); + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $engine = new PhpEngine(new TemplateNameParser(), $container, $loader, new GlobalVariables($container)); + + $container->set('request_stack', null); + + $globals = $engine->getGlobals(); + $this->assertEmpty($globals['app']->getRequest()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetInvalidHelper() + { + $container = $this->getContainer(); + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $engine = new PhpEngine(new TemplateNameParser(), $container, $loader); + + $engine->get('non-existing-helper'); + } + + /** + * Creates a Container with a Session-containing Request service. + * + * @return Container + */ + protected function getContainer() + { + $container = new Container(); + $session = new Session(new MockArraySessionStorage()); + $request = new Request(); + $stack = new RequestStack(); + $stack->push($request); + + $request->setSession($session); + $container->set('request_stack', $stack); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateFilenameParserTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateFilenameParserTest.php new file mode 100644 index 0000000..77dd269 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateFilenameParserTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; + +class TemplateFilenameParserTest extends TestCase +{ + protected $parser; + + protected function setUp() + { + $this->parser = new TemplateFilenameParser(); + } + + protected function tearDown() + { + $this->parser = null; + } + + /** + * @dataProvider getFilenameToTemplateProvider + */ + public function testParseFromFilename($file, $ref) + { + $template = $this->parser->parse($file); + + if ($ref === false) { + $this->assertFalse($template); + } else { + $this->assertEquals($template->getLogicalName(), $ref->getLogicalName()); + } + } + + public function getFilenameToTemplateProvider() + { + return array( + array('/path/to/section/name.format.engine', new TemplateReference('', '/path/to/section', 'name', 'format', 'engine')), + array('\\path\\to\\section\\name.format.engine', new TemplateReference('', '/path/to/section', 'name', 'format', 'engine')), + array('name.format.engine', new TemplateReference('', '', 'name', 'format', 'engine')), + array('name.format', false), + array('name', false), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php new file mode 100644 index 0000000..ca10c3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateNameParserTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\Templating\TemplateReference as BaseTemplateReference; + +class TemplateNameParserTest extends TestCase +{ + protected $parser; + + protected function setUp() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnCallback(function ($bundle) { + if (in_array($bundle, array('SensioFooBundle', 'SensioCmsFooBundle', 'FooBundle'))) { + return true; + } + + throw new \InvalidArgumentException(); + })) + ; + $this->parser = new TemplateNameParser($kernel); + } + + protected function tearDown() + { + $this->parser = null; + } + + /** + * @dataProvider getLogicalNameToTemplateProvider + */ + public function testParse($name, $ref) + { + $template = $this->parser->parse($name); + + $this->assertEquals($template->getLogicalName(), $ref->getLogicalName()); + $this->assertEquals($template->getLogicalName(), $name); + } + + public function getLogicalNameToTemplateProvider() + { + return array( + array('FooBundle:Post:index.html.php', new TemplateReference('FooBundle', 'Post', 'index', 'html', 'php')), + array('FooBundle:Post:index.html.twig', new TemplateReference('FooBundle', 'Post', 'index', 'html', 'twig')), + array('FooBundle:Post:index.xml.php', new TemplateReference('FooBundle', 'Post', 'index', 'xml', 'php')), + array('SensioFooBundle:Post:index.html.php', new TemplateReference('SensioFooBundle', 'Post', 'index', 'html', 'php')), + array('SensioCmsFooBundle:Post:index.html.php', new TemplateReference('SensioCmsFooBundle', 'Post', 'index', 'html', 'php')), + array(':Post:index.html.php', new TemplateReference('', 'Post', 'index', 'html', 'php')), + array('::index.html.php', new TemplateReference('', '', 'index', 'html', 'php')), + array('FooBundle:Post:foo.bar.index.html.php', new TemplateReference('FooBundle', 'Post', 'foo.bar.index', 'html', 'php')), + array('/path/to/section/name.php', new BaseTemplateReference('/path/to/section/name.php', 'php')), + array('name.twig', new BaseTemplateReference('name.twig', 'twig')), + array('name', new BaseTemplateReference('name')), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testParseValidNameWithNotFoundBundle() + { + $this->parser->parse('BarBundle:Post:index.html.php'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateReferenceTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateReferenceTest.php new file mode 100644 index 0000000..5ee2a8e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateReferenceTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; + +class TemplateReferenceTest extends TestCase +{ + public function testGetPathWorksWithNamespacedControllers() + { + $reference = new TemplateReference('AcmeBlogBundle', 'Admin\Post', 'index', 'html', 'twig'); + + $this->assertSame( + '@AcmeBlogBundle/Resources/views/Admin/Post/index.html.twig', + $reference->getPath() + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php new file mode 100644 index 0000000..c49010b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TemplateTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; + +class TemplateTest extends TestCase +{ + /** + * @dataProvider getTemplateToPathProvider + */ + public function testGetPathForTemplatesInABundle($template, $path) + { + if ($template->get('bundle')) { + $this->assertEquals($template->getPath(), $path); + } + } + + /** + * @dataProvider getTemplateToPathProvider + */ + public function testGetPathForTemplatesOutOfABundle($template, $path) + { + if (!$template->get('bundle')) { + $this->assertEquals($template->getPath(), $path); + } + } + + public function getTemplateToPathProvider() + { + return array( + array(new TemplateReference('FooBundle', 'Post', 'index', 'html', 'php'), '@FooBundle/Resources/views/Post/index.html.php'), + array(new TemplateReference('FooBundle', '', 'index', 'html', 'twig'), '@FooBundle/Resources/views/index.html.twig'), + array(new TemplateReference('', 'Post', 'index', 'html', 'php'), 'views/Post/index.html.php'), + array(new TemplateReference('', '', 'index', 'html', 'php'), 'views/index.html.php'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TimedPhpEngineTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TimedPhpEngineTest.php new file mode 100644 index 0000000..9411c1c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/TimedPhpEngineTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\TimedPhpEngine; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TimedPhpEngineTest extends TestCase +{ + public function testThatRenderLogsTime() + { + $container = $this->getContainer(); + $templateNameParser = $this->getTemplateNameParser(); + $globalVariables = $this->getGlobalVariables(); + $loader = $this->getLoader($this->getStorage()); + + $stopwatch = $this->getStopwatch(); + $stopwatchEvent = $this->getStopwatchEvent(); + + $stopwatch->expects($this->once()) + ->method('start') + ->with('template.php (index.php)', 'template') + ->will($this->returnValue($stopwatchEvent)); + + $stopwatchEvent->expects($this->once())->method('stop'); + + $engine = new TimedPhpEngine($templateNameParser, $container, $loader, $stopwatch, $globalVariables); + $engine->render('index.php'); + } + + /** + * @return Container + */ + private function getContainer() + { + return $this->getMock('Symfony\Component\DependencyInjection\Container'); + } + + /** + * @return \Symfony\Component\Templating\TemplateNameParserInterface + */ + private function getTemplateNameParser() + { + $templateReference = $this->getMock('Symfony\Component\Templating\TemplateReferenceInterface'); + $templateNameParser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + $templateNameParser->expects($this->any()) + ->method('parse') + ->will($this->returnValue($templateReference)); + + return $templateNameParser; + } + + /** + * @return GlobalVariables + */ + private function getGlobalVariables() + { + return $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return \Symfony\Component\Templating\Storage\StringStorage + */ + private function getStorage() + { + return $this->getMockBuilder('Symfony\Component\Templating\Storage\StringStorage') + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + } + + /** + * @param \Symfony\Component\Templating\Storage\StringStorage $storage + * + * @return \Symfony\Component\Templating\Loader\Loader + */ + private function getLoader($storage) + { + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $loader->expects($this->once()) + ->method('load') + ->will($this->returnValue($storage)); + + return $loader; + } + + /** + * @return \Symfony\Component\Stopwatch\StopwatchEvent + */ + private function getStopwatchEvent() + { + return $this->getMockBuilder('Symfony\Component\Stopwatch\StopwatchEvent') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return \Symfony\Component\Stopwatch\Stopwatch + */ + private function getStopwatch() + { + return $this->getMock('Symfony\Component\Stopwatch\Stopwatch'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/TestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/TestCase.php new file mode 100644 index 0000000..8e72430 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/TestCase.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests; + +class TestCase extends \PHPUnit_Framework_TestCase +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php new file mode 100644 index 0000000..420d2f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Translation; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor; +use Symfony\Component\Translation\MessageCatalogue; + +class PhpExtractorTest extends TestCase +{ + /** + * @dataProvider resourcesProvider + * + * @param array|string $resource + */ + public function testExtraction($resource) + { + // Arrange + $extractor = new PhpExtractor(); + $extractor->setPrefix('prefix'); + $catalogue = new MessageCatalogue('en'); + + // Act + $extractor->extract($resource, $catalogue); + + $expectedHeredoc = << array( + 'single-quoted key' => 'prefixsingle-quoted key', + 'double-quoted key' => 'prefixdouble-quoted key', + 'heredoc key' => 'prefixheredoc key', + 'nowdoc key' => 'prefixnowdoc key', + "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences", + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences', + 'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"', + $expectedHeredoc => 'prefix'.$expectedHeredoc, + $expectedNowdoc => 'prefix'.$expectedNowdoc, + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + )); + $actualCatalogue = $catalogue->all(); + + $this->assertEquals($expectedCatalogue, $actualCatalogue); + } + + public function resourcesProvider() + { + $directory = __DIR__.'/../Fixtures/Resources/views/'; + $splFiles = array(); + foreach (new \DirectoryIterator($directory) as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + if ('translation.html.php' === $fileInfo->getBasename()) { + $phpFile = $fileInfo->getPathname(); + } + $splFiles[] = $fileInfo->getFileInfo(); + } + + return array( + array($directory), + array($phpFile), + array(glob($directory.'*')), + array($splFiles), + array(new \ArrayObject(glob($directory.'*'))), + array(new \ArrayObject($splFiles)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php new file mode 100644 index 0000000..a2bcbcb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -0,0 +1,283 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Translation; + +use Symfony\Bundle\FrameworkBundle\Translation\Translator; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Translation\MessageSelector; + +class TranslatorTest extends \PHPUnit_Framework_TestCase +{ + protected $tmpDir; + + protected function setUp() + { + $this->tmpDir = sys_get_temp_dir().'/sf2_translation'; + $this->deleteTmpDir(); + } + + protected function tearDown() + { + $this->deleteTmpDir(); + } + + protected function deleteTmpDir() + { + if (!file_exists($dir = $this->tmpDir)) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } + + public function testTransWithoutCaching() + { + $translator = $this->getTranslator($this->getLoader()); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + } + + public function testTransWithCaching() + { + // prime the cache + $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + + // do it another time as the cache is primed now + $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $loader->expects($this->never())->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTransWithCachingWithInvalidLocale() + { + $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Tests\Translation\TranslatorWithInvalidLocale'); + + $translator->trans('foo'); + } + + public function testLoadResourcesWithoutCaching() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = array( + 'fr' => array( + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ), + ); + + $translator = $this->getTranslator($loader, array('resource_files' => $resourceFiles), 'yml'); + $translator->setLocale('fr'); + + $this->assertEquals('répertoire', $translator->trans('folder')); + } + + public function testGetDefaultLocale() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->once()) + ->method('getParameter') + ->with('kernel.default_locale') + ->will($this->returnValue('en')) + ; + + $translator = new Translator($container, new MessageSelector()); + + $this->assertSame('en', $translator->getLocale()); + } + + protected function getCatalogue($locale, $messages, $resources = array()) + { + $catalogue = new MessageCatalogue($locale); + foreach ($messages as $key => $translation) { + $catalogue->set($key, $translation); + } + foreach ($resources as $resource) { + $catalogue->addResource($resource); + } + + return $catalogue; + } + + protected function getLoader() + { + $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $loader + ->expects($this->at(0)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('fr', array( + 'foo' => 'foo (FR)', + )))) + ; + $loader + ->expects($this->at(1)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('en', array( + 'foo' => 'foo (EN)', + 'bar' => 'bar (EN)', + 'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)', + )))) + ; + $loader + ->expects($this->at(2)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('es', array( + 'foobar' => 'foobar (ES)', + )))) + ; + $loader + ->expects($this->at(3)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('pt-PT', array( + 'foobarfoo' => 'foobarfoo (PT-PT)', + )))) + ; + $loader + ->expects($this->at(4)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('pt_BR', array( + 'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)', + )))) + ; + $loader + ->expects($this->at(5)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('fr.UTF-8', array( + 'foobarbaz' => 'foobarbaz (fr.UTF-8)', + )))) + ; + $loader + ->expects($this->at(6)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('sr@latin', array( + 'foobarbax' => 'foobarbax (sr@latin)', + )))) + ; + + return $loader; + } + + protected function getContainer($loader) + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container + ->expects($this->any()) + ->method('get') + ->will($this->returnValue($loader)) + ; + + return $container; + } + + public function getTranslator($loader, $options = array(), $loaderFomat = 'loader', $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator') + { + $translator = $this->createTranslator($loader, $options, $translatorClass, $loaderFomat); + + if ('loader' === $loaderFomat) { + $translator->addResource('loader', 'foo', 'fr'); + $translator->addResource('loader', 'foo', 'en'); + $translator->addResource('loader', 'foo', 'es'); + $translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese + $translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese + $translator->addResource('loader', 'foo', 'fr.UTF-8'); + $translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian + } + + return $translator; + } + + public function testWarmup() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = array( + 'fr' => array( + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ), + ); + + // prime the cache + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml'); + $translator->setFallbackLocales(array('fr')); + $translator->warmup($this->tmpDir); + + $loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $loader + ->expects($this->never()) + ->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml'); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('fr')); + $this->assertEquals('répertoire', $translator->trans('folder')); + } + + private function createTranslator($loader, $options, $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $loaderFomat = 'loader') + { + return new $translatorClass( + $this->getContainer($loader), + new MessageSelector(), + array($loaderFomat => array($loaderFomat)), + $options + ); + } +} + +class TranslatorWithInvalidLocale extends Translator +{ + /** + * {@inheritdoc} + */ + public function getLocale() + { + return 'invalid locale'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php new file mode 100644 index 0000000..b73c9f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Tests/Validator/ConstraintValidatorFactoryTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Validator; + +use Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; + +class ConstraintValidatorFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testGetInstanceCreatesValidator() + { + $class = get_class($this->getMockForAbstractClass('Symfony\\Component\\Validator\\ConstraintValidator')); + + $constraint = $this->getMock('Symfony\\Component\\Validator\\Constraint'); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue($class)); + + $factory = new ConstraintValidatorFactory(new Container()); + $this->assertInstanceOf($class, $factory->getInstance($constraint)); + } + + public function testGetInstanceReturnsExistingValidator() + { + $factory = new ConstraintValidatorFactory(new Container()); + $v1 = $factory->getInstance(new BlankConstraint()); + $v2 = $factory->getInstance(new BlankConstraint()); + $this->assertSame($v1, $v2); + } + + public function testGetInstanceReturnsService() + { + $service = 'validator_constraint_service'; + $alias = 'validator_constraint_alias'; + $validator = $this->getMockForAbstractClass('Symfony\\Component\\Validator\\ConstraintValidator'); + + // mock ContainerBuilder b/c it implements TaggedContainerInterface + $container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerBuilder', array('get')); + $container + ->expects($this->once()) + ->method('get') + ->with($service) + ->will($this->returnValue($validator)); + + $constraint = $this->getMock('Symfony\\Component\\Validator\\Constraint'); + $constraint + ->expects($this->once()) + ->method('validatedBy') + ->will($this->returnValue($alias)); + + $factory = new ConstraintValidatorFactory($container, array('validator_constraint_alias' => 'validator_constraint_service')); + $this->assertSame($validator, $factory->getInstance($constraint)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php new file mode 100644 index 0000000..21406fe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Translation; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\Extractor\AbstractFileExtractor; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Extractor\ExtractorInterface; + +/** + * PhpExtractor extracts translation messages from a PHP template. + * + * @author Michel Salib + */ +class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface +{ + const MESSAGE_TOKEN = 300; + + /** + * Prefix for new found message. + * + * @var string + */ + private $prefix = ''; + + /** + * The sequence that captures translation messages. + * + * @var array + */ + protected $sequences = array( + array( + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ), + array( + '->', + 'transChoice', + '(', + self::MESSAGE_TOKEN, + ), + ); + + /** + * {@inheritdoc} + */ + public function extract($resource, MessageCatalogue $catalog) + { + $files = $this->extractFiles($resource); + foreach ($files as $file) { + $this->parseTokens(token_get_all(file_get_contents($file)), $catalog); + } + } + + /** + * {@inheritdoc} + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Normalizes a token. + * + * @param mixed $token + * + * @return string + */ + protected function normalizeToken($token) + { + if (is_array($token)) { + return $token[1]; + } + + return $token; + } + + /** + * Seeks to a non-whitespace token. + */ + private function seekToNextRelevantToken(\Iterator $tokenIterator) + { + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (!is_array($t) || ($t[0] !== T_WHITESPACE)) { + break; + } + } + } + + /** + * Extracts the message from the iterator while the tokens + * match allowed message tokens. + */ + private function getMessage(\Iterator $tokenIterator) + { + $message = ''; + $docToken = ''; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (!is_array($t)) { + break; + } + + switch ($t[0]) { + case T_START_HEREDOC: + $docToken = $t[1]; + break; + case T_ENCAPSED_AND_WHITESPACE: + case T_CONSTANT_ENCAPSED_STRING: + $message .= $t[1]; + break; + case T_END_HEREDOC: + return PhpStringTokenParser::parseDocString($docToken, $message); + default: + break 2; + } + } + + if ($message) { + $message = PhpStringTokenParser::parse($message); + } + + return $message; + } + + /** + * Extracts trans message from PHP tokens. + * + * @param array $tokens + * @param MessageCatalogue $catalog + */ + protected function parseTokens($tokens, MessageCatalogue $catalog) + { + $tokenIterator = new \ArrayIterator($tokens); + + for ($key = 0; $key < $tokenIterator->count(); ++$key) { + foreach ($this->sequences as $sequence) { + $message = ''; + $tokenIterator->seek($key); + + foreach ($sequence as $item) { + $this->seekToNextRelevantToken($tokenIterator); + + if ($this->normalizeToken($tokenIterator->current()) == $item) { + $tokenIterator->next(); + continue; + } elseif (self::MESSAGE_TOKEN == $item) { + $message = $this->getMessage($tokenIterator); + break; + } else { + break; + } + } + + if ($message) { + $catalog->set($message, $this->prefix.$message); + break; + } + } + } + } + + /** + * @param string $file + * + * @throws \InvalidArgumentException + * + * @return bool + */ + protected function canBeExtracted($file) + { + return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION); + } + + /** + * @param string|array $directory + * + * @return array + */ + protected function extractFromDirectory($directory) + { + $finder = new Finder(); + + return $finder->files()->name('*.php')->in($directory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php new file mode 100644 index 0000000..e950ac4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Translation; + +/* + * The following is derived from code at http://github.com/nikic/PHP-Parser + * + * Copyright (c) 2011 by Nikita Popov + * + * Some rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * The names of the contributors may not be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +class PhpStringTokenParser +{ + protected static $replacements = array( + '\\' => '\\', + '$' => '$', + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'f' => "\f", + 'v' => "\v", + 'e' => "\x1B", + ); + + /** + * Parses a string token. + * + * @param string $str String token content + * + * @return string The parsed string + */ + public static function parse($str) + { + $bLength = 0; + if ('b' === $str[0]) { + $bLength = 1; + } + + if ('\'' === $str[$bLength]) { + return str_replace( + array('\\\\', '\\\''), + array('\\', '\''), + substr($str, $bLength + 1, -1) + ); + } else { + return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"'); + } + } + + /** + * Parses escape sequences in strings (all string types apart from single quoted). + * + * @param string $str String without quotes + * @param null|string $quote Quote type + * + * @return string String with escape sequences parsed + */ + public static function parseEscapeSequences($str, $quote) + { + if (null !== $quote) { + $str = str_replace('\\'.$quote, $quote, $str); + } + + return preg_replace_callback( + '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', + array(__CLASS__, 'parseCallback'), + $str + ); + } + + private static function parseCallback($matches) + { + $str = $matches[1]; + + if (isset(self::$replacements[$str])) { + return self::$replacements[$str]; + } elseif ('x' === $str[0] || 'X' === $str[0]) { + return chr(hexdec($str)); + } else { + return chr(octdec($str)); + } + } + + /** + * Parses a constant doc string. + * + * @param string $startToken Doc string start token content (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Translation; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Loader\LoaderInterface; + +/** + * TranslationLoader loads translation messages from translation files. + * + * @author Michel Salib + */ +class TranslationLoader +{ + /** + * Loaders used for import. + * + * @var array + */ + private $loaders = array(); + + /** + * Adds a loader to the translation extractor. + * + * @param string $format The format of the loader + * @param LoaderInterface $loader + */ + public function addLoader($format, LoaderInterface $loader) + { + $this->loaders[$format] = $loader; + } + + /** + * Loads translation messages from a directory to the catalogue. + * + * @param string $directory the directory to look into + * @param MessageCatalogue $catalogue the catalogue + */ + public function loadMessages($directory, MessageCatalogue $catalogue) + { + if (!is_dir($directory)) { + return; + } + + foreach ($this->loaders as $format => $loader) { + // load any existing translation files + $finder = new Finder(); + $extension = $catalogue->getLocale().'.'.$format; + $files = $finder->files()->name('*.'.$extension)->in($directory); + foreach ($files as $file) { + $domain = substr($file->getFileName(), 0, -1 * strlen($extension) - 1); + $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain)); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php new file mode 100644 index 0000000..fba6d70 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Translation; + +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Translation\Translator as BaseTranslator; +use Symfony\Component\Translation\MessageSelector; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Translator. + * + * @author Fabien Potencier + */ +class Translator extends BaseTranslator implements WarmableInterface +{ + protected $container; + protected $loaderIds; + + protected $options = array( + 'cache_dir' => null, + 'debug' => false, + 'resource_files' => array(), + ); + + /** + * @var array + */ + private $resourceLocales; + + /** + * Constructor. + * + * Available options: + * + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * * resource_files: List of translation resources available grouped by locale. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param MessageSelector $selector The message selector for pluralization + * @param array $loaderIds An array of loader Ids + * @param array $options An array of options + * + * @throws \InvalidArgumentException + */ + public function __construct(ContainerInterface $container, MessageSelector $selector, $loaderIds = array(), array $options = array()) + { + $this->container = $container; + $this->loaderIds = $loaderIds; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new \InvalidArgumentException(sprintf('The Translator does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + $this->resourceLocales = array_keys($this->options['resource_files']); + if (null !== $this->options['cache_dir'] && $this->options['debug']) { + $this->loadResources(); + } + + parent::__construct($container->getParameter('kernel.default_locale'), $selector, $this->options['cache_dir'], $this->options['debug']); + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + // skip warmUp when translator doesn't use cache + if (null === $this->options['cache_dir']) { + return; + } + + $locales = array_merge($this->getFallbackLocales(), array($this->getLocale()), $this->resourceLocales); + foreach (array_unique($locales) as $locale) { + // reset catalogue in case it's already loaded during the dump of the other locales. + if (isset($this->catalogues[$locale])) { + unset($this->catalogues[$locale]); + } + + $this->loadCatalogue($locale); + } + } + + /** + * {@inheritdoc} + */ + protected function initializeCatalogue($locale) + { + $this->initialize(); + parent::initializeCatalogue($locale); + } + + protected function initialize() + { + $this->loadResources(); + foreach ($this->loaderIds as $id => $aliases) { + foreach ($aliases as $alias) { + $this->addLoader($alias, $this->container->get($id)); + } + } + } + + private function loadResources() + { + foreach ($this->options['resource_files'] as $locale => $files) { + foreach ($files as $key => $file) { + // filename is domain.locale.format + list($domain, $locale, $format) = explode('.', basename($file), 3); + $this->addResource($format, $file, $locale, $domain); + unset($this->options['resource_files'][$locale][$key]); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php new file mode 100644 index 0000000..66f0d01 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Validator/ConstraintValidatorFactory.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Validator; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; +use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * Uses a service container to create constraint validators. + * + * A constraint validator should be tagged as "validator.constraint_validator" + * in the service container and include an "alias" attribute: + * + * + * + * + * + * + * A constraint may then return this alias in its validatedBy() method: + * + * public function validatedBy() + * { + * return 'some_alias'; + * } + * + * @author Kris Wallsmith + */ +class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface +{ + protected $container; + protected $validators; + + /** + * Constructor. + * + * @param ContainerInterface $container The service container + * @param array $validators An array of validators + */ + public function __construct(ContainerInterface $container, array $validators = array()) + { + $this->container = $container; + $this->validators = $validators; + } + + /** + * Returns the validator for the supplied constraint. + * + * @param Constraint $constraint A constraint + * + * @return ConstraintValidatorInterface A validator for the supplied constraint + * + * @throws UnexpectedTypeException When the validator is not an instance of ConstraintValidatorInterface + */ + public function getInstance(Constraint $constraint) + { + $name = $constraint->validatedBy(); + + if (!isset($this->validators[$name])) { + $this->validators[$name] = new $name(); + } elseif (is_string($this->validators[$name])) { + $this->validators[$name] = $this->container->get($this->validators[$name]); + } + + if (!$this->validators[$name] instanceof ConstraintValidatorInterface) { + throw new UnexpectedTypeException($this->validators[$name], 'Symfony\Component\Validator\ConstraintValidatorInterface'); + } + + return $this->validators[$name]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/composer.json b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/composer.json new file mode 100644 index 0000000..0b59f5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -0,0 +1,75 @@ +{ + "name": "symfony/framework-bundle", + "type": "symfony-bundle", + "description": "Symfony FrameworkBundle", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/asset": "~2.8|~3.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/routing": "~3.0", + "symfony/security-core": "~2.8|~3.0", + "symfony/security-csrf": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "doctrine/cache": "~1.0", + "doctrine/annotations": "~1.0" + }, + "require-dev": { + "symfony/browser-kit": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/security": "~2.8|~3.0", + "symfony/form": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/validator": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0", + "symfony/property-info": "~2.8|~3.0", + "phpdocumentor/reflection": "^1.0.7" + }, + "suggest": { + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/yaml": "For using the debug:config and lint:yaml commands", + "symfony/property-info": "For using the property_info service", + "symfony/process": "For using the server:run, server:start, server:stop, and server:status commands" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist new file mode 100644 index 0000000..1d25eeb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md new file mode 100644 index 0000000..3d2cf37 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -0,0 +1,117 @@ +CHANGELOG +========= + +3.0.0 +----- + + * Removed the `security.context` service. + +2.8.0 +----- + + * deprecated the `key` setting of `anonymous`, `remember_me` and `http_digest` + in favor of the `secret` setting. + * deprecated the `intention` firewall listener setting in favor of the `csrf_token_id`. + +2.6.0 +----- + + * Added the possibility to override the default success/failure handler + to get the provider key and the options injected + * Deprecated the `security.context` service for the `security.token_storage` and + `security.authorization_checker` services. + +2.4.0 +----- + + * Added 'host' option to firewall configuration + * Added 'csrf_token_generator' and 'csrf_token_id' options to firewall logout + listener configuration to supersede/alias 'csrf_provider' and 'intention' + respectively + * Moved 'security.secure_random' service configuration to FrameworkBundle + +2.3.0 +----- + + * allowed for multiple IP address in security access_control rules + +2.2.0 +----- + + * Added PBKDF2 Password encoder + * Added BCrypt password encoder + +2.1.0 +----- + + * [BC BREAK] The custom factories for the firewall configuration are now + registered during the build method of bundles instead of being registered + by the end-user (you need to remove the 'factories' keys in your security + configuration). + + * [BC BREAK] The Firewall listener is now registered after the Router one. This + means that specific Firewall URLs (like /login_check and /logout must now + have proper route defined in your routing configuration) + + * [BC BREAK] refactored the user provider configuration. The configuration + changed for the chain provider and the memory provider: + + Before: + + ``` yaml + security: + providers: + my_chain_provider: + providers: [my_memory_provider, my_doctrine_provider] + my_memory_provider: + users: + toto: { password: foobar, roles: [ROLE_USER] } + foo: { password: bar, roles: [ROLE_USER, ROLE_ADMIN] } + ``` + + After: + + ``` yaml + security: + providers: + my_chain_provider: + chain: + providers: [my_memory_provider, my_doctrine_provider] + my_memory_provider: + memory: + users: + toto: { password: foobar, roles: [ROLE_USER] } + foo: { password: bar, roles: [ROLE_USER, ROLE_ADMIN] } + ``` + + * [BC BREAK] Method `equals` was removed from `UserInterface` to its own new + `EquatableInterface`. The user class can now implement this interface to override + the default implementation of users equality test. + + * added a validator for the user password + * added 'erase_credentials' as a configuration key (true by default) + * added new events: `security.authentication.success` and `security.authentication.failure` + fired on authentication success/failure, regardless of authentication method, + events are defined in new event class: `Symfony\Component\Security\Core\AuthenticationEvents`. + + * Added optional CSRF protection to LogoutListener: + + ``` yaml + security: + firewalls: + default: + logout: + path: /logout_path + target: / + csrf_parameter: _csrf_token # Optional (defaults to "_csrf_token") + csrf_provider: security.csrf.token_generator # Required to enable protection + intention: logout # Optional (defaults to "logout") + ``` + + If the LogoutListener has CSRF protection enabled but cannot validate a token, + then a LogoutException will be thrown. + + * Added `logout_url` templating helper and Twig extension, which may be used to + generate logout URL's within templates. The security firewall's config key + must be specified. If a firewall's logout listener has CSRF protection + enabled, a token will be automatically added to the generated URL. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php new file mode 100644 index 0000000..14271dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/InitAclCommand.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Doctrine\DBAL\Schema\SchemaException; + +/** + * Installs the tables required by the ACL system. + * + * @author Johannes M. Schmitt + */ +class InitAclCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (!$this->getContainer()->has('security.acl.dbal.connection')) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('init:acl') + ->setDescription('Mounts ACL tables in the database') + ->setHelp(<<%command.name% command mounts ACL tables in the database. + + php %command.full_name% + +The name of the DBAL connection must be configured in your app/config/security.yml configuration file in the security.acl.connection variable. + + security: + acl: + connection: default +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $container = $this->getContainer(); + + $connection = $container->get('security.acl.dbal.connection'); + $schema = $container->get('security.acl.dbal.schema'); + + try { + $schema->addToSchema($connection->getSchemaManager()->createSchema()); + } catch (SchemaException $e) { + $output->writeln('Aborting: '.$e->getMessage()); + + return 1; + } + + foreach ($schema->toSql($connection->getDatabasePlatform()) as $sql) { + $connection->exec($sql); + } + + $output->writeln('ACL tables have been initialized successfully.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php new file mode 100644 index 0000000..ba34782 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/SetAclCommand.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Security\Acl\Domain\ObjectIdentity; +use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; +use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; +use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException; +use Symfony\Component\Security\Acl\Permission\MaskBuilder; +use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface; + +/** + * Sets ACL for objects. + * + * @author Kévin Dunglas + */ +class SetAclCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + public function isEnabled() + { + if (!$this->getContainer()->has('security.acl.provider')) { + return false; + } + + $provider = $this->getContainer()->get('security.acl.provider'); + if (!$provider instanceof MutableAclProviderInterface) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('acl:set') + ->setDescription('Sets ACL for objects') + ->setHelp(<<%command.name% command sets ACL. +The ACL system must have been initialized with the init:acl command. + +To set VIEW and EDIT permissions for the user kevin on the instance of +Acme\MyClass having the identifier 42: + + php %command.full_name% --user=Symfony/Component/Security/Core/User/User:kevin VIEW EDIT Acme/MyClass:42 + +Note that you can use / instead of \\ for the namespace delimiter to avoid any +problem. + +To set permissions for a role, use the --role option: + + php %command.full_name% --role=ROLE_USER VIEW Acme/MyClass:1936 + +To set permissions at the class scope, use the --class-scope option: + + php %command.full_name% --class-scope --user=Symfony/Component/Security/Core/User/User:anne OWNER Acme/MyClass:42 + +EOF + ) + ->addArgument('arguments', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of permissions and object identities (class name and ID separated by a column)') + ->addOption('user', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of security identities') + ->addOption('role', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'A list of roles') + ->addOption('class-scope', null, InputOption::VALUE_NONE, 'Use class-scope entries') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Parse arguments + $objectIdentities = array(); + $maskBuilder = $this->getMaskBuilder(); + foreach ($input->getArgument('arguments') as $argument) { + $data = explode(':', $argument, 2); + + if (count($data) > 1) { + $objectIdentities[] = new ObjectIdentity($data[1], strtr($data[0], '/', '\\')); + } else { + $maskBuilder->add($data[0]); + } + } + + // Build permissions mask + $mask = $maskBuilder->get(); + + $userOption = $input->getOption('user'); + $roleOption = $input->getOption('role'); + $classScopeOption = $input->getOption('class-scope'); + + if (empty($userOption) && empty($roleOption)) { + throw new \InvalidArgumentException('A Role or a User must be specified.'); + } + + // Create security identities + $securityIdentities = array(); + + if ($userOption) { + foreach ($userOption as $user) { + $data = explode(':', $user, 2); + + if (count($data) === 1) { + throw new \InvalidArgumentException('The user must follow the format "Acme/MyUser:username".'); + } + + $securityIdentities[] = new UserSecurityIdentity($data[1], strtr($data[0], '/', '\\')); + } + } + + if ($roleOption) { + foreach ($roleOption as $role) { + $securityIdentities[] = new RoleSecurityIdentity($role); + } + } + + /** @var $container \Symfony\Component\DependencyInjection\ContainerInterface */ + $container = $this->getContainer(); + /** @var $aclProvider MutableAclProviderInterface */ + $aclProvider = $container->get('security.acl.provider'); + + // Sets ACL + foreach ($objectIdentities as $objectIdentity) { + // Creates a new ACL if it does not already exist + try { + $aclProvider->createAcl($objectIdentity); + } catch (AclAlreadyExistsException $e) { + } + + $acl = $aclProvider->findAcl($objectIdentity, $securityIdentities); + + foreach ($securityIdentities as $securityIdentity) { + if ($classScopeOption) { + $acl->insertClassAce($securityIdentity, $mask); + } else { + $acl->insertObjectAce($securityIdentity, $mask); + } + } + + $aclProvider->updateAcl($acl); + } + } + + /** + * Gets the mask builder. + * + * @return MaskBuilder + */ + protected function getMaskBuilder() + { + return new MaskBuilder(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php new file mode 100644 index 0000000..5d1a245 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Command/UserPasswordEncoderCommand.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; + +/** + * Encode a user's password. + * + * @author Sarah Khalil + */ +class UserPasswordEncoderCommand extends ContainerAwareCommand +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('security:encode-password') + ->setDescription('Encodes a password.') + ->addArgument('password', InputArgument::OPTIONAL, 'The plain password to encode.') + ->addArgument('user-class', InputArgument::OPTIONAL, 'The User entity class path associated with the encoder used to encode the password.', 'Symfony\Component\Security\Core\User\User') + ->addOption('empty-salt', null, InputOption::VALUE_NONE, 'Do not generate a salt or let the encoder generate one.') + ->setHelp(<<%command.name% command encodes passwords according to your +security configuration. This command is mainly used to generate passwords for +the in_memory user provider type and for changing passwords +in the database while developing the application. + +Suppose that you have the following security configuration in your application: + + +# app/config/security.yml +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + AppBundle\Entity\User: bcrypt + + +If you execute the command non-interactively, the default Symfony User class +is used and a random salt is generated to encode the password: + + php %command.full_name% --no-interaction [password] + +Pass the full user class path as the second argument to encode passwords for +your own entities: + + php %command.full_name% --no-interaction [password] AppBundle\Entity\User + +Executing the command interactively allows you to generate a random salt for +encoding the password: + + php %command.full_name% [password] AppBundle\Entity\User + +In case your encoder doesn't require a salt, add the empty-salt option: + + php %command.full_name% --empty-salt [password] AppBundle\Entity\User + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $input->isInteractive() ? $io->title('Symfony Password Encoder Utility') : $io->newLine(); + + $password = $input->getArgument('password'); + $userClass = $input->getArgument('user-class'); + $emptySalt = $input->getOption('empty-salt'); + + $encoder = $this->getContainer()->get('security.encoder_factory')->getEncoder($userClass); + $bcryptWithoutEmptySalt = !$emptySalt && $encoder instanceof BCryptPasswordEncoder; + + if ($bcryptWithoutEmptySalt) { + $emptySalt = true; + } + + if (!$password) { + if (!$input->isInteractive()) { + $io->error('The password must not be empty.'); + + return 1; + } + $passwordQuestion = $this->createPasswordQuestion($input, $output); + $password = $io->askQuestion($passwordQuestion); + } + + $salt = null; + + if ($input->isInteractive() && !$emptySalt) { + $emptySalt = true; + + $io->note('The command will take care of generating a salt for you. Be aware that some encoders advise to let them generate their own salt. If you\'re using one of those encoders, please answer \'no\' to the question below. '.PHP_EOL.'Provide the \'empty-salt\' option in order to let the encoder handle the generation itself.'); + + if ($io->confirm('Confirm salt generation ?')) { + $salt = $this->generateSalt(); + $emptySalt = false; + } + } elseif (!$emptySalt) { + $salt = $this->generateSalt(); + } + + $encodedPassword = $encoder->encodePassword($password, $salt); + + $rows = array( + array('Encoder used', get_class($encoder)), + array('Encoded password', $encodedPassword), + ); + if (!$emptySalt) { + $rows[] = array('Generated salt', $salt); + } + $io->table(array('Key', 'Value'), $rows); + + if (!$emptySalt) { + $io->note(sprintf('Make sure that your salt storage field fits the salt length: %s chars', strlen($salt))); + } elseif ($bcryptWithoutEmptySalt) { + $io->note('Bcrypt encoder used: the encoder generated its own built-in salt.'); + } + + $io->success('Password encoding succeeded'); + } + + /** + * Create the password question to ask the user for the password to be encoded. + * + * @return Question + */ + private function createPasswordQuestion() + { + $passwordQuestion = new Question('Type in your password to be encoded'); + + return $passwordQuestion->setValidator(function ($value) { + if ('' === trim($value)) { + throw new \Exception('The password must not be empty.'); + } + + return $value; + })->setHidden(true)->setMaxAttempts(20); + } + + private function generateSalt() + { + return base64_encode(random_bytes(30)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php new file mode 100644 index 0000000..7b3e111 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DataCollector; + +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\Security\Core\Role\RoleInterface; +use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; + +/** + * SecurityDataCollector. + * + * @author Fabien Potencier + */ +class SecurityDataCollector extends DataCollector +{ + private $tokenStorage; + private $roleHierarchy; + private $logoutUrlGenerator; + + /** + * Constructor. + * + * @param TokenStorageInterface|null $tokenStorage + * @param RoleHierarchyInterface|null $roleHierarchy + * @param LogoutUrlGenerator|null $logoutUrlGenerator + */ + public function __construct(TokenStorageInterface $tokenStorage = null, RoleHierarchyInterface $roleHierarchy = null, LogoutUrlGenerator $logoutUrlGenerator = null) + { + $this->tokenStorage = $tokenStorage; + $this->roleHierarchy = $roleHierarchy; + $this->logoutUrlGenerator = $logoutUrlGenerator; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (null === $this->tokenStorage) { + $this->data = array( + 'enabled' => false, + 'authenticated' => false, + 'token_class' => null, + 'logout_url' => null, + 'user' => '', + 'roles' => array(), + 'inherited_roles' => array(), + 'supports_role_hierarchy' => null !== $this->roleHierarchy, + ); + } elseif (null === $token = $this->tokenStorage->getToken()) { + $this->data = array( + 'enabled' => true, + 'authenticated' => false, + 'token_class' => null, + 'logout_url' => null, + 'user' => '', + 'roles' => array(), + 'inherited_roles' => array(), + 'supports_role_hierarchy' => null !== $this->roleHierarchy, + ); + } else { + $inheritedRoles = array(); + $assignedRoles = $token->getRoles(); + + if (null !== $this->roleHierarchy) { + $allRoles = $this->roleHierarchy->getReachableRoles($assignedRoles); + foreach ($allRoles as $role) { + if (!in_array($role, $assignedRoles, true)) { + $inheritedRoles[] = $role; + } + } + } + + $logoutUrl = null; + try { + if (null !== $this->logoutUrlGenerator) { + $logoutUrl = $this->logoutUrlGenerator->getLogoutPath(); + } + } catch(\Exception $e) { + // fail silently when the logout URL cannot be generated + } + + $this->data = array( + 'enabled' => true, + 'authenticated' => $token->isAuthenticated(), + 'token_class' => get_class($token), + 'logout_url' => $logoutUrl, + 'user' => $token->getUsername(), + 'roles' => array_map(function (RoleInterface $role) { return $role->getRole();}, $assignedRoles), + 'inherited_roles' => array_map(function (RoleInterface $role) { return $role->getRole(); }, $inheritedRoles), + 'supports_role_hierarchy' => null !== $this->roleHierarchy, + ); + } + } + + /** + * Checks if security is enabled. + * + * @return bool true if security is enabled, false otherwise + */ + public function isEnabled() + { + return $this->data['enabled']; + } + + /** + * Gets the user. + * + * @return string The user + */ + public function getUser() + { + return $this->data['user']; + } + + /** + * Gets the roles of the user. + * + * @return array The roles + */ + public function getRoles() + { + return $this->data['roles']; + } + + /** + * Gets the inherited roles of the user. + * + * @return array The inherited roles + */ + public function getInheritedRoles() + { + return $this->data['inherited_roles']; + } + + /** + * Checks if the data contains information about inherited roles. Still the inherited + * roles can be an empty array. + * + * @return bool true if the profile was contains inherited role information. + */ + public function supportsRoleHierarchy() + { + return $this->data['supports_role_hierarchy']; + } + + /** + * Checks if the user is authenticated or not. + * + * @return bool true if the user is authenticated, false otherwise + */ + public function isAuthenticated() + { + return $this->data['authenticated']; + } + + /** + * Get the class name of the security token. + * + * @return string The token + */ + public function getTokenClass() + { + return $this->data['token_class']; + } + + /** + * Get the provider key (i.e. the name of the active firewall). + * + * @return string The provider key + */ + public function getLogoutUrl() + { + return $this->data['logout_url']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'security'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php new file mode 100644 index 0000000..992d5a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * Adds all configured security voters to the access decision manager. + * + * @author Johannes M. Schmitt + */ +class AddSecurityVotersPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('security.access.decision_manager')) { + return; + } + + $voters = new \SplPriorityQueue(); + foreach ($container->findTaggedServiceIds('security.voter') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $voters->insert(new Reference($id), $priority); + } + + $voters = iterator_to_array($voters); + ksort($voters); + + if (!$voters) { + throw new LogicException('No security voters found. You need to tag at least one with "security.voter"'); + } + + $container->getDefinition('security.access.decision_manager')->addMethodCall('setVoters', array(array_values($voters))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php new file mode 100644 index 0000000..bf828ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -0,0 +1,421 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory; +use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; + +/** + * This class contains the configuration information. + * + * This information is for the following tags: + * + * * security.config + * * security.acl + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * @author Johannes M. Schmitt + */ +class MainConfiguration implements ConfigurationInterface +{ + private $factories; + private $userProviderFactories; + + /** + * Constructor. + * + * @param array $factories + * @param array $userProviderFactories + */ + public function __construct(array $factories, array $userProviderFactories) + { + $this->factories = $factories; + $this->userProviderFactories = $userProviderFactories; + } + + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $tb = new TreeBuilder(); + $rootNode = $tb->root('security'); + + $rootNode + ->children() + ->scalarNode('access_denied_url')->defaultNull()->example('/foo/error403')->end() + ->enumNode('session_fixation_strategy') + ->values(array(SessionAuthenticationStrategy::NONE, SessionAuthenticationStrategy::MIGRATE, SessionAuthenticationStrategy::INVALIDATE)) + ->defaultValue(SessionAuthenticationStrategy::MIGRATE) + ->end() + ->booleanNode('hide_user_not_found')->defaultTrue()->end() + ->booleanNode('always_authenticate_before_granting')->defaultFalse()->end() + ->booleanNode('erase_credentials')->defaultTrue()->end() + ->arrayNode('access_decision_manager') + ->addDefaultsIfNotSet() + ->children() + ->enumNode('strategy') + ->values(array(AccessDecisionManager::STRATEGY_AFFIRMATIVE, AccessDecisionManager::STRATEGY_CONSENSUS, AccessDecisionManager::STRATEGY_UNANIMOUS)) + ->defaultValue(AccessDecisionManager::STRATEGY_AFFIRMATIVE) + ->end() + ->booleanNode('allow_if_all_abstain')->defaultFalse()->end() + ->booleanNode('allow_if_equal_granted_denied')->defaultTrue()->end() + ->end() + ->end() + ->end() + ; + + $this->addAclSection($rootNode); + $this->addEncodersSection($rootNode); + $this->addProvidersSection($rootNode); + $this->addFirewallsSection($rootNode, $this->factories); + $this->addAccessControlSection($rootNode); + $this->addRoleHierarchySection($rootNode); + + return $tb; + } + + private function addAclSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('acl') + ->children() + ->scalarNode('connection') + ->defaultNull() + ->info('any name configured in doctrine.dbal section') + ->end() + ->arrayNode('cache') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('id')->end() + ->scalarNode('prefix')->defaultValue('sf2_acl_')->end() + ->end() + ->end() + ->scalarNode('provider')->end() + ->arrayNode('tables') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('class')->defaultValue('acl_classes')->end() + ->scalarNode('entry')->defaultValue('acl_entries')->end() + ->scalarNode('object_identity')->defaultValue('acl_object_identities')->end() + ->scalarNode('object_identity_ancestors')->defaultValue('acl_object_identity_ancestors')->end() + ->scalarNode('security_identity')->defaultValue('acl_security_identities')->end() + ->end() + ->end() + ->arrayNode('voter') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('allow_if_object_identity_unavailable')->defaultTrue()->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRoleHierarchySection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('role', 'role_hierarchy') + ->children() + ->arrayNode('role_hierarchy') + ->useAttributeAsKey('id') + ->prototype('array') + ->performNoDeepMerging() + ->beforeNormalization()->ifString()->then(function ($v) { return array('value' => $v); })->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && isset($v['value']); }) + ->then(function ($v) { return preg_split('/\s*,\s*/', $v['value']); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ; + } + + private function addAccessControlSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('rule', 'access_control') + ->children() + ->arrayNode('access_control') + ->cannotBeOverwritten() + ->prototype('array') + ->fixXmlConfig('ip') + ->fixXmlConfig('method') + ->children() + ->scalarNode('requires_channel')->defaultNull()->end() + ->scalarNode('path') + ->defaultNull() + ->info('use the urldecoded format') + ->example('^/path to resource/') + ->end() + ->scalarNode('host')->defaultNull()->end() + ->arrayNode('ips') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->arrayNode('methods') + ->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->scalarNode('allow_if')->defaultNull()->end() + ->end() + ->fixXmlConfig('role') + ->children() + ->arrayNode('roles') + ->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $factories) + { + $firewallNodeBuilder = $rootNode + ->fixXmlConfig('firewall') + ->children() + ->arrayNode('firewalls') + ->isRequired() + ->requiresAtLeastOneElement() + ->disallowNewKeysInSubsequentConfigs() + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ; + + $firewallNodeBuilder + ->scalarNode('pattern')->end() + ->scalarNode('host')->end() + ->arrayNode('methods') + ->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->booleanNode('security')->defaultTrue()->end() + ->scalarNode('user_checker') + ->defaultValue('security.user_checker') + ->treatNullLike('security.user_checker') + ->info('The UserChecker to use when authenticating users in this firewall.') + ->end() + ->scalarNode('request_matcher')->end() + ->scalarNode('access_denied_url')->end() + ->scalarNode('access_denied_handler')->end() + ->scalarNode('entry_point')->end() + ->scalarNode('provider')->end() + ->booleanNode('stateless')->defaultFalse()->end() + ->scalarNode('context')->cannotBeEmpty()->end() + ->arrayNode('logout') + ->treatTrueLike(array()) + ->canBeUnset() + ->children() + ->scalarNode('csrf_parameter')->defaultValue('_csrf_token')->end() + ->scalarNode('csrf_token_generator')->cannotBeEmpty()->end() + ->scalarNode('csrf_token_id')->defaultValue('logout')->end() + ->scalarNode('path')->defaultValue('/logout')->end() + ->scalarNode('target')->defaultValue('/')->end() + ->scalarNode('success_handler')->end() + ->booleanNode('invalidate_session')->defaultTrue()->end() + ->end() + ->fixXmlConfig('delete_cookie') + ->children() + ->arrayNode('delete_cookies') + ->beforeNormalization() + ->ifTrue(function ($v) { return is_array($v) && is_int(key($v)); }) + ->then(function ($v) { return array_map(function ($v) { return array('name' => $v); }, $v); }) + ->end() + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('path')->defaultNull()->end() + ->scalarNode('domain')->defaultNull()->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('handler') + ->children() + ->arrayNode('handlers') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->arrayNode('anonymous') + ->canBeUnset() + ->children() + ->scalarNode('secret')->defaultValue(uniqid('', true))->end() + ->end() + ->end() + ->arrayNode('switch_user') + ->canBeUnset() + ->children() + ->scalarNode('provider')->end() + ->scalarNode('parameter')->defaultValue('_switch_user')->end() + ->scalarNode('role')->defaultValue('ROLE_ALLOWED_TO_SWITCH')->end() + ->end() + ->end() + ; + + $abstractFactoryKeys = array(); + foreach ($factories as $factoriesAtPosition) { + foreach ($factoriesAtPosition as $factory) { + $name = str_replace('-', '_', $factory->getKey()); + $factoryNode = $firewallNodeBuilder->arrayNode($name) + ->canBeUnset() + ; + + if ($factory instanceof AbstractFactory) { + $abstractFactoryKeys[] = $name; + } + + $factory->addConfiguration($factoryNode); + } + } + + // check for unreachable check paths + $firewallNodeBuilder + ->end() + ->validate() + ->ifTrue(function ($v) { + return true === $v['security'] && isset($v['pattern']) && !isset($v['request_matcher']); + }) + ->then(function ($firewall) use ($abstractFactoryKeys) { + foreach ($abstractFactoryKeys as $k) { + if (!isset($firewall[$k]['check_path'])) { + continue; + } + + if (false !== strpos($firewall[$k]['check_path'], '/') && !preg_match('#'.$firewall['pattern'].'#', $firewall[$k]['check_path'])) { + throw new \LogicException(sprintf('The check_path "%s" for login method "%s" is not matched by the firewall pattern "%s".', $firewall[$k]['check_path'], $k, $firewall['pattern'])); + } + } + + return $firewall; + }) + ->end() + ; + } + + private function addProvidersSection(ArrayNodeDefinition $rootNode) + { + $providerNodeBuilder = $rootNode + ->fixXmlConfig('provider') + ->children() + ->arrayNode('providers') + ->example(array( + 'my_memory_provider' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'), + 'bar' => array('password' => 'bar', 'roles' => '[ROLE_USER, ROLE_ADMIN]'), + ), + ), + ), + 'my_entity_provider' => array('entity' => array('class' => 'SecurityBundle:User', 'property' => 'username')), + )) + ->isRequired() + ->requiresAtLeastOneElement() + ->useAttributeAsKey('name') + ->prototype('array') + ; + + $providerNodeBuilder + ->children() + ->scalarNode('id')->end() + ->arrayNode('chain') + ->fixXmlConfig('provider') + ->children() + ->arrayNode('providers') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return preg_split('/\s*,\s*/', $v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ; + + foreach ($this->userProviderFactories as $factory) { + $name = str_replace('-', '_', $factory->getKey()); + $factoryNode = $providerNodeBuilder->children()->arrayNode($name)->canBeUnset(); + + $factory->addConfiguration($factoryNode); + } + + $providerNodeBuilder + ->validate() + ->ifTrue(function ($v) {return count($v) > 1;}) + ->thenInvalid('You cannot set multiple provider types for the same provider') + ->end() + ->validate() + ->ifTrue(function ($v) {return count($v) === 0;}) + ->thenInvalid('You must set a provider definition for the provider.') + ->end() + ; + } + + private function addEncodersSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('encoder') + ->children() + ->arrayNode('encoders') + ->example(array( + 'Acme\DemoBundle\Entity\User1' => 'sha512', + 'Acme\DemoBundle\Entity\User2' => array( + 'algorithm' => 'sha512', + 'encode_as_base64' => 'true', + 'iterations' => 5000, + ), + )) + ->requiresAtLeastOneElement() + ->useAttributeAsKey('class') + ->prototype('array') + ->canBeUnset() + ->performNoDeepMerging() + ->beforeNormalization()->ifString()->then(function ($v) { return array('algorithm' => $v); })->end() + ->children() + ->scalarNode('algorithm')->cannotBeEmpty()->end() + ->scalarNode('hash_algorithm')->info('Name of hashing algorithm for PBKDF2 (i.e. sha256, sha512, etc..) See hash_algos() for a list of supported algorithms.')->defaultValue('sha512')->end() + ->scalarNode('key_length')->defaultValue(40)->end() + ->booleanNode('ignore_case')->defaultFalse()->end() + ->booleanNode('encode_as_base64')->defaultTrue()->end() + ->scalarNode('iterations')->defaultValue(5000)->end() + ->integerNode('cost') + ->min(4) + ->max(31) + ->defaultValue(13) + ->end() + ->scalarNode('id')->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php new file mode 100644 index 0000000..cdb0a96 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -0,0 +1,216 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * AbstractFactory is the base class for all classes inheriting from + * AbstractAuthenticationListener. + * + * @author Fabien Potencier + * @author Lukas Kahwe Smith + * @author Johannes M. Schmitt + */ +abstract class AbstractFactory implements SecurityFactoryInterface +{ + protected $options = array( + 'check_path' => '/login_check', + 'use_forward' => false, + 'require_previous_session' => true, + ); + + protected $defaultSuccessHandlerOptions = array( + 'always_use_default_target_path' => false, + 'default_target_path' => '/', + 'login_path' => '/login', + 'target_path_parameter' => '_target_path', + 'use_referer' => false, + ); + + protected $defaultFailureHandlerOptions = array( + 'failure_path' => null, + 'failure_forward' => false, + 'login_path' => '/login', + 'failure_path_parameter' => '_failure_path', + ); + + public function create(ContainerBuilder $container, $id, $config, $userProviderId, $defaultEntryPointId) + { + // authentication provider + $authProviderId = $this->createAuthProvider($container, $id, $config, $userProviderId); + + // authentication listener + $listenerId = $this->createListener($container, $id, $config, $userProviderId); + + // add remember-me aware tag if requested + if ($this->isRememberMeAware($config)) { + $container + ->getDefinition($listenerId) + ->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProviderId)) + ; + } + + // create entry point if applicable (optional) + $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPointId); + + return array($authProviderId, $listenerId, $entryPointId); + } + + public function addConfiguration(NodeDefinition $node) + { + $builder = $node->children(); + + $builder + ->scalarNode('provider')->end() + ->booleanNode('remember_me')->defaultTrue()->end() + ->scalarNode('success_handler')->end() + ->scalarNode('failure_handler')->end() + ; + + foreach (array_merge($this->options, $this->defaultSuccessHandlerOptions, $this->defaultFailureHandlerOptions) as $name => $default) { + if (is_bool($default)) { + $builder->booleanNode($name)->defaultValue($default); + } else { + $builder->scalarNode($name)->defaultValue($default); + } + } + } + + final public function addOption($name, $default = null) + { + $this->options[$name] = $default; + } + + /** + * Subclasses must return the id of a service which implements the + * AuthenticationProviderInterface. + * + * @param ContainerBuilder $container + * @param string $id The unique id of the firewall + * @param array $config The options array for this listener + * @param string $userProviderId The id of the user provider + * + * @return string never null, the id of the authentication provider + */ + abstract protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId); + + /** + * Subclasses must return the id of the abstract listener template. + * + * Listener definitions should inherit from the AbstractAuthenticationListener + * like this: + * + * + * + * In the above case, this method would return "my.listener.id". + * + * @return string + */ + abstract protected function getListenerId(); + + /** + * Subclasses may create an entry point of their as they see fit. The + * default implementation does not change the default entry point. + * + * @param ContainerBuilder $container + * @param string $id + * @param array $config + * @param string $defaultEntryPointId + * + * @return string the entry point id + */ + protected function createEntryPoint($container, $id, $config, $defaultEntryPointId) + { + return $defaultEntryPointId; + } + + /** + * Subclasses may disable remember-me features for the listener, by + * always returning false from this method. + * + * @param array $config + * + * @return bool Whether a possibly configured RememberMeServices should be set for this listener + */ + protected function isRememberMeAware($config) + { + return $config['remember_me']; + } + + protected function createListener($container, $id, $config, $userProvider) + { + $listenerId = $this->getListenerId(); + $listener = new DefinitionDecorator($listenerId); + $listener->replaceArgument(4, $id); + $listener->replaceArgument(5, new Reference($this->createAuthenticationSuccessHandler($container, $id, $config))); + $listener->replaceArgument(6, new Reference($this->createAuthenticationFailureHandler($container, $id, $config))); + $listener->replaceArgument(7, array_intersect_key($config, $this->options)); + + $listenerId .= '.'.$id; + $container->setDefinition($listenerId, $listener); + + return $listenerId; + } + + protected function createAuthenticationSuccessHandler($container, $id, $config) + { + $successHandlerId = $this->getSuccessHandlerId($id); + $options = array_intersect_key($config, $this->defaultSuccessHandlerOptions); + + if (isset($config['success_handler'])) { + $successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.custom_success_handler')); + $successHandler->replaceArgument(0, new Reference($config['success_handler'])); + $successHandler->replaceArgument(1, $options); + $successHandler->replaceArgument(2, $id); + } else { + $successHandler = $container->setDefinition($successHandlerId, new DefinitionDecorator('security.authentication.success_handler')); + $successHandler->addMethodCall('setOptions', array($options)); + $successHandler->addMethodCall('setProviderKey', array($id)); + } + + return $successHandlerId; + } + + protected function createAuthenticationFailureHandler($container, $id, $config) + { + $id = $this->getFailureHandlerId($id); + $options = array_intersect_key($config, $this->defaultFailureHandlerOptions); + + if (isset($config['failure_handler'])) { + $failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.custom_failure_handler')); + $failureHandler->replaceArgument(0, new Reference($config['failure_handler'])); + $failureHandler->replaceArgument(1, $options); + } else { + $failureHandler = $container->setDefinition($id, new DefinitionDecorator('security.authentication.failure_handler')); + $failureHandler->addMethodCall('setOptions', array($options)); + } + + return $id; + } + + protected function getSuccessHandlerId($id) + { + return 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey()); + } + + protected function getFailureHandlerId($id) + { + return 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php new file mode 100644 index 0000000..021c050 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * FormLoginFactory creates services for form login authentication. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class FormLoginFactory extends AbstractFactory +{ + public function __construct() + { + $this->addOption('username_parameter', '_username'); + $this->addOption('password_parameter', '_password'); + $this->addOption('csrf_parameter', '_csrf_token'); + $this->addOption('csrf_token_id', 'authenticate'); + $this->addOption('post_only', true); + } + + public function getPosition() + { + return 'form'; + } + + public function getKey() + { + return 'form-login'; + } + + public function addConfiguration(NodeDefinition $node) + { + parent::addConfiguration($node); + + $node + ->children() + ->scalarNode('csrf_token_generator')->cannotBeEmpty()->end() + ->end() + ; + } + + protected function getListenerId() + { + return 'security.authentication.listener.form'; + } + + protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId) + { + $provider = 'security.authentication.provider.dao.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao')) + ->replaceArgument(0, new Reference($userProviderId)) + ->replaceArgument(1, new Reference('security.user_checker.'.$id)) + ->replaceArgument(2, $id) + ; + + return $provider; + } + + protected function createListener($container, $id, $config, $userProvider) + { + $listenerId = parent::createListener($container, $id, $config, $userProvider); + + $container + ->getDefinition($listenerId) + ->addArgument(isset($config['csrf_token_generator']) ? new Reference($config['csrf_token_generator']) : null) + ; + + return $listenerId; + } + + protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) + { + $entryPointId = 'security.authentication.form_entry_point.'.$id; + $container + ->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point')) + ->addArgument(new Reference('security.http_utils')) + ->addArgument($config['login_path']) + ->addArgument($config['use_forward']) + ; + + return $entryPointId; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php new file mode 100644 index 0000000..026a3d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * FormLoginLdapFactory creates services for form login ldap authentication. + * + * @author Grégoire Pineau + * @author Charles Sarrazin + */ +class FormLoginLdapFactory extends FormLoginFactory +{ + protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId) + { + $provider = 'security.authentication.provider.ldap_bind.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap_bind')) + ->replaceArgument(0, new Reference($userProviderId)) + ->replaceArgument(1, new Reference('security.user_checker.'.$id)) + ->replaceArgument(2, $id) + ->replaceArgument(3, new Reference($config['service'])) + ->replaceArgument(4, $config['dn_string']) + ; + + return $provider; + } + + public function addConfiguration(NodeDefinition $node) + { + parent::addConfiguration($node); + + $node + ->children() + ->scalarNode('service')->end() + ->scalarNode('dn_string')->defaultValue('{username}')->end() + ->end() + ; + } + + public function getKey() + { + return 'form-login-ldap'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php new file mode 100644 index 0000000..67bdece --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Configures the "guard" authentication provider key under a firewall. + * + * @author Ryan Weaver + */ +class GuardAuthenticationFactory implements SecurityFactoryInterface +{ + public function getPosition() + { + return 'pre_auth'; + } + + public function getKey() + { + return 'guard'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->fixXmlConfig('authenticator') + ->children() + ->scalarNode('provider') + ->info('A key from the "providers" section of your security config, in case your user provider is different than the firewall') + ->end() + ->scalarNode('entry_point') + ->info('A service id (of one of your authenticators) whose start() method should be called when an anonymous user hits a page that requires authentication') + ->defaultValue(null) + ->end() + ->arrayNode('authenticators') + ->info('An array of service ids for all of your "authenticators"') + ->requiresAtLeastOneElement() + ->prototype('scalar')->end() + ->end() + ->end() + ; + } + + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $authenticatorIds = $config['authenticators']; + $authenticatorReferences = array(); + foreach ($authenticatorIds as $authenticatorId) { + $authenticatorReferences[] = new Reference($authenticatorId); + } + + // configure the GuardAuthenticationFactory to have the dynamic constructor arguments + $providerId = 'security.authentication.provider.guard.'.$id; + $container + ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.guard')) + ->replaceArgument(0, $authenticatorReferences) + ->replaceArgument(1, new Reference($userProvider)) + ->replaceArgument(2, $id) + ->replaceArgument(3, new Reference('security.user_checker.'.$id)) + ; + + // listener + $listenerId = 'security.authentication.listener.guard.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard')); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, $authenticatorReferences); + + // determine the entryPointId to use + $entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config); + + // this is always injected - then the listener decides if it should be used + $container + ->getDefinition($listenerId) + ->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProvider)); + + return array($providerId, $listenerId, $entryPointId); + } + + private function determineEntryPoint($defaultEntryPointId, array $config) + { + if ($defaultEntryPointId) { + // explode if they've configured the entry_point, but there is already one + if ($config['entry_point']) { + throw new \LogicException(sprintf( + 'The guard authentication provider cannot use the "%s" entry_point because another entry point is already configured by another provider! Either remove the other provider or move the entry_point configuration as a root key under your firewall (i.e. at the same level as "guard").', + $config['entry_point'] + )); + } + + return $defaultEntryPointId; + } + + if ($config['entry_point']) { + // if it's configured explicitly, use it! + return $config['entry_point']; + } + + $authenticatorIds = $config['authenticators']; + if (count($authenticatorIds) == 1) { + // if there is only one authenticator, use that as the entry point + return array_shift($authenticatorIds); + } + + // we have multiple entry points - we must ask them to configure one + throw new \LogicException(sprintf( + 'Because you have multiple guard configurators, you need to set the "guard.entry_point" key to one of you configurators (%s)', + implode(', ', $authenticatorIds) + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php new file mode 100644 index 0000000..162ea05 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * HttpBasicFactory creates services for HTTP basic authentication. + * + * @author Fabien Potencier + */ +class HttpBasicFactory implements SecurityFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $provider = 'security.authentication.provider.dao.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao')) + ->replaceArgument(0, new Reference($userProvider)) + ->replaceArgument(1, new Reference('security.user_checker.'.$id)) + ->replaceArgument(2, $id) + ; + + // entry point + $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); + + // listener + $listenerId = 'security.authentication.listener.basic.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic')); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, new Reference($entryPointId)); + + return array($provider, $listenerId, $entryPointId); + } + + public function getPosition() + { + return 'http'; + } + + public function getKey() + { + return 'http-basic'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('provider')->end() + ->scalarNode('realm')->defaultValue('Secured Area')->end() + ->end() + ; + } + + protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) + { + if (null !== $defaultEntryPoint) { + return $defaultEntryPoint; + } + + $entryPointId = 'security.authentication.basic_entry_point.'.$id; + $container + ->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.basic_entry_point')) + ->addArgument($config['realm']) + ; + + return $entryPointId; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php new file mode 100644 index 0000000..f2b1695 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * HttpBasicFactory creates services for HTTP basic authentication. + * + * @author Fabien Potencier + * @author Grégoire Pineau + * @author Charles Sarrazin + */ +class HttpBasicLdapFactory extends HttpBasicFactory +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $provider = 'security.authentication.provider.ldap_bind.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.ldap_bind')) + ->replaceArgument(0, new Reference($userProvider)) + ->replaceArgument(1, new Reference('security.user_checker.'.$id)) + ->replaceArgument(2, $id) + ->replaceArgument(3, new Reference($config['service'])) + ->replaceArgument(4, $config['dn_string']) + ; + + // entry point + $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); + + // listener + $listenerId = 'security.authentication.listener.basic.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.basic')); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, new Reference($entryPointId)); + + return array($provider, $listenerId, $entryPointId); + } + + public function addConfiguration(NodeDefinition $node) + { + parent::addConfiguration($node); + + $node + ->children() + ->scalarNode('service')->end() + ->scalarNode('dn_string')->defaultValue('{username}')->end() + ->end() + ; + } + + public function getKey() + { + return 'http-basic-ldap'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php new file mode 100644 index 0000000..ae9322f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpDigestFactory.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * HttpDigestFactory creates services for HTTP digest authentication. + * + * @author Fabien Potencier + */ +class HttpDigestFactory implements SecurityFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $provider = 'security.authentication.provider.dao.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.dao')) + ->replaceArgument(0, new Reference($userProvider)) + ->replaceArgument(2, $id) + ; + + // entry point + $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); + + // listener + $listenerId = 'security.authentication.listener.digest.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.digest')); + $listener->replaceArgument(1, new Reference($userProvider)); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, new Reference($entryPointId)); + + return array($provider, $listenerId, $entryPointId); + } + + public function getPosition() + { + return 'http'; + } + + public function getKey() + { + return 'http-digest'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('provider')->end() + ->scalarNode('realm')->defaultValue('Secured Area')->end() + ->scalarNode('secret')->isRequired()->cannotBeEmpty()->end() + ->end() + ; + } + + protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) + { + if (null !== $defaultEntryPoint) { + return $defaultEntryPoint; + } + + $entryPointId = 'security.authentication.digest_entry_point.'.$id; + $container + ->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.digest_entry_point')) + ->addArgument($config['realm']) + ->addArgument($config['secret']) + ; + + return $entryPointId; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php new file mode 100644 index 0000000..fc008a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class RememberMeFactory implements SecurityFactoryInterface +{ + protected $options = array( + 'name' => 'REMEMBERME', + 'lifetime' => 31536000, + 'path' => '/', + 'domain' => null, + 'secure' => false, + 'httponly' => true, + 'always_remember_me' => false, + 'remember_me_parameter' => '_remember_me', + ); + + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + // authentication provider + $authProviderId = 'security.authentication.provider.rememberme.'.$id; + $container + ->setDefinition($authProviderId, new DefinitionDecorator('security.authentication.provider.rememberme')) + ->replaceArgument(0, new Reference('security.user_checker.'.$id)) + ->addArgument($config['secret']) + ->addArgument($id) + ; + + // remember me services + if (isset($config['token_provider'])) { + $templateId = 'security.authentication.rememberme.services.persistent'; + $rememberMeServicesId = $templateId.'.'.$id; + } else { + $templateId = 'security.authentication.rememberme.services.simplehash'; + $rememberMeServicesId = $templateId.'.'.$id; + } + + if ($container->hasDefinition('security.logout_listener.'.$id)) { + $container + ->getDefinition('security.logout_listener.'.$id) + ->addMethodCall('addHandler', array(new Reference($rememberMeServicesId))) + ; + } + + $rememberMeServices = $container->setDefinition($rememberMeServicesId, new DefinitionDecorator($templateId)); + $rememberMeServices->replaceArgument(1, $config['secret']); + $rememberMeServices->replaceArgument(2, $id); + + if (isset($config['token_provider'])) { + $rememberMeServices->addMethodCall('setTokenProvider', array( + new Reference($config['token_provider']), + )); + } + + // remember-me options + $rememberMeServices->replaceArgument(3, array_intersect_key($config, $this->options)); + + // attach to remember-me aware listeners + $userProviders = array(); + foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['id']) || $attribute['id'] !== $id) { + continue; + } + + if (!isset($attribute['provider'])) { + throw new \RuntimeException('Each "security.remember_me_aware" tag must have a provider attribute.'); + } + + $userProviders[] = new Reference($attribute['provider']); + $container + ->getDefinition($serviceId) + ->addMethodCall('setRememberMeServices', array(new Reference($rememberMeServicesId))) + ; + } + } + if ($config['user_providers']) { + $userProviders = array(); + foreach ($config['user_providers'] as $providerName) { + $userProviders[] = new Reference('security.user.provider.concrete.'.$providerName); + } + } + if (count($userProviders) === 0) { + throw new \RuntimeException('You must configure at least one remember-me aware listener (such as form-login) for each firewall that has remember-me enabled.'); + } + $rememberMeServices->replaceArgument(0, $userProviders); + + // remember-me listener + $listenerId = 'security.authentication.listener.rememberme.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.rememberme')); + $listener->replaceArgument(1, new Reference($rememberMeServicesId)); + $listener->replaceArgument(5, $config['catch_exceptions']); + + return array($authProviderId, $listenerId, $defaultEntryPoint); + } + + public function getPosition() + { + return 'remember_me'; + } + + public function getKey() + { + return 'remember-me'; + } + + public function addConfiguration(NodeDefinition $node) + { + $builder = $node + ->fixXmlConfig('user_provider') + ->children() + ; + + $builder + ->scalarNode('secret')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('token_provider')->end() + ->arrayNode('user_providers') + ->beforeNormalization() + ->ifString()->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->scalarNode('catch_exceptions')->defaultTrue()->end() + ; + + foreach ($this->options as $name => $value) { + if (is_bool($value)) { + $builder->booleanNode($name)->defaultValue($value); + } else { + $builder->scalarNode($name)->defaultValue($value); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php new file mode 100644 index 0000000..cf2e2ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * RemoteUserFactory creates services for REMOTE_USER based authentication. + * + * @author Fabien Potencier + * @author Maxime Douailin + */ +class RemoteUserFactory implements SecurityFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $providerId = 'security.authentication.provider.pre_authenticated.'.$id; + $container + ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.pre_authenticated')) + ->replaceArgument(0, new Reference($userProvider)) + ->replaceArgument(1, new Reference('security.user_checker.'.$id)) + ->addArgument($id) + ; + + $listenerId = 'security.authentication.listener.remote_user.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.remote_user')); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, $config['user']); + + return array($providerId, $listenerId, $defaultEntryPoint); + } + + public function getPosition() + { + return 'pre_auth'; + } + + public function getKey() + { + return 'remote-user'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('provider')->end() + ->scalarNode('user')->defaultValue('REMOTE_USER')->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php new file mode 100644 index 0000000..fce2f07 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * SecurityFactoryInterface is the interface for all security authentication listener. + * + * @author Fabien Potencier + */ +interface SecurityFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint); + + /** + * Defines the position at which the provider is called. + * Possible values: pre_auth, form, http, and remember_me. + * + * @return string + */ + public function getPosition(); + + public function getKey(); + + public function addConfiguration(NodeDefinition $builder); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php new file mode 100644 index 0000000..9da6601 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimpleFormFactory.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Jordi Boggiano + */ +class SimpleFormFactory extends FormLoginFactory +{ + public function __construct() + { + parent::__construct(); + + $this->addOption('authenticator', null); + } + + public function getKey() + { + return 'simple-form'; + } + + public function addConfiguration(NodeDefinition $node) + { + parent::addConfiguration($node); + + $node->children() + ->scalarNode('authenticator')->cannotBeEmpty()->end() + ->end(); + } + + protected function getListenerId() + { + return 'security.authentication.listener.simple_form'; + } + + protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId) + { + $provider = 'security.authentication.provider.simple_form.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.simple')) + ->replaceArgument(0, new Reference($config['authenticator'])) + ->replaceArgument(1, new Reference($userProviderId)) + ->replaceArgument(2, $id) + ; + + return $provider; + } + + protected function createListener($container, $id, $config, $userProvider) + { + $listenerId = parent::createListener($container, $id, $config, $userProvider); + + $simpleAuthHandlerId = 'security.authentication.simple_success_failure_handler.'.$id; + $simpleAuthHandler = $container->setDefinition($simpleAuthHandlerId, new DefinitionDecorator('security.authentication.simple_success_failure_handler')); + $simpleAuthHandler->replaceArgument(0, new Reference($config['authenticator'])); + $simpleAuthHandler->replaceArgument(1, new Reference($this->getSuccessHandlerId($id))); + $simpleAuthHandler->replaceArgument(2, new Reference($this->getFailureHandlerId($id))); + + $listener = $container->getDefinition($listenerId); + $listener->replaceArgument(5, new Reference($simpleAuthHandlerId)); + $listener->replaceArgument(6, new Reference($simpleAuthHandlerId)); + $listener->addArgument(new Reference($config['authenticator'])); + + return $listenerId; + } + + protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) + { + $entryPointId = 'security.authentication.form_entry_point.'.$id; + $container + ->setDefinition($entryPointId, new DefinitionDecorator('security.authentication.form_entry_point')) + ->addArgument(new Reference('security.http_utils')) + ->addArgument($config['login_path']) + ->addArgument($config['use_forward']) + ; + + return $entryPointId; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimplePreAuthenticationFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimplePreAuthenticationFactory.php new file mode 100644 index 0000000..27d8c5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SimplePreAuthenticationFactory.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Jordi Boggiano + */ +class SimplePreAuthenticationFactory implements SecurityFactoryInterface +{ + public function getPosition() + { + return 'pre_auth'; + } + + public function getKey() + { + return 'simple-preauth'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('provider')->end() + ->scalarNode('authenticator')->cannotBeEmpty()->end() + ->end() + ; + } + + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $provider = 'security.authentication.provider.simple_preauth.'.$id; + $container + ->setDefinition($provider, new DefinitionDecorator('security.authentication.provider.simple')) + ->replaceArgument(0, new Reference($config['authenticator'])) + ->replaceArgument(1, new Reference($userProvider)) + ->replaceArgument(2, $id) + ; + + // listener + $listenerId = 'security.authentication.listener.simple_preauth.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.simple_preauth')); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, new Reference($config['authenticator'])); + + return array($provider, $listenerId, null); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php new file mode 100644 index 0000000..0467ef2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * X509Factory creates services for X509 certificate authentication. + * + * @author Fabien Potencier + */ +class X509Factory implements SecurityFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + { + $providerId = 'security.authentication.provider.pre_authenticated.'.$id; + $container + ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.pre_authenticated')) + ->replaceArgument(0, new Reference($userProvider)) + ->replaceArgument(1, new Reference('security.user_checker.'.$id)) + ->addArgument($id) + ; + + // listener + $listenerId = 'security.authentication.listener.x509.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.x509')); + $listener->replaceArgument(2, $id); + $listener->replaceArgument(3, $config['user']); + $listener->replaceArgument(4, $config['credentials']); + + return array($providerId, $listenerId, $defaultEntryPoint); + } + + public function getPosition() + { + return 'pre_auth'; + } + + public function getKey() + { + return 'x509'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('provider')->end() + ->scalarNode('user')->defaultValue('SSL_CLIENT_S_DN_Email')->end() + ->scalarNode('credentials')->defaultValue('SSL_CLIENT_S_DN')->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php new file mode 100644 index 0000000..b184385 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/InMemoryFactory.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * InMemoryFactory creates services for the memory provider. + * + * @author Fabien Potencier + * @author Christophe Coevoet + */ +class InMemoryFactory implements UserProviderFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config) + { + $definition = $container->setDefinition($id, new DefinitionDecorator('security.user.provider.in_memory')); + + foreach ($config['users'] as $username => $user) { + $userId = $id.'_'.$username; + + $container + ->setDefinition($userId, new DefinitionDecorator('security.user.provider.in_memory.user')) + ->setArguments(array($username, (string) $user['password'], $user['roles'])) + ; + + $definition->addMethodCall('createUser', array(new Reference($userId))); + } + } + + public function getKey() + { + return 'memory'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->fixXmlConfig('user') + ->children() + ->arrayNode('users') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('password')->defaultValue(uniqid('', true))->end() + ->arrayNode('roles') + ->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php new file mode 100644 index 0000000..068cda6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/LdapFactory.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * LdapFactory creates services for Ldap user provider. + * + * @author Grégoire Pineau + * @author Charles Sarrazin + */ +class LdapFactory implements UserProviderFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config) + { + $container + ->setDefinition($id, new DefinitionDecorator('security.user.provider.ldap')) + ->replaceArgument(0, new Reference($config['service'])) + ->replaceArgument(1, $config['base_dn']) + ->replaceArgument(2, $config['search_dn']) + ->replaceArgument(3, $config['search_password']) + ->replaceArgument(4, $config['default_roles']) + ->replaceArgument(5, $config['uid_key']) + ->replaceArgument(6, $config['filter']) + ; + } + + public function getKey() + { + return 'ldap'; + } + + public function addConfiguration(NodeDefinition $node) + { + $node + ->children() + ->scalarNode('service')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('base_dn')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('search_dn')->end() + ->scalarNode('search_password')->end() + ->arrayNode('default_roles') + ->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end() + ->requiresAtLeastOneElement() + ->prototype('scalar')->end() + ->end() + ->scalarNode('uid_key')->defaultValue('sAMAccountName')->end() + ->scalarNode('filter')->defaultValue('({uid_key}={username})')->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/UserProviderFactoryInterface.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/UserProviderFactoryInterface.php new file mode 100644 index 0000000..df25035 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/UserProvider/UserProviderFactoryInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * UserProviderFactoryInterface is the interface for all user provider factories. + * + * @author Fabien Potencier + * @author Christophe Coevoet + */ +interface UserProviderFactoryInterface +{ + public function create(ContainerBuilder $container, $id, $config); + + public function getKey(); + + public function addConfiguration(NodeDefinition $builder); +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php new file mode 100644 index 0000000..ac05211 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -0,0 +1,681 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; + +/** + * SecurityExtension. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class SecurityExtension extends Extension +{ + private $requestMatchers = array(); + private $expressions = array(); + private $contextListeners = array(); + private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me'); + private $factories = array(); + private $userProviderFactories = array(); + private $expressionLanguage; + + public function __construct() + { + foreach ($this->listenerPositions as $position) { + $this->factories[$position] = array(); + } + } + + public function load(array $configs, ContainerBuilder $container) + { + if (!array_filter($configs)) { + return; + } + + $mainConfig = $this->getConfiguration($configs, $container); + + $config = $this->processConfiguration($mainConfig, $configs); + + // load services + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('security.xml'); + $loader->load('security_listeners.xml'); + $loader->load('security_rememberme.xml'); + $loader->load('templating_php.xml'); + $loader->load('templating_twig.xml'); + $loader->load('collectors.xml'); + $loader->load('guard.xml'); + + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + $container->removeDefinition('security.expression_language'); + $container->removeDefinition('security.access.expression_voter'); + } + + // set some global scalars + $container->setParameter('security.access.denied_url', $config['access_denied_url']); + $container->setParameter('security.authentication.manager.erase_credentials', $config['erase_credentials']); + $container->setParameter('security.authentication.session_strategy.strategy', $config['session_fixation_strategy']); + $container + ->getDefinition('security.access.decision_manager') + ->addArgument($config['access_decision_manager']['strategy']) + ->addArgument($config['access_decision_manager']['allow_if_all_abstain']) + ->addArgument($config['access_decision_manager']['allow_if_equal_granted_denied']) + ; + $container->setParameter('security.access.always_authenticate_before_granting', $config['always_authenticate_before_granting']); + $container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']); + + $this->createFirewalls($config, $container); + $this->createAuthorization($config, $container); + $this->createRoleHierarchy($config, $container); + + if ($config['encoders']) { + $this->createEncoders($config['encoders'], $container); + } + + // load ACL + if (isset($config['acl'])) { + $this->aclLoad($config['acl'], $container); + } + + // add some required classes for compilation + $this->addClassesToCompile(array( + 'Symfony\Component\Security\Http\Firewall', + 'Symfony\Component\Security\Core\User\UserProviderInterface', + 'Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager', + 'Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage', + 'Symfony\Component\Security\Core\Authorization\AccessDecisionManager', + 'Symfony\Component\Security\Core\Authorization\AuthorizationChecker', + 'Symfony\Component\Security\Core\Authorization\Voter\VoterInterface', + 'Symfony\Bundle\SecurityBundle\Security\FirewallMap', + 'Symfony\Bundle\SecurityBundle\Security\FirewallContext', + 'Symfony\Component\HttpFoundation\RequestMatcher', + )); + } + + private function aclLoad($config, ContainerBuilder $container) + { + if (!interface_exists('Symfony\Component\Security\Acl\Model\AclInterface')) { + throw new \LogicException('You must install symfony/security-acl in order to use the ACL functionality.'); + } + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('security_acl.xml'); + + if (isset($config['cache']['id'])) { + $container->setAlias('security.acl.cache', $config['cache']['id']); + } + $container->getDefinition('security.acl.voter.basic_permissions')->addArgument($config['voter']['allow_if_object_identity_unavailable']); + + // custom ACL provider + if (isset($config['provider'])) { + $container->setAlias('security.acl.provider', $config['provider']); + + return; + } + + $this->configureDbalAclProvider($config, $container, $loader); + } + + private function configureDbalAclProvider(array $config, ContainerBuilder $container, $loader) + { + $loader->load('security_acl_dbal.xml'); + + if (null !== $config['connection']) { + $container->setAlias('security.acl.dbal.connection', sprintf('doctrine.dbal.%s_connection', $config['connection'])); + } + + $container + ->getDefinition('security.acl.dbal.schema_listener') + ->addTag('doctrine.event_listener', array( + 'connection' => $config['connection'], + 'event' => 'postGenerateSchema', + 'lazy' => true, + )) + ; + + $container->getDefinition('security.acl.cache.doctrine')->addArgument($config['cache']['prefix']); + + $container->setParameter('security.acl.dbal.class_table_name', $config['tables']['class']); + $container->setParameter('security.acl.dbal.entry_table_name', $config['tables']['entry']); + $container->setParameter('security.acl.dbal.oid_table_name', $config['tables']['object_identity']); + $container->setParameter('security.acl.dbal.oid_ancestors_table_name', $config['tables']['object_identity_ancestors']); + $container->setParameter('security.acl.dbal.sid_table_name', $config['tables']['security_identity']); + } + + /** + * Loads the web configuration. + * + * @param array $config An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + private function createRoleHierarchy($config, ContainerBuilder $container) + { + if (!isset($config['role_hierarchy']) || 0 === count($config['role_hierarchy'])) { + $container->removeDefinition('security.access.role_hierarchy_voter'); + + return; + } + + $container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']); + $container->removeDefinition('security.access.simple_role_voter'); + } + + private function createAuthorization($config, ContainerBuilder $container) + { + if (!$config['access_control']) { + return; + } + + $this->addClassesToCompile(array( + 'Symfony\\Component\\Security\\Http\\AccessMap', + )); + + foreach ($config['access_control'] as $access) { + $matcher = $this->createRequestMatcher( + $container, + $access['path'], + $access['host'], + $access['methods'], + $access['ips'] + ); + + $attributes = $access['roles']; + if ($access['allow_if']) { + $attributes[] = $this->createExpression($container, $access['allow_if']); + } + + $container->getDefinition('security.access_map') + ->addMethodCall('add', array($matcher, $attributes, $access['requires_channel'])); + } + } + + private function createFirewalls($config, ContainerBuilder $container) + { + if (!isset($config['firewalls'])) { + return; + } + + $firewalls = $config['firewalls']; + $providerIds = $this->createUserProviders($config, $container); + + // make the ContextListener aware of the configured user providers + $definition = $container->getDefinition('security.context_listener'); + $arguments = $definition->getArguments(); + $userProviders = array(); + foreach ($providerIds as $userProviderId) { + $userProviders[] = new Reference($userProviderId); + } + $arguments[1] = $userProviders; + $definition->setArguments($arguments); + + // load firewall map + $mapDef = $container->getDefinition('security.firewall.map'); + $map = $authenticationProviders = array(); + foreach ($firewalls as $name => $firewall) { + list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds); + + $contextId = 'security.firewall.map.context.'.$name; + $context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context')); + $context + ->replaceArgument(0, $listeners) + ->replaceArgument(1, $exceptionListener) + ; + $map[$contextId] = $matcher; + } + $mapDef->replaceArgument(1, $map); + + // add authentication providers to authentication manager + $authenticationProviders = array_map(function ($id) { + return new Reference($id); + }, array_values(array_unique($authenticationProviders))); + $container + ->getDefinition('security.authentication.manager') + ->replaceArgument(0, $authenticationProviders) + ; + } + + private function createFirewall(ContainerBuilder $container, $id, $firewall, &$authenticationProviders, $providerIds) + { + // Matcher + $matcher = null; + if (isset($firewall['request_matcher'])) { + $matcher = new Reference($firewall['request_matcher']); + } elseif (isset($firewall['pattern']) || isset($firewall['host'])) { + $pattern = isset($firewall['pattern']) ? $firewall['pattern'] : null; + $host = isset($firewall['host']) ? $firewall['host'] : null; + $methods = isset($firewall['methods']) ? $firewall['methods'] : array(); + $matcher = $this->createRequestMatcher($container, $pattern, $host, $methods); + } + + // Security disabled? + if (false === $firewall['security']) { + return array($matcher, array(), null); + } + + // Provider id (take the first registered provider if none defined) + if (isset($firewall['provider'])) { + $defaultProvider = $this->getUserProviderId($firewall['provider']); + } else { + $defaultProvider = reset($providerIds); + } + + // Register listeners + $listeners = array(); + + // Channel listener + $listeners[] = new Reference('security.channel_listener'); + + // Context serializer listener + if (false === $firewall['stateless']) { + $contextKey = $id; + if (isset($firewall['context'])) { + $contextKey = $firewall['context']; + } + + $listeners[] = new Reference($this->createContextListener($container, $contextKey)); + } + + // Logout listener + if (isset($firewall['logout'])) { + $listenerId = 'security.logout_listener.'.$id; + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener')); + $listener->replaceArgument(3, array( + 'csrf_parameter' => $firewall['logout']['csrf_parameter'], + 'csrf_token_id' => $firewall['logout']['csrf_token_id'], + 'logout_path' => $firewall['logout']['path'], + )); + $listeners[] = new Reference($listenerId); + + // add logout success handler + if (isset($firewall['logout']['success_handler'])) { + $logoutSuccessHandlerId = $firewall['logout']['success_handler']; + } else { + $logoutSuccessHandlerId = 'security.logout.success_handler.'.$id; + $logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('security.logout.success_handler')); + $logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']); + } + $listener->replaceArgument(2, new Reference($logoutSuccessHandlerId)); + + // add CSRF provider + if (isset($firewall['logout']['csrf_token_generator'])) { + $listener->addArgument(new Reference($firewall['logout']['csrf_token_generator'])); + } + + // add session logout handler + if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) { + $listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session'))); + } + + // add cookie logout handler + if (count($firewall['logout']['delete_cookies']) > 0) { + $cookieHandlerId = 'security.logout.handler.cookie_clearing.'.$id; + $cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('security.logout.handler.cookie_clearing')); + $cookieHandler->addArgument($firewall['logout']['delete_cookies']); + + $listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId))); + } + + // add custom handlers + foreach ($firewall['logout']['handlers'] as $handlerId) { + $listener->addMethodCall('addHandler', array(new Reference($handlerId))); + } + + // register with LogoutUrlGenerator + $container + ->getDefinition('security.logout_url_generator') + ->addMethodCall('registerListener', array( + $id, + $firewall['logout']['path'], + $firewall['logout']['csrf_token_id'], + $firewall['logout']['csrf_parameter'], + isset($firewall['logout']['csrf_token_generator']) ? new Reference($firewall['logout']['csrf_token_generator']) : null, + )) + ; + } + + // Determine default entry point + $configuredEntryPoint = isset($firewall['entry_point']) ? $firewall['entry_point'] : null; + + // Authentication listeners + list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $configuredEntryPoint); + + $listeners = array_merge($listeners, $authListeners); + + // Switch user listener + if (isset($firewall['switch_user'])) { + $listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider)); + } + + // Access listener + $listeners[] = new Reference('security.access_listener'); + + // Exception listener + $exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless'])); + + $container->setAlias(new Alias('security.user_checker.'.$id, false), $firewall['user_checker']); + + return array($matcher, $listeners, $exceptionListener); + } + + private function createContextListener($container, $contextKey) + { + if (isset($this->contextListeners[$contextKey])) { + return $this->contextListeners[$contextKey]; + } + + $listenerId = 'security.context_listener.'.count($this->contextListeners); + $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.context_listener')); + $listener->replaceArgument(2, $contextKey); + + return $this->contextListeners[$contextKey] = $listenerId; + } + + private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider, $defaultEntryPoint) + { + $listeners = array(); + $hasListeners = false; + + foreach ($this->listenerPositions as $position) { + foreach ($this->factories[$position] as $factory) { + $key = str_replace('-', '_', $factory->getKey()); + + if (isset($firewall[$key])) { + $userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider; + + list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); + + $listeners[] = new Reference($listenerId); + $authenticationProviders[] = $provider; + $hasListeners = true; + } + } + } + + // Anonymous + if (isset($firewall['anonymous'])) { + $listenerId = 'security.authentication.listener.anonymous.'.$id; + $container + ->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.anonymous')) + ->replaceArgument(1, $firewall['anonymous']['secret']) + ; + + $listeners[] = new Reference($listenerId); + + $providerId = 'security.authentication.provider.anonymous.'.$id; + $container + ->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.anonymous')) + ->replaceArgument(0, $firewall['anonymous']['secret']) + ; + + $authenticationProviders[] = $providerId; + $hasListeners = true; + } + + if (false === $hasListeners) { + throw new InvalidConfigurationException(sprintf('No authentication listener registered for firewall "%s".', $id)); + } + + return array($listeners, $defaultEntryPoint); + } + + private function createEncoders($encoders, ContainerBuilder $container) + { + $encoderMap = array(); + foreach ($encoders as $class => $encoder) { + $encoderMap[$class] = $this->createEncoder($encoder, $container); + } + + $container + ->getDefinition('security.encoder_factory.generic') + ->setArguments(array($encoderMap)) + ; + } + + private function createEncoder($config, ContainerBuilder $container) + { + // a custom encoder service + if (isset($config['id'])) { + return new Reference($config['id']); + } + + // plaintext encoder + if ('plaintext' === $config['algorithm']) { + $arguments = array($config['ignore_case']); + + return array( + 'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder', + 'arguments' => $arguments, + ); + } + + // pbkdf2 encoder + if ('pbkdf2' === $config['algorithm']) { + return array( + 'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder', + 'arguments' => array( + $config['hash_algorithm'], + $config['encode_as_base64'], + $config['iterations'], + $config['key_length'], + ), + ); + } + + // bcrypt encoder + if ('bcrypt' === $config['algorithm']) { + return array( + 'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder', + 'arguments' => array($config['cost']), + ); + } + + // message digest encoder + return array( + 'class' => 'Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder', + 'arguments' => array( + $config['algorithm'], + $config['encode_as_base64'], + $config['iterations'], + ), + ); + } + + // Parses user providers and returns an array of their ids + private function createUserProviders($config, ContainerBuilder $container) + { + $providerIds = array(); + foreach ($config['providers'] as $name => $provider) { + $id = $this->createUserDaoProvider($name, $provider, $container); + $providerIds[] = $id; + } + + return $providerIds; + } + + // Parses a tag and returns the id for the related user provider service + private function createUserDaoProvider($name, $provider, ContainerBuilder $container) + { + $name = $this->getUserProviderId(strtolower($name)); + + // Doctrine Entity and In-memory DAO provider are managed by factories + foreach ($this->userProviderFactories as $factory) { + $key = str_replace('-', '_', $factory->getKey()); + + if (!empty($provider[$key])) { + $factory->create($container, $name, $provider[$key]); + + return $name; + } + } + + // Existing DAO service provider + if (isset($provider['id'])) { + $container->setAlias($name, new Alias($provider['id'], false)); + + return $provider['id']; + } + + // Chain provider + if (isset($provider['chain'])) { + $providers = array(); + foreach ($provider['chain']['providers'] as $providerName) { + $providers[] = new Reference($this->getUserProviderId(strtolower($providerName))); + } + + $container + ->setDefinition($name, new DefinitionDecorator('security.user.provider.chain')) + ->addArgument($providers); + + return $name; + } + + throw new InvalidConfigurationException(sprintf('Unable to create definition for "%s" user provider', $name)); + } + + private function getUserProviderId($name) + { + return 'security.user.provider.concrete.'.$name; + } + + private function createExceptionListener($container, $config, $id, $defaultEntryPoint, $stateless) + { + $exceptionListenerId = 'security.exception_listener.'.$id; + $listener = $container->setDefinition($exceptionListenerId, new DefinitionDecorator('security.exception_listener')); + $listener->replaceArgument(3, $id); + $listener->replaceArgument(4, null === $defaultEntryPoint ? null : new Reference($defaultEntryPoint)); + $listener->replaceArgument(8, $stateless); + + // access denied handler setup + if (isset($config['access_denied_handler'])) { + $listener->replaceArgument(6, new Reference($config['access_denied_handler'])); + } elseif (isset($config['access_denied_url'])) { + $listener->replaceArgument(5, $config['access_denied_url']); + } + + return $exceptionListenerId; + } + + private function createSwitchUserListener($container, $id, $config, $defaultProvider) + { + $userProvider = isset($config['provider']) ? $this->getUserProviderId($config['provider']) : $defaultProvider; + + $switchUserListenerId = 'security.authentication.switchuser_listener.'.$id; + $listener = $container->setDefinition($switchUserListenerId, new DefinitionDecorator('security.authentication.switchuser_listener')); + $listener->replaceArgument(1, new Reference($userProvider)); + $listener->replaceArgument(2, new Reference('security.user_checker.'.$id)); + $listener->replaceArgument(3, $id); + $listener->replaceArgument(6, $config['parameter']); + $listener->replaceArgument(7, $config['role']); + + return $switchUserListenerId; + } + + private function createExpression($container, $expression) + { + if (isset($this->expressions[$id = 'security.expression.'.sha1($expression)])) { + return $this->expressions[$id]; + } + + $container + ->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression') + ->setPublic(false) + ->addArgument($expression) + ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request', 'trust_resolver'))->getNodes())) + ; + + return $this->expressions[$id] = new Reference($id); + } + + private function createRequestMatcher($container, $path = null, $host = null, $methods = array(), $ip = null, array $attributes = array()) + { + if ($methods) { + $methods = array_map('strtoupper', (array) $methods); + } + + $serialized = serialize(array($path, $host, $methods, $ip, $attributes)); + $id = 'security.request_matcher.'.md5($serialized).sha1($serialized); + + if (isset($this->requestMatchers[$id])) { + return $this->requestMatchers[$id]; + } + + // only add arguments that are necessary + $arguments = array($path, $host, $methods, $ip, $attributes); + while (count($arguments) > 0 && !end($arguments)) { + array_pop($arguments); + } + + $container + ->register($id, 'Symfony\Component\HttpFoundation\RequestMatcher') + ->setPublic(false) + ->setArguments($arguments) + ; + + return $this->requestMatchers[$id] = new Reference($id); + } + + public function addSecurityListenerFactory(SecurityFactoryInterface $factory) + { + $this->factories[$factory->getPosition()][] = $factory; + } + + public function addUserProviderFactory(UserProviderFactoryInterface $factory) + { + $this->userProviderFactories[] = $factory; + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/security'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + // first assemble the factories + return new MainConfiguration($this->factories, $this->userProviderFactories); + } + + private function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $this->expressionLanguage = new ExpressionLanguage(); + } + + return $this->expressionLanguage; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/EventListener/AclSchemaListener.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/EventListener/AclSchemaListener.php new file mode 100644 index 0000000..8faa9ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/EventListener/AclSchemaListener.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\EventListener; + +use Symfony\Component\Security\Acl\Dbal\Schema; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; + +/** + * Merges ACL schema into the given schema. + * + * @author Johannes M. Schmitt + */ +class AclSchemaListener +{ + private $schema; + + public function __construct(Schema $schema) + { + $this->schema = $schema; + } + + public function postGenerateSchema(GenerateSchemaEventArgs $args) + { + $schema = $args->getSchema(); + $this->schema->addToSchema($schema); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml new file mode 100644 index 0000000..329815f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/collectors.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml new file mode 100644 index 0000000..80318c2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml new file mode 100644 index 0000000..d19ae1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -0,0 +1,147 @@ + + + + + + Symfony\Component\Security\Core\Authentication\Token\AnonymousToken + Symfony\Component\Security\Core\Authentication\Token\RememberMeToken + + + + + + + + + %security.access.always_authenticate_before_granting% + + + + + + + + %security.authentication.manager.erase_credentials% + + + + + + + %security.authentication.trust_resolver.anonymous_class% + %security.authentication.trust_resolver.rememberme_class% + + + + %security.authentication.session_strategy.strategy% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %security.role_hierarchy.roles% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml new file mode 100644 index 0000000..982a661 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml new file mode 100644 index 0000000..581c938 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_acl_dbal.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + %security.acl.dbal.class_table_name% + %security.acl.dbal.entry_table_name% + %security.acl.dbal.oid_table_name% + %security.acl.dbal.oid_ancestors_table_name% + %security.acl.dbal.sid_table_name% + + + + + + + %security.acl.dbal.class_table_name% + %security.acl.dbal.entry_table_name% + %security.acl.dbal.oid_table_name% + %security.acl.dbal.oid_ancestors_table_name% + %security.acl.dbal.sid_table_name% + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml new file mode 100644 index 0000000..1d3c9c2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + %request_listener.http_port% + %request_listener.https_port% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %security.authentication.hide_user_not_found% + + + + + + + + + %security.authentication.hide_user_not_found% + + + + + + + + + + + + + + + + + + + + + %security.access.denied_url% + + + false + + + + + + + + + + + _switch_user + ROLE_ALLOWED_TO_SWITCH + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml new file mode 100644 index 0000000..7c67e8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security_rememberme.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml new file mode 100644 index 0000000..e800500 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_php.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml new file mode 100644 index 0000000..85be547 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/templating_twig.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/meta/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg new file mode 100644 index 0000000..02033fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig new file mode 100644 index 0000000..dd72468 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -0,0 +1,122 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block page_title 'Security' %} + +{% block toolbar %} + {% if collector.tokenClass %} + {% set is_authenticated = collector.enabled and collector.authenticated %} + {% set color_code = is_authenticated ? '' : 'yellow' %} + {% else %} + {% set color_code = collector.enabled ? 'red' : '' %} + {% endif %} + + {% set icon %} + {{ include('@Security/Collector/icon.svg') }} + {{ collector.user|default('n/a') }} + {% endset %} + + {% set text %} + {% if collector.tokenClass %} +
    + Logged in as + {{ collector.user }} +
    + +
    + Authenticated + {{ is_authenticated ? 'Yes' : 'No' }} +
    + + {% if collector.tokenClass != null %} +
    + Token class + {{ collector.tokenClass|abbr_class }} +
    + {% endif %} + {% if collector.logoutUrl %} +
    + Actions + Logout +
    + {% endif %} + {% elseif collector.enabled %} +
    + You are not authenticated. +
    + {% else %} +
    + The security is disabled. +
    + {% endif %} + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: color_code }) }} +{% endblock %} + +{% block menu %} + + {{ include('@Security/Collector/icon.svg') }} + Security + +{% endblock %} + +{% block panel %} +

    Security Token

    + + {% if collector.tokenClass %} +
    +
    + {{ collector.user == 'anon.' ? 'Anonymous' : collector.user }} + Username +
    + +
    + {{ include('@WebProfiler/Icon/' ~ (collector.authenticated ? 'yes' : 'no') ~ '.svg') }} + Authenticated +
    +
    + + + + + + + + + + + + + + + {% if collector.supportsRoleHierarchy %} + + + + + {% endif %} + + {% if collector.tokenClass %} + + + + + {% endif %} + +
    PropertyValue
    Roles + {{ collector.roles is empty ? 'none' : collector.roles|yaml_encode }} + + {% if not collector.authenticated and collector.roles is empty %} +

    User is not authenticated probably because they have no roles.

    + {% endif %} +
    Inherited Roles{{ collector.inheritedRoles is empty ? 'none' : collector.inheritedRoles|yaml_encode }}
    Token class{{ collector.tokenClass }}
    + {% elseif collector.enabled %} +
    +

    There is no security token.

    +
    + {% else %} +
    +

    The security component is disabled.

    +
    + {% endif %} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php new file mode 100644 index 0000000..13d096d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Security; + +use Symfony\Component\Security\Http\Firewall\ExceptionListener; + +/** + * This is a wrapper around the actual firewall configuration which allows us + * to lazy load the context for one specific firewall only when we need it. + * + * @author Johannes M. Schmitt + */ +class FirewallContext +{ + private $listeners; + private $exceptionListener; + + public function __construct(array $listeners, ExceptionListener $exceptionListener = null) + { + $this->listeners = $listeners; + $this->exceptionListener = $exceptionListener; + } + + public function getContext() + { + return array($this->listeners, $this->exceptionListener); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php new file mode 100644 index 0000000..dc87681 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Security; + +use Symfony\Component\Security\Http\FirewallMapInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * This is a lazy-loading firewall map implementation. + * + * Listeners will only be initialized if we really need them. + * + * @author Johannes M. Schmitt + */ +class FirewallMap implements FirewallMapInterface +{ + protected $container; + protected $map; + + public function __construct(ContainerInterface $container, array $map) + { + $this->container = $container; + $this->map = $map; + } + + public function getListeners(Request $request) + { + foreach ($this->map as $contextId => $requestMatcher) { + if (null === $requestMatcher || $requestMatcher->matches($request)) { + return $this->container->get($contextId)->getContext(); + } + } + + return array(array(), null); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php new file mode 100644 index 0000000..f2dfc99 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/SecurityBundle.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSecurityVotersPass; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicLdapFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpDigestFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\X509Factory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RemoteUserFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\LdapFactory; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class SecurityBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $extension = $container->getExtension('security'); + $extension->addSecurityListenerFactory(new FormLoginFactory()); + $extension->addSecurityListenerFactory(new FormLoginLdapFactory()); + $extension->addSecurityListenerFactory(new HttpBasicFactory()); + $extension->addSecurityListenerFactory(new HttpBasicLdapFactory()); + $extension->addSecurityListenerFactory(new HttpDigestFactory()); + $extension->addSecurityListenerFactory(new RememberMeFactory()); + $extension->addSecurityListenerFactory(new X509Factory()); + $extension->addSecurityListenerFactory(new RemoteUserFactory()); + $extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory()); + $extension->addSecurityListenerFactory(new SimpleFormFactory()); + $extension->addSecurityListenerFactory(new GuardAuthenticationFactory()); + + $extension->addUserProviderFactory(new InMemoryFactory()); + $extension->addUserProviderFactory(new LdapFactory()); + $container->addCompilerPass(new AddSecurityVotersPass()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php new file mode 100644 index 0000000..abb6de3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Templating\Helper; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; +use Symfony\Component\Templating\Helper\Helper; + +/** + * LogoutUrlHelper provides generator functions for the logout URL. + * + * @author Jeremy Mikola + */ +class LogoutUrlHelper extends Helper +{ + private $generator; + + /** + * Constructor. + * + * @param LogoutUrlGenerator $generator A LogoutUrlGenerator instance + */ + public function __construct(LogoutUrlGenerator $generator) + { + $this->generator = $generator; + } + + /** + * Generates the absolute logout path for the firewall. + * + * @param string|null $key The firewall key or null to use the current firewall key + * + * @return string The logout path + */ + public function getLogoutPath($key) + { + return $this->generator->getLogoutPath($key, UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * Generates the absolute logout URL for the firewall. + * + * @param string|null $key The firewall key or null to use the current firewall key + * + * @return string The logout URL + */ + public function getLogoutUrl($key) + { + return $this->generator->getLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'logout_url'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/SecurityHelper.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/SecurityHelper.php new file mode 100644 index 0000000..7df8511 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Templating/Helper/SecurityHelper.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Templating\Helper; + +use Symfony\Component\Security\Acl\Voter\FieldVote; +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; + +/** + * SecurityHelper provides read-only access to the security checker. + * + * @author Fabien Potencier + */ +class SecurityHelper extends Helper +{ + private $securityChecker; + + public function __construct(AuthorizationCheckerInterface $securityChecker = null) + { + $this->securityChecker = $securityChecker; + } + + public function isGranted($role, $object = null, $field = null) + { + if (null === $this->securityChecker) { + return false; + } + + if (null !== $field) { + $object = new FieldVote($object, $field); + } + + return $this->securityChecker->isGranted($role, $object); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'security'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php new file mode 100644 index 0000000..d8435e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DataCollector; + +use Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Security\Core\Role\RoleHierarchy; + +class SecurityDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollectWhenSecurityIsDisabled() + { + $collector = new SecurityDataCollector(); + $collector->collect($this->getRequest(), $this->getResponse()); + + $this->assertSame('security', $collector->getName()); + $this->assertFalse($collector->isEnabled()); + $this->assertFalse($collector->isAuthenticated()); + $this->assertNull($collector->getTokenClass()); + $this->assertFalse($collector->supportsRoleHierarchy()); + $this->assertCount(0, $collector->getRoles()); + $this->assertCount(0, $collector->getInheritedRoles()); + $this->assertEmpty($collector->getUser()); + } + + public function testCollectWhenAuthenticationTokenIsNull() + { + $tokenStorage = new TokenStorage(); + $collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy()); + $collector->collect($this->getRequest(), $this->getResponse()); + + $this->assertTrue($collector->isEnabled()); + $this->assertFalse($collector->isAuthenticated()); + $this->assertNull($collector->getTokenClass()); + $this->assertTrue($collector->supportsRoleHierarchy()); + $this->assertCount(0, $collector->getRoles()); + $this->assertCount(0, $collector->getInheritedRoles()); + $this->assertEmpty($collector->getUser()); + } + + /** @dataProvider provideRoles */ + public function testCollectAuthenticationTokenAndRoles(array $roles, array $normalizedRoles, array $inheritedRoles) + { + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken(new UsernamePasswordToken('hhamon', 'P4$$w0rD', 'provider', $roles)); + + $collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy()); + $collector->collect($this->getRequest(), $this->getResponse()); + + $this->assertTrue($collector->isEnabled()); + $this->assertTrue($collector->isAuthenticated()); + $this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()); + $this->assertTrue($collector->supportsRoleHierarchy()); + $this->assertSame($normalizedRoles, $collector->getRoles()); + $this->assertSame($inheritedRoles, $collector->getInheritedRoles()); + $this->assertSame('hhamon', $collector->getUser()); + } + + public function provideRoles() + { + return array( + // Basic roles + array( + array('ROLE_USER'), + array('ROLE_USER'), + array(), + ), + array( + array(new Role('ROLE_USER')), + array('ROLE_USER'), + array(), + ), + // Inherited roles + array( + array('ROLE_ADMIN'), + array('ROLE_ADMIN'), + array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'), + ), + array( + array(new Role('ROLE_ADMIN')), + array('ROLE_ADMIN'), + array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'), + ), + ); + } + + private function getRoleHierarchy() + { + return new RoleHierarchy(array( + 'ROLE_ADMIN' => array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'), + )); + } + + private function getRequest() + { + return $this + ->getMockBuilder('Symfony\Component\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + } + + private function getResponse() + { + return $this + ->getMockBuilder('Symfony\Component\HttpFoundation\Response') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php new file mode 100644 index 0000000..c6aef2b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -0,0 +1,272 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +abstract class CompleteConfigurationTest extends \PHPUnit_Framework_TestCase +{ + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testRolesHierarchy() + { + $container = $this->getContainer('container1'); + $this->assertEquals(array( + 'ROLE_ADMIN' => array('ROLE_USER'), + 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'), + 'ROLE_REMOTE' => array('ROLE_USER', 'ROLE_ADMIN'), + ), $container->getParameter('security.role_hierarchy.roles')); + } + + public function testUserProviders() + { + $container = $this->getContainer('container1'); + + $providers = array_values(array_filter($container->getServiceIds(), function ($key) { return 0 === strpos($key, 'security.user.provider.concrete'); })); + + $expectedProviders = array( + 'security.user.provider.concrete.default', + 'security.user.provider.concrete.default_foo', + 'security.user.provider.concrete.digest', + 'security.user.provider.concrete.digest_foo', + 'security.user.provider.concrete.basic', + 'security.user.provider.concrete.basic_foo', + 'security.user.provider.concrete.basic_bar', + 'security.user.provider.concrete.service', + 'security.user.provider.concrete.chain', + ); + + $this->assertEquals(array(), array_diff($expectedProviders, $providers)); + $this->assertEquals(array(), array_diff($providers, $expectedProviders)); + + // chain provider + $this->assertEquals(array(array( + new Reference('security.user.provider.concrete.service'), + new Reference('security.user.provider.concrete.basic'), + )), $container->getDefinition('security.user.provider.concrete.chain')->getArguments()); + } + + public function testFirewalls() + { + $container = $this->getContainer('container1'); + + $arguments = $container->getDefinition('security.firewall.map')->getArguments(); + $listeners = array(); + foreach (array_keys($arguments[1]) as $contextId) { + $contextDef = $container->getDefinition($contextId); + $arguments = $contextDef->getArguments(); + $listeners[] = array_map(function ($ref) { return (string) $ref; }, $arguments['index_0']); + } + + $this->assertEquals(array( + array(), + array( + 'security.channel_listener', + 'security.logout_listener.secure', + 'security.authentication.listener.x509.secure', + 'security.authentication.listener.remote_user.secure', + 'security.authentication.listener.form.secure', + 'security.authentication.listener.basic.secure', + 'security.authentication.listener.digest.secure', + 'security.authentication.listener.rememberme.secure', + 'security.authentication.listener.anonymous.secure', + 'security.authentication.switchuser_listener.secure', + 'security.access_listener', + ), + array( + 'security.channel_listener', + 'security.context_listener.0', + 'security.authentication.listener.basic.host', + 'security.authentication.listener.anonymous.host', + 'security.access_listener', + ), + array( + 'security.channel_listener', + 'security.context_listener.1', + 'security.authentication.listener.basic.with_user_checker', + 'security.authentication.listener.anonymous.with_user_checker', + 'security.access_listener', + ), + ), $listeners); + } + + public function testFirewallRequestMatchers() + { + $container = $this->getContainer('container1'); + + $arguments = $container->getDefinition('security.firewall.map')->getArguments(); + $matchers = array(); + + foreach ($arguments[1] as $reference) { + if ($reference instanceof Reference) { + $definition = $container->getDefinition((string) $reference); + $matchers[] = $definition->getArguments(); + } + } + + $this->assertEquals(array( + array( + '/login', + ), + array( + '/test', + 'foo\\.example\\.org', + array('GET', 'POST'), + ), + ), $matchers); + } + + public function testAccess() + { + $container = $this->getContainer('container1'); + + $rules = array(); + foreach ($container->getDefinition('security.access_map')->getMethodCalls() as $call) { + if ($call[0] == 'add') { + $rules[] = array((string) $call[1][0], $call[1][1], $call[1][2]); + } + } + + $matcherIds = array(); + foreach ($rules as list($matcherId, $attributes, $channel)) { + $requestMatcher = $container->getDefinition($matcherId); + + $this->assertFalse(isset($matcherIds[$matcherId])); + $matcherIds[$matcherId] = true; + + $i = count($matcherIds); + if (1 === $i) { + $this->assertEquals(array('ROLE_USER'), $attributes); + $this->assertEquals('https', $channel); + $this->assertEquals( + array('/blog/524', null, array('GET', 'POST')), + $requestMatcher->getArguments() + ); + } elseif (2 === $i) { + $this->assertEquals(array('IS_AUTHENTICATED_ANONYMOUSLY'), $attributes); + $this->assertNull($channel); + $this->assertEquals( + array('/blog/.*'), + $requestMatcher->getArguments() + ); + } elseif (3 === $i) { + $this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]); + $expression = $container->getDefinition($attributes[1])->getArgument(0); + $this->assertEquals("token.getUsername() matches '/^admin/'", $expression); + } + } + } + + public function testMerge() + { + $container = $this->getContainer('merge'); + + $this->assertEquals(array( + 'FOO' => array('MOO'), + 'ADMIN' => array('USER'), + ), $container->getParameter('security.role_hierarchy.roles')); + } + + public function testEncoders() + { + $container = $this->getContainer('container1'); + + $this->assertEquals(array(array( + 'JMS\FooBundle\Entity\User1' => array( + 'class' => 'Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder', + 'arguments' => array(false), + ), + 'JMS\FooBundle\Entity\User2' => array( + 'class' => 'Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder', + 'arguments' => array('sha1', false, 5), + ), + 'JMS\FooBundle\Entity\User3' => array( + 'class' => 'Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder', + 'arguments' => array('md5', true, 5000), + ), + 'JMS\FooBundle\Entity\User4' => new Reference('security.encoder.foo'), + 'JMS\FooBundle\Entity\User5' => array( + 'class' => 'Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder', + 'arguments' => array('sha1', false, 5, 30), + ), + 'JMS\FooBundle\Entity\User6' => array( + 'class' => 'Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder', + 'arguments' => array(15), + ), + )), $container->getDefinition('security.encoder_factory.generic')->getArguments()); + } + + public function testAcl() + { + $container = $this->getContainer('container1'); + + $this->assertTrue($container->hasDefinition('security.acl.dbal.provider')); + $this->assertEquals('security.acl.dbal.provider', (string) $container->getAlias('security.acl.provider')); + } + + public function testCustomAclProvider() + { + $container = $this->getContainer('custom_acl_provider'); + + $this->assertFalse($container->hasDefinition('security.acl.dbal.provider')); + $this->assertEquals('foo', (string) $container->getAlias('security.acl.provider')); + } + + public function testRememberMeThrowExceptionsDefault() + { + $container = $this->getContainer('container1'); + $this->assertTrue($container->getDefinition('security.authentication.listener.rememberme.secure')->getArgument(5)); + } + + public function testRememberMeThrowExceptions() + { + $container = $this->getContainer('remember_me_options'); + $service = $container->getDefinition('security.authentication.listener.rememberme.main'); + $this->assertEquals('security.authentication.rememberme.services.persistent.main', $service->getArgument(1)); + $this->assertFalse($service->getArgument(5)); + } + + public function testUserCheckerConfig() + { + $this->assertEquals('app.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.with_user_checker')); + } + + public function testUserCheckerConfigWithDefaultChecker() + { + $this->assertEquals('security.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.host')); + } + + public function testUserCheckerConfigWithNoCheckers() + { + $this->assertEquals('security.user_checker', $this->getContainer('container1')->getAlias('security.user_checker.secure')); + } + + protected function getContainer($file) + { + $container = new ContainerBuilder(); + $security = new SecurityExtension(); + $container->registerExtension($security); + + $bundle = new SecurityBundle(); + $bundle->build($container); // Attach all default factories + $this->loadFromFile($container, $file); + + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/UserProvider/DummyProvider.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/UserProvider/DummyProvider.php new file mode 100644 index 0000000..55d3d6e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/UserProvider/DummyProvider.php @@ -0,0 +1,23 @@ +loadFromExtension('security', array( + 'acl' => array(), + 'encoders' => array( + 'JMS\FooBundle\Entity\User1' => 'plaintext', + 'JMS\FooBundle\Entity\User2' => array( + 'algorithm' => 'sha1', + 'encode_as_base64' => false, + 'iterations' => 5, + ), + 'JMS\FooBundle\Entity\User3' => array( + 'algorithm' => 'md5', + ), + 'JMS\FooBundle\Entity\User4' => array( + 'id' => 'security.encoder.foo', + ), + 'JMS\FooBundle\Entity\User5' => array( + 'algorithm' => 'pbkdf2', + 'hash_algorithm' => 'sha1', + 'encode_as_base64' => false, + 'iterations' => 5, + 'key_length' => 30, + ), + 'JMS\FooBundle\Entity\User6' => array( + 'algorithm' => 'bcrypt', + 'cost' => 15, + ), + ), + 'providers' => array( + 'default' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'), + ), + ), + ), + 'digest' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER, ROLE_ADMIN'), + ), + ), + ), + 'basic' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => 'ROLE_SUPER_ADMIN'), + 'bar' => array('password' => '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'roles' => array('ROLE_USER', 'ROLE_ADMIN')), + ), + ), + ), + 'service' => array( + 'id' => 'user.manager', + ), + 'chain' => array( + 'chain' => array( + 'providers' => array('service', 'basic'), + ), + ), + ), + + 'firewalls' => array( + 'simple' => array('pattern' => '/login', 'security' => false), + 'secure' => array('stateless' => true, + 'http_basic' => true, + 'http_digest' => array('secret' => 'TheSecret'), + 'form_login' => true, + 'anonymous' => true, + 'switch_user' => true, + 'x509' => true, + 'remote_user' => true, + 'logout' => true, + 'remember_me' => array('secret' => 'TheSecret'), + 'user_checker' => null, + ), + 'host' => array( + 'pattern' => '/test', + 'host' => 'foo\\.example\\.org', + 'methods' => array('GET', 'POST'), + 'anonymous' => true, + 'http_basic' => true, + ), + 'with_user_checker' => array( + 'user_checker' => 'app.user_checker', + 'anonymous' => true, + 'http_basic' => true, + ), + ), + + 'access_control' => array( + array('path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => array('get', 'POST')), + array('path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), + array('path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUsername() matches '/^admin/'"), + ), + + 'role_hierarchy' => array( + 'ROLE_ADMIN' => 'ROLE_USER', + 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'), + 'ROLE_REMOTE' => 'ROLE_USER,ROLE_ADMIN', + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/custom_acl_provider.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/custom_acl_provider.php new file mode 100644 index 0000000..351dc6c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/custom_acl_provider.php @@ -0,0 +1,9 @@ +load('container1.php', $container); + +$container->loadFromExtension('security', array( + 'acl' => array( + 'provider' => 'foo', + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php new file mode 100644 index 0000000..50ef504 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php @@ -0,0 +1,20 @@ +load('merge_import.php', $container); + +$container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'firewalls' => array( + 'main' => array( + 'form_login' => false, + 'http_basic' => null, + ), + ), + + 'role_hierarchy' => array( + 'FOO' => array('MOO'), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php new file mode 100644 index 0000000..912b912 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php @@ -0,0 +1,15 @@ +loadFromExtension('security', array( + 'firewalls' => array( + 'main' => array( + 'form_login' => array( + 'login_path' => '/login', + ), + ), + ), + 'role_hierarchy' => array( + 'FOO' => 'BAR', + 'ADMIN' => 'USER', + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php new file mode 100644 index 0000000..e0ca4f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php @@ -0,0 +1,18 @@ +loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'firewalls' => array( + 'main' => array( + 'form_login' => true, + 'remember_me' => array( + 'secret' => 'TheSecret', + 'catch_exceptions' => false, + 'token_provider' => 'token_provider_id', + ), + ), + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml new file mode 100644 index 0000000..1916755 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app.user_checker + + + ROLE_USER + ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH + ROLE_USER,ROLE_ADMIN + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_acl_provider.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_acl_provider.xml new file mode 100644 index 0000000..6addc81 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_acl_provider.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml new file mode 100644 index 0000000..8a17f6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml new file mode 100644 index 0000000..81b8cff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml new file mode 100644 index 0000000..b6ade91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml new file mode 100644 index 0000000..e8ed61e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -0,0 +1,83 @@ +security: + acl: ~ + encoders: + JMS\FooBundle\Entity\User1: plaintext + JMS\FooBundle\Entity\User2: + algorithm: sha1 + encode_as_base64: false + iterations: 5 + JMS\FooBundle\Entity\User3: + algorithm: md5 + JMS\FooBundle\Entity\User4: + id: security.encoder.foo + JMS\FooBundle\Entity\User5: + algorithm: pbkdf2 + hash_algorithm: sha1 + encode_as_base64: false + iterations: 5 + key_length: 30 + JMS\FooBundle\Entity\User6: + algorithm: bcrypt + cost: 15 + + providers: + default: + memory: + users: + foo: { password: foo, roles: ROLE_USER } + digest: + memory: + users: + foo: { password: foo, roles: 'ROLE_USER, ROLE_ADMIN' } + basic: + memory: + users: + foo: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: ROLE_SUPER_ADMIN } + bar: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: [ROLE_USER, ROLE_ADMIN] } + service: + id: user.manager + chain: + chain: + providers: [service, basic] + + + firewalls: + simple: { pattern: /login, security: false } + secure: + stateless: true + http_basic: true + http_digest: + secret: TheSecret + form_login: true + anonymous: true + switch_user: true + x509: true + remote_user: true + logout: true + remember_me: + secret: TheSecret + user_checker: ~ + + host: + pattern: /test + host: foo\.example\.org + methods: [GET,POST] + anonymous: true + http_basic: true + + with_user_checker: + anonymous: ~ + http_basic: ~ + user_checker: app.user_checker + + role_hierarchy: + ROLE_ADMIN: ROLE_USER + ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] + ROLE_REMOTE: ROLE_USER,ROLE_ADMIN + + access_control: + - { path: /blog/524, role: ROLE_USER, requires_channel: https, methods: [get, POST]} + - + path: /blog/.* + role: IS_AUTHENTICATED_ANONYMOUSLY + - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUsername() matches '/^admin/'" } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/custom_acl_provider.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/custom_acl_provider.yml new file mode 100644 index 0000000..633eed0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/custom_acl_provider.yml @@ -0,0 +1,6 @@ +imports: + - { resource: container1.yml } + +security: + acl: + provider: foo diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml new file mode 100644 index 0000000..60c0bbe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml @@ -0,0 +1,14 @@ +imports: + - { resource: merge_import.yml } + +security: + providers: + default: { id: foo } + + firewalls: + main: + form_login: false + http_basic: ~ + + role_hierarchy: + FOO: [MOO] diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml new file mode 100644 index 0000000..4f8db0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml @@ -0,0 +1,9 @@ +security: + firewalls: + main: + form_login: + login_path: /login + + role_hierarchy: + FOO: BAR + ADMIN: USER diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml new file mode 100644 index 0000000..a521c8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml @@ -0,0 +1,12 @@ +security: + providers: + default: + id: foo + + firewalls: + main: + form_login: true + remember_me: + secret: TheSecret + catch_exceptions: false + token_provider: token_provider_id diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php new file mode 100644 index 0000000..8a8f5c8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\MainConfiguration; +use Symfony\Component\Config\Definition\Processor; + +class MainConfigurationTest extends \PHPUnit_Framework_TestCase +{ + /** + * The minimal, required config needed to not have any required validation + * issues. + * + * @var array + */ + protected static $minimalConfig = array( + 'providers' => array( + 'stub' => array( + 'id' => 'foo', + ), + ), + 'firewalls' => array( + 'stub' => array(), + ), + ); + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testNoConfigForProvider() + { + $config = array( + 'providers' => array( + 'stub' => array(), + ), + ); + + $processor = new Processor(); + $configuration = new MainConfiguration(array(), array()); + $processor->processConfiguration($configuration, array($config)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testManyConfigForProvider() + { + $config = array( + 'providers' => array( + 'stub' => array( + 'id' => 'foo', + 'chain' => array(), + ), + ), + ); + + $processor = new Processor(); + $configuration = new MainConfiguration(array(), array()); + $processor->processConfiguration($configuration, array($config)); + } + + public function testCsrfAliases() + { + $config = array( + 'firewalls' => array( + 'stub' => array( + 'logout' => array( + 'csrf_token_generator' => 'a_token_generator', + 'csrf_token_id' => 'a_token_id', + ), + ), + ), + ); + $config = array_merge(static::$minimalConfig, $config); + + $processor = new Processor(); + $configuration = new MainConfiguration(array(), array()); + $processedConfig = $processor->processConfiguration($configuration, array($config)); + $this->assertTrue(isset($processedConfig['firewalls']['stub']['logout']['csrf_token_generator'])); + $this->assertEquals('a_token_generator', $processedConfig['firewalls']['stub']['logout']['csrf_token_generator']); + $this->assertTrue(isset($processedConfig['firewalls']['stub']['logout']['csrf_token_id'])); + $this->assertEquals('a_token_id', $processedConfig['firewalls']['stub']['logout']['csrf_token_id']); + } + + public function testDefaultUserCheckers() + { + $processor = new Processor(); + $configuration = new MainConfiguration(array(), array()); + $processedConfig = $processor->processConfiguration($configuration, array(static::$minimalConfig)); + + $this->assertEquals('security.user_checker', $processedConfig['firewalls']['stub']['user_checker']); + } + + public function testUserCheckers() + { + $config = array( + 'firewalls' => array( + 'stub' => array( + 'user_checker' => 'app.henk_checker', + ), + ), + ); + $config = array_merge(static::$minimalConfig, $config); + + $processor = new Processor(); + $configuration = new MainConfiguration(array(), array()); + $processedConfig = $processor->processConfiguration($configuration, array($config)); + + $this->assertEquals('app.henk_checker', $processedConfig['firewalls']['stub']['user_checker']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php new file mode 100644 index 0000000..131c38d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Config\FileLocator; + +class PhpCompleteConfigurationTest extends CompleteConfigurationTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loadXml = new PhpFileLoader($container, new FileLocator(__DIR__.'/Fixtures/php')); + $loadXml->load($file.'.php'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php new file mode 100644 index 0000000..39daf99 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class AbstractFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testCreate() + { + list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', array( + 'use_forward' => true, + 'failure_path' => '/foo', + 'success_handler' => 'custom_success_handler', + 'failure_handler' => 'custom_failure_handler', + 'remember_me' => true, + ), 'user_provider', 'entry_point'); + + // auth provider + $this->assertEquals('auth_provider', $authProviderId); + + // listener + $this->assertEquals('abstract_listener.foo', $listenerId); + $this->assertTrue($container->hasDefinition('abstract_listener.foo')); + $definition = $container->getDefinition('abstract_listener.foo'); + $this->assertEquals(array( + 'index_4' => 'foo', + 'index_5' => new Reference('security.authentication.success_handler.foo.abstract_factory'), + 'index_6' => new Reference('security.authentication.failure_handler.foo.abstract_factory'), + 'index_7' => array( + 'use_forward' => true, + ), + ), $definition->getArguments()); + + // entry point + $this->assertEquals('entry_point', $entryPointId, '->create() does not change the default entry point.'); + } + + /** + * @dataProvider getFailureHandlers + */ + public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection) + { + $options = array( + 'remember_me' => true, + 'login_path' => '/bar', + ); + + if ($serviceId) { + $options['failure_handler'] = $serviceId; + } + + list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); + + $definition = $container->getDefinition('abstract_listener.foo'); + $arguments = $definition->getArguments(); + $this->assertEquals(new Reference('security.authentication.failure_handler.foo.abstract_factory'), $arguments['index_6']); + $failureHandler = $container->findDefinition((string) $arguments['index_6']); + + $methodCalls = $failureHandler->getMethodCalls(); + if ($defaultHandlerInjection) { + $this->assertEquals('setOptions', $methodCalls[0][0]); + $this->assertEquals(array('login_path' => '/bar'), $methodCalls[0][1][0]); + } else { + $this->assertCount(0, $methodCalls); + } + } + + public function getFailureHandlers() + { + return array( + array(null, true), + array('custom_failure_handler', false), + ); + } + + /** + * @dataProvider getSuccessHandlers + */ + public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection) + { + $options = array( + 'remember_me' => true, + 'default_target_path' => '/bar', + ); + + if ($serviceId) { + $options['success_handler'] = $serviceId; + } + + list($container, $authProviderId, $listenerId, $entryPointId) = $this->callFactory('foo', $options, 'user_provider', 'entry_point'); + + $definition = $container->getDefinition('abstract_listener.foo'); + $arguments = $definition->getArguments(); + $this->assertEquals(new Reference('security.authentication.success_handler.foo.abstract_factory'), $arguments['index_5']); + $successHandler = $container->findDefinition((string) $arguments['index_5']); + $methodCalls = $successHandler->getMethodCalls(); + + if ($defaultHandlerInjection) { + $this->assertEquals('setOptions', $methodCalls[0][0]); + $this->assertEquals(array('default_target_path' => '/bar'), $methodCalls[0][1][0]); + $this->assertEquals('setProviderKey', $methodCalls[1][0]); + $this->assertEquals(array('foo'), $methodCalls[1][1]); + } else { + $this->assertCount(0, $methodCalls); + } + } + + public function getSuccessHandlers() + { + return array( + array(null, true), + array('custom_success_handler', false), + ); + } + + protected function callFactory($id, $config, $userProviderId, $defaultEntryPointId) + { + $factory = $this->getMockForAbstractClass('Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory', array()); + + $factory + ->expects($this->once()) + ->method('createAuthProvider') + ->will($this->returnValue('auth_provider')) + ; + $factory + ->expects($this->atLeastOnce()) + ->method('getListenerId') + ->will($this->returnValue('abstract_listener')) + ; + $factory + ->expects($this->any()) + ->method('getKey') + ->will($this->returnValue('abstract_factory')) + ; + + $container = new ContainerBuilder(); + $container->register('auth_provider'); + $container->register('custom_success_handler'); + $container->register('custom_failure_handler'); + + list($authProviderId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); + + return array($container, $authProviderId, $listenerId, $entryPointId); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php new file mode 100644 index 0000000..4c16348 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/GuardAuthenticationFactoryTest.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class GuardAuthenticationFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getValidConfigurationTests + */ + public function testAddValidConfiguration(array $inputConfig, array $expectedConfig) + { + $factory = new GuardAuthenticationFactory(); + $nodeDefinition = new ArrayNodeDefinition('guard'); + $factory->addConfiguration($nodeDefinition); + + $node = $nodeDefinition->getNode(); + $normalizedConfig = $node->normalize($inputConfig); + $finalizedConfig = $node->finalize($normalizedConfig); + + $this->assertEquals($expectedConfig, $finalizedConfig); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @dataProvider getInvalidConfigurationTests + */ + public function testAddInvalidConfiguration(array $inputConfig) + { + $factory = new GuardAuthenticationFactory(); + $nodeDefinition = new ArrayNodeDefinition('guard'); + $factory->addConfiguration($nodeDefinition); + + $node = $nodeDefinition->getNode(); + $normalizedConfig = $node->normalize($inputConfig); + // will validate and throw an exception on invalid + $node->finalize($normalizedConfig); + } + + public function getValidConfigurationTests() + { + $tests = array(); + + // completely basic + $tests[] = array( + array( + 'authenticators' => array('authenticator1', 'authenticator2'), + 'provider' => 'some_provider', + 'entry_point' => 'the_entry_point', + ), + array( + 'authenticators' => array('authenticator1', 'authenticator2'), + 'provider' => 'some_provider', + 'entry_point' => 'the_entry_point', + ), + ); + + // testing xml config fix: authenticator -> authenticators + $tests[] = array( + array( + 'authenticator' => array('authenticator1', 'authenticator2'), + ), + array( + 'authenticators' => array('authenticator1', 'authenticator2'), + 'entry_point' => null, + ), + ); + + return $tests; + } + + public function getInvalidConfigurationTests() + { + $tests = array(); + + // testing not empty + $tests[] = array( + array('authenticators' => array()), + ); + + return $tests; + } + + public function testBasicCreate() + { + // simple configuration + $config = array( + 'authenticators' => array('authenticator123'), + 'entry_point' => null, + ); + list($container, $entryPointId) = $this->executeCreate($config, null); + $this->assertEquals('authenticator123', $entryPointId); + + $providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall'); + $this->assertEquals(array( + 'index_0' => array(new Reference('authenticator123')), + 'index_1' => new Reference('my_user_provider'), + 'index_2' => 'my_firewall', + 'index_3' => new Reference('security.user_checker.my_firewall'), + ), $providerDefinition->getArguments()); + + $listenerDefinition = $container->getDefinition('security.authentication.listener.guard.my_firewall'); + $this->assertEquals('my_firewall', $listenerDefinition->getArgument(2)); + $this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3)); + } + + public function testExistingDefaultEntryPointUsed() + { + // any existing default entry point is used + $config = array( + 'authenticators' => array('authenticator123'), + 'entry_point' => null, + ); + list(, $entryPointId) = $this->executeCreate($config, 'some_default_entry_point'); + $this->assertEquals('some_default_entry_point', $entryPointId); + } + + /** + * @expectedException \LogicException + */ + public function testCannotOverrideDefaultEntryPoint() + { + // any existing default entry point is used + $config = array( + 'authenticators' => array('authenticator123'), + 'entry_point' => 'authenticator123', + ); + $this->executeCreate($config, 'some_default_entry_point'); + } + + /** + * @expectedException \LogicException + */ + public function testMultipleAuthenticatorsRequiresEntryPoint() + { + // any existing default entry point is used + $config = array( + 'authenticators' => array('authenticator123', 'authenticatorABC'), + 'entry_point' => null, + ); + $this->executeCreate($config, null); + } + + public function testCreateWithEntryPoint() + { + // any existing default entry point is used + $config = array( + 'authenticators' => array('authenticator123', 'authenticatorABC'), + 'entry_point' => 'authenticatorABC', + ); + list($container, $entryPointId) = $this->executeCreate($config, null); + $this->assertEquals('authenticatorABC', $entryPointId); + } + + private function executeCreate(array $config, $defaultEntryPointId) + { + $container = new ContainerBuilder(); + $container->register('security.authentication.provider.guard'); + $container->register('security.authentication.listener.guard'); + $id = 'my_firewall'; + $userProviderId = 'my_user_provider'; + + $factory = new GuardAuthenticationFactory(); + list($providerId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId); + + return array($container, $entryPointId); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php new file mode 100644 index 0000000..dce30c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\DummyProvider; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class SecurityExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The check_path "/some_area/login_check" for login method "form_login" is not matched by the firewall pattern "/secured_area/.*". + */ + public function testInvalidCheckPath() + { + $container = $this->getRawContainer(); + + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/secured_area/.*', + 'form_login' => array( + 'check_path' => '/some_area/login_check', + ), + ), + ), + )); + + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage No authentication listener registered for firewall "some_firewall" + */ + public function testFirewallWithoutAuthenticationListener() + { + $container = $this->getRawContainer(); + + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + ), + ), + )); + + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Unable to create definition for "security.user.provider.concrete.my_foo" user provider + */ + public function testFirewallWithInvalidUserProvider() + { + $container = $this->getRawContainer(); + + $extension = $container->getExtension('security'); + $extension->addUserProviderFactory(new DummyProvider()); + + $container->loadFromExtension('security', array( + 'providers' => array( + 'my_foo' => array('foo' => array()), + ), + + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => array(), + ), + ), + )); + + $container->compile(); + } + + public function testDisableRoleHierarchyVoter() + { + $container = $this->getRawContainer(); + + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'role_hierarchy' => null, + + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => null, + ), + ), + )); + + $container->compile(); + + $this->assertFalse($container->hasDefinition('security.access.role_hierarchy_voter')); + } + + protected function getRawContainer() + { + $container = new ContainerBuilder(); + $security = new SecurityExtension(); + $container->registerExtension($security); + + $bundle = new SecurityBundle(); + $bundle->build($container); + + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + + return $container; + } + + protected function getContainer() + { + $container = $this->getRawContainer(); + $container->compile(); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php new file mode 100644 index 0000000..cf6833a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\Config\FileLocator; + +class XmlCompleteConfigurationTest extends CompleteConfigurationTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loadXml = new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')); + $loadXml->load($file.'.xml'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php new file mode 100644 index 0000000..568b862 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\Config\FileLocator; + +class YamlCompleteConfigurationTest extends CompleteConfigurationTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loadXml = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml')); + $loadXml->load($file.'.yml'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php new file mode 100644 index 0000000..6ac0e6a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticationCommencingTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class AuthenticationCommencingTest extends WebTestCase +{ + public function testAuthenticationIsCommencingIfAccessDeniedExceptionIsWrapped() + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'config.yml')); + + $client->request('GET', '/secure-but-not-covered-by-access-control'); + $this->assertRedirect($client->getResponse(), '/login'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php new file mode 100644 index 0000000..1208003 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/AclBundle.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * @author Kévin Dunglas + */ +class AclBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php new file mode 100644 index 0000000..c85a589 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AclBundle/Entity/Car.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity; + +/** + * Car. + * + * @author Kévin Dunglas + */ +class Car +{ + public $id; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php new file mode 100644 index 0000000..c232b4c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; + +class LoginController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function loginAction() + { + $form = $this->container->get('form.factory')->create('Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType'); + + return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:login.html.twig', array( + 'form' => $form->createView(), + )); + } + + public function afterLoginAction() + { + return $this->container->get('templating')->renderResponse('CsrfFormLoginBundle:Login:after_login.html.twig'); + } + + public function loginCheckAction() + { + return new Response('', 400); + } + + public function secureAction() + { + throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/CsrfFormLoginBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/CsrfFormLoginBundle.php new file mode 100644 index 0000000..41309d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/CsrfFormLoginBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class CsrfFormLoginBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php new file mode 100644 index 0000000..b82e1f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\Security\Core\Security; + +/** + * Form type for use with the Security component's form-based authentication + * listener. + * + * @author Henrik Bjornskov + * @author Jeremy Mikola + */ +class UserLoginType extends AbstractType +{ + private $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('username', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('password', 'Symfony\Component\Form\Extension\Core\Type\PasswordType') + ->add('_target_path', 'Symfony\Component\Form\Extension\Core\Type\HiddenType') + ; + + $request = $this->requestStack->getCurrentRequest(); + + /* Note: since the Security component's form login listener intercepts + * the POST request, this form will never really be bound to the + * request; however, we can match the expected behavior by checking the + * session for an authentication error and last username. + */ + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($request) { + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(Security::AUTHENTICATION_ERROR); + } else { + $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); + } + + if ($error) { + $event->getForm()->addError(new FormError($error->getMessage())); + } + + $event->setData(array_replace((array) $event->getData(), array( + 'username' => $request->getSession()->get(Security::LAST_USERNAME), + ))); + }); + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + /* Note: the form's csrf_token_id must correspond to that for the form login + * listener in order for the CSRF token to validate successfully. + */ + + $resolver->setDefaults(array( + 'csrf_token_id' => 'authenticate', + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/config/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/config/routing.yml new file mode 100644 index 0000000..0a02730 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/config/routing.yml @@ -0,0 +1,30 @@ +form_login: + path: /login + defaults: { _controller: CsrfFormLoginBundle:Login:login } + +form_login_check: + path: /login_check + defaults: { _controller: CsrfFormLoginBundle:Login:loginCheck } + +form_login_homepage: + path: / + defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin } + +form_login_custom_target_path: + path: /foo + defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin } + +form_login_default_target_path: + path: /profile + defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin } + +form_login_redirect_to_protected_resource_after_login: + path: /protected-resource + defaults: { _controller: CsrfFormLoginBundle:Login:afterLogin } + +form_logout: + path: /logout_path + +form_secure_action: + path: /secure-but-not-covered-by-access-control + defaults: { _controller: CsrfFormLoginBundle:Login:secure } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig new file mode 100644 index 0000000..b56c186 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/after_login.html.twig @@ -0,0 +1,8 @@ +{% extends "::base.html.twig" %} + +{% block body %} + Hello {{ app.user.username }}!

    + You're browsing to path "{{ app.request.pathInfo }}".

    + Log out. + Log out. +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig new file mode 100644 index 0000000..36ae015 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Resources/views/Login/login.html.twig @@ -0,0 +1,12 @@ +{% extends "::base.html.twig" %} + +{% block body %} + + + {{ form_widget(form) }} + + {# Note: ensure the submit name does not conflict with the form's name or it may clobber field data #} + + + +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/DependencyInjection/FirewallEntryPointExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/DependencyInjection/FirewallEntryPointExtension.php new file mode 100644 index 0000000..90b6b3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/DependencyInjection/FirewallEntryPointExtension.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class FirewallEntryPointExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/FirewallEntryPointBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/FirewallEntryPointBundle.php new file mode 100644 index 0000000..f4247ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/FirewallEntryPointBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class FirewallEntryPointBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/Resources/config/services.xml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/Resources/config/services.xml new file mode 100644 index 0000000..76abc3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/Resources/config/services.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/Security/EntryPointStub.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/Security/EntryPointStub.php new file mode 100644 index 0000000..e1d3280 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FirewallEntryPointBundle/Security/EntryPointStub.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\Security; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; + +class EntryPointStub implements AuthenticationEntryPointInterface +{ + const RESPONSE_TEXT = '2be8e651259189d841a19eecdf37e771e2431741'; + + public function start(Request $request, AuthenticationException $authException = null) + { + return new Response(self::RESPONSE_TEXT); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php new file mode 100644 index 0000000..7a6faf4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\Security\Core\Security; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class LocalizedController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function loginAction(Request $request) + { + // get the login error if there is one + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(Security::AUTHENTICATION_ERROR); + } else { + $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); + } + + return $this->container->get('templating')->renderResponse('FormLoginBundle:Localized:login.html.twig', array( + // last username entered by the user + 'last_username' => $request->getSession()->get(Security::LAST_USERNAME), + 'error' => $error, + )); + } + + public function loginCheckAction() + { + throw new \RuntimeException('loginCheckAction() should never be called.'); + } + + public function logoutAction() + { + throw new \RuntimeException('logoutAction() should never be called.'); + } + + public function secureAction() + { + throw new \RuntimeException('secureAction() should never be called.'); + } + + public function profileAction() + { + return new Response('Profile'); + } + + public function homepageAction() + { + return new Response('Homepage'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php new file mode 100644 index 0000000..ad00315 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Security; + +class LoginController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function loginAction(Request $request) + { + // get the login error if there is one + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(Security::AUTHENTICATION_ERROR); + } else { + $error = $request->getSession()->get(Security::AUTHENTICATION_ERROR); + } + + return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:login.html.twig', array( + // last username entered by the user + 'last_username' => $request->getSession()->get(Security::LAST_USERNAME), + 'error' => $error, + )); + } + + public function afterLoginAction() + { + return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:after_login.html.twig'); + } + + public function loginCheckAction() + { + return new Response('', 400); + } + + public function secureAction() + { + throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/DependencyInjection/FormLoginExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/DependencyInjection/FormLoginExtension.php new file mode 100644 index 0000000..4bdd3e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/DependencyInjection/FormLoginExtension.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class FormLoginExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $container + ->register('localized_form_failure_handler', 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Security\LocalizedFormFailureHandler') + ->addArgument(new Reference('router')) + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php new file mode 100644 index 0000000..eab1913 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class FormLoginBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/localized_routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/localized_routing.yml new file mode 100644 index 0000000..964a74f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/localized_routing.yml @@ -0,0 +1,29 @@ +localized_login_path: + path: /{_locale}/login + defaults: { _controller: FormLoginBundle:Localized:login } + requirements: { _locale: "^[a-z]{2}$" } + +localized_check_path: + path: /{_locale}/login_check + defaults: { _controller: FormLoginBundle:Localized:loginCheck } + requirements: { _locale: "^[a-z]{2}$" } + +localized_default_target_path: + path: /{_locale}/profile + defaults: { _controller: FormLoginBundle:Localized:profile } + requirements: { _locale: "^[a-z]{2}$" } + +localized_logout_path: + path: /{_locale}/logout + defaults: { _controller: FormLoginBundle:Localized:logout } + requirements: { _locale: "^[a-z]{2}$" } + +localized_logout_target_path: + path: /{_locale}/ + defaults: { _controller: FormLoginBundle:Localized:homepage } + requirements: { _locale: "^[a-z]{2}$" } + +localized_secure_path: + path: /{_locale}/secure/ + defaults: { _controller: FormLoginBundle:Localized:secure } + requirements: { _locale: "^[a-z]{2}$" } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml new file mode 100644 index 0000000..6992f80 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml @@ -0,0 +1,42 @@ +form_login: + path: /login + defaults: { _controller: FormLoginBundle:Login:login } + +form_login_check: + path: /login_check + defaults: { _controller: FormLoginBundle:Login:loginCheck } + +form_login_homepage: + path: / + defaults: { _controller: FormLoginBundle:Login:afterLogin } + +form_login_custom_target_path: + path: /foo + defaults: { _controller: FormLoginBundle:Login:afterLogin } + +form_login_default_target_path: + path: /profile + defaults: { _controller: FormLoginBundle:Login:afterLogin } + +form_login_redirect_to_protected_resource_after_login: + path: /protected_resource + defaults: { _controller: FormLoginBundle:Login:afterLogin } + +highly_protected_resource: + path: /highly_protected_resource + +secured-by-one-ip: + path: /secured-by-one-ip + +secured-by-two-ips: + path: /secured-by-two-ips + +form_logout: + path: /logout_path + +form_secure_action: + path: /secure-but-not-covered-by-access-control + defaults: { _controller: FormLoginBundle:Login:secure } + +protected-via-expression: + path: /protected-via-expression diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig new file mode 100644 index 0000000..60dd2f1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Localized/login.html.twig @@ -0,0 +1,21 @@ +{% extends "::base.html.twig" %} + +{% block body %} + + {% if error %} +
    {{ error.message }}
    + {% endif %} + +
    + + + + + + + + + +
    + +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig new file mode 100644 index 0000000..3ef1f9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig @@ -0,0 +1,16 @@ +{% extends "::base.html.twig" %} + +{% block body %} + Hello {{ app.user.username }}!

    + You're browsing to path "{{ app.request.pathInfo }}". + + Log out. + Log out. + + Log out. + Log out. + + Log out. + Log out. + +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig new file mode 100644 index 0000000..6c1a224 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/login.html.twig @@ -0,0 +1,21 @@ +{% extends "::base.html.twig" %} + +{% block body %} + + {% if error %} +
    {{ error.message }}
    + {% endif %} + +
    + + + + + + + + + +
    + +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php new file mode 100644 index 0000000..7b97199 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Security; + +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; + +class LocalizedFormFailureHandler implements AuthenticationFailureHandlerInterface +{ + private $router; + + public function __construct(RouterInterface $router) + { + $this->router = $router; + } + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception) + { + return new RedirectResponse($this->router->generate('localized_login_path', array(), UrlGeneratorInterface::ABSOLUTE_URL)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php new file mode 100644 index 0000000..80211f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class CsrfFormLoginTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testFormLoginAndLogoutWithCsrfTokens($config) + { + $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['user_login[username]'] = 'johannes'; + $form['user_login[password]'] = 'test'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/profile'); + + $crawler = $client->followRedirect(); + + $text = $crawler->text(); + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/profile".', $text); + + $logoutLinks = $crawler->selectLink('Log out')->links(); + $this->assertCount(2, $logoutLinks); + $this->assertContains('_csrf_token=', $logoutLinks[0]->getUri()); + $this->assertSame($logoutLinks[0]->getUri(), $logoutLinks[1]->getUri()); + + $client->click($logoutLinks[0]); + + $this->assertRedirect($client->getResponse(), '/'); + } + + /** + * @dataProvider getConfigs + */ + public function testFormLoginWithInvalidCsrfToken($config) + { + $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['user_login[_token]'] = ''; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/login'); + + $text = $client->followRedirect()->text(); + $this->assertContains('Invalid CSRF token.', $text); + } + + /** + * @dataProvider getConfigs + */ + public function testFormLoginWithCustomTargetPath($config) + { + $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['user_login[username]'] = 'johannes'; + $form['user_login[password]'] = 'test'; + $form['user_login[_target_path]'] = '/foo'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/foo'); + + $text = $client->followRedirect()->text(); + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/foo".', $text); + } + + /** + * @dataProvider getConfigs + */ + public function testFormLoginRedirectsToProtectedResourceAfterLogin($config) + { + $client = $this->createClient(array('test_case' => 'CsrfFormLogin', 'root_config' => $config)); + + $client->request('GET', '/protected-resource'); + $this->assertRedirect($client->getResponse(), '/login'); + + $form = $client->followRedirect()->selectButton('login')->form(); + $form['user_login[username]'] = 'johannes'; + $form['user_login[password]'] = 'test'; + $client->submit($form); + $this->assertRedirect($client->getResponse(), '/protected-resource'); + + $text = $client->followRedirect()->text(); + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/protected-resource".', $text); + } + + public function getConfigs() + { + return array( + array('config.yml'), + array('routes_as_path.yml'), + ); + } + + public static function setUpBeforeClass() + { + parent::deleteTmpDir('CsrfFormLogin'); + } + + public static function tearDownAfterClass() + { + parent::deleteTmpDir('CsrfFormLogin'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php new file mode 100644 index 0000000..30d0935 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FirewallEntryPointTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\Security\EntryPointStub; + +class FirewallEntryPointTest extends WebTestCase +{ + public function testItUsesTheConfiguredEntryPointWhenUsingUnknownCredentials() + { + $client = $this->createClient(array('test_case' => 'FirewallEntryPoint')); + + $client->request('GET', '/secure/resource', array(), array(), array( + 'PHP_AUTH_USER' => 'unknown', + 'PHP_AUTH_PW' => 'credentials', + )); + + $this->assertEquals( + EntryPointStub::RESPONSE_TEXT, + $client->getResponse()->getContent(), + "Custom entry point wasn't started" + ); + } + + public function testItUsesTheConfiguredEntryPointFromTheExceptionListenerWithFormLoginAndNoCredentials() + { + $client = $this->createClient(array('test_case' => 'FirewallEntryPoint', 'root_config' => 'config_form_login.yml')); + + $client->request('GET', '/secure/resource'); + + $this->assertEquals( + EntryPointStub::RESPONSE_TEXT, + $client->getResponse()->getContent(), + "Custom entry point wasn't started" + ); + } + + public static function setUpBeforeClass() + { + parent::deleteTmpDir('FirewallEntryPoint'); + } + + public static function tearDownAfterClass() + { + parent::deleteTmpDir('FirewallEntryPoint'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php new file mode 100644 index 0000000..2f19f3f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class FormLoginTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testFormLogin($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/profile'); + + $text = $client->followRedirect()->text(); + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/profile".', $text); + } + + /** + * @dataProvider getConfigs + */ + public function testFormLogout($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/profile'); + + $crawler = $client->followRedirect(); + $text = $crawler->text(); + + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/profile".', $text); + + $logoutLinks = $crawler->selectLink('Log out')->links(); + $this->assertCount(6, $logoutLinks); + $this->assertSame($logoutLinks[0]->getUri(), $logoutLinks[1]->getUri()); + $this->assertSame($logoutLinks[2]->getUri(), $logoutLinks[3]->getUri()); + $this->assertSame($logoutLinks[4]->getUri(), $logoutLinks[5]->getUri()); + + $this->assertNotSame($logoutLinks[0]->getUri(), $logoutLinks[2]->getUri()); + $this->assertNotSame($logoutLinks[1]->getUri(), $logoutLinks[3]->getUri()); + + $this->assertSame($logoutLinks[0]->getUri(), $logoutLinks[4]->getUri()); + $this->assertSame($logoutLinks[1]->getUri(), $logoutLinks[5]->getUri()); + } + + /** + * @dataProvider getConfigs + */ + public function testFormLoginWithCustomTargetPath($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $form['_target_path'] = '/foo'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/foo'); + + $text = $client->followRedirect()->text(); + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/foo".', $text); + } + + /** + * @dataProvider getConfigs + */ + public function testFormLoginRedirectsToProtectedResourceAfterLogin($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + + $client->request('GET', '/protected_resource'); + $this->assertRedirect($client->getResponse(), '/login'); + + $form = $client->followRedirect()->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $client->submit($form); + $this->assertRedirect($client->getResponse(), '/protected_resource'); + + $text = $client->followRedirect()->text(); + $this->assertContains('Hello johannes!', $text); + $this->assertContains('You\'re browsing to path "/protected_resource".', $text); + } + + public function getConfigs() + { + return array( + array('config.yml'), + array('routes_as_path.yml'), + ); + } + + public static function setUpBeforeClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } + + public static function tearDownAfterClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php new file mode 100644 index 0000000..14c3179 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class LocalizedRoutesAsPathTest extends WebTestCase +{ + /** + * @dataProvider getLocales + */ + public function testLoginLogoutProcedure($locale) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml')); + + $crawler = $client->request('GET', '/'.$locale.'/login'); + $form = $crawler->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/'.$locale.'/profile'); + $this->assertEquals('Profile', $client->followRedirect()->text()); + + $client->request('GET', '/'.$locale.'/logout'); + $this->assertRedirect($client->getResponse(), '/'.$locale.'/'); + $this->assertEquals('Homepage', $client->followRedirect()->text()); + } + + /** + * @dataProvider getLocales + */ + public function testLoginFailureWithLocalizedFailurePath($locale) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_form_failure_handler.yml')); + + $crawler = $client->request('GET', '/'.$locale.'/login'); + $form = $crawler->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'foobar'; + $client->submit($form); + + $this->assertRedirect($client->getResponse(), '/'.$locale.'/login'); + } + + /** + * @dataProvider getLocales + */ + public function testAccessRestrictedResource($locale) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes.yml')); + + $client->request('GET', '/'.$locale.'/secure/'); + $this->assertRedirect($client->getResponse(), '/'.$locale.'/login'); + } + + /** + * @dataProvider getLocales + */ + public function testAccessRestrictedResourceWithForward($locale) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'localized_routes_with_forward.yml')); + + $crawler = $client->request('GET', '/'.$locale.'/secure/'); + $this->assertCount(1, $crawler->selectButton('login'), (string) $client->getResponse()); + } + + public function getLocales() + { + return array(array('en'), array('de')); + } + + public static function setUpBeforeClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } + + public static function tearDownAfterClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php new file mode 100644 index 0000000..c7db437 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class SecurityRoutingIntegrationTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testRoutingErrorIsNotExposedForProtectedResourceWhenAnonymous($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->request('GET', '/protected_resource'); + + $this->assertRedirect($client->getResponse(), '/login'); + } + + /** + * @dataProvider getConfigs + */ + public function testRoutingErrorIsExposedWhenNotProtected($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + $client->request('GET', '/unprotected_resource'); + + $this->assertEquals(404, $client->getResponse()->getStatusCode(), (string) $client->getResponse()); + } + + /** + * @dataProvider getConfigs + */ + public function testRoutingErrorIsNotExposedForProtectedResourceWhenLoggedInWithInsufficientRights($config) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config)); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $client->submit($form); + + $client->request('GET', '/highly_protected_resource'); + + $this->assertNotEquals(404, $client->getResponse()->getStatusCode()); + } + + /** + * @dataProvider getConfigs + */ + public function testSecurityConfigurationForSingleIPAddress($config) + { + $allowedClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array('REMOTE_ADDR' => '10.10.10.10')); + $barredClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array('REMOTE_ADDR' => '10.10.20.10')); + + $this->assertAllowed($allowedClient, '/secured-by-one-ip'); + $this->assertRestricted($barredClient, '/secured-by-one-ip'); + } + + /** + * @dataProvider getConfigs + */ + public function testSecurityConfigurationForMultipleIPAddresses($config) + { + $allowedClientA = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array('REMOTE_ADDR' => '1.1.1.1')); + $allowedClientB = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array('REMOTE_ADDR' => '2.2.2.2')); + $barredClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array('REMOTE_ADDR' => '192.168.1.1')); + + $this->assertAllowed($allowedClientA, '/secured-by-two-ips'); + $this->assertAllowed($allowedClientB, '/secured-by-two-ips'); + $this->assertRestricted($barredClient, '/secured-by-two-ips'); + } + + /** + * @dataProvider getConfigs + */ + public function testSecurityConfigurationForExpression($config) + { + $allowedClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array('HTTP_USER_AGENT' => 'Firefox 1.0')); + $this->assertAllowed($allowedClient, '/protected-via-expression'); + + $barredClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array()); + $this->assertRestricted($barredClient, '/protected-via-expression'); + + $allowedClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array()); + + $allowedClient->request('GET', '/protected-via-expression'); + $form = $allowedClient->followRedirect()->selectButton('login')->form(); + $form['_username'] = 'johannes'; + $form['_password'] = 'test'; + $allowedClient->submit($form); + $this->assertRedirect($allowedClient->getResponse(), '/protected-via-expression'); + $this->assertAllowed($allowedClient, '/protected-via-expression'); + } + + private function assertAllowed($client, $path) + { + $client->request('GET', $path); + $this->assertEquals(404, $client->getResponse()->getStatusCode()); + } + + private function assertRestricted($client, $path) + { + $client->request('GET', $path); + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + } + + public function getConfigs() + { + return array(array('config.yml'), array('routes_as_path.yml')); + } + + public static function setUpBeforeClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } + + public static function tearDownAfterClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php new file mode 100644 index 0000000..db4c51c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SetAclCommandTest.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\SecurityBundle\Command\InitAclCommand; +use Symfony\Bundle\SecurityBundle\Command\SetAclCommand; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Security\Acl\Domain\ObjectIdentity; +use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; +use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; +use Symfony\Component\Security\Acl\Exception\NoAceFoundException; +use Symfony\Component\Security\Acl\Permission\BasicPermissionMap; + +/** + * Tests SetAclCommand. + * + * @author Kévin Dunglas + * @requires extension pdo_sqlite + */ +class SetAclCommandTest extends WebTestCase +{ + const OBJECT_CLASS = 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\Entity\Car'; + const SECURITY_CLASS = 'Symfony\Component\Security\Core\User\User'; + + protected function setUp() + { + parent::setUp(); + + $this->deleteTmpDir('Acl'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->deleteTmpDir('Acl'); + } + + public function testSetAclUser() + { + $objectId = 1; + $securityUsername1 = 'kevin'; + $securityUsername2 = 'anne'; + $grantedPermission1 = 'VIEW'; + $grantedPermission2 = 'EDIT'; + + $application = $this->getApplication(); + $application->add(new SetAclCommand()); + + $setAclCommand = $application->find('acl:set'); + $setAclCommandTester = new CommandTester($setAclCommand); + $setAclCommandTester->execute(array( + 'command' => 'acl:set', + 'arguments' => array($grantedPermission1, $grantedPermission2, sprintf('%s:%s', self::OBJECT_CLASS, $objectId)), + '--user' => array(sprintf('%s:%s', self::SECURITY_CLASS, $securityUsername1), sprintf('%s:%s', self::SECURITY_CLASS, $securityUsername2)), + )); + + $objectIdentity = new ObjectIdentity($objectId, self::OBJECT_CLASS); + $securityIdentity1 = new UserSecurityIdentity($securityUsername1, self::SECURITY_CLASS); + $securityIdentity2 = new UserSecurityIdentity($securityUsername2, self::SECURITY_CLASS); + $permissionMap = new BasicPermissionMap(); + + /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ + $aclProvider = $application->getKernel()->getContainer()->get('security.acl.provider'); + $acl = $aclProvider->findAcl($objectIdentity, array($securityIdentity1)); + + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission1, null), array($securityIdentity1))); + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission1, null), array($securityIdentity2))); + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission2, null), array($securityIdentity2))); + + try { + $acl->isGranted($permissionMap->getMasks('OWNER', null), array($securityIdentity1)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + + try { + $acl->isGranted($permissionMap->getMasks('OPERATOR', null), array($securityIdentity2)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + } + + public function testSetAclRole() + { + $objectId = 1; + $securityUsername = 'kevin'; + $grantedPermission = 'VIEW'; + $role = 'ROLE_ADMIN'; + + $application = $this->getApplication(); + $application->add(new SetAclCommand()); + + $setAclCommand = $application->find('acl:set'); + $setAclCommandTester = new CommandTester($setAclCommand); + $setAclCommandTester->execute(array( + 'command' => 'acl:set', + 'arguments' => array($grantedPermission, sprintf('%s:%s', str_replace('\\', '/', self::OBJECT_CLASS), $objectId)), + '--role' => array($role), + )); + + $objectIdentity = new ObjectIdentity($objectId, self::OBJECT_CLASS); + $userSecurityIdentity = new UserSecurityIdentity($securityUsername, self::SECURITY_CLASS); + $roleSecurityIdentity = new RoleSecurityIdentity($role); + $permissionMap = new BasicPermissionMap(); + + /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ + $aclProvider = $application->getKernel()->getContainer()->get('security.acl.provider'); + $acl = $aclProvider->findAcl($objectIdentity, array($roleSecurityIdentity, $userSecurityIdentity)); + + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + $this->assertTrue($acl->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + + try { + $acl->isGranted($permissionMap->getMasks('VIEW', null), array($userSecurityIdentity)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + + try { + $acl->isGranted($permissionMap->getMasks('OPERATOR', null), array($userSecurityIdentity)); + $this->fail('NoAceFoundException not throwed'); + } catch (NoAceFoundException $e) { + } + } + + public function testSetAclClassScope() + { + $objectId = 1; + $grantedPermission = 'VIEW'; + $role = 'ROLE_USER'; + + $application = $this->getApplication(); + $application->add(new SetAclCommand()); + + $setAclCommand = $application->find('acl:set'); + $setAclCommandTester = new CommandTester($setAclCommand); + $setAclCommandTester->execute(array( + 'command' => 'acl:set', + 'arguments' => array($grantedPermission, sprintf('%s:%s', self::OBJECT_CLASS, $objectId)), + '--class-scope' => true, + '--role' => array($role), + )); + + $objectIdentity1 = new ObjectIdentity($objectId, self::OBJECT_CLASS); + $objectIdentity2 = new ObjectIdentity(2, self::OBJECT_CLASS); + $roleSecurityIdentity = new RoleSecurityIdentity($role); + $permissionMap = new BasicPermissionMap(); + + /** @var \Symfony\Component\Security\Acl\Model\AclProviderInterface $aclProvider */ + $aclProvider = $application->getKernel()->getContainer()->get('security.acl.provider'); + + $acl1 = $aclProvider->findAcl($objectIdentity1, array($roleSecurityIdentity)); + $this->assertTrue($acl1->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + + $acl2 = $aclProvider->createAcl($objectIdentity2); + $this->assertTrue($acl2->isGranted($permissionMap->getMasks($grantedPermission, null), array($roleSecurityIdentity))); + } + + private function getApplication() + { + $kernel = $this->createKernel(array('test_case' => 'Acl')); + $kernel->boot(); + + $application = new Application($kernel); + $application->add(new InitAclCommand()); + + $initAclCommand = $application->find('init:acl'); + $initAclCommandTester = new CommandTester($initAclCommand); + $initAclCommandTester->execute(array('command' => 'init:acl')); + + return $application; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php new file mode 100644 index 0000000..e5079c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class SwitchUserTest extends WebTestCase +{ + /** + * @dataProvider getTestParameters + */ + public function testSwitchUser($originalUser, $targetUser, $expectedUser, $expectedStatus) + { + $client = $this->createAuthenticatedClient($originalUser); + + $client->request('GET', '/profile?_switch_user='.$targetUser); + + $this->assertEquals($expectedStatus, $client->getResponse()->getStatusCode()); + $this->assertEquals($expectedUser, $client->getProfile()->getCollector('security')->getUser()); + } + + public function testSwitchedUserCannotSwitchToOther() + { + $client = $this->createAuthenticatedClient('user_can_switch'); + + $client->request('GET', '/profile?_switch_user=user_cannot_switch_1'); + $client->request('GET', '/profile?_switch_user=user_cannot_switch_2'); + + $this->assertEquals(500, $client->getResponse()->getStatusCode()); + $this->assertEquals('user_cannot_switch_1', $client->getProfile()->getCollector('security')->getUser()); + } + + public function testSwitchedUserExit() + { + $client = $this->createAuthenticatedClient('user_can_switch'); + + $client->request('GET', '/profile?_switch_user=user_cannot_switch_1'); + $client->request('GET', '/profile?_switch_user=_exit'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + $this->assertEquals('user_can_switch', $client->getProfile()->getCollector('security')->getUser()); + } + + public function getTestParameters() + { + return array( + 'unauthorized_user_cannot_switch' => array('user_cannot_switch_1', 'user_cannot_switch_1', 'user_cannot_switch_1', 403), + 'authorized_user_can_switch' => array('user_can_switch', 'user_cannot_switch_1', 'user_cannot_switch_1', 200), + 'authorized_user_cannot_switch_to_non_existent' => array('user_can_switch', 'user_does_not_exist', 'user_can_switch', 500), + 'authorized_user_can_switch_to_himself' => array('user_can_switch', 'user_can_switch', 'user_can_switch', 200), + ); + } + + protected function createAuthenticatedClient($username) + { + $client = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => 'switchuser.yml')); + $client->followRedirects(true); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['_username'] = $username; + $form['_password'] = 'test'; + $client->submit($form); + + return $client; + } + + public static function setUpBeforeClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } + + public static function tearDownAfterClass() + { + parent::deleteTmpDir('StandardFormLogin'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php new file mode 100644 index 0000000..86a69fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/UserPasswordEncoderCommandTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder; + +/** + * Tests UserPasswordEncoderCommand. + * + * @author Sarah Khalil + */ +class UserPasswordEncoderCommandTest extends WebTestCase +{ + private $passwordEncoderCommandTester; + + public function testEncodePasswordEmptySalt() + { + $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + 'user-class' => 'Symfony\Component\Security\Core\User\User', + '--empty-salt' => true, + ), array('decorated' => false)); + $expected = str_replace("\n", PHP_EOL, file_get_contents(__DIR__.'/app/PasswordEncode/emptysalt.txt')); + + $this->assertEquals($expected, $this->passwordEncoderCommandTester->getDisplay()); + } + + public function testEncodeNoPasswordNoInteraction() + { + $statusCode = $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + ), array('interactive' => false)); + + $this->assertContains('[ERROR] The password must not be empty.', $this->passwordEncoderCommandTester->getDisplay()); + $this->assertEquals($statusCode, 1); + } + + public function testEncodePasswordBcrypt() + { + $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + 'user-class' => 'Custom\Class\Bcrypt\User', + ), array('interactive' => false)); + + $output = $this->passwordEncoderCommandTester->getDisplay(); + $this->assertContains('Password encoding succeeded', $output); + + $encoder = new BCryptPasswordEncoder(17); + preg_match('# Encoded password\s{1,}([\w+\/$.]+={0,2})\s+#', $output, $matches); + $hash = $matches[1]; + $this->assertTrue($encoder->isPasswordValid($hash, 'password', null)); + } + + public function testEncodePasswordPbkdf2() + { + $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + 'user-class' => 'Custom\Class\Pbkdf2\User', + ), array('interactive' => false)); + + $output = $this->passwordEncoderCommandTester->getDisplay(); + $this->assertContains('Password encoding succeeded', $output); + + $encoder = new Pbkdf2PasswordEncoder('sha512', true, 1000); + preg_match('# Encoded password\s{1,}([\w+\/]+={0,2})\s+#', $output, $matches); + $hash = $matches[1]; + preg_match('# Generated salt\s{1,}([\w+\/]+={0,2})\s+#', $output, $matches); + $salt = $matches[1]; + $this->assertTrue($encoder->isPasswordValid($hash, 'password', $salt)); + } + + public function testEncodePasswordOutput() + { + $this->passwordEncoderCommandTester->execute( + array( + 'command' => 'security:encode-password', + 'password' => 'p@ssw0rd', + ), array('interactive' => false) + ); + + $this->assertContains('Password encoding succeeded', $this->passwordEncoderCommandTester->getDisplay()); + $this->assertContains(' Encoded password p@ssw0rd', $this->passwordEncoderCommandTester->getDisplay()); + $this->assertContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay()); + } + + public function testEncodePasswordEmptySaltOutput() + { + $this->passwordEncoderCommandTester->execute( + array( + 'command' => 'security:encode-password', + 'password' => 'p@ssw0rd', + '--empty-salt' => true, + ) + ); + + $this->assertContains('Password encoding succeeded', $this->passwordEncoderCommandTester->getDisplay()); + $this->assertContains(' Encoded password p@ssw0rd', $this->passwordEncoderCommandTester->getDisplay()); + $this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay()); + } + + public function testEncodePasswordBcryptOutput() + { + $this->passwordEncoderCommandTester->execute( + array( + 'command' => 'security:encode-password', + 'password' => 'p@ssw0rd', + 'user-class' => 'Custom\Class\Bcrypt\User', + ) + ); + + $this->assertNotContains(' Generated salt ', $this->passwordEncoderCommandTester->getDisplay()); + } + + public function testEncodePasswordNoConfigForGivenUserClass() + { + $this->setExpectedException('\RuntimeException', 'No encoder has been configured for account "Foo\Bar\User".'); + + $this->passwordEncoderCommandTester->execute(array( + 'command' => 'security:encode-password', + 'password' => 'password', + 'user-class' => 'Foo\Bar\User', + ), array('interactive' => false)); + } + + protected function setUp() + { + $kernel = $this->createKernel(array('test_case' => 'PasswordEncode')); + $kernel->boot(); + + $application = new Application($kernel); + + $application->add(new UserPasswordEncoderCommand()); + $passwordEncoderCommand = $application->find('security:encode-password'); + + $this->passwordEncoderCommandTester = new CommandTester($passwordEncoderCommand); + } + + protected function tearDown() + { + $this->passwordEncoderCommandTester = null; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php new file mode 100644 index 0000000..33da902 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/WebTestCase.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Kernel; + +class WebTestCase extends BaseWebTestCase +{ + public static function assertRedirect($response, $location) + { + self::assertTrue($response->isRedirect(), 'Response is not a redirect, got status code: '.substr($response, 0, 2000)); + self::assertEquals('http://localhost'.$location, $response->headers->get('Location')); + } + + protected static function deleteTmpDir($testCase) + { + if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$testCase)) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } + + protected static function getKernelClass() + { + require_once __DIR__.'/app/AppKernel.php'; + + return 'Symfony\Bundle\SecurityBundle\Tests\Functional\app\AppKernel'; + } + + protected static function createKernel(array $options = array()) + { + $class = self::getKernelClass(); + + if (!isset($options['test_case'])) { + throw new \InvalidArgumentException('The option "test_case" must be set.'); + } + + return new $class( + $options['test_case'], + isset($options['root_config']) ? $options['root_config'] : 'config.yml', + isset($options['environment']) ? $options['environment'] : 'securitybundletest'.strtolower($options['test_case']), + isset($options['debug']) ? $options['debug'] : true + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php new file mode 100644 index 0000000..5133791 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/bundles.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), + new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AclBundle\AclBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml new file mode 100644 index 0000000..33eadbc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Acl/config.yml @@ -0,0 +1,24 @@ +imports: + - { resource: ./../config/framework.yml } + +doctrine: + dbal: + driver: pdo_sqlite + memory: true + charset: UTF8 + +security: + firewalls: + test: + pattern: ^/ + security: false + acl: + connection: default + encoders: + Symfony\Component\Security\Core\User\User: plaintext + providers: + in_memory: + memory: + users: + kevin: { password: test, roles: [ROLE_USER] } + anne: { password: test, roles: [ROLE_ADMIN]} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php new file mode 100644 index 0000000..b828c5a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\app; + +// get the autoload file +$dir = __DIR__; +$lastDir = null; +while ($dir !== $lastDir) { + $lastDir = $dir; + + if (is_file($dir.'/autoload.php')) { + require_once $dir.'/autoload.php'; + break; + } + + if (is_file($dir.'/autoload.php.dist')) { + require_once $dir.'/autoload.php.dist'; + break; + } + + if (file_exists($dir.'/vendor/autoload.php')) { + require_once $dir.'/vendor/autoload.php'; + break; + } + + $dir = dirname($dir); +} + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Kernel; + +/** + * App Test Kernel for functional tests. + * + * @author Johannes M. Schmitt + */ +class AppKernel extends Kernel +{ + private $testCase; + private $rootConfig; + + public function __construct($testCase, $rootConfig, $environment, $debug) + { + if (!is_dir(__DIR__.'/'.$testCase)) { + throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); + } + $this->testCase = $testCase; + + $fs = new Filesystem(); + if (!$fs->isAbsolutePath($rootConfig) && !is_file($rootConfig = __DIR__.'/'.$testCase.'/'.$rootConfig)) { + throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig)); + } + $this->rootConfig = $rootConfig; + + parent::__construct($environment, $debug); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + if (null === $this->name) { + $this->name = parent::getName().md5($this->rootConfig); + } + + return $this->name; + } + + public function registerBundles() + { + if (!is_file($filename = $this->getRootDir().'/'.$this->testCase.'/bundles.php')) { + throw new \RuntimeException(sprintf('The bundles file "%s" does not exist.', $filename)); + } + + return include $filename; + } + + public function getRootDir() + { + return __DIR__; + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/'.$this->testCase.'/logs'; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load($this->rootConfig); + } + + public function serialize() + { + return serialize(array($this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); + } + + public function unserialize($str) + { + $a = unserialize($str); + $this->__construct($a[0], $a[1], $a[2], $a[3]); + } + + protected function getKernelParameters() + { + $parameters = parent::getKernelParameters(); + $parameters['kernel.test_case'] = $this->testCase; + + return $parameters; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php new file mode 100644 index 0000000..c16ab12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\TwigBundle\TwigBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\CsrfFormLoginBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml new file mode 100644 index 0000000..5a00ac3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml @@ -0,0 +1,47 @@ +imports: + - { resource: ./../config/default.yml } + +services: + csrf_form_login.form.type: + class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\Form\UserLoginType + arguments: + - '@request_stack' + tags: + - { name: form.type } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + # This firewall doesn't make sense in combination with the rest of the + # configuration file, but it's here for testing purposes (do not use + # this file in a real world scenario though) + login_form: + pattern: ^/login$ + security: false + + default: + form_login: + check_path: /login_check + default_target_path: /profile + target_path_parameter: "user_login[_target_path]" + failure_path_parameter: "user_login[_failure_path]" + username_parameter: "user_login[username]" + password_parameter: "user_login[password]" + csrf_parameter: "user_login[_token]" + csrf_token_generator: security.csrf.token_manager + anonymous: ~ + logout: + path: /logout_path + target: / + csrf_token_generator: security.csrf.token_manager + + access_control: + - { path: .*, roles: IS_AUTHENTICATED_FULLY } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml new file mode 100644 index 0000000..d481e6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml @@ -0,0 +1,13 @@ +imports: + - { resource: ./config.yml } + +security: + firewalls: + default: + form_login: + login_path: form_login + check_path: form_login_check + default_target_path: form_login_default_target_path + logout: + path: form_logout + target: form_login_homepage diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routing.yml new file mode 100644 index 0000000..ecfae00 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routing.yml @@ -0,0 +1,2 @@ +_csrf_form_login_bundle: + resource: '@CsrfFormLoginBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php new file mode 100644 index 0000000..412e1c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\FirewallEntryPointBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml new file mode 100644 index 0000000..955c411 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml @@ -0,0 +1,32 @@ +framework: + secret: test + router: { resource: "%kernel.root_dir%/%kernel.test_case%/routing.yml" } + validation: { enabled: true, enable_annotations: true } + csrf_protection: true + form: true + test: ~ + default_locale: en + session: + storage_id: session.storage.mock_file + profiler: { only_exceptions: false } + +services: + logger: { class: Psr\Log\NullLogger } + +security: + firewalls: + secure: + pattern: ^/secure/ + http_basic: { realm: "Secure Gateway API" } + entry_point: firewall_entry_point.entry_point.stub + default: + anonymous: ~ + access_control: + - { path: ^/secure/, roles: ROLE_SECURE } + providers: + in_memory: + memory: + users: + john: { password: doe, roles: [ROLE_SECURE] } + encoders: + Symfony\Component\Security\Core\User\User: plaintext diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config_form_login.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config_form_login.yml new file mode 100644 index 0000000..8763b08 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config_form_login.yml @@ -0,0 +1,9 @@ +imports: + - { resource: ./config.yml } + +security: + firewalls: + secure: + pattern: ^/ + form_login: + check_path: /login_check diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/routing.yml new file mode 100644 index 0000000..f3ff912 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/routing.yml @@ -0,0 +1,2 @@ +secure_resource: + path: /secure/resource diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php new file mode 100644 index 0000000..2e92437 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return array( + new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/config.yml new file mode 100644 index 0000000..82416b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/config.yml @@ -0,0 +1,27 @@ +imports: + - { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + Custom\Class\Bcrypt\User: + algorithm: bcrypt + cost: 10 + Custom\Class\Pbkdf2\User: + algorithm: pbkdf2 + hash_algorithm: sha512 + encode_as_base64: true + iterations: 1000 + Custom\Class\Test\User: test + + providers: + in_memory: + memory: + users: + user: { password: userpass, roles: [ 'ROLE_USER' ] } + admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] } + + firewalls: + test: + pattern: ^/ + security: false diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/emptysalt.txt b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/emptysalt.txt new file mode 100644 index 0000000..9c8d3de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/emptysalt.txt @@ -0,0 +1,13 @@ + +Symfony Password Encoder Utility +================================ + + ------------------ ------------------------------------------------------------------ + Key Value + ------------------ ------------------------------------------------------------------ + Encoder used Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder + Encoded password password + ------------------ ------------------------------------------------------------------ + + [OK] Password encoding succeeded + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Resources/views/base.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Resources/views/base.html.twig new file mode 100644 index 0000000..caf6f6e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Resources/views/base.html.twig @@ -0,0 +1,12 @@ + + + + + {% block title %}Welcome!{% endblock %} + {% block stylesheets %}{% endblock %} + + + {% block body %}{% endblock %} + {% block javascripts %}{% endblock %} + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php new file mode 100644 index 0000000..d53ff3e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\FormLoginBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new SecurityBundle(), + new TwigBundle(), + new FormLoginBundle(), +); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml new file mode 100644 index 0000000..19b9d89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml @@ -0,0 +1,44 @@ +imports: + - { resource: ./../config/default.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + # This firewall doesn't make sense in combination with the rest of the + # configuration file, but it's here for testing purposes (do not use + # this file in a real world scenario though) + login_form: + pattern: ^/login$ + security: false + + default: + form_login: + check_path: /login_check + default_target_path: /profile + logout: ~ + anonymous: ~ + + # This firewall is here just to check its the logout functionality + second_area: + http_basic: ~ + anonymous: ~ + logout: + target: /second/target + path: /second/logout + + access_control: + - { path: ^/unprotected_resource$, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/secure-but-not-covered-by-access-control$, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/secured-by-one-ip$, ip: 10.10.10.10, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/secured-by-two-ips$, ips: [1.1.1.1, 2.2.2.2], roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/highly_protected_resource$, roles: IS_ADMIN } + - { path: ^/protected-via-expression$, allow_if: "(is_anonymous() and request.headers.get('user-agent') matches '/Firefox/i') or has_role('ROLE_USER')" } + - { path: .*, roles: IS_AUTHENTICATED_FULLY } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml new file mode 100644 index 0000000..e01ed36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml @@ -0,0 +1,20 @@ +imports: + - { resource: ./../config/default.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + login_path: localized_login_path + check_path: localized_check_path + failure_handler: localized_form_failure_handler + anonymous: ~ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml new file mode 100644 index 0000000..5251fd1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml @@ -0,0 +1,26 @@ +imports: + - { resource: ./../config/default.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + login_path: localized_login_path + check_path: localized_check_path + default_target_path: localized_default_target_path + logout: + path: localized_logout_path + target: localized_logout_target_path + anonymous: ~ + + access_control: + - { path: '^/(?:[a-z]{2})/secure/.*', roles: ROLE_USER } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml new file mode 100644 index 0000000..12d90d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml @@ -0,0 +1,9 @@ +imports: + - { resource: ./localized_routes.yml } + +security: + firewalls: + default: + form_login: + use_forward: true + failure_forward: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml new file mode 100644 index 0000000..d481e6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml @@ -0,0 +1,13 @@ +imports: + - { resource: ./config.yml } + +security: + firewalls: + default: + form_login: + login_path: form_login + check_path: form_login_check + default_target_path: form_login_default_target_path + logout: + path: form_logout + target: form_login_homepage diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routing.yml new file mode 100644 index 0000000..0920ea1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routing.yml @@ -0,0 +1,5 @@ +_form_login_bundle: + resource: '@FormLoginBundle/Resources/config/routing.yml' + +_form_login_localized: + resource: '@FormLoginBundle/Resources/config/localized_routing.yml' diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml new file mode 100644 index 0000000..2f144aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml @@ -0,0 +1,14 @@ +imports: + - { resource: ./config.yml } + +security: + providers: + in_memory: + memory: + users: + user_can_switch: { password: test, roles: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH] } + user_cannot_switch_1: { password: test, roles: [ROLE_USER] } + user_cannot_switch_2: { password: test, roles: [ROLE_USER] } + firewalls: + default: + switch_user: true diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/default.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/default.yml new file mode 100644 index 0000000..7a0717f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/default.yml @@ -0,0 +1,3 @@ +imports: + - { resource: framework.yml } + - { resource: twig.yml } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml new file mode 100644 index 0000000..2fa2338 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml @@ -0,0 +1,15 @@ +framework: + secret: test + router: { resource: "%kernel.root_dir%/%kernel.test_case%/routing.yml" } + validation: { enabled: true, enable_annotations: true } + assets: ~ + csrf_protection: true + form: true + test: ~ + default_locale: en + session: + storage_id: session.storage.mock_file + profiler: { only_exceptions: false } + +services: + logger: { class: Psr\Log\NullLogger } diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml new file mode 100644 index 0000000..861b1e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/twig.yml @@ -0,0 +1,7 @@ +framework: + templating: { engines: ['twig'] } + +# Twig Configuration +twig: + debug: %kernel.debug% + strict_variables: %kernel.debug% diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/composer.json b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/composer.json new file mode 100644 index 0000000..b185aac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/composer.json @@ -0,0 +1,57 @@ +{ + "name": "symfony/security-bundle", + "type": "symfony-bundle", + "description": "Symfony SecurityBundle", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/security": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "symfony/browser-kit": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/form": "~2.8|~3.0", + "symfony/framework-bundle": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/security-acl": "~2.8|~3.0", + "symfony/twig-bundle": "~2.8|~3.0", + "symfony/twig-bridge": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/validator": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "doctrine/doctrine-bundle": "~1.4", + "twig/twig": "~1.23|~2.0" + }, + "suggest": { + "symfony/security-acl": "For using the ACL functionality of this bundle" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\SecurityBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist new file mode 100644 index 0000000..a7fdc32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CHANGELOG.md new file mode 100644 index 0000000..90f7cbd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CHANGELOG.md @@ -0,0 +1,36 @@ +CHANGELOG +========= + +2.7.0 +----- + + * made it possible to configure the default formats for both the `date` and the `number_format` filter + * added support for the new Asset component (from Twig bridge) + * deprecated the assets extension (use the one from the Twig bridge instead) + +2.6.0 +----- + + * [BC BREAK] changed exception.json.twig to match same structure as error.json.twig making clients independent of runtime environment. + +2.3.0 +----- + + * added option to configure a custom template escaping guesser (via `autoescape_service` and `autoescape_service_method`) + +2.2.0 +----- + + * moved the exception controller to be a service (`twig.controller.exception:showAction` vs `Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController::showAction`) + * added support for multiple loaders via the "twig.loader" tag. + * added automatic registration of namespaced paths for registered bundles + * added support for namespaced paths + +2.1.0 +----- + + * added a new setting ("paths") to configure more paths for the Twig filesystem loader + * added contextual escaping based on the template file name (disabled if you explicitly pass an autoescape option) + * added a command that extracts translation messages from templates + * added the real template name when an error occurs in a Twig template + * added the twig:lint command that will validate a Twig template syntax. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php new file mode 100644 index 0000000..9eea901 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheCacheWarmer.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\CacheWarmer; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface; +use Symfony\Component\Templating\TemplateReference; + +/** + * Generates the Twig cache for all templates. + * + * This warmer must be registered after TemplatePathsCacheWarmer, + * as the Twig loader will need the cache generated by it. + * + * @author Fabien Potencier + */ +class TemplateCacheCacheWarmer implements CacheWarmerInterface +{ + protected $container; + protected $finder; + private $paths; + + /** + * Constructor. + * + * @param ContainerInterface $container The dependency injection container + * @param TemplateFinderInterface $finder The template paths cache warmer + * @param array $paths Additional twig paths to warm + */ + public function __construct(ContainerInterface $container, TemplateFinderInterface $finder = null, array $paths = array()) + { + // We don't inject the Twig environment directly as it depends on the + // template locator (via the loader) which might be a cached one. + // The cached template locator is available once the TemplatePathsCacheWarmer + // has been warmed up. + // But it can also be null if templating has been disabled. + $this->container = $container; + $this->finder = $finder; + $this->paths = $paths; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + if (null === $this->finder) { + return; + } + + $twig = $this->container->get('twig'); + + $templates = $this->finder->findAllTemplates(); + + foreach ($this->paths as $path => $namespace) { + $templates = array_merge($templates, $this->findTemplatesInFolder($namespace, $path)); + } + + foreach ($templates as $template) { + if ('twig' !== $template->get('engine')) { + continue; + } + + try { + $twig->loadTemplate($template); + } catch (\Twig_Error $e) { + // problem during compilation, give up + } + } + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always true + */ + public function isOptional() + { + return true; + } + + /** + * Find templates in the given directory. + * + * @param string $namespace The namespace for these templates + * @param string $dir The folder where to look for templates + * + * @return array An array of templates of type TemplateReferenceInterface + */ + private function findTemplatesInFolder($namespace, $dir) + { + if (!is_dir($dir)) { + return array(); + } + + $templates = array(); + $finder = new Finder(); + + foreach ($finder->files()->followLinks()->in($dir) as $file) { + $name = $file->getRelativePathname(); + $templates[] = new TemplateReference( + $namespace ? sprintf('@%s/%s', $namespace, $name) : $name, + 'twig' + ); + } + + return $templates; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php new file mode 100644 index 0000000..aefb5c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +/** + * Generates the Twig cache for all templates. + * + * @author Fabien Potencier + */ +class TemplateCacheWarmer implements CacheWarmerInterface +{ + private $twig; + private $iterator; + + public function __construct(\Twig_Environment $twig, \Traversable $iterator) + { + $this->twig = $twig; + $this->iterator = $iterator; + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + foreach ($this->iterator as $template) { + try { + $this->twig->loadTemplate($template); + } catch (\Twig_Error $e) { + // problem during compilation, give up + // might be a syntax error or a non-Twig template + } + } + } + + /** + * {@inheritdoc} + */ + public function isOptional() + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/DebugCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/DebugCommand.php new file mode 100644 index 0000000..e2f25cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/DebugCommand.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Command; + +use Symfony\Bridge\Twig\Command\DebugCommand as BaseDebugCommand; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; + +/** + * Lists twig functions, filters, globals and tests present in the current project. + * + * @author Jordi Boggiano + */ +final class DebugCommand extends BaseDebugCommand implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * {@inheritdoc} + */ + protected function getTwigEnvironment() + { + return $this->container->get('twig'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php new file mode 100644 index 0000000..36e4f05 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Command; + +use Symfony\Bridge\Twig\Command\LintCommand as BaseLintCommand; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\Finder\Finder; + +/** + * Command that will validate your template syntax and output encountered errors. + * + * @author Marc Weistroff + * @author Jérôme Tamarelle + */ +final class LintCommand extends BaseLintCommand implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * {@inheritdoc} + */ + protected function getTwigEnvironment() + { + return $this->container->get('twig'); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + parent::configure(); + + $this + ->setHelp( + $this->getHelp().<<php %command.full_name% @AcmeDemoBundle + +EOF + ) + ; + } + + protected function findFiles($filename) + { + if (0 === strpos($filename, '@')) { + $dir = $this->getApplication()->getKernel()->locateResource($filename); + + return Finder::create()->files()->in($dir)->name('*.twig'); + } + + return parent::findFiles($filename); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php new file mode 100644 index 0000000..3f9d52c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/ExceptionController.php @@ -0,0 +1,145 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Controller; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * ExceptionController renders error or exception pages for a given + * FlattenException. + * + * @author Fabien Potencier + * @author Matthias Pigulla + */ +class ExceptionController +{ + protected $twig; + + /** + * @var bool Show error (false) or exception (true) pages by default. + */ + protected $debug; + + public function __construct(\Twig_Environment $twig, $debug) + { + $this->twig = $twig; + $this->debug = $debug; + } + + /** + * Converts an Exception to a Response. + * + * A "showException" request parameter can be used to force display of an error page (when set to false) or + * the exception page (when true). If it is not present, the "debug" value passed into the constructor will + * be used. + * + * @param Request $request The request + * @param FlattenException $exception A FlattenException instance + * @param DebugLoggerInterface $logger A DebugLoggerInterface instance + * + * @return Response + * + * @throws \InvalidArgumentException When the exception template does not exist + */ + public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) + { + $currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1)); + $showException = $request->attributes->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC + + $code = $exception->getStatusCode(); + + return new Response($this->twig->render( + (string) $this->findTemplate($request, $request->getRequestFormat(), $code, $showException), + array( + 'status_code' => $code, + 'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '', + 'exception' => $exception, + 'logger' => $logger, + 'currentContent' => $currentContent, + ) + )); + } + + /** + * @param int $startObLevel + * + * @return string + */ + protected function getAndCleanOutputBuffering($startObLevel) + { + if (ob_get_level() <= $startObLevel) { + return ''; + } + + Response::closeOutputBuffers($startObLevel + 1, true); + + return ob_get_clean(); + } + + /** + * @param Request $request + * @param string $format + * @param int $code An HTTP response status code + * @param bool $showException + * + * @return string + */ + protected function findTemplate(Request $request, $format, $code, $showException) + { + $name = $showException ? 'exception' : 'error'; + if ($showException && 'html' == $format) { + $name = 'exception_full'; + } + + // For error pages, try to find a template for the specific HTTP status code and format + if (!$showException) { + $template = sprintf('@Twig/Exception/%s%s.%s.twig', $name, $code, $format); + if ($this->templateExists($template)) { + return $template; + } + } + + // try to find a template for the given format + $template = sprintf('@Twig/Exception/%s.%s.twig', $name, $format); + if ($this->templateExists($template)) { + return $template; + } + + // default to a generic HTML exception + $request->setRequestFormat('html'); + + return sprintf('@Twig/Exception/%s.html.twig', $showException ? 'exception_full' : $name); + } + + // to be removed when the minimum required version of Twig is >= 3.0 + protected function templateExists($template) + { + $template = (string) $template; + + $loader = $this->twig->getLoader(); + if ($loader instanceof \Twig_ExistsLoaderInterface) { + return $loader->exists($template); + } + + try { + $loader->getSource($template); + + return true; + } catch (\Twig_Error_Loader $e) { + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php new file mode 100644 index 0000000..a907ef0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Controller/PreviewErrorController.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Controller; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * PreviewErrorController can be used to test error pages. + * + * It will create a test exception and forward it to another controller. + * + * @author Matthias Pigulla + */ +class PreviewErrorController +{ + protected $kernel; + protected $controller; + + public function __construct(HttpKernelInterface $kernel, $controller) + { + $this->kernel = $kernel; + $this->controller = $controller; + } + + public function previewErrorPageAction(Request $request, $code) + { + $exception = FlattenException::create(new \Exception('Something has intentionally gone wrong.'), $code); + + /* + * This Request mimics the parameters set by + * \Symfony\Component\HttpKernel\EventListener\ExceptionListener::duplicateRequest, with + * the additional "showException" flag. + */ + + $subRequest = $request->duplicate(null, null, array( + '_controller' => $this->controller, + 'exception' => $exception, + 'logger' => null, + 'format' => $request->getRequestFormat(), + 'showException' => false, + )); + + return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php new file mode 100644 index 0000000..9fbfd65 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Registers the Twig exception listener if Twig is registered as a templating engine. + * + * @author Fabien Potencier + */ +class ExceptionListenerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('twig')) { + return; + } + + // register the exception controller only if Twig is enabled + if ($container->hasParameter('templating.engines')) { + $engines = $container->getParameter('templating.engines'); + if (!in_array('twig', $engines)) { + $container->removeDefinition('twig.exception_listener'); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php new file mode 100644 index 0000000..2aaf085 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Jean-François Simon + */ +class ExtensionPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->has('form.extension')) { + $container->getDefinition('twig.extension.form')->addTag('twig.extension'); + $reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension'); + $container->getDefinition('twig.loader.filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form')); + } + + if ($container->has('translator')) { + $container->getDefinition('twig.extension.trans')->addTag('twig.extension'); + } + + if ($container->has('router')) { + $container->getDefinition('twig.extension.routing')->addTag('twig.extension'); + } + + if ($container->has('fragment.handler')) { + $container->getDefinition('twig.extension.httpkernel')->addTag('twig.extension'); + + // inject Twig in the hinclude service if Twig is the only registered templating engine + if ( + !$container->hasParameter('templating.engines') + || array('twig') == $container->getParameter('templating.engines') + ) { + $container->getDefinition('fragment.renderer.hinclude') + ->addTag('kernel.fragment_renderer', array('alias' => 'hinclude')) + ->replaceArgument(0, new Reference('twig')) + ; + } + } + + if ($container->has('request_stack')) { + $container->getDefinition('twig.extension.httpfoundation')->addTag('twig.extension'); + } + + if ($container->hasParameter('templating.helper.code.file_link_format')) { + $container->getDefinition('twig.extension.code')->replaceArgument(0, $container->getParameter('templating.helper.code.file_link_format')); + } + + if ($container->getParameter('kernel.debug')) { + $container->getDefinition('twig.extension.profiler')->addTag('twig.extension'); + $container->getDefinition('twig.extension.debug')->addTag('twig.extension'); + } + + if (!$container->has('templating')) { + $loader = $container->getDefinition('twig.loader.native_filesystem'); + $loader->addTag('twig.loader'); + $loader->setMethodCalls($container->getDefinition('twig.loader.filesystem')->getMethodCalls()); + + $container->setDefinition('twig.loader.filesystem', $loader); + } + + if ($container->has('assets.packages')) { + $container->getDefinition('twig.extension.assets')->addTag('twig.extension'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php new file mode 100644 index 0000000..927e4b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds tagged twig.extension services to twig service. + * + * @author Fabien Potencier + */ +class TwigEnvironmentPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('twig')) { + return; + } + + $definition = $container->getDefinition('twig'); + + // Extensions must always be registered before everything else. + // For instance, global variable definitions must be registered + // afterward. If not, the globals from the extensions will never + // be registered. + $calls = $definition->getMethodCalls(); + $definition->setMethodCalls(array()); + foreach ($container->findTaggedServiceIds('twig.extension') as $id => $attributes) { + $definition->addMethodCall('addExtension', array(new Reference($id))); + } + $definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php new file mode 100644 index 0000000..bc3b71c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigLoaderPass.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * Adds services tagged twig.loader as Twig loaders. + * + * @author Daniel Leech + */ +class TwigLoaderPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('twig')) { + return; + } + + // register additional template loaders + $loaderIds = $container->findTaggedServiceIds('twig.loader'); + + if (count($loaderIds) === 0) { + throw new LogicException('No twig loaders found. You need to tag at least one loader with "twig.loader"'); + } + + if (count($loaderIds) === 1) { + $container->setAlias('twig.loader', key($loaderIds)); + } else { + $chainLoader = $container->getDefinition('twig.loader.chain'); + + $prioritizedLoaders = array(); + + foreach ($loaderIds as $id => $tags) { + foreach ($tags as $tag) { + $priority = isset($tag['priority']) ? $tag['priority'] : 0; + $prioritizedLoaders[$priority][] = $id; + } + } + + krsort($prioritizedLoaders); + + foreach ($prioritizedLoaders as $loaders) { + foreach ($loaders as $loader) { + $chainLoader->addMethodCall('addLoader', array(new Reference($loader))); + } + } + + $container->setAlias('twig.loader', 'twig.loader.chain'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..380dfe0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * TwigExtension configuration structure. + * + * @author Jeremy Mikola + */ +class Configuration implements ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('twig'); + + $rootNode + ->children() + ->scalarNode('exception_controller')->defaultValue('twig.controller.exception:showAction')->end() + ->end() + ; + + $this->addFormThemesSection($rootNode); + $this->addGlobalsSection($rootNode); + $this->addTwigOptions($rootNode); + $this->addTwigFormatOptions($rootNode); + + return $treeBuilder; + } + + private function addFormThemesSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('form_theme') + ->children() + ->arrayNode('form_themes') + ->addDefaultChildrenIfNoneSet() + ->prototype('scalar')->defaultValue('form_div_layout.html.twig')->end() + ->example(array('MyBundle::form.html.twig')) + ->validate() + ->ifTrue(function ($v) { return !in_array('form_div_layout.html.twig', $v); }) + ->then(function ($v) { + return array_merge(array('form_div_layout.html.twig'), $v); + }) + ->end() + ->end() + ->end() + ; + } + + private function addGlobalsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('global') + ->children() + ->arrayNode('globals') + ->normalizeKeys(false) + ->useAttributeAsKey('key') + ->example(array('foo' => '"@bar"', 'pi' => 3.14)) + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) { return is_string($v) && 0 === strpos($v, '@'); }) + ->then(function ($v) { + if (0 === strpos($v, '@@')) { + return substr($v, 1); + } + + return array('id' => substr($v, 1), 'type' => 'service'); + }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { + if (is_array($v)) { + $keys = array_keys($v); + sort($keys); + + return $keys !== array('id', 'type') && $keys !== array('value'); + } + + return true; + }) + ->then(function ($v) { return array('value' => $v); }) + ->end() + ->children() + ->scalarNode('id')->end() + ->scalarNode('type') + ->validate() + ->ifNotInArray(array('service')) + ->thenInvalid('The %s type is not supported') + ->end() + ->end() + ->variableNode('value')->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTwigOptions(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('path') + ->children() + ->variableNode('autoescape')->defaultValue('filename')->end() + ->scalarNode('autoescape_service')->defaultNull()->end() + ->scalarNode('autoescape_service_method')->defaultNull()->end() + ->scalarNode('base_template_class')->example('Twig_Template')->cannotBeEmpty()->end() + ->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end() + ->scalarNode('charset')->defaultValue('%kernel.charset%')->end() + ->booleanNode('debug')->defaultValue('%kernel.debug%')->end() + ->booleanNode('strict_variables')->end() + ->scalarNode('auto_reload')->end() + ->integerNode('optimizations')->min(-1)->end() + ->arrayNode('paths') + ->normalizeKeys(false) + ->useAttributeAsKey('paths') + ->beforeNormalization() + ->always() + ->then(function ($paths) { + $normalized = array(); + foreach ($paths as $path => $namespace) { + if (is_array($namespace)) { + // xml + $path = $namespace['value']; + $namespace = $namespace['namespace']; + } + + // path within the default namespace + if (ctype_digit((string) $path)) { + $path = $namespace; + $namespace = null; + } + + $normalized[$path] = $namespace; + } + + return $normalized; + }) + ->end() + ->prototype('variable')->end() + ->end() + ->end() + ; + } + + private function addTwigFormatOptions(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('date') + ->info('The default format options used by the date filter') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('format')->defaultValue('F j, Y H:i')->end() + ->scalarNode('interval_format')->defaultValue('%d days')->end() + ->scalarNode('timezone') + ->info('The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used') + ->defaultNull() + ->end() + ->end() + ->end() + ->arrayNode('number_format') + ->info('The default format options for the number_format filter') + ->addDefaultsIfNotSet() + ->children() + ->integerNode('decimals')->defaultValue(0)->end() + ->scalarNode('decimal_point')->defaultValue('.')->end() + ->scalarNode('thousands_separator')->defaultValue(',')->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php new file mode 100644 index 0000000..21e9a1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configurator/EnvironmentConfigurator.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection\Configurator; + +/** + * Twig environment configurator. + * + * @author Christian Flothmann + */ +class EnvironmentConfigurator +{ + private $dateFormat; + private $intervalFormat; + private $timezone; + private $decimals; + private $decimalPoint; + private $thousandsSeparator; + + public function __construct($dateFormat, $intervalFormat, $timezone, $decimals, $decimalPoint, $thousandsSeparator) + { + $this->dateFormat = $dateFormat; + $this->intervalFormat = $intervalFormat; + $this->timezone = $timezone; + $this->decimals = $decimals; + $this->decimalPoint = $decimalPoint; + $this->thousandsSeparator = $thousandsSeparator; + } + + public function configure(\Twig_Environment $environment) + { + $environment->getExtension('core')->setDateFormat($this->dateFormat, $this->intervalFormat); + + if (null !== $this->timezone) { + $environment->getExtension('core')->setTimezone($this->timezone); + } + + $environment->getExtension('core')->setNumberFormat($this->decimals, $this->decimalPoint, $this->thousandsSeparator); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php new file mode 100644 index 0000000..5296ac5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +/** + * TwigExtension. + * + * @author Fabien Potencier + * @author Jeremy Mikola + */ +class TwigExtension extends Extension +{ + /** + * Responds to the twig configuration parameter. + * + * @param array $configs + * @param ContainerBuilder $container + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('twig.xml'); + + foreach ($configs as $key => $config) { + if (isset($config['globals'])) { + foreach ($config['globals'] as $name => $value) { + if (is_array($value) && isset($value['key'])) { + $configs[$key]['globals'][$name] = array( + 'key' => $name, + 'value' => $value, + ); + } + } + } + } + + $configuration = $this->getConfiguration($configs, $container); + + $config = $this->processConfiguration($configuration, $configs); + + $container->setParameter('twig.exception_listener.controller', $config['exception_controller']); + + $container->setParameter('twig.form.resources', $config['form_themes']); + + $envConfiguratorDefinition = $container->getDefinition('twig.configurator.environment'); + $envConfiguratorDefinition->replaceArgument(0, $config['date']['format']); + $envConfiguratorDefinition->replaceArgument(1, $config['date']['interval_format']); + $envConfiguratorDefinition->replaceArgument(2, $config['date']['timezone']); + $envConfiguratorDefinition->replaceArgument(3, $config['number_format']['decimals']); + $envConfiguratorDefinition->replaceArgument(4, $config['number_format']['decimal_point']); + $envConfiguratorDefinition->replaceArgument(5, $config['number_format']['thousands_separator']); + + $twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.filesystem'); + + // register user-configured paths + foreach ($config['paths'] as $path => $namespace) { + if (!$namespace) { + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path)); + } else { + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace)); + } + } + + $container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']); + $container->getDefinition('twig.template_iterator')->replaceArgument(2, $config['paths']); + + // register bundles as Twig namespaces + foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { + $dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/views'; + if (is_dir($dir)) { + $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); + } + $container->addResource(new FileExistenceResource($dir)); + + $reflection = new \ReflectionClass($class); + $dir = dirname($reflection->getFileName()).'/Resources/views'; + if (is_dir($dir)) { + $this->addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle); + } + $container->addResource(new FileExistenceResource($dir)); + } + + $dir = $container->getParameter('kernel.root_dir').'/Resources/views'; + if (is_dir($dir)) { + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir)); + } + $container->addResource(new FileExistenceResource($dir)); + + if (!empty($config['globals'])) { + $def = $container->getDefinition('twig'); + foreach ($config['globals'] as $key => $global) { + if (isset($global['type']) && 'service' === $global['type']) { + $def->addMethodCall('addGlobal', array($key, new Reference($global['id']))); + } else { + $def->addMethodCall('addGlobal', array($key, $global['value'])); + } + } + } + + unset( + $config['form'], + $config['globals'], + $config['extensions'] + ); + + if (isset($config['autoescape_service']) && isset($config['autoescape_service_method'])) { + $config['autoescape'] = array(new Reference($config['autoescape_service']), $config['autoescape_service_method']); + } + unset($config['autoescape_service'], $config['autoescape_service_method']); + + $container->getDefinition('twig')->replaceArgument(1, $config); + + $this->addClassesToCompile(array( + 'Twig_Environment', + 'Twig_Extension', + 'Twig_Extension_Core', + 'Twig_Extension_Escaper', + 'Twig_Extension_Optimizer', + 'Twig_LoaderInterface', + 'Twig_Markup', + 'Twig_Template', + )); + } + + private function addTwigPath($twigFilesystemLoaderDefinition, $dir, $bundle) + { + $name = $bundle; + if ('Bundle' === substr($name, -6)) { + $name = substr($name, 0, -6); + } + $twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir, $name)); + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/twig'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php new file mode 100644 index 0000000..769976b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * FilesystemLoader extends the default Twig filesystem loader + * to work with the Symfony paths and template references. + * + * @author Fabien Potencier + */ +class FilesystemLoader extends \Twig_Loader_Filesystem +{ + protected $locator; + protected $parser; + + /** + * Constructor. + * + * @param FileLocatorInterface $locator A FileLocatorInterface instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + */ + public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser) + { + parent::__construct(array()); + + $this->locator = $locator; + $this->parser = $parser; + } + + /** + * {@inheritdoc} + * + * The name parameter might also be a TemplateReferenceInterface. + */ + public function exists($name) + { + return parent::exists((string) $name); + } + + /** + * Returns the path to the template file. + * + * The file locator is used to locate the template when the naming convention + * is the symfony one (i.e. the name can be parsed). + * Otherwise the template is located using the locator from the twig library. + * + * @param string|TemplateReferenceInterface $template The template + * + * @return string The path to the template file + * + * @throws \Twig_Error_Loader if the template could not be found + */ + protected function findTemplate($template, $throw = true) + { + $logicalName = (string) $template; + + if (isset($this->cache[$logicalName])) { + return $this->cache[$logicalName]; + } + + $file = null; + $previous = null; + try { + $file = parent::findTemplate($logicalName); + } catch (\Twig_Error_Loader $e) { + $previous = $e; + + // for BC + try { + $template = $this->parser->parse($template); + $file = $this->locator->locate($template); + } catch (\Exception $e) { + $previous = $e; + } + } + + if (false === $file || null === $file) { + throw new \Twig_Error_Loader(sprintf('Unable to find template "%s".', $logicalName), -1, null, $previous); + } + + return $this->cache[$logicalName] = $file; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml new file mode 100644 index 0000000..bf87f8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/routing/errors.xml @@ -0,0 +1,12 @@ + + + + + + twig.controller.preview_error:previewErrorPageAction + html + \d+ + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd new file mode 100644 index 0000000..3314091 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml new file mode 100644 index 0000000..bd1635e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -0,0 +1,163 @@ + + + + + + + + + + app + + + + + + + %kernel.environment% + %kernel.debug% + + + + + + + + + + + + + + %kernel.root_dir% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.root_dir% + %kernel.charset% + + + + + + + + + + + + + + %kernel.debug% + + + + + + + + + + + + + + + + + + + + + + %twig.form.resources% + + + + + + + + + + + + + + + + %twig.exception_listener.controller% + + + + + + %kernel.debug% + + + + + %twig.exception_listener.controller% + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/meta/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig new file mode 100644 index 0000000..c27cc56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.atom.twig @@ -0,0 +1 @@ +{% include '@Twig/Exception/error.xml.twig' %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.css.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.css.twig new file mode 100644 index 0000000..d8a9369 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.css.twig @@ -0,0 +1,4 @@ +/* +{{ status_code }} {{ status_text }} + +*/ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.html.twig new file mode 100644 index 0000000..138a60a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.html.twig @@ -0,0 +1,16 @@ + + + + + An Error Occurred: {{ status_text }} + + +

    Oops! An Error Occurred

    +

    The server returned a "{{ status_code }} {{ status_text }}".

    + +
    + Something is broken. Please let us know what you were doing when this error occurred. + We will fix it as soon as possible. Sorry for any inconvenience caused. +
    + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.js.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.js.twig new file mode 100644 index 0000000..d8a9369 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.js.twig @@ -0,0 +1,4 @@ +/* +{{ status_code }} {{ status_text }} + +*/ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.json.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.json.twig new file mode 100644 index 0000000..fc19fd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.json.twig @@ -0,0 +1 @@ +{{ { 'error': { 'code': status_code, 'message': status_text } }|json_encode|raw }} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig new file mode 100644 index 0000000..c27cc56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.rdf.twig @@ -0,0 +1 @@ +{% include '@Twig/Exception/error.xml.twig' %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.txt.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.txt.twig new file mode 100644 index 0000000..bec5b1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.txt.twig @@ -0,0 +1,7 @@ +Oops! An Error Occurred +======================= + +The server returned a "{{ status_code }} {{ status_text }}". + +Something is broken. Please let us know what you were doing when this error occurred. +We will fix it as soon as possible. Sorry for any inconvenience caused. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.xml.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.xml.twig new file mode 100644 index 0000000..5ea8f56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/error.xml.twig @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig new file mode 100644 index 0000000..d507ce4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.atom.twig @@ -0,0 +1 @@ +{% include '@Twig/Exception/exception.xml.twig' with { 'exception': exception } %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig new file mode 100644 index 0000000..bdf242b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.css.twig @@ -0,0 +1,3 @@ +/* +{% include '@Twig/Exception/exception.txt.twig' with { 'exception': exception } %} +*/ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig new file mode 100644 index 0000000..947df65 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.html.twig @@ -0,0 +1,123 @@ +
    +
    +
    + Exception detected! +
    +
    +
    + +
    + +

    + {{ exception.message|nl2br|format_file_from_text }} +

    + +
    + {{ status_code }} {{ status_text }} - {{ exception.class|abbr_class }} +
    + + {% set previous_count = exception.allPrevious|length %} + {% if previous_count %} +
    {{ previous_count }} linked Exception{{ previous_count > 1 ? 's' : '' }}: +
      + {% for i, previous in exception.allPrevious %} +
    • + {{ previous.class|abbr_class }} » +
    • + {% endfor %} +
    +
    + {% endif %} + +
    + +
    +
    +
    +
    + +{% for position, e in exception.toarray %} + {% include '@Twig/Exception/traces.html.twig' with { 'exception': e, 'position': position, 'count': previous_count } only %} +{% endfor %} + +{% if logger %} +
    +
    + {% spaceless %} +

    + Logs  + + + - + +

    + {% endspaceless %} + + {% if logger.counterrors %} +
    + + {{ logger.counterrors }} error{{ logger.counterrors > 1 ? 's' : ''}} + +
    + {% endif %} +
    + +
    + {% include '@Twig/Exception/logs.html.twig' with { 'logs': logger.logs } only %} +
    +
    +{% endif %} + +{% if currentContent %} +
    + {% spaceless %} +

    + Content of the Output  + + + + + +

    + {% endspaceless %} + + + +
    +
    +{% endif %} + +{% include '@Twig/Exception/traces_text.html.twig' with { 'exception': exception } only %} + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.js.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.js.twig new file mode 100644 index 0000000..bdf242b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.js.twig @@ -0,0 +1,3 @@ +/* +{% include '@Twig/Exception/exception.txt.twig' with { 'exception': exception } %} +*/ diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.json.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.json.twig new file mode 100644 index 0000000..13a4147 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.json.twig @@ -0,0 +1 @@ +{{ { 'error': { 'code': status_code, 'message': status_text, 'exception': exception.toarray } }|json_encode|raw }} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.rdf.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.rdf.twig new file mode 100644 index 0000000..d507ce4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.rdf.twig @@ -0,0 +1 @@ +{% include '@Twig/Exception/exception.xml.twig' with { 'exception': exception } %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.txt.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.txt.twig new file mode 100644 index 0000000..ae297c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.txt.twig @@ -0,0 +1,7 @@ +[exception] {{ status_code ~ ' | ' ~ status_text ~ ' | ' ~ exception.class }} +[message] {{ exception.message }} +{% for i, e in exception.toarray %} +[{{ i + 1 }}] {{ e.class }}: {{ e.message }} +{% include '@Twig/Exception/traces.txt.twig' with { 'exception': e } only %} + +{% endfor %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.xml.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.xml.twig new file mode 100644 index 0000000..2e3f30a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception.xml.twig @@ -0,0 +1,9 @@ + + + +{% for e in exception.toarray %} + +{% include '@Twig/Exception/traces.xml.twig' with { 'exception': e } only %} + +{% endfor %} + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig new file mode 100644 index 0000000..fa6f321 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/exception_full.html.twig @@ -0,0 +1,13 @@ +{% extends '@Twig/layout.html.twig' %} + +{% block head %} + +{% endblock %} + +{% block title %} + {{ exception.message }} ({{ status_code }} {{ status_text }}) +{% endblock %} + +{% block body %} + {% include '@Twig/Exception/exception.html.twig' %} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/logs.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/logs.html.twig new file mode 100644 index 0000000..f75ec58 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/logs.html.twig @@ -0,0 +1,7 @@ +
      + {% for log in logs %} + = 400 %} class="error"{% elseif log.priority >= 300 %} class="warning"{% endif %}> + {{ log.priorityName }} - {{ log.message }} + + {% endfor %} +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig new file mode 100644 index 0000000..d00a376 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.html.twig @@ -0,0 +1,22 @@ +{% if trace.function %} + at + + {{ trace.short_class }} + {{ trace.type ~ trace.function }} + + ({{ trace.args|format_args }}) +{% endif %} + +{% if trace.file is defined and trace.file and trace.line is defined and trace.line %} + {{ trace.function ? '
    ' : '' }} + in {{ trace.file|format_file(trace.line) }}  + {% spaceless %} + + - + + + + {% endspaceless %} +
    + {{ trace.file|file_excerpt(trace.line) }} +
    +{% endif %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.txt.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.txt.twig new file mode 100644 index 0000000..ff20469 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/trace.txt.twig @@ -0,0 +1,8 @@ +{% if trace.function %} + at {{ trace.class ~ trace.type ~ trace.function }}({{ trace.args|format_args_as_text }}) +{% else %} + at n/a +{% endif %} +{% if trace.file is defined and trace.line is defined %} + in {{ trace.file }} line {{ trace.line }} +{% endif %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.html.twig new file mode 100644 index 0000000..e846f7a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.html.twig @@ -0,0 +1,25 @@ +
    + {% if count > 0 %} +

    + [{{ count - position + 1 }}/{{ count + 1 }}] + {{ exception.class|abbr_class }}: {{ exception.message|nl2br|format_file_from_text }}  + {% spaceless %} + + - + + + + {% endspaceless %} +

    + {% else %} +

    Stack Trace

    + {% endif %} + + +
      + {% for i, trace in exception.trace %} +
    1. + {% include '@Twig/Exception/trace.html.twig' with { 'prefix': position, 'i': i, 'trace': trace } only %} +
    2. + {% endfor %} +
    +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.txt.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.txt.twig new file mode 100644 index 0000000..906be78 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.txt.twig @@ -0,0 +1,6 @@ +{% if exception.trace|length %} +{% for trace in exception.trace %} +{% include '@Twig/Exception/trace.txt.twig' with { 'trace': trace } only %} + +{% endfor %} +{% endif %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.xml.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.xml.twig new file mode 100644 index 0000000..d04af93 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces.xml.twig @@ -0,0 +1,8 @@ + +{% for trace in exception.trace %} + +{% include '@Twig/Exception/trace.txt.twig' with { 'trace': trace } only %} + + +{% endfor %} + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig new file mode 100644 index 0000000..16b3579 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/Exception/traces_text.html.twig @@ -0,0 +1,18 @@ +
    +

    + Stack Trace (Plain Text)  + {% spaceless %} + + + + + + {% endspaceless %} +

    + + +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig new file mode 100644 index 0000000..5672135 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Resources/views/layout.html.twig @@ -0,0 +1,44 @@ + + + + + + {% block title %}{% endblock %} + + + {% block head %}{% endblock %} + + +
    +
    + + + +
    + +
    + {% block body %}{% endblock %} +
    +
    + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TemplateIterator.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TemplateIterator.php new file mode 100644 index 0000000..63f8da5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TemplateIterator.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Finder\Finder; + +/** + * Iterator for all templates in bundles and in the application Resources directory. + * + * @author Fabien Potencier + */ +class TemplateIterator implements \IteratorAggregate +{ + private $kernel; + private $rootDir; + private $templates; + private $paths; + + /** + * @param KernelInterface $kernel A KernelInterface instance + * @param string $rootDir The directory where global templates can be stored + * @param array $paths Additional Twig paths to warm + */ + public function __construct(KernelInterface $kernel, $rootDir, array $paths = array()) + { + $this->kernel = $kernel; + $this->rootDir = $rootDir; + $this->paths = $paths; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + if (null !== $this->templates) { + return $this->templates; + } + + $this->templates = $this->findTemplatesInDirectory($this->rootDir.'/Resources/views'); + foreach ($this->kernel->getBundles() as $bundle) { + $name = $bundle->getName(); + if ('Bundle' === substr($name, -6)) { + $name = substr($name, 0, -6); + } + + $this->templates = array_merge( + $this->templates, + $this->findTemplatesInDirectory($bundle->getPath().'/Resources/views', $name), + $this->findTemplatesInDirectory($this->rootDir.'/'.$bundle->getName().'/views', $name) + ); + } + + foreach ($this->paths as $dir => $namespace) { + $this->templates = array_merge($this->templates, $this->findTemplatesInDirectory($dir, $namespace)); + } + + return $this->templates = new \ArrayIterator(array_unique($this->templates)); + } + + /** + * Find templates in the given directory. + * + * @param string $dir The directory where to look for templates + * @param string|null $namespace The template namespace + * + * @return array + */ + private function findTemplatesInDirectory($dir, $namespace = null) + { + if (!is_dir($dir)) { + return array(); + } + + $templates = array(); + foreach (Finder::create()->files()->followLinks()->in($dir) as $file) { + $templates[] = (null !== $namespace ? '@'.$namespace.'/' : '').str_replace('\\', '/', $file->getRelativePathname()); + } + + return $templates; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php new file mode 100644 index 0000000..920a833 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/ExceptionControllerTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\Controller; + +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Bundle\TwigBundle\Controller\ExceptionController; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Request; + +class ExceptionControllerTest extends TestCase +{ + public function testShowActionCanBeForcedToShowErrorPage() + { + $twig = new \Twig_Environment( + new \Twig_Loader_Array(array( + '@Twig/Exception/error404.html.twig' => 'ok', + )) + ); + + $request = Request::create('whatever', 'GET'); + $request->headers->set('X-Php-Ob-Level', 1); + $request->attributes->set('showException', false); + $exception = FlattenException::create(new \Exception(), 404); + $controller = new ExceptionController($twig, /* "showException" defaults to --> */ true); + + $response = $controller->showAction($request, $exception, null); + + $this->assertEquals(200, $response->getStatusCode()); // successful request + $this->assertEquals('ok', $response->getContent()); // content of the error404.html template + } + + public function testFallbackToHtmlIfNoTemplateForRequestedFormat() + { + $twig = new \Twig_Environment( + new \Twig_Loader_Array(array( + '@Twig/Exception/error.html.twig' => 'html', + )) + ); + + $request = Request::create('whatever'); + $request->headers->set('X-Php-Ob-Level', 1); + $request->setRequestFormat('txt'); + $exception = FlattenException::create(new \Exception()); + $controller = new ExceptionController($twig, false); + + $response = $controller->showAction($request, $exception); + + $this->assertEquals('html', $request->getRequestFormat()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php new file mode 100644 index 0000000..d64fc4a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Controller/PreviewErrorControllerTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\Controller; + +use Symfony\Bundle\TwigBundle\Controller\PreviewErrorController; +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class PreviewErrorControllerTest extends TestCase +{ + public function testForwardRequestToConfiguredController() + { + $request = Request::create('whatever'); + $response = new Response(''); + $code = 123; + $logicalControllerName = 'foo:bar:baz'; + + $kernel = $this->getMock('\Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel + ->expects($this->once()) + ->method('handle') + ->with( + $this->callback(function (Request $request) use ($logicalControllerName, $code) { + + $this->assertEquals($logicalControllerName, $request->attributes->get('_controller')); + + $exception = $request->attributes->get('exception'); + $this->assertInstanceOf(FlattenException::class, $exception); + $this->assertEquals($code, $exception->getStatusCode()); + $this->assertFalse($request->attributes->get('showException')); + + return true; + }), + $this->equalTo(HttpKernelInterface::SUB_REQUEST) + ) + ->will($this->returnValue($response)); + + $controller = new PreviewErrorController($kernel, $logicalControllerName); + + $this->assertSame($response, $controller->previewErrorPageAction($request, $code)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigLoaderPassTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigLoaderPassTest.php new file mode 100644 index 0000000..08e9d46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Compiler/TwigLoaderPassTest.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass; + +class TwigLoaderPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $builder; + /** + * @var Definition + */ + private $chainLoader; + /** + * @var TwigLoaderPass + */ + private $pass; + + protected function setUp() + { + $this->builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'setAlias', 'getDefinition') + ); + $this->chainLoader = new Definition('loader'); + $this->pass = new TwigLoaderPass(); + } + + public function testMapperPassWithOneTaggedLoaders() + { + $serviceIds = array( + 'test_loader_1' => array( + array(), + ), + ); + + $this->builder->expects($this->once()) + ->method('hasDefinition') + ->with('twig') + ->will($this->returnValue(true)); + $this->builder->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('twig.loader') + ->will($this->returnValue($serviceIds)); + $this->builder->expects($this->once()) + ->method('setAlias') + ->with('twig.loader', 'test_loader_1'); + + $this->pass->process($this->builder); + } + + public function testMapperPassWithTwoTaggedLoaders() + { + $serviceIds = array( + 'test_loader_1' => array( + array(), + ), + 'test_loader_2' => array( + array(), + ), + ); + + $this->builder->expects($this->once()) + ->method('hasDefinition') + ->with('twig') + ->will($this->returnValue(true)); + $this->builder->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('twig.loader') + ->will($this->returnValue($serviceIds)); + $this->builder->expects($this->once()) + ->method('getDefinition') + ->with('twig.loader.chain') + ->will($this->returnValue($this->chainLoader)); + $this->builder->expects($this->once()) + ->method('setAlias') + ->with('twig.loader', 'twig.loader.chain'); + + $this->pass->process($this->builder); + $calls = $this->chainLoader->getMethodCalls(); + $this->assertCount(2, $calls); + $this->assertEquals('addLoader', $calls[0][0]); + $this->assertEquals('addLoader', $calls[1][0]); + $this->assertEquals('test_loader_1', (string) $calls[0][1][0]); + $this->assertEquals('test_loader_2', (string) $calls[1][1][0]); + } + + public function testMapperPassWithTwoTaggedLoadersWithPriority() + { + $serviceIds = array( + 'test_loader_1' => array( + array('priority' => 100), + ), + 'test_loader_2' => array( + array('priority' => 200), + ), + ); + + $this->builder->expects($this->once()) + ->method('hasDefinition') + ->with('twig') + ->will($this->returnValue(true)); + $this->builder->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('twig.loader') + ->will($this->returnValue($serviceIds)); + $this->builder->expects($this->once()) + ->method('getDefinition') + ->with('twig.loader.chain') + ->will($this->returnValue($this->chainLoader)); + $this->builder->expects($this->once()) + ->method('setAlias') + ->with('twig.loader', 'twig.loader.chain'); + + $this->pass->process($this->builder); + $calls = $this->chainLoader->getMethodCalls(); + $this->assertCount(2, $calls); + $this->assertEquals('addLoader', $calls[0][0]); + $this->assertEquals('addLoader', $calls[1][0]); + $this->assertEquals('test_loader_2', (string) $calls[0][1][0]); + $this->assertEquals('test_loader_1', (string) $calls[1][1][0]); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + */ + public function testMapperPassWithZeroTaggedLoaders() + { + $this->builder->expects($this->once()) + ->method('hasDefinition') + ->with('twig') + ->will($this->returnValue(true)); + $this->builder->expects($this->once()) + ->method('findTaggedServiceIds') + ->with('twig.loader') + ->will($this->returnValue(array())); + + $this->pass->process($this->builder); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..4dfb54e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection; + +use Symfony\Bundle\TwigBundle\DependencyInjection\Configuration; +use Symfony\Component\Config\Definition\Processor; + +class ConfigurationTest extends \PHPUnit_Framework_TestCase +{ + public function testDoNoDuplicateDefaultFormResources() + { + $input = array( + 'form_themes' => array('form_div_layout.html.twig'), + ); + + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(), array($input)); + + $this->assertEquals(array('form_div_layout.html.twig'), $config['form_themes']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Resources/TwigBundle/views/layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Resources/TwigBundle/views/layout.html.twig new file mode 100644 index 0000000..bb07ecf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Resources/TwigBundle/views/layout.html.twig @@ -0,0 +1 @@ +This is a layout diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Resources/views/layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Resources/views/layout.html.twig new file mode 100644 index 0000000..bb07ecf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/Resources/views/layout.html.twig @@ -0,0 +1 @@ +This is a layout diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php new file mode 100644 index 0000000..ab42923 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/customTemplateEscapingGuesser.php @@ -0,0 +1,6 @@ +loadFromExtension('twig', array( + 'autoescape_service' => 'my_project.some_bundle.template_escaping_guesser', + 'autoescape_service_method' => 'guess', +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php new file mode 100644 index 0000000..efd2df5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/empty.php @@ -0,0 +1,3 @@ +loadFromExtension('twig', array()); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php new file mode 100644 index 0000000..28b8281 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/extra.php @@ -0,0 +1,7 @@ +loadFromExtension('twig', array( + 'paths' => array( + 'namespaced_path3' => 'namespace3', + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php new file mode 100644 index 0000000..9385131 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -0,0 +1,26 @@ +loadFromExtension('twig', array( + 'form_themes' => array( + 'MyBundle::form.html.twig', + ), + 'globals' => array( + 'foo' => '@bar', + 'baz' => '@@qux', + 'pi' => 3.14, + 'bad' => array('key' => 'foo'), + ), + 'auto_reload' => true, + 'autoescape' => true, + 'base_template_class' => 'stdClass', + 'cache' => '/tmp', + 'charset' => 'ISO-8859-1', + 'debug' => true, + 'strict_variables' => true, + 'paths' => array( + 'path1', + 'path2', + 'namespaced_path1' => 'namespace1', + 'namespaced_path2' => 'namespace2', + ), +)); diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml new file mode 100644 index 0000000..fa28361 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/customTemplateEscapingGuesser.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml new file mode 100644 index 0000000..771e382 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/empty.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/extra.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/extra.xml new file mode 100644 index 0000000..174b3e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/extra.xml @@ -0,0 +1,12 @@ + + + + + + namespaced_path3 + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml new file mode 100644 index 0000000..14c95af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -0,0 +1,19 @@ + + + + + + MyBundle::form.html.twig + + @@qux + 3.14 + path1 + path2 + namespaced_path1 + namespaced_path2 + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml new file mode 100644 index 0000000..eb26e71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/customTemplateEscapingGuesser.yml @@ -0,0 +1,3 @@ +twig: + autoescape_service: my_project.some_bundle.template_escaping_guesser + autoescape_service_method: guess diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml new file mode 100644 index 0000000..a472b26 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/empty.yml @@ -0,0 +1 @@ +twig: diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml new file mode 100644 index 0000000..3c5e6a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/extra.yml @@ -0,0 +1,3 @@ +twig: + paths: + namespaced_path3: namespace3 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml new file mode 100644 index 0000000..68ecf46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -0,0 +1,20 @@ +twig: + form_themes: + - MyBundle::form.html.twig + globals: + foo: "@bar" + baz: "@@qux" + pi: 3.14 + bad: {key: foo} + auto_reload: true + autoescape: true + base_template_class: stdClass + cache: /tmp + charset: ISO-8859-1 + debug: true + strict_variables: true + paths: + path1: '' + path2: '' + namespaced_path1: namespace1 + namespaced_path2: namespace2 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php new file mode 100644 index 0000000..a99125a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -0,0 +1,254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection; + +use Symfony\Bundle\TwigBundle\DependencyInjection\TwigExtension; +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class TwigExtensionTest extends TestCase +{ + public function testLoadEmptyConfiguration() + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $container->loadFromExtension('twig', array()); + $this->compileContainer($container); + + $this->assertEquals('Twig_Environment', $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file'); + + $this->assertContains('form_div_layout.html.twig', $container->getParameter('twig.form.resources'), '->load() includes default template for form resources'); + + // Twig options + $options = $container->getDefinition('twig')->getArgument(1); + $this->assertEquals('%kernel.cache_dir%/twig', $options['cache'], '->load() sets default value for cache option'); + $this->assertEquals('%kernel.charset%', $options['charset'], '->load() sets default value for charset option'); + $this->assertEquals('%kernel.debug%', $options['debug'], '->load() sets default value for debug option'); + } + + /** + * @dataProvider getFormats + */ + public function testLoadFullConfiguration($format) + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'full', $format); + $this->compileContainer($container); + + $this->assertEquals('Twig_Environment', $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file'); + + // Form resources + $resources = $container->getParameter('twig.form.resources'); + $this->assertContains('form_div_layout.html.twig', $resources, '->load() includes default template for form resources'); + $this->assertContains('MyBundle::form.html.twig', $resources, '->load() merges new templates into form resources'); + + // Globals + $calls = $container->getDefinition('twig')->getMethodCalls(); + $this->assertEquals('app', $calls[0][1][0], '->load() registers services as Twig globals'); + $this->assertEquals(new Reference('twig.app_variable'), $calls[0][1][1]); + $this->assertEquals('foo', $calls[1][1][0], '->load() registers services as Twig globals'); + $this->assertEquals(new Reference('bar'), $calls[1][1][1], '->load() registers services as Twig globals'); + $this->assertEquals('baz', $calls[2][1][0], '->load() registers variables as Twig globals'); + $this->assertEquals('@qux', $calls[2][1][1], '->load() allows escaping of service identifiers'); + $this->assertEquals('pi', $calls[3][1][0], '->load() registers variables as Twig globals'); + $this->assertEquals(3.14, $calls[3][1][1], '->load() registers variables as Twig globals'); + + // Yaml and Php specific configs + if (in_array($format, array('yml', 'php'))) { + $this->assertEquals('bad', $calls[4][1][0], '->load() registers variables as Twig globals'); + $this->assertEquals(array('key' => 'foo'), $calls[4][1][1], '->load() registers variables as Twig globals'); + } + + // Twig options + $options = $container->getDefinition('twig')->getArgument(1); + $this->assertTrue($options['auto_reload'], '->load() sets the auto_reload option'); + $this->assertTrue($options['autoescape'], '->load() sets the autoescape option'); + $this->assertEquals('stdClass', $options['base_template_class'], '->load() sets the base_template_class option'); + $this->assertEquals('/tmp', $options['cache'], '->load() sets the cache option'); + $this->assertEquals('ISO-8859-1', $options['charset'], '->load() sets the charset option'); + $this->assertTrue($options['debug'], '->load() sets the debug option'); + $this->assertTrue($options['strict_variables'], '->load() sets the strict_variables option'); + } + + /** + * @dataProvider getFormats + */ + public function testLoadCustomTemplateEscapingGuesserConfiguration($format) + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'customTemplateEscapingGuesser', $format); + $this->compileContainer($container); + + $options = $container->getDefinition('twig')->getArgument(1); + $this->assertEquals(array(new Reference('my_project.some_bundle.template_escaping_guesser'), 'guess'), $options['autoescape']); + } + + /** + * @dataProvider getFormats + */ + public function testLoadDefaultTemplateEscapingGuesserConfiguration($format) + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'empty', $format); + $this->compileContainer($container); + + $options = $container->getDefinition('twig')->getArgument(1); + $this->assertEquals('filename', $options['autoescape']); + } + + public function testGlobalsWithDifferentTypesAndValues() + { + $globals = array( + 'array' => array(), + 'false' => false, + 'float' => 2.0, + 'integer' => 3, + 'null' => null, + 'object' => new \stdClass(), + 'string' => 'foo', + 'true' => true, + ); + + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $container->loadFromExtension('twig', array('globals' => $globals)); + $this->compileContainer($container); + + $calls = $container->getDefinition('twig')->getMethodCalls(); + foreach (array_slice($calls, 1) as $call) { + list($name, $value) = each($globals); + $this->assertEquals($name, $call[1][0]); + $this->assertSame($value, $call[1][1]); + } + } + + /** + * @dataProvider getFormats + */ + public function testTwigLoaderPaths($format) + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'full', $format); + $this->loadFromFile($container, 'extra', $format); + $this->compileContainer($container); + + $def = $container->getDefinition('twig.loader.filesystem'); + $paths = array(); + foreach ($def->getMethodCalls() as $call) { + if ('addPath' === $call[0] && false === strpos($call[1][0], 'Form')) { + $paths[] = $call[1]; + } + } + + $this->assertEquals(array( + array('path1'), + array('path2'), + array('namespaced_path1', 'namespace1'), + array('namespaced_path2', 'namespace2'), + array('namespaced_path3', 'namespace3'), + array(__DIR__.'/Fixtures/Resources/TwigBundle/views', 'Twig'), + array(realpath(__DIR__.'/../..').'/Resources/views', 'Twig'), + array(__DIR__.'/Fixtures/Resources/views'), + ), $paths); + } + + public function getFormats() + { + return array( + array('php'), + array('yml'), + array('xml'), + ); + } + + /** + * @dataProvider stopwatchExtensionAvailabilityProvider + */ + public function testStopwatchExtensionAvailability($debug, $stopwatchEnabled, $expected) + { + $container = $this->createContainer(); + $container->setParameter('kernel.debug', $debug); + if ($stopwatchEnabled) { + $container->register('debug.stopwatch', 'Symfony\Component\Stopwatch\Stopwatch'); + } + $container->registerExtension(new TwigExtension()); + $container->loadFromExtension('twig', array()); + $this->compileContainer($container); + + $tokenParsers = $container->get('twig.extension.debug.stopwatch')->getTokenParsers(); + $stopwatchIsAvailable = new \ReflectionProperty($tokenParsers[0], 'stopwatchIsAvailable'); + $stopwatchIsAvailable->setAccessible(true); + + $this->assertSame($expected, $stopwatchIsAvailable->getValue($tokenParsers[0])); + } + + public function stopwatchExtensionAvailabilityProvider() + { + return array( + 'debug-and-stopwatch-enabled' => array(true, true, true), + 'only-stopwatch-enabled' => array(false, true, false), + 'only-debug-enabled' => array(true, false, false), + 'debug-and-stopwatch-disabled' => array(false, false, false), + ); + } + + private function createContainer() + { + $container = new ContainerBuilder(new ParameterBag(array( + 'kernel.cache_dir' => __DIR__, + 'kernel.root_dir' => __DIR__.'/Fixtures', + 'kernel.charset' => 'UTF-8', + 'kernel.debug' => false, + 'kernel.bundles' => array('TwigBundle' => 'Symfony\\Bundle\\TwigBundle\\TwigBundle'), + ))); + + return $container; + } + + private function compileContainer(ContainerBuilder $container) + { + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + } + + private function loadFromFile(ContainerBuilder $container, $file, $format) + { + $locator = new FileLocator(__DIR__.'/Fixtures/'.$format); + + switch ($format) { + case 'php': + $loader = new PhpFileLoader($container, $locator); + break; + case 'xml': + $loader = new XmlFileLoader($container, $locator); + break; + case 'yml': + $loader = new YamlFileLoader($container, $locator); + break; + default: + throw new \InvalidArgumentException(sprintf('Unsupported format: %s', $format)); + } + + $loader->load($file.'.'.$format); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/BarBundle/Resources/views/index.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/BarBundle/Resources/views/index.html.twig new file mode 100644 index 0000000..1975b0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/BarBundle/Resources/views/index.html.twig @@ -0,0 +1 @@ +{# Twig template #} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Foo/index.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Foo/index.html.twig new file mode 100644 index 0000000..1975b0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Foo/index.html.twig @@ -0,0 +1 @@ +{# Twig template #} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Resources/views/layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Resources/views/layout.html.twig new file mode 100644 index 0000000..1975b0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Resources/views/layout.html.twig @@ -0,0 +1 @@ +{# Twig template #} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Resources/views/sub/sub.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Resources/views/sub/sub.html.twig new file mode 100644 index 0000000..1975b0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Fixtures/templates/Resources/views/sub/sub.html.twig @@ -0,0 +1 @@ +{# Twig template #} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php new file mode 100644 index 0000000..610bfa3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/CacheWarmingTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; + +class NewCacheWamingTest extends \PHPUnit_Framework_TestCase +{ + public function testCacheIsProperlyWarmedWhenTemplatingIsAvailable() + { + $kernel = new CacheWarmingKernel(true); + $kernel->boot(); + + $warmer = $kernel->getContainer()->get('cache_warmer'); + $warmer->enableOptionalWarmers(); + $warmer->warmUp($kernel->getCacheDir()); + + $this->assertTrue(file_exists($kernel->getCacheDir().'/twig')); + } + + public function testCacheIsNotWarmedWhenTemplatingIsDisabled() + { + $kernel = new CacheWarmingKernel(false); + $kernel->boot(); + + $warmer = $kernel->getContainer()->get('cache_warmer'); + $warmer->enableOptionalWarmers(); + $warmer->warmUp($kernel->getCacheDir()); + + $this->assertFalse(file_exists($kernel->getCacheDir().'/twig')); + } + + protected function setUp() + { + $this->deleteTempDir(); + } + + protected function tearDown() + { + $this->deleteTempDir(); + } + + private function deleteTempDir() + { + if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/CacheWarmingKernel')) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } +} + +class CacheWarmingKernel extends Kernel +{ + private $withTemplating; + + public function __construct($withTemplating) + { + $this->withTemplating = $withTemplating; + + parent::__construct('dev', true); + } + + public function getName() + { + return 'CacheWarming'; + } + + public function registerBundles() + { + return array(new FrameworkBundle(), new TwigBundle()); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(function ($container) { + $container->loadFromExtension('framework', array( + 'secret' => '$ecret', + )); + }); + + if ($this->withTemplating) { + $loader->load(function ($container) { + $container->loadFromExtension('framework', array( + 'secret' => '$ecret', + 'templating' => array('engines' => array('twig')), + 'router' => array('resource' => '%kernel.root_dir%/Resources/config/empty_routing.yml'), + )); + }); + } + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/CacheWarmingKernel/cache'; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/CacheWarmingKernel/logs'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php new file mode 100644 index 0000000..068a105 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/NoTemplatingEntryTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; + +class NoTemplatingEntryTest extends \PHPUnit_Framework_TestCase +{ + public function test() + { + $kernel = new NoTemplatingEntryKernel('dev', true); + $kernel->boot(); + + $container = $kernel->getContainer(); + $content = $container->get('twig')->render('index.html.twig'); + $this->assertContains('{ a: b }', $content); + } + + protected function setUp() + { + $this->deleteTempDir(); + } + + protected function tearDown() + { + $this->deleteTempDir(); + } + + protected function deleteTempDir() + { + if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/NoTemplatingEntryKernel')) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } +} + +class NoTemplatingEntryKernel extends Kernel +{ + public function registerBundles() + { + return array(new FrameworkBundle(), new TwigBundle()); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(function ($container) { + $container->loadFromExtension('framework', array( + 'secret' => '$ecret', + )); + }); + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/NoTemplatingEntryKernel/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/NoTemplatingEntryKernel/logs'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/Resources/config/empty_routing.yml b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/Resources/config/empty_routing.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/Resources/views/index.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/Resources/views/index.html.twig new file mode 100644 index 0000000..ddc4eb4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Functional/Resources/views/index.html.twig @@ -0,0 +1 @@ +{{ {a: 'b'}|yaml_encode }} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php new file mode 100644 index 0000000..c6d5e27 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests\Loader; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\TwigBundle\Loader\FilesystemLoader; +use Symfony\Bundle\TwigBundle\Tests\TestCase; + +class FilesystemLoaderTest extends TestCase +{ + public function testGetSource() + { + $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + $locator + ->expects($this->once()) + ->method('locate') + ->will($this->returnValue(__DIR__.'/../DependencyInjection/Fixtures/Resources/views/layout.html.twig')) + ; + $loader = new FilesystemLoader($locator, $parser); + $loader->addPath(__DIR__.'/../DependencyInjection/Fixtures/Resources/views', 'namespace'); + + // Twig-style + $this->assertEquals("This is a layout\n", $loader->getSource('@namespace/layout.html.twig')); + + // Symfony-style + $this->assertEquals("This is a layout\n", $loader->getSource('TwigBundle::layout.html.twig')); + } + + public function testExists() + { + // should return true for templates that Twig does not find, but Symfony does + $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + $locator + ->expects($this->once()) + ->method('locate') + ->will($this->returnValue($template = __DIR__.'/../DependencyInjection/Fixtures/Resources/views/layout.html.twig')) + ; + $loader = new FilesystemLoader($locator, $parser); + + return $this->assertTrue($loader->exists($template)); + } + + /** + * @expectedException \Twig_Error_Loader + */ + public function testTwigErrorIfLocatorThrowsInvalid() + { + $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + $parser + ->expects($this->once()) + ->method('parse') + ->with('name.format.engine') + ->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine'))) + ; + + $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + $locator + ->expects($this->once()) + ->method('locate') + ->will($this->throwException(new \InvalidArgumentException('Unable to find template "NonExistent".'))) + ; + + $loader = new FilesystemLoader($locator, $parser); + $loader->getCacheKey('name.format.engine'); + } + + /** + * @expectedException \Twig_Error_Loader + */ + public function testTwigErrorIfLocatorReturnsFalse() + { + $parser = $this->getMock('Symfony\Component\Templating\TemplateNameParserInterface'); + $parser + ->expects($this->once()) + ->method('parse') + ->with('name.format.engine') + ->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine'))) + ; + + $locator = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + $locator + ->expects($this->once()) + ->method('locate') + ->will($this->returnValue(false)) + ; + + $loader = new FilesystemLoader($locator, $parser); + $loader->getCacheKey('name.format.engine'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TemplateIteratorTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TemplateIteratorTest.php new file mode 100644 index 0000000..13bb9ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TemplateIteratorTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests; + +use Symfony\Bundle\TwigBundle\TemplateIterator; + +class TemplateIteratorTest extends TestCase +{ + public function testGetIterator() + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\BundleInterface'); + $bundle->expects($this->any())->method('getName')->will($this->returnValue('BarBundle')); + $bundle->expects($this->any())->method('getPath')->will($this->returnValue(__DIR__.'/Fixtures/templates/BarBundle')); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Kernel')->disableOriginalConstructor()->getMock(); + $kernel->expects($this->any())->method('getBundles')->will($this->returnValue(array( + $bundle, + ))); + $iterator = new TemplateIterator($kernel, __DIR__.'/Fixtures/templates', array(__DIR__.'/Fixtures/templates/Foo' => 'Foo')); + + $sorted = iterator_to_array($iterator); + sort($sorted); + $this->assertEquals( + array( + '@Bar/index.html.twig', + '@Foo/index.html.twig', + 'layout.html.twig', + 'sub/sub.html.twig', + ), + $sorted + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TestCase.php new file mode 100644 index 0000000..0c7af8a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/Tests/TestCase.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle\Tests; + +class TestCase extends \PHPUnit_Framework_TestCase +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigBundle.php new file mode 100644 index 0000000..7b4a205 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigBundle.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExceptionListenerPass; +use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExtensionPass; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class TwigBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new ExtensionPass()); + $container->addCompilerPass(new TwigEnvironmentPass()); + $container->addCompilerPass(new TwigLoaderPass()); + $container->addCompilerPass(new ExceptionListenerPass()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigEngine.php b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigEngine.php new file mode 100644 index 0000000..1e0d613 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/TwigEngine.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\TwigBundle; + +use Symfony\Bridge\Twig\TwigEngine as BaseEngine; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Config\FileLocatorInterface; + +/** + * This engine renders Twig templates. + * + * @author Fabien Potencier + */ +class TwigEngine extends BaseEngine implements EngineInterface +{ + protected $locator; + + /** + * Constructor. + * + * @param \Twig_Environment $environment A \Twig_Environment instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param FileLocatorInterface $locator A FileLocatorInterface instance + */ + public function __construct(\Twig_Environment $environment, TemplateNameParserInterface $parser, FileLocatorInterface $locator) + { + parent::__construct($environment, $parser); + + $this->locator = $locator; + } + + /** + * {@inheritdoc} + */ + public function render($name, array $parameters = array()) + { + try { + return parent::render($name, $parameters); + } catch (\Twig_Error $e) { + if ($name instanceof TemplateReference) { + try { + // try to get the real file name of the template where the error occurred + $e->setTemplateFile(sprintf('%s', $this->locator->locate($this->parser->parse($e->getTemplateFile())))); + } catch (\Exception $e2) { + } + } + + throw $e; + } + } + + /** + * {@inheritdoc} + * + * @throws \Twig_Error if something went wrong like a thrown exception while rendering the template + */ + public function renderResponse($view, array $parameters = array(), Response $response = null) + { + if (null === $response) { + $response = new Response(); + } + + $response->setContent($this->render($view, $parameters)); + + return $response; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/composer.json b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/composer.json new file mode 100644 index 0000000..c99928a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/composer.json @@ -0,0 +1,48 @@ +{ + "name": "symfony/twig-bundle", + "type": "symfony-bundle", + "description": "Symfony TwigBundle", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/asset": "~2.8|~3.0", + "symfony/twig-bridge": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "require-dev": { + "symfony/stopwatch": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/routing": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0", + "symfony/framework-bundle": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\TwigBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist new file mode 100644 index 0000000..9a8c38f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md new file mode 100644 index 0000000..b0cf847 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG +========= + +3.0.0 +----- + + * removed profiler:import and profiler:export commands + +2.8.0 +----- + + * deprecated profiler:import and profiler:export commands + +2.7.0 +----- + + * [BC BREAK] if you are using a DB to store profiles, the table must be dropped + * added the HTTP status code to profiles + +2.3.0 +----- + + * draw retina canvas if devicePixelRatio is bigger than 1 + +2.1.0 +----- + + * deprecated the verbose setting (not relevant anymore) + * [BC BREAK] You must clear old profiles after upgrading to 2.1 (don't forget + to remove the table if you are using a DB) + * added support for the request method + * added a routing panel + * added a timeline panel + * The toolbar position can now be configured via the `position` option (can + be `top` or `bottom`) diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php new file mode 100644 index 0000000..fd0dbec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ExceptionController.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Controller; + +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpFoundation\Response; + +/** + * ExceptionController. + * + * @author Fabien Potencier + */ +class ExceptionController +{ + protected $twig; + protected $debug; + protected $profiler; + + public function __construct(Profiler $profiler = null, \Twig_Environment $twig, $debug) + { + $this->profiler = $profiler; + $this->twig = $twig; + $this->debug = $debug; + } + + /** + * Renders the exception panel for the given token. + * + * @param string $token The profiler token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function showAction($token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + $exception = $this->profiler->loadProfile($token)->getCollector('exception')->getException(); + $template = $this->getTemplate(); + + if (!$this->twig->getLoader()->exists($template)) { + $handler = new ExceptionHandler($this->debug, $this->twig->getCharset()); + + return new Response($handler->getContent($exception), 200, array('Content-Type' => 'text/html')); + } + + $code = $exception->getStatusCode(); + + return new Response($this->twig->render( + $template, + array( + 'status_code' => $code, + 'status_text' => Response::$statusTexts[$code], + 'exception' => $exception, + 'logger' => null, + 'currentContent' => '', + ) + ), 200, array('Content-Type' => 'text/html')); + } + + /** + * Renders the exception panel stylesheet for the given token. + * + * @param string $token The profiler token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function cssAction($token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + $exception = $this->profiler->loadProfile($token)->getCollector('exception')->getException(); + $template = $this->getTemplate(); + + if (!$this->templateExists($template)) { + $handler = new ExceptionHandler($this->debug, $this->twig->getCharset()); + + return new Response($handler->getStylesheet($exception), 200, array('Content-Type' => 'text/css')); + } + + return new Response($this->twig->render('@WebProfiler/Collector/exception.css.twig'), 200, array('Content-Type' => 'text/css')); + } + + protected function getTemplate() + { + return '@Twig/Exception/'.($this->debug ? 'exception' : 'error').'.html.twig'; + } + + // to be removed when the minimum required version of Twig is >= 2.0 + protected function templateExists($template) + { + $loader = $this->twig->getLoader(); + if ($loader instanceof \Twig_ExistsLoaderInterface) { + return $loader->exists($template); + } + + try { + $loader->getSource($template); + + return true; + } catch (\Twig_Error_Loader $e) { + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php new file mode 100644 index 0000000..a5590c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -0,0 +1,381 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Controller; + +use Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * ProfilerController. + * + * @author Fabien Potencier + */ +class ProfilerController +{ + private $templateManager; + private $generator; + private $profiler; + private $twig; + private $templates; + private $toolbarPosition; + + /** + * Constructor. + * + * @param UrlGeneratorInterface $generator The URL Generator + * @param Profiler $profiler The profiler + * @param \Twig_Environment $twig The twig environment + * @param array $templates The templates + * @param string $toolbarPosition The toolbar position (top, bottom, normal, or null -- use the configuration) + */ + public function __construct(UrlGeneratorInterface $generator, Profiler $profiler = null, \Twig_Environment $twig, array $templates, $toolbarPosition = 'normal') + { + $this->generator = $generator; + $this->profiler = $profiler; + $this->twig = $twig; + $this->templates = $templates; + $this->toolbarPosition = $toolbarPosition; + } + + /** + * Redirects to the last profiles. + * + * @return RedirectResponse A RedirectResponse instance + * + * @throws NotFoundHttpException + */ + public function homeAction() + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + return new RedirectResponse($this->generator->generate('_profiler_search_results', array('token' => 'empty', 'limit' => 10)), 302, array('Content-Type' => 'text/html')); + } + + /** + * Renders a profiler panel for the given token. + * + * @param Request $request The current HTTP request + * @param string $token The profiler token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function panelAction(Request $request, $token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + $panel = $request->query->get('panel', 'request'); + $page = $request->query->get('page', 'home'); + + if ('latest' === $token && $latest = current($this->profiler->find(null, null, 1, null, null, null))) { + $token = $latest['token']; + } + + if (!$profile = $this->profiler->loadProfile($token)) { + return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', array('about' => 'no_token', 'token' => $token, 'request' => $request)), 200, array('Content-Type' => 'text/html')); + } + + if (!$profile->hasCollector($panel)) { + throw new NotFoundHttpException(sprintf('Panel "%s" is not available for token "%s".', $panel, $token)); + } + + return new Response($this->twig->render($this->getTemplateManager()->getName($profile, $panel), array( + 'token' => $token, + 'profile' => $profile, + 'collector' => $profile->getCollector($panel), + 'panel' => $panel, + 'page' => $page, + 'request' => $request, + 'templates' => $this->getTemplateManager()->getTemplates($profile), + 'is_ajax' => $request->isXmlHttpRequest(), + 'profiler_markup_version' => 2, // 1 = original profiler, 2 = Symfony 2.8+ profiler + )), 200, array('Content-Type' => 'text/html')); + } + + /** + * Displays information page. + * + * @param Request $request The current HTTP Request + * @param string $about The about message + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function infoAction(Request $request, $about) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', array( + 'about' => $about, + 'request' => $request, + )), 200, array('Content-Type' => 'text/html')); + } + + /** + * Renders the Web Debug Toolbar. + * + * @param Request $request The current HTTP Request + * @param string $token The profiler token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function toolbarAction(Request $request, $token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $session = $request->getSession(); + + if (null !== $session && $session->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag) { + // keep current flashes for one more request if using AutoExpireFlashBag + $session->getFlashBag()->setAll($session->getFlashBag()->peekAll()); + } + + if ('empty' === $token || null === $token) { + return new Response('', 200, array('Content-Type' => 'text/html')); + } + + $this->profiler->disable(); + + if (!$profile = $this->profiler->loadProfile($token)) { + return new Response('', 404, array('Content-Type' => 'text/html')); + } + + // the toolbar position (top, bottom, normal, or null -- use the configuration) + if (null === $position = $request->query->get('position')) { + $position = $this->toolbarPosition; + } + + $url = null; + try { + $url = $this->generator->generate('_profiler', array('token' => $token)); + } catch (\Exception $e) { + // the profiler is not enabled + } + + return new Response($this->twig->render('@WebProfiler/Profiler/toolbar.html.twig', array( + 'request' => $request, + 'position' => $position, + 'profile' => $profile, + 'templates' => $this->getTemplateManager()->getTemplates($profile), + 'profiler_url' => $url, + 'token' => $token, + 'profiler_markup_version' => 2, // 1 = original toolbar, 2 = Symfony 2.8+ toolbar + )), 200, array('Content-Type' => 'text/html')); + } + + /** + * Renders the profiler search bar. + * + * @param Request $request The current HTTP Request + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function searchBarAction(Request $request) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + if (null === $session = $request->getSession()) { + $ip = + $method = + $url = + $start = + $end = + $limit = + $token = null; + } else { + $ip = $request->query->get('ip', $session->get('_profiler_search_ip')); + $method = $request->query->get('method', $session->get('_profiler_search_method')); + $url = $request->query->get('url', $session->get('_profiler_search_url')); + $start = $request->query->get('start', $session->get('_profiler_search_start')); + $end = $request->query->get('end', $session->get('_profiler_search_end')); + $limit = $request->query->get('limit', $session->get('_profiler_search_limit')); + $token = $request->query->get('token', $session->get('_profiler_search_token')); + } + + return new Response( + $this->twig->render('@WebProfiler/Profiler/search.html.twig', array( + 'token' => $token, + 'ip' => $ip, + 'method' => $method, + 'url' => $url, + 'start' => $start, + 'end' => $end, + 'limit' => $limit, + 'request' => $request, + )), + 200, + array('Content-Type' => 'text/html') + ); + } + + /** + * Renders the search results. + * + * @param Request $request The current HTTP Request + * @param string $token The token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function searchResultsAction(Request $request, $token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + $profile = $this->profiler->loadProfile($token); + + $ip = $request->query->get('ip'); + $method = $request->query->get('method'); + $url = $request->query->get('url'); + $start = $request->query->get('start', null); + $end = $request->query->get('end', null); + $limit = $request->query->get('limit'); + + return new Response($this->twig->render('@WebProfiler/Profiler/results.html.twig', array( + 'request' => $request, + 'token' => $token, + 'profile' => $profile, + 'tokens' => $this->profiler->find($ip, $url, $limit, $method, $start, $end), + 'ip' => $ip, + 'method' => $method, + 'url' => $url, + 'start' => $start, + 'end' => $end, + 'limit' => $limit, + 'panel' => null, + 'request' => $request, + )), 200, array('Content-Type' => 'text/html')); + } + + /** + * Narrows the search bar. + * + * @param Request $request The current HTTP Request + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function searchAction(Request $request) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + $ip = preg_replace('/[^:\d\.]/', '', $request->query->get('ip')); + $method = $request->query->get('method'); + $url = $request->query->get('url'); + $start = $request->query->get('start', null); + $end = $request->query->get('end', null); + $limit = $request->query->get('limit'); + $token = $request->query->get('token'); + + if (null !== $session = $request->getSession()) { + $session->set('_profiler_search_ip', $ip); + $session->set('_profiler_search_method', $method); + $session->set('_profiler_search_url', $url); + $session->set('_profiler_search_start', $start); + $session->set('_profiler_search_end', $end); + $session->set('_profiler_search_limit', $limit); + $session->set('_profiler_search_token', $token); + } + + if (!empty($token)) { + return new RedirectResponse($this->generator->generate('_profiler', array('token' => $token)), 302, array('Content-Type' => 'text/html')); + } + + $tokens = $this->profiler->find($ip, $url, $limit, $method, $start, $end); + + return new RedirectResponse($this->generator->generate('_profiler_search_results', array( + 'request' => $request, + 'token' => $tokens ? $tokens[0]['token'] : 'empty', + 'ip' => $ip, + 'method' => $method, + 'url' => $url, + 'start' => $start, + 'end' => $end, + 'limit' => $limit, + )), 302, array('Content-Type' => 'text/html')); + } + + /** + * Displays the PHP info. + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function phpinfoAction() + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + ob_start(); + phpinfo(); + $phpinfo = ob_get_clean(); + + return new Response($phpinfo, 200, array('Content-Type' => 'text/html')); + } + + /** + * Gets the Template Manager. + * + * @return TemplateManager The Template Manager + */ + protected function getTemplateManager() + { + if (null === $this->templateManager) { + $this->templateManager = new TemplateManager($this->profiler, $this->twig, $this->templates); + } + + return $this->templateManager; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php new file mode 100644 index 0000000..f4a84bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Controller/RouterController.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Controller; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler; + +/** + * RouterController. + * + * @author Fabien Potencier + */ +class RouterController +{ + private $profiler; + private $twig; + private $matcher; + private $routes; + + public function __construct(Profiler $profiler = null, \Twig_Environment $twig, UrlMatcherInterface $matcher = null, RouteCollection $routes = null) + { + $this->profiler = $profiler; + $this->twig = $twig; + $this->matcher = $matcher; + $this->routes = (null === $routes && $matcher instanceof RouterInterface) ? $matcher->getRouteCollection() : $routes; + } + + /** + * Renders the profiler panel for the given token. + * + * @param string $token The profiler token + * + * @return Response A Response instance + * + * @throws NotFoundHttpException + */ + public function panelAction($token) + { + if (null === $this->profiler) { + throw new NotFoundHttpException('The profiler must be enabled.'); + } + + $this->profiler->disable(); + + if (null === $this->matcher || null === $this->routes) { + return new Response('The Router is not enabled.', 200, array('Content-Type' => 'text/html')); + } + + $profile = $this->profiler->loadProfile($token); + + $context = $this->matcher->getContext(); + $context->setMethod($profile->getMethod()); + $matcher = new TraceableUrlMatcher($this->routes, $context); + + $request = $profile->getCollector('request'); + + return new Response($this->twig->render('@WebProfiler/Router/panel.html.twig', array( + 'request' => $request, + 'router' => $profile->getCollector('router'), + 'traces' => $matcher->getTraces($request->getPathInfo()), + )), 200, array('Content-Type' => 'text/html')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..957a683 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This class contains the configuration information for the bundle. + * + * This information is solely responsible for how the different configuration + * sections are normalized, and merged. + * + * @author Fabien Potencier + */ +class Configuration implements ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('web_profiler'); + + $rootNode + ->children() + ->booleanNode('toolbar')->defaultFalse()->end() + ->scalarNode('position') + ->defaultValue('bottom') + ->validate() + ->ifNotInArray(array('bottom', 'top')) + ->thenInvalid('The CSS position %s is not supported') + ->end() + ->end() + ->booleanNode('intercept_redirects')->defaultFalse()->end() + ->scalarNode('excluded_ajax_paths')->defaultValue('^/(app(_[\\w]+)?\\.php/)?_wdt')->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php new file mode 100644 index 0000000..724fe55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener; + +/** + * WebProfilerExtension. + * + * Usage: + * + * + * + * @author Fabien Potencier + */ +class WebProfilerExtension extends Extension +{ + /** + * Loads the web profiler configuration. + * + * @param array $configs An array of configuration settings + * @param ContainerBuilder $container A ContainerBuilder instance + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('profiler.xml'); + $container->setParameter('web_profiler.debug_toolbar.position', $config['position']); + + if ($config['toolbar'] || $config['intercept_redirects']) { + $loader->load('toolbar.xml'); + $container->getDefinition('web_profiler.debug_toolbar')->replaceArgument(5, $config['excluded_ajax_paths']); + $container->setParameter('web_profiler.debug_toolbar.intercept_redirects', $config['intercept_redirects']); + $container->setParameter('web_profiler.debug_toolbar.mode', $config['toolbar'] ? WebDebugToolbarListener::ENABLED : WebDebugToolbarListener::DISABLED); + } + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/webprofiler'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php new file mode 100644 index 0000000..71c5090 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\EventListener; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * WebDebugToolbarListener injects the Web Debug Toolbar. + * + * The onKernelResponse method must be connected to the kernel.response event. + * + * The WDT is only injected on well-formed HTML (with a proper tag). + * This means that the WDT is never included in sub-requests or ESI requests. + * + * @author Fabien Potencier + */ +class WebDebugToolbarListener implements EventSubscriberInterface +{ + const DISABLED = 1; + const ENABLED = 2; + + protected $twig; + protected $urlGenerator; + protected $interceptRedirects; + protected $mode; + protected $position; + protected $excludedAjaxPaths; + + public function __construct(\Twig_Environment $twig, $interceptRedirects = false, $mode = self::ENABLED, $position = 'bottom', UrlGeneratorInterface $urlGenerator = null, $excludedAjaxPaths = '^/bundles|^/_wdt') + { + $this->twig = $twig; + $this->urlGenerator = $urlGenerator; + $this->interceptRedirects = (bool) $interceptRedirects; + $this->mode = (int) $mode; + $this->position = $position; + $this->excludedAjaxPaths = $excludedAjaxPaths; + } + + public function isEnabled() + { + return self::DISABLED !== $this->mode; + } + + public function onKernelResponse(FilterResponseEvent $event) + { + $response = $event->getResponse(); + $request = $event->getRequest(); + + if ($response->headers->has('X-Debug-Token') && null !== $this->urlGenerator) { + try { + $response->headers->set( + 'X-Debug-Token-Link', + $this->urlGenerator->generate('_profiler', array('token' => $response->headers->get('X-Debug-Token'))) + ); + } catch (\Exception $e) { + $response->headers->set('X-Debug-Error', get_class($e).': '.$e->getMessage()); + } + } + + if (!$event->isMasterRequest()) { + return; + } + + // do not capture redirects or modify XML HTTP Requests + if ($request->isXmlHttpRequest()) { + return; + } + + if ($response->headers->has('X-Debug-Token') && $response->isRedirect() && $this->interceptRedirects) { + $session = $request->getSession(); + if (null !== $session && $session->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag) { + // keep current flashes for one more request if using AutoExpireFlashBag + $session->getFlashBag()->setAll($session->getFlashBag()->peekAll()); + } + + $response->setContent($this->twig->render('@WebProfiler/Profiler/toolbar_redirect.html.twig', array('location' => $response->headers->get('Location')))); + $response->setStatusCode(200); + $response->headers->remove('Location'); + } + + if (self::DISABLED === $this->mode + || !$response->headers->has('X-Debug-Token') + || $response->isRedirection() + || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html')) + || 'html' !== $request->getRequestFormat() + ) { + return; + } + + $this->injectToolbar($response, $request); + } + + /** + * Injects the web debug toolbar into the given Response. + * + * @param Response $response A Response instance + */ + protected function injectToolbar(Response $response, Request $request) + { + $content = $response->getContent(); + $pos = strripos($content, ''); + + if (false !== $pos) { + $toolbar = "\n".str_replace("\n", '', $this->twig->render( + '@WebProfiler/Profiler/toolbar_js.html.twig', + array( + 'position' => $this->position, + 'excluded_ajax_paths' => $this->excludedAjaxPaths, + 'token' => $response->headers->get('X-Debug-Token'), + 'request' => $request, + ) + ))."\n"; + $content = substr($content, 0, $pos).$toolbar.substr($content, $pos); + $response->setContent($content); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => array('onKernelResponse', -128), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php new file mode 100644 index 0000000..44a3ba7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Profiler/TemplateManager.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Profiler; + +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpKernel\Profiler\Profile; + +/** + * Profiler Templates Manager. + * + * @author Fabien Potencier + * @author Artur Wielogórski + */ +class TemplateManager +{ + protected $twig; + protected $templates; + protected $profiler; + + /** + * Constructor. + * + * @param Profiler $profiler + * @param \Twig_Environment $twig + * @param array $templates + */ + public function __construct(Profiler $profiler, \Twig_Environment $twig, array $templates) + { + $this->profiler = $profiler; + $this->twig = $twig; + $this->templates = $templates; + } + + /** + * Gets the template name for a given panel. + * + * @param Profile $profile + * @param string $panel + * + * @return mixed + * + * @throws NotFoundHttpException + */ + public function getName(Profile $profile, $panel) + { + $templates = $this->getNames($profile); + + if (!isset($templates[$panel])) { + throw new NotFoundHttpException(sprintf('Panel "%s" is not registered in profiler or is not present in viewed profile.', $panel)); + } + + return $templates[$panel]; + } + + /** + * Gets the templates for a given profile. + * + * @param Profile $profile + * + * @return array + */ + public function getTemplates(Profile $profile) + { + $templates = $this->getNames($profile); + + foreach ($templates as $name => $template) { + $templates[$name] = $this->twig->loadTemplate($template); + } + + return $templates; + } + + /** + * Gets template names of templates that are present in the viewed profile. + * + * @param Profile $profile + * + * @return array + * + * @throws \UnexpectedValueException + */ + protected function getNames(Profile $profile) + { + $templates = array(); + + foreach ($this->templates as $arguments) { + if (null === $arguments) { + continue; + } + + list($name, $template) = $arguments; + + if (!$this->profiler->has($name) || !$profile->hasCollector($name)) { + continue; + } + + if ('.html.twig' === substr($template, -10)) { + $template = substr($template, 0, -10); + } + + if (!$this->templateExists($template.'.html.twig')) { + throw new \UnexpectedValueException(sprintf('The profiler template "%s.html.twig" for data collector "%s" does not exist.', $template, $name)); + } + + $templates[$name] = $template.'.html.twig'; + } + + return $templates; + } + + // to be removed when the minimum required version of Twig is >= 2.0 + protected function templateExists($template) + { + $loader = $this->twig->getLoader(); + if ($loader instanceof \Twig_ExistsLoaderInterface) { + return $loader->exists($template); + } + + try { + $loader->getSource($template); + + return true; + } catch (\Twig_Error_Loader $e) { + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/ICONS_LICENSE.txt b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/ICONS_LICENSE.txt new file mode 100644 index 0000000..2e20272 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/ICONS_LICENSE.txt @@ -0,0 +1,5 @@ +Icons License +============= + +Icons created by Sensio (http://www.sensio.com/) are shared under a Creative +Commons Attribution license (http://creativecommons.org/licenses/by-sa/3.0/). \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/bin/sync_assets.sh b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/bin/sync_assets.sh new file mode 100755 index 0000000..6cff8d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/bin/sync_assets.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +DIR=`php -r "echo realpath(dirname('$0'));"` + +cp $DIR/../../../FrameworkBundle/Resources/public/css/body.css $DIR/../views/Profiler/body.css.twig +cp $DIR/../../../FrameworkBundle/Resources/public/css/exception.css $DIR/../views/Collector/exception.css.twig diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml new file mode 100644 index 0000000..fe8b323 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.xml @@ -0,0 +1,32 @@ + + + + + + + + + + %data_collector.templates% + %web_profiler.debug_toolbar.position% + + + + + + + + + + + + %kernel.debug% + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml new file mode 100644 index 0000000..001418c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml @@ -0,0 +1,47 @@ + + + + + + web_profiler.controller.profiler:homeAction + + + + web_profiler.controller.profiler:searchAction + + + + web_profiler.controller.profiler:searchBarAction + + + + web_profiler.controller.profiler:infoAction + + + + web_profiler.controller.profiler:phpinfoAction + + + + web_profiler.controller.profiler:searchResultsAction + + + + web_profiler.controller.profiler:panelAction + + + + web_profiler.controller.router:panelAction + + + + web_profiler.controller.exception:showAction + + + + web_profiler.controller.exception:cssAction + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.xml b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.xml new file mode 100644 index 0000000..1027ce4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.xml @@ -0,0 +1,10 @@ + + + + + + web_profiler.controller.profiler:toolbarAction + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd new file mode 100644 index 0000000..84cc8ae --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml new file mode 100644 index 0000000..f624cb3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.xml @@ -0,0 +1,18 @@ + + + + + + + + + %web_profiler.debug_toolbar.intercept_redirects% + %web_profiler.debug_toolbar.mode% + %web_profiler.debug_toolbar.position% + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/meta/LICENSE b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/meta/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/meta/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/ajax.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/ajax.html.twig new file mode 100644 index 0000000..fe73b66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/ajax.html.twig @@ -0,0 +1,29 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set icon %} + {{ include('@WebProfiler/Icon/ajax.svg') }} + 0 + {% endset %} + + {% set text %} +
    + +
    +
    + + + + + + + + + + +
    MethodURLTimeProfile
    +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: false }) }} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig new file mode 100644 index 0000000..a2a7dff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig @@ -0,0 +1,242 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% if 'unknown' == collector.symfonyState %} + {% set block_status = '' %} + {% set symfony_version_status = 'Unable to retrieve information about the Symfony version.' %} + {% elseif 'eol' == collector.symfonyState %} + {% set block_status = 'red' %} + {% set symfony_version_status = 'This Symfony version will no longer receive security fixes.' %} + {% elseif 'eom' == collector.symfonyState %} + {% set block_status = 'yellow' %} + {% set symfony_version_status = 'This Symfony version will only receive security fixes.' %} + {% elseif 'dev' == collector.symfonyState %} + {% set block_status = 'yellow' %} + {% set symfony_version_status = 'This Symfony version is still in the development phase.' %} + {% else %} + {% set block_status = '' %} + {% set symfony_version_status = '' %} + {% endif %} + + {% set icon %} + {% if collector.applicationname %} + {{ collector.applicationname }} + {{ collector.applicationversion }} + {% elseif collector.symfonyState is defined %} + + {{ include('@WebProfiler/Icon/symfony.svg') }} + + {{ collector.symfonyversion }} + {% endif %} + {% endset %} + + {% set text %} +
    + {% if collector.applicationname %} +
    + {{ collector.applicationname }} + {{ collector.applicationversion }} +
    + {% endif %} + +
    + Profiler token + + {% if profiler_url %} + {{ collector.token }} + {% else %} + {{ collector.token }} + {% endif %} + +
    + + {% if 'n/a' != collector.appname %} +
    + Kernel name + {{ collector.appname }} +
    + {% endif %} + + {% if 'n/a' != collector.env %} +
    + Environment + {{ collector.env }} +
    + {% endif %} + + {% if 'n/a' != collector.debug %} +
    + Debug + {{ collector.debug ? 'enabled' : 'disabled' }} +
    + {% endif %} +
    + +
    +
    + PHP version + + {{ collector.phpversion }} +   View phpinfo() + +
    + +
    + PHP Extensions + xdebug + accel +
    + +
    + PHP SAPI + {{ collector.sapiName }} +
    +
    + +
    + {% if collector.symfonyversion is defined %} +
    + Resources + + {% if 'Silex' == collector.applicationname %} + + Read Silex Docs + + {% else %} + + Read Symfony {{ collector.symfonyversion }} Docs + + {% endif %} + +
    + {% endif %} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: true, name: 'config', status: block_status, additional_classes: 'sf-toolbar-block-right' }) }} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/config.svg') }} + Configuration + +{% endblock %} + +{% block panel %} + {% if collector.applicationname %} + {# this application is not the Symfony framework #} +

    Project Configuration

    + +
    +
    + {{ collector.applicationname }} + Application name +
    + +
    + {{ collector.applicationversion }} + Application version +
    +
    + +

    + Based on Symfony {{ collector.symfonyversion }} +

    + {% else %} +

    Symfony Configuration

    + +
    +
    + {{ collector.symfonyversion }} + Symfony version +
    + + {% if 'n/a' != collector.appname %} +
    + {{ collector.appname }} + Application name +
    + {% endif %} + + {% if 'n/a' != collector.env %} +
    + {{ collector.env }} + Environment +
    + {% endif %} + + {% if 'n/a' != collector.debug %} +
    + {{ collector.debug ? 'enabled' : 'disabled' }} + Debug +
    + {% endif %} +
    + {% endif %} + +

    PHP Configuration

    + +
    +
    + {{ collector.phpversion }} + PHP version +
    + +
    + {{ include('@WebProfiler/Icon/' ~ (collector.hasaccelerator ? 'yes' : 'no') ~ '.svg') }} + PHP acceleration +
    + +
    + {{ include('@WebProfiler/Icon/' ~ (collector.hasxdebug ? 'yes' : 'no') ~ '.svg') }} + Xdebug +
    +
    + +
    +
    + {{ include('@WebProfiler/Icon/' ~ (collector.haszendopcache ? 'yes' : 'no') ~ '.svg') }} + OPcache +
    + +
    + {{ include('@WebProfiler/Icon/' ~ (collector.hasapc ? 'yes' : 'no') ~ '.svg') }} + APC +
    + +
    + {{ include('@WebProfiler/Icon/' ~ (collector.hasxcache ? 'yes' : 'no') ~ '.svg') }} + XCache +
    + +
    + {{ include('@WebProfiler/Icon/' ~ (collector.haseaccelerator ? 'yes' : 'no') ~ '.svg') }} + EAccelerator +
    +
    + +

    + View full PHP configuration +

    + + {% if collector.bundles %} +

    Enabled Bundles ({{ collector.bundles|length }})

    + + + + + + + + + {% for name in collector.bundles|keys|sort %} + + + + + {% endfor %} + +
    NamePath
    {{ name }}{{ collector.bundles[name] }}
    + {% endif %} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig new file mode 100644 index 0000000..24a1e3f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -0,0 +1,117 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% import _self as helper %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/event.svg') }} + Events + +{% endblock %} + +{% block panel %} +

    Event Dispatcher

    + + {% if collector.calledlisteners is empty %} +
    +

    No events have been recorded. Check that debugging is enabled in the kernel.

    +
    + {% else %} +
    +
    +

    Called Listeners {{ collector.calledlisteners|length }}

    + +
    + {{ helper.render_table(collector.calledlisteners) }} +
    +
    + +
    +

    Not Called Listeners {{ collector.notcalledlisteners|length }}

    +
    + {% if collector.notcalledlisteners is empty %} +
    +

    + There are no uncalled listeners. +

    +

    + All listeners were called for this request or an error occurred + when trying to collect uncalled listeners (in which case check the + logs to get more information). +

    +
    + {% else %} + {{ helper.render_table(collector.notcalledlisteners) }} + {% endif %} +
    +
    +
    + {% endif %} +{% endblock %} + +{% macro render_table(listeners) %} + + + + + + + + + {% set previous_event = (listeners|first).event %} + {% for listener in listeners %} + {% if loop.first or listener.event != previous_event %} + {% if not loop.first %} + + {% endif %} + + + + + + + {% set previous_event = listener.event %} + {% endif %} + + + + + + + {% if loop.last %} + + {% endif %} + {% endfor %} +
    PriorityListener
    {{ listener.event }}
    {{ listener.priority|default('-') }} + {% if listener.type == 'Closure' %} + + Closure + (there is no class or file information) + + {% elseif listener.type == 'Function' %} + + {% set link = listener.file|file_link(listener.line) %} + {% if link %} + {{ listener.function }}() + ({{ listener.file }}) + {% else %} + {{ listener.function }}() + {{ listener.file }} (line {{ listener.line }}) + {% endif %} + + {% elseif listener.type == "Method" %} + + {% set link = listener.file|file_link(listener.line) %} + {% set class_namespace = listener.class|split('\\', -1)|join('\\') %} + + {% if link %} + {{ listener.class|abbr_class|striptags }}::{{ listener.method }}() + ({{ listener.class }}) + {% else %} + {{ class_namespace }}\{{ listener.class|abbr_class|striptags }}::{{ listener.method }}() + {{ listener.file }} (line {{ listener.line }}) + {% endif %} + + {% endif %} +
    +{% endmacro %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig new file mode 100644 index 0000000..8d5eaf7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.css.twig @@ -0,0 +1,97 @@ +.sf-reset .traces { + padding: 0 0 1em 1.5em; +} +.sf-reset .traces a { + font-size: 14px; +} +.sf-reset .traces abbr { + border-bottom-color: #AAA; + padding-bottom: 2px; +} +.sf-reset .traces li { + ccolor: #222; + font-size: 14px; + padding: 5px 0; + list-style-type: decimal; + margin: 0 0 0 1em; + white-space: break-word; +} +.sf-reset .traces li.selected { + background: rgba(255, 255, 153, 0.5); +} + +.sf-reset .traces ol li { + font-size: 12px; + color: #777; +} +.sf-reset #logs .traces li.error { + color: #AA3333; +} +.sf-reset #logs .traces li.warning { + background: #FFCC00; +} +.sf-reset .trace { + border: 1px solid #DDD; + background: #FFF; + padding: 10px; + overflow: auto; + margin: 1em 0; +} +.sf-reset .trace code, +#traces-text pre { + font-size: 13px; +} +.sf-reset .block-exception { + margin-bottom: 2em; + background-color: #FFF; + border: 1px solid #EEE; + padding: 28px; + word-wrap: break-word; + overflow: hidden; +} +.sf-reset .block-exception h1 { + font-size: 21px; + font-weight: normal; + margin: 0 0 12px; +} +.sf-reset .block-exception .linked { + margin-top: 1em; +} + +.sf-reset .block { + margin-bottom: 2em; +} +.sf-reset .block h2 { + font-size: 16px; +} +.sf-reset .block-exception div { + font-size: 14px; +} +.sf-reset .block-exception-detected .illustration-exception, +.sf-reset .block-exception-detected .text-exception { + float: left; +} +.sf-reset .block-exception-detected .illustration-exception { + width: 110px; +} +.sf-reset .block-exception-detected .text-exception { + width: 650px; + margin-left: 20px; + padding: 30px 44px 24px 46px; + position: relative; +} +.sf-reset .text-exception .open-quote, +.sf-reset .text-exception .close-quote { + position: absolute; +} +.sf-reset .open-quote { + top: 0; + left: 0; +} +.sf-reset .close-quote { + bottom: 0; + right: 50px; +} +.sf-reset .toggle { + vertical-align: middle; +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig new file mode 100644 index 0000000..94dfbb6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig @@ -0,0 +1,36 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block head %} + {% if collector.hasexception %} + + {% endif %} + {{ parent() }} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/exception.svg') }} + Exception + {% if collector.hasexception %} + + 1 + + {% endif %} + +{% endblock %} + +{% block panel %} +

    Exceptions

    + + {% if not collector.hasexception %} +
    +

    No exception was thrown and caught during the request.

    +
    + {% else %} +
    + {{ render(path('_profiler_exception', { token: token })) }} +
    + {% endif %} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig new file mode 100644 index 0000000..07dd6bb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -0,0 +1,723 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% from _self import form_tree_entry, form_tree_details %} + +{% block toolbar %} + {% if collector.data.nb_errors > 0 or collector.data.forms|length %} + {% set status_color = collector.data.nb_errors ? 'red' : '' %} + {% set icon %} + {{ include('@WebProfiler/Icon/form.svg') }} + + {{ collector.data.nb_errors ?: collector.data.forms|length }} + + {% endset %} + + {% set text %} +
    + Number of forms + {{ collector.data.forms|length }} +
    +
    + Number of errors + {{ collector.data.nb_errors }} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }} + {% endif %} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/form.svg') }} + Forms + {% if collector.data.nb_errors > 0 %} + + {{ collector.data.nb_errors }} + + {% endif %} + +{% endblock %} + +{% block head %} + {{ parent() }} + + +{% endblock %} + +{% block panel %} +

    Forms

    + + {% if collector.data.forms|length %} +
    +
      + {% for formName, formData in collector.data.forms %} + {{ form_tree_entry(formName, formData, true) }} + {% endfor %} +
    +
    + +
    + {% for formName, formData in collector.data.forms %} + {{ form_tree_details(formName, formData, collector.data.forms_by_hash) }} + {% endfor %} +
    + {% else %} +
    +

    No forms were submitted for this request.

    +
    + {% endif %} + + +{% endblock %} + +{% macro form_tree_entry(name, data, expanded) %} + {% import _self as tree %} +
  • +
    + {% if data.children is not empty %} + + {% else %} +
    + {% endif %} + + {{ name|default('(no name)') }} {% if data.type_class is defined and data.type is defined %}[{{ data.type }}]{% endif %} + + {% if data.errors is defined and data.errors|length > 0 %} +
    {{ data.errors|length }}
    + {% endif %} +
    + + {% if data.children is not empty %} +
      + {% for childName, childData in data.children %} + {{ tree.form_tree_entry(childName, childData, false) }} + {% endfor %} +
    + {% endif %} +
  • +{% endmacro %} + +{% macro form_tree_details(name, data, forms_by_hash) %} + {% import _self as tree %} +
    +

    + {{ name|default('(no name)') }} + {% if data.type_class is defined and data.type is defined %} + [{{ data.type }}] + {% endif %} +

    + + {% if data.errors is defined and data.errors|length > 0 %} +
    +

    + + Errors + +

    + + + + + + + + + + + {% for error in data.errors %} + + + + + + {% endfor %} + +
    MessageOriginCause
    {{ error.message }} + {% if error.origin is empty %} + This form. + {% elseif forms_by_hash[error.origin] is not defined %} + Unknown. + {% else %} + {{ forms_by_hash[error.origin].name }} + {% endif %} + + {% for trace in error.trace %} + {% if not loop.first %} + Caused by: + {% endif %} + + {% if trace.root is defined %} + {{ trace.class }} +
    +                                    {{- trace.root -}}
    +                                    {%- if trace.path is not empty -%}
    +                                        {%- if trace.path|first != '[' %}.{% endif -%}
    +                                        {{- trace.path -}}
    +                                    {%- endif %} = {{ trace.value -}}
    +                                
    + {% elseif trace.message is defined %} + {{ trace.class }} +
    {{ trace.message }}
    + {% else %} +
    {{ trace }}
    + {% endif %} + {% else %} + Unknown. + {% endfor %} +
    +
    + {% endif %} + + {% if data.default_data is defined %} +

    + + Default Data + +

    + +
    + + + + + + + + + + + + + + + + + + + + + +
    PropertyValue
    Model Format + {% if data.default_data.model is defined %} + {{ data.default_data.model }} + {% else %} + same as normalized format + {% endif %} +
    Normalized Format{{ data.default_data.norm }}
    View Format + {% if data.default_data.view is defined %} + {{ data.default_data.view }} + {% else %} + same as normalized format + {% endif %} +
    +
    + {% endif %} + + {% if data.submitted_data is defined %} +

    + + Submitted Data + +

    + +
    + {% if data.submitted_data.norm is defined %} + + + + + + + + + + + + + + + + + + + + + +
    PropertyValue
    View Format + {% if data.submitted_data.view is defined %} + {{ data.submitted_data.view }} + {% else %} + same as normalized format + {% endif %} +
    Normalized Format{{ data.submitted_data.norm }}
    Model Format + {% if data.submitted_data.model is defined %} + {{ data.submitted_data.model }} + {% else %} + same as normalized format + {% endif %} +
    + {% else %} +
    +

    This form was not submitted.

    +
    + {% endif %} +
    + {% endif %} + + {% if data.passed_options is defined %} +

    + + Passed Options + +

    + +
    + {% if data.passed_options|length %} + + + + + + + + + + {% for option, value in data.passed_options %} + + + + + + {% endfor %} + +
    OptionPassed ValueResolved Value
    {{ option }}{{ value }} + {% if data.resolved_options[option] is same as(value) %} + same as passed value + {% else %} + {{ data.resolved_options[option] }} + {% endif %} +
    + {% else %} +
    +

    No options where passed when constructing this form.

    +
    + {% endif %} +
    + {% endif %} + + {% if data.resolved_options is defined %} +

    + + Resolved Options + +

    + + + {% endif %} + + {% if data.view_vars is defined %} +

    + + View Variables + +

    + + + {% endif %} +
    + + {% for childName, childData in data.children %} + {{ tree.form_tree_details(childName, childData, forms_by_hash) }} + {% endfor %} +{% endmacro %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig new file mode 100644 index 0000000..6f819e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -0,0 +1,222 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% import _self as helper %} + +{% block toolbar %} + {% if collector.counterrors or collector.countdeprecations or collector.countscreams %} + {% set icon %} + {% set status_color = collector.counterrors ? 'red' : collector.countdeprecations ? 'yellow' : '' %} + {% set error_count = collector.counterrors + collector.countdeprecations + collector.countscreams %} + {{ include('@WebProfiler/Icon/logger.svg') }} + {{ error_count }} + {% endset %} + + {% set text %} +
    + Errors + {{ collector.counterrors|default(0) }} +
    + +
    + Deprecated Calls + {{ collector.countdeprecations|default(0) }} +
    + +
    + Silenced Errors + {{ collector.countscreams|default(0) }} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }} + {% endif %} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/logger.svg') }} + Logs + {% if collector.counterrors or collector.countdeprecations %} + + {{ collector.counterrors ?: collector.countdeprecations }} + + {% endif %} + +{% endblock %} + +{% block panel %} +

    Log Messages

    + + {% if collector.logs is empty %} +
    +

    No log messages available.

    +
    + {% else %} + {# sort collected logs in groups #} + {% set deprecation_logs, debug_logs, info_and_error_logs, silenced_logs = [], [], [], [] %} + {% for log in collector.logs %} + {% if log.context.level is defined and log.context.type is defined and log.context.type in [constant('E_DEPRECATED'), constant('E_USER_DEPRECATED')] %} + {% set deprecation_logs = deprecation_logs|merge([log]) %} + {% elseif log.context.scream is defined and log.context.scream == true %} + {% set silenced_logs = silenced_logs|merge([log]) %} + {% elseif log.priorityName == 'DEBUG' %} + {% set debug_logs = debug_logs|merge([log]) %} + {% else %} + {% set info_and_error_logs = info_and_error_logs|merge([log]) %} + {% endif %} + {% endfor %} + +
    +
    +

    Info. & Errors {{ info_and_error_logs|length }}

    + +
    + {% if info_and_error_logs is empty %} +
    +

    There are no log messages of this level.

    +
    + {% else %} + {{ helper.render_table(info_and_error_logs, true) }} + {% endif %} +
    +
    + +
    + {# 'deprecation_logs|length' is not used because deprecations are + now grouped and the group count doesn't match the message count #} +

    Deprecations {{ collector.countdeprecations|default(0) }}

    + +
    + {% if deprecation_logs is empty %} +
    +

    There are no log messages about deprecated features.

    +
    + {% else %} + {{ helper.render_table(deprecation_logs, false, true) }} + {% endif %} +
    +
    + +
    +

    Debug {{ debug_logs|length }}

    + +
    + {% if debug_logs is empty %} +
    +

    There are no log messages of this level.

    +
    + {% else %} + {{ helper.render_table(debug_logs) }} + {% endif %} +
    +
    + +
    +

    Silenced Errors {{ collector.countscreams|default(0) }}

    + +
    + {% if silenced_logs is empty %} +
    +

    There are no log messages of this level.

    +
    + {% else %} + {{ helper.render_table(silenced_logs) }} + {% endif %} +
    +
    + +
    + {% endif %} +{% endblock %} + +{% macro render_table(logs, show_level = false, is_deprecation = false) %} + {% import _self as helper %} + {% set channel_is_defined = (logs|first).channel is defined %} + + + + + + {% if channel_is_defined %}{% endif %} + + + + + + {% for log in logs %} + {% set css_class = is_deprecation ? '' + : log.priorityName in ['CRITICAL', 'ERROR', 'ALERT', 'EMERGENCY'] ? 'status-error' + : log.priorityName in ['NOTICE', 'WARNING'] ? 'status-warning' + %} + + + + {% if channel_is_defined %} + + {% endif %} + + + + {% endfor %} + +
    {{ show_level ? 'Level' : 'Time' }}ChannelMessage
    + {% if show_level %} + {{ log.priorityName }} + {% endif %} + {{ log.timestamp|date('H:i:s') }} + {{ log.channel }}{{ helper.render_log_message(loop.index, log, is_deprecation) }}
    +{% endmacro %} + +{% macro render_log_message(log_index, log, is_deprecation = false) %} + {{ log.message }} + + {% if is_deprecation %} + {% set stack = log.context.stack|default([]) %} + {% set id = 'sf-call-stack-' ~ log_index %} + + {% if log.context.errorCount is defined %} + ({{ log.context.errorCount }} times) + {% endif %} + + {% if stack %} + + {% endif %} + + {% for index, call in stack if index > 1 %} + {% if index == 2 %} +
      + {% endif %} + + {% if call.class is defined %} + {% set from = call.class|abbr_class ~ '::' ~ call.function|abbr_method() %} + {% elseif call.function is defined %} + {% set from = call.function|abbr_method %} + {% elseif call.file is defined %} + {% set from = call.file %} + {% else %} + {% set from = '-' %} + {% endif %} + + {% set file_name = (call.file is defined and call.line is defined) ? call.file|replace({'\\': '/'})|split('/')|last %} + +
    • + {{ from|raw }} + {% if file_name %} + (called from {{ call.file|format_file(call.line, file_name)|raw }}) + {% endif %} +
    • + + {% if index == stack|length - 1 %} +
    + {% endif %} + {% endfor %} + {% else %} + {% if log.context is defined and log.context is not empty %} + + {% endif %} + {% endif %} +{% endmacro %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig new file mode 100644 index 0000000..268f8fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig @@ -0,0 +1,24 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set icon %} + {% set status_color = (collector.memory / 1024 / 1024) > 50 ? 'yellow' : '' %} + {{ include('@WebProfiler/Icon/memory.svg') }} + {{ '%.1f'|format(collector.memory / 1024 / 1024) }} + MB + {% endset %} + + {% set text %} +
    + Peak memory usage + {{ '%.1f'|format(collector.memory / 1024 / 1024) }} MB +
    + +
    + PHP memory limit + {{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MB'|format(collector.memoryLimit / 1024 / 1024) }} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, name: 'time', status: status_color }) }} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig new file mode 100644 index 0000000..db842c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/request.html.twig @@ -0,0 +1,226 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set request_handler %} + {% if collector.controller.class is defined %} + {% set link = collector.controller.file|file_link(collector.controller.line) %} + {% if link %}{% else %}{% endif %} + + {{ collector.controller.class|abbr_class|striptags }} + + {%- if collector.controller.method -%} +  :: {{ collector.controller.method }} + {%- endif -%} + + {% if link %}{% else %}
    {% endif %} + {% else %} + {{ collector.controller }} + {% endif %} + {% endset %} + + {% set request_status_code_color = (collector.statuscode >= 400) ? 'red' : (collector.statuscode >= 300) ? 'yellow' : 'green' %} + + {% set icon %} + {{ collector.statuscode }} + {% if collector.route %} + @ + {{ collector.route }} + {% endif %} + {% endset %} + + {% set text %} +
    + HTTP status + {{ collector.statuscode }} {{ collector.statustext }} +
    + +
    + Controller + {{ request_handler }} +
    + + {% if collector.controller.class is defined %} +
    + Controller class + {{ collector.controller.class }} +
    + {% endif %} + +
    + Route name + {{ collector.route|default('NONE') }} +
    + +
    + Has session + {% if collector.sessionmetadata|length %}yes{% else %}no{% endif %} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/request.svg') }} + Request / Response + +{% endblock %} + +{% block panel %} +
    +
    +

    Request

    + +
    +

    GET Parameters

    + + {% if collector.requestquery.all is empty %} +
    +

    No GET parameters

    +
    + {% else %} + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestquery }, with_context = false) }} + {% endif %} + +

    POST Parameters

    + + {% if collector.requestrequest.all is empty %} +
    +

    No POST parameters

    +
    + {% else %} + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestrequest }, with_context = false) }} + {% endif %} + +

    Request Attributes

    + + {% if collector.requestattributes.all is empty %} +
    +

    No attributes

    +
    + {% else %} + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestattributes }, with_context = false) }} + {% endif %} + +

    Cookies

    + + {% if collector.requestcookies.all is empty %} +
    +

    No cookies

    +
    + {% else %} + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestcookies }, with_context = false) }} + {% endif %} + +

    Request Headers

    + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestheaders, labels: ['Header', 'Value'] }, with_context = false) }} + +

    Request Content

    + + {% if collector.content == false %} +
    +

    Request content not available (it was retrieved as a resource).

    +
    + {% elseif collector.content %} +
    +
    {{ collector.content }}
    +
    + {% else %} +
    +

    No content

    +
    + {% endif %} + +

    Server Parameters

    + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestserver }, with_context = false) }} +
    +
    + +
    +

    Response

    + +
    +

    Response Headers

    + + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responseheaders, labels: ['Header', 'Value'] }, with_context = false) }} +
    +
    + + + +
    +

    Flashes

    + +
    +

    Flashes

    + + {% if collector.flashes is empty %} +
    +

    No flash messages were created.

    +
    + {% else %} + {{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.flashes }, with_context = false) }} + {% endif %} +
    +
    + + {% if profile.parent %} +
    +

    Parent Request

    + +
    +

    + Return to parent request + (token = {{ profile.parent.token }}) +

    + + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: profile.parent.getcollector('request').requestattributes }, with_context = false) }} +
    +
    + {% endif %} + + {% if profile.children|length %} +
    +

    Sub Requests {{ profile.children|length }}

    + +
    + {% for child in profile.children %} +

    + + {{- child.getcollector('request').requestattributes.get('_controller') -}} + + (token = {{ child.token }}) +

    + + {{ include('@WebProfiler/Profiler/bag.html.twig', { bag: child.getcollector('request').requestattributes }, with_context = false) }} + {% endfor %} +
    +
    + {% endif %} +
    +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/router.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/router.html.twig new file mode 100644 index 0000000..94faa71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/router.html.twig @@ -0,0 +1,14 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %}{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/router.svg') }} + Routing + +{% endblock %} + +{% block panel %} + {{ render(path('_profiler_router', { token: token })) }} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig new file mode 100644 index 0000000..1c065ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -0,0 +1,544 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% import _self as helper %} + +{% if colors is not defined %} + {% set colors = { + 'default': '#999', + 'section': '#444', + 'event_listener': '#00B8F5', + 'event_listener_loading': '#00B8F5', + 'template': '#66CC00', + 'doctrine': '#FF6633', + 'propel': '#FF6633', + } %} +{% endif %} + +{% block toolbar %} + {% set total_time = collector.events|length ? '%.0f'|format(collector.duration) : 'n/a' %} + {% set initialization_time = collector.events|length ? '%.0f'|format(collector.inittime) : 'n/a' %} + {% set status_color = collector.events|length and collector.duration > 1000 ? 'yellow' : '' %} + + {% set icon %} + {{ include('@WebProfiler/Icon/time.svg') }} + {{ total_time }} + ms + {% endset %} + + {% set text %} +
    + Total time + {{ total_time }} ms +
    +
    + Initialization time + {{ initialization_time }} ms +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/time.svg') }} + Performance + +{% endblock %} + +{% block panel %} +

    Performance metrics

    + +
    +
    + {{ '%.0f'|format(collector.duration) }} ms + Total execution time +
    + +
    + {{ '%.0f'|format(collector.inittime) }} ms + Symfony initialization +
    + + {% if profile.children|length > 0 %} +
    + {{ profile.children|length }} + Sub-Requests +
    + + {% set subrequests_time = 0 %} + {% for child in profile.children %} + {% set subrequests_time = subrequests_time + child.getcollector('time').events.__section__.duration %} + {% endfor %} + +
    + {{ subrequests_time }} ms + Sub-Requests time +
    + {% endif %} + + {% if profile.collectors.memory %} +
    + {{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} MB + Peak memory usage +
    + {% endif %} +
    + +

    Execution timeline

    + + {% if collector.events is empty %} +
    +

    No timing events have been recorded. Are you sure that debugging is enabled in the kernel?

    +
    + {% else %} + {{ block('panelContent') }} + {% endif %} +{% endblock %} + +{% block panelContent %} +
    + + + ms + (timeline only displays events with a duration longer than this threshold) +
    + + {% if profile.parent %} +

    + Sub-Request {{ profile.getcollector('request').requestattributes.get('_controller') }} + + {{ collector.events.__section__.duration }} ms + Return to parent request + +

    + {% elseif profile.children|length > 0 %} +

    + Main Request {{ collector.events.__section__.duration }} ms +

    + {% endif %} + + {{ helper.display_timeline('timeline_' ~ token, collector.events, colors) }} + + {% if profile.children|length %} +

    Note: sections with a striped background correspond to sub-requests.

    + +

    Sub-requests ({{ profile.children|length }})

    + + {% for child in profile.children %} + {% set events = child.getcollector('time').events %} +

    + {{ child.getcollector('request').requestattributes.get('_controller') }} + {{ events.__section__.duration }} ms +

    + + {{ helper.display_timeline('timeline_' ~ child.token, events, colors) }} + {% endfor %} + {% endif %} + + +{% endblock %} + +{% macro dump_request_data(token, profile, events, origin) %} +{% autoescape 'js' %} +{% from _self import dump_events %} + { + "id": "{{ token }}", + "left": {{ "%F"|format(events.__section__.origin - origin) }}, + "events": [ +{{ dump_events(events) }} + ] + } +{% endautoescape %} +{% endmacro %} + +{% macro dump_events(events) %} +{% autoescape 'js' %} +{% for name, event in events %} +{% if '__section__' != name %} + { + "name": "{{ name }}", + "category": "{{ event.category }}", + "origin": {{ "%F"|format(event.origin) }}, + "starttime": {{ "%F"|format(event.starttime) }}, + "endtime": {{ "%F"|format(event.endtime) }}, + "duration": {{ "%F"|format(event.duration) }}, + "memory": {{ "%.1F"|format(event.memory / 1024 / 1024) }}, + "periods": [ + {%- for period in event.periods -%} + {"start": {{ "%F"|format(period.starttime) }}, "end": {{ "%F"|format(period.endtime) }}}{{ loop.last ? '' : ', ' }} + {%- endfor -%} + ] + }{{ loop.last ? '' : ',' }} +{% endif %} +{% endfor %} +{% endautoescape %} +{% endmacro %} + +{% macro display_timeline(id, events, colors) %} +
    +
    + {% for category, color in colors %} + {{ category }} + {% endfor %} +
    + +
    +{% endmacro %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig new file mode 100644 index 0000000..0be04e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig @@ -0,0 +1,197 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% import _self as helper %} + +{% block toolbar %} + {% if collector.messages|length %} + {% set icon %} + {{ include('@WebProfiler/Icon/translation.svg') }} + {% set status_color = collector.countMissings ? 'red' : collector.countFallbacks ? 'yellow' %} + {% set error_count = collector.countMissings + collector.countFallbacks %} + {{ error_count ?: collector.countdefines }} + {% endset %} + + {% set text %} +
    + Missing messages + + {{ collector.countMissings }} + +
    + +
    + Fallback messages + + {{ collector.countFallbacks }} + +
    + +
    + Defined messages + {{ collector.countdefines }} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }} + {% endif %} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/translation.svg') }} + Translation + {% if collector.countMissings or collector.countFallbacks %} + {% set error_count = collector.countMissings + collector.countFallbacks %} + + {{ error_count }} + + {% endif %} + +{% endblock %} + +{% block panel %} + {% if collector.messages is empty %} +

    Translations

    +
    +

    No translations have been called.

    +
    + {% else %} + {{ block('panelContent') }} + {% endif %} +{% endblock %} + +{% block panelContent %} +

    Translation Metrics

    + +
    +
    + {{ collector.countdefines }} + Defined messages +
    + +
    + {{ collector.countFallbacks }} + Fallback messages +
    + +
    + {{ collector.countMissings }} + Missing messages +
    +
    + +

    Translation Messages

    + + {# sort translation messages in groups #} + {% set messages_defined, messages_missing, messages_fallback = [], [], [] %} + {% for message in collector.messages %} + {% if message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_DEFINED') %} + {% set messages_defined = messages_defined|merge([message]) %} + {% elseif message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_MISSING') %} + {% set messages_missing = messages_missing|merge([message]) %} + {% elseif message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK') %} + {% set messages_fallback = messages_fallback|merge([message]) %} + {% endif %} + {% endfor %} + +
    +
    +

    Defined {{ messages_defined|length }}

    + +
    +

    + These messages are correctly translated into the given locale. +

    + + {% if messages_defined is empty %} +
    +

    None of the used translation messages are defined for the given locale.

    +
    + {% else %} + {{ helper.render_table(messages_defined) }} + {% endif %} +
    +
    + +
    +

    Fallback {{ messages_fallback|length }}

    + +
    +

    + These messages are not available for the given locale + but Symfony found them in the fallback locale catalog. +

    + + {% if messages_fallback is empty %} +
    +

    No fallback translation messages were used.

    +
    + {% else %} + {{ helper.render_table(messages_fallback) }} + {% endif %} +
    +
    + +
    +

    Missing {{ messages_missing|length }}

    + +
    +

    + These messages are not available for the given locale and cannot + be found in the fallback locales. Add them to the translation + catalogue to avoid Symfony outputting untranslated contents. +

    + + {% if messages_missing is empty %} +
    +

    There are no messages of this category.

    +
    + {% else %} + {{ helper.render_table(messages_missing) }} + {% endif %} +
    +
    +
    +{% endblock %} + +{% macro render_table(messages) %} + + + + + + + + + + + + {% for message in messages %} + + + + + + + + {% endfor %} + +
    LocaleDomainTimes usedMessage IDMessage Preview
    {{ message.locale }}{{ message.domain }}{{ message.count }} + {{ message.id }} + + {% if message.transChoiceNumber is not null %} + (pluralization is used) + {% endif %} + + {% if message.parameters|length > 0 %} + + + + {% endif %} + {{ message.translation }}
    +{% endmacro %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig new file mode 100644 index 0000000..c3c8a5d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig @@ -0,0 +1,101 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set time = collector.templatecount ? '%0.0f'|format(collector.time) : 'n/a' %} + {% set icon %} + {{ include('@WebProfiler/Icon/twig.svg') }} + {{ time }} + ms + {% endset %} + + {% set text %} +
    + Render Time + {{ time }} ms +
    +
    + Template Calls + {{ collector.templatecount }} +
    +
    + Block Calls + {{ collector.blockcount }} +
    +
    + Macro Calls + {{ collector.macrocount }} +
    + {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }} +{% endblock %} + +{% block menu %} + + {{ include('@WebProfiler/Icon/twig.svg') }} + Twig + +{% endblock %} + +{% block panel %} + {% if collector.templatecount == 0 %} +

    Twig

    + +
    +

    No Twig templates were rendered for this request.

    +
    + {% else %} +

    Twig Metrics

    + +
    +
    + {{ '%0.0f'|format(collector.time) }} ms + Render time +
    + +
    + {{ collector.templatecount }} + Template calls +
    + +
    + {{ collector.blockcount }} + Block calls +
    + +
    + {{ collector.macrocount }} + Macro calls +
    +
    + +

    + Render time includes sub-requests rendering time (if any). +

    + +

    Rendered Templates

    + + + + + + + + + + {% for template, count in collector.templates %} + + + + + {% endfor %} + +
    Template NameRender Count
    {{ template }}{{ count }}
    + +

    Rendering Call Graph

    + +
    + {{ collector.htmlcallgraph }} +
    + {% endif %} +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg new file mode 100644 index 0000000..d8cad84 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/ajax.svg @@ -0,0 +1,6 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg new file mode 100644 index 0000000..49a75fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/close.svg @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg new file mode 100644 index 0000000..9bafe38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/config.svg @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg new file mode 100644 index 0000000..7d15a66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/event.svg @@ -0,0 +1,11 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg new file mode 100644 index 0000000..98e4eb1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/exception.svg @@ -0,0 +1,15 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg new file mode 100644 index 0000000..18bdb6e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/form.svg @@ -0,0 +1,6 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg new file mode 100644 index 0000000..3270a33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/logger.svg @@ -0,0 +1,7 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg new file mode 100644 index 0000000..5cd2fc4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/memory.svg @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg new file mode 100644 index 0000000..856ea09 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg new file mode 100644 index 0000000..02eb4e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/no.svg @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg new file mode 100644 index 0000000..d74a728 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/request.svg @@ -0,0 +1,16 @@ + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg new file mode 100644 index 0000000..061a0a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/router.svg @@ -0,0 +1,6 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg new file mode 100644 index 0000000..b5f0a2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/search.svg @@ -0,0 +1,7 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg new file mode 100644 index 0000000..8d0ab14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/symfony.svg @@ -0,0 +1,12 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg new file mode 100644 index 0000000..ec280a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/time.svg @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg new file mode 100644 index 0000000..93d4232 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/translation.svg @@ -0,0 +1,13 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg new file mode 100644 index 0000000..21d81ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/twig.svg @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg new file mode 100644 index 0000000..0f129bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/yes.svg @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/ajax_layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/ajax_layout.html.twig new file mode 100644 index 0000000..3e2f6f0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/ajax_layout.html.twig @@ -0,0 +1 @@ +{% block panel '' %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig new file mode 100644 index 0000000..bfcef94 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/bag.html.twig @@ -0,0 +1,20 @@ + + + + + + + + + {% for key in bag.keys|sort %} + + + + + {% else %} + + + + {% endfor %} + +
    {{ labels is defined ? labels[0] : 'Key' }}{{ labels is defined ? labels[1] : 'Value' }}
    {{ key }}{{ profiler_dump(bag.get(key)) }}
    (no data)
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig new file mode 100644 index 0000000..5386b01 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base.html.twig @@ -0,0 +1,19 @@ + + + + + + + Symfony Profiler + + + {% block head %} + + {% endblock %} + + + {% block body '' %} + + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig new file mode 100644 index 0000000..17f3d4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -0,0 +1,433 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/header.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/header.html.twig new file mode 100644 index 0000000..d04cf37 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/header.html.twig @@ -0,0 +1,14 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig new file mode 100644 index 0000000..ef381fe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig @@ -0,0 +1,37 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% set messages = { + 'purge' : { + status: 'success', + title: 'The profiler database was purged successfully', + message: 'Now you need to browse some pages with the Symfony Profiler enabled to collect data.' + }, + 'no_token' : { + status: 'error', + title: (token|default('') == 'latest') ? 'There are no profiles' : 'Token not found', + message: (token|default('') == 'latest') ? 'No profiles found in the database.' : 'Token "' ~ token|default('') ~ '" was not found in the database.' + }, + 'upload_error' : { + status: 'error', + title: 'A problem occurred when uploading the data', + message: 'No file given or the file was not uploaded successfully.' + }, + 'already_exists' : { + status: 'error', + title: 'A problem occurred when uploading the data', + message: 'The token already exists in the database.' + } +} %} + +{% block summary %} +
    +
    +

    {{ messages[about].status|title }}

    +
    +
    +{% endblock %} + +{% block panel %} +

    {{ messages[about].title }}

    +

    {{ messages[about].message }}

    +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig new file mode 100644 index 0000000..e2bc8f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig @@ -0,0 +1,86 @@ +{% extends '@WebProfiler/Profiler/base.html.twig' %} + +{% block body %} + {{ include('@WebProfiler/Profiler/header.html.twig', with_context = false) }} + +
    + {% block summary %} + {% if profile is defined %} + {% set status_code = ('request' in profile.collectors|keys) ? profile.getcollector('request').statuscode|default(0) : 0 %} + {% set css_class = status_code > 399 ? 'status-error' : status_code > 299 ? 'status-warning' : 'status-success' %} + +
    +
    +

    + {% if profile.method|upper in ['GET', 'HEAD'] %} + {{ profile.url }} + {% else %} + {{ profile.url }} + {% endif %} +

    + + +
    +
    + {% endif %} + {% endblock %} +
    + +
    +
    +
    +
    + {{ include('@WebProfiler/Profiler/base_js.html.twig') }} + {% block panel '' %} +
    +
    + + +
    +
    +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig new file mode 100644 index 0000000..b61f1c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -0,0 +1,952 @@ +{# Mixins + ========================================================================= #} +{% set mixins = { + 'break_long_words': '-ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto;', + 'monospace_font': 'font-family: monospace; font-size: 13px; font-size-adjust: 0.5;', + 'sans_serif_font': 'font-family: Helvetica, Arial, sans-serif;', + 'subtle_border_and_shadow': 'background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2);' +} %} + +{# when updating any of these colors, do the same in toolbar.css.twig #} +{% set colors = { 'success': '#4F805D', 'warning': '#A46A1F', 'error': '#B0413E' } %} + +{# Normalization + (normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css) + ========================================================================= #} +html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0} + +{# Basic styles + ========================================================================= #} +body { + background-color: #F9F9F9; + color: #222; + {{ mixins.sans_serif_font|raw }} + font-size: 14px; + line-height: 1.4; +} + +h2, h3, h4 { + font-weight: 500; + margin: 1.5em 0 .5em; +} +h2 + h3, +h3 + h4 { + margin-top: 1em; +} +h2 { + font-size: 24px; +} +h3 { + font-size: 21px; +} +h4 { + font-size: 18px; +} +h2 span, h3 span, h4 span, +h2 small, h3 small, h4 small { + color: #999; +} + +li { + margin-bottom: 10px; +} + +p { + font-size: 16px; + margin-bottom: 1em; +} + +a { + color: #218BC3; + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +a.link-inverse { + text-decoration: underline; +} +a.link-inverse:hover { + text-decoration: none; +} +a:active, +a:hover { + outline: 0; +} +h2 a, +h3 a, +h4 a { + text-decoration: underline; +} +h2 a:hover, +h3 a:hover, +h4 a:hover { + text-decoration: none; +} + +abbr { + border-bottom: 1px dotted #444; + cursor: help; +} + +code, pre { + {{ mixins.monospace_font|raw }} +} + +{# Buttons + ------------------------------------------------------------------------- #} +button { + {{ mixins.sans_serif_font|raw }} +} +.btn { + background: #777; + border-radius: 2px; + border: 0; + color: #F5F5F5; + display: inline-block; + padding: .5em .75em; +} +.btn:hover { + cursor: pointer; + opacity: 0.8; + text-decoration: none; +} +.btn-sm { + font-size: 12px; +} +.btn-sm svg { + height: 16px; + vertical-align: middle; +} +.btn-link { + border-color: transparent; + color: #218BC3; + text-decoration: none; + background-color: transparent; + outline: none; + border: 0; + padding: 0; +} +.btn-link:hover { + text-decoration: underline; +} +{# Tables + ------------------------------------------------------------------------- #} +table, tr, th, td { + background: #FFF; + border-collapse: collapse; + line-height: 1.5; + vertical-align: top; +} +table { + {{ mixins.subtle_border_and_shadow|raw }}; + margin: 1em 0; + width: 100%; +} + +table th, table td { + padding: 8px 10px; +} + +table th { + font-weight: bold; + text-align: left; +} +table thead th { + background-color: #E0E0E0; +} +table thead th.key { + width: 19%; +} + +table tbody th, +table tbody td { + {{ mixins.monospace_font|raw }} + border: 1px solid #E0E0E0; + border-width: 1px 0; +} + +table tbody td { + {{ mixins.break_long_words|raw }} +} + +table tbody div { + margin: .25em 0; +} +table tbody ul { + margin: 0; + padding: 0 0 0 1em; +} + +{# Utility classes + ========================================================================= #} +.block { + display: block; +} +.hidden { + display: none; +} +.nowrap { + white-space: pre; +} +.newline { + display: block; +} +.break-long-words { + {{ mixins.break_long_words|raw }} +} +.text-small { + font-size: 12px !important; +} +.text-muted { + color: #999; +} +.text-bold { + font-weight: bold; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.font-normal { + {{ mixins.sans_serif_font|raw }} + font-size: 14px; +} +.help { + color: #999; + font-size: 14px; + margin-bottom: .5em; +} +.empty { + border: 4px dashed #E0E0E0; + color: #999; + margin: 1em 0; + padding: .5em 2em; +} + +.label { + background-color: #666; + color: #FAFAFA; + display: inline-block; + font-size: 12px; + font-weight: bold; + padding: 3px 7px; + white-space: nowrap; +} +.label.status-success { background: {{ colors.success|raw }}; color: #FFF; } +.label.status-warning { background: {{ colors.warning|raw }}; color: #FFF; } +.label.status-error { background: {{ colors.error|raw }}; color: #FFF; } + +{# Metrics + ------------------------------------------------------------------------- #} +.metrics { + margin: 1em 0; + overflow: auto; +} +.metrics .metric { + float: left; + margin-right: 1em; +} + +.metric { + {{ mixins.subtle_border_and_shadow|raw }}; + min-width: 100px; + min-height: 70px; +} +.metric .value { + display: block; + font-size: 28px; + padding: 8px 15px 4px; + text-align: center; +} +.metric .value svg { + margin: 5px 0 -5px; +} +.metric .unit { + color: #999; + font-size: 18px; + margin-left: -4px; +} +.metric .label { + background: #E0E0E0; + color: #222; + display: block; + font-size: 12px; + padding: 5px; + text-align: center; +} + +.metrics-horizontal .metric { + min-height: 0; + min-width: 0; +} +.metrics-horizontal .metric .value, +.metrics-horizontal .metric .label { + display: inline; + padding: 2px 6px; +} +.metrics-horizontal .metric .label { + display: inline-block; + padding: 6px; +} +.metrics-horizontal .metric .value { + font-size: 16px; +} +.metrics-horizontal .metric .value svg { + max-height: 14px; + line-height: 10px; + margin: 0; + padding-left: 4px; + vertical-align: middle; +} + +{# Cards + ------------------------------------------------------------------------- #} +.card { + {{ mixins.subtle_border_and_shadow|raw }}; + margin: 1em 0; + padding: 10px; +} +.card-block + .card-block { + border-top: 1px solid #E0E0E0; + padding-top: 10px; +} +.card *:first-child, +.card-block *:first-child { + margin-top: 0; +} +.card .label { + background-color: #EEE; + color: #222; +} + +{# Status + ------------------------------------------------------------------------- #} +.status-success { + background: rgba(94, 151, 110, 0.3); +} +.status-warning { + background: rgba(240, 181, 24, 0.3); +} +.status-error { + background: rgba(176, 65, 62, 0.2); +} +.status-success td, +.status-warning td, +.status-error td { + background: transparent; +} +tr.status-error td, +tr.status-warning td { + border-bottom: 1px solid #FAFAFA; + border-top: 1px solid #FAFAFA; +} + +.status-warning .colored { + color: {{ colors.warning|raw }}; +} +.status-error .colored { + color: {{ colors.error|raw }}; +} + +{# Syntax highlighting + ========================================================================= #} +.highlight pre { + margin: 0; + white-space: pre-wrap; +} + +.highlight .keyword { color: #8959A8; font-weight: bold; } +.highlight .word { color: #222222; } +.highlight .variable { color: #916319; } +.highlight .symbol { color: #222222; } +.highlight .comment { color: #999999; } +.highlight .backtick { color: #718C00; } +.highlight .string { color: #718C00; } +.highlight .number { color: #F5871F; font-weight: bold; } +.highlight .error { color: #C82829; } + +{# Icons + ========================================================================= #} +.sf-icon { + vertical-align: middle; + background-repeat: no-repeat; + background-size: contain; + width: 16px; + height: 16px; + display: inline-block; +} +.sf-icon svg { + width: 16px; + height: 16px; +} +.sf-icon.sf-medium, +.sf-icon.sf-medium svg { + width: 24px; + height: 24px; +} +.sf-icon.sf-large, +.sf-icon.sf-large svg { + width: 32px; + height: 32px; +} + + +{# Layout + ========================================================================= #} +.container { + max-width: 1300px; + padding-right: 15px; +} +#content { + min-height: 1024px; + overflow: hidden; +} +#collector-wrapper { + float: left; + width: 100%; +} +#collector-content { + margin: 0 0 30px 220px; + padding: 14px 0 14px 20px; +} + +#main h2:first-of-type { + margin-top: 0; +} + +{# Header + ========================================================================= #} +#header { + background-color: #222; + overflow: hidden; +} +#header h1 { + color: #FFF; + font-weight: normal; + font-size: 21px; + float: left; + margin: 0; + padding: 10px 10px 8px; +} +#header h1 span { + color: #CCC; +} +#header h1 svg { + height: 40px; + margin-top: -4px; + vertical-align: middle; +} +#header h1 svg path { + fill: #FFF; +} +#header .search { + float: right; + padding-top: 11px; +} +#header .search input { + border: 1px solid #DDD; + margin-right: 4px; + padding: 7px 8px; + width: 200px; +} + +{# Summary + ========================================================================= #} +#summary .status { + background: #E0E0E0; + border: solid rgba(0, 0, 0, 0.1); + border-width: 2px 0; + padding: 10px; +} +#summary h2, +#summary h2 a { + color: #222; + font-size: 21px; + margin: 0; + text-decoration: none; +} +#summary h2 a:hover { + text-decoration: underline; +} + +#summary .status-success { background: {{ colors.success|raw }}; } +#summary .status-warning { background: {{ colors.warning|raw }}; } +#summary .status-error { background: {{ colors.error|raw }}; } + +#summary .status-success h2, +#summary .status-success h2 a, +#summary .status-warning h2, +#summary .status-warning h2 a, +#summary .status-error h2, +#summary .status-error h2 a { + color: #FFF; +} + +#summary dl.metadata { + margin: 5px 0 0; + color: rgba(255, 255, 255, 0.75); +} +#summary dl.metadata dt, +#summary dl.metadata dd { + display: inline-block; + font-size: 13px; +} +#summary dl.metadata dt { + font-weight: bold; +} +#summary dl.metadata dt:after { + content: ':'; +} +#summary dl.metadata dd { + margin: 0 1.5em 0 0; +} + +{# Sidebar + ========================================================================= #} +#sidebar { + background: #444; + color: #CCC; + float: left; + margin-bottom: -99999px; {# needed for 'same-height columns' trick #} + margin-left: -100%; + padding-bottom: 99999px; {# needed for 'same-height columns' trick #} + width: 220px; +} +#sidebar .module { + padding: 10px; + width: 220px; +} + +{# Sidebar Shortcuts + ------------------------------------------------------------------------- #} +#sidebar #sidebar-shortcuts { + background: #333; + width: 220px; +} +#sidebar #sidebar-shortcuts .shortcuts { + position: relative; + padding: 16px 10px; +} +#sidebar-shortcuts .icon { + display: block; + float: left; + width: 50px; + margin: 2px 0 0 -10px; + text-align: center; +} +#sidebar #sidebar-shortcuts .btn { + color: #F5F5F5; +} +#sidebar #sidebar-shortcuts .btn + .btn { + margin-left: 5px; +} +#sidebar #sidebar-shortcuts .btn { + padding: .5em .6em; +} + +{# Sidebar Search + ------------------------------------------------------------------------- #} +#sidebar-search .form-group:first-of-type { + padding-top: 20px; +} +#sidebar-search .form-group { + clear: both; + overflow: hidden; + padding-bottom: 10px; +} +#sidebar-search .form-group label { + float: left; + font-size: 13px; + line-height: 24px; + width: 60px; +} +#sidebar-search .form-group input, +#sidebar-search .form-group select { + float: left; + font-size: 13px; + padding: 3px 6px; +} +#sidebar-search .form-group input { + background: #CCC; + border: 1px solid #999; + color: #222; + width: 120px; +} +#sidebar-search .form-group select { + color: #222; +} +#sidebar-search .form-group .btn { + float: right; + margin-right: 10px; +} + +{# Sidebar Menu + ------------------------------------------------------------------------- #} +#menu-profiler { + margin: 0; + padding: 0; +} +#menu-profiler li { + position: relative; + margin-bottom: 0; + width: 220px; +} +#menu-profiler li a { + border: solid transparent; + border-width: 2px 0; + color: #CCC; + display: block; +} +#menu-profiler li a:hover { + text-decoration: none; +} +#menu-profiler li a .label { + background: transparent; + color: #EEE; + display: block; + padding: 8px 10px 8px 50px; + overflow: hidden; + white-space: nowrap; +} +#menu-profiler li a .label .icon { + display: block; + position: absolute; + left: 0; + top: 8px; + width: 50px; + text-align: center; +} +#menu-profiler li a .label .icon svg path { + fill: #DDD; +} +#menu-profiler li a .label strong { + font-size: 16px; + font-weight: normal; +} +#menu-profiler li a .label.disabled { + opacity: .25; +} +#menu-profiler li a:hover .label.disabled, +#menu-profiler li.selected a .label.disabled { + opacity: 1; +} + +#menu-profiler li.selected a, +#menu-profiler:hover li.selected a:hover, +#menu-profiler li a:hover { + background: #666; + border: solid #555; + border-width: 2px 0; +} +#menu-profiler li.selected a .label, +#menu-profiler li a:hover .label { + color: #FFF; +} +#menu-profiler li.selected a .icon svg path, +#menu-profiler li a:hover .icon svg path { + fill: #FFF; +} + +#menu-profiler li a .count { + background-color: #666; + color: #FFF; + display: inline-block; + font-weight: bold; + min-width: 10px; + padding: 2px 6px; + position: absolute; + right: 10px; + text-align: center; + vertical-align: baseline; + white-space: nowrap; +} +#menu-profiler li a span.count span { + font-size: 12px; + +} +#menu-profiler li a span.count span + span::before { + content: " / "; + color: #AAA; +} + +#menu-profiler .label-status-warning .count { + background: {{ colors.warning|raw }}; +} +#menu-profiler .label-status-error .count { + background: {{ colors.error|raw }}; +} + +{# Timeline panel + ========================================================================= #} +#timeline-control { + background: #FFF; + margin: 1em 0; + padding: 10px; +} +#timeline-control label { + font-weight: bold; + margin-right: 1em; +} +#timeline-control input { + font-size: 16px; + padding: 4px; + text-align: right; + width: 40px; +} +#timeline-control .help { + margin-left: 1em; +} + +.sf-profiler-timeline .legends { + font-size: 12px; + line-height: 1.5em; +} +.sf-profiler-timeline .legends span { + border-left: solid 14px; + padding: 0 10px 0 5px; +} +.sf-profiler-timeline canvas { + border: 1px solid #DDD; + background: #FFF; + margin: .5em 0; +} +.sf-profiler-timeline + p.help { + margin-top: 0; +} + +{# Tabbed navigation + ========================================================================= #} +.tab-navigation { + margin: 0 0 1em 0; + padding: 0; +} +.tab-navigation li { + background: #FFF; + border: 1px solid #DDD; + color: #444; + cursor: pointer; + display: inline-block; + font-size: 16px; + margin: 0 0 0 -1px; + padding: .5em .75em; + z-index: 1; +} +.tab-navigation li:hover { + background: #EEE; +} +.tab-navigation li .badge { + background-color: #F5F5F5; + color: #777; + display: inline-block; + font-size: 14px; + font-weight: bold; + margin-left: 8px; + min-width: 10px; + padding: 1px 6px; + text-align: center; + white-space: nowrap; +} +.tab-navigation li:hover .badge { + background: #FAFAFA; + color: #777; +} +.tab-navigation li.disabled { + background: #F5F5F5; + color: #999; +} +.tab-navigation li.active { + background: #666; + border-color: #666; + color: #FAFAFA; + z-index: 1100; +} +.tab-navigation li.active .badge { + background-color: #444; + color: #FFF; +} +.tab-content > *:first-child { + margin-top: 0; +} + +{# Toggles + ========================================================================= #} +.sf-toggle-content { + -moz-transition: display .25s ease; + -webkit-transition: display .25s ease; + transition: display .25s ease; +} +.sf-toggle-content.sf-toggle-hidden { + display: none; +} +.sf-toggle-content.sf-toggle-visible { + display: block; +} + +{# Twig panel + ========================================================================= #} +#twig-dump pre { + font-size: 12px; + line-height: 1.7; +} +#twig-dump span { + border-radius: 2px; + padding: 1px 2px; +} +#twig-dump .status-error { background: transparent; color: #B0413E; } +#twig-dump .status-warning { background: rgba(240, 181, 24, 0.3); } +#twig-dump .status-success { background: rgba(100, 189, 99, 0.2); } + +{# Logger panel + ========================================================================= #} +table.logs .metadata { + color: #777; + display: block; + font-size: 12px; + padding-top: 4px; +} +table.logs .metadata strong { + color: #222; +} + +table.logs .sf-call-stack { + margin: 1em 0 1em 1.5em; +} +table.logs .sf-call-stack li { + margin-bottom: 5px; +} +table.logs .sf-call-stack abbr { + border: none; +} + +{# Doctrine panel + ========================================================================= #} +.sql-runnable { + background: #F5F5F5; + margin: .5em 0; + padding: 1em; +} +.queries-table pre { + {{ mixins.break_long_words|raw }} + margin: 0; + white-space: pre-wrap; +} + +{# Dump panel + ========================================================================= #} +#collector-content .sf-dump { + margin-bottom: 2em; +} +#collector-content pre.sf-dump, +#collector-content .sf-dump code, +#collector-content .sf-dump samp { + {{ mixins.monospace_font|raw }} +} +#collector-content pre.sf-dump { + background: #222; + line-height: 1.4; + margin-top: .5em; + padding: 1em; +} +#collector-content .sf-dump h3 { + font-size: 18px; + margin: .5em 0 0; +} +#collector-content .sf-dump h3 a { + cursor: pointer; +} + +#collector-content pre.sf-dump { color: #CC7832; } +#collector-content .sf-dump-str { color: #629755; } +#collector-content .sf-dump-private, +#collector-content .sf-dump-protected, +#collector-content .sf-dump-public { color: #E0E0E0; } +#collector-content .sf-dump-note { color: #6897BB; } +#collector-content .sf-dump-key { color: #A5C261; } + +#collector-content .sf-dump .trace { + border: 1px solid #DDD; + background: #FFF; + padding: 10px; + margin: 1em 0; +} +#collector-content .sf-dump .trace { + font-size: 12px; +} +#collector-content .sf-dump .trace code { + font-size: 14px; +} +#collector-content .sf-dump .trace li { + margin-bottom: 0; + padding: 5px 0; +} +#collector-content .sf-dump .trace li.selected { + background: rgba(255, 255, 153, 0.5); +} + +{# Search Results page + ========================================================================= #} +#search-results td { + {{ mixins.sans_serif_font|raw }} + vertical-align: middle; +} + +#search-results .sf-search { + visibility: hidden; + margin-left: 2px; +} +#search-results tr:hover .sf-search { + visibility: visible; +} + +{# Small screens + ========================================================================= #} + +.visible-small { + display: none; +} +.hidden-small { + display: inherit; +} + +@media (max-width: 768px) { + #collector-content { + margin-left: 50px; + } + + #sidebar { + width: 50px; + overflow-y: hidden; + transition: width 200ms ease-out; + } + #sidebar:hover, #sidebar.expanded { + width: 220px; + } + + #sidebar-search { + display: none; + } + #sidebar:hover #sidebar-search.sf-toggle-visible, #sidebar.expanded #sidebar-search.sf-toggle-visible { + display: block; + } + + #sidebar .module { + display: none; + } + #sidebar:hover .module, #sidebar.expanded .module { + display: block; + } + + .visible-small { + display: inherit; + } + .hidden-small { + display: none; + } + + .btn-sm svg { + margin-left: 2px; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/results.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/results.html.twig new file mode 100644 index 0000000..cd9fd06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/results.html.twig @@ -0,0 +1,73 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block summary %} +
    +
    +

    Profile Search

    +
    +
    +{% endblock %} + +{% block panel %} +

    {{ tokens ? tokens|length : 'No' }} results found

    + + {% if tokens %} + + + + + + + + + + + + + {% for result in tokens %} + {% set css_class = result.status_code|default(0) > 399 ? 'status-error' : result.status_code|default(0) > 299 ? 'status-warning' : 'status-success' %} + + + + + + + + + + {% endfor %} + +
    StatusIPMethodURLTimeToken
    + {{ result.status_code|default('n/a') }} + + {{ result.ip }} + {% if request.session is not null %} + + {{ include('@WebProfiler/Icon/search.svg') }} + + {% endif %} + + {{ result.method }} + {% if request.session is not null %} + + {{ include('@WebProfiler/Icon/search.svg') }} + + {% endif %} + + {{ result.url }} + {% if request.session is not null %} + + {{ include('@WebProfiler/Icon/search.svg') }} + + {% endif %} + + {{ result.time|date('d-M-Y') }} + {{ result.time|date('H:i:s') }} + {{ result.token }}
    + {% else %} +
    +

    The query returned no result.

    +
    + {% endif %} + +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig new file mode 100644 index 0000000..3661c8a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/search.html.twig @@ -0,0 +1,51 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/table.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/table.html.twig new file mode 100644 index 0000000..cb9986b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/table.html.twig @@ -0,0 +1,16 @@ + + + + + + + + + {% for key in data|keys|sort %} + + + + + {% endfor %} + +
    {{ labels is defined ? labels[0] : 'Key' }}{{ labels is defined ? labels[1] : 'Value' }}
    {{ key }}{{ profiler_dump(data[key]) }}
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig new file mode 100644 index 0000000..4ae44c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -0,0 +1,483 @@ +{# when updating any of these colors, do the same in profiler.css.twig #} +{% set colors = { 'success': '#4F805D', 'warning': '#A46A1F', 'error': '#B0413E' } %} + +.sf-minitoolbar { + background-color: #222; + border-top-left-radius: 4px; + bottom: 0; + display: none; + height: 30px; + padding: 6px 6px 0; + position: fixed; + right: 0; + z-index: 99999; +} + +.sf-minitoolbar a { + display: block; +} +.sf-minitoolbar svg, +.sf-minitoolbar img { + max-height: 24px; + max-width: 24px; +} + +.sf-toolbarreset * { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + vertical-align: baseline; +} + +.sf-toolbarreset { + background-color: #222; + bottom: 0; + box-shadow: 0 -1px 0px rgba(0, 0, 0, 0.2); + color: #EEE; + font: 11px Arial, sans-serif; + left: 0; + margin: 0; + padding: 0 36px 0 0; + position: fixed; + right: 0; + text-align: left; + z-index: 99999; +} +.sf-toolbarreset abbr { + border: dashed #777; + border-width: 0 0 1px; +} +.sf-toolbarreset svg, +.sf-toolbarreset img { + max-height: 20px; + max-width: 20px; +} + +.sf-toolbarreset .hide-button { + background: #444; + cursor: pointer; + display: block; + position: absolute; + top: 0; + right: 0; + width: 36px; + height: 36px; + cursor: pointer; + text-align: center; +} +.sf-toolbarreset .hide-button svg { + max-height: 18px; + margin-top: 10px; +} + +.sf-toolbar-block { + cursor: default; + display: block; + float: left; + height: 36px; + margin-right: 0; + white-space: nowrap; +} +.sf-toolbar-block > a, +.sf-toolbar-block > a:hover { + display: block; + text-decoration: none; +} + +.sf-toolbar-block span { + display: inline-block; +} +.sf-toolbar-block .sf-toolbar-value { + color: #F5F5F5; + font-size: 13px; + line-height: 36px; + padding: 0; +} +.sf-toolbar-block .sf-toolbar-label, +.sf-toolbar-block .sf-toolbar-class-separator { + color: #AAA; + font-size: 12px; +} + +.sf-toolbar-block .sf-toolbar-info { + border-collapse: collapse; + display: table; + z-index: 100000; +} +.sf-toolbar-block hr { + border-top: 1px solid #777; + margin: 4px 0; + padding-top: 4px; +} +.sf-toolbar-block .sf-toolbar-info-piece { + /* this 'border-bottom' trick is needed because 'margin-bottom' doesn't work for table rows */ + border-bottom: solid transparent 3px; + display: table-row; +} +.sf-toolbar-block .sf-toolbar-info-piece-additional, +.sf-toolbar-block .sf-toolbar-info-piece-additional-detail { + display: none; +} +.sf-toolbar-block .sf-toolbar-info-group { + margin-bottom: 4px; + padding-bottom: 2px; + border-bottom: 1px solid #333333; +} +.sf-toolbar-block .sf-toolbar-info-group:last-child { + margin-bottom: 0; + padding-bottom: 0; + border-bottom: none; +} + +.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status { + padding: 2px 5px; + margin-bottom: 0px; +} +.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status + .sf-toolbar-status { + margin-left: 4px; +} + +.sf-toolbar-block .sf-toolbar-info-piece:last-child { + margin-bottom: 0; +} + +.sf-toolbar-block .sf-toolbar-info-piece a { + color: #99CDD8; + text-decoration: underline; +} +.sf-toolbar-block .sf-toolbar-info-piece a:hover { + text-decoration: none; +} + +.sf-toolbar-block .sf-toolbar-info-piece b { + color: #AAA; + display: table-cell; + font-size: 11px; + padding: 4px 8px 4px 0; +} +.sf-toolbar-block .sf-toolbar-info-piece span { + +} +.sf-toolbar-block .sf-toolbar-info-piece span { + color: #F5F5F5; + font-size: 12px; +} + +.sf-toolbar-block .sf-toolbar-info { + background-color: #444; + bottom: 36px; + color: #F5F5F5; + display: none; + padding: 9px 0; + position: absolute; +} + +.sf-toolbar-block .sf-toolbar-info:empty { + visibility: hidden; +} + +.sf-toolbar-block .sf-toolbar-status { + display: inline-block; + color: #FFF; + background-color: #666; + padding: 3px 6px; + margin-bottom: 2px; + vertical-align: middle; + min-width: 15px; + min-height: 13px; + text-align: center; +} + +.sf-toolbar-block .sf-toolbar-status-green { + background-color: {{ colors.success|raw }}; +} +.sf-toolbar-block .sf-toolbar-status-red { + background-color: {{ colors.error|raw }}; +} +.sf-toolbar-block .sf-toolbar-status-yellow { + background-color: {{ colors.warning|raw }}; +} + +.sf-toolbar-block.sf-toolbar-status-green { + background-color: {{ colors.success|raw }}; + color: #FFF; +} +.sf-toolbar-block.sf-toolbar-status-red { + background-color: {{ colors.error|raw }}; + color: #FFF; +} +.sf-toolbar-block.sf-toolbar-status-yellow { + background-color: {{ colors.warning|raw }}; + color: #FFF; +} + +.sf-toolbar-block-request .sf-toolbar-status { + color: #FFF; + display: inline-block; + font-size: 14px; + height: 36px; + line-height: 36px; + padding: 0 10px; +} +.sf-toolbar-block-request .sf-toolbar-info-piece a { + text-decoration: none; +} +.sf-toolbar-block-request .sf-toolbar-info-piece a:hover { + text-decoration: underline; +} + +.sf-toolbar-status-green .sf-toolbar-label, +.sf-toolbar-status-yellow .sf-toolbar-label, +.sf-toolbar-status-red .sf-toolbar-label { + color: #FFF; +} +.sf-toolbar-status-green svg path, +.sf-toolbar-status-red svg path, +.sf-toolbar-status-yellow svg path { + fill: #FFF; +} +.sf-toolbar-block-config svg path { + fill: #FFF; +} + +.sf-toolbar-block .sf-toolbar-icon { + display: block; + height: 36px; + padding: 0 7px; +} +.sf-toolbar-block-request .sf-toolbar-icon { + padding-left: 0; + padding-right: 0; +} + +.sf-toolbar-block .sf-toolbar-icon img, +.sf-toolbar-block .sf-toolbar-icon svg { + border-width: 0; + position: relative; + top: 8px; +} + +.sf-toolbar-block .sf-toolbar-icon img + span, +.sf-toolbar-block .sf-toolbar-icon svg + span { + margin-left: 4px; +} +.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-value { + margin-left: 4px; +} + +.sf-toolbar-block:hover { + position: relative; +} +.sf-toolbar-block:hover .sf-toolbar-icon { + background-color: #444; + position: relative; + z-index: 10002; +} +.sf-toolbar-block:hover .sf-toolbar-info { + display: block; + padding: 10px; + max-width: 480px; + max-height: 480px; + word-wrap: break-word; + overflow: hidden; + overflow-y: auto; +} +.sf-toolbar-info-piece b.sf-toolbar-ajax-info { + color: #F5F5F5; +} +.sf-toolbar-ajax-requests { + width: 100%; +} +.sf-toolbar-ajax-requests td { + background-color: #444; + border-bottom: 1px solid #777; + color: #F5F5F5; + font-size: 12px; + padding: 4px; +} +.sf-toolbar-ajax-requests tr:last-child td { + border-bottom: 0; +} +.sf-toolbar-ajax-requests th { + background-color: #222; + border-bottom: 0; + color: #AAA; + font-size: 11px; + padding: 4px; +} +.sf-ajax-request-url { + max-width: 300px; + line-height: 9px; + overflow: hidden; + text-overflow: ellipsis; +} +.sf-toolbar-ajax-requests .sf-ajax-request-url a { + text-decoration: none; +} +.sf-toolbar-ajax-requests .sf-ajax-request-url a:hover { + text-decoration: underline; +} +.sf-ajax-request-duration { + text-align: right; +} +.sf-ajax-request-loading { + -webkit-animation: sf-blink .5s ease-in-out infinite; + -o-animation: sf-blink .5s ease-in-out infinite; + -moz-animation: sf-blink .5s ease-in-out infinite; + animation: sf-blink .5s ease-in-out infinite; +} +@-webkit-keyframes sf-blink { + 0% { background: #222; } + 50% { background: #444; } + 100% { background: #222; } +} +@-moz-keyframes sf-blink { + 0% { background: #222; } + 50% { background: #444; } + 100% { background: #222; } +} +@keyframes sf-blink { + 0% { background: #222; } + 50% { background: #444; } + 100% { background: #222; } +} + +.sf-toolbar-block-dump pre.sf-dump { + background-color: #222; + border-color: #777; + border-radius: 0; + margin: 6px 0 12px 0; + width: 200px; +} +.sf-toolbar-block-dump pre.sf-dump:last-child { + margin-bottom: 0; +} +.sf-toolbar-block-dump .sf-toolbar-info-piece .sf-toolbar-file-line { + color: #AAA; + margin-left: 4px; +} +.sf-toolbar-block-dump .sf-toolbar-info img { + display: none; +} + +/* Override the setting when the toolbar is on the top */ +{% if position == 'top' %} + .sf-minitoolbar { + border-bottom-left-radius: 4px; + border-top-left-radius: 0; + bottom: auto; + right: 0; + top: 0; + } + + .sf-toolbarreset { + bottom: auto; + box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2); + top: 0; + } + + .sf-toolbar-block .sf-toolbar-info { + bottom: auto; + top: 36px; + } +{% endif %} + +{% if not floatable %} + .sf-toolbarreset { + position: static; + } +{% endif %} + +/* Responsive Design */ +.sf-toolbar-icon .sf-toolbar-label, +.sf-toolbar-icon .sf-toolbar-value { + display: none; +} +.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label { + display: inline-block; +} + +/* Legacy Design - these styles are maintained to make old panels look + a bit better on the new toolbar */ +.sf-toolbar-block .sf-toolbar-info-piece-additional-detail { + color: #AAA; + font-size: 12px; +} +.sf-toolbar-status-green .sf-toolbar-info-piece-additional-detail, +.sf-toolbar-status-yellow .sf-toolbar-info-piece-additional-detail, +.sf-toolbar-status-red .sf-toolbar-info-piece-additional-detail { + color: #FFF; +} + +@media (min-width: 768px) { + + .sf-toolbar-icon .sf-toolbar-label, + .sf-toolbar-icon .sf-toolbar-value { + display: inline; + } + + .sf-toolbar-block .sf-toolbar-icon img, + .sf-toolbar-block .sf-toolbar-icon svg { + top: 6px; + } + .sf-toolbar-block-config:hover .sf-toolbar-info { + right: 0; + } + .sf-toolbar-block-time .sf-toolbar-icon svg, + .sf-toolbar-block-memory .sf-toolbar-icon svg { + display: none; + } + .sf-toolbar-block-time .sf-toolbar-icon svg + span, + .sf-toolbar-block-memory .sf-toolbar-icon svg + span { + margin-left: 0; + } + + .sf-toolbar-block .sf-toolbar-icon { + padding: 0 10px; + } + .sf-toolbar-block-time .sf-toolbar-icon { + padding-right: 5px; + } + .sf-toolbar-block-memory .sf-toolbar-icon { + padding-left: 5px; + } + .sf-toolbar-block-request .sf-toolbar-icon { + padding-left: 0; + padding-right: 0; + } + .sf-toolbar-block-request .sf-toolbar-status + .sf-toolbar-label { + margin-left: 4px; + } + .sf-toolbar-block-request .sf-toolbar-label + .sf-toolbar-value { + margin-right: 10px; + } + + .sf-toolbar-block-request:hover .sf-toolbar-info { + max-width: none; + } + + .sf-toolbar-block .sf-toolbar-info-piece b { + font-size: 12px; + } + .sf-toolbar-block .sf-toolbar-info-piece span { + font-size: 13px; + } + + .sf-toolbar-block-right { + float: right; + margin-left: 0; + margin-right: 0; + } +} + +@media (min-width: 1024px) { + .sf-toolbar-block .sf-toolbar-info-piece-additional, + .sf-toolbar-block .sf-toolbar-info-piece-additional-detail { + display: inline-block; + } + + .sf-toolbar-block .sf-toolbar-info-piece-additional:empty, + .sf-toolbar-block .sf-toolbar-info-piece-additional-detail:empty { + display: none; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.html.twig new file mode 100644 index 0000000..825631b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.html.twig @@ -0,0 +1,51 @@ + +{% if 'normal' != position %} + + +
    +{% endif %} + +
    + {% for name, template in templates %} + {{ template.renderblock('toolbar', { + 'collector': profile.getcollector(name), + 'profiler_url': profiler_url, + 'token': profile.token, + 'name': name, + 'profiler_markup_version': profiler_markup_version + }) + }} + {% endfor %} + + {% if 'normal' != position %} + + {{ include('@WebProfiler/Icon/close.svg') }} + + {% endif %} +
    + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig new file mode 100644 index 0000000..7424f1f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_item.html.twig @@ -0,0 +1,6 @@ +
    + {% if link %}{% endif %} +
    {{ icon|default('') }}
    + {% if link %}
    {% endif %} +
    {{ text|default('') }}
    +
    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig new file mode 100644 index 0000000..a82a59e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig @@ -0,0 +1,70 @@ + +{{ include('@WebProfiler/Profiler/base_js.html.twig') }} + diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig new file mode 100644 index 0000000..35b6e90 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_redirect.html.twig @@ -0,0 +1,18 @@ +{% extends '@Twig/layout.html.twig' %} + +{% block title 'Redirection Intercepted' %} + +{% block body %} +
    +
    +

    This request redirects to {{ location }}.

    + +

    + + The redirect was intercepted by the web debug toolbar to help debugging. + For more information, see the "intercept-redirects" option of the Profiler. + +

    +
    +
    +{% endblock %} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Router/panel.html.twig b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Router/panel.html.twig new file mode 100644 index 0000000..8a6929c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Router/panel.html.twig @@ -0,0 +1,78 @@ +

    Routing

    + +
    +
    + {{ request.route ?: '(none)' }} + Matched route +
    + + {% if request.route %} +
    + {{ traces|length }} + Tested routes before match +
    + {% endif %} +
    + +{% if request.route %} +

    Route Parameters

    + + {% if request.routeParams is empty %} +
    +

    No parameters.

    +
    + {% else %} + {{ include('@WebProfiler/Profiler/table.html.twig', { data: request.routeParams, labels: ['Name', 'Value'] }, with_context = false) }} + {% endif %} +{% endif %} + +{% if router.redirect %} +

    Route Redirection

    + +

    This page redirects to:

    +
    + {{ router.targetUrl }} + {% if router.targetRoute %}(route: "{{ router.targetRoute }}"){% endif %} +
    +{% endif %} + +

    Route Matching Logs

    + +
    + Path to match: {{ request.pathinfo }} +
    + + + + + + + + + + + + {% for trace in traces %} + + + + + + + {% endfor %} + +
    #Route namePathLog
    {{ loop.index }}{{ trace.name }}{{ trace.path }} + {% if trace.level == 1 %} + Path almost matches, but + {{ trace.log }} + {% elseif trace.level == 2 %} + {{ trace.log }} + {% else %} + Path does not match + {% endif %} +
    + +

    + Note: These matching logs are based on the current router configuration, + which might differ from the configuration used when profiling this request. +

    diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php new file mode 100644 index 0000000..f8cec7a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\Controller; + +use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController; +use Symfony\Component\HttpKernel\Profiler\Profile; +use Symfony\Component\HttpFoundation\Request; + +class ProfilerControllerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getEmptyTokenCases + */ + public function testEmptyToken($token) + { + $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'); + $twig = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); + $profiler = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array()); + + $response = $controller->toolbarAction(Request::create('/_wdt/empty'), $token); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function getEmptyTokenCases() + { + return array( + array(null), + // "empty" is also a valid empty token case, see https://github.com/symfony/symfony/issues/10806 + array('empty'), + ); + } + + public function testReturns404onTokenNotFound() + { + $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'); + $twig = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); + $profiler = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array()); + + $profiler + ->expects($this->exactly(2)) + ->method('loadProfile') + ->will($this->returnCallback(function ($token) { + if ('found' == $token) { + return new Profile($token); + } + + return; + })) + ; + + $response = $controller->toolbarAction(Request::create('/_wdt/found'), 'found'); + $this->assertEquals(200, $response->getStatusCode()); + + $response = $controller->toolbarAction(Request::create('/_wdt/notFound'), 'notFound'); + $this->assertEquals(404, $response->getStatusCode()); + } + + public function testSearchResult() + { + $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'); + $twig = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); + $profiler = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $controller = new ProfilerController($urlGenerator, $profiler, $twig, array()); + + $tokens = array( + array( + 'token' => 'token1', + 'ip' => '127.0.0.1', + 'method' => 'GET', + 'url' => 'http://example.com/', + 'time' => 0, + 'parent' => null, + 'status_code' => 200, + ), + array( + 'token' => 'token2', + 'ip' => '127.0.0.1', + 'method' => 'GET', + 'url' => 'http://example.com/not_found', + 'time' => 0, + 'parent' => null, + 'status_code' => 404, + ), + ); + $profiler + ->expects($this->once()) + ->method('find') + ->will($this->returnValue($tokens)); + + $request = Request::create('/_profiler/empty/search/results', 'GET', array( + 'limit' => 2, + 'ip' => '127.0.0.1', + 'method' => 'GET', + 'url' => 'http://example.com/', + )); + + $twig->expects($this->once()) + ->method('render') + ->with($this->stringEndsWith('results.html.twig'), $this->equalTo(array( + 'token' => 'empty', + 'profile' => null, + 'tokens' => $tokens, + 'ip' => '127.0.0.1', + 'method' => 'GET', + 'url' => 'http://example.com/', + 'start' => null, + 'end' => null, + 'limit' => 2, + 'panel' => null, + 'request' => $request, + ))); + + $response = $controller->searchResultsAction($request, 'empty'); + $this->assertEquals(200, $response->getStatusCode()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..5203b9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\DependencyInjection; + +use Symfony\Bundle\WebProfilerBundle\DependencyInjection\Configuration; +use Symfony\Component\Config\Definition\Processor; + +class ConfigurationTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getDebugModes + */ + public function testConfigTree($options, $results) + { + $processor = new Processor(); + $configuration = new Configuration(); + $config = $processor->processConfiguration($configuration, array($options)); + + $this->assertEquals($results, $config); + } + + public function getDebugModes() + { + return array( + array(array(), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('intercept_redirects' => true), array('intercept_redirects' => true, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('intercept_redirects' => false), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('toolbar' => true), array('intercept_redirects' => false, 'toolbar' => true, 'position' => 'bottom', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('position' => 'top'), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'top', 'excluded_ajax_paths' => '^/(app(_[\\w]+)?\\.php/)?_wdt')), + array(array('excluded_ajax_paths' => 'test'), array('intercept_redirects' => false, 'toolbar' => false, 'position' => 'bottom', 'excluded_ajax_paths' => 'test')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php new file mode 100644 index 0000000..a3732fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\DependencyInjection; + +use Symfony\Bundle\WebProfilerBundle\Tests\TestCase; +use Symfony\Bundle\WebProfilerBundle\DependencyInjection\WebProfilerExtension; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; + +class WebProfilerExtensionTest extends TestCase +{ + private $kernel; + /** + * @var \Symfony\Component\DependencyInjection\Container + */ + private $container; + + public static function assertSaneContainer(Container $container, $message = '') + { + $errors = array(); + foreach ($container->getServiceIds() as $id) { + try { + $container->get($id); + } catch (\Exception $e) { + $errors[$id] = $e->getMessage(); + } + } + + self::assertEquals(array(), $errors, $message); + } + + protected function setUp() + { + parent::setUp(); + + $this->kernel = $this->getMock('Symfony\\Component\\HttpKernel\\KernelInterface'); + + $this->container = new ContainerBuilder(); + $this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface')); + $this->container->register('twig', 'Twig_Environment'); + $this->container->register('twig_loader', 'Twig_Loader_Array')->addArgument(array()); + $this->container->register('twig', 'Twig_Environment')->addArgument(new Reference('twig_loader')); + $this->container->setParameter('kernel.bundles', array()); + $this->container->setParameter('kernel.cache_dir', __DIR__); + $this->container->setParameter('kernel.debug', false); + $this->container->setParameter('kernel.root_dir', __DIR__); + $this->container->setParameter('profiler.class', array('Symfony\\Component\\HttpKernel\\Profiler\\Profiler')); + $this->container->register('profiler', $this->getMockClass('Symfony\\Component\\HttpKernel\\Profiler\\Profiler')) + ->addArgument(new Definition($this->getMockClass('Symfony\\Component\\HttpKernel\\Profiler\\ProfilerStorageInterface'))); + $this->container->setParameter('data_collector.templates', array()); + $this->container->set('kernel', $this->kernel); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->container = null; + $this->kernel = null; + } + + /** + * @dataProvider getDebugModes + */ + public function testDefaultConfig($debug) + { + $this->container->setParameter('kernel.debug', $debug); + + $extension = new WebProfilerExtension(); + $extension->load(array(array()), $this->container); + + $this->assertFalse($this->container->has('web_profiler.debug_toolbar')); + + $this->assertSaneContainer($this->getDumpedContainer()); + } + + /** + * @dataProvider getDebugModes + */ + public function testToolbarConfig($toolbarEnabled, $interceptRedirects, $listenerInjected, $listenerEnabled) + { + $extension = new WebProfilerExtension(); + $extension->load(array(array('toolbar' => $toolbarEnabled, 'intercept_redirects' => $interceptRedirects)), $this->container); + + $this->assertSame($listenerInjected, $this->container->has('web_profiler.debug_toolbar')); + + if ($listenerInjected) { + $this->assertSame($listenerEnabled, $this->container->get('web_profiler.debug_toolbar')->isEnabled()); + } + + $this->assertSaneContainer($this->getDumpedContainer()); + } + + public function getDebugModes() + { + return array( + array(false, false, false, false), + array(true, false, true, true), + array(false, true, true, false), + array(true, true, true, true), + ); + } + + private function getDumpedContainer() + { + static $i = 0; + $class = 'WebProfilerExtensionTestContainer'.$i++; + + $this->container->compile(); + + $dumper = new PhpDumper($this->container); + eval('?>'.$dumper->dump(array('class' => $class))); + + $container = new $class(); + $container->set('kernel', $this->kernel); + + return $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php new file mode 100644 index 0000000..446aefb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\EventListener; + +use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class WebDebugToolbarListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getInjectToolbarTests + */ + public function testInjectToolbar($content, $expected) + { + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $m = new \ReflectionMethod($listener, 'injectToolbar'); + $m->setAccessible(true); + + $response = new Response($content); + + $m->invoke($listener, $response, Request::create('/')); + $this->assertEquals($expected, $response->getContent()); + } + + public function getInjectToolbarTests() + { + return array( + array('', "\nWDT\n"), + array(' + + + + + ', " + + + + \nWDT\n + "), + ); + } + + /** + * @dataProvider provideRedirects + */ + public function testRedirectionIsIntercepted($statusCode, $hasSession) + { + $response = new Response('Some content', $statusCode); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(false, 'html', $hasSession), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock('Redirection'), true); + $listener->onKernelResponse($event); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('Redirection', $response->getContent()); + } + + public function testToolbarIsInjected() + { + $response = new Response(''); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals("\nWDT\n", $response->getContent()); + } + + /** + * @depends testToolbarIsInjected + * @dataProvider provideRedirects + */ + public function testToolbarIsNotInjectedOnRedirection($statusCode, $hasSession) + { + $response = new Response('', $statusCode); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(false, 'html', $hasSession), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals('', $response->getContent()); + } + + public function provideRedirects() + { + return array( + array(301, true), + array(302, true), + array(301, false), + array(302, false), + ); + } + + /** + * @depends testToolbarIsInjected + */ + public function testToolbarIsNotInjectedWhenThereIsNoNoXDebugTokenResponseHeader() + { + $response = new Response(''); + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals('', $response->getContent()); + } + + /** + * @depends testToolbarIsInjected + */ + public function testToolbarIsNotInjectedWhenOnSubRequest() + { + $response = new Response(''); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::SUB_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals('', $response->getContent()); + } + + /** + * @depends testToolbarIsInjected + */ + public function testToolbarIsNotInjectedOnIncompleteHtmlResponses() + { + $response = new Response('
    Some content
    '); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals('
    Some content
    ', $response->getContent()); + } + + /** + * @depends testToolbarIsInjected + */ + public function testToolbarIsNotInjectedOnXmlHttpRequests() + { + $response = new Response(''); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(true), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals('', $response->getContent()); + } + + /** + * @depends testToolbarIsInjected + */ + public function testToolbarIsNotInjectedOnNonHtmlRequests() + { + $response = new Response(''); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(false, 'json'), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock()); + $listener->onKernelResponse($event); + + $this->assertEquals('', $response->getContent()); + } + + public function testXDebugUrlHeader() + { + $response = new Response(); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $urlGenerator = $this->getUrlGeneratorMock(); + $urlGenerator + ->expects($this->once()) + ->method('generate') + ->with('_profiler', array('token' => 'xxxxxxxx')) + ->will($this->returnValue('/_profiler/xxxxxxxx')) + ; + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); + $listener->onKernelResponse($event); + + $this->assertEquals('/_profiler/xxxxxxxx', $response->headers->get('X-Debug-Token-Link')); + } + + public function testThrowingUrlGenerator() + { + $response = new Response(); + $response->headers->set('X-Debug-Token', 'xxxxxxxx'); + + $urlGenerator = $this->getUrlGeneratorMock(); + $urlGenerator + ->expects($this->once()) + ->method('generate') + ->with('_profiler', array('token' => 'xxxxxxxx')) + ->will($this->throwException(new \Exception('foo'))) + ; + + $event = new FilterResponseEvent($this->getKernelMock(), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response); + + $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, 'bottom', $urlGenerator); + $listener->onKernelResponse($event); + + $this->assertEquals('Exception: foo', $response->headers->get('X-Debug-Error')); + } + + protected function getRequestMock($isXmlHttpRequest = false, $requestFormat = 'html', $hasSession = true) + { + $request = $this->getMock( + 'Symfony\Component\HttpFoundation\Request', + array('getSession', 'isXmlHttpRequest', 'getRequestFormat'), + array(), '', false + ); + $request->expects($this->any()) + ->method('isXmlHttpRequest') + ->will($this->returnValue($isXmlHttpRequest)); + $request->expects($this->any()) + ->method('getRequestFormat') + ->will($this->returnValue($requestFormat)); + + if ($hasSession) { + $session = $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array(), array(), '', false); + $request->expects($this->any()) + ->method('getSession') + ->will($this->returnValue($session)); + } + + return $request; + } + + protected function getTwigMock($render = 'WDT') + { + $templating = $this->getMock('Twig_Environment', array(), array(), '', false); + $templating->expects($this->any()) + ->method('render') + ->will($this->returnValue($render)); + + return $templating; + } + + protected function getUrlGeneratorMock() + { + return $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface'); + } + + protected function getKernelMock() + { + return $this->getMock('Symfony\Component\HttpKernel\Kernel', array(), array(), '', false); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Fixtures/profile.data b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Fixtures/profile.data new file mode 100644 index 0000000..ab76cea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Fixtures/profile.data @@ -0,0 +1 @@ +Tzo0NToiU3ltZm9ueVxDb21wb25lbnRcSHR0cEtlcm5lbFxQcm9maWxlclxQcm9maWxlIjo4OntzOjUyOiIAU3ltZm9ueVxDb21wb25lbnRcSHR0cEtlcm5lbFxQcm9maWxlclxQcm9maWxlAHRva2VuIjtzOjU6IlRPS0VOIjtzOjUzOiIAU3ltZm9ueVxDb21wb25lbnRcSHR0cEtlcm5lbFxQcm9maWxlclxQcm9maWxlAHBhcmVudCI7TjtzOjU1OiIAU3ltZm9ueVxDb21wb25lbnRcSHR0cEtlcm5lbFxQcm9maWxlclxQcm9maWxlAGNoaWxkcmVuIjthOjA6e31zOjU3OiIAU3ltZm9ueVxDb21wb25lbnRcSHR0cEtlcm5lbFxQcm9maWxlclxQcm9maWxlAGNvbGxlY3RvcnMiO2E6MDp7fXM6NDk6IgBTeW1mb255XENvbXBvbmVudFxIdHRwS2VybmVsXFByb2ZpbGVyXFByb2ZpbGUAaXAiO047czo1MzoiAFN5bWZvbnlcQ29tcG9uZW50XEh0dHBLZXJuZWxcUHJvZmlsZXJcUHJvZmlsZQBtZXRob2QiO047czo1MDoiAFN5bWZvbnlcQ29tcG9uZW50XEh0dHBLZXJuZWxcUHJvZmlsZXJcUHJvZmlsZQB1cmwiO047czo1MToiAFN5bWZvbnlcQ29tcG9uZW50XEh0dHBLZXJuZWxcUHJvZmlsZXJcUHJvZmlsZQB0aW1lIjtOO30= \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php new file mode 100644 index 0000000..29238a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/Profiler/TemplateManagerTest.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests\Profiler; + +use Symfony\Bundle\WebProfilerBundle\Tests\TestCase; +use Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager; + +/** + * Test for TemplateManager class. + * + * @author Artur Wielogórski + */ +class TemplateManagerTest extends TestCase +{ + /** + * @var \Twig_Environment + */ + protected $twigEnvironment; + + /** + * @var \Symfony\Component\HttpKernel\Profiler\Profiler + */ + protected $profiler; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $profile; + + /** + * @var \Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager + */ + protected $templateManager; + + protected function setUp() + { + parent::setUp(); + + $profiler = $this->mockProfiler(); + $twigEnvironment = $this->mockTwigEnvironment(); + $templates = array( + 'data_collector.foo' => array('foo', 'FooBundle:Collector:foo'), + 'data_collector.bar' => array('bar', 'FooBundle:Collector:bar'), + 'data_collector.baz' => array('baz', 'FooBundle:Collector:baz'), + ); + + $this->templateManager = new TemplateManager($profiler, $twigEnvironment, $templates); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public function testGetNameOfInvalidTemplate() + { + $profile = $this->mockProfile(); + $this->templateManager->getName($profile, 'notexistingpanel'); + } + + /** + * if template exists in both profile and profiler then its name should be returned. + */ + public function testGetNameValidTemplate() + { + $this->profiler->expects($this->any()) + ->method('has') + ->withAnyParameters() + ->will($this->returnCallback(array($this, 'profilerHasCallback'))); + + $profile = $this->mockProfile(); + $profile->expects($this->any()) + ->method('hasCollector') + ->will($this->returnCallback(array($this, 'profileHasCollectorCallback'))); + + $this->assertEquals('FooBundle:Collector:foo.html.twig', $this->templateManager->getName($profile, 'foo')); + } + + /** + * template should be loaded for 'foo' because other collectors are + * missing in profile or in profiler. + */ + public function testGetTemplates() + { + $profile = $this->mockProfile(); + $profile->expects($this->any()) + ->method('hasCollector') + ->will($this->returnCallback(array($this, 'profilerHasCallback'))); + + $this->profiler->expects($this->any()) + ->method('has') + ->withAnyParameters() + ->will($this->returnCallback(array($this, 'profileHasCollectorCallback'))); + + $result = $this->templateManager->getTemplates($profile); + $this->assertArrayHasKey('foo', $result); + $this->assertArrayNotHasKey('bar', $result); + $this->assertArrayNotHasKey('baz', $result); + } + + public function profilerHasCallback($panel) + { + switch ($panel) { + case 'foo': + case 'bar': + return true; + default: + return false; + } + } + + public function profileHasCollectorCallback($panel) + { + switch ($panel) { + case 'foo': + case 'baz': + return true; + default: + return false; + } + } + + protected function mockProfile() + { + $this->profile = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profile') + ->disableOriginalConstructor() + ->getMock(); + + return $this->profile; + } + + protected function mockTwigEnvironment() + { + $this->twigEnvironment = $this->getMockBuilder('Twig_Environment')->disableOriginalConstructor()->getMock(); + + $this->twigEnvironment->expects($this->any()) + ->method('loadTemplate') + ->will($this->returnValue('loadedTemplate')); + + $this->twigEnvironment->expects($this->any()) + ->method('getLoader') + ->will($this->returnValue($this->getMock('\Twig_LoaderInterface'))); + + return $this->twigEnvironment; + } + + protected function mockProfiler() + { + $this->profiler = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + return $this->profiler; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/TestCase.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/TestCase.php new file mode 100644 index 0000000..0c0efeb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Tests/TestCase.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Tests; + +class TestCase extends \PHPUnit_Framework_TestCase +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php new file mode 100644 index 0000000..13df2f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle\Twig; + +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; + +/** + * Twig extension for the profiler. + * + * @author Fabien Potencier + */ +class WebProfilerExtension extends \Twig_Extension +{ + /** + * @var ValueExporter + */ + private $valueExporter; + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('profiler_dump', array($this, 'dumpValue')), + ); + } + + public function dumpValue($value) + { + if (null === $this->valueExporter) { + $this->valueExporter = new ValueExporter(); + } + + return $this->valueExporter->exportValue($value); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'profiler'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php new file mode 100644 index 0000000..fecc0f3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/WebProfilerBundle.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\WebProfilerBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class WebProfilerBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/composer.json b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/composer.json new file mode 100644 index 0000000..b486cc2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -0,0 +1,42 @@ +{ + "name": "symfony/web-profiler-bundle", + "type": "symfony-bundle", + "description": "Symfony WebProfilerBundle", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/routing": "~2.8|~3.0", + "symfony/twig-bridge": "~2.8|~3.0" + }, + "require-dev": { + "symfony/config": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\WebProfilerBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist new file mode 100644 index 0000000..2bcccd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Asset/CHANGELOG.md new file mode 100644 index 0000000..619a423 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +2.7.0 +----- + + * added the component diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/ContextInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/ContextInterface.php new file mode 100644 index 0000000..8328202 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/ContextInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Context; + +/** + * Holds information about the current request. + * + * @author Fabien Potencier + */ +interface ContextInterface +{ + /** + * Gets the base path. + * + * @return string The base path + */ + public function getBasePath(); + + /** + * Checks whether the request is secure or not. + * + * @return bool true if the request is secure, false otherwise + */ + public function isSecure(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/NullContext.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/NullContext.php new file mode 100644 index 0000000..0743d8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/NullContext.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Context; + +/** + * A context that does nothing. + * + * @author Fabien Potencier + */ +class NullContext implements ContextInterface +{ + /** + * {@inheritdoc} + */ + public function getBasePath() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function isSecure() + { + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/RequestStackContext.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/RequestStackContext.php new file mode 100644 index 0000000..ba71138 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Context/RequestStackContext.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Context; + +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * Uses a RequestStack to populate the context. + * + * @author Fabien Potencier + */ +class RequestStackContext implements ContextInterface +{ + private $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * {@inheritdoc} + */ + public function getBasePath() + { + if (!$request = $this->requestStack->getMasterRequest()) { + return ''; + } + + return $request->getBasePath(); + } + + /** + * {@inheritdoc} + */ + public function isSecure() + { + if (!$request = $this->requestStack->getMasterRequest()) { + return false; + } + + return $request->isSecure(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/ExceptionInterface.php new file mode 100644 index 0000000..cce1b5c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Exception; + +/** + * Base ExceptionInterface for the Asset component. + * + * @author Fabien Potencier + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/InvalidArgumentException.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..0945d8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Exception; + +/** + * Base InvalidArgumentException for the Asset component. + * + * @author Fabien Potencier + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/LogicException.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/LogicException.php new file mode 100644 index 0000000..f291d88 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Exception; + +/** + * Base LogicException for the Asset component. + * + * @author Fabien Potencier + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Asset/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Package.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Package.php new file mode 100644 index 0000000..77b1c93 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Package.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset; + +use Symfony\Component\Asset\Context\ContextInterface; +use Symfony\Component\Asset\Context\NullContext; +use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface; + +/** + * Basic package that adds a version to asset URLs. + * + * @author Kris Wallsmith + * @author Fabien Potencier + */ +class Package implements PackageInterface +{ + private $versionStrategy; + private $context; + + public function __construct(VersionStrategyInterface $versionStrategy, ContextInterface $context = null) + { + $this->versionStrategy = $versionStrategy; + $this->context = $context ?: new NullContext(); + } + + /** + * {@inheritdoc} + */ + public function getVersion($path) + { + return $this->versionStrategy->getVersion($path); + } + + /** + * {@inheritdoc} + */ + public function getUrl($path) + { + if ($this->isAbsoluteUrl($path)) { + return $path; + } + + return $this->versionStrategy->applyVersion($path); + } + + /** + * @return ContextInterface + */ + protected function getContext() + { + return $this->context; + } + + /** + * @return VersionStrategyInterface + */ + protected function getVersionStrategy() + { + return $this->versionStrategy; + } + + protected function isAbsoluteUrl($url) + { + return false !== strpos($url, '://') || '//' === substr($url, 0, 2); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/PackageInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/PackageInterface.php new file mode 100644 index 0000000..b9e9ff9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/PackageInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset; + +/** + * Asset package interface. + * + * @author Kris Wallsmith + */ +interface PackageInterface +{ + /** + * Returns the asset version for an asset. + * + * @param string $path A path + * + * @return string The version string + */ + public function getVersion($path); + + /** + * Returns an absolute or root-relative public path. + * + * @param string $path A path + * + * @return string The public path + */ + public function getUrl($path); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Packages.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Packages.php new file mode 100644 index 0000000..0a8c963 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Packages.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset; + +use Symfony\Component\Asset\Exception\InvalidArgumentException; +use Symfony\Component\Asset\Exception\LogicException; + +/** + * Helps manage asset URLs. + * + * @author Fabien Potencier + * @author Kris Wallsmith + */ +class Packages +{ + private $defaultPackage; + private $packages = array(); + + /** + * @param PackageInterface $defaultPackage The default package + * @param PackageInterface[] $packages Additional packages indexed by name + */ + public function __construct(PackageInterface $defaultPackage = null, array $packages = array()) + { + $this->defaultPackage = $defaultPackage; + + foreach ($packages as $name => $package) { + $this->addPackage($name, $package); + } + } + + /** + * Sets the default package. + * + * @param PackageInterface $defaultPackage The default package + */ + public function setDefaultPackage(PackageInterface $defaultPackage) + { + $this->defaultPackage = $defaultPackage; + } + + /** + * Adds a package. + * + * @param string $name The package name + * @param PackageInterface $package The package + */ + public function addPackage($name, PackageInterface $package) + { + $this->packages[$name] = $package; + } + + /** + * Returns an asset package. + * + * @param string $name The name of the package or null for the default package + * + * @return PackageInterface An asset package + * + * @throws InvalidArgumentException If there is no package by that name + * @throws LogicException If no default package is defined + */ + public function getPackage($name = null) + { + if (null === $name) { + if (null === $this->defaultPackage) { + throw new LogicException('There is no default asset package, configure one first.'); + } + + return $this->defaultPackage; + } + + if (!isset($this->packages[$name])) { + throw new InvalidArgumentException(sprintf('There is no "%s" asset package.', $name)); + } + + return $this->packages[$name]; + } + + /** + * Gets the version to add to public URL. + * + * @param string $path A public path + * @param string $packageName A package name + * + * @return string The current version + */ + public function getVersion($path, $packageName = null) + { + return $this->getPackage($packageName)->getVersion($path); + } + + /** + * Returns the public path. + * + * Absolute paths (i.e. http://...) are returned unmodified. + * + * @param string $path A public path + * @param string $packageName The name of the asset package to use + * + * @return string A public path which takes into account the base path and URL path + */ + public function getUrl($path, $packageName = null) + { + return $this->getPackage($packageName)->getUrl($path); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/PathPackage.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/PathPackage.php new file mode 100644 index 0000000..906879f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/PathPackage.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset; + +use Symfony\Component\Asset\Context\ContextInterface; +use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface; + +/** + * Package that adds a base path to asset URLs in addition to a version. + * + * In addition to the provided base path, this package also automatically + * prepends the current request base path if a Context is available to + * allow a website to be hosted easily under any given path under the Web + * Server root directory. + * + * @author Fabien Potencier + */ +class PathPackage extends Package +{ + private $basePath; + + /** + * @param string $basePath The base path to be prepended to relative paths + * @param VersionStrategyInterface $versionStrategy The version strategy + */ + public function __construct($basePath, VersionStrategyInterface $versionStrategy, ContextInterface $context = null) + { + parent::__construct($versionStrategy, $context); + + if (!$basePath) { + $this->basePath = '/'; + } else { + if ('/' != $basePath[0]) { + $basePath = '/'.$basePath; + } + + $this->basePath = rtrim($basePath, '/').'/'; + } + } + + /** + * {@inheritdoc} + */ + public function getUrl($path) + { + if ($this->isAbsoluteUrl($path)) { + return $path; + } + + return $this->getBasePath().ltrim($this->getVersionStrategy()->applyVersion($path), '/'); + } + + /** + * Returns the base path. + * + * @return string The base path + */ + public function getBasePath() + { + return $this->getContext()->getBasePath().$this->basePath; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/README.md b/vendor/symfony/symfony/src/Symfony/Component/Asset/README.md new file mode 100644 index 0000000..f2e66b8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/README.md @@ -0,0 +1,166 @@ +Asset Component +=============== + +The Asset component manages asset URLs. + +Versioned Asset URLs +-------------------- + +The basic `Package` adds a version to generated asset URLs: + +```php +use Symfony\Component\Asset\Package; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; + +$package = new Package(new StaticVersionStrategy('v1')); + +echo $package->getUrl('/me.png'); +// /me.png?v1 +``` + +The default format can be configured: + +```php +$package = new Package(new StaticVersionStrategy('v1', '%s?version=%s')); + +echo $package->getUrl('/me.png'); +// /me.png?version=v1 + +// put the version before the path +$package = new Package(new StaticVersionStrategy('v1', 'version-%2$s/%1$s')); + +echo $package->getUrl('/me.png'); +// /version-v1/me.png +``` + +Asset URLs Base Path +-------------------- + +When all assets are stored in a common path, use the `PathPackage` to avoid +repeating yourself: + +```php +use Symfony\Component\Asset\PathPackage; + +$package = new PathPackage('/images', new StaticVersionStrategy('v1')); + +echo $package->getUrl('/me.png'); +// /images/me.png?v1 +``` + +Asset URLs Base URLs +-------------------- + +If your assets are hosted on different domain name than the main website, use +the `UrlPackage` class: + +```php +use Symfony\Component\Asset\UrlPackage; + +$package = new UrlPackage('http://assets.example.com/images/', new StaticVersionStrategy('v1')); + +echo $package->getUrl('/me.png'); +// http://assets.example.com/images/me.png?v1 +``` + +One technique used to speed up page rendering in browsers is to use several +domains for assets; this is possible by passing more than one base URLs: + +```php +use Symfony\Component\Asset\UrlPackage; + +$urls = array( + 'http://a1.example.com/images/', + 'http://a2.example.com/images/', +); +$package = new UrlPackage($urls, new StaticVersionStrategy('v1')); + +echo $package->getUrl('/me.png'); +// http://a1.example.com/images/me.png?v1 +``` + +Note that it's also guaranteed that any given path will always use the same +base URL to be nice with HTTP caching mechanisms. + +HttpFoundation Integration +-------------------------- + +If you are using HttpFoundation for your project, set the Context to get +additional features for free: + +```php +use Symfony\Component\Asset\PathPackage; +use Symfony\Component\Asset\Context\RequestStackContext; + +$package = new PathPackage('images', new StaticVersionStrategy('v1')); +$package->setContext(new RequestStackContext($requestStack)); + +echo $package->getUrl('/me.png'); +// /somewhere/images/me.png?v1 +``` + +In addition to the configured base path, `PathPackage` now also automatically +prepends the current request base URL to assets to allow your website to be +hosted anywhere under the web server root directory. + +```php +use Symfony\Component\Asset\UrlPackage; +use Symfony\Component\Asset\Context\RequestStackContext; + +$package = new UrlPackage(array('http://example.com/', 'https://example.com/'), new StaticVersionStrategy('v1')); +$package->setContext(new RequestStackContext($requestStack)); + +echo $package->getUrl('/me.png'); +// https://example.com/images/me.png?v1 +``` + +`UrlPackage` now uses the current request scheme (HTTP or HTTPs) to select an +appropriate base URL (HTTPs or protocol-relative URLs for HTTPs requests, any +base URL for HTTP requests). + +Named Packages +-------------- + +The `Packages` class allows to easily manages several packages in a single +project by naming packages: + +```php +use Symfony\Component\Asset\Package; +use Symfony\Component\Asset\PathPackage; +use Symfony\Component\Asset\UrlPackage; +use Symfony\Component\Asset\Packages; + +// by default, just add a version to all assets +$versionStrategy = new StaticVersionStrategy('v1'); +$defaultPackage = new Asset\Package($versionStrategy); + +$namedPackages = array( + // images are hosted on another web server + 'img' => new Asset\UrlPackage('http://img.example.com/', $versionStrategy), + + // documents are stored deeply under the web root directory + // let's create a shortcut + 'doc' => new Asset\PathPackage('/somewhere/deep/for/documents', $versionStrategy), +); + +// bundle all packages to make it easy to use them +$packages = new Asset\Packages($defaultPackage, $namedPackages); + +echo $packages->getUrl('/some.css'); +// /some.css?v1 + +echo $packages->getUrl('/me.png', 'img'); +// http://img.example.com/me.png?v1 + +echo $packages->getUrl('/me.pdf', 'doc'); +// /somewhere/deep/for/documents/me.pdf?v1 +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Asset/ + $ composer update + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/Context/NullContextTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/Context/NullContextTest.php new file mode 100644 index 0000000..13e0e43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/Context/NullContextTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests\Context; + +use Symfony\Component\Asset\Context\NullContext; + +class NullContextTest extends \PHPUnit_Framework_TestCase +{ + public function testGetBasePath() + { + $nullContext = new NullContext(); + + $this->assertEmpty($nullContext->getBasePath()); + } + + public function testIsSecure() + { + $nullContext = new NullContext(); + + $this->assertFalse($nullContext->isSecure()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php new file mode 100644 index 0000000..07f75c9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/Context/RequestStackContextTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests\Context; + +use Symfony\Component\Asset\Context\RequestStackContext; + +class RequestStackContextTest extends \PHPUnit_Framework_TestCase +{ + public function testGetBasePathEmpty() + { + $requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStackContext = new RequestStackContext($requestStack); + + $this->assertEmpty($requestStackContext->getBasePath()); + } + + public function testGetBasePathSet() + { + $testBasePath = 'test-path'; + + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request->method('getBasePath') + ->willReturn($testBasePath); + $requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStack->method('getMasterRequest') + ->willReturn($request); + + $requestStackContext = new RequestStackContext($requestStack); + + $this->assertEquals($testBasePath, $requestStackContext->getBasePath()); + } + + public function testIsSecureFalse() + { + $requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStackContext = new RequestStackContext($requestStack); + + $this->assertFalse($requestStackContext->isSecure()); + } + + public function testIsSecureTrue() + { + $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request->method('isSecure') + ->willReturn(true); + $requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStack->method('getMasterRequest') + ->willReturn($request); + + $requestStackContext = new RequestStackContext($requestStack); + + $this->assertTrue($requestStackContext->isSecure()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PackageTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PackageTest.php new file mode 100644 index 0000000..a2310d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PackageTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests; + +use Symfony\Component\Asset\Package; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; +use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy; + +class PackageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getConfigs + */ + public function testGetUrl($version, $format, $path, $expected) + { + $package = new Package($version ? new StaticVersionStrategy($version, $format) : new EmptyVersionStrategy()); + $this->assertEquals($expected, $package->getUrl($path)); + } + + public function getConfigs() + { + return array( + array('v1', '', 'http://example.com/foo', 'http://example.com/foo'), + array('v1', '', 'https://example.com/foo', 'https://example.com/foo'), + array('v1', '', '//example.com/foo', '//example.com/foo'), + + array('v1', '', '/foo', '/foo?v1'), + array('v1', '', 'foo', 'foo?v1'), + + array(null, '', '/foo', '/foo'), + array(null, '', 'foo', 'foo'), + + array('v1', 'version-%2$s/%1$s', '/foo', '/version-v1/foo'), + array('v1', 'version-%2$s/%1$s', 'foo', 'version-v1/foo'), + array('v1', 'version-%2$s/%1$s', 'foo/', 'version-v1/foo/'), + array('v1', 'version-%2$s/%1$s', '/foo/', '/version-v1/foo/'), + ); + } + + public function testGetVersion() + { + $package = new Package(new StaticVersionStrategy('v1')); + $this->assertEquals('v1', $package->getVersion('/foo')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PackagesTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PackagesTest.php new file mode 100644 index 0000000..81db37b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PackagesTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests; + +use Symfony\Component\Asset\Package; +use Symfony\Component\Asset\Packages; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; +use Symfony\Component\Asset\Exception\InvalidArgumentException; +use Symfony\Component\Asset\Exception\LogicException; + +class PackagesTest extends \PHPUnit_Framework_TestCase +{ + public function testGetterSetters() + { + $packages = new Packages(); + $packages->setDefaultPackage($default = $this->getMock('Symfony\Component\Asset\PackageInterface')); + $packages->addPackage('a', $a = $this->getMock('Symfony\Component\Asset\PackageInterface')); + + $this->assertEquals($default, $packages->getPackage()); + $this->assertEquals($a, $packages->getPackage('a')); + + $packages = new Packages($default, array('a' => $a)); + + $this->assertEquals($default, $packages->getPackage()); + $this->assertEquals($a, $packages->getPackage('a')); + } + + public function testGetVersion() + { + $packages = new Packages( + new Package(new StaticVersionStrategy('default')), + array('a' => new Package(new StaticVersionStrategy('a'))) + ); + + $this->assertEquals('default', $packages->getVersion('/foo')); + $this->assertEquals('a', $packages->getVersion('/foo', 'a')); + } + + public function testGetUrl() + { + $packages = new Packages( + new Package(new StaticVersionStrategy('default')), + array('a' => new Package(new StaticVersionStrategy('a'))) + ); + + $this->assertEquals('/foo?default', $packages->getUrl('/foo')); + $this->assertEquals('/foo?a', $packages->getUrl('/foo', 'a')); + } + + /** + * @expectedException LogicException + */ + public function testNoDefaultPackage() + { + $packages = new Packages(); + $packages->getPackage(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testUndefinedPackage() + { + $packages = new Packages(); + $packages->getPackage('a'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PathPackageTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PathPackageTest.php new file mode 100644 index 0000000..ff5b0a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/PathPackageTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests; + +use Symfony\Component\Asset\PathPackage; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; + +class PathPackageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getConfigs + */ + public function testGetUrl($basePath, $format, $path, $expected) + { + $package = new PathPackage($basePath, new StaticVersionStrategy('v1', $format)); + $this->assertEquals($expected, $package->getUrl($path)); + } + + public function getConfigs() + { + return array( + array('/foo', '', 'http://example.com/foo', 'http://example.com/foo'), + array('/foo', '', 'https://example.com/foo', 'https://example.com/foo'), + array('/foo', '', '//example.com/foo', '//example.com/foo'), + + array('', '', '/foo', '/foo?v1'), + + array('/foo', '', '/foo', '/foo/foo?v1'), + array('/foo', '', 'foo', '/foo/foo?v1'), + array('foo', '', 'foo', '/foo/foo?v1'), + array('foo/', '', 'foo', '/foo/foo?v1'), + array('/foo/', '', 'foo', '/foo/foo?v1'), + + array('/foo', 'version-%2$s/%1$s', '/foo', '/foo/version-v1/foo'), + array('/foo', 'version-%2$s/%1$s', 'foo', '/foo/version-v1/foo'), + array('/foo', 'version-%2$s/%1$s', 'foo/', '/foo/version-v1/foo/'), + array('/foo', 'version-%2$s/%1$s', '/foo/', '/foo/version-v1/foo/'), + ); + } + + /** + * @dataProvider getContextConfigs + */ + public function testGetUrlWithContext($basePathRequest, $basePath, $format, $path, $expected) + { + $package = new PathPackage($basePath, new StaticVersionStrategy('v1', $format), $this->getContext($basePathRequest)); + + $this->assertEquals($expected, $package->getUrl($path)); + } + + public function getContextConfigs() + { + return array( + array('', '/foo', '', '/foo', '/foo/foo?v1'), + array('', '/foo', '', 'foo', '/foo/foo?v1'), + array('', 'foo', '', 'foo', '/foo/foo?v1'), + array('', 'foo/', '', 'foo', '/foo/foo?v1'), + array('', '/foo/', '', 'foo', '/foo/foo?v1'), + + array('/bar', '/foo', '', '/foo', '/bar/foo/foo?v1'), + array('/bar', '/foo', '', 'foo', '/bar/foo/foo?v1'), + array('/bar', 'foo', '', 'foo', '/bar/foo/foo?v1'), + array('/bar', 'foo/', '', 'foo', '/bar/foo/foo?v1'), + array('/bar', '/foo/', '', 'foo', '/bar/foo/foo?v1'), + ); + } + + private function getContext($basePath) + { + $context = $this->getMock('Symfony\Component\Asset\Context\ContextInterface'); + $context->expects($this->any())->method('getBasePath')->will($this->returnValue($basePath)); + + return $context; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/UrlPackageTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/UrlPackageTest.php new file mode 100644 index 0000000..515bd92 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/UrlPackageTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests; + +use Symfony\Component\Asset\UrlPackage; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; +use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy; +use Symfony\Component\Asset\Exception\InvalidArgumentException; +use Symfony\Component\Asset\Exception\LogicException; + +class UrlPackageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getConfigs + */ + public function testGetUrl($baseUrls, $format, $path, $expected) + { + $package = new UrlPackage($baseUrls, new StaticVersionStrategy('v1', $format)); + $this->assertEquals($expected, $package->getUrl($path)); + } + + public function getConfigs() + { + return array( + array('http://example.net', '', 'http://example.com/foo', 'http://example.com/foo'), + array('http://example.net', '', 'https://example.com/foo', 'https://example.com/foo'), + array('http://example.net', '', '//example.com/foo', '//example.com/foo'), + + array('http://example.com', '', '/foo', 'http://example.com/foo?v1'), + array('http://example.com', '', 'foo', 'http://example.com/foo?v1'), + array('http://example.com/', '', 'foo', 'http://example.com/foo?v1'), + array('http://example.com/foo', '', 'foo', 'http://example.com/foo/foo?v1'), + array('http://example.com/foo/', '', 'foo', 'http://example.com/foo/foo?v1'), + + array(array('http://example.com'), '', '/foo', 'http://example.com/foo?v1'), + array(array('http://example.com', 'http://example.net'), '', '/foo', 'http://example.com/foo?v1'), + array(array('http://example.com', 'http://example.net'), '', '/fooa', 'http://example.net/fooa?v1'), + + array('http://example.com', 'version-%2$s/%1$s', '/foo', 'http://example.com/version-v1/foo'), + array('http://example.com', 'version-%2$s/%1$s', 'foo', 'http://example.com/version-v1/foo'), + array('http://example.com', 'version-%2$s/%1$s', 'foo/', 'http://example.com/version-v1/foo/'), + array('http://example.com', 'version-%2$s/%1$s', '/foo/', 'http://example.com/version-v1/foo/'), + ); + } + + /** + * @dataProvider getContextConfigs + */ + public function testGetUrlWithContext($secure, $baseUrls, $format, $path, $expected) + { + $package = new UrlPackage($baseUrls, new StaticVersionStrategy('v1', $format), $this->getContext($secure)); + + $this->assertEquals($expected, $package->getUrl($path)); + } + + public function getContextConfigs() + { + return array( + array(false, 'http://example.com', '', 'foo', 'http://example.com/foo?v1'), + array(false, array('http://example.com'), '', 'foo', 'http://example.com/foo?v1'), + array(false, array('http://example.com', 'https://example.com'), '', 'foo', 'http://example.com/foo?v1'), + array(false, array('http://example.com', 'https://example.com'), '', 'fooa', 'https://example.com/fooa?v1'), + array(false, array('http://example.com/bar'), '', 'foo', 'http://example.com/bar/foo?v1'), + array(false, array('http://example.com/bar/'), '', 'foo', 'http://example.com/bar/foo?v1'), + array(false, array('//example.com/bar/'), '', 'foo', '//example.com/bar/foo?v1'), + + array(true, array('http://example.com'), '', 'foo', 'http://example.com/foo?v1'), + array(true, array('http://example.com', 'https://example.com'), '', 'foo', 'https://example.com/foo?v1'), + ); + } + + /** + * @expectedException LogicException + */ + public function testNoBaseUrls() + { + new UrlPackage(array(), new EmptyVersionStrategy()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testWrongBaseUrl() + { + new UrlPackage(array('not-a-url'), new EmptyVersionStrategy()); + } + + private function getContext($secure) + { + $context = $this->getMock('Symfony\Component\Asset\Context\ContextInterface'); + $context->expects($this->any())->method('isSecure')->will($this->returnValue($secure)); + + return $context; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php new file mode 100644 index 0000000..fb842dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/Tests/VersionStrategy/EmptyVersionStrategyTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\Tests\VersionStrategy; + +use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy; + +class EmptyVersionStrategyTest extends \PHPUnit_Framework_TestCase +{ + public function testGetVersion() + { + $emptyVersionStrategy = new EmptyVersionStrategy(); + $path = 'test-path'; + + $this->assertEmpty($emptyVersionStrategy->getVersion($path)); + } + + public function testApplyVersion() + { + $emptyVersionStrategy = new EmptyVersionStrategy(); + $path = 'test-path'; + + $this->assertEquals($path, $emptyVersionStrategy->applyVersion($path)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/UrlPackage.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/UrlPackage.php new file mode 100644 index 0000000..3626ec8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/UrlPackage.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset; + +use Symfony\Component\Asset\Context\ContextInterface; +use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface; +use Symfony\Component\Asset\Exception\InvalidArgumentException; +use Symfony\Component\Asset\Exception\LogicException; + +/** + * Package that adds a base URL to asset URLs in addition to a version. + * + * The package allows to use more than one base URLs in which case + * it randomly chooses one for each asset; it also guarantees that + * any given path will always use the same base URL to be nice with + * HTTP caching mechanisms. + * + * When the request context is available, this package can choose the + * best base URL to use based on the current request scheme: + * + * * For HTTP request, it chooses between all base URLs; + * * For HTTPs requests, it chooses between HTTPs base URLs and relative protocol URLs + * or falls back to any base URL if no secure ones are available. + * + * @author Fabien Potencier + */ +class UrlPackage extends Package +{ + private $baseUrls = array(); + private $sslPackage; + + /** + * @param string|string[] $baseUrls Base asset URLs + * @param VersionStrategyInterface $versionStrategy The version strategy + * @param ContextInterface|null $context Context + */ + public function __construct($baseUrls, VersionStrategyInterface $versionStrategy, ContextInterface $context = null) + { + parent::__construct($versionStrategy, $context); + + if (!is_array($baseUrls)) { + $baseUrls = (array) $baseUrls; + } + + if (!$baseUrls) { + throw new LogicException('You must provide at least one base URL.'); + } + + foreach ($baseUrls as $baseUrl) { + $this->baseUrls[] = rtrim($baseUrl, '/'); + } + + $sslUrls = $this->getSslUrls($baseUrls); + + if ($sslUrls && $baseUrls !== $sslUrls) { + $this->sslPackage = new self($sslUrls, $versionStrategy); + } + } + + /** + * {@inheritdoc} + */ + public function getUrl($path) + { + if ($this->isAbsoluteUrl($path)) { + return $path; + } + + if (null !== $this->sslPackage && $this->getContext()->isSecure()) { + return $this->sslPackage->getUrl($path); + } + + $url = $this->getVersionStrategy()->applyVersion($path); + + if ($url && '/' != $url[0]) { + $url = '/'.$url; + } + + return $this->getBaseUrl($path).$url; + } + + /** + * Returns the base URL for a path. + * + * @param string $path + * + * @return string The base URL + */ + public function getBaseUrl($path) + { + if (1 === count($this->baseUrls)) { + return $this->baseUrls[0]; + } + + return $this->baseUrls[$this->chooseBaseUrl($path)]; + } + + /** + * Determines which base URL to use for the given path. + * + * Override this method to change the default distribution strategy. + * This method should always return the same base URL for a given path. + * + * @param string $path + * + * @return string The base URL for the given path + */ + protected function chooseBaseUrl($path) + { + return fmod(hexdec(substr(hash('sha256', $path), 0, 10)), count($this->baseUrls)); + } + + private function getSslUrls($urls) + { + $sslUrls = array(); + foreach ($urls as $url) { + if ('https://' === substr($url, 0, 8) || '//' === substr($url, 0, 2)) { + $sslUrls[] = $url; + } elseif ('http://' !== substr($url, 0, 7)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid URL', $url)); + } + } + + return $sslUrls; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/EmptyVersionStrategy.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/EmptyVersionStrategy.php new file mode 100644 index 0000000..aa06eaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/EmptyVersionStrategy.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\VersionStrategy; + +/** + * Disable version for all assets. + * + * @author Fabien Potencier + */ +class EmptyVersionStrategy implements VersionStrategyInterface +{ + /** + * {@inheritdoc} + */ + public function getVersion($path) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function applyVersion($path) + { + return $path; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php new file mode 100644 index 0000000..857cf94 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/StaticVersionStrategy.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\VersionStrategy; + +/** + * Returns the same version for all assets. + * + * @author Fabien Potencier + */ +class StaticVersionStrategy implements VersionStrategyInterface +{ + private $version; + private $format; + + /** + * @param string $version Version number + * @param string $format Url format + */ + public function __construct($version, $format = null) + { + $this->version = $version; + $this->format = $format ?: '%s?%s'; + } + + /** + * {@inheritdoc} + */ + public function getVersion($path) + { + return $this->version; + } + + /** + * {@inheritdoc} + */ + public function applyVersion($path) + { + $versionized = sprintf($this->format, ltrim($path, '/'), $this->getVersion($path)); + + if ($path && '/' == $path[0]) { + return '/'.$versionized; + } + + return $versionized; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/VersionStrategyInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/VersionStrategyInterface.php new file mode 100644 index 0000000..a0fb260 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/VersionStrategy/VersionStrategyInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Asset\VersionStrategy; + +/** + * Asset version strategy interface. + * + * @author Fabien Potencier + */ +interface VersionStrategyInterface +{ + /** + * Returns the asset version for an asset. + * + * @param string $path A path + * + * @return string The version string + */ + public function getVersion($path); + + /** + * Applies version to the supplied path. + * + * @param string $path A path + * + * @return string The versionized path + */ + public function applyVersion($path); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Asset/composer.json new file mode 100644 index 0000000..e268105 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/composer.json @@ -0,0 +1,39 @@ +{ + "name": "symfony/asset", + "type": "library", + "description": "Symfony Asset Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "suggest": { + "symfony/http-foundation": "" + }, + "require-dev": { + "symfony/http-foundation": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Asset\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Asset/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Asset/phpunit.xml.dist new file mode 100644 index 0000000..b66906d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Asset/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CHANGELOG.md new file mode 100644 index 0000000..d2b1074 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CHANGELOG.md @@ -0,0 +1,18 @@ +CHANGELOG +========= + +2.3.0 +----- + + * [BC BREAK] `Client::followRedirect()` won't redirect responses with + a non-3xx Status Code and `Location` header anymore, as per + http://tools.ietf.org/html/rfc2616#section-14.30 + + * added `Client::getInternalRequest()` and `Client::getInternalResponse()` to + have access to the BrowserKit internal request and response objects + +2.1.0 +----- + + * [BC BREAK] The CookieJar internals have changed to allow cookies with the + same name on different sub-domains/sub-paths diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php new file mode 100644 index 0000000..00edcf3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php @@ -0,0 +1,598 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\DomCrawler\Link; +use Symfony\Component\DomCrawler\Form; +use Symfony\Component\Process\PhpProcess; + +/** + * Client simulates a browser. + * + * To make the actual request, you need to implement the doRequest() method. + * + * If you want to be able to run requests in their own process (insulated flag), + * you need to also implement the getScript() method. + * + * @author Fabien Potencier + */ +abstract class Client +{ + protected $history; + protected $cookieJar; + protected $server = array(); + protected $internalRequest; + protected $request; + protected $internalResponse; + protected $response; + protected $crawler; + protected $insulated = false; + protected $redirect; + protected $followRedirects = true; + + private $maxRedirects = -1; + private $redirectCount = 0; + private $isMainRequest = true; + + /** + * Constructor. + * + * @param array $server The server parameters (equivalent of $_SERVER) + * @param History $history A History instance to store the browser history + * @param CookieJar $cookieJar A CookieJar instance to store the cookies + */ + public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + $this->setServerParameters($server); + $this->history = $history ?: new History(); + $this->cookieJar = $cookieJar ?: new CookieJar(); + } + + /** + * Sets whether to automatically follow redirects or not. + * + * @param bool $followRedirect Whether to follow redirects + */ + public function followRedirects($followRedirect = true) + { + $this->followRedirects = (bool) $followRedirect; + } + + /** + * Returns whether client automatically follows redirects or not. + * + * @return bool + */ + public function isFollowingRedirects() + { + return $this->followRedirects; + } + + /** + * Sets the maximum number of requests that crawler can follow. + * + * @param int $maxRedirects + */ + public function setMaxRedirects($maxRedirects) + { + $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects; + $this->followRedirects = -1 != $this->maxRedirects; + } + + /** + * Returns the maximum number of requests that crawler can follow. + * + * @return int + */ + public function getMaxRedirects() + { + return $this->maxRedirects; + } + + /** + * Sets the insulated flag. + * + * @param bool $insulated Whether to insulate the requests or not + * + * @throws \RuntimeException When Symfony Process Component is not installed + */ + public function insulate($insulated = true) + { + if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) { + throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.'); + } + + $this->insulated = (bool) $insulated; + } + + /** + * Sets server parameters. + * + * @param array $server An array of server parameters + */ + public function setServerParameters(array $server) + { + $this->server = array_merge(array( + 'HTTP_HOST' => 'localhost', + 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit', + ), $server); + } + + /** + * Sets single server parameter. + * + * @param string $key A key of the parameter + * @param string $value A value of the parameter + */ + public function setServerParameter($key, $value) + { + $this->server[$key] = $value; + } + + /** + * Gets single server parameter for specified key. + * + * @param string $key A key of the parameter to get + * @param string $default A default value when key is undefined + * + * @return string A value of the parameter + */ + public function getServerParameter($key, $default = '') + { + return (isset($this->server[$key])) ? $this->server[$key] : $default; + } + + /** + * Returns the History instance. + * + * @return History A History instance + */ + public function getHistory() + { + return $this->history; + } + + /** + * Returns the CookieJar instance. + * + * @return CookieJar A CookieJar instance + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Returns the current Crawler instance. + * + * @return Crawler|null A Crawler instance + */ + public function getCrawler() + { + return $this->crawler; + } + + /** + * Returns the current BrowserKit Response instance. + * + * @return Response|null A BrowserKit Response instance + */ + public function getInternalResponse() + { + return $this->internalResponse; + } + + /** + * Returns the current origin response instance. + * + * The origin response is the response instance that is returned + * by the code that handles requests. + * + * @return object|null A response instance + * + * @see doRequest() + */ + public function getResponse() + { + return $this->response; + } + + /** + * Returns the current BrowserKit Request instance. + * + * @return Request|null A BrowserKit Request instance + */ + public function getInternalRequest() + { + return $this->internalRequest; + } + + /** + * Returns the current origin Request instance. + * + * The origin request is the request instance that is sent + * to the code that handles requests. + * + * @return object|null A Request instance + * + * @see doRequest() + */ + public function getRequest() + { + return $this->request; + } + + /** + * Clicks on a given link. + * + * @param Link $link A Link instance + * + * @return Crawler + */ + public function click(Link $link) + { + if ($link instanceof Form) { + return $this->submit($link); + } + + return $this->request($link->getMethod(), $link->getUri()); + } + + /** + * Submits a form. + * + * @param Form $form A Form instance + * @param array $values An array of form field values + * + * @return Crawler + */ + public function submit(Form $form, array $values = array()) + { + $form->setValues($values); + + return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles()); + } + + /** + * Calls a URI. + * + * @param string $method The request method + * @param string $uri The URI to fetch + * @param array $parameters The Request parameters + * @param array $files The files + * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does) + * @param string $content The raw body data + * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) + * + * @return Crawler + */ + public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true) + { + if ($this->isMainRequest) { + $this->redirectCount = 0; + } else { + ++$this->redirectCount; + } + + $uri = $this->getAbsoluteUri($uri); + + if (!empty($server['HTTP_HOST'])) { + $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri); + } + + if (isset($server['HTTPS'])) { + $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri); + } + + $server = array_merge($this->server, $server); + + if (!$this->history->isEmpty()) { + $server['HTTP_REFERER'] = $this->history->current()->getUri(); + } + + $server['HTTP_HOST'] = $this->extractHost($uri); + $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME); + + $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content); + + $this->request = $this->filterRequest($this->internalRequest); + + if (true === $changeHistory) { + $this->history->add($this->internalRequest); + } + + if ($this->insulated) { + $this->response = $this->doRequestInProcess($this->request); + } else { + $this->response = $this->doRequest($this->request); + } + + $this->internalResponse = $this->filterResponse($this->response); + + $this->cookieJar->updateFromResponse($this->internalResponse, $uri); + + $status = $this->internalResponse->getStatus(); + + if ($status >= 300 && $status < 400) { + $this->redirect = $this->internalResponse->getHeader('Location'); + } else { + $this->redirect = null; + } + + if ($this->followRedirects && $this->redirect) { + return $this->crawler = $this->followRedirect(); + } + + return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type')); + } + + /** + * Makes a request in another process. + * + * @param object $request An origin request instance + * + * @return object An origin response instance + * + * @throws \RuntimeException When processing returns exit code + */ + protected function doRequestInProcess($request) + { + $process = new PhpProcess($this->getScript($request), null, null); + $process->run(); + + if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) { + throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput())); + } + + return unserialize($process->getOutput()); + } + + /** + * Makes a request. + * + * @param object $request An origin request instance + * + * @return object An origin response instance + */ + abstract protected function doRequest($request); + + /** + * Returns the script to execute when the request must be insulated. + * + * @param object $request An origin request instance + * + * @throws \LogicException When this abstract class is not implemented + */ + protected function getScript($request) + { + throw new \LogicException('To insulate requests, you need to override the getScript() method.'); + } + + /** + * Filters the BrowserKit request to the origin one. + * + * @param Request $request The BrowserKit Request to filter + * + * @return object An origin request instance + */ + protected function filterRequest(Request $request) + { + return $request; + } + + /** + * Filters the origin response to the BrowserKit one. + * + * @param object $response The origin response to filter + * + * @return Response An BrowserKit Response instance + */ + protected function filterResponse($response) + { + return $response; + } + + /** + * Creates a crawler. + * + * This method returns null if the DomCrawler component is not available. + * + * @param string $uri A URI + * @param string $content Content for the crawler to use + * @param string $type Content type + * + * @return Crawler|null + */ + protected function createCrawlerFromContent($uri, $content, $type) + { + if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { + return; + } + + $crawler = new Crawler(null, $uri); + $crawler->addContent($content, $type); + + return $crawler; + } + + /** + * Goes back in the browser history. + * + * @return Crawler + */ + public function back() + { + return $this->requestFromRequest($this->history->back(), false); + } + + /** + * Goes forward in the browser history. + * + * @return Crawler + */ + public function forward() + { + return $this->requestFromRequest($this->history->forward(), false); + } + + /** + * Reloads the current browser. + * + * @return Crawler + */ + public function reload() + { + return $this->requestFromRequest($this->history->current(), false); + } + + /** + * Follow redirects? + * + * @return Crawler + * + * @throws \LogicException If request was not a redirect + */ + public function followRedirect() + { + if (empty($this->redirect)) { + throw new \LogicException('The request was not redirected.'); + } + + if (-1 !== $this->maxRedirects) { + if ($this->redirectCount > $this->maxRedirects) { + throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects)); + } + } + + $request = $this->internalRequest; + + if (in_array($this->internalResponse->getStatus(), array(302, 303))) { + $method = 'get'; + $files = array(); + $content = null; + } else { + $method = $request->getMethod(); + $files = $request->getFiles(); + $content = $request->getContent(); + } + + if ('get' === strtolower($method)) { + // Don't forward parameters for GET request as it should reach the redirection URI + $parameters = array(); + } else { + $parameters = $request->getParameters(); + } + + $server = $request->getServer(); + $server = $this->updateServerFromUri($server, $this->redirect); + + $this->isMainRequest = false; + + $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content); + + $this->isMainRequest = true; + + return $response; + } + + /** + * Restarts the client. + * + * It flushes history and all cookies. + */ + public function restart() + { + $this->cookieJar->clear(); + $this->history->clear(); + } + + /** + * Takes a URI and converts it to absolute if it is not already absolute. + * + * @param string $uri A URI + * + * @return string An absolute URI + */ + protected function getAbsoluteUri($uri) + { + // already absolute? + if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) { + return $uri; + } + + if (!$this->history->isEmpty()) { + $currentUri = $this->history->current()->getUri(); + } else { + $currentUri = sprintf('http%s://%s/', + isset($this->server['HTTPS']) ? 's' : '', + isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost' + ); + } + + // protocol relative URL + if (0 === strpos($uri, '//')) { + return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri; + } + + // anchor? + if (!$uri || '#' == $uri[0]) { + return preg_replace('/#.*?$/', '', $currentUri).$uri; + } + + if ('/' !== $uri[0]) { + $path = parse_url($currentUri, PHP_URL_PATH); + + if ('/' !== substr($path, -1)) { + $path = substr($path, 0, strrpos($path, '/') + 1); + } + + $uri = $path.$uri; + } + + return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri; + } + + /** + * Makes a request from a Request object directly. + * + * @param Request $request A Request instance + * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) + * + * @return Crawler + */ + protected function requestFromRequest(Request $request, $changeHistory = true) + { + return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory); + } + + private function updateServerFromUri($server, $uri) + { + $server['HTTP_HOST'] = $this->extractHost($uri); + $scheme = parse_url($uri, PHP_URL_SCHEME); + $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme; + unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']); + + return $server; + } + + private function extractHost($uri) + { + $host = parse_url($uri, PHP_URL_HOST); + + if ($port = parse_url($uri, PHP_URL_PORT)) { + return $host.':'.$port; + } + + return $host; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Cookie.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Cookie.php new file mode 100644 index 0000000..604d12d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Cookie.php @@ -0,0 +1,309 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * Cookie represents an HTTP cookie. + * + * @author Fabien Potencier + */ +class Cookie +{ + /** + * Handles dates as defined by RFC 2616 section 3.3.1, and also some other + * non-standard, but common formats. + * + * @var array + */ + private static $dateFormats = array( + 'D, d M Y H:i:s T', + 'D, d-M-y H:i:s T', + 'D, d-M-Y H:i:s T', + 'D, d-m-y H:i:s T', + 'D, d-m-Y H:i:s T', + 'D M j G:i:s Y', + 'D M d H:i:s Y T', + ); + + protected $name; + protected $value; + protected $expires; + protected $path; + protected $domain; + protected $secure; + protected $httponly; + protected $rawValue; + + /** + * Sets a cookie. + * + * @param string $name The cookie name + * @param string $value The value of the cookie + * @param string $expires The time the cookie expires + * @param string $path The path on the server in which the cookie will be available on + * @param string $domain The domain that the cookie is available + * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client + * @param bool $httponly The cookie httponly flag + * @param bool $encodedValue Whether the value is encoded or not + */ + public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false) + { + if ($encodedValue) { + $this->value = urldecode($value); + $this->rawValue = $value; + } else { + $this->value = $value; + $this->rawValue = urlencode($value); + } + $this->name = $name; + $this->path = empty($path) ? '/' : $path; + $this->domain = $domain; + $this->secure = (bool) $secure; + $this->httponly = (bool) $httponly; + + if (null !== $expires) { + $timestampAsDateTime = \DateTime::createFromFormat('U', $expires); + if (false === $timestampAsDateTime) { + throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.', $expires)); + } + + $this->expires = $timestampAsDateTime->getTimestamp(); + } + } + + /** + * Returns the HTTP representation of the Cookie. + * + * @return string The HTTP representation of the Cookie + * + * @throws \UnexpectedValueException + */ + public function __toString() + { + $cookie = sprintf('%s=%s', $this->name, $this->rawValue); + + if (null !== $this->expires) { + $dateTime = \DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT')); + $cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::$dateFormats[0])); + } + + if ('' !== $this->domain) { + $cookie .= '; domain='.$this->domain; + } + + if ($this->path) { + $cookie .= '; path='.$this->path; + } + + if ($this->secure) { + $cookie .= '; secure'; + } + + if ($this->httponly) { + $cookie .= '; httponly'; + } + + return $cookie; + } + + /** + * Creates a Cookie instance from a Set-Cookie header value. + * + * @param string $cookie A Set-Cookie header value + * @param string $url The base URL + * + * @return Cookie A Cookie instance + * + * @throws \InvalidArgumentException + */ + public static function fromString($cookie, $url = null) + { + $parts = explode(';', $cookie); + + if (false === strpos($parts[0], '=')) { + throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0])); + } + + list($name, $value) = explode('=', array_shift($parts), 2); + + $values = array( + 'name' => trim($name), + 'value' => trim($value), + 'expires' => null, + 'path' => '/', + 'domain' => '', + 'secure' => false, + 'httponly' => false, + 'passedRawValue' => true, + ); + + if (null !== $url) { + if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) { + throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url)); + } + + $values['domain'] = $urlParts['host']; + $values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : ''; + } + + foreach ($parts as $part) { + $part = trim($part); + + if ('secure' === strtolower($part)) { + // Ignore the secure flag if the original URI is not given or is not HTTPS + if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) { + continue; + } + + $values['secure'] = true; + + continue; + } + + if ('httponly' === strtolower($part)) { + $values['httponly'] = true; + + continue; + } + + if (2 === count($elements = explode('=', $part, 2))) { + if ('expires' === strtolower($elements[0])) { + $elements[1] = self::parseDate($elements[1]); + } + + $values[strtolower($elements[0])] = $elements[1]; + } + } + + return new static( + $values['name'], + $values['value'], + $values['expires'], + $values['path'], + $values['domain'], + $values['secure'], + $values['httponly'], + $values['passedRawValue'] + ); + } + + private static function parseDate($dateValue) + { + // trim single quotes around date if present + if (($length = strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length - 1]) { + $dateValue = substr($dateValue, 1, -1); + } + + foreach (self::$dateFormats as $dateFormat) { + if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) { + return $date->getTimestamp(); + } + } + + // attempt a fallback for unusual formatting + if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) { + return $date->getTimestamp(); + } + + throw new \InvalidArgumentException(sprintf('Could not parse date "%s".', $dateValue)); + } + + /** + * Gets the name of the cookie. + * + * @return string The cookie name + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string The cookie value + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the raw value of the cookie. + * + * @return string The cookie value + */ + public function getRawValue() + { + return $this->rawValue; + } + + /** + * Gets the expires time of the cookie. + * + * @return string The cookie expires time + */ + public function getExpiresTime() + { + return $this->expires; + } + + /** + * Gets the path of the cookie. + * + * @return string The cookie path + */ + public function getPath() + { + return $this->path; + } + + /** + * Gets the domain of the cookie. + * + * @return string The cookie domain + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Returns the secure flag of the cookie. + * + * @return bool The cookie secure flag + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Returns the httponly flag of the cookie. + * + * @return bool The cookie httponly flag + */ + public function isHttpOnly() + { + return $this->httponly; + } + + /** + * Returns true if the cookie has expired. + * + * @return bool true if the cookie has expired, false otherwise + */ + public function isExpired() + { + return null !== $this->expires && 0 !== $this->expires && $this->expires < time(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CookieJar.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CookieJar.php new file mode 100644 index 0000000..4b9661b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/CookieJar.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * CookieJar. + * + * @author Fabien Potencier + */ +class CookieJar +{ + protected $cookieJar = array(); + + /** + * Sets a cookie. + * + * @param Cookie $cookie A Cookie instance + */ + public function set(Cookie $cookie) + { + $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + } + + /** + * Gets a cookie by name. + * + * You should never use an empty domain, but if you do so, + * this method returns the first cookie for the given name/path + * (this behavior ensures a BC behavior with previous versions of + * Symfony). + * + * @param string $name The cookie name + * @param string $path The cookie path + * @param string $domain The cookie domain + * + * @return Cookie|null A Cookie instance or null if the cookie does not exist + */ + public function get($name, $path = '/', $domain = null) + { + $this->flushExpiredCookies(); + + if (!empty($domain)) { + foreach ($this->cookieJar as $cookieDomain => $pathCookies) { + if ($cookieDomain) { + $cookieDomain = '.'.ltrim($cookieDomain, '.'); + if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) { + continue; + } + } + + foreach ($pathCookies as $cookiePath => $namedCookies) { + if ($cookiePath != substr($path, 0, strlen($cookiePath))) { + continue; + } + if (isset($namedCookies[$name])) { + return $namedCookies[$name]; + } + } + } + + return; + } + + // avoid relying on this behavior that is mainly here for BC reasons + foreach ($this->cookieJar as $cookies) { + if (isset($cookies[$path][$name])) { + return $cookies[$path][$name]; + } + } + } + + /** + * Removes a cookie by name. + * + * You should never use an empty domain, but if you do so, + * all cookies for the given name/path expire (this behavior + * ensures a BC behavior with previous versions of Symfony). + * + * @param string $name The cookie name + * @param string $path The cookie path + * @param string $domain The cookie domain + */ + public function expire($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + + if (empty($domain)) { + // an empty domain means any domain + // this should never happen but it allows for a better BC + $domains = array_keys($this->cookieJar); + } else { + $domains = array($domain); + } + + foreach ($domains as $domain) { + unset($this->cookieJar[$domain][$path][$name]); + + if (empty($this->cookieJar[$domain][$path])) { + unset($this->cookieJar[$domain][$path]); + + if (empty($this->cookieJar[$domain])) { + unset($this->cookieJar[$domain]); + } + } + } + } + + /** + * Removes all the cookies from the jar. + */ + public function clear() + { + $this->cookieJar = array(); + } + + /** + * Updates the cookie jar from a response Set-Cookie headers. + * + * @param array $setCookies Set-Cookie headers from an HTTP response + * @param string $uri The base URL + */ + public function updateFromSetCookie(array $setCookies, $uri = null) + { + $cookies = array(); + + foreach ($setCookies as $cookie) { + foreach (explode(',', $cookie) as $i => $part) { + if (0 === $i || preg_match('/^(?P\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) { + $cookies[] = ltrim($part); + } else { + $cookies[count($cookies) - 1] .= ','.$part; + } + } + } + + foreach ($cookies as $cookie) { + try { + $this->set(Cookie::fromString($cookie, $uri)); + } catch (\InvalidArgumentException $e) { + // invalid cookies are just ignored + } + } + } + + /** + * Updates the cookie jar from a Response object. + * + * @param Response $response A Response object + * @param string $uri The base URL + */ + public function updateFromResponse(Response $response, $uri = null) + { + $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri); + } + + /** + * Returns not yet expired cookies. + * + * @return Cookie[] An array of cookies + */ + public function all() + { + $this->flushExpiredCookies(); + + $flattenedCookies = array(); + foreach ($this->cookieJar as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; + } + + /** + * Returns not yet expired cookie values for the given URI. + * + * @param string $uri A URI + * @param bool $returnsRawValue Returns raw value or urldecoded value + * + * @return array An array of cookie values + */ + public function allValues($uri, $returnsRawValue = false) + { + $this->flushExpiredCookies(); + + $parts = array_replace(array('path' => '/'), parse_url($uri)); + $cookies = array(); + foreach ($this->cookieJar as $domain => $pathCookies) { + if ($domain) { + $domain = '.'.ltrim($domain, '.'); + if ($domain != substr('.'.$parts['host'], -strlen($domain))) { + continue; + } + } + + foreach ($pathCookies as $path => $namedCookies) { + if ($path != substr($parts['path'], 0, strlen($path))) { + continue; + } + + foreach ($namedCookies as $cookie) { + if ($cookie->isSecure() && 'https' != $parts['scheme']) { + continue; + } + + $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue(); + } + } + } + + return $cookies; + } + + /** + * Returns not yet expired raw cookie values for the given URI. + * + * @param string $uri A URI + * + * @return array An array of cookie values + */ + public function allRawValues($uri) + { + return $this->allValues($uri, true); + } + + /** + * Removes all expired cookies. + */ + public function flushExpiredCookies() + { + foreach ($this->cookieJar as $domain => $pathCookies) { + foreach ($pathCookies as $path => $namedCookies) { + foreach ($namedCookies as $name => $cookie) { + if ($cookie->isExpired()) { + unset($this->cookieJar[$domain][$path][$name]); + } + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/History.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/History.php new file mode 100644 index 0000000..8e38fe5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/History.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * History. + * + * @author Fabien Potencier + */ +class History +{ + protected $stack = array(); + protected $position = -1; + + /** + * Clears the history. + */ + public function clear() + { + $this->stack = array(); + $this->position = -1; + } + + /** + * Adds a Request to the history. + * + * @param Request $request A Request instance + */ + public function add(Request $request) + { + $this->stack = array_slice($this->stack, 0, $this->position + 1); + $this->stack[] = clone $request; + $this->position = count($this->stack) - 1; + } + + /** + * Returns true if the history is empty. + * + * @return bool true if the history is empty, false otherwise + */ + public function isEmpty() + { + return count($this->stack) == 0; + } + + /** + * Goes back in the history. + * + * @return Request A Request instance + * + * @throws \LogicException if the stack is already on the first page + */ + public function back() + { + if ($this->position < 1) { + throw new \LogicException('You are already on the first page.'); + } + + return clone $this->stack[--$this->position]; + } + + /** + * Goes forward in the history. + * + * @return Request A Request instance + * + * @throws \LogicException if the stack is already on the last page + */ + public function forward() + { + if ($this->position > count($this->stack) - 2) { + throw new \LogicException('You are already on the last page.'); + } + + return clone $this->stack[++$this->position]; + } + + /** + * Returns the current element in the history. + * + * @return Request A Request instance + * + * @throws \LogicException if the stack is empty + */ + public function current() + { + if (-1 == $this->position) { + throw new \LogicException('The page history is empty.'); + } + + return clone $this->stack[$this->position]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/README.md b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/README.md new file mode 100644 index 0000000..5600fe2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/README.md @@ -0,0 +1,23 @@ +BrowserKit Component +==================== + +BrowserKit simulates the behavior of a web browser. + +The component only provides an abstract client and does not provide any +"default" backend for the HTTP layer. + +Resources +--------- + +For a simple implementation of a browser based on an HTTP layer, have a look +at [Goutte](https://github.com/FriendsOfPHP/Goutte). + +For an implementation based on HttpKernelInterface, have a look at the +[Client](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Client.php) +provided by the HttpKernel component. + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/BrowserKit/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Request.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Request.php new file mode 100644 index 0000000..c79b341 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Request.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * Request object. + * + * @author Fabien Potencier + */ +class Request +{ + protected $uri; + protected $method; + protected $parameters; + protected $files; + protected $cookies; + protected $server; + protected $content; + + /** + * Constructor. + * + * @param string $uri The request URI + * @param string $method The HTTP method request + * @param array $parameters The request parameters + * @param array $files An array of uploaded files + * @param array $cookies An array of cookies + * @param array $server An array of server parameters + * @param string $content The raw body data + */ + public function __construct($uri, $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), $content = null) + { + $this->uri = $uri; + $this->method = $method; + $this->parameters = $parameters; + $this->files = $files; + $this->cookies = $cookies; + $this->server = $server; + $this->content = $content; + } + + /** + * Gets the request URI. + * + * @return string The request URI + */ + public function getUri() + { + return $this->uri; + } + + /** + * Gets the request HTTP method. + * + * @return string The request HTTP method + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the request parameters. + * + * @return array The request parameters + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets the request server files. + * + * @return array The request files + */ + public function getFiles() + { + return $this->files; + } + + /** + * Gets the request cookies. + * + * @return array The request cookies + */ + public function getCookies() + { + return $this->cookies; + } + + /** + * Gets the request server parameters. + * + * @return array The request server parameters + */ + public function getServer() + { + return $this->server; + } + + /** + * Gets the request raw body data. + * + * @return string The request raw body data. + */ + public function getContent() + { + return $this->content; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Response.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Response.php new file mode 100644 index 0000000..984442f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Response.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit; + +/** + * Response object. + * + * @author Fabien Potencier + */ +class Response +{ + protected $content; + protected $status; + protected $headers; + + /** + * Constructor. + * + * The headers array is a set of key/value pairs. If a header is present multiple times + * then the value is an array of all the values. + * + * @param string $content The content of the response + * @param int $status The response status code + * @param array $headers An array of headers + */ + public function __construct($content = '', $status = 200, array $headers = array()) + { + $this->content = $content; + $this->status = $status; + $this->headers = $headers; + } + + /** + * Converts the response object to string containing all headers and the response content. + * + * @return string The response with headers and content + */ + public function __toString() + { + $headers = ''; + foreach ($this->headers as $name => $value) { + if (is_string($value)) { + $headers .= $this->buildHeader($name, $value); + } else { + foreach ($value as $headerValue) { + $headers .= $this->buildHeader($name, $headerValue); + } + } + } + + return $headers."\n".$this->content; + } + + /** + * Returns the build header line. + * + * @param string $name The header name + * @param string $value The header value + * + * @return string The built header line + */ + protected function buildHeader($name, $value) + { + return sprintf("%s: %s\n", $name, $value); + } + + /** + * Gets the response content. + * + * @return string The response content + */ + public function getContent() + { + return $this->content; + } + + /** + * Gets the response status code. + * + * @return int The response status code + */ + public function getStatus() + { + return $this->status; + } + + /** + * Gets the response headers. + * + * @return array The response headers + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Gets a response header. + * + * @param string $header The header name + * @param bool $first Whether to return the first value or all header values + * + * @return string|array The first header value if $first is true, an array of values otherwise + */ + public function getHeader($header, $first = true) + { + $normalizedHeader = str_replace('-', '_', strtolower($header)); + foreach ($this->headers as $key => $value) { + if (str_replace('-', '_', strtolower($key)) === $normalizedHeader) { + if ($first) { + return is_array($value) ? (count($value) ? $value[0] : '') : $value; + } + + return is_array($value) ? $value : array($value); + } + } + + return $first ? null : array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ClientTest.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ClientTest.php new file mode 100644 index 0000000..95aaff4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ClientTest.php @@ -0,0 +1,663 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Client; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\BrowserKit\Response; + +class SpecialResponse extends Response +{ +} + +class TestClient extends Client +{ + protected $nextResponse = null; + protected $nextScript = null; + + public function setNextResponse(Response $response) + { + $this->nextResponse = $response; + } + + public function setNextScript($script) + { + $this->nextScript = $script; + } + + protected function doRequest($request) + { + if (null === $this->nextResponse) { + return new Response(); + } + + $response = $this->nextResponse; + $this->nextResponse = null; + + return $response; + } + + protected function filterResponse($response) + { + if ($response instanceof SpecialResponse) { + return new Response($response->getContent(), $response->getStatus(), $response->getHeaders()); + } + + return $response; + } + + protected function getScript($request) + { + $r = new \ReflectionClass('Symfony\Component\BrowserKit\Response'); + $path = $r->getFileName(); + + return <<nextScript); +EOF; + } +} + +class ClientTest extends \PHPUnit_Framework_TestCase +{ + public function testGetHistory() + { + $client = new TestClient(array(), $history = new History()); + $this->assertSame($history, $client->getHistory(), '->getHistory() returns the History'); + } + + public function testGetCookieJar() + { + $client = new TestClient(array(), null, $cookieJar = new CookieJar()); + $this->assertSame($cookieJar, $client->getCookieJar(), '->getCookieJar() returns the CookieJar'); + } + + public function testGetRequest() + { + $client = new TestClient(); + $client->request('GET', 'http://example.com/'); + + $this->assertEquals('http://example.com/', $client->getRequest()->getUri(), '->getCrawler() returns the Request of the last request'); + } + + public function testGetRequestWithIpAsHost() + { + $client = new TestClient(); + $client->request('GET', 'https://example.com/foo', array(), array(), array('HTTP_HOST' => '127.0.0.1')); + + $this->assertEquals('https://127.0.0.1/foo', $client->getRequest()->getUri()); + } + + public function testGetResponse() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo')); + $client->request('GET', 'http://example.com/'); + + $this->assertEquals('foo', $client->getResponse()->getContent(), '->getCrawler() returns the Response of the last request'); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getResponse(), '->getCrawler() returns the Response of the last request'); + } + + public function testGetInternalResponse() + { + $client = new TestClient(); + $client->setNextResponse(new SpecialResponse('foo')); + $client->request('GET', 'http://example.com/'); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getInternalResponse()); + $this->assertNotInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialResponse', $client->getInternalResponse()); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Tests\SpecialResponse', $client->getResponse()); + } + + public function testGetContent() + { + $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; + + $client = new TestClient(); + $client->request('POST', 'http://example.com/jsonrpc', array(), array(), array(), $json); + $this->assertEquals($json, $client->getRequest()->getContent()); + } + + public function testGetCrawler() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo')); + $crawler = $client->request('GET', 'http://example.com/'); + + $this->assertSame($crawler, $client->getCrawler(), '->getCrawler() returns the Crawler of the last request'); + } + + public function testRequestHttpHeaders() + { + $client = new TestClient(); + $client->request('GET', '/'); + $headers = $client->getRequest()->getServer(); + $this->assertEquals('localhost', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com'); + $headers = $client->getRequest()->getServer(); + $this->assertEquals('www.example.com', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header'); + + $client->request('GET', 'https://www.example.com'); + $headers = $client->getRequest()->getServer(); + $this->assertTrue($headers['HTTPS'], '->request() sets the HTTPS header'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com:8080'); + $headers = $client->getRequest()->getServer(); + $this->assertEquals('www.example.com:8080', $headers['HTTP_HOST'], '->request() sets the HTTP_HOST header with port'); + } + + public function testRequestURIConversion() + { + $client = new TestClient(); + $client->request('GET', '/foo'); + $this->assertEquals('http://localhost/foo', $client->getRequest()->getUri(), '->request() converts the URI to an absolute one'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com'); + $this->assertEquals('http://www.example.com', $client->getRequest()->getUri(), '->request() does not change absolute URIs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/'); + $client->request('GET', '/foo'); + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo'); + $client->request('GET', '#'); + $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #'); + $client->request('GET', '#'); + $this->assertEquals('http://www.example.com/foo#', $client->getRequest()->getUri(), '->request() uses the previous request for #'); + $client->request('GET', '#foo'); + $this->assertEquals('http://www.example.com/foo#foo', $client->getRequest()->getUri(), '->request() uses the previous request for #'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/'); + $client->request('GET', 'bar'); + $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'bar'); + $this->assertEquals('http://www.example.com/foo/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/'); + $client->request('GET', 'http'); + $this->assertEquals('http://www.example.com/foo/http', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo'); + $client->request('GET', 'http/bar'); + $this->assertEquals('http://www.example.com/http/bar', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/'); + $client->request('GET', 'http'); + $this->assertEquals('http://www.example.com/http', $client->getRequest()->getUri(), '->request() uses the previous request for relative URLs'); + } + + public function testRequestURIConversionByServerHost() + { + $client = new TestClient(); + + $server = array('HTTP_HOST' => 'www.exampl+e.com:8000'); + $parameters = array(); + $files = array(); + + $client->request('GET', 'http://exampl+e.com', $parameters, $files, $server); + $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to add port'); + + $client->request('GET', 'http://exampl+e.com:8888', $parameters, $files, $server); + $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST to modify existing port'); + + $client->request('GET', 'http://exampl+e.com:8000', $parameters, $files, $server); + $this->assertEquals('http://www.exampl+e.com:8000', $client->getRequest()->getUri(), '->request() uses HTTP_HOST respects correct set port'); + } + + public function testRequestReferer() + { + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'bar'); + $server = $client->getRequest()->getServer(); + $this->assertEquals('http://www.example.com/foo/foobar', $server['HTTP_REFERER'], '->request() sets the referer'); + } + + public function testRequestHistory() + { + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'bar'); + + $this->assertEquals('http://www.example.com/foo/bar', $client->getHistory()->current()->getUri(), '->request() updates the History'); + $this->assertEquals('http://www.example.com/foo/foobar', $client->getHistory()->back()->getUri(), '->request() updates the History'); + } + + public function testRequestCookies() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo', 200, array('Set-Cookie' => 'foo=bar'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar'); + + $client->request('GET', 'bar'); + $this->assertEquals(array('foo' => 'bar'), $client->getCookieJar()->allValues('http://www.example.com/foo/foobar'), '->request() updates the CookieJar'); + } + + public function testRequestSecureCookies() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo', 200, array('Set-Cookie' => 'foo=bar; path=/; secure'))); + $client->request('GET', 'https://www.example.com/foo/foobar'); + + $this->assertTrue($client->getCookieJar()->get('foo', '/', 'www.example.com')->isSecure()); + } + + public function testClick() + { + $client = new TestClient(); + $client->setNextResponse(new Response('foo')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $client->click($crawler->filter('a')->link()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() clicks on links'); + } + + public function testClickForm() + { + $client = new TestClient(); + $client->setNextResponse(new Response('
    ')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $client->click($crawler->filter('input')->form()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->click() Form submit forms'); + } + + public function testSubmit() + { + $client = new TestClient(); + $client->setNextResponse(new Response('
    ')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $client->submit($crawler->filter('input')->form()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms'); + } + + public function testSubmitPreserveAuth() + { + $client = new TestClient(array('PHP_AUTH_USER' => 'foo', 'PHP_AUTH_PW' => 'bar')); + $client->setNextResponse(new Response('
    ')); + $crawler = $client->request('GET', 'http://www.example.com/foo/foobar'); + + $server = $client->getRequest()->getServer(); + $this->assertArrayHasKey('PHP_AUTH_USER', $server); + $this->assertEquals('foo', $server['PHP_AUTH_USER']); + $this->assertArrayHasKey('PHP_AUTH_PW', $server); + $this->assertEquals('bar', $server['PHP_AUTH_PW']); + + $client->submit($crawler->filter('input')->form()); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->submit() submit forms'); + + $server = $client->getRequest()->getServer(); + $this->assertArrayHasKey('PHP_AUTH_USER', $server); + $this->assertEquals('foo', $server['PHP_AUTH_USER']); + $this->assertArrayHasKey('PHP_AUTH_PW', $server); + $this->assertEquals('bar', $server['PHP_AUTH_PW']); + } + + public function testFollowRedirect() + { + $client = new TestClient(); + $client->followRedirects(false); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + try { + $client->followRedirect(); + $this->fail('->followRedirect() throws a \LogicException if the request was not redirected'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->followRedirect() throws a \LogicException if the request was not redirected'); + } + + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->followRedirect(); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() automatically follows redirects if followRedirects is true'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 201, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->followRedirect() does not follow redirect if HTTP Code is not 30x'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 201, array('Location' => 'http://www.example.com/redirected'))); + $client->followRedirects(false); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + try { + $client->followRedirect(); + $this->fail('->followRedirect() throws a \LogicException if the request did not respond with 30x HTTP Code'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->followRedirect() throws a \LogicException if the request did not respond with 30x HTTP Code'); + } + } + + public function testFollowRelativeRedirect() + { + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => '/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => '/redirected:1234'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected:1234', $client->getRequest()->getUri(), '->followRedirect() follows relative urls'); + } + + public function testFollowRedirectWithMaxRedirects() + { + $client = new TestClient(); + $client->setMaxRedirects(1); + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected2'))); + try { + $client->followRedirect(); + $this->fail('->followRedirect() throws a \LogicException if the request was redirected and limit of redirections was reached'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->followRedirect() throws a \LogicException if the request was redirected and limit of redirections was reached'); + } + + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows a redirect if any'); + + $client->setNextResponse(new Response('', 302, array('Location' => '/redirected'))); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('http://www.example.com/redirected', $client->getRequest()->getUri(), '->followRedirect() follows relative URLs'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => '//www.example.org/'))); + $client->request('GET', 'https://www.example.com/'); + + $this->assertEquals('https://www.example.org/', $client->getRequest()->getUri(), '->followRedirect() follows protocol-relative URLs'); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array('Location' => 'http://www.example.com/redirected'))); + $client->request('POST', 'http://www.example.com/foo/foobar', array('name' => 'bar')); + + $this->assertEquals('get', $client->getRequest()->getMethod(), '->followRedirect() uses a get for 302'); + $this->assertEquals(array(), $client->getRequest()->getParameters(), '->followRedirect() does not submit parameters when changing the method'); + } + + public function testFollowRedirectWithCookies() + { + $client = new TestClient(); + $client->followRedirects(false); + $client->setNextResponse(new Response('', 302, array( + 'Location' => 'http://www.example.com/redirected', + 'Set-Cookie' => 'foo=bar', + ))); + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals(array(), $client->getRequest()->getCookies()); + $client->followRedirect(); + $this->assertEquals(array('foo' => 'bar'), $client->getRequest()->getCookies()); + } + + public function testFollowRedirectWithHeaders() + { + $headers = array( + 'HTTP_HOST' => 'www.example.com', + 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit', + 'CONTENT_TYPE' => 'application/vnd.custom+xml', + 'HTTPS' => false, + ); + + $client = new TestClient(); + $client->followRedirects(false); + $client->setNextResponse(new Response('', 302, array( + 'Location' => 'http://www.example.com/redirected', + ))); + $client->request('GET', 'http://www.example.com/', array(), array(), array( + 'CONTENT_TYPE' => 'application/vnd.custom+xml', + )); + + $this->assertEquals($headers, $client->getRequest()->getServer()); + + $client->followRedirect(); + + $headers['HTTP_REFERER'] = 'http://www.example.com/'; + + $this->assertEquals($headers, $client->getRequest()->getServer()); + } + + public function testFollowRedirectWithPort() + { + $headers = array( + 'HTTP_HOST' => 'www.example.com:8080', + 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit', + 'HTTPS' => false, + 'HTTP_REFERER' => 'http://www.example.com:8080/', + ); + + $client = new TestClient(); + $client->setNextResponse(new Response('', 302, array( + 'Location' => 'http://www.example.com:8080/redirected', + ))); + $client->request('GET', 'http://www.example.com:8080/'); + + $this->assertEquals($headers, $client->getRequest()->getServer()); + } + + public function testIsFollowingRedirects() + { + $client = new TestClient(); + $this->assertTrue($client->isFollowingRedirects(), '->getFollowRedirects() returns default value'); + $client->followRedirects(false); + $this->assertFalse($client->isFollowingRedirects(), '->getFollowRedirects() returns assigned value'); + } + + public function testGetMaxRedirects() + { + $client = new TestClient(); + $this->assertEquals(-1, $client->getMaxRedirects(), '->getMaxRedirects() returns default value'); + $client->setMaxRedirects(3); + $this->assertEquals(3, $client->getMaxRedirects(), '->getMaxRedirects() returns assigned value'); + } + + public function testBack() + { + $client = new TestClient(); + + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content); + $client->request('GET', 'http://www.example.com/foo'); + $client->back(); + + $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->back() goes back in the history'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->back() keeps parameters'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->back() keeps files'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->back() keeps $_SERVER'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->back() keeps content'); + } + + public function testForward() + { + $client = new TestClient(); + + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->request('GET', 'http://www.example.com/foo', $parameters, $files, $server, $content); + $client->back(); + $client->forward(); + + $this->assertEquals('http://www.example.com/foo', $client->getRequest()->getUri(), '->forward() goes forward in the history'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->forward() keeps parameters'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->forward() keeps files'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->forward() keeps $_SERVER'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->forward() keeps content'); + } + + public function testReload() + { + $client = new TestClient(); + + $parameters = array('foo' => 'bar'); + $files = array('myfile.foo' => 'baz'); + $server = array('X_TEST_FOO' => 'bazbar'); + $content = 'foobarbaz'; + + $client->request('GET', 'http://www.example.com/foo/foobar', $parameters, $files, $server, $content); + $client->reload(); + + $this->assertEquals('http://www.example.com/foo/foobar', $client->getRequest()->getUri(), '->reload() reloads the current page'); + $this->assertArrayHasKey('foo', $client->getRequest()->getParameters(), '->reload() keeps parameters'); + $this->assertArrayHasKey('myfile.foo', $client->getRequest()->getFiles(), '->reload() keeps files'); + $this->assertArrayHasKey('X_TEST_FOO', $client->getRequest()->getServer(), '->reload() keeps $_SERVER'); + $this->assertEquals($content, $client->getRequest()->getContent(), '->reload() keeps content'); + } + + public function testRestart() + { + $client = new TestClient(); + $client->request('GET', 'http://www.example.com/foo/foobar'); + $client->restart(); + + $this->assertTrue($client->getHistory()->isEmpty(), '->restart() clears the history'); + $this->assertEquals(array(), $client->getCookieJar()->all(), '->restart() clears the cookies'); + } + + public function testInsulatedRequests() + { + $client = new TestClient(); + $client->insulate(); + $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar')"); + $client->request('GET', 'http://www.example.com/foo/foobar'); + + $this->assertEquals('foobar', $client->getResponse()->getContent(), '->insulate() process the request in a forked process'); + + $client->setNextScript("new Symfony\Component\BrowserKit\Response('foobar)"); + + try { + $client->request('GET', 'http://www.example.com/foo/foobar'); + $this->fail('->request() throws a \RuntimeException if the script has an error'); + } catch (\Exception $e) { + $this->assertInstanceOf('RuntimeException', $e, '->request() throws a \RuntimeException if the script has an error'); + } + } + + public function testGetServerParameter() + { + $client = new TestClient(); + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + $this->assertEquals('testvalue', $client->getServerParameter('testkey', 'testvalue')); + } + + public function testSetServerParameter() + { + $client = new TestClient(); + + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + + $client->setServerParameter('HTTP_HOST', 'testhost'); + $this->assertEquals('testhost', $client->getServerParameter('HTTP_HOST')); + + $client->setServerParameter('HTTP_USER_AGENT', 'testua'); + $this->assertEquals('testua', $client->getServerParameter('HTTP_USER_AGENT')); + } + + public function testSetServerParameterInRequest() + { + $client = new TestClient(); + + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + + $client->request('GET', 'https://www.example.com/https/www.example.com', array(), array(), array( + 'HTTP_HOST' => 'testhost', + 'HTTP_USER_AGENT' => 'testua', + 'HTTPS' => false, + 'NEW_SERVER_KEY' => 'new-server-key-value', + )); + + $this->assertEquals('localhost', $client->getServerParameter('HTTP_HOST')); + $this->assertEquals('Symfony2 BrowserKit', $client->getServerParameter('HTTP_USER_AGENT')); + + $this->assertEquals('http://testhost/https/www.example.com', $client->getRequest()->getUri()); + + $server = $client->getRequest()->getServer(); + + $this->assertArrayHasKey('HTTP_USER_AGENT', $server); + $this->assertEquals('testua', $server['HTTP_USER_AGENT']); + + $this->assertArrayHasKey('HTTP_HOST', $server); + $this->assertEquals('testhost', $server['HTTP_HOST']); + + $this->assertArrayHasKey('NEW_SERVER_KEY', $server); + $this->assertEquals('new-server-key-value', $server['NEW_SERVER_KEY']); + + $this->assertArrayHasKey('HTTPS', $server); + $this->assertFalse($server['HTTPS']); + } + + public function testInternalRequest() + { + $client = new TestClient(); + + $client->request('GET', 'https://www.example.com/https/www.example.com', array(), array(), array( + 'HTTP_HOST' => 'testhost', + 'HTTP_USER_AGENT' => 'testua', + 'HTTPS' => false, + 'NEW_SERVER_KEY' => 'new-server-key-value', + )); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Request', $client->getInternalRequest()); + } + + public function testInternalRequestNull() + { + $client = new TestClient(); + $this->assertNull($client->getInternalRequest()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php new file mode 100644 index 0000000..54a84b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\BrowserKit\Response; + +class CookieJarTest extends \PHPUnit_Framework_TestCase +{ + public function testSetGet() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie = new Cookie('foo', 'bar')); + + $this->assertEquals($cookie, $cookieJar->get('foo'), '->set() sets a cookie'); + + $this->assertNull($cookieJar->get('foobar'), '->get() returns null if the cookie does not exist'); + + $cookieJar->set($cookie = new Cookie('foo', 'bar', time() - 86400)); + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + } + + public function testExpire() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie = new Cookie('foo', 'bar')); + $cookieJar->expire('foo'); + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + } + + public function testAll() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar')); + $cookieJar->set($cookie2 = new Cookie('bar', 'foo')); + + $this->assertEquals(array($cookie1, $cookie2), $cookieJar->all(), '->all() returns all cookies in the jar'); + } + + public function testClear() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar')); + $cookieJar->set($cookie2 = new Cookie('bar', 'foo')); + + $cookieJar->clear(); + + $this->assertEquals(array(), $cookieJar->all(), '->clear() expires all cookies'); + } + + public function testUpdateFromResponse() + { + $response = new Response('', 200, array('Set-Cookie' => 'foo=foo')); + + $cookieJar = new CookieJar(); + $cookieJar->updateFromResponse($response); + + $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromResponse() updates cookies from a Response objects'); + } + + public function testUpdateFromSetCookie() + { + $setCookies = array('foo=foo'); + + $cookieJar = new CookieJar(); + $cookieJar->set(new Cookie('bar', 'bar')); + $cookieJar->updateFromSetCookie($setCookies); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('foo')); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $cookieJar->get('bar')); + $this->assertEquals('foo', $cookieJar->get('foo')->getValue(), '->updateFromSetCookie() updates cookies from a Set-Cookie header'); + $this->assertEquals('bar', $cookieJar->get('bar')->getValue(), '->updateFromSetCookie() keeps existing cookies'); + } + + public function testUpdateFromEmptySetCookie() + { + $cookieJar = new CookieJar(); + $cookieJar->updateFromSetCookie(array('')); + $this->assertEquals(array(), $cookieJar->all()); + } + + public function testUpdateFromSetCookieWithMultipleCookies() + { + $timestamp = time() + 3600; + $date = gmdate('D, d M Y H:i:s \G\M\T', $timestamp); + $setCookies = array(sprintf('foo=foo; expires=%s; domain=.symfony.com; path=/, bar=bar; domain=.blog.symfony.com, PHPSESSID=id; expires=%s', $date, $date)); + + $cookieJar = new CookieJar(); + $cookieJar->updateFromSetCookie($setCookies); + + $fooCookie = $cookieJar->get('foo', '/', '.symfony.com'); + $barCookie = $cookieJar->get('bar', '/', '.blog.symfony.com'); + $phpCookie = $cookieJar->get('PHPSESSID'); + + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $fooCookie); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $barCookie); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Cookie', $phpCookie); + $this->assertEquals('foo', $fooCookie->getValue()); + $this->assertEquals('bar', $barCookie->getValue()); + $this->assertEquals('id', $phpCookie->getValue()); + $this->assertEquals($timestamp, $fooCookie->getExpiresTime()); + $this->assertNull($barCookie->getExpiresTime()); + $this->assertEquals($timestamp, $phpCookie->getExpiresTime()); + } + + /** + * @dataProvider provideAllValuesValues + */ + public function testAllValues($uri, $values) + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo_nothing', 'foo')); + $cookieJar->set($cookie2 = new Cookie('foo_expired', 'foo', time() - 86400)); + $cookieJar->set($cookie3 = new Cookie('foo_path', 'foo', null, '/foo')); + $cookieJar->set($cookie4 = new Cookie('foo_domain', 'foo', null, '/', '.example.com')); + $cookieJar->set($cookie4 = new Cookie('foo_strict_domain', 'foo', null, '/', '.www4.example.com')); + $cookieJar->set($cookie5 = new Cookie('foo_secure', 'foo', null, '/', '', true)); + + $this->assertEquals($values, array_keys($cookieJar->allValues($uri)), '->allValues() returns the cookie for a given URI'); + } + + public function provideAllValuesValues() + { + return array( + array('http://www.example.com', array('foo_nothing', 'foo_domain')), + array('http://www.example.com/', array('foo_nothing', 'foo_domain')), + array('http://foo.example.com/', array('foo_nothing', 'foo_domain')), + array('http://foo.example1.com/', array('foo_nothing')), + array('https://foo.example.com/', array('foo_nothing', 'foo_secure', 'foo_domain')), + array('http://www.example.com/foo/bar', array('foo_nothing', 'foo_path', 'foo_domain')), + array('http://www4.example.com/', array('foo_nothing', 'foo_domain', 'foo_strict_domain')), + ); + } + + public function testEncodedValues() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true)); + + $this->assertEquals(array('foo' => 'bar=baz'), $cookieJar->allValues('/')); + $this->assertEquals(array('foo' => 'bar%3Dbaz'), $cookieJar->allRawValues('/')); + } + + public function testCookieExpireWithSameNameButDifferentPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar')); + $cookieJar->expire('foo', '/foo'); + + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array(), $cookieJar->allValues('http://example.com/foo')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar')); + } + + public function testCookieExpireWithNullPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/')); + $cookieJar->expire('foo', null); + + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + } + + public function testCookieExpireWithDomain() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo', 'http://example2.com/')); + $cookieJar->expire('foo', '/foo', 'http://example2.com/'); + + $this->assertNull($cookieJar->get('foo'), '->get() returns null if the cookie is expired'); + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example2.com/'))); + } + + public function testCookieWithSameNameButDifferentPaths() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/foo')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/bar')); + + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://example.com/foo')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://example.com/bar')); + } + + public function testCookieWithSameNameButDifferentDomains() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar1', null, '/', 'foo.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo', 'bar2', null, '/', 'bar.example.com')); + + $this->assertEquals(array(), array_keys($cookieJar->allValues('http://example.com/'))); + $this->assertEquals(array('foo' => 'bar1'), $cookieJar->allValues('http://foo.example.com/')); + $this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://bar.example.com/')); + } + + public function testCookieGetWithSubdomain() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/', '.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo1', 'bar', null, '/', 'test.example.com')); + + $this->assertEquals($cookie1, $cookieJar->get('foo', '/', 'foo.example.com')); + $this->assertEquals($cookie1, $cookieJar->get('foo', '/', 'example.com')); + $this->assertEquals($cookie2, $cookieJar->get('foo1', '/', 'test.example.com')); + } + + public function testCookieGetWithWrongSubdomain() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo1', 'bar', null, '/', 'test.example.com')); + + $this->assertNull($cookieJar->get('foo1', '/', 'foo.example.com')); + } + + public function testCookieGetWithSubdirectory() + { + $cookieJar = new CookieJar(); + $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/test', '.example.com')); + $cookieJar->set($cookie2 = new Cookie('foo1', 'bar1', null, '/', '.example.com')); + + $this->assertNull($cookieJar->get('foo', '/', '.example.com')); + $this->assertNull($cookieJar->get('foo', '/bar', '.example.com')); + $this->assertEquals($cookie1, $cookieJar->get('foo', '/test', 'example.com')); + $this->assertEquals($cookie2, $cookieJar->get('foo1', '/', 'example.com')); + $this->assertEquals($cookie2, $cookieJar->get('foo1', '/bar', 'example.com')); + } + + public function testCookieWithWildcardDomain() + { + $cookieJar = new CookieJar(); + $cookieJar->set(new Cookie('foo', 'bar', null, '/', '.example.com')); + + $this->assertEquals(array('foo' => 'bar'), $cookieJar->allValues('http://www.example.com')); + $this->assertEmpty($cookieJar->allValues('http://wwwexample.com')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieTest.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieTest.php new file mode 100644 index 0000000..61b364e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/CookieTest.php @@ -0,0 +1,188 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Cookie; + +class CookieTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTestsForToFromString + */ + public function testToFromString($cookie, $url = null) + { + $this->assertEquals($cookie, (string) Cookie::fromString($cookie, $url)); + } + + public function getTestsForToFromString() + { + return array( + array('foo=bar; path=/'), + array('foo=bar; path=/foo'), + array('foo=bar; domain=google.com; path=/'), + array('foo=bar; domain=example.com; path=/; secure', 'https://example.com/'), + array('foo=bar; path=/; httponly'), + array('foo=bar; domain=google.com; path=/foo; secure; httponly', 'https://google.com/'), + array('foo=bar=baz; path=/'), + array('foo=bar%3Dbaz; path=/'), + ); + } + + public function testFromStringIgnoreSecureFlag() + { + $this->assertFalse(Cookie::fromString('foo=bar; secure')->isSecure()); + $this->assertFalse(Cookie::fromString('foo=bar; secure', 'http://example.com/')->isSecure()); + } + + /** + * @dataProvider getExpireCookieStrings + */ + public function testFromStringAcceptsSeveralExpiresDateFormats($cookie) + { + $this->assertEquals(1596185377, Cookie::fromString($cookie)->getExpiresTime()); + } + + public function getExpireCookieStrings() + { + return array( + array('foo=bar; expires=Fri, 31-Jul-2020 08:49:37 GMT'), + array('foo=bar; expires=Fri, 31 Jul 2020 08:49:37 GMT'), + array('foo=bar; expires=Fri, 31-07-2020 08:49:37 GMT'), + array('foo=bar; expires=Fri, 31-07-20 08:49:37 GMT'), + array('foo=bar; expires=Friday, 31-Jul-20 08:49:37 GMT'), + array('foo=bar; expires=Fri Jul 31 08:49:37 2020'), + array('foo=bar; expires=\'Fri Jul 31 08:49:37 2020\''), + array('foo=bar; expires=Friday July 31st 2020, 08:49:37 GMT'), + ); + } + + public function testFromStringWithCapitalization() + { + $this->assertEquals('Foo=Bar; path=/', (string) Cookie::fromString('Foo=Bar')); + $this->assertEquals('foo=bar; expires=Fri, 31 Dec 2010 23:59:59 GMT; path=/', (string) Cookie::fromString('foo=bar; Expires=Fri, 31 Dec 2010 23:59:59 GMT')); + $this->assertEquals('foo=bar; domain=www.example.org; path=/; httponly', (string) Cookie::fromString('foo=bar; DOMAIN=www.example.org; HttpOnly')); + } + + public function testFromStringWithUrl() + { + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::fromString('foo=bar', 'http://www.example.com/')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::fromString('foo=bar', 'http://www.example.com')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::fromString('foo=bar', 'http://www.example.com?foo')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/foo', (string) Cookie::fromString('foo=bar', 'http://www.example.com/foo/bar')); + $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::fromString('foo=bar; path=/', 'http://www.example.com/foo/bar')); + $this->assertEquals('foo=bar; domain=www.myotherexample.com; path=/', (string) Cookie::fromString('foo=bar; domain=www.myotherexample.com', 'http://www.example.com/')); + } + + public function testFromStringThrowsAnExceptionIfCookieIsNotValid() + { + $this->setExpectedException('InvalidArgumentException'); + Cookie::fromString('foo'); + } + + public function testFromStringThrowsAnExceptionIfCookieDateIsNotValid() + { + $this->setExpectedException('InvalidArgumentException'); + Cookie::fromString('foo=bar; expires=Flursday July 31st 2020, 08:49:37 GMT'); + } + + public function testFromStringThrowsAnExceptionIfUrlIsNotValid() + { + $this->setExpectedException('InvalidArgumentException'); + Cookie::fromString('foo=bar', 'foobar'); + } + + public function testGetName() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertEquals('foo', $cookie->getName(), '->getName() returns the cookie name'); + } + + public function testGetValue() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertEquals('bar', $cookie->getValue(), '->getValue() returns the cookie value'); + + $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value + $this->assertEquals('bar=baz', $cookie->getValue(), '->getValue() returns the urldecoded cookie value'); + } + + public function testGetRawValue() + { + $cookie = new Cookie('foo', 'bar=baz'); // decoded value + $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the urlencoded cookie value'); + $cookie = new Cookie('foo', 'bar%3Dbaz', null, '/', '', false, true, true); // raw value + $this->assertEquals('bar%3Dbaz', $cookie->getRawValue(), '->getRawValue() returns the non-urldecoded cookie value'); + } + + public function testGetPath() + { + $cookie = new Cookie('foo', 'bar', 0); + $this->assertEquals('/', $cookie->getPath(), '->getPath() returns / is no path is defined'); + + $cookie = new Cookie('foo', 'bar', 0, '/foo'); + $this->assertEquals('/foo', $cookie->getPath(), '->getPath() returns the cookie path'); + } + + public function testGetDomain() + { + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com'); + $this->assertEquals('foo.com', $cookie->getDomain(), '->getDomain() returns the cookie domain'); + } + + public function testIsSecure() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertFalse($cookie->isSecure(), '->isSecure() returns false if not defined'); + + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', true); + $this->assertTrue($cookie->isSecure(), '->isSecure() returns the cookie secure flag'); + } + + public function testIsHttponly() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns false if not defined'); + + $cookie = new Cookie('foo', 'bar', 0, '/', 'foo.com', false, true); + $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns the cookie httponly flag'); + } + + public function testGetExpiresTime() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertNull($cookie->getExpiresTime(), '->getExpiresTime() returns the expires time'); + + $cookie = new Cookie('foo', 'bar', $time = time() - 86400); + $this->assertEquals($time, $cookie->getExpiresTime(), '->getExpiresTime() returns the expires time'); + } + + public function testIsExpired() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertFalse($cookie->isExpired(), '->isExpired() returns false when the cookie never expires (null as expires time)'); + + $cookie = new Cookie('foo', 'bar', time() - 86400); + $this->assertTrue($cookie->isExpired(), '->isExpired() returns true when the cookie is expired'); + + $cookie = new Cookie('foo', 'bar', 0); + $this->assertFalse($cookie->isExpired()); + } + + /** + * @expectedException UnexpectedValueException + * @expectedExceptionMessage The cookie expiration time "string" is not valid. + */ + public function testConstructException() + { + $cookie = new Cookie('foo', 'bar', 'string'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/HistoryTest.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/HistoryTest.php new file mode 100644 index 0000000..d6d830e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/HistoryTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\Request; + +class HistoryTest extends \PHPUnit_Framework_TestCase +{ + public function testAdd() + { + $history = new History(); + $history->add(new Request('http://www.example1.com/', 'get')); + $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->add() adds a request to the history'); + + $history->add(new Request('http://www.example2.com/', 'get')); + $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history'); + + $history->add(new Request('http://www.example3.com/', 'get')); + $history->back(); + $history->add(new Request('http://www.example4.com/', 'get')); + $this->assertSame('http://www.example4.com/', $history->current()->getUri(), '->add() adds a request to the history'); + + $history->back(); + $this->assertSame('http://www.example2.com/', $history->current()->getUri(), '->add() adds a request to the history'); + } + + public function testClearIsEmpty() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + + $this->assertFalse($history->isEmpty(), '->isEmpty() returns false if the history is not empty'); + + $history->clear(); + + $this->assertTrue($history->isEmpty(), '->isEmpty() true if the history is empty'); + } + + public function testCurrent() + { + $history = new History(); + + try { + $history->current(); + $this->fail('->current() throws a \LogicException if the history is empty'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->current() throws a \LogicException if the history is empty'); + } + + $history->add(new Request('http://www.example.com/', 'get')); + + $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->current() returns the current request in the history'); + } + + public function testBack() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + + try { + $history->back(); + $this->fail('->back() throws a \LogicException if the history is already on the first page'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->current() throws a \LogicException if the history is already on the first page'); + } + + $history->add(new Request('http://www.example1.com/', 'get')); + $history->back(); + + $this->assertSame('http://www.example.com/', $history->current()->getUri(), '->back() returns the previous request in the history'); + } + + public function testForward() + { + $history = new History(); + $history->add(new Request('http://www.example.com/', 'get')); + $history->add(new Request('http://www.example1.com/', 'get')); + + try { + $history->forward(); + $this->fail('->forward() throws a \LogicException if the history is already on the last page'); + } catch (\Exception $e) { + $this->assertInstanceOf('LogicException', $e, '->forward() throws a \LogicException if the history is already on the last page'); + } + + $history->back(); + $history->forward(); + + $this->assertSame('http://www.example1.com/', $history->current()->getUri(), '->forward() returns the next request in the history'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/RequestTest.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/RequestTest.php new file mode 100644 index 0000000..b75b5fb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/RequestTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Request; + +class RequestTest extends \PHPUnit_Framework_TestCase +{ + public function testGetUri() + { + $request = new Request('http://www.example.com/', 'get'); + $this->assertEquals('http://www.example.com/', $request->getUri(), '->getUri() returns the URI of the request'); + } + + public function testGetMethod() + { + $request = new Request('http://www.example.com/', 'get'); + $this->assertEquals('get', $request->getMethod(), '->getMethod() returns the method of the request'); + } + + public function testGetParameters() + { + $request = new Request('http://www.example.com/', 'get', array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getParameters(), '->getParameters() returns the parameters of the request'); + } + + public function testGetFiles() + { + $request = new Request('http://www.example.com/', 'get', array(), array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getFiles(), '->getFiles() returns the uploaded files of the request'); + } + + public function testGetCookies() + { + $request = new Request('http://www.example.com/', 'get', array(), array(), array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getCookies(), '->getCookies() returns the cookies of the request'); + } + + public function testGetServer() + { + $request = new Request('http://www.example.com/', 'get', array(), array(), array(), array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $request->getServer(), '->getServer() returns the server parameters of the request'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php new file mode 100644 index 0000000..bfe3cd5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Tests/ResponseTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\BrowserKit\Tests; + +use Symfony\Component\BrowserKit\Response; + +class ResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testGetUri() + { + $response = new Response('foo'); + $this->assertEquals('foo', $response->getContent(), '->getContent() returns the content of the response'); + } + + public function testGetStatus() + { + $response = new Response('foo', 304); + $this->assertEquals('304', $response->getStatus(), '->getStatus() returns the status of the response'); + } + + public function testGetHeaders() + { + $response = new Response('foo', 200, array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $response->getHeaders(), '->getHeaders() returns the headers of the response'); + } + + public function testGetHeader() + { + $response = new Response('foo', 200, array( + 'Content-Type' => 'text/html', + 'Set-Cookie' => array('foo=bar', 'bar=foo'), + )); + + $this->assertEquals('text/html', $response->getHeader('Content-Type'), '->getHeader() returns a header of the response'); + $this->assertEquals('text/html', $response->getHeader('content-type'), '->getHeader() returns a header of the response'); + $this->assertEquals('text/html', $response->getHeader('content_type'), '->getHeader() returns a header of the response'); + $this->assertEquals('foo=bar', $response->getHeader('Set-Cookie'), '->getHeader() returns the first header value'); + $this->assertEquals(array('foo=bar', 'bar=foo'), $response->getHeader('Set-Cookie', false), '->getHeader() returns all header values if first is false'); + + $this->assertNull($response->getHeader('foo'), '->getHeader() returns null if the header is not defined'); + $this->assertEquals(array(), $response->getHeader('foo', false), '->getHeader() returns an empty array if the header is not defined and first is set to false'); + } + + public function testMagicToString() + { + $response = new Response('foo', 304, array('foo' => 'bar')); + + $this->assertEquals("foo: bar\n\nfoo", $response->__toString(), '->__toString() returns the headers and the content as a string'); + } + + public function testMagicToStringWithMultipleSetCookieHeader() + { + $headers = array( + 'content-type' => 'text/html; charset=utf-8', + 'set-cookie' => array('foo=bar', 'bar=foo'), + ); + + $expected = 'content-type: text/html; charset=utf-8'."\n"; + $expected .= 'set-cookie: foo=bar'."\n"; + $expected .= 'set-cookie: bar=foo'."\n\n"; + $expected .= 'foo'; + + $response = new Response('foo', 304, $headers); + + $this->assertEquals($expected, $response->__toString(), '->__toString() returns the headers and the content as a string'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/composer.json b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/composer.json new file mode 100644 index 0000000..a63ec96 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/browser-kit", + "type": "library", + "description": "Symfony BrowserKit Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/dom-crawler": "~2.8|~3.0" + }, + "require-dev": { + "symfony/process": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0" + }, + "suggest": { + "symfony/process": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/phpunit.xml.dist new file mode 100644 index 0000000..d76b2b9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ApcClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ApcClassLoader.php new file mode 100644 index 0000000..12d94d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ApcClassLoader.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3. + * + * It expects an object implementing a findFile method to find the file. This + * allows using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * use Symfony\Component\ClassLoader\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new ApcClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + */ +class ApcClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + protected $decorated; + + /** + * Constructor. + * + * @param string $prefix The APC namespace prefix to use. + * @param object $decorated A class loader object that implements the findFile() method. + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!extension_loaded('apc')) { + throw new \RuntimeException('Unable to use ApcClassLoader as APC is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to APC. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + if (false === $file = apc_fetch($this->prefix.$class)) { + apc_store($this->prefix.$class, $file = $this->decorated->findFile($class)); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/CHANGELOG.md new file mode 100644 index 0000000..64ef8d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/CHANGELOG.md @@ -0,0 +1,36 @@ +CHANGELOG +========= + +3.0.0 +----- + + * The DebugClassLoader class has been removed + * The DebugUniversalClassLoader class has been removed + * The UniversalClassLoader class has been removed + * The ApcUniversalClassLoader class has been removed + +2.4.0 +----- + + * deprecated the UniversalClassLoader in favor of the ClassLoader class instead + * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead + * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component + * deprecated the DebugClassLoader as it has been moved to the Debug component instead + +2.3.0 +----- + + * added a WinCacheClassLoader for WinCache + +2.1.0 +----- + + * added a DebugClassLoader able to wrap any autoloader providing a findFile + method + * added a new ApcClassLoader and XcacheClassLoader using composition to wrap + other loaders + * added a new ClassLoader which does not distinguish between namespaced and + pear-like classes (as the PEAR convention is a subset of PSR-0) and + supports using Composer's namespace maps + * added a class map generator + * added support for loading globally-installed PEAR packages diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php new file mode 100644 index 0000000..9a500bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php @@ -0,0 +1,365 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * ClassCollectionLoader. + * + * @author Fabien Potencier + */ +class ClassCollectionLoader +{ + private static $loaded; + private static $seen; + private static $useTokenizer = true; + + /** + * Loads a list of classes and caches them in one big file. + * + * @param array $classes An array of classes to load + * @param string $cacheDir A cache directory + * @param string $name The cache name prefix + * @param bool $autoReload Whether to flush the cache when the cache is stale or not + * @param bool $adaptive Whether to remove already declared classes or not + * @param string $extension File extension of the resulting file + * + * @throws \InvalidArgumentException When class can't be loaded + */ + public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') + { + // each $name can only be loaded once per PHP process + if (isset(self::$loaded[$name])) { + return; + } + + self::$loaded[$name] = true; + + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + + if ($adaptive) { + // don't include already declared classes + $classes = array_diff($classes, $declared); + + // the cache is different depending on which classes are already declared + $name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5); + } + + $classes = array_unique($classes); + + $cache = $cacheDir.'/'.$name.$extension; + + // auto-reload + $reload = false; + if ($autoReload) { + $metadata = $cache.'.meta'; + if (!is_file($metadata) || !is_file($cache)) { + $reload = true; + } else { + $time = filemtime($cache); + $meta = unserialize(file_get_contents($metadata)); + + sort($meta[1]); + sort($classes); + + if ($meta[1] != $classes) { + $reload = true; + } else { + foreach ($meta[0] as $resource) { + if (!is_file($resource) || filemtime($resource) > $time) { + $reload = true; + + break; + } + } + } + } + } + + if (!$reload && is_file($cache)) { + require_once $cache; + + return; + } + + $files = array(); + $content = ''; + foreach (self::getOrderedClasses($classes) as $class) { + if (in_array($class->getName(), $declared)) { + continue; + } + + $files[] = $class->getFileName(); + + $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($class->getFileName())); + + // fakes namespace declaration for global code + if (!$class->inNamespace()) { + $c = "\nnamespace\n{\n".$c."\n}\n"; + } + + $c = self::fixNamespaceDeclarations('getName()])) { + return array(); + } + + self::$seen[$class->getName()] = true; + + $classes = array($class); + $parent = $class; + while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { + self::$seen[$parent->getName()] = true; + + array_unshift($classes, $parent); + } + + $traits = array(); + + foreach ($classes as $c) { + foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) { + if ($trait !== $c) { + $traits[] = $trait; + } + } + } + + return array_merge(self::getInterfaces($class), $traits, $classes); + } + + private static function getInterfaces(\ReflectionClass $class) + { + $classes = array(); + + foreach ($class->getInterfaces() as $interface) { + $classes = array_merge($classes, self::getInterfaces($interface)); + } + + if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) { + self::$seen[$class->getName()] = true; + + $classes[] = $class; + } + + return $classes; + } + + private static function computeTraitDeps(\ReflectionClass $class) + { + $traits = $class->getTraits(); + $deps = array($class->getName() => $traits); + while ($trait = array_pop($traits)) { + if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { + self::$seen[$trait->getName()] = true; + $traitDeps = $trait->getTraits(); + $deps[$trait->getName()] = $traitDeps; + $traits = array_merge($traits, $traitDeps); + } + } + + return $deps; + } + + /** + * Dependencies resolution. + * + * This function does not check for circular dependencies as it should never + * occur with PHP traits. + * + * @param array $tree The dependency tree + * @param \ReflectionClass $node The node + * @param \ArrayObject $resolved An array of already resolved dependencies + * @param \ArrayObject $unresolved An array of dependencies to be resolved + * + * @return \ArrayObject The dependencies for the given node + * + * @throws \RuntimeException if a circular dependency is detected + */ + private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null) + { + if (null === $resolved) { + $resolved = new \ArrayObject(); + } + if (null === $unresolved) { + $unresolved = new \ArrayObject(); + } + $nodeName = $node->getName(); + + if (isset($tree[$nodeName])) { + $unresolved[$nodeName] = $node; + foreach ($tree[$nodeName] as $dependency) { + if (!$resolved->offsetExists($dependency->getName())) { + self::resolveDependencies($tree, $dependency, $resolved, $unresolved); + } + } + $resolved[$nodeName] = $node; + unset($unresolved[$nodeName]); + } + + return $resolved; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassLoader.php new file mode 100644 index 0000000..a506dc0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassLoader.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * ClassLoader implements an PSR-0 class loader. + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new ClassLoader(); + * + * // register classes with namespaces + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (e.g. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + private $prefixes = array(); + private $fallbackDirs = array(); + private $useIncludePath = false; + + /** + * Returns prefixes. + * + * @return array + */ + public function getPrefixes() + { + return $this->prefixes; + } + + /** + * Returns fallback directories. + * + * @return array + */ + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + /** + * Adds prefixes. + * + * @param array $prefixes Prefixes to add + */ + public function addPrefixes(array $prefixes) + { + foreach ($prefixes as $prefix => $path) { + $this->addPrefix($prefix, $path); + } + } + + /** + * Registers a set of classes. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function addPrefix($prefix, $paths) + { + if (!$prefix) { + foreach ((array) $paths as $path) { + $this->fallbackDirs[] = $path; + } + + return; + } + if (isset($this->prefixes[$prefix])) { + if (is_array($paths)) { + $this->prefixes[$prefix] = array_unique(array_merge( + $this->prefixes[$prefix], + $paths + )); + } elseif (!in_array($paths, $this->prefixes[$prefix])) { + $this->prefixes[$prefix][] = $paths; + } + } else { + $this->prefixes[$prefix] = array_unique((array) $paths); + } + } + + /** + * Turns on searching the include for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = (bool) $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)).DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if ($class === strstr($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) { + return $dir.DIRECTORY_SEPARATOR.$classPath; + } + } + } + } + + foreach ($this->fallbackDirs as $dir) { + if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) { + return $dir.DIRECTORY_SEPARATOR.$classPath; + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { + return $file; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassMapGenerator.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassMapGenerator.php new file mode 100644 index 0000000..3f3dc18 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassMapGenerator.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * ClassMapGenerator. + * + * @author Gyula Sallai + */ +class ClassMapGenerator +{ + /** + * Generate a class map file. + * + * @param array|string $dirs Directories or a single path to search in + * @param string $file The name of the class map file + */ + public static function dump($dirs, $file) + { + $dirs = (array) $dirs; + $maps = array(); + + foreach ($dirs as $dir) { + $maps = array_merge($maps, static::createMap($dir)); + } + + file_put_contents($file, sprintf('isFile()) { + continue; + } + + $path = $file->getRealPath(); + + if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') { + continue; + } + + $classes = self::findClasses($path); + + foreach ($classes as $class) { + $map[$class] = $path; + } + } + + return $map; + } + + /** + * Extract the classes in the given file. + * + * @param string $path The file to check + * + * @return array The found classes + */ + private static function findClasses($path) + { + $contents = file_get_contents($path); + $tokens = token_get_all($contents); + + $classes = array(); + + $namespace = ''; + for ($i = 0, $max = count($tokens); $i < $max; ++$i) { + $token = $tokens[$i]; + + if (is_string($token)) { + continue; + } + + $class = ''; + + switch ($token[0]) { + case T_NAMESPACE: + $namespace = ''; + // If there is a namespace, extract it + while (($t = $tokens[++$i]) && is_array($t)) { + if (in_array($t[0], array(T_STRING, T_NS_SEPARATOR))) { + $namespace .= $t[1]; + } + } + $namespace .= '\\'; + break; + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + // Skip usage of ::class constant + $isClassConstant = false; + for ($j = $i - 1; $j > 0; --$j) { + if (is_string($tokens[$j])) { + break; + } + + if (T_DOUBLE_COLON === $tokens[$j][0]) { + $isClassConstant = true; + break; + } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) { + break; + } + } + + if ($isClassConstant) { + continue; + } + + // Find the classname + while (($t = $tokens[++$i]) && is_array($t)) { + if (T_STRING === $t[0]) { + $class .= $t[1]; + } elseif ($class !== '' && T_WHITESPACE == $t[0]) { + break; + } + } + + $classes[] = ltrim($namespace.$class, '\\'); + break; + default: + break; + } + } + + return $classes; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/MapClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/MapClassLoader.php new file mode 100644 index 0000000..1d8bcc2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/MapClassLoader.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * A class loader that uses a mapping file to look up paths. + * + * @author Fabien Potencier + */ +class MapClassLoader +{ + private $map = array(); + + /** + * Constructor. + * + * @param array $map A map where keys are classes and values the absolute file path + */ + public function __construct(array $map) + { + $this->map = $map; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + */ + public function loadClass($class) + { + if (isset($this->map[$class])) { + require $this->map[$class]; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (isset($this->map[$class])) { + return $this->map[$class]; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php new file mode 100644 index 0000000..84647b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * A PSR-4 compatible class loader. + * + * See http://www.php-fig.org/psr/psr-4/ + * + * @author Alexander M. Turek + */ +class Psr4ClassLoader +{ + /** + * @var array + */ + private $prefixes = array(); + + /** + * @param string $prefix + * @param string $baseDir + */ + public function addPrefix($prefix, $baseDir) + { + $prefix = trim($prefix, '\\').'\\'; + $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + $this->prefixes[] = array($prefix, $baseDir); + } + + /** + * @param string $class + * + * @return string|null + */ + public function findFile($class) + { + $class = ltrim($class, '\\'); + + foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) { + if (0 === strpos($class, $currentPrefix)) { + $classWithoutPrefix = substr($class, strlen($currentPrefix)); + $file = $currentBaseDir.str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + } + + /** + * @param string $class + * + * @return bool + */ + public function loadClass($class) + { + $file = $this->findFile($class); + if (null !== $file) { + require $file; + + return true; + } + + return false; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Removes this instance from the registered autoloaders. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/README.md b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/README.md new file mode 100644 index 0000000..45093c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/README.md @@ -0,0 +1,85 @@ +ClassLoader Component +===================== + +ClassLoader loads your project classes automatically if they follow some +standard PHP conventions. + +The ClassLoader object is able to autoload classes that implement the PSR-0 +standard or the PEAR naming convention. + +First, register the autoloader: + +```php +require_once __DIR__.'/src/Symfony/Component/ClassLoader/ClassLoader.php'; + +use Symfony\Component\ClassLoader\ClassLoader; + +$loader = new ClassLoader(); +$loader->register(); +``` + +Then, register some namespaces with the `addPrefix()` method: + +```php +$loader->addPrefix('Symfony', __DIR__.'/src'); +$loader->addPrefix('Monolog', __DIR__.'/vendor/monolog/src'); +``` + +The `addPrefix()` method takes a namespace prefix and a path where to look for +the classes as arguments. + +You can also register a sub-namespaces: + +```php +$loader->addPrefix('Doctrine\\Common', __DIR__.'/vendor/doctrine-common/lib'); +``` + +The order of registration is significant and the first registered namespace +takes precedence over later registered one. + +You can also register more than one path for a given namespace: + +```php +$loader->addPrefix('Symfony', array(__DIR__.'/src', __DIR__.'/symfony/src')); +``` + +Alternatively, you can use the `addPrefixes()` method to register more +than one namespace at once: + +```php +$loader->addPrefixes(array( + 'Symfony' => array(__DIR__.'/src', __DIR__.'/symfony/src'), + 'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib', + 'Doctrine' => __DIR__.'/vendor/doctrine/lib', + 'Monolog' => __DIR__.'/vendor/monolog/src', +)); +``` + +For better performance, you can use the APC class loader: + +```php +require_once __DIR__.'/src/Symfony/Component/ClassLoader/ClassLoader.php'; +require_once __DIR__.'/src/Symfony/Component/ClassLoader/ApcClassLoader.php'; + +use Symfony\Component\ClassLoader\ClassLoader; +use Symfony\Component\ClassLoader\ApcClassLoader; + +$loader = new ClassLoader(); +$loader->addPrefix('Symfony', __DIR__.'/src'); + +$loader = new ApcClassLoader('apc.prefix.', $loader); +$loader->register(); +``` + +Furthermore, the component provides tools to aggregate classes into a single +file, which is especially useful to improve performance on servers that do not +provide byte caches. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/ClassLoader/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ApcClassLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ApcClassLoaderTest.php new file mode 100644 index 0000000..e929d0e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ApcClassLoaderTest.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ApcClassLoader; +use Symfony\Component\ClassLoader\ClassLoader; + +/** + * @requires extension apc + */ +class ApcClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) { + apc_clear_cache('user'); + } else { + $this->markTestSkipped('APC is not enabled.'); + } + } + + protected function tearDown() + { + if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) { + apc_clear_cache('user'); + } + } + + public function testConstructor() + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + + $loader = new ApcClassLoader('test.prefix.', $loader); + + $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apc_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument'); + } + + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + + $loader = new ApcClassLoader('test.prefix.', $loader); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return array( + array('\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'), + array('Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'), + ); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Apc_Pearlike_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback')); + + $loader = new ApcClassLoader('test.prefix.fallback', $loader); + $loader->loadClass($testClassName); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return array( + array('\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'), + array('Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'), + array('\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'), + array('Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'), + ); + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($namespaces); + + $loader = new ApcClassLoader('test.prefix.collision.', $loader); + $loader->loadClass($className); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return array( + array( + array( + 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + ), + 'Apc\NamespaceCollision\A\Foo', + '->loadClass() loads NamespaceCollision\A\Foo from alpha.', + ), + array( + array( + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + ), + 'Apc\NamespaceCollision\A\Bar', + '->loadClass() loads NamespaceCollision\A\Bar from alpha.', + ), + array( + array( + 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + ), + 'Apc\NamespaceCollision\A\B\Foo', + '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', + ), + array( + array( + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + 'Apc\\NamespaceCollision\\A' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + ), + 'Apc\NamespaceCollision\A\B\Bar', + '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', + ), + ); + } + + /** + * @dataProvider getLoadClassPrefixCollisionTests + */ + public function testLoadClassPrefixCollision($prefixes, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($prefixes); + + $loader = new ApcClassLoader('test.prefix.collision.', $loader); + $loader->loadClass($className); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassPrefixCollisionTests() + { + return array( + array( + array( + 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + ), + 'ApcPrefixCollision_A_Foo', + '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.', + ), + array( + array( + 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + ), + 'ApcPrefixCollision_A_Bar', + '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.', + ), + array( + array( + 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + ), + 'ApcPrefixCollision_A_B_Foo', + '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.', + ), + array( + array( + 'ApcPrefixCollision_A_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + 'ApcPrefixCollision_A_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + ), + 'ApcPrefixCollision_A_B_Bar', + '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.', + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php new file mode 100644 index 0000000..7af10d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassCollectionLoaderTest.php @@ -0,0 +1,274 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ClassCollectionLoader; + +require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/A.php'; + +class ClassCollectionLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testTraitDependencies() + { + require_once __DIR__.'/Fixtures/deps/traits.php'; + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', array('CTFoo')); + + $this->assertEquals( + array('TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'), + array_map(function ($class) { return $class->getName(); }, $ordered) + ); + + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', array('CTBar')); + + $this->assertEquals( + array('TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'), + array_map(function ($class) { return $class->getName(); }, $ordered) + ); + } + + /** + * @dataProvider getDifferentOrders + */ + public function testClassReordering(array $classes) + { + $expected = array( + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrders() + { + return array( + array(array( + 'ClassesWithParents\\A', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\B', + )), + array(array( + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\CInterface', + )), + array(array( + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + )), + array(array( + 'ClassesWithParents\\A', + )), + ); + } + + /** + * @dataProvider getDifferentOrdersForTraits + */ + public function testClassWithTraitsReordering(array $classes) + { + require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; + + $expected = array( + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\ATrait', + 'ClassesWithParents\\BTrait', + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\D', + 'ClassesWithParents\\E', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrdersForTraits() + { + return array( + array(array( + 'ClassesWithParents\\E', + 'ClassesWithParents\\ATrait', + )), + array(array( + 'ClassesWithParents\\E', + )), + ); + } + + public function testFixClassWithTraitsOrdering() + { + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/F.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/G.php'; + + $classes = array( + 'ClassesWithParents\\F', + 'ClassesWithParents\\G', + ); + + $expected = array( + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\F', + 'ClassesWithParents\\G', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke('Symfony\Component\ClassLoader\ClassCollectionLoader', $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + /** + * @dataProvider getFixNamespaceDeclarationsData + */ + public function testFixNamespaceDeclarations($source, $expected) + { + $this->assertEquals('assertEquals('assertEquals(<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ClassLoader; + +class ClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testGetPrefixes() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Bar', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Bas', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertArrayNotHasKey('Foo1', $prefixes); + $this->assertArrayHasKey('Bar', $prefixes); + $this->assertArrayHasKey('Bas', $prefixes); + } + + public function testGetFallbackDirs() + { + $loader = new ClassLoader(); + $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix(null, __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $fallback_dirs = $loader->getFallbackDirs(); + $this->assertCount(2, $fallback_dirs); + } + + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return array( + array('\\Namespaced2\\Foo', 'Namespaced2\\Foo', '->loadClass() loads Namespaced2\Foo class'), + array('\\Pearlike2_Foo', 'Pearlike2_Foo', '->loadClass() loads Pearlike2_Foo class'), + ); + } + + /** + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->loadClass($testClassName); + $this->assertFalse(class_exists($className), $message); + } + + public function getLoadNonexistentClassTests() + { + return array( + array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'), + ); + } + + public function testAddPrefixSingle() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(1, $prefixes['Foo']); + } + + public function testAddPrefixesSingle() + { + $loader = new ClassLoader(); + $loader->addPrefixes(array('Foo' => array('foo', 'foo'))); + $loader->addPrefixes(array('Foo' => array('foo'))); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true)); + } + + public function testAddPrefixMulti() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', 'foo'); + $loader->addPrefix('Foo', 'bar'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(2, $prefixes['Foo']); + $this->assertContains('foo', $prefixes['Foo']); + $this->assertContains('bar', $prefixes['Foo']); + } + + public function testUseIncludePath() + { + $loader = new ClassLoader(); + $this->assertFalse($loader->getUseIncludePath()); + + $this->assertNull($loader->findFile('Foo')); + + $includePath = get_include_path(); + + $loader->setUseIncludePath(true); + $this->assertTrue($loader->getUseIncludePath()); + + set_include_path(__DIR__.'/Fixtures/includepath'.PATH_SEPARATOR.$includePath); + + $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'includepath'.DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo')); + + set_include_path($includePath); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('', array(__DIR__.DIRECTORY_SEPARATOR.'Fixtures/fallback')); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return array( + array('\\Namespaced2\\Baz', 'Namespaced2\\Baz', '->loadClass() loads Namespaced2\Baz class'), + array('\\Pearlike2_Baz', 'Pearlike2_Baz', '->loadClass() loads Pearlike2_Baz class'), + array('\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'), + array('\\Pearlike2_FooBar', 'Pearlike2_FooBar', '->loadClass() loads Pearlike2_Baz class from fallback dir'), + ); + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($namespaces); + + $loader->loadClass($className); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return array( + array( + array( + 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'NamespaceCollision\C\Foo', + '->loadClass() loads NamespaceCollision\C\Foo from alpha.', + ), + array( + array( + 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'NamespaceCollision\C\Bar', + '->loadClass() loads NamespaceCollision\C\Bar from alpha.', + ), + array( + array( + 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'NamespaceCollision\C\B\Foo', + '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', + ), + array( + array( + 'NamespaceCollision\\C\\B' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\C' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'NamespaceCollision\C\B\Bar', + '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', + ), + array( + array( + 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'PrefixCollision_C_Foo', + '->loadClass() loads PrefixCollision_C_Foo from alpha.', + ), + array( + array( + 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'PrefixCollision_C_Bar', + '->loadClass() loads PrefixCollision_C_Bar from alpha.', + ), + array( + array( + 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'PrefixCollision_C_B_Foo', + '->loadClass() loads PrefixCollision_C_B_Foo from beta.', + ), + array( + array( + 'PrefixCollision_C_B_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_C_' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'PrefixCollision_C_B_Bar', + '->loadClass() loads PrefixCollision_C_B_Bar from beta.', + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.php new file mode 100644 index 0000000..6aed6ef --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/ClassMapGeneratorTest.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\ClassMapGenerator; + +class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string|null + */ + private $workspace = null; + + public function prepare_workspace() + { + $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().mt_rand(0, 1000); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + /** + * @param string $file + */ + private function clean($file) + { + if (is_dir($file) && !is_link($file)) { + $dir = new \FilesystemIterator($file); + foreach ($dir as $childFile) { + $this->clean($childFile); + } + + rmdir($file); + } else { + unlink($file); + } + } + + /** + * @dataProvider getTestCreateMapTests + */ + public function testDump($directory) + { + $this->prepare_workspace(); + + $file = $this->workspace.'/file'; + + $generator = new ClassMapGenerator(); + $generator->dump($directory, $file); + $this->assertFileExists($file); + + $this->clean($this->workspace); + } + + /** + * @dataProvider getTestCreateMapTests + */ + public function testCreateMap($directory, $expected) + { + $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); + } + + public function getTestCreateMapTests() + { + $data = array( + array(__DIR__.'/Fixtures/Namespaced', array( + 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', + 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', + 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', + 'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php', + ), + ), + array(__DIR__.'/Fixtures/beta/NamespaceCollision', array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', + 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', + )), + array(__DIR__.'/Fixtures/Pearlike', array( + 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', + 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', + 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', + 'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php', + )), + array(__DIR__.'/Fixtures/classmap', array( + 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', + 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', + 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', + )), + array(__DIR__.'/Fixtures/php5.4', array( + 'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php', + 'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', + )), + array(__DIR__.'/Fixtures/php5.5', array( + 'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php', + )), + ); + + return $data; + } + + public function testCreateMapFinderSupport() + { + $finder = new \Symfony\Component\Finder\Finder(); + $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision'); + + $this->assertEqualsNormalized(array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', + 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', + ), ClassMapGenerator::createMap($finder)); + } + + protected function assertEqualsNormalized($expected, $actual, $message = null) + { + foreach ($expected as $ns => $path) { + $expected[$ns] = str_replace('\\', '/', $path); + } + foreach ($actual as $ns => $path) { + $actual[$ns] = str_replace('\\', '/', $path); + } + $this->assertEquals($expected, $actual, $message); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Bar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Bar.php new file mode 100644 index 0000000..4259f14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Bar.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Baz.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Baz.php new file mode 100644 index 0000000..3ddb595 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Baz.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class Baz +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Foo.php new file mode 100644 index 0000000..cf0a4b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/FooBar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/FooBar.php new file mode 100644 index 0000000..bbbc815 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Namespaced/FooBar.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Pearlike/Bar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Pearlike/Bar.php new file mode 100644 index 0000000..e774cb9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/Pearlike/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php new file mode 100644 index 0000000..184a1b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php new file mode 100644 index 0000000..3892f70 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A\B; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php new file mode 100644 index 0000000..450eeb5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A\B; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php new file mode 100644 index 0000000..96f2f76 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php new file mode 100644 index 0000000..b0f9425 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/ClassesWithParents/A.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Baz.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Baz.php new file mode 100644 index 0000000..0b0bbd0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Baz.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class Baz +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Foo.php new file mode 100644 index 0000000..df5e1f4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php new file mode 100644 index 0000000..361e53d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Namespaced/WithComments.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class WithComments +{ + /** @Boolean */ + public static $loaded = true; +} + +$string = 'string should not be modified {$string}'; + +$heredoc = (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class Pearlike_WithComments +{ + /** @Boolean */ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Pearlike2/Bar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Pearlike2/Bar.php new file mode 100644 index 0000000..7f5f797 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/Pearlike2/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php new file mode 100644 index 0000000..aee6a08 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php new file mode 100644 index 0000000..c1b8dd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A\B; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php new file mode 100644 index 0000000..f5f2d72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A\B; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php new file mode 100644 index 0000000..4bb03dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +class SomeClass extends SomeParent implements SomeInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.php new file mode 100644 index 0000000..1fe5e09 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +interface SomeInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.php new file mode 100644 index 0000000..ce2f9fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/SomeParent.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +abstract class SomeParent +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php new file mode 100644 index 0000000..c7cec64 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/classmap/multipleNs.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Foo\Bar; + +class A +{ +} +class B +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.php new file mode 100644 index 0000000..82b30a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/deps/traits.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/fallback/Namespaced2/FooBar.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/fallback/Namespaced2/FooBar.php new file mode 100644 index 0000000..1036d43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/Tests/Fixtures/fallback/Namespaced2/FooBar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\Psr4ClassLoader; + +class Psr4ClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param string $className + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4' + ); + $loader->loadClass($className); + $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); + } + + /** + * @return array + */ + public function getLoadClassTests() + { + return array( + array('Acme\\DemoLib\\Foo'), + array('Acme\\DemoLib\\Class_With_Underscores'), + array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'), + array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'), + ); + } + + /** + * @param string $className + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'psr-4' + ); + $loader->loadClass($className); + $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); + } + + /** + * @return array + */ + public function getLoadNonexistentClassTests() + { + return array( + array('Acme\\DemoLib\\I_Do_Not_Exist'), + array('UnknownVendor\\SomeLib\\I_Do_Not_Exist'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php new file mode 100644 index 0000000..7ba0d9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/WinCacheClassLoader.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * WinCacheClassLoader implements a wrapping autoloader cached in WinCache. + * + * It expects an object implementing a findFile method to find the file. This + * allow using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new WinCacheClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * @author Artem Ryzhkov + */ +class WinCacheClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + protected $decorated; + + /** + * Constructor. + * + * @param string $prefix The WinCache namespace prefix to use. + * @param object $decorated A class loader object that implements the findFile() method. + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!extension_loaded('wincache')) { + throw new \RuntimeException('Unable to use WinCacheClassLoader as WinCache is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to WinCache. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + if (false === $file = wincache_ucache_get($this->prefix.$class)) { + wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class), 0); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/XcacheClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/XcacheClassLoader.php new file mode 100644 index 0000000..5aacac6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/XcacheClassLoader.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3. + * + * It expects an object implementing a findFile method to find the file. This + * allows using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new XcacheClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * @author Kim Hemsø Rasmussen + */ +class XcacheClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + private $decorated; + + /** + * Constructor. + * + * @param string $prefix The XCache namespace prefix to use. + * @param object $decorated A class loader object that implements the findFile() method. + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!extension_loaded('xcache')) { + throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to Xcache. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + if (xcache_isset($this->prefix.$class)) { + $file = xcache_get($this->prefix.$class); + } else { + $file = $this->decorated->findFile($class); + xcache_set($this->prefix.$class, $file); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/composer.json b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/composer.json new file mode 100644 index 0000000..324cbb1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/class-loader", + "type": "library", + "description": "Symfony ClassLoader Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\ClassLoader\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/phpunit.xml.dist new file mode 100644 index 0000000..4856db5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Config/CHANGELOG.md new file mode 100644 index 0000000..b752df6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/CHANGELOG.md @@ -0,0 +1,62 @@ +CHANGELOG +========= + +3.0.0 +----- + + * removed `ReferenceDumper` class + * removed the `ResourceInterface::isFresh()` method + * removed `BCResourceInterfaceChecker` class + * removed `ResourceInterface::getResource()` method + +2.8.0 +----- + +The edge case of defining just one value for nodes of type Enum is now allowed: + +```php +$rootNode + ->children() + ->enumNode('variable') + ->values(array('value')) + ->end() + ->end() +; +``` + +Before: `InvalidArgumentException` (variable must contain at least two +distinct elements). +After: the code will work as expected and it will restrict the values of the +`variable` option to just `value`. + + * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they + can be validated that way, make them implement the new `SelfCheckingResourceInterface`. + * deprecated the getResource() method in ResourceInterface. You can still call this method + on concrete classes implementing the interface, but it does not make sense at the interface + level as you need to know about the particular type of resource at hand to understand the + semantics of the returned value. + +2.7.0 +----- + + * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` + implementation to delegate creation of ConfigCache instances + +2.2.0 +----- + + * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` + to ease configuration when some sections are respectively disabled / enabled + by default. + * added a `normalizeKeys()` method for array nodes (to avoid key normalization) + * added numerical type handling for config definitions + * added convenience methods for optional configuration sections to `ArrayNodeDefinition` + * added a utils class for XML manipulations + +2.1.0 +----- + + * added a way to add documentation on configuration + * implemented `Serializable` on resources + * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type + hinting diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCache.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCache.php new file mode 100644 index 0000000..8318684 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCache.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; + +/** + * ConfigCache caches arbitrary content in files on disk. + * + * When in debug mode, those metadata resources that implement + * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will + * be used to check cache freshness. + * + * @author Fabien Potencier + * @author Matthias Pigulla + */ +class ConfigCache extends ResourceCheckerConfigCache +{ + private $debug; + + /** + * @param string $file The absolute cache path + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($file, $debug) + { + parent::__construct($file, array( + new SelfCheckingResourceChecker(), + )); + $this->debug = (bool) $debug; + } + + /** + * Checks if the cache is still fresh. + * + * This implementation always returns true when debug is off and the + * cache file exists. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!$this->debug && is_file($this->getPath())) { + return true; + } + + return parent::isFresh(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheFactory.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheFactory.php new file mode 100644 index 0000000..396536e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheFactory.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * Basic implementation of ConfigCacheFactoryInterface that + * creates an instance of the default ConfigCache. + * + * This factory and/or cache do not support cache validation + * by means of ResourceChecker instances (that is, service-based). + * + * @author Matthias Pigulla + */ +class ConfigCacheFactory implements ConfigCacheFactoryInterface +{ + /** + * @var bool Debug flag passed to the ConfigCache + */ + private $debug; + + /** + * @param bool $debug The debug flag to pass to ConfigCache + */ + public function __construct($debug) + { + $this->debug = $debug; + } + + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback))); + } + + $cache = new ConfigCache($file, $this->debug); + if (!$cache->isFresh()) { + call_user_func($callback, $cache); + } + + return $cache; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php new file mode 100644 index 0000000..bd614c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheFactoryInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * Interface for a ConfigCache factory. This factory creates + * an instance of ConfigCacheInterface and initializes the + * cache if necessary. + * + * @author Matthias Pigulla + */ +interface ConfigCacheFactoryInterface +{ + /** + * Creates a cache instance and (re-)initializes it if necessary. + * + * @param string $file The absolute cache file path + * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback + * + * @return ConfigCacheInterface $configCache The cache instance + */ + public function cache($file, $callable); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheInterface.php new file mode 100644 index 0000000..e367ad1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ConfigCacheInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ConfigCache. + * + * @author Matthias Pigulla + */ +interface ConfigCacheInterface +{ + /** + * Gets the cache file path. + * + * @return string The cache file path + */ + public function getPath(); + + /** + * Checks if the cache is still fresh. + * + * This check should take the metadata passed to the write() method into consideration. + * + * @return bool Whether the cache is still fresh. + */ + public function isFresh(); + + /** + * Writes the given content into the cache file. Metadata will be stored + * independently and can be used to check cache freshness at a later time. + * + * @param string $content The content to write into the cache + * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances + * + * @throws \RuntimeException When the cache file cannot be written + */ + public function write($content, array $metadata = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php new file mode 100644 index 0000000..166db67 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php @@ -0,0 +1,399 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents an Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class ArrayNode extends BaseNode implements PrototypeNodeInterface +{ + protected $xmlRemappings = array(); + protected $children = array(); + protected $allowFalse = false; + protected $allowNewKeys = true; + protected $addIfNotSet = false; + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $normalizeKeys = true; + + public function setNormalizeKeys($normalizeKeys) + { + $this->normalizeKeys = (bool) $normalizeKeys; + } + + /** + * Normalizes keys between the different configuration formats. + * + * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. + * After running this method, all keys are normalized to foo_bar. + * + * If you have a mixed key like foo-bar_moo, it will not be altered. + * The key will also not be altered if the target key already exists. + * + * @param mixed $value + * + * @return array The value with normalized keys + */ + protected function preNormalize($value) + { + if (!$this->normalizeKeys || !is_array($value)) { + return $value; + } + + $normalized = array(); + + foreach ($value as $k => $v) { + if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { + $normalized[$normalizedKey] = $v; + } else { + $normalized[$k] = $v; + } + } + + return $normalized; + } + + /** + * Retrieves the children of this node. + * + * @return array The children + */ + public function getChildren() + { + return $this->children; + } + + /** + * Sets the xml remappings that should be performed. + * + * @param array $remappings an array of the form array(array(string, string)) + */ + public function setXmlRemappings(array $remappings) + { + $this->xmlRemappings = $remappings; + } + + /** + * Gets the xml remappings that should be performed. + * + * @return array $remappings an array of the form array(array(string, string)) + */ + public function getXmlRemappings() + { + return $this->xmlRemappings; + } + + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + * + * @param bool $boolean + */ + public function setAddIfNotSet($boolean) + { + $this->addIfNotSet = (bool) $boolean; + } + + /** + * Sets whether false is allowed as value indicating that the array should be unset. + * + * @param bool $allow + */ + public function setAllowFalse($allow) + { + $this->allowFalse = (bool) $allow; + } + + /** + * Sets whether new keys can be defined in subsequent configurations. + * + * @param bool $allow + */ + public function setAllowNewKeys($allow) + { + $this->allowNewKeys = (bool) $allow; + } + + /** + * Sets if deep merging should occur. + * + * @param bool $boolean + */ + public function setPerformDeepMerging($boolean) + { + $this->performDeepMerging = (bool) $boolean; + } + + /** + * Whether extra keys should just be ignore without an exception. + * + * @param bool $boolean To allow extra keys + * @param bool $remove To remove extra keys + */ + public function setIgnoreExtraKeys($boolean, $remove = true) + { + $this->ignoreExtraKeys = (bool) $boolean; + $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; + } + + /** + * Sets the node Name. + * + * @param string $name The node's name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Checks if the node has a default value. + * + * @return bool + */ + public function hasDefaultValue() + { + return $this->addIfNotSet; + } + + /** + * Retrieves the default value. + * + * @return array The default value + * + * @throws \RuntimeException if the node has no default value + */ + public function getDefaultValue() + { + if (!$this->hasDefaultValue()) { + throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); + } + + $defaults = array(); + foreach ($this->children as $name => $child) { + if ($child->hasDefaultValue()) { + $defaults[$name] = $child->getDefaultValue(); + } + } + + return $defaults; + } + + /** + * Adds a child node. + * + * @param NodeInterface $node The child node to add + * + * @throws \InvalidArgumentException when the child node has no name + * @throws \InvalidArgumentException when the child node's name is not unique + */ + public function addChild(NodeInterface $node) + { + $name = $node->getName(); + if (!strlen($name)) { + throw new \InvalidArgumentException('Child nodes must be named.'); + } + if (isset($this->children[$name])) { + throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); + } + + $this->children[$name] = $node; + } + + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalised value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (false === $value) { + $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)); + throw new UnsetKeyException($msg); + } + + foreach ($this->children as $name => $child) { + if (!array_key_exists($name, $value)) { + if ($child->isRequired()) { + $msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()); + $ex = new InvalidConfigurationException($msg); + $ex->setPath($this->getPath()); + + throw $ex; + } + + if ($child->hasDefaultValue()) { + $value[$name] = $child->getDefaultValue(); + } + + continue; + } + + try { + $value[$name] = $child->finalize($value[$name]); + } catch (UnsetKeyException $e) { + unset($value[$name]); + } + } + + return $value; + } + + /** + * Validates the type of the value. + * + * @param mixed $value + * + * @throws InvalidTypeException + */ + protected function validateType($value) + { + if (!is_array($value) && (!$this->allowFalse || false !== $value)) { + $ex = new InvalidTypeException(sprintf( + 'Invalid type for path "%s". Expected array, but got %s', + $this->getPath(), + gettype($value) + )); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + */ + protected function normalizeValue($value) + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $normalized = array(); + foreach ($value as $name => $val) { + if (isset($this->children[$name])) { + $normalized[$name] = $this->children[$name]->normalize($val); + unset($value[$name]); + } elseif (!$this->removeExtraKeys) { + $normalized[$name] = $val; + } + } + + // if extra fields are present, throw exception + if (count($value) && !$this->ignoreExtraKeys) { + $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath()); + $ex = new InvalidConfigurationException($msg); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $normalized; + } + + /** + * Remaps multiple singular values to a single plural value. + * + * @param array $value The source values + * + * @return array The remapped values + */ + protected function remapXml($value) + { + foreach ($this->xmlRemappings as list($singular, $plural)) { + if (!isset($value[$singular])) { + continue; + } + + $value[$plural] = Processor::normalizeConfig($value, $singular, $plural); + unset($value[$singular]); + } + + return $value; + } + + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge. + * @param mixed $rightSide The right side to merge. + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // no conflict + if (!array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf( + 'You are not allowed to define new elements for path "%s". ' + .'Please define all elements for this path in one config file. ' + .'If you are trying to overwrite an element, make sure you redefine it ' + .'with the same name.', + $this->getPath() + )); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + if (!isset($this->children[$k])) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); + } + + return $leftSide; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BaseNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BaseNode.php new file mode 100644 index 0000000..fc3e012 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BaseNode.php @@ -0,0 +1,356 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * The base node class. + * + * @author Johannes M. Schmitt + */ +abstract class BaseNode implements NodeInterface +{ + protected $name; + protected $parent; + protected $normalizationClosures = array(); + protected $finalValidationClosures = array(); + protected $allowOverwrite = true; + protected $required = false; + protected $equivalentValues = array(); + protected $attributes = array(); + + /** + * Constructor. + * + * @param string $name The name of the node + * @param NodeInterface $parent The parent of this node + * + * @throws \InvalidArgumentException if the name contains a period. + */ + public function __construct($name, NodeInterface $parent = null) + { + if (false !== strpos($name, '.')) { + throw new \InvalidArgumentException('The name must not contain ".".'); + } + + $this->name = $name; + $this->parent = $parent; + } + + public function setAttribute($key, $value) + { + $this->attributes[$key] = $value; + } + + public function getAttribute($key, $default = null) + { + return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + } + + public function hasAttribute($key) + { + return isset($this->attributes[$key]); + } + + public function getAttributes() + { + return $this->attributes; + } + + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + + public function removeAttribute($key) + { + unset($this->attributes[$key]); + } + + /** + * Sets an info message. + * + * @param string $info + */ + public function setInfo($info) + { + $this->setAttribute('info', $info); + } + + /** + * Returns info message. + * + * @return string The info text + */ + public function getInfo() + { + return $this->getAttribute('info'); + } + + /** + * Sets the example configuration for this node. + * + * @param string|array $example + */ + public function setExample($example) + { + $this->setAttribute('example', $example); + } + + /** + * Retrieves the example configuration for this node. + * + * @return string|array The example + */ + public function getExample() + { + return $this->getAttribute('example'); + } + + /** + * Adds an equivalent value. + * + * @param mixed $originalValue + * @param mixed $equivalentValue + */ + public function addEquivalentValue($originalValue, $equivalentValue) + { + $this->equivalentValues[] = array($originalValue, $equivalentValue); + } + + /** + * Set this node as required. + * + * @param bool $boolean Required node + */ + public function setRequired($boolean) + { + $this->required = (bool) $boolean; + } + + /** + * Sets if this node can be overridden. + * + * @param bool $allow + */ + public function setAllowOverwrite($allow) + { + $this->allowOverwrite = (bool) $allow; + } + + /** + * Sets the closures used for normalization. + * + * @param \Closure[] $closures An array of Closures used for normalization + */ + public function setNormalizationClosures(array $closures) + { + $this->normalizationClosures = $closures; + } + + /** + * Sets the closures used for final validation. + * + * @param \Closure[] $closures An array of Closures used for final validation + */ + public function setFinalValidationClosures(array $closures) + { + $this->finalValidationClosures = $closures; + } + + /** + * Checks if this node is required. + * + * @return bool + */ + public function isRequired() + { + return $this->required; + } + + /** + * Returns the name of this node. + * + * @return string The Node's name. + */ + public function getName() + { + return $this->name; + } + + /** + * Retrieves the path of this node. + * + * @return string The Node's path + */ + public function getPath() + { + $path = $this->name; + + if (null !== $this->parent) { + $path = $this->parent->getPath().'.'.$path; + } + + return $path; + } + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + * + * @throws ForbiddenOverwriteException + */ + final public function merge($leftSide, $rightSide) + { + if (!$this->allowOverwrite) { + throw new ForbiddenOverwriteException(sprintf( + 'Configuration path "%s" cannot be overwritten. You have to ' + .'define all options for this path, and any of its sub-paths in ' + .'one configuration section.', + $this->getPath() + )); + } + + $this->validateType($leftSide); + $this->validateType($rightSide); + + return $this->mergeValues($leftSide, $rightSide); + } + + /** + * Normalizes a value, applying all normalization closures. + * + * @param mixed $value Value to normalize. + * + * @return mixed The normalized value. + */ + final public function normalize($value) + { + $value = $this->preNormalize($value); + + // run custom normalization closures + foreach ($this->normalizationClosures as $closure) { + $value = $closure($value); + } + + // replace value with their equivalent + foreach ($this->equivalentValues as $data) { + if ($data[0] === $value) { + $value = $data[1]; + } + } + + // validate type + $this->validateType($value); + + // normalize value + return $this->normalizeValue($value); + } + + /** + * Normalizes the value before any other normalization is applied. + * + * @param $value + * + * @return $value The normalized array value + */ + protected function preNormalize($value) + { + return $value; + } + + /** + * Returns parent node for this node. + * + * @return NodeInterface|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * Finalizes a value, applying all finalization closures. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + * + * @throws Exception + * @throws InvalidConfigurationException + */ + final public function finalize($value) + { + $this->validateType($value); + + $value = $this->finalizeValue($value); + + // Perform validation on the final value if a closure has been set. + // The closure is also allowed to return another value. + foreach ($this->finalValidationClosures as $closure) { + try { + $value = $closure($value); + } catch (Exception $e) { + throw $e; + } catch (\Exception $e) { + throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e); + } + } + + return $value; + } + + /** + * Validates the type of a Node. + * + * @param mixed $value The value to validate + * + * @throws InvalidTypeException when the value is invalid + */ + abstract protected function validateType($value); + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize. + * + * @return mixed The normalized value + */ + abstract protected function normalizeValue($value); + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + */ + abstract protected function mergeValues($leftSide, $rightSide); + + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + */ + abstract protected function finalizeValue($value); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BooleanNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BooleanNode.php new file mode 100644 index 0000000..08e1a77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/BooleanNode.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a Boolean value in the config tree. + * + * @author Johannes M. Schmitt + */ +class BooleanNode extends ScalarNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!is_bool($value)) { + $ex = new InvalidTypeException(sprintf( + 'Invalid type for path "%s". Expected boolean, but got %s.', + $this->getPath(), + gettype($value) + )); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a boolean value cannot be empty + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php new file mode 100644 index 0000000..fb34cfa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -0,0 +1,493 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * This class provides a fluent interface for defining an array node. + * + * @author Johannes M. Schmitt + */ +class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface +{ + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $children = array(); + protected $prototype; + protected $atLeastOne = false; + protected $allowNewKeys = true; + protected $key; + protected $removeKeyItem; + protected $addDefaults = false; + protected $addDefaultChildren = false; + protected $nodeBuilder; + protected $normalizeKeys = true; + + /** + * {@inheritdoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = array(); + $this->trueEquivalent = array(); + } + + /** + * Sets a custom children builder. + * + * @param NodeBuilder $builder A custom NodeBuilder + */ + public function setBuilder(NodeBuilder $builder) + { + $this->nodeBuilder = $builder; + } + + /** + * Returns a builder to add children nodes. + * + * @return NodeBuilder + */ + public function children() + { + return $this->getNodeBuilder(); + } + + /** + * Sets a prototype for child nodes. + * + * @param string $type the type of node + * + * @return NodeDefinition + */ + public function prototype($type) + { + return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); + } + + /** + * Adds the default value if the node is not set in the configuration. + * + * This method is applicable to concrete nodes only (not to prototype nodes). + * If this function has been called and the node is not set during the finalization + * phase, it's default value will be derived from its children default values. + * + * @return ArrayNodeDefinition + */ + public function addDefaultsIfNotSet() + { + $this->addDefaults = true; + + return $this; + } + + /** + * Adds children with a default value when none are defined. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + * + * This method is applicable to prototype nodes only. + * + * @return ArrayNodeDefinition + */ + public function addDefaultChildrenIfNoneSet($children = null) + { + $this->addDefaultChildren = $children; + + return $this; + } + + /** + * Requires the node to have at least one element. + * + * This method is applicable to prototype nodes only. + * + * @return ArrayNodeDefinition + */ + public function requiresAtLeastOneElement() + { + $this->atLeastOne = true; + + return $this; + } + + /** + * Disallows adding news keys in a subsequent configuration. + * + * If used all keys have to be defined in the same configuration file. + * + * @return ArrayNodeDefinition + */ + public function disallowNewKeysInSubsequentConfigs() + { + $this->allowNewKeys = false; + + return $this; + } + + /** + * Sets a normalization rule for XML configurations. + * + * @param string $singular The key to remap + * @param string $plural The plural of the key for irregular plurals + * + * @return ArrayNodeDefinition + */ + public function fixXmlConfig($singular, $plural = null) + { + $this->normalization()->remap($singular, $plural); + + return $this; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * array( + * array('id' => 'my_name', 'foo' => 'bar'), + * ); + * + * becomes + * + * array( + * 'my_name' => array('foo' => 'bar'), + * ); + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * This method is applicable to prototype nodes only. + * + * @param string $name The name of the key + * @param bool $removeKeyItem Whether or not the key item should be removed. + * + * @return ArrayNodeDefinition + */ + public function useAttributeAsKey($name, $removeKeyItem = true) + { + $this->key = $name; + $this->removeKeyItem = $removeKeyItem; + + return $this; + } + + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return ArrayNodeDefinition + */ + public function canBeUnset($allow = true) + { + $this->merge()->allowUnset($allow); + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is disabled. If any configuration is specified then + * the node will be automatically enabled: + * + * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden + * enableableArrayNode: {enabled: false, ...} # The config is disabled + * enableableArrayNode: false # The config is disabled + * + * @return ArrayNodeDefinition + */ + public function canBeEnabled() + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->beforeNormalization() + ->ifArray() + ->then(function ($v) { + $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; + + return $v; + }) + ->end() + ->children() + ->booleanNode('enabled') + ->defaultFalse() + ; + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is enabled. + * + * @return ArrayNodeDefinition + */ + public function canBeDisabled() + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->children() + ->booleanNode('enabled') + ->defaultTrue() + ; + + return $this; + } + + /** + * Disables the deep merging of the node. + * + * @return ArrayNodeDefinition + */ + public function performNoDeepMerging() + { + $this->performDeepMerging = false; + + return $this; + } + + /** + * Allows extra config keys to be specified under an array without + * throwing an exception. + * + * Those config values are simply ignored and removed from the + * resulting array. This should be used only in special cases where + * you want to send an entire configuration array through a special + * tree that processes only part of the array. + * + * @param bool $remove Whether to remove the extra keys + * + * @return ArrayNodeDefinition + */ + public function ignoreExtraKeys($remove = true) + { + $this->ignoreExtraKeys = true; + $this->removeExtraKeys = $remove; + + return $this; + } + + /** + * Sets key normalization. + * + * @param bool $bool Whether to enable key normalization + * + * @return ArrayNodeDefinition + */ + public function normalizeKeys($bool) + { + $this->normalizeKeys = (bool) $bool; + + return $this; + } + + /** + * Appends a node definition. + * + * $node = new ArrayNodeDefinition() + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->end() + * ->append($this->getBarNodeDefinition()) + * ; + * + * @param NodeDefinition $node A NodeDefinition instance + * + * @return ArrayNodeDefinition This node + */ + public function append(NodeDefinition $node) + { + $this->children[$node->name] = $node->setParent($this); + + return $this; + } + + /** + * Returns a node builder to be used to add children and prototype. + * + * @return NodeBuilder The node builder + */ + protected function getNodeBuilder() + { + if (null === $this->nodeBuilder) { + $this->nodeBuilder = new NodeBuilder(); + } + + return $this->nodeBuilder->setParent($this); + } + + /** + * {@inheritdoc} + */ + protected function createNode() + { + if (null === $this->prototype) { + $node = new ArrayNode($this->name, $this->parent); + + $this->validateConcreteNode($node); + + $node->setAddIfNotSet($this->addDefaults); + + foreach ($this->children as $child) { + $child->parent = $node; + $node->addChild($child->getNode()); + } + } else { + $node = new PrototypedArrayNode($this->name, $this->parent); + + $this->validatePrototypeNode($node); + + if (null !== $this->key) { + $node->setKeyAttribute($this->key, $this->removeKeyItem); + } + + if (true === $this->atLeastOne) { + $node->setMinNumberOfElements(1); + } + + if ($this->default) { + $node->setDefaultValue($this->defaultValue); + } + + if (false !== $this->addDefaultChildren) { + $node->setAddChildrenIfNoneSet($this->addDefaultChildren); + if ($this->prototype instanceof static && null === $this->prototype->prototype) { + $this->prototype->addDefaultsIfNotSet(); + } + } + + $this->prototype->parent = $node; + $node->setPrototype($this->prototype->getNode()); + } + + $node->setAllowNewKeys($this->allowNewKeys); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setPerformDeepMerging($this->performDeepMerging); + $node->setRequired($this->required); + $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); + $node->setNormalizeKeys($this->normalizeKeys); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + $node->setXmlRemappings($this->normalization->remappings); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + $node->setAllowFalse($this->merge->allowFalse); + } + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } + + /** + * Validate the configuration of a concrete node. + * + * @param ArrayNode $node The related node + * + * @throws InvalidDefinitionException + */ + protected function validateConcreteNode(ArrayNode $node) + { + $path = $node->getPath(); + + if (null !== $this->key) { + throw new InvalidDefinitionException( + sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path) + ); + } + + if (true === $this->atLeastOne) { + throw new InvalidDefinitionException( + sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path) + ); + } + + if ($this->default) { + throw new InvalidDefinitionException( + sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path) + ); + } + + if (false !== $this->addDefaultChildren) { + throw new InvalidDefinitionException( + sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path) + ); + } + } + + /** + * Validate the configuration of a prototype node. + * + * @param PrototypedArrayNode $node The related node + * + * @throws InvalidDefinitionException + */ + protected function validatePrototypeNode(PrototypedArrayNode $node) + { + $path = $node->getPath(); + + if ($this->addDefaults) { + throw new InvalidDefinitionException( + sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path) + ); + } + + if (false !== $this->addDefaultChildren) { + if ($this->default) { + throw new InvalidDefinitionException( + sprintf('A default value and default children might not be used together at path "%s"', $path) + ); + } + + if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + throw new InvalidDefinitionException( + sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path) + ); + } + + if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) { + throw new InvalidDefinitionException( + sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path) + ); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php new file mode 100644 index 0000000..28e5657 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/BooleanNodeDefinition.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\BooleanNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class BooleanNodeDefinition extends ScalarNodeDefinition +{ + /** + * {@inheritdoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = true; + } + + /** + * Instantiate a Node. + * + * @return BooleanNode The node + */ + protected function instantiateNode() + { + return new BooleanNode($this->name, $this->parent); + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php new file mode 100644 index 0000000..dc25fcb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\EnumNode; + +/** + * Enum Node Definition. + * + * @author Johannes M. Schmitt + */ +class EnumNodeDefinition extends ScalarNodeDefinition +{ + private $values; + + /** + * @param array $values + * + * @return EnumNodeDefinition|$this + */ + public function values(array $values) + { + $values = array_unique($values); + + if (count($values) <= 1) { + throw new \InvalidArgumentException('->values() must be called with at least two distinct values.'); + } + + $this->values = $values; + + return $this; + } + + /** + * Instantiate a Node. + * + * @return EnumNode The node + * + * @throws \RuntimeException + */ + protected function instantiateNode() + { + if (null === $this->values) { + throw new \RuntimeException('You must call ->values() on enum nodes.'); + } + + return new EnumNode($this->name, $this->parent, $this->values); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php new file mode 100644 index 0000000..3d79b29 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * This class builds an if expression. + * + * @author Johannes M. Schmitt + * @author Christophe Coevoet + */ +class ExprBuilder +{ + protected $node; + public $ifPart; + public $thenPart; + + /** + * Constructor. + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Marks the expression as being always used. + * + * @param \Closure $then + * + * @return ExprBuilder + */ + public function always(\Closure $then = null) + { + $this->ifPart = function ($v) { return true; }; + + if (null !== $then) { + $this->thenPart = $then; + } + + return $this; + } + + /** + * Sets a closure to use as tests. + * + * The default one tests if the value is true. + * + * @param \Closure $closure + * + * @return ExprBuilder + */ + public function ifTrue(\Closure $closure = null) + { + if (null === $closure) { + $closure = function ($v) { return true === $v; }; + } + + $this->ifPart = $closure; + + return $this; + } + + /** + * Tests if the value is a string. + * + * @return ExprBuilder + */ + public function ifString() + { + $this->ifPart = function ($v) { return is_string($v); }; + + return $this; + } + + /** + * Tests if the value is null. + * + * @return ExprBuilder + */ + public function ifNull() + { + $this->ifPart = function ($v) { return null === $v; }; + + return $this; + } + + /** + * Tests if the value is an array. + * + * @return ExprBuilder + */ + public function ifArray() + { + $this->ifPart = function ($v) { return is_array($v); }; + + return $this; + } + + /** + * Tests if the value is in an array. + * + * @param array $array + * + * @return ExprBuilder + */ + public function ifInArray(array $array) + { + $this->ifPart = function ($v) use ($array) { return in_array($v, $array, true); }; + + return $this; + } + + /** + * Tests if the value is not in an array. + * + * @param array $array + * + * @return ExprBuilder + */ + public function ifNotInArray(array $array) + { + $this->ifPart = function ($v) use ($array) { return !in_array($v, $array, true); }; + + return $this; + } + + /** + * Sets the closure to run if the test pass. + * + * @param \Closure $closure + * + * @return ExprBuilder + */ + public function then(\Closure $closure) + { + $this->thenPart = $closure; + + return $this; + } + + /** + * Sets a closure returning an empty array. + * + * @return ExprBuilder + */ + public function thenEmptyArray() + { + $this->thenPart = function ($v) { return array(); }; + + return $this; + } + + /** + * Sets a closure marking the value as invalid at validation time. + * + * if you want to add the value of the node in your message just use a %s placeholder. + * + * @param string $message + * + * @return ExprBuilder + * + * @throws \InvalidArgumentException + */ + public function thenInvalid($message) + { + $this->thenPart = function ($v) use ($message) {throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + + return $this; + } + + /** + * Sets a closure unsetting this key of the array at validation time. + * + * @return ExprBuilder + * + * @throws UnsetKeyException + */ + public function thenUnset() + { + $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); }; + + return $this; + } + + /** + * Returns the related node. + * + * @return NodeDefinition + * + * @throws \RuntimeException + */ + public function end() + { + if (null === $this->ifPart) { + throw new \RuntimeException('You must specify an if part.'); + } + if (null === $this->thenPart) { + throw new \RuntimeException('You must specify a then part.'); + } + + return $this->node; + } + + /** + * Builds the expressions. + * + * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build + * + * @return array + */ + public static function buildExpressions(array $expressions) + { + foreach ($expressions as $k => $expr) { + if ($expr instanceof self) { + $if = $expr->ifPart; + $then = $expr->thenPart; + $expressions[$k] = function ($v) use ($if, $then) { + return $if($v) ? $then($v) : $v; + }; + } + } + + return $expressions; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php new file mode 100644 index 0000000..c0bed46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/FloatNodeDefinition.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\FloatNode; + +/** + * This class provides a fluent interface for defining a float node. + * + * @author Jeanmonod David + */ +class FloatNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return FloatNode The node + */ + protected function instantiateNode() + { + return new FloatNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php new file mode 100644 index 0000000..f6c3c14 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/IntegerNodeDefinition.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\IntegerNode; + +/** + * This class provides a fluent interface for defining an integer node. + * + * @author Jeanmonod David + */ +class IntegerNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return IntegerNode The node + */ + protected function instantiateNode() + { + return new IntegerNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php new file mode 100644 index 0000000..f908a49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/MergeBuilder.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds merge conditions. + * + * @author Johannes M. Schmitt + */ +class MergeBuilder +{ + protected $node; + public $allowFalse = false; + public $allowOverwrite = true; + + /** + * Constructor. + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return MergeBuilder + */ + public function allowUnset($allow = true) + { + $this->allowFalse = $allow; + + return $this; + } + + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return MergeBuilder + */ + public function denyOverwrite($deny = true) + { + $this->allowOverwrite = !$deny; + + return $this; + } + + /** + * Returns the related node. + * + * @return NodeDefinition + */ + public function end() + { + return $this->node; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php new file mode 100644 index 0000000..b2b6336 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php @@ -0,0 +1,245 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class provides a fluent interface for building a node. + * + * @author Johannes M. Schmitt + */ +class NodeBuilder implements NodeParentInterface +{ + protected $parent; + protected $nodeMapping; + + /** + * Constructor. + */ + public function __construct() + { + $this->nodeMapping = array( + 'variable' => __NAMESPACE__.'\\VariableNodeDefinition', + 'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition', + 'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition', + 'integer' => __NAMESPACE__.'\\IntegerNodeDefinition', + 'float' => __NAMESPACE__.'\\FloatNodeDefinition', + 'array' => __NAMESPACE__.'\\ArrayNodeDefinition', + 'enum' => __NAMESPACE__.'\\EnumNodeDefinition', + ); + } + + /** + * Set the parent node. + * + * @param ParentNodeDefinitionInterface $parent The parent node + * + * @return NodeBuilder This node builder + */ + public function setParent(ParentNodeDefinitionInterface $parent = null) + { + $this->parent = $parent; + + return $this; + } + + /** + * Creates a child array node. + * + * @param string $name The name of the node + * + * @return ArrayNodeDefinition The child node + */ + public function arrayNode($name) + { + return $this->node($name, 'array'); + } + + /** + * Creates a child scalar node. + * + * @param string $name the name of the node + * + * @return ScalarNodeDefinition The child node + */ + public function scalarNode($name) + { + return $this->node($name, 'scalar'); + } + + /** + * Creates a child Boolean node. + * + * @param string $name The name of the node + * + * @return BooleanNodeDefinition The child node + */ + public function booleanNode($name) + { + return $this->node($name, 'boolean'); + } + + /** + * Creates a child integer node. + * + * @param string $name the name of the node + * + * @return IntegerNodeDefinition The child node + */ + public function integerNode($name) + { + return $this->node($name, 'integer'); + } + + /** + * Creates a child float node. + * + * @param string $name the name of the node + * + * @return FloatNodeDefinition The child node + */ + public function floatNode($name) + { + return $this->node($name, 'float'); + } + + /** + * Creates a child EnumNode. + * + * @param string $name + * + * @return EnumNodeDefinition + */ + public function enumNode($name) + { + return $this->node($name, 'enum'); + } + + /** + * Creates a child variable node. + * + * @param string $name The name of the node + * + * @return VariableNodeDefinition The builder of the child node + */ + public function variableNode($name) + { + return $this->node($name, 'variable'); + } + + /** + * Returns the parent node. + * + * @return ParentNodeDefinitionInterface The parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates a child node. + * + * @param string $name The name of the node + * @param string $type The type of the node + * + * @return NodeDefinition The child node + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + public function node($name, $type) + { + $class = $this->getNodeClass($type); + + $node = new $class($name); + + $this->append($node); + + return $node; + } + + /** + * Appends a node definition. + * + * Usage: + * + * $node = new ArrayNodeDefinition('name') + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @param NodeDefinition $node + * + * @return NodeBuilder This node builder + */ + public function append(NodeDefinition $node) + { + if ($node instanceof ParentNodeDefinitionInterface) { + $builder = clone $this; + $builder->setParent(null); + $node->setBuilder($builder); + } + + if (null !== $this->parent) { + $this->parent->append($node); + // Make this builder the node parent to allow for a fluid interface + $node->setParent($this); + } + + return $this; + } + + /** + * Adds or overrides a node Type. + * + * @param string $type The name of the type + * @param string $class The fully qualified name the node definition class + * + * @return NodeBuilder This node builder + */ + public function setNodeClass($type, $class) + { + $this->nodeMapping[strtolower($type)] = $class; + + return $this; + } + + /** + * Returns the class name of the node definition. + * + * @param string $type The node type + * + * @return string The node definition class name + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + protected function getNodeClass($type) + { + $type = strtolower($type); + + if (!isset($this->nodeMapping[$type])) { + throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); + } + + $class = $this->nodeMapping[$type]; + + if (!class_exists($class)) { + throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); + } + + return $class; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php new file mode 100644 index 0000000..f7f84bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -0,0 +1,343 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +abstract class NodeDefinition implements NodeParentInterface +{ + protected $name; + protected $normalization; + protected $validation; + protected $defaultValue; + protected $default = false; + protected $required = false; + protected $merge; + protected $allowEmptyValue = true; + protected $nullEquivalent; + protected $trueEquivalent = true; + protected $falseEquivalent = false; + + /** + * @var NodeParentInterface|null + */ + protected $parent; + protected $attributes = array(); + + /** + * Constructor. + * + * @param string $name The name of the node + * @param NodeParentInterface|null $parent The parent + */ + public function __construct($name, NodeParentInterface $parent = null) + { + $this->parent = $parent; + $this->name = $name; + } + + /** + * Sets the parent node. + * + * @param NodeParentInterface $parent The parent + * + * @return NodeDefinition|$this + */ + public function setParent(NodeParentInterface $parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Sets info message. + * + * @param string $info The info text + * + * @return NodeDefinition|$this + */ + public function info($info) + { + return $this->attribute('info', $info); + } + + /** + * Sets example configuration. + * + * @param string|array $example + * + * @return NodeDefinition|$this + */ + public function example($example) + { + return $this->attribute('example', $example); + } + + /** + * Sets an attribute on the node. + * + * @param string $key + * @param mixed $value + * + * @return NodeDefinition|$this + */ + public function attribute($key, $value) + { + $this->attributes[$key] = $value; + + return $this; + } + + /** + * Returns the parent node. + * + * @return NodeParentInterface|null The builder of the parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates the node. + * + * @param bool $forceRootNode Whether to force this node as the root node + * + * @return NodeInterface + */ + public function getNode($forceRootNode = false) + { + if ($forceRootNode) { + $this->parent = null; + } + + if (null !== $this->normalization) { + $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); + } + + if (null !== $this->validation) { + $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); + } + + $node = $this->createNode(); + $node->setAttributes($this->attributes); + + return $node; + } + + /** + * Sets the default value. + * + * @param mixed $value The default value + * + * @return NodeDefinition|$this + */ + public function defaultValue($value) + { + $this->default = true; + $this->defaultValue = $value; + + return $this; + } + + /** + * Sets the node as required. + * + * @return NodeDefinition|$this + */ + public function isRequired() + { + $this->required = true; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains null. + * + * @param mixed $value + * + * @return NodeDefinition|$this + */ + public function treatNullLike($value) + { + $this->nullEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains true. + * + * @param mixed $value + * + * @return NodeDefinition|$this + */ + public function treatTrueLike($value) + { + $this->trueEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains false. + * + * @param mixed $value + * + * @return NodeDefinition|$this + */ + public function treatFalseLike($value) + { + $this->falseEquivalent = $value; + + return $this; + } + + /** + * Sets null as the default value. + * + * @return NodeDefinition|$this + */ + public function defaultNull() + { + return $this->defaultValue(null); + } + + /** + * Sets true as the default value. + * + * @return NodeDefinition|$this + */ + public function defaultTrue() + { + return $this->defaultValue(true); + } + + /** + * Sets false as the default value. + * + * @return NodeDefinition|$this + */ + public function defaultFalse() + { + return $this->defaultValue(false); + } + + /** + * Sets an expression to run before the normalization. + * + * @return ExprBuilder + */ + public function beforeNormalization() + { + return $this->normalization()->before(); + } + + /** + * Denies the node value being empty. + * + * @return NodeDefinition|$this + */ + public function cannotBeEmpty() + { + $this->allowEmptyValue = false; + + return $this; + } + + /** + * Sets an expression to run for the validation. + * + * The expression receives the value of the node and must return it. It can + * modify it. + * An exception should be thrown when the node is not valid. + * + * @return ExprBuilder + */ + public function validate() + { + return $this->validation()->rule(); + } + + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return NodeDefinition|$this + */ + public function cannotBeOverwritten($deny = true) + { + $this->merge()->denyOverwrite($deny); + + return $this; + } + + /** + * Gets the builder for validation rules. + * + * @return ValidationBuilder + */ + protected function validation() + { + if (null === $this->validation) { + $this->validation = new ValidationBuilder($this); + } + + return $this->validation; + } + + /** + * Gets the builder for merging rules. + * + * @return MergeBuilder + */ + protected function merge() + { + if (null === $this->merge) { + $this->merge = new MergeBuilder($this); + } + + return $this->merge; + } + + /** + * Gets the builder for normalization rules. + * + * @return NormalizationBuilder + */ + protected function normalization() + { + if (null === $this->normalization) { + $this->normalization = new NormalizationBuilder($this); + } + + return $this->normalization; + } + + /** + * Instantiate and configure the node according to this definition. + * + * @return NodeInterface $node The node instance + * + * @throws InvalidDefinitionException When the definition is invalid + */ + abstract protected function createNode(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeParentInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeParentInterface.php new file mode 100644 index 0000000..305e993 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NodeParentInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by all node parents. + * + * @author Victor Berchet + */ +interface NodeParentInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php new file mode 100644 index 0000000..748c9f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds normalization conditions. + * + * @author Johannes M. Schmitt + */ +class NormalizationBuilder +{ + protected $node; + public $before = array(); + public $remappings = array(); + + /** + * Constructor. + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a key to remap to its plural form. + * + * @param string $key The key to remap + * @param string $plural The plural of the key in case of irregular plural + * + * @return NormalizationBuilder + */ + public function remap($key, $plural = null) + { + $this->remappings[] = array($key, null === $plural ? $key.'s' : $plural); + + return $this; + } + + /** + * Registers a closure to run before the normalization or an expression builder to build it if null is provided. + * + * @param \Closure $closure + * + * @return ExprBuilder|NormalizationBuilder + */ + public function before(\Closure $closure = null) + { + if (null !== $closure) { + $this->before[] = $closure; + + return $this; + } + + return $this->before[] = new ExprBuilder($this->node); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.php new file mode 100644 index 0000000..8451e75 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/NumericNodeDefinition.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * Abstract class that contains common code of integer and float node definitions. + * + * @author David Jeanmonod + */ +abstract class NumericNodeDefinition extends ScalarNodeDefinition +{ + protected $min; + protected $max; + + /** + * Ensures that the value is smaller than the given reference. + * + * @param mixed $max + * + * @return NumericNodeDefinition + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function max($max) + { + if (isset($this->min) && $this->min > $max) { + throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min)); + } + $this->max = $max; + + return $this; + } + + /** + * Ensures that the value is bigger than the given reference. + * + * @param mixed $min + * + * @return NumericNodeDefinition + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function min($min) + { + if (isset($this->max) && $this->max < $min) { + throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max)); + } + $this->min = $min; + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php new file mode 100644 index 0000000..575495b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by nodes which can have children. + * + * @author Victor Berchet + */ +interface ParentNodeDefinitionInterface +{ + public function children(); + + public function append(NodeDefinition $node); + + public function setBuilder(NodeBuilder $builder); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php new file mode 100644 index 0000000..6170555 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ScalarNodeDefinition.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ScalarNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class ScalarNodeDefinition extends VariableNodeDefinition +{ + /** + * Instantiate a Node. + * + * @return ScalarNode The node + */ + protected function instantiateNode() + { + return new ScalarNode($this->name, $this->parent); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php new file mode 100644 index 0000000..5d02848 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This is the entry class for building a config tree. + * + * @author Johannes M. Schmitt + */ +class TreeBuilder implements NodeParentInterface +{ + protected $tree; + protected $root; + protected $builder; + + /** + * Creates the root node. + * + * @param string $name The name of the root node + * @param string $type The type of the root node + * @param NodeBuilder $builder A custom node builder instance + * + * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') + * + * @throws \RuntimeException When the node type is not supported + */ + public function root($name, $type = 'array', NodeBuilder $builder = null) + { + $builder = $builder ?: new NodeBuilder(); + + return $this->root = $builder->node($name, $type)->setParent($this); + } + + /** + * Builds the tree. + * + * @return NodeInterface + * + * @throws \RuntimeException + */ + public function buildTree() + { + if (null === $this->root) { + throw new \RuntimeException('The configuration tree has no root node.'); + } + if (null !== $this->tree) { + return $this->tree; + } + + return $this->tree = $this->root->getNode(true); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ValidationBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ValidationBuilder.php new file mode 100644 index 0000000..e885823 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/ValidationBuilder.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds validation conditions. + * + * @author Christophe Coevoet + */ +class ValidationBuilder +{ + protected $node; + public $rules = array(); + + /** + * Constructor. + * + * @param NodeDefinition $node The related node + */ + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a closure to run as normalization or an expression builder to build it if null is provided. + * + * @param \Closure $closure + * + * @return ExprBuilder|ValidationBuilder + */ + public function rule(\Closure $closure = null) + { + if (null !== $closure) { + $this->rules[] = $closure; + + return $this; + } + + return $this->rules[] = new ExprBuilder($this->node); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php new file mode 100644 index 0000000..a46b7ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Builder/VariableNodeDefinition.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\VariableNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class VariableNodeDefinition extends NodeDefinition +{ + /** + * Instantiate a Node. + * + * @return VariableNode The node + */ + protected function instantiateNode() + { + return new VariableNode($this->name, $this->parent); + } + + /** + * {@inheritdoc} + */ + protected function createNode() + { + $node = $this->instantiateNode(); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + } + + if (true === $this->default) { + $node->setDefaultValue($this->defaultValue); + } + + $node->setAllowEmptyValue($this->allowEmptyValue); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setRequired($this->required); + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ConfigurationInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ConfigurationInterface.php new file mode 100644 index 0000000..d6456ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ConfigurationInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * Configuration interface. + * + * @author Victor Berchet + */ +interface ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php new file mode 100644 index 0000000..2cc71b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * Dumps a XML reference configuration for the given configuration/node instance. + * + * @author Wouter J + */ +class XmlReferenceDumper +{ + private $reference; + + public function dump(ConfigurationInterface $configuration, $namespace = null) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); + } + + public function dumpNode(NodeInterface $node, $namespace = null) + { + $this->reference = ''; + $this->writeNode($node, 0, true, $namespace); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + /** + * @param NodeInterface $node + * @param int $depth + * @param bool $root If the node is the root node + * @param string $namespace The namespace of the node + */ + private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null) + { + $rootName = ($root ? 'config' : $node->getName()); + $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); + + // xml remapping + if ($node->getParent()) { + $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) { + return $rootName === $mapping[1]; + }); + + if (count($remapping)) { + list($singular) = current($remapping); + $rootName = $singular; + } + } + $rootName = str_replace('_', '-', $rootName); + + $rootAttributes = array(); + $rootAttributeComments = array(); + $rootChildren = array(); + $rootComments = array(); + + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + // comments about the root node + if ($rootInfo = $node->getInfo()) { + $rootComments[] = $rootInfo; + } + + if ($rootNamespace) { + $rootComments[] = 'Namespace: '.$rootNamespace; + } + + // render prototyped nodes + if ($node instanceof PrototypedArrayNode) { + $prototype = $node->getPrototype(); + + $info = 'prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + array_unshift($rootComments, $info); + + if ($key = $node->getKeyAttribute()) { + $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key; + } + + if ($prototype instanceof ArrayNode) { + $children = $prototype->getChildren(); + } else { + if ($prototype->hasDefaultValue()) { + $prototypeValue = $prototype->getDefaultValue(); + } else { + switch (get_class($prototype)) { + case 'Symfony\Component\Config\Definition\ScalarNode': + $prototypeValue = 'scalar value'; + break; + + case 'Symfony\Component\Config\Definition\FloatNode': + case 'Symfony\Component\Config\Definition\IntegerNode': + $prototypeValue = 'numeric value'; + break; + + case 'Symfony\Component\Config\Definition\BooleanNode': + $prototypeValue = 'true|false'; + break; + + case 'Symfony\Component\Config\Definition\EnumNode': + $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); + break; + + default: + $prototypeValue = 'value'; + } + } + } + } + + // get attributes and elements + foreach ($children as $child) { + if (!$child instanceof ArrayNode) { + // get attributes + + // metadata + $name = str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world + + // comments + $comments = array(); + if ($info = $child->getInfo()) { + $comments[] = $info; + } + + if ($example = $child->getExample()) { + $comments[] = 'Example: '.$example; + } + + if ($child->isRequired()) { + $comments[] = 'Required'; + } + + if ($child instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); + } + + if (count($comments)) { + $rootAttributeComments[$name] = implode(";\n", $comments); + } + + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + + // append attribute + $rootAttributes[$name] = $value; + } else { + // get elements + $rootChildren[] = $child; + } + } + } + + // render comments + + // root node comment + if (count($rootComments)) { + foreach ($rootComments as $comment) { + $this->writeLine('', $depth); + } + } + + // attribute comments + if (count($rootAttributeComments)) { + foreach ($rootAttributeComments as $attrName => $comment) { + $commentDepth = $depth + 4 + strlen($attrName) + 2; + $commentLines = explode("\n", $comment); + $multiline = (count($commentLines) > 1); + $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); + + if ($multiline) { + $this->writeLine('', $depth); + } else { + $this->writeLine('', $depth); + } + } + } + + // render start tag + attributes + $rootIsVariablePrototype = isset($prototypeValue); + $rootIsEmptyTag = (0 === count($rootChildren) && !$rootIsVariablePrototype); + $rootOpenTag = '<'.$rootName; + if (1 >= ($attributesCount = count($rootAttributes))) { + if (1 === $attributesCount) { + $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes))); + } + + $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + + $this->writeLine($rootOpenTag, $depth); + } else { + $this->writeLine($rootOpenTag, $depth); + + $i = 1; + + foreach ($rootAttributes as $attrName => $attrValue) { + $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); + + $this->writeLine($attr, $depth + 4); + + if ($attributesCount === $i++) { + $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + } + } + } + + // render children tags + foreach ($rootChildren as $child) { + $this->writeLine(''); + $this->writeNode($child, $depth + 4); + } + + // render end tag + if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { + $this->writeLine(''); + + $rootEndTag = ''; + $this->writeLine($rootEndTag, $depth); + } + } + + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text).PHP_EOL; + } + + /** + * Renders the string conversion of the value. + * + * @param mixed $value + * + * @return string + */ + private function writeValue($value) + { + if ('%%%%not_defined%%%%' === $value) { + return ''; + } + + if (is_string($value) || is_numeric($value)) { + return $value; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + if (null === $value) { + return 'null'; + } + + if (empty($value)) { + return ''; + } + + if (is_array($value)) { + return implode(',', $value); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php new file mode 100644 index 0000000..d38e6eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Yaml\Inline; + +/** + * Dumps a Yaml reference configuration for the given configuration/node instance. + * + * @author Kevin Bond + */ +class YamlReferenceDumper +{ + private $reference; + + public function dump(ConfigurationInterface $configuration) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); + } + + public function dumpNode(NodeInterface $node) + { + $this->reference = ''; + $this->writeNode($node); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + /** + * @param NodeInterface $node + * @param int $depth + */ + private function writeNode(NodeInterface $node, $depth = 0) + { + $comments = array(); + $default = ''; + $defaultArray = null; + $children = null; + $example = $node->getExample(); + + // defaults + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + if ($node instanceof PrototypedArrayNode) { + $prototype = $node->getPrototype(); + + if ($prototype instanceof ArrayNode) { + $children = $prototype->getChildren(); + } + + // check for attribute as key + if ($key = $node->getKeyAttribute()) { + $keyNodeClass = 'Symfony\Component\Config\Definition\\'.($prototype instanceof ArrayNode ? 'ArrayNode' : 'ScalarNode'); + $keyNode = new $keyNodeClass($key, $node); + + $info = 'Prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + $keyNode->setInfo($info); + + // add children + foreach ($children as $childNode) { + $keyNode->addChild($childNode); + } + $children = array($key => $keyNode); + } + } + + if (!$children) { + if ($node->hasDefaultValue() && count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!is_array($example)) { + $default = '[]'; + } + } + } elseif ($node instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues())); + $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~'; + } else { + $default = '~'; + + if ($node->hasDefaultValue()) { + $default = $node->getDefaultValue(); + + if (is_array($default)) { + if (count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!is_array($example)) { + $default = '[]'; + } + } else { + $default = Inline::dump($default); + } + } + } + + // required? + if ($node->isRequired()) { + $comments[] = 'Required'; + } + + // example + if ($example && !is_array($example)) { + $comments[] = 'Example: '.$example; + } + + $default = (string) $default != '' ? ' '.$default : ''; + $comments = count($comments) ? '# '.implode(', ', $comments) : ''; + + $text = rtrim(sprintf('%-20s %s %s', $node->getName().':', $default, $comments), ' '); + + if ($info = $node->getInfo()) { + $this->writeLine(''); + // indenting multi-line info + $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info); + $this->writeLine('# '.$info, $depth * 4); + } + + $this->writeLine($text, $depth * 4); + + // output defaults + if ($defaultArray) { + $this->writeLine(''); + + $message = count($defaultArray) > 1 ? 'Defaults' : 'Default'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($defaultArray, $depth + 1); + } + + if (is_array($example)) { + $this->writeLine(''); + + $message = count($example) > 1 ? 'Examples' : 'Example'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($example, $depth + 1); + } + + if ($children) { + foreach ($children as $childNode) { + $this->writeNode($childNode, $depth + 1); + } + } + } + + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text)."\n"; + } + + private function writeArray(array $array, $depth) + { + $isIndexed = array_values($array) === $array; + + foreach ($array as $key => $value) { + if (is_array($value)) { + $val = ''; + } else { + $val = $value; + } + + if ($isIndexed) { + $this->writeLine('- '.$val, $depth * 4); + } else { + $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4); + } + + if (is_array($value)) { + $this->writeArray($value, $depth + 1); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/EnumNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/EnumNode.php new file mode 100644 index 0000000..9b4c415 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/EnumNode.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * Node which only allows a finite set of values. + * + * @author Johannes M. Schmitt + */ +class EnumNode extends ScalarNode +{ + private $values; + + public function __construct($name, NodeInterface $parent = null, array $values = array()) + { + $values = array_unique($values); + if (empty($values)) { + throw new \InvalidArgumentException('$values must contain at least one element.'); + } + + parent::__construct($name, $parent); + $this->values = $values; + } + + public function getValues() + { + return $this->values; + } + + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + + if (!in_array($value, $this->values, true)) { + $ex = new InvalidConfigurationException(sprintf( + 'The value %s is not allowed for path "%s". Permissible values: %s', + json_encode($value), + $this->getPath(), + implode(', ', array_map('json_encode', $this->values)))); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/DuplicateKeyException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/DuplicateKeyException.php new file mode 100644 index 0000000..48dd932 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/DuplicateKeyException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown whenever the key of an array is not unique. This can + * only be the case if the configuration is coming from an XML file. + * + * @author Johannes M. Schmitt + */ +class DuplicateKeyException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/Exception.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/Exception.php new file mode 100644 index 0000000..8933a49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/Exception.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * Base exception for all configuration exceptions. + * + * @author Johannes M. Schmitt + */ +class Exception extends \RuntimeException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/ForbiddenOverwriteException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/ForbiddenOverwriteException.php new file mode 100644 index 0000000..726c07f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/ForbiddenOverwriteException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown when a configuration path is overwritten from a + * subsequent configuration file, but the entry node specifically forbids this. + * + * @author Johannes M. Schmitt + */ +class ForbiddenOverwriteException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php new file mode 100644 index 0000000..3dbc57b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidConfigurationException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * A very general exception which can be thrown whenever non of the more specific + * exceptions is suitable. + * + * @author Johannes M. Schmitt + */ +class InvalidConfigurationException extends Exception +{ + private $path; + private $containsHints = false; + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + /** + * Adds extra information that is suffixed to the original exception message. + * + * @param string $hint + */ + public function addHint($hint) + { + if (!$this->containsHints) { + $this->message .= "\nHint: ".$hint; + $this->containsHints = true; + } else { + $this->message .= ', '.$hint; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidDefinitionException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidDefinitionException.php new file mode 100644 index 0000000..98310da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidDefinitionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * Thrown when an error is detected in a node Definition. + * + * @author Victor Berchet + */ +class InvalidDefinitionException extends Exception +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidTypeException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidTypeException.php new file mode 100644 index 0000000..d7ca8c9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/InvalidTypeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown if an invalid type is encountered. + * + * @author Johannes M. Schmitt + */ +class InvalidTypeException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/UnsetKeyException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/UnsetKeyException.php new file mode 100644 index 0000000..863181a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Exception/UnsetKeyException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is usually not encountered by the end-user, but only used + * internally to signal the parent scope to unset a key. + * + * @author Johannes M. Schmitt + */ +class UnsetKeyException extends Exception +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/FloatNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/FloatNode.php new file mode 100644 index 0000000..5e1af17 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/FloatNode.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a float value in the config tree. + * + * @author Jeanmonod David + */ +class FloatNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + // Integers are also accepted, we just cast them + if (is_int($value)) { + $value = (float) $value; + } + + if (!is_float($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected float, but got %s.', $this->getPath(), gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/IntegerNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/IntegerNode.php new file mode 100644 index 0000000..ba23070 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/IntegerNode.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents an integer value in the config tree. + * + * @author Jeanmonod David + */ +class IntegerNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!is_int($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected int, but got %s.', $this->getPath(), gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NodeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NodeInterface.php new file mode 100644 index 0000000..b9bddc4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NodeInterface.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * Common Interface among all nodes. + * + * In most cases, it is better to inherit from BaseNode instead of implementing + * this interface yourself. + * + * @author Johannes M. Schmitt + */ +interface NodeInterface +{ + /** + * Returns the name of the node. + * + * @return string The name of the node + */ + public function getName(); + + /** + * Returns the path of the node. + * + * @return string The node path + */ + public function getPath(); + + /** + * Returns true when the node is required. + * + * @return bool If the node is required + */ + public function isRequired(); + + /** + * Returns true when the node has a default value. + * + * @return bool If the node has a default value + */ + public function hasDefaultValue(); + + /** + * Returns the default value of the node. + * + * @return mixed The default value + * + * @throws \RuntimeException if the node has no default value + */ + public function getDefaultValue(); + + /** + * Normalizes the supplied value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + */ + public function normalize($value); + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged values + */ + public function merge($leftSide, $rightSide); + + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + */ + public function finalize($value); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NumericNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NumericNode.php new file mode 100644 index 0000000..439935e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/NumericNode.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a numeric value in the config tree. + * + * @author David Jeanmonod + */ +class NumericNode extends ScalarNode +{ + protected $min; + protected $max; + + public function __construct($name, NodeInterface $parent = null, $min = null, $max = null) + { + parent::__construct($name, $parent); + $this->min = $min; + $this->max = $max; + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + + $errorMsg = null; + if (isset($this->min) && $value < $this->min) { + $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); + } + if (isset($this->max) && $value > $this->max) { + $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); + } + if (isset($errorMsg)) { + $ex = new InvalidConfigurationException($errorMsg); + $ex->setPath($this->getPath()); + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a numeric value cannot be empty + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Processor.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Processor.php new file mode 100644 index 0000000..025e693 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/Processor.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * This class is the entry point for config normalization/merging/finalization. + * + * @author Johannes M. Schmitt + */ +class Processor +{ + /** + * Processes an array of configurations. + * + * @param NodeInterface $configTree The node tree describing the configuration + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function process(NodeInterface $configTree, array $configs) + { + $currentConfig = array(); + foreach ($configs as $config) { + $config = $configTree->normalize($config); + $currentConfig = $configTree->merge($currentConfig, $config); + } + + return $configTree->finalize($currentConfig); + } + + /** + * Processes an array of configurations. + * + * @param ConfigurationInterface $configuration The configuration class + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); + } + + /** + * Normalizes a configuration entry. + * + * This method returns a normalize configuration array for a given key + * to remove the differences due to the original format (YAML and XML mainly). + * + * Here is an example. + * + * The configuration in XML: + * + * twig.extension.foo + * twig.extension.bar + * + * And the same configuration in YAML: + * + * extensions: ['twig.extension.foo', 'twig.extension.bar'] + * + * @param array $config A config array + * @param string $key The key to normalize + * @param string $plural The plural form of the key if it is irregular + * + * @return array + */ + public static function normalizeConfig($config, $key, $plural = null) + { + if (null === $plural) { + $plural = $key.'s'; + } + + if (isset($config[$plural])) { + return $config[$plural]; + } + + if (isset($config[$key])) { + if (is_string($config[$key]) || !is_int(key($config[$key]))) { + // only one + return array($config[$key]); + } + + return $config[$key]; + } + + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php new file mode 100644 index 0000000..8bbb84d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypeNodeInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * This interface must be implemented by nodes which can be used as prototypes. + * + * @author Johannes M. Schmitt + */ +interface PrototypeNodeInterface extends NodeInterface +{ + /** + * Sets the name of the node. + * + * @param string $name The name of the node + */ + public function setName($name); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php new file mode 100644 index 0000000..931b467 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php @@ -0,0 +1,331 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\DuplicateKeyException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; +use Symfony\Component\Config\Definition\Exception\Exception; + +/** + * Represents a prototyped Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class PrototypedArrayNode extends ArrayNode +{ + protected $prototype; + protected $keyAttribute; + protected $removeKeyAttribute = false; + protected $minNumberOfElements = 0; + protected $defaultValue = array(); + protected $defaultChildren; + + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + * + * @param int $number + */ + public function setMinNumberOfElements($number) + { + $this->minNumberOfElements = $number; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * array( + * array('id' => 'my_name', 'foo' => 'bar'), + * ); + * + * becomes + * + * array( + * 'my_name' => array('foo' => 'bar'), + * ); + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * @param string $attribute The name of the attribute which value is to be used as a key + * @param bool $remove Whether or not to remove the key + */ + public function setKeyAttribute($attribute, $remove = true) + { + $this->keyAttribute = $attribute; + $this->removeKeyAttribute = $remove; + } + + /** + * Retrieves the name of the attribute which value should be used as key. + * + * @return string The name of the attribute + */ + public function getKeyAttribute() + { + return $this->keyAttribute; + } + + /** + * Sets the default value of this node. + * + * @param string $value + * + * @throws \InvalidArgumentException if the default value is not an array + */ + public function setDefaultValue($value) + { + if (!is_array($value)) { + throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.'); + } + + $this->defaultValue = $value; + } + + /** + * Checks if the node has a default value. + * + * @return bool + */ + public function hasDefaultValue() + { + return true; + } + + /** + * Adds default children when none are set. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + */ + public function setAddChildrenIfNoneSet($children = array('defaults')) + { + if (null === $children) { + $this->defaultChildren = array('defaults'); + } else { + $this->defaultChildren = is_int($children) && $children > 0 ? range(1, $children) : (array) $children; + } + } + + /** + * Retrieves the default value. + * + * The default value could be either explicited or derived from the prototype + * default value. + * + * @return array The default value + */ + public function getDefaultValue() + { + if (null !== $this->defaultChildren) { + $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array(); + $defaults = array(); + foreach (array_values($this->defaultChildren) as $i => $name) { + $defaults[null === $this->keyAttribute ? $i : $name] = $default; + } + + return $defaults; + } + + return $this->defaultValue; + } + + /** + * Sets the node prototype. + * + * @param PrototypeNodeInterface $node + */ + public function setPrototype(PrototypeNodeInterface $node) + { + $this->prototype = $node; + } + + /** + * Retrieves the prototype. + * + * @return PrototypeNodeInterface The prototype + */ + public function getPrototype() + { + return $this->prototype; + } + + /** + * Disable adding concrete children for prototyped nodes. + * + * @param NodeInterface $node The child node to add + * + * @throws Exception + */ + public function addChild(NodeInterface $node) + { + throw new Exception('A prototyped array node can not have concrete children.'); + } + + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalized value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (false === $value) { + $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value)); + throw new UnsetKeyException($msg); + } + + foreach ($value as $k => $v) { + $this->prototype->setName($k); + try { + $value[$k] = $this->prototype->finalize($v); + } catch (UnsetKeyException $e) { + unset($value[$k]); + } + } + + if (count($value) < $this->minNumberOfElements) { + $msg = sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements); + $ex = new InvalidConfigurationException($msg); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + * @throws DuplicateKeyException + */ + protected function normalizeValue($value) + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $isAssoc = array_keys($value) !== range(0, count($value) - 1); + $normalized = array(); + foreach ($value as $k => $v) { + if (null !== $this->keyAttribute && is_array($v)) { + if (!isset($v[$this->keyAttribute]) && is_int($k) && !$isAssoc) { + $msg = sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath()); + $ex = new InvalidConfigurationException($msg); + $ex->setPath($this->getPath()); + + throw $ex; + } elseif (isset($v[$this->keyAttribute])) { + $k = $v[$this->keyAttribute]; + + // remove the key attribute when required + if ($this->removeKeyAttribute) { + unset($v[$this->keyAttribute]); + } + + // if only "value" is left + if (1 == count($v) && isset($v['value'])) { + $v = $v['value']; + } + } + + if (array_key_exists($k, $normalized)) { + $msg = sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath()); + $ex = new DuplicateKeyException($msg); + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + $this->prototype->setName($k); + if (null !== $this->keyAttribute || $isAssoc) { + $normalized[$k] = $this->prototype->normalize($v); + } else { + $normalized[] = $this->prototype->normalize($v); + } + } + + return $normalized; + } + + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge. + * @param mixed $rightSide The right side to merge. + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // prototype, and key is irrelevant, so simply append the element + if (null === $this->keyAttribute) { + $leftSide[] = $v; + continue; + } + + // no conflict + if (!array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf( + 'You are not allowed to define new elements for path "%s". '. + 'Please define all elements for this path in one config file.', + $this->getPath() + )); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + $this->prototype->setName($k); + $leftSide[$k] = $this->prototype->merge($leftSide[$k], $v); + } + + return $leftSide; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ScalarNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ScalarNode.php new file mode 100644 index 0000000..6b3fd0b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/ScalarNode.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a scalar value in the config tree. + * + * The following values are considered scalars: + * * booleans + * * strings + * * null + * * integers + * * floats + * + * @author Johannes M. Schmitt + */ +class ScalarNode extends VariableNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!is_scalar($value) && null !== $value) { + $ex = new InvalidTypeException(sprintf( + 'Invalid type for path "%s". Expected scalar, but got %s.', + $this->getPath(), + gettype($value) + )); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + return null === $value || '' === $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/VariableNode.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/VariableNode.php new file mode 100644 index 0000000..e2c0677 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Definition/VariableNode.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a value of variable type in the config tree. + * + * This node is intended for values of arbitrary type. + * Any PHP type is accepted as a value. + * + * @author Jeremy Mikola + */ +class VariableNode extends BaseNode implements PrototypeNodeInterface +{ + protected $defaultValueSet = false; + protected $defaultValue; + protected $allowEmptyValue = true; + + /** + * {@inheritdoc} + */ + public function setDefaultValue($value) + { + $this->defaultValueSet = true; + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->defaultValueSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + $v = $this->defaultValue; + + return $v instanceof \Closure ? $v() : $v; + } + + /** + * Sets if this node is allowed to have an empty value. + * + * @param bool $boolean True if this entity will accept empty values. + */ + public function setAllowEmptyValue($boolean) + { + $this->allowEmptyValue = (bool) $boolean; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { + $ex = new InvalidConfigurationException(sprintf( + 'The path "%s" cannot contain an empty value, but got %s.', + $this->getPath(), + json_encode($value) + )); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function normalizeValue($value) + { + return $value; + } + + /** + * {@inheritdoc} + */ + protected function mergeValues($leftSide, $rightSide) + { + return $rightSide; + } + + /** + * Evaluates if the given value is to be treated as empty. + * + * By default, PHP's empty() function is used to test for emptiness. This + * method may be overridden by subtypes to better match their understanding + * of empty data. + * + * @param mixed $value + * + * @return bool + */ + protected function isValueEmpty($value) + { + return empty($value); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php new file mode 100644 index 0000000..6a3b01c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderImportCircularReferenceException.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * Exception class for when a circular reference is detected when importing resources. + * + * @author Fabien Potencier + */ +class FileLoaderImportCircularReferenceException extends FileLoaderLoadException +{ + public function __construct(array $resources, $code = null, $previous = null) + { + $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); + + \Exception::__construct($message, $code, $previous); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php new file mode 100644 index 0000000..6af3dd0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Exception/FileLoaderLoadException.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * Exception class for when a resource cannot be loaded or imported. + * + * @author Ryan Weaver + */ +class FileLoaderLoadException extends \Exception +{ + /** + * @param string $resource The resource that could not be imported + * @param string $sourceResource The original resource importing the new resource + * @param int $code The error code + * @param \Exception $previous A previous exception + */ + public function __construct($resource, $sourceResource = null, $code = null, $previous = null) + { + $message = ''; + if ($previous) { + // Include the previous exception, to help the user see what might be the underlying cause + + // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... + if ('.' === substr($previous->getMessage(), -1)) { + $trimmedMessage = substr($previous->getMessage(), 0, -1); + $message .= sprintf('%s', $trimmedMessage).' in '; + } else { + $message .= sprintf('%s', $previous->getMessage()).' in '; + } + $message .= $resource.' '; + + // show tweaked trace to complete the human readable sentence + if (null === $sourceResource) { + $message .= sprintf('(which is loaded in resource "%s")', $this->varToString($resource)); + } else { + $message .= sprintf('(which is being imported from "%s")', $this->varToString($sourceResource)); + } + $message .= '.'; + + // if there's no previous message, present it the default way + } elseif (null === $sourceResource) { + $message .= sprintf('Cannot load resource "%s".', $this->varToString($resource)); + } else { + $message .= sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource)); + } + + // Is the resource located inside a bundle? + if ('@' === $resource[0]) { + $parts = explode(DIRECTORY_SEPARATOR, $resource); + $bundle = substr($parts[0], 1); + $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); + $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); + } + + parent::__construct($message, $code, $previous); + } + + protected function varToString($var) + { + if (is_object($var)) { + return sprintf('Object(%s)', get_class($var)); + } + + if (is_array($var)) { + $a = array(); + foreach ($var as $k => $v) { + $a[] = sprintf('%s => %s', $k, $this->varToString($v)); + } + + return sprintf('Array(%s)', implode(', ', $a)); + } + + if (is_resource($var)) { + return sprintf('Resource(%s)', get_resource_type($var)); + } + + if (null === $var) { + return 'null'; + } + + if (false === $var) { + return 'false'; + } + + if (true === $var) { + return 'true'; + } + + return (string) $var; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocator.php b/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocator.php new file mode 100644 index 0000000..c6600c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocator.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * FileLocator uses an array of pre-defined paths to find files. + * + * @author Fabien Potencier + */ +class FileLocator implements FileLocatorInterface +{ + protected $paths; + + /** + * Constructor. + * + * @param string|array $paths A path or an array of paths where to look for resources + */ + public function __construct($paths = array()) + { + $this->paths = (array) $paths; + } + + /** + * {@inheritdoc} + */ + public function locate($name, $currentPath = null, $first = true) + { + if ('' == $name) { + throw new \InvalidArgumentException('An empty file name is not valid to be located.'); + } + + if ($this->isAbsolutePath($name)) { + if (!file_exists($name)) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name)); + } + + return $name; + } + + $paths = $this->paths; + + if (null !== $currentPath) { + array_unshift($paths, $currentPath); + } + + $paths = array_unique($paths); + $filepaths = array(); + + foreach ($paths as $path) { + if (file_exists($file = $path.DIRECTORY_SEPARATOR.$name)) { + if (true === $first) { + return $file; + } + $filepaths[] = $file; + } + } + + if (!$filepaths) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths))); + } + + return $filepaths; + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + private function isAbsolutePath($file) + { + if ($file[0] === '/' || $file[0] === '\\' + || (strlen($file) > 3 && ctype_alpha($file[0]) + && $file[1] === ':' + && ($file[2] === '\\' || $file[2] === '/') + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocatorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocatorInterface.php new file mode 100644 index 0000000..6605798 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/FileLocatorInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * @author Fabien Potencier + */ +interface FileLocatorInterface +{ + /** + * Returns a full path for a given file name. + * + * @param string $name The file name to locate + * @param string|null $currentPath The current path + * @param bool $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file or an array of file paths + * + * @throws \InvalidArgumentException When file is not found + */ + public function locate($name, $currentPath = null, $first = true); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Config/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/DelegatingLoader.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/DelegatingLoader.php new file mode 100644 index 0000000..3097878 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/DelegatingLoader.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +/** + * DelegatingLoader delegates loading to other loaders using a loader resolver. + * + * This loader acts as an array of LoaderInterface objects - each having + * a chance to load a given resource (handled by the resolver) + * + * @author Fabien Potencier + */ +class DelegatingLoader extends Loader +{ + /** + * Constructor. + * + * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + */ + public function __construct(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + if (false === $loader = $this->resolver->resolve($resource, $type)) { + throw new FileLoaderLoadException($resource); + } + + return $loader->load($resource, $type); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return false !== $this->resolver->resolve($resource, $type); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.php new file mode 100644 index 0000000..cdc4329 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/FileLoader.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Exception\FileLoaderLoadException; +use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; + +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends Loader +{ + /** + * @var array + */ + protected static $loading = array(); + + /** + * @var FileLocatorInterface + */ + protected $locator; + + private $currentDir; + + /** + * Constructor. + * + * @param FileLocatorInterface $locator A FileLocatorInterface instance + */ + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * Sets the current directory. + * + * @param string $dir + */ + public function setCurrentDir($dir) + { + $this->currentDir = $dir; + } + + /** + * Returns the file locator used by this loader. + * + * @return FileLocatorInterface + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string|null $type The resource type or null if unknown + * @param bool $ignoreErrors Whether to ignore import errors or not + * @param string|null $sourceResource The original resource importing the new resource + * + * @return mixed + * + * @throws FileLoaderLoadException + * @throws FileLoaderImportCircularReferenceException + */ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + try { + $loader = $this->resolve($resource, $type); + + if ($loader instanceof self && null !== $this->currentDir) { + $resource = $loader->getLocator()->locate($resource, $this->currentDir, false); + } + + $resources = is_array($resource) ? $resource : array($resource); + for ($i = 0; $i < $resourcesCount = count($resources); ++$i) { + if (isset(self::$loading[$resources[$i]])) { + if ($i == $resourcesCount - 1) { + throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading)); + } + } else { + $resource = $resources[$i]; + break; + } + } + self::$loading[$resource] = true; + + try { + $ret = $loader->load($resource, $type); + } finally { + unset(self::$loading[$resource]); + } + + return $ret; + } catch (FileLoaderImportCircularReferenceException $e) { + throw $e; + } catch (\Exception $e) { + if (!$ignoreErrors) { + // prevent embedded imports from nesting multiple exceptions + if ($e instanceof FileLoaderLoadException) { + throw $e; + } + + throw new FileLoaderLoadException($resource, $sourceResource, null, $e); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/Loader.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/Loader.php new file mode 100644 index 0000000..de4e127 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/Loader.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +/** + * Loader is the abstract class used by all built-in loaders. + * + * @author Fabien Potencier + */ +abstract class Loader implements LoaderInterface +{ + protected $resolver; + + /** + * {@inheritdoc} + */ + public function getResolver() + { + return $this->resolver; + } + + /** + * {@inheritdoc} + */ + public function setResolver(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * Imports a resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return mixed + */ + public function import($resource, $type = null) + { + return $this->resolve($resource, $type)->load($resource, $type); + } + + /** + * Finds a loader able to load an imported resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return LoaderInterface A LoaderInterface instance + * + * @throws FileLoaderLoadException If no loader is found + */ + public function resolve($resource, $type = null) + { + if ($this->supports($resource, $type)) { + return $this; + } + + $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type); + + if (false === $loader) { + throw new FileLoaderLoadException($resource); + } + + return $loader; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderInterface.php new file mode 100644 index 0000000..dd0a85a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderInterface.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderInterface is the interface implemented by all loader classes. + * + * @author Fabien Potencier + */ +interface LoaderInterface +{ + /** + * Loads a resource. + * + * @param mixed $resource The resource + * @param string|null $type The resource type or null if unknown + * + * @throws \Exception If something went wrong + */ + public function load($resource, $type = null); + + /** + * Returns whether this class supports the given resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return bool True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null); + + /** + * Gets the loader resolver. + * + * @return LoaderResolverInterface A LoaderResolverInterface instance + */ + public function getResolver(); + + /** + * Sets the loader resolver. + * + * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + */ + public function setResolver(LoaderResolverInterface $resolver); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolver.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolver.php new file mode 100644 index 0000000..dc6846d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolver.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderResolver selects a loader for a given resource. + * + * A resource can be anything (e.g. a full path to a config file or a Closure). + * Each loader determines whether it can load a resource and how. + * + * @author Fabien Potencier + */ +class LoaderResolver implements LoaderResolverInterface +{ + /** + * @var LoaderInterface[] An array of LoaderInterface objects + */ + private $loaders = array(); + + /** + * Constructor. + * + * @param LoaderInterface[] $loaders An array of loaders + */ + public function __construct(array $loaders = array()) + { + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * {@inheritdoc} + */ + public function resolve($resource, $type = null) + { + foreach ($this->loaders as $loader) { + if ($loader->supports($resource, $type)) { + return $loader; + } + } + + return false; + } + + /** + * Adds a loader. + * + * @param LoaderInterface $loader A LoaderInterface instance + */ + public function addLoader(LoaderInterface $loader) + { + $this->loaders[] = $loader; + $loader->setResolver($this); + } + + /** + * Returns the registered loaders. + * + * @return LoaderInterface[] An array of LoaderInterface instances + */ + public function getLoaders() + { + return $this->loaders; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php new file mode 100644 index 0000000..40f1a1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Loader/LoaderResolverInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderResolverInterface selects a loader for a given resource. + * + * @author Fabien Potencier + */ +interface LoaderResolverInterface +{ + /** + * Returns a loader able to load the resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return LoaderInterface|false The loader or false if none is able to load the resource + */ + public function resolve($resource, $type = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/README.md b/vendor/symfony/symfony/src/Symfony/Component/Config/README.md new file mode 100644 index 0000000..690d7d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/README.md @@ -0,0 +1,17 @@ +Config Component +================ + +Config provides the infrastructure for loading configurations from different +data sources and optionally monitoring these data sources for changes. There +are additional tools for validating, normalizing and handling of defaults that +can optionally be used to convert from different formats to arrays. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Config/ + $ composer install + $ phpunit + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/DirectoryResource.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/DirectoryResource.php new file mode 100644 index 0000000..3841393 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/DirectoryResource.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * DirectoryResource represents a resources stored in a subdirectory tree. + * + * @author Fabien Potencier + */ +class DirectoryResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $pattern; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + * @param string|null $pattern A pattern to restrict monitored files + */ + public function __construct($resource, $pattern = null) + { + $this->resource = $resource; + $this->pattern = $pattern; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return md5(serialize(array($this->resource, $this->pattern))); + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * Returns the pattern to restrict monitored files. + * + * @return string|null + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + if (!is_dir($this->resource)) { + return false; + } + + $newestMTime = filemtime($this->resource); + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { + // if regex filtering is enabled only check matching files + if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) { + continue; + } + + // always monitor directories for changes, except the .. entries + // (otherwise deleted files wouldn't get detected) + if ($file->isDir() && '/..' === substr($file, -3)) { + continue; + } + + $newestMTime = max($file->getMTime(), $newestMTime); + } + + return $newestMTime < $timestamp; + } + + public function serialize() + { + return serialize(array($this->resource, $this->pattern)); + } + + public function unserialize($serialized) + { + list($this->resource, $this->pattern) = unserialize($serialized); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileExistenceResource.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileExistenceResource.php new file mode 100644 index 0000000..349402e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileExistenceResource.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + */ +class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + + private $exists; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = (string) $resource; + $this->exists = file_exists($resource); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return file_exists($this->resource) === $this->exists; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->resource, $this->exists)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileResource.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileResource.php new file mode 100644 index 0000000..54a974c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/FileResource.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileResource represents a resource stored on the filesystem. + * + * The resource can be a file or a directory. + * + * @author Fabien Potencier + */ +class FileResource implements SelfCheckingResourceInterface, \Serializable +{ + /** + * @var string|false + */ + private $resource; + + /** + * Constructor. + * + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = realpath($resource); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return (string) $this->resource; + } + + /** + * @return string|false The canonicalized, absolute path to the resource or false if the resource does not exist. + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + if (false === $this->resource || !file_exists($this->resource)) { + return false; + } + + return filemtime($this->resource) <= $timestamp; + } + + public function serialize() + { + return serialize($this->resource); + } + + public function unserialize($serialized) + { + $this->resource = unserialize($serialized); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/ResourceInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/ResourceInterface.php new file mode 100644 index 0000000..d98fd42 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/ResourceInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ResourceInterface is the interface that must be implemented by all Resource classes. + * + * @author Fabien Potencier + */ +interface ResourceInterface +{ + /** + * Returns a string representation of the Resource. + * + * This method is necessary to allow for resource de-duplication, for example by means + * of array_unique(). The string returned need not have a particular meaning, but has + * to be identical for different ResourceInterface instances referring to the same + * resource; and it should be unlikely to collide with that of other, unrelated + * resource instances. + * + * @return string A string representation unique to the underlying Resource + */ + public function __toString(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php new file mode 100644 index 0000000..d72203b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/SelfCheckingResourceChecker.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\Config\ResourceCheckerInterface; + +/** + * Resource checker for instances of SelfCheckingResourceInterface. + * + * As these resources perform the actual check themselves, we can provide + * this class as a standard way of validating them. + * + * @author Matthias Pigulla + */ +class SelfCheckingResourceChecker implements ResourceCheckerInterface +{ + public function supports(ResourceInterface $metadata) + { + return $metadata instanceof SelfCheckingResourceInterface; + } + + public function isFresh(ResourceInterface $resource, $timestamp) + { + /* @var SelfCheckingResourceInterface $resource */ + return $resource->isFresh($timestamp); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php new file mode 100644 index 0000000..b3260f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Resource/SelfCheckingResourceInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * Interface for Resources that can check for freshness autonomously, + * without special support from external services. + * + * @author Matthias Pigulla + */ +interface SelfCheckingResourceInterface extends ResourceInterface +{ + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param int $timestamp The last time the resource was loaded + * + * @return bool True if the resource has not been updated, false otherwise + */ + public function isFresh($timestamp); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCache.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCache.php new file mode 100644 index 0000000..3cef781 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCache.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface + * to check whether cached data is still fresh. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCache implements ConfigCacheInterface +{ + /** + * @var string + */ + private $file; + + /** + * @var ResourceCheckerInterface[] + */ + private $resourceCheckers; + + /** + * @param string $file The absolute cache path + * @param ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check + */ + public function __construct($file, array $resourceCheckers = array()) + { + $this->file = $file; + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->file; + } + + /** + * Checks if the cache is still fresh. + * + * This implementation will make a decision solely based on the ResourceCheckers + * passed in the constructor. + * + * The first ResourceChecker that supports a given resource is considered authoritative. + * Resources with no matching ResourceChecker will silently be ignored and considered fresh. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!is_file($this->file)) { + return false; + } + + if (!$this->resourceCheckers) { + return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all + } + + $metadata = $this->getMetaFile(); + if (!is_file($metadata)) { + return true; + } + + $time = filemtime($this->file); + $meta = unserialize(file_get_contents($metadata)); + + foreach ($meta as $resource) { + /* @var ResourceInterface $resource */ + foreach ($this->resourceCheckers as $checker) { + if (!$checker->supports($resource)) { + continue; // next checker + } + if ($checker->isFresh($resource, $time)) { + break; // no need to further check this resource + } + + return false; // cache is stale + } + // no suitable checker found, ignore this resource + } + + return true; + } + + /** + * Writes cache. + * + * @param string $content The content to write in the cache + * @param ResourceInterface[] $metadata An array of metadata + * + * @throws \RuntimeException When cache file can't be written + */ + public function write($content, array $metadata = null) + { + $mode = 0666; + $umask = umask(); + $filesystem = new Filesystem(); + $filesystem->dumpFile($this->file, $content, null); + try { + $filesystem->chmod($this->file, $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + + if (null !== $metadata) { + $filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null); + try { + $filesystem->chmod($this->getMetaFile(), $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + } + + /** + * Gets the meta file path. + * + * @return string The meta file path + */ + private function getMetaFile() + { + return $this->file.'.meta'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php new file mode 100644 index 0000000..61d732c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCacheFactory.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * A ConfigCacheFactory implementation that validates the + * cache with an arbitrary set of ResourceCheckers. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface +{ + /** + * @var ResourceCheckerInterface[] + */ + private $resourceCheckers = array(); + + /** + * @param ResourceCheckerInterface[] $resourceCheckers + */ + public function __construct(array $resourceCheckers = array()) + { + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback))); + } + + $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); + if (!$cache->isFresh()) { + call_user_func($callback, $cache); + } + + return $cache; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerInterface.php new file mode 100644 index 0000000..aaa5c50 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ResourceCheckers. + * + * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated + * metadata resources are passed to ResourceCheckers. The ResourceCheckers + * can then inspect the resources and decide whether the cache can be considered + * fresh or not. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +interface ResourceCheckerInterface +{ + /** + * Queries the ResourceChecker whether it can validate a given + * resource or not. + * + * @param ResourceInterface $metadata The resource to be checked for freshness + * + * @return bool True if the ResourceChecker can handle this resource type, false if not + */ + public function supports(ResourceInterface $metadata); + + /** + * Validates the resource. + * + * @param ResourceInterface $resource The resource to be validated. + * @param int $timestamp The timestamp at which the cache associated with this resource was created. + * + * @return bool True if the resource has not changed since the given timestamp, false otherwise. + */ + public function isFresh(ResourceInterface $resource, $timestamp); + +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php new file mode 100644 index 0000000..291243d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheFactoryTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use Symfony\Component\Config\ConfigCacheFactory; + +class ConfigCacheFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid type for callback argument. Expected callable, but got "object". + */ + public function testCachWithInvalidCallback() + { + $cacheFactory = new ConfigCacheFactory(true); + + $cacheFactory->cache('file', new \stdClass()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheTest.php new file mode 100644 index 0000000..faa37f0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ConfigCacheTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Config\Tests\Resource\ResourceStub; + +class ConfigCacheTest extends \PHPUnit_Framework_TestCase +{ + private $cacheFile = null; + + protected function setUp() + { + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + } + + protected function tearDown() + { + $files = array($this->cacheFile, $this->cacheFile.'.meta'); + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + /** + * @dataProvider debugModes + */ + public function testCacheIsNotValidIfNothingHasBeenCached($debug) + { + unlink($this->cacheFile); // remove tempnam() side effect + $cache = new ConfigCache($this->cacheFile, $debug); + + $this->assertFalse($cache->isFresh()); + } + + public function testIsAlwaysFreshInProduction() + { + $staleResource = new ResourceStub(); + $staleResource->setFresh(false); + + $cache = new ConfigCache($this->cacheFile, false); + $cache->write('', array($staleResource)); + + $this->assertTrue($cache->isFresh()); + } + + /** + * @dataProvider debugModes + */ + public function testIsFreshWhenNoResourceProvided($debug) + { + $cache = new ConfigCache($this->cacheFile, $debug); + $cache->write('', array()); + $this->assertTrue($cache->isFresh()); + } + + public function testFreshResourceInDebug() + { + $freshResource = new ResourceStub(); + $freshResource->setFresh(true); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('', array($freshResource)); + + $this->assertTrue($cache->isFresh()); + } + + public function testStaleResourceInDebug() + { + $staleResource = new ResourceStub(); + $staleResource->setFresh(false); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('', array($staleResource)); + + $this->assertFalse($cache->isFresh()); + } + + public function debugModes() + { + return array( + array(true), + array(false), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php new file mode 100644 index 0000000..993941f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\ScalarNode; + +class ArrayNodeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed() + { + $node = new ArrayNode('root'); + $node->normalize(false); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Unrecognized option "foo" under "root" + */ + public function testExceptionThrownOnUnrecognizedChild() + { + $node = new ArrayNode('root'); + $node->normalize(array('foo' => 'bar')); + } + + public function ignoreAndRemoveMatrixProvider() + { + $unrecognizedOptionException = new InvalidConfigurationException('Unrecognized option "foo" under "root"'); + + return array( + array(true, true, array(), 'no exception is thrown for an unrecognized child if the ignoreExtraKeys option is set to true'), + array(true, false, array('foo' => 'bar'), 'extra keys are not removed when ignoreExtraKeys second option is set to false'), + array(false, true, $unrecognizedOptionException), + array(false, false, $unrecognizedOptionException), + ); + } + + /** + * @dataProvider ignoreAndRemoveMatrixProvider + */ + public function testIgnoreAndRemoveBehaviors($ignore, $remove, $expected, $message = '') + { + if ($expected instanceof \Exception) { + $this->setExpectedException(get_class($expected), $expected->getMessage()); + } + $node = new ArrayNode('root'); + $node->setIgnoreExtraKeys($ignore, $remove); + $result = $node->normalize(array('foo' => 'bar')); + $this->assertSame($expected, $result, $message); + } + + /** + * @dataProvider getPreNormalizationTests + */ + public function testPreNormalize($denormalized, $normalized) + { + $node = new ArrayNode('foo'); + + $r = new \ReflectionMethod($node, 'preNormalize'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($node, $denormalized)); + } + + public function getPreNormalizationTests() + { + return array( + array( + array('foo-bar' => 'foo'), + array('foo_bar' => 'foo'), + ), + array( + array('foo-bar_moo' => 'foo'), + array('foo-bar_moo' => 'foo'), + ), + array( + array('anything-with-dash-and-no-underscore' => 'first', 'no_dash' => 'second'), + array('anything_with_dash_and_no_underscore' => 'first', 'no_dash' => 'second'), + ), + array( + array('foo-bar' => null, 'foo_bar' => 'foo'), + array('foo-bar' => null, 'foo_bar' => 'foo'), + ), + ); + } + + /** + * @dataProvider getZeroNamedNodeExamplesData + */ + public function testNodeNameCanBeZero($denormalized, $normalized) + { + $zeroNode = new ArrayNode(0); + $zeroNode->addChild(new ScalarNode('name')); + $fiveNode = new ArrayNode(5); + $fiveNode->addChild(new ScalarNode(0)); + $fiveNode->addChild(new ScalarNode('new_key')); + $rootNode = new ArrayNode('root'); + $rootNode->addChild($zeroNode); + $rootNode->addChild($fiveNode); + $rootNode->addChild(new ScalarNode('string_key')); + $r = new \ReflectionMethod($rootNode, 'normalizeValue'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($rootNode, $denormalized)); + } + + public function getZeroNamedNodeExamplesData() + { + return array( + array( + array( + 0 => array( + 'name' => 'something', + ), + 5 => array( + 0 => 'this won\'t work too', + 'new_key' => 'some other value', + ), + 'string_key' => 'just value', + ), + array( + 0 => array( + 'name' => 'something', + ), + 5 => array( + 0 => 'this won\'t work too', + 'new_key' => 'some other value', + ), + 'string_key' => 'just value', + ), + ), + ); + } + + /** + * @dataProvider getPreNormalizedNormalizedOrderedData + */ + public function testChildrenOrderIsMaintainedOnNormalizeValue($prenormalized, $normalized) + { + $scalar1 = new ScalarNode('1'); + $scalar2 = new ScalarNode('2'); + $scalar3 = new ScalarNode('3'); + $node = new ArrayNode('foo'); + $node->addChild($scalar1); + $node->addChild($scalar3); + $node->addChild($scalar2); + + $r = new \ReflectionMethod($node, 'normalizeValue'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($node, $prenormalized)); + } + + public function getPreNormalizedNormalizedOrderedData() + { + return array( + array( + array('2' => 'two', '1' => 'one', '3' => 'three'), + array('2' => 'two', '1' => 'one', '3' => 'three'), + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/BooleanNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/BooleanNodeTest.php new file mode 100644 index 0000000..b0cb079 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/BooleanNodeTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\BooleanNode; + +class BooleanNodeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new BooleanNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param bool $value + */ + public function testValidNonEmptyValues($value) + { + $node = new BooleanNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return array( + array(false), + array(true), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new BooleanNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(null), + array(''), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php new file mode 100644 index 0000000..e75ed34 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase +{ + public function testAppendingSomeNode() + { + $parent = new ArrayNodeDefinition('root'); + $child = new ScalarNodeDefinition('child'); + + $parent + ->children() + ->scalarNode('foo')->end() + ->scalarNode('bar')->end() + ->end() + ->append($child); + + $this->assertCount(3, $this->getField($parent, 'children')); + $this->assertTrue(in_array($child, $this->getField($parent, 'children'))); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @dataProvider providePrototypeNodeSpecificCalls + */ + public function testPrototypeNodeSpecificOption($method, $args) + { + $node = new ArrayNodeDefinition('root'); + + call_user_func_array(array($node, $method), $args); + + $node->getNode(); + } + + public function providePrototypeNodeSpecificCalls() + { + return array( + array('defaultValue', array(array())), + array('addDefaultChildrenIfNoneSet', array()), + array('requiresAtLeastOneElement', array()), + array('useAttributeAsKey', array('foo')), + ); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + */ + public function testConcreteNodeSpecificOption() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultsIfNotSet() + ->prototype('array') + ; + $node->getNode(); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + */ + public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren() + { + $node = new ArrayNodeDefinition('root'); + $node + ->defaultValue(array()) + ->addDefaultChildrenIfNoneSet('foo') + ->prototype('array') + ; + $node->getNode(); + } + + public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet() + ->prototype('array') + ; + $tree = $node->getNode(); + $this->assertEquals(array(array()), $tree->getDefaultValue()); + } + + /** + * @dataProvider providePrototypedArrayNodeDefaults + */ + public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults) + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet($args) + ->prototype('array') + ; + + try { + $tree = $node->getNode(); + $this->assertFalse($shouldThrowWhenNotUsingAttrAsKey); + $this->assertEquals($defaults, $tree->getDefaultValue()); + } catch (InvalidDefinitionException $e) { + $this->assertTrue($shouldThrowWhenNotUsingAttrAsKey); + } + + $node = new ArrayNodeDefinition('root'); + $node + ->useAttributeAsKey('attr') + ->addDefaultChildrenIfNoneSet($args) + ->prototype('array') + ; + + try { + $tree = $node->getNode(); + $this->assertFalse($shouldThrowWhenUsingAttrAsKey); + $this->assertEquals($defaults, $tree->getDefaultValue()); + } catch (InvalidDefinitionException $e) { + $this->assertTrue($shouldThrowWhenUsingAttrAsKey); + } + } + + public function providePrototypedArrayNodeDefaults() + { + return array( + array(null, true, false, array(array())), + array(2, true, false, array(array(), array())), + array('2', false, true, array('2' => array())), + array('foo', false, true, array('foo' => array())), + array(array('foo'), false, true, array('foo' => array())), + array(array('foo', 'bar'), false, true, array('foo' => array(), 'bar' => array())), + ); + } + + public function testNestedPrototypedArrayNodes() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet() + ->prototype('array') + ->prototype('array') + ; + $node->getNode(); + } + + public function testEnabledNodeDefaults() + { + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals(array('enabled' => false, 'foo' => 'bar'), $node->getNode()->getDefaultValue()); + } + + /** + * @dataProvider getEnableableNodeFixtures + */ + public function testTrueEnableEnabledNode($expected, $config, $message) + { + $processor = new Processor(); + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals( + $expected, + $processor->process($node->getNode(), $config), + $message + ); + } + + public function getEnableableNodeFixtures() + { + return array( + array(array('enabled' => true, 'foo' => 'bar'), array(true), 'true enables an enableable node'), + array(array('enabled' => true, 'foo' => 'bar'), array(null), 'null enables an enableable node'), + array(array('enabled' => true, 'foo' => 'bar'), array(array('enabled' => true)), 'An enableable node can be enabled'), + array(array('enabled' => true, 'foo' => 'baz'), array(array('foo' => 'baz')), 'any configuration enables an enableable node'), + array(array('enabled' => false, 'foo' => 'baz'), array(array('foo' => 'baz', 'enabled' => false)), 'An enableable node can be disabled'), + array(array('enabled' => false, 'foo' => 'bar'), array(false), 'false disables an enableable node'), + ); + } + + protected function getField($object, $field) + { + $reflection = new \ReflectionProperty($object, $field); + $reflection->setAccessible(true); + + return $reflection->getValue($object); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php new file mode 100644 index 0000000..4a6716c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition; + +class BooleanNodeDefinitionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to BooleanNodeDefinition. + */ + public function testCannotBeEmptyThrowsAnException() + { + $def = new BooleanNodeDefinition('foo'); + $def->cannotBeEmpty(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php new file mode 100644 index 0000000..69f7fcf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition; + +class EnumNodeDefinitionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage ->values() must be called with at least two distinct values. + */ + public function testNoDistinctValues() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array('foo', 'foo')); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must call ->values() on enum nodes. + */ + public function testNoValuesPassed() + { + $def = new EnumNodeDefinition('foo'); + $def->getNode(); + } + + public function testGetNode() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array('foo', 'bar')); + + $node = $def->getNode(); + $this->assertEquals(array('foo', 'bar'), $node->getValues()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php new file mode 100644 index 0000000..147bf13 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/ExprBuilderTest.php @@ -0,0 +1,215 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class ExprBuilderTest extends \PHPUnit_Framework_TestCase +{ + public function testAlwaysExpression() + { + $test = $this->getTestBuilder() + ->always($this->returnClosure('new_value')) + ->end(); + + $this->assertFinalizedValueIs('new_value', $test); + } + + public function testIfTrueExpression() + { + $test = $this->getTestBuilder() + ->ifTrue() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => true)); + + $test = $this->getTestBuilder() + ->ifTrue(function ($v) { return true; }) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifTrue(function ($v) { return false; }) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfStringExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifString() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs(45, $test, array('key' => 45)); + } + + public function testIfNullExpression() + { + $test = $this->getTestBuilder() + ->ifNull() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => null)); + + $test = $this->getTestBuilder() + ->ifNull() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfArrayExpression() + { + $test = $this->getTestBuilder() + ->ifArray() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => array())); + + $test = $this->getTestBuilder() + ->ifArray() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfInArrayExpression() + { + $test = $this->getTestBuilder() + ->ifInArray(array('foo', 'bar', 'value')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifInArray(array('foo', 'bar')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfNotInArrayExpression() + { + $test = $this->getTestBuilder() + ->ifNotInArray(array('foo', 'bar')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifNotInArray(array('foo', 'bar', 'value_from_config')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + } + + public function testThenEmptyArrayExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenEmptyArray() + ->end(); + $this->assertFinalizedValueIs(array(), $test); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testThenInvalid() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenInvalid('Invalid value') + ->end(); + $this->finalizeTestBuilder($test); + } + + public function testThenUnsetExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenUnset() + ->end(); + $this->assertEquals(array(), $this->finalizeTestBuilder($test)); + } + + /** + * Create a test treebuilder with a variable node, and init the validation. + * + * @return TreeBuilder + */ + protected function getTestBuilder() + { + $builder = new TreeBuilder(); + + return $builder + ->root('test') + ->children() + ->variableNode('key') + ->validate() + ; + } + + /** + * Close the validation process and finalize with the given config. + * + * @param TreeBuilder $testBuilder The tree builder to finalize + * @param array $config The config you want to use for the finalization, if nothing provided + * a simple array('key'=>'value') will be used + * + * @return array The finalized config values + */ + protected function finalizeTestBuilder($testBuilder, $config = null) + { + return $testBuilder + ->end() + ->end() + ->end() + ->buildTree() + ->finalize(null === $config ? array('key' => 'value') : $config) + ; + } + + /** + * Return a closure that will return the given value. + * + * @param mixed $val The value that the closure must return + * + * @return \Closure + */ + protected function returnClosure($val) + { + return function ($v) use ($val) { + return $val; + }; + } + + /** + * Assert that the given test builder, will return the given value. + * + * @param mixed $value The value to test + * @param TreeBuilder $treeBuilder The tree builder to finalize + * @param mixed $config The config values that new to be finalized + */ + protected function assertFinalizedValueIs($value, $treeBuilder, $config = null) + { + $this->assertEquals(array('key' => $value), $this->finalizeTestBuilder($treeBuilder, $config)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php new file mode 100644 index 0000000..22c399c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; +use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; + +class NodeBuilderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testThrowsAnExceptionWhenTryingToCreateANonRegisteredNodeType() + { + $builder = new BaseNodeBuilder(); + $builder->node('', 'foobar'); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsAnExceptionWhenTheNodeClassIsNotFound() + { + $builder = new BaseNodeBuilder(); + $builder + ->setNodeClass('noclasstype', '\\foo\\bar\\noclass') + ->node('', 'noclasstype'); + } + + public function testAddingANewNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('newtype', $class) + ->node('', 'newtype'); + + $this->assertInstanceOf($class, $node); + } + + public function testOverridingAnExistingNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('variable', $class) + ->node('', 'variable'); + + $this->assertInstanceOf($class, $node); + } + + public function testNodeTypesAreNotCaseSensitive() + { + $builder = new BaseNodeBuilder(); + + $node1 = $builder->node('', 'VaRiAbLe'); + $node2 = $builder->node('', 'variable'); + + $this->assertInstanceOf(get_class($node1), $node2); + + $builder->setNodeClass('CuStOm', __NAMESPACE__.'\\SomeNodeDefinition'); + + $node1 = $builder->node('', 'CUSTOM'); + $node2 = $builder->node('', 'custom'); + + $this->assertInstanceOf(get_class($node1), $node2); + } + + public function testNumericNodeCreation() + { + $builder = new BaseNodeBuilder(); + + $node = $builder->integerNode('foo')->min(3)->max(5); + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $node); + + $node = $builder->floatNode('bar')->min(3.0)->max(5.0); + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $node); + } +} + +class SomeNodeDefinition extends BaseVariableNodeDefinition +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NumericNodeDefinitionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NumericNodeDefinitionTest.php new file mode 100644 index 0000000..8f0cdbb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/NumericNodeDefinitionTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition as NumericNodeDefinition; +use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition; +use Symfony\Component\Config\Definition\Builder\FloatNodeDefinition; + +class NumericNodeDefinitionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You cannot define a min(4) as you already have a max(3) + */ + public function testIncoherentMinAssertion() + { + $def = new NumericNodeDefinition('foo'); + $def->max(3)->min(4); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You cannot define a max(2) as you already have a min(3) + */ + public function testIncoherentMaxAssertion() + { + $node = new NumericNodeDefinition('foo'); + $node->min(3)->max(2); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4 is too small for path "foo". Should be greater than or equal to 5 + */ + public function testIntegerMinAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $def->min(5)->getNode()->finalize(4); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4 is too big for path "foo". Should be less than or equal to 3 + */ + public function testIntegerMaxAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $def->max(3)->getNode()->finalize(4); + } + + public function testIntegerValidMinMaxAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $node = $def->min(3)->max(7)->getNode(); + $this->assertEquals(4, $node->finalize(4)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 400 is too small for path "foo". Should be greater than or equal to 500 + */ + public function testFloatMinAssertion() + { + $def = new FloatNodeDefinition('foo'); + $def->min(5E2)->getNode()->finalize(4e2); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4.3 is too big for path "foo". Should be less than or equal to 0.3 + */ + public function testFloatMaxAssertion() + { + $def = new FloatNodeDefinition('foo'); + $def->max(0.3)->getNode()->finalize(4.3); + } + + public function testFloatValidMinMaxAssertion() + { + $def = new FloatNodeDefinition('foo'); + $node = $def->min(3.0)->max(7e2)->getNode(); + $this->assertEquals(4.5, $node->finalize(4.5)); + } + + /** + * @expectedException Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to NumericNodeDefinition. + */ + public function testCannotBeEmptyThrowsAnException() + { + $def = new NumericNodeDefinition('foo'); + $def->cannotBeEmpty(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php new file mode 100644 index 0000000..00e27c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Builder/TreeBuilderTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder as CustomNodeBuilder; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +require __DIR__.'/../../Fixtures/Builder/NodeBuilder.php'; +require __DIR__.'/../../Fixtures/Builder/BarNodeDefinition.php'; +require __DIR__.'/../../Fixtures/Builder/VariableNodeDefinition.php'; + +class TreeBuilderTest extends \PHPUnit_Framework_TestCase +{ + public function testUsingACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('custom', 'array', new CustomNodeBuilder()); + + $nodeBuilder = $root->children(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder', $nodeBuilder); + + $nodeBuilder = $nodeBuilder->arrayNode('deeper')->children(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\NodeBuilder', $nodeBuilder); + } + + public function testOverrideABuiltInNodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->variableNode('variable'); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\VariableNodeDefinition', $definition); + } + + public function testAddANodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->barNode('variable'); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Definition\Builder\BarNodeDefinition', $definition); + } + + public function testCreateABuiltInNodeTypeWithACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('builtin', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->booleanNode('boolean'); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $definition); + } + + public function testPrototypedArrayNodeUseTheCustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $root->prototype('bar')->end(); + } + + public function testAnExtendedNodeBuilderGetsPropagatedToTheChildren() + { + $builder = new TreeBuilder(); + + $builder->root('propagation') + ->children() + ->setNodeClass('extended', 'Symfony\Component\Config\Tests\Definition\Builder\VariableNodeDefinition') + ->node('foo', 'extended')->end() + ->arrayNode('child') + ->children() + ->node('foo', 'extended') + ->end() + ->end() + ->end() + ->end(); + } + + public function testDefinitionInfoGetsTransferredToNode() + { + $builder = new TreeBuilder(); + + $builder->root('test')->info('root info') + ->children() + ->node('child', 'variable')->info('child info')->defaultValue('default') + ->end() + ->end(); + + $tree = $builder->buildTree(); + $children = $tree->getChildren(); + + $this->assertEquals('root info', $tree->getInfo()); + $this->assertEquals('child info', $children['child']->getInfo()); + } + + public function testDefinitionExampleGetsTransferredToNode() + { + $builder = new TreeBuilder(); + + $builder->root('test') + ->example(array('key' => 'value')) + ->children() + ->node('child', 'variable')->info('child info')->defaultValue('default')->example('example') + ->end() + ->end(); + + $tree = $builder->buildTree(); + $children = $tree->getChildren(); + + $this->assertTrue(is_array($tree->getExample())); + $this->assertEquals('example', $children['child']->getExample()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php new file mode 100644 index 0000000..6d9d52d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Dumper; + +use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class XmlReferenceDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new XmlReferenceDumper(); + $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); + } + + public function testNamespaceDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new XmlReferenceDumper(); + $this->assertEquals(str_replace('http://example.org/schema/dic/acme_root', 'http://symfony.com/schema/dic/symfony', $this->getConfigurationAsString()), $dumper->dump($configuration, 'http://symfony.com/schema/dic/symfony')); + } + + private function getConfigurationAsString() + { + return str_replace("\n", PHP_EOL, << + + + + + + + + + + + scalar value + + + + + + +EOL + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php new file mode 100644 index 0000000..32deeaf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Dumper; + +use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class YamlReferenceDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new YamlReferenceDumper(); + + $this->markTestIncomplete('The Yaml Dumper currently does not support prototyped arrays'); + $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); + } + + private function getConfigurationAsString() + { + return << + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\EnumNode; + +class EnumNodeTest extends \PHPUnit_Framework_TestCase +{ + public function testFinalizeValue() + { + $node = new EnumNode('foo', null, array('foo', 'bar')); + $this->assertSame('foo', $node->finalize('foo')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage $values must contain at least one element. + */ + public function testConstructionWithNoValues() + { + new EnumNode('foo', null, array()); + } + + public function testConstructionWithOneValue() + { + $node = new EnumNode('foo', null, array('foo')); + $this->assertSame('foo', $node->finalize('foo')); + } + + public function testConstructionWithOneDistinctValue() + { + $node = new EnumNode('foo', null, array('foo', 'foo')); + $this->assertSame('foo', $node->finalize('foo')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar" + */ + public function testFinalizeWithInvalidValue() + { + $node = new EnumNode('foo', null, array('foo', 'bar')); + $node->finalize('foobar'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FinalizationTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FinalizationTest.php new file mode 100644 index 0000000..19fc347 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FinalizationTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\Definition\NodeInterface; + +class FinalizationTest extends \PHPUnit_Framework_TestCase +{ + public function testUnsetKeyWithDeepHierarchy() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->children() + ->node('level1', 'array') + ->canBeUnset() + ->children() + ->node('level2', 'array') + ->canBeUnset() + ->children() + ->node('somevalue', 'scalar')->end() + ->node('anothervalue', 'scalar')->end() + ->end() + ->end() + ->node('level1_scalar', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'level1' => array( + 'level2' => array( + 'somevalue' => 'foo', + 'anothervalue' => 'bar', + ), + 'level1_scalar' => 'foo', + ), + ); + + $b = array( + 'level1' => array( + 'level2' => false, + ), + ); + + $this->assertEquals(array( + 'level1' => array( + 'level1_scalar' => 'foo', + ), + ), $this->process($tree, array($a, $b))); + } + + protected function process(NodeInterface $tree, array $configs) + { + $processor = new Processor(); + + return $processor->process($tree, $configs); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FloatNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FloatNodeTest.php new file mode 100644 index 0000000..84afd6c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/FloatNodeTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\FloatNode; + +class FloatNodeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new FloatNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param int $value + */ + public function testValidNonEmptyValues($value) + { + $node = new FloatNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return array( + array(1798.0), + array(-678.987), + array(12.56E45), + array(0.0), + // Integer are accepted too, they will be cast + array(17), + array(-10), + array(0), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new FloatNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(null), + array(''), + array('foo'), + array(true), + array(false), + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/IntegerNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/IntegerNodeTest.php new file mode 100644 index 0000000..58d2148 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/IntegerNodeTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\IntegerNode; + +class IntegerNodeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new IntegerNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param int $value + */ + public function testValidNonEmptyValues($value) + { + $node = new IntegerNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return array( + array(1798), + array(-678), + array(0), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new IntegerNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(null), + array(''), + array('foo'), + array(true), + array(false), + array(0.0), + array(0.1), + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/MergeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/MergeTest.php new file mode 100644 index 0000000..08ddc32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/MergeTest.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class MergeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException + */ + public function testForbiddenOverwrite() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('foo', 'scalar') + ->cannotBeOverwritten() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'foo' => 'bar', + ); + + $b = array( + 'foo' => 'moo', + ); + + $tree->merge($a, $b); + } + + public function testUnsetKey() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->node('unsettable', 'array') + ->canBeUnset() + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->node('unsetted', 'array') + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'foo' => 'bar', + 'unsettable' => array( + 'foo' => 'a', + 'bar' => 'b', + ), + 'unsetted' => false, + ); + + $b = array( + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => array('a', 'b'), + ); + + $this->assertEquals(array( + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => array('a', 'b'), + ), $tree->merge($a, $b)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testDoesNotAllowNewKeysInSubsequentConfigs() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->children() + ->node('test', 'array') + ->disallowNewKeysInSubsequentConfigs() + ->useAttributeAsKey('key') + ->prototype('array') + ->children() + ->node('value', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree(); + + $a = array( + 'test' => array( + 'a' => array('value' => 'foo'), + ), + ); + + $b = array( + 'test' => array( + 'b' => array('value' => 'foo'), + ), + ); + + $tree->merge($a, $b); + } + + public function testPerformsNoDeepMerging() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->children() + ->node('no_deep_merging', 'array') + ->performNoDeepMerging() + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'no_deep_merging' => array( + 'foo' => 'a', + 'bar' => 'b', + ), + ); + + $b = array( + 'no_deep_merging' => array( + 'c' => 'd', + ), + ); + + $this->assertEquals(array( + 'no_deep_merging' => array( + 'c' => 'd', + ), + ), $tree->merge($a, $b)); + } + + public function testPrototypeWithoutAKeyAttribute() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->children() + ->arrayNode('append_elements') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'append_elements' => array('a', 'b'), + ); + + $b = array( + 'append_elements' => array('c', 'd'), + ); + + $this->assertEquals(array('append_elements' => array('a', 'b', 'c', 'd')), $tree->merge($a, $b)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/NormalizationTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/NormalizationTest.php new file mode 100644 index 0000000..a896f96 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/NormalizationTest.php @@ -0,0 +1,229 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class NormalizationTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getEncoderTests + */ + public function testNormalizeEncoders($denormalized) + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root_name', 'array') + ->fixXmlConfig('encoder') + ->children() + ->node('encoders', 'array') + ->useAttributeAsKey('class') + ->prototype('array') + ->beforeNormalization()->ifString()->then(function ($v) { return array('algorithm' => $v); })->end() + ->children() + ->node('algorithm', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $normalized = array( + 'encoders' => array( + 'foo' => array('algorithm' => 'plaintext'), + ), + ); + + $this->assertNormalized($tree, $denormalized, $normalized); + } + + public function getEncoderTests() + { + $configs = array(); + + // XML + $configs[] = array( + 'encoder' => array( + array('class' => 'foo', 'algorithm' => 'plaintext'), + ), + ); + + // XML when only one element of this type + $configs[] = array( + 'encoder' => array('class' => 'foo', 'algorithm' => 'plaintext'), + ); + + // YAML/PHP + $configs[] = array( + 'encoders' => array( + array('class' => 'foo', 'algorithm' => 'plaintext'), + ), + ); + + // YAML/PHP + $configs[] = array( + 'encoders' => array( + 'foo' => 'plaintext', + ), + ); + + // YAML/PHP + $configs[] = array( + 'encoders' => array( + 'foo' => array('algorithm' => 'plaintext'), + ), + ); + + return array_map(function ($v) { + return array($v); + }, $configs); + } + + /** + * @dataProvider getAnonymousKeysTests + */ + public function testAnonymousKeysArray($denormalized) + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('logout', 'array') + ->fixXmlConfig('handler') + ->children() + ->node('handlers', 'array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $normalized = array('logout' => array('handlers' => array('a', 'b', 'c'))); + + $this->assertNormalized($tree, $denormalized, $normalized); + } + + public function getAnonymousKeysTests() + { + $configs = array(); + + $configs[] = array( + 'logout' => array( + 'handlers' => array('a', 'b', 'c'), + ), + ); + + $configs[] = array( + 'logout' => array( + 'handler' => array('a', 'b', 'c'), + ), + ); + + return array_map(function ($v) { return array($v); }, $configs); + } + + /** + * @dataProvider getNumericKeysTests + */ + public function testNumericKeysAsAttributes($denormalized) + { + $normalized = array( + 'thing' => array(42 => array('foo', 'bar'), 1337 => array('baz', 'qux')), + ); + + $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, $normalized); + } + + public function getNumericKeysTests() + { + $configs = array(); + + $configs[] = array( + 'thing' => array( + 42 => array('foo', 'bar'), 1337 => array('baz', 'qux'), + ), + ); + + $configs[] = array( + 'thing' => array( + array('foo', 'bar', 'id' => 42), array('baz', 'qux', 'id' => 1337), + ), + ); + + return array_map(function ($v) { return array($v); }, $configs); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The attribute "id" must be set for path "root.thing". + */ + public function testNonAssociativeArrayThrowsExceptionIfAttributeNotSet() + { + $denormalized = array( + 'thing' => array( + array('foo', 'bar'), array('baz', 'qux'), + ), + ); + + $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, array()); + } + + public function testAssociativeArrayPreserveKeys() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->prototype('array') + ->children() + ->node('foo', 'scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $data = array('first' => array('foo' => 'bar')); + + $this->assertNormalized($tree, $data, $data); + } + + public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized) + { + self::assertSame($normalized, $tree->normalize($denormalized)); + } + + private function getNumericKeysTestTree() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('thing', 'array') + ->useAttributeAsKey('id') + ->prototype('array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + return $tree; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/PrototypedArrayNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/PrototypedArrayNodeTest.php new file mode 100644 index 0000000..c343fcf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/PrototypedArrayNodeTest.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; + +class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase +{ + public function testGetDefaultValueReturnsAnEmptyArrayForPrototypes() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $node->setPrototype($prototype); + $this->assertEmpty($node->getDefaultValue()); + } + + public function testGetDefaultValueReturnsDefaultValueForPrototypes() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $node->setPrototype($prototype); + $node->setDefaultValue(array('test')); + $this->assertEquals(array('test'), $node->getDefaultValue()); + } + + // a remapped key (e.g. "mapping" -> "mappings") should be unset after being used + public function testRemappedKeysAreUnset() + { + $node = new ArrayNode('root'); + $mappingsNode = new PrototypedArrayNode('mappings'); + $node->addChild($mappingsNode); + + // each item under mappings is just a scalar + $prototype = new ScalarNode(null, $mappingsNode); + $mappingsNode->setPrototype($prototype); + + $remappings = array(); + $remappings[] = array('mapping', 'mappings'); + $node->setXmlRemappings($remappings); + + $normalized = $node->normalize(array('mapping' => array('foo', 'bar'))); + $this->assertEquals(array('mappings' => array('foo', 'bar')), $normalized); + } + + /** + * Tests that when a key attribute is mapped, that key is removed from the array. + * + * + * + * + * The above should finally be mapped to an array that looks like this + * (because "id" is the key attribute). + * + * array( + * 'things' => array( + * 'option1' => 'foo', + * 'option2' => 'bar', + * ) + * ) + */ + public function testMappedAttributeKeyIsRemoved() + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', true); + + // each item under the root is an array, with one scalar item + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('foo')); + $node->setPrototype($prototype); + + $children = array(); + $children[] = array('id' => 'item_name', 'foo' => 'bar'); + $normalized = $node->normalize($children); + + $expected = array(); + $expected['item_name'] = array('foo' => 'bar'); + $this->assertEquals($expected, $normalized); + } + + /** + * Tests the opposite of the testMappedAttributeKeyIsRemoved because + * the removal can be toggled with an option. + */ + public function testMappedAttributeKeyNotRemoved() + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', false); + + // each item under the root is an array, with two scalar items + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('foo')); + $prototype->addChild(new ScalarNode('id')); // the key attribute will remain + $node->setPrototype($prototype); + + $children = array(); + $children[] = array('id' => 'item_name', 'foo' => 'bar'); + $normalized = $node->normalize($children); + + $expected = array(); + $expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar'); + $this->assertEquals($expected, $normalized); + } + + public function testAddDefaultChildren() + { + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('defaults' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet('defaultkey'); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(array('defaultkey')); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(array('dk1', 'dk2')); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('dk1' => array('foo' => 'bar'), 'dk2' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(array(5, 6)); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(0 => array('foo' => 'bar'), 1 => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(2); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(array('foo' => 'bar'), array('foo' => 'bar')), $node->getDefaultValue()); + } + + public function testDefaultChildrenWinsOverDefaultValue() + { + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(); + $node->setDefaultValue(array('bar' => 'foo')); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); + } + + protected function getPrototypeNodeWithDefaultChildren() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $child = new ScalarNode('foo'); + $child->setDefaultValue('bar'); + $prototype->addChild($child); + $prototype->setAddIfNotSet(true); + $node->setPrototype($prototype); + + return $node; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php new file mode 100644 index 0000000..86c1803 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use Symfony\Component\Config\Definition\ScalarNode; + +class ScalarNodeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new ScalarNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + public function getValidValues() + { + return array( + array(false), + array(true), + array(null), + array(''), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new ScalarNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } + + public function testNormalizeThrowsExceptionWithoutHint() + { + $node = new ScalarNode('test'); + + $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', 'Invalid type for path "test". Expected scalar, but got array.'); + + $node->normalize(array()); + } + + public function testNormalizeThrowsExceptionWithErrorMessage() + { + $node = new ScalarNode('test'); + $node->setInfo('"the test value"'); + + $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', "Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); + + $node->normalize(array()); + } + + /** + * @dataProvider getValidNonEmptyValues + * + * @param mixed $value + */ + public function testValidNonEmptyValues($value) + { + $node = new ScalarNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidNonEmptyValues() + { + return array( + array(false), + array(true), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + ); + } + + /** + * @dataProvider getEmptyValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * + * @param mixed $value + */ + public function testNotAllowedEmptyValuesThrowException($value) + { + $node = new ScalarNode('test'); + $node->setAllowEmptyValue(false); + $node->finalize($value); + } + + public function getEmptyValues() + { + return array( + array(null), + array(''), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Exception/FileLoaderLoadExceptionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Exception/FileLoaderLoadExceptionTest.php new file mode 100644 index 0000000..c3d050c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Exception/FileLoaderLoadExceptionTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Exception; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +class FileLoaderLoadExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testMessageCannotLoadResource() + { + $exception = new FileLoaderLoadException('resource', null); + $this->assertEquals('Cannot load resource "resource".', $exception->getMessage()); + } + + public function testMessageCannotImportResourceFromSource() + { + $exception = new FileLoaderLoadException('resource', 'sourceResource'); + $this->assertEquals('Cannot import resource "resource" from "sourceResource".', $exception->getMessage()); + } + + public function testMessageCannotImportBundleResource() + { + $exception = new FileLoaderLoadException('@resource', 'sourceResource'); + $this->assertEquals( + 'Cannot import resource "@resource" from "sourceResource". '. + 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. + 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorWithDotAndUnableToLoad() + { + $exception = new FileLoaderLoadException( + 'resource', + null, + null, + new \Exception('There was a previous error with an ending dot.') + ); + $this->assertEquals( + 'There was a previous error with an ending dot in resource (which is loaded in resource "resource").', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorWithoutDotAndUnableToLoad() + { + $exception = new FileLoaderLoadException( + 'resource', + null, + null, + new \Exception('There was a previous error with no ending dot') + ); + $this->assertEquals( + 'There was a previous error with no ending dot in resource (which is loaded in resource "resource").', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorAndUnableToLoadBundle() + { + $exception = new FileLoaderLoadException( + '@resource', + null, + null, + new \Exception('There was a previous error with an ending dot.') + ); + $this->assertEquals( + 'There was a previous error with an ending dot in @resource '. + '(which is loaded in resource "@resource"). '. + 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. + 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', + $exception->getMessage() + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/FileLocatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/FileLocatorTest.php new file mode 100644 index 0000000..d479f25 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/FileLocatorTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use Symfony\Component\Config\FileLocator; + +class FileLocatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getIsAbsolutePathTests + */ + public function testIsAbsolutePath($path) + { + $loader = new FileLocator(array()); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('isAbsolutePath'); + $m->setAccessible(true); + + $this->assertTrue($m->invoke($loader, $path), '->isAbsolutePath() returns true for an absolute path'); + } + + public function getIsAbsolutePathTests() + { + return array( + array('/foo.xml'), + array('c:\\\\foo.xml'), + array('c:/foo.xml'), + array('\\server\\foo.xml'), + array('https://server/foo.xml'), + array('phar://server/foo.xml'), + ); + } + + public function testLocate() + { + $loader = new FileLocator(__DIR__.'/Fixtures'); + + $this->assertEquals( + __DIR__.DIRECTORY_SEPARATOR.'FileLocatorTest.php', + $loader->locate('FileLocatorTest.php', __DIR__), + '->locate() returns the absolute filename if the file exists in the given path' + ); + + $this->assertEquals( + __DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', + $loader->locate('foo.xml', __DIR__), + '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' + ); + + $this->assertEquals( + __DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', + $loader->locate(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__), + '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' + ); + + $loader = new FileLocator(array(__DIR__.'/Fixtures', __DIR__.'/Fixtures/Again')); + + $this->assertEquals( + array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), + $loader->locate('foo.xml', __DIR__, false), + '->locate() returns an array of absolute filenames' + ); + + $this->assertEquals( + array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), + $loader->locate('foo.xml', __DIR__.'/Fixtures', false), + '->locate() returns an array of absolute filenames' + ); + + $loader = new FileLocator(__DIR__.'/Fixtures/Again'); + + $this->assertEquals( + array(__DIR__.'/Fixtures'.DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.DIRECTORY_SEPARATOR.'foo.xml'), + $loader->locate('foo.xml', __DIR__.'/Fixtures', false), + '->locate() returns an array of absolute filenames' + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The file "foobar.xml" does not exist + */ + public function testLocateThrowsAnExceptionIfTheFileDoesNotExists() + { + $loader = new FileLocator(array(__DIR__.'/Fixtures')); + + $loader->locate('foobar.xml', __DIR__); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateThrowsAnExceptionIfTheFileDoesNotExistsInAbsolutePath() + { + $loader = new FileLocator(array(__DIR__.'/Fixtures')); + + $loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage An empty file name is not valid to be located. + */ + public function testLocateEmpty() + { + $loader = new FileLocator(array(__DIR__.'/Fixtures')); + + $loader->locate(null, __DIR__); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Again/foo.xml b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Again/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/BarNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/BarNodeDefinition.php new file mode 100644 index 0000000..47701c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/BarNodeDefinition.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; + +class BarNodeDefinition extends NodeDefinition +{ + protected function createNode() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php new file mode 100644 index 0000000..aa59863 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/NodeBuilder.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; + +class NodeBuilder extends BaseNodeBuilder +{ + public function barNode($name) + { + return $this->node($name, 'bar'); + } + + protected function getNodeClass($type) + { + switch ($type) { + case 'variable': + return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; + case 'bar': + return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; + default: + return parent::getNodeClass($type); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/VariableNodeDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/VariableNodeDefinition.php new file mode 100644 index 0000000..1017880 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Builder/VariableNodeDefinition.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; + +class VariableNodeDefinition extends BaseVariableNodeDefinition +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php new file mode 100644 index 0000000..7125dc4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Fixtures\Configuration; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class ExampleConfiguration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('acme_root'); + + $rootNode + ->fixXmlConfig('parameter') + ->fixXmlConfig('connection') + ->children() + ->booleanNode('boolean')->defaultTrue()->end() + ->scalarNode('scalar_empty')->end() + ->scalarNode('scalar_null')->defaultNull()->end() + ->scalarNode('scalar_true')->defaultTrue()->end() + ->scalarNode('scalar_false')->defaultFalse()->end() + ->scalarNode('scalar_default')->defaultValue('default')->end() + ->scalarNode('scalar_array_empty')->defaultValue(array())->end() + ->scalarNode('scalar_array_defaults')->defaultValue(array('elem1', 'elem2'))->end() + ->scalarNode('scalar_required')->isRequired()->end() + ->enumNode('enum_with_default')->values(array('this', 'that'))->defaultValue('this')->end() + ->enumNode('enum')->values(array('this', 'that'))->end() + ->arrayNode('array') + ->info('some info') + ->canBeUnset() + ->children() + ->scalarNode('child1')->end() + ->scalarNode('child2')->end() + ->scalarNode('child3') + ->info( + "this is a long\n". + "multi-line info text\n". + 'which should be indented' + ) + ->example('example setting') + ->end() + ->end() + ->end() + ->arrayNode('parameters') + ->useAttributeAsKey('name') + ->prototype('scalar')->info('Parameter name')->end() + ->end() + ->arrayNode('connections') + ->prototype('array') + ->children() + ->scalarNode('user')->end() + ->scalarNode('pass')->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/document_type.xml b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/document_type.xml new file mode 100644 index 0000000..4c25228 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/document_type.xml @@ -0,0 +1,3 @@ + +]> + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/invalid.xml b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/invalid.xml new file mode 100644 index 0000000..a07af9f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/invalid.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/invalid_schema.xml b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/invalid_schema.xml new file mode 100644 index 0000000..e2725a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/invalid_schema.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/schema.xsd b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/schema.xsd new file mode 100644 index 0000000..e56820f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/schema.xsd @@ -0,0 +1,9 @@ + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/valid.xml b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/valid.xml new file mode 100644 index 0000000..a96bb38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/Util/valid.xml @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/foo.xml b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Fixtures/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php new file mode 100644 index 0000000..cdb0980 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/DelegatingLoaderTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Loader\DelegatingLoader; + +class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $loader = new DelegatingLoader($resolver = new LoaderResolver()); + $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); + } + + public function testGetSetResolver() + { + $resolver = new LoaderResolver(); + $loader = new DelegatingLoader($resolver); + $this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader'); + $loader->setResolver($resolver = new LoaderResolver()); + $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); + } + + public function testSupports() + { + $loader1 = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $loader1->expects($this->once())->method('supports')->will($this->returnValue(true)); + $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); + $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); + + $loader1 = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $loader1->expects($this->once())->method('supports')->will($this->returnValue(false)); + $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); + } + + public function testLoad() + { + $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); + $loader->expects($this->once())->method('load'); + $resolver = new LoaderResolver(array($loader)); + $loader = new DelegatingLoader($resolver); + + $loader->load('foo'); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException + */ + public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded() + { + $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $loader->expects($this->once())->method('supports')->will($this->returnValue(false)); + $resolver = new LoaderResolver(array($loader)); + $loader = new DelegatingLoader($resolver); + + $loader->load('foo'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php new file mode 100644 index 0000000..1e8e91e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/FileLoaderTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; + +class FileLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testImportWithFileLocatorDelegation() + { + $locatorMock = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + + $locatorMockForAdditionalLoader = $this->getMock('Symfony\Component\Config\FileLocatorInterface'); + $locatorMockForAdditionalLoader->expects($this->any())->method('locate')->will($this->onConsecutiveCalls( + array('path/to/file1'), // Default + array('path/to/file1', 'path/to/file2'), // First is imported + array('path/to/file1', 'path/to/file2'), // Second is imported + array('path/to/file1'), // Exception + array('path/to/file1', 'path/to/file2') // Exception + )); + + $fileLoader = new TestFileLoader($locatorMock); + $fileLoader->setSupports(false); + $fileLoader->setCurrentDir('.'); + + $additionalLoader = new TestFileLoader($locatorMockForAdditionalLoader); + $additionalLoader->setCurrentDir('.'); + + $fileLoader->setResolver($loaderResolver = new LoaderResolver(array($fileLoader, $additionalLoader))); + + // Default case + $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); + + // Check first file is imported if not already loading + $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); + + // Check second file is imported if first is already loading + $fileLoader->addLoading('path/to/file1'); + $this->assertSame('path/to/file2', $fileLoader->import('my_resource')); + + // Check exception throws if first (and only available) file is already loading + try { + $fileLoader->import('my_resource'); + $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } + + // Check exception throws if all files are already loading + try { + $fileLoader->addLoading('path/to/file2'); + $fileLoader->import('my_resource'); + $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } + } +} + +class TestFileLoader extends FileLoader +{ + private $supports = true; + + public function load($resource, $type = null) + { + return $resource; + } + + public function supports($resource, $type = null) + { + return $this->supports; + } + + public function addLoading($resource) + { + self::$loading[$resource] = true; + } + + public function removeLoading($resource) + { + unset(self::$loading[$resource]); + } + + public function clearLoading() + { + self::$loading = array(); + } + + public function setSupports($supports) + { + $this->supports = $supports; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderResolverTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderResolverTest.php new file mode 100644 index 0000000..03db21b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderResolverTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use Symfony\Component\Config\Loader\LoaderResolver; + +class LoaderResolverTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $resolver = new LoaderResolver(array( + $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'), + )); + + $this->assertEquals(array($loader), $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument'); + } + + public function testResolve() + { + $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $resolver = new LoaderResolver(array($loader)); + $this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource'); + + $loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); + $resolver = new LoaderResolver(array($loader)); + $this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource'); + } + + public function testLoaders() + { + $resolver = new LoaderResolver(); + $resolver->addLoader($loader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface')); + + $this->assertEquals(array($loader), $resolver->getLoaders(), 'addLoader() adds a loader'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php new file mode 100644 index 0000000..e938a4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Loader/LoaderTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use Symfony\Component\Config\Loader\Loader; + +class LoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testGetSetResolver() + { + $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); + } + + public function testResolve() + { + $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + + $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo.xml') + ->will($this->returnValue($resolvedLoader)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader'); + $this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader'); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException + */ + public function testResolveWhenResolverCannotFindLoader() + { + $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); + $resolver->expects($this->once()) + ->method('resolve') + ->with('FOOBAR') + ->will($this->returnValue(false)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $loader->resolve('FOOBAR'); + } + + public function testImport() + { + $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $resolvedLoader->expects($this->once()) + ->method('load') + ->with('foo') + ->will($this->returnValue('yes')); + + $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo') + ->will($this->returnValue($resolvedLoader)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertEquals('yes', $loader->import('foo')); + } + + public function testImportWithType() + { + $resolvedLoader = $this->getMock('Symfony\Component\Config\Loader\LoaderInterface'); + $resolvedLoader->expects($this->once()) + ->method('load') + ->with('foo', 'bar') + ->will($this->returnValue('yes')); + + $resolver = $this->getMock('Symfony\Component\Config\Loader\LoaderResolverInterface'); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo', 'bar') + ->will($this->returnValue($resolvedLoader)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertEquals('yes', $loader->import('foo', 'bar')); + } +} + +class ProjectLoader1 extends Loader +{ + public function load($resource, $type = null) + { + } + + public function supports($resource, $type = null) + { + return is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION); + } + + public function getType() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php new file mode 100644 index 0000000..2bbaadc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\DirectoryResource; + +class DirectoryResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $directory; + + protected function setUp() + { + $this->directory = sys_get_temp_dir().'/symfonyDirectoryIterator'; + if (!file_exists($this->directory)) { + mkdir($this->directory); + } + touch($this->directory.'/tmp.xml'); + } + + protected function tearDown() + { + if (!is_dir($this->directory)) { + return; + } + $this->removeDirectory($this->directory); + } + + protected function removeDirectory($directory) + { + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::CHILD_FIRST); + foreach ($iterator as $path) { + if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) { + continue; + } + if ($path->isDir()) { + rmdir($path->__toString()); + } else { + unlink($path->__toString()); + } + } + rmdir($directory); + } + + public function testGetResource() + { + $resource = new DirectoryResource($this->directory); + $this->assertSame($this->directory, $resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testGetPattern() + { + $resource = new DirectoryResource('foo', 'bar'); + $this->assertEquals('bar', $resource->getPattern()); + } + + public function testIsFresh() + { + $resource = new DirectoryResource($this->directory); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated'); + + $resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist'); + } + + public function testIsFreshUpdateFile() + { + $resource = new DirectoryResource($this->directory); + touch($this->directory.'/tmp.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an existing file is modified'); + } + + public function testIsFreshNewFile() + { + $resource = new DirectoryResource($this->directory); + touch($this->directory.'/new.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file is added'); + } + + public function testIsFreshNewFileWithDifferentPattern() + { + $resource = new DirectoryResource($this->directory, '/.xml$/'); + touch($this->directory.'/new.yaml', time() + 20); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file with a non-matching pattern is added'); + } + + public function testIsFreshDeleteFile() + { + $resource = new DirectoryResource($this->directory); + unlink($this->directory.'/tmp.xml'); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if an existing file is removed'); + } + + public function testIsFreshDeleteDirectory() + { + $resource = new DirectoryResource($this->directory); + $this->removeDirectory($this->directory); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the whole resource is removed'); + } + + public function testIsFreshCreateFileInSubdirectory() + { + $subdirectory = $this->directory.'/subdirectory'; + mkdir($subdirectory); + + $resource = new DirectoryResource($this->directory); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if an unmodified subdirectory exists'); + + touch($subdirectory.'/newfile.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file in a subdirectory is added'); + } + + public function testIsFreshModifySubdirectory() + { + $resource = new DirectoryResource($this->directory); + + $subdirectory = $this->directory.'/subdirectory'; + mkdir($subdirectory); + touch($subdirectory, time() + 20); + + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a subdirectory is modified (e.g. a file gets deleted)'); + } + + public function testFilterRegexListNoMatch() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + touch($this->directory.'/new.bar', time() + 20); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file not matching the filter regex is created'); + } + + public function testFilterRegexListMatch() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + touch($this->directory.'/new.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an new file matching the filter regex is created '); + } + + public function testSerializeUnserialize() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + $unserialized = unserialize(serialize($resource)); + + $this->assertSame($this->directory, $resource->getResource()); + $this->assertSame('/\.(foo|xml)$/', $resource->getPattern()); + } + + public function testResourcesWithDifferentPatternsAreDifferent() + { + $resourceA = new DirectoryResource($this->directory, '/.xml$/'); + $resourceB = new DirectoryResource($this->directory, '/.yaml$/'); + + $this->assertEquals(2, count(array_unique(array($resourceA, $resourceB)))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php new file mode 100644 index 0000000..56ee584 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileExistenceResourceTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\FileExistenceResource; + +class FileExistenceResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; + $this->time = time(); + $this->resource = new FileExistenceResource($this->file); + } + + protected function tearDown() + { + if (file_exists($this->file)) { + unlink($this->file); + } + } + + public function testToString() + { + $this->assertSame($this->file, (string) $this->resource); + } + + public function testGetResource() + { + $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testIsFreshWithExistingResource() + { + touch($this->file, $this->time); + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); + + unlink($this->file); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); + } + + public function testIsFreshWithAbsentResource() + { + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); + + touch($this->file, $this->time); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php new file mode 100644 index 0000000..db85cf7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\FileResource; + +class FileResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; + $this->time = time(); + touch($this->file, $this->time); + $this->resource = new FileResource($this->file); + } + + protected function tearDown() + { + unlink($this->file); + } + + public function testGetResource() + { + $this->assertSame(realpath($this->file), $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testToString() + { + $this->assertSame(realpath($this->file), (string) $this->resource); + } + + public function testIsFresh() + { + $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); + $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); + + $resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999)); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist'); + } + + public function testSerializeUnserialize() + { + $unserialized = unserialize(serialize($this->resource)); + + $this->assertSame(realpath($this->file), $this->resource->getResource()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/ResourceStub.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/ResourceStub.php new file mode 100644 index 0000000..b01729c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Resource/ResourceStub.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; + +class ResourceStub implements SelfCheckingResourceInterface +{ + private $fresh = true; + + public function setFresh($isFresh) + { + $this->fresh = $isFresh; + } + + public function __toString() + { + return 'stub'; + } + + public function isFresh($timestamp) + { + return $this->fresh; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ResourceCheckerConfigCacheTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ResourceCheckerConfigCacheTest.php new file mode 100644 index 0000000..6a915ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/ResourceCheckerConfigCacheTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use Symfony\Component\Config\Tests\Resource\ResourceStub; +use Symfony\Component\Config\ResourceCheckerConfigCache; + +class ResourceCheckerConfigCacheTest extends \PHPUnit_Framework_TestCase +{ + private $cacheFile = null; + + protected function setUp() + { + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + } + + protected function tearDown() + { + $files = array($this->cacheFile, "{$this->cacheFile}.meta"); + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + public function testGetPath() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile); + + $this->assertSame($this->cacheFile, $cache->getPath()); + } + + public function testCacheIsNotFreshIfEmpty() + { + $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface') + ->expects($this->never())->method('supports'); + + /* If there is nothing in the cache, it needs to be filled (and thus it's not fresh). + It does not matter if you provide checkers or not. */ + + unlink($this->cacheFile); // remove tempnam() side effect + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsFreshIfNocheckerProvided() + { + /* For example in prod mode, you may choose not to run any checkers + at all. In that case, the cache should always be considered fresh. */ + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $this->assertTrue($cache->isFresh()); + } + + public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh() + { + /* As in the previous test, but this time we have a resource. */ + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $cache->write('', array(new ResourceStub())); + + $this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed + } + + public function testIsFreshWithchecker() + { + $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); + + $checker->expects($this->once()) + ->method('supports') + ->willReturn(true); + + $checker->expects($this->once()) + ->method('isFresh') + ->willReturn(true); + + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + $cache->write('', array(new ResourceStub())); + + $this->assertTrue($cache->isFresh()); + } + + public function testIsNotFreshWithchecker() + { + $checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface'); + + $checker->expects($this->once()) + ->method('supports') + ->willReturn(true); + + $checker->expects($this->once()) + ->method('isFresh') + ->willReturn(false); + + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + $cache->write('', array(new ResourceStub())); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheKeepsContent() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $cache->write('FOOBAR'); + + $this->assertSame('FOOBAR', file_get_contents($cache->getPath())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php new file mode 100644 index 0000000..740a205 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Tests/Util/XmlUtilsTest.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use Symfony\Component\Config\Util\XmlUtils; + +class XmlUtilsTest extends \PHPUnit_Framework_TestCase +{ + public function testLoadFile() + { + $fixtures = __DIR__.'/../Fixtures/Util/'; + + try { + XmlUtils::loadFile($fixtures.'invalid.xml'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('ERROR 77', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'document_type.xml'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('Document types are not allowed', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('ERROR 1845', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('XSD file or callable', $e->getMessage()); + } + + $mock = $this->getMock(__NAMESPACE__.'\Validator'); + $mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true)); + + try { + XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('is not valid', $e->getMessage()); + } + + $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'))); + $this->assertSame(array(), libxml_get_errors()); + } + + public function testLoadFileWithInternalErrorsEnabled() + { + $internalErrors = libxml_use_internal_errors(true); + + $this->assertSame(array(), libxml_get_errors()); + $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/invalid_schema.xml')); + $this->assertSame(array(), libxml_get_errors()); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** + * @dataProvider getDataForConvertDomToArray + */ + public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true) + { + $dom = new \DOMDocument(); + $dom->loadXML($root ? $xml : ''.$xml.''); + + $this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix)); + } + + public function getDataForConvertDomToArray() + { + return array( + array(null, ''), + array('bar', 'bar'), + array(array('bar' => 'foobar'), '', true), + array(array('foo' => null), ''), + array(array('foo' => 'bar'), 'bar'), + array(array('foo' => array('foo' => 'bar')), ''), + array(array('foo' => array('foo' => 0)), '0'), + array(array('foo' => array('foo' => 'bar')), 'bar'), + array(array('foo' => array('foo' => 'bar', 'value' => 'text')), 'text'), + array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), 'text'), + array(array('foo' => array('bar', 'text')), 'bartext'), + array(array('foo' => array(array('foo' => 'bar'), array('foo' => 'text'))), ''), + array(array('foo' => array('foo' => array('bar', 'text'))), 'text'), + array(array('foo' => 'bar'), 'bar'), + array(array('foo' => 'text'), 'text'), + array(array('foo' => array('bar' => 'bar', 'value' => 'text')), 'text', false, false), + array(array('attr' => 1, 'b' => 'hello'), 'hello2', true), + ); + } + + /** + * @dataProvider getDataForPhpize + */ + public function testPhpize($expected, $value) + { + $this->assertSame($expected, XmlUtils::phpize($value)); + } + + public function getDataForPhpize() + { + return array( + array('', ''), + array(null, 'null'), + array(true, 'true'), + array(false, 'false'), + array(null, 'Null'), + array(true, 'True'), + array(false, 'False'), + array(0, '0'), + array(1, '1'), + array(-1, '-1'), + array(0777, '0777'), + array(255, '0xFF'), + array(100.0, '1e2'), + array(-120.0, '-1.2E2'), + array(-10100.1, '-10100.1'), + array('-10,100.1', '-10,100.1'), + array('1234 5678 9101 1121 3141', '1234 5678 9101 1121 3141'), + array('1,2,3,4', '1,2,3,4'), + array('11,22,33,44', '11,22,33,44'), + array('11,222,333,4', '11,222,333,4'), + array('1,222,333,444', '1,222,333,444'), + array('11,222,333,444', '11,222,333,444'), + array('111,222,333,444', '111,222,333,444'), + array('1111,2222,3333,4444,5555', '1111,2222,3333,4444,5555'), + array('foo', 'foo'), + array(6, '0b0110'), + ); + } + + public function testLoadEmptyXmlFile() + { + $file = __DIR__.'/../Fixtures/foo.xml'; + $this->setExpectedException('InvalidArgumentException', sprintf('File %s does not contain valid XML, it is empty.', $file)); + XmlUtils::loadFile($file); + } + + // test for issue https://github.com/symfony/symfony/issues/9731 + public function testLoadWrongEmptyXMLWithErrorHandler() + { + $originalDisableEntities = libxml_disable_entity_loader(false); + $errorReporting = error_reporting(-1); + + set_error_handler(function ($errno, $errstr) { + throw new \Exception($errstr, $errno); + }); + + $file = __DIR__.'/../Fixtures/foo.xml'; + try { + try { + XmlUtils::loadFile($file); + $this->fail('An exception should have been raised'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals(sprintf('File %s does not contain valid XML, it is empty.', $file), $e->getMessage()); + } + } finally { + restore_error_handler(); + error_reporting($errorReporting); + } + + $disableEntities = libxml_disable_entity_loader(true); + libxml_disable_entity_loader($disableEntities); + + libxml_disable_entity_loader($originalDisableEntities); + + $this->assertFalse($disableEntities); + + // should not throw an exception + XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd'); + } +} + +interface Validator +{ + public function validate(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/Util/XmlUtils.php b/vendor/symfony/symfony/src/Symfony/Component/Config/Util/XmlUtils.php new file mode 100644 index 0000000..d8d4eaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/Util/XmlUtils.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util; + +/** + * XMLUtils is a bunch of utility methods to XML operations. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class XmlUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + */ + public static function loadFile($file, $schemaOrCallable = null) + { + $content = @file_get_contents($file); + if ('' === trim($content)) { + throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + $dom = new \DOMDocument(); + $dom->validateOnParse = true; + if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { + libxml_disable_entity_loader($disableEntities); + + throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors))); + } + + $dom->normalizeDocument(); + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + throw new \InvalidArgumentException('Document types are not allowed.'); + } + } + + if (null !== $schemaOrCallable) { + $internalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $e = null; + if (is_callable($schemaOrCallable)) { + try { + $valid = call_user_func($schemaOrCallable, $dom, $internalErrors); + } catch (\Exception $e) { + $valid = false; + } + } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { + $schemaSource = file_get_contents((string) $schemaOrCallable); + $valid = @$dom->schemaValidateSource($schemaSource); + } else { + libxml_use_internal_errors($internalErrors); + + throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); + } + + if (!$valid) { + $messages = static::getXmlErrors($internalErrors); + if (empty($messages)) { + $messages = array(sprintf('The XML file "%s" is not valid.', $file)); + } + throw new \InvalidArgumentException(implode("\n", $messages), 0, $e); + } + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $dom; + } + + /** + * Converts a \DomElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DomElement $element A \DomElement instance + * @param bool $checkPrefix Check prefix in an element or an attribute name + * + * @return array A PHP array + */ + public static function convertDomElementToArray(\DomElement $element, $checkPrefix = true) + { + $prefix = (string) $element->prefix; + $empty = true; + $config = array(); + foreach ($element->attributes as $name => $node) { + if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) { + continue; + } + $config[$name] = static::phpize($node->value); + $empty = false; + } + + $nodeValue = false; + foreach ($element->childNodes as $node) { + if ($node instanceof \DOMText) { + if ('' !== trim($node->nodeValue)) { + $nodeValue = trim($node->nodeValue); + $empty = false; + } + } elseif ($checkPrefix && $prefix != (string) $node->prefix) { + continue; + } elseif (!$node instanceof \DOMComment) { + $value = static::convertDomElementToArray($node, $checkPrefix); + + $key = $node->localName; + if (isset($config[$key])) { + if (!is_array($config[$key]) || !is_int(key($config[$key]))) { + $config[$key] = array($config[$key]); + } + $config[$key][] = $value; + } else { + $config[$key] = $value; + } + + $empty = false; + } + } + + if (false !== $nodeValue) { + $value = static::phpize($nodeValue); + if (count($config)) { + $config['value'] = $value; + } else { + $config = $value; + } + } + + return !$empty ? $config : null; + } + + /** + * Converts an xml value to a PHP type. + * + * @param mixed $value + * + * @return mixed + */ + public static function phpize($value) + { + $value = (string) $value; + $lowercaseValue = strtolower($value); + + switch (true) { + case 'null' === $lowercaseValue: + return; + case ctype_digit($value): + $raw = $value; + $cast = (int) $value; + + return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): + $raw = $value; + $cast = (int) $value; + + return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + case 'true' === $lowercaseValue: + return true; + case 'false' === $lowercaseValue: + return false; + case isset($value[1]) && '0b' == $value[0].$value[1]: + return bindec($value); + case is_numeric($value): + return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; + case preg_match('/^0x[0-9a-f]++$/i', $value): + return hexdec($value); + case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value): + return (float) $value; + default: + return $value; + } + } + + protected static function getXmlErrors($internalErrors) + { + $errors = array(); + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', + LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), + $error->file ?: 'n/a', + $error->line, + $error->column + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $errors; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Config/composer.json new file mode 100644 index 0000000..d686929 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/config", + "type": "library", + "description": "Symfony Config Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/filesystem": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Config\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Config/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Config/phpunit.xml.dist new file mode 100644 index 0000000..3fe6fd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Config/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php new file mode 100644 index 0000000..730ac88 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php @@ -0,0 +1,1084 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application +{ + private $commands = array(); + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions = true; + private $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher; + private $terminalDimensions; + private $defaultCommand; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->defaultCommand = 'list'; + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if (0 === $exitCode) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--version', '-V'), true)) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(array('--help', '-h'), true)) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $input = new ArrayInput(array('command' => $this->defaultCommand)); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Set an input definition set to be used with this application. + * + * @param InputDefinition $definition The input definition + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param bool $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + if (null === $command->getDefinition()) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws CommandNotFoundException When command name given does not exist + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return bool true if the command exists, false otherwise + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->commands as $command) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws CommandNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + $exact = in_array($namespace, $namespaces, true); + if (count($namespaces) > 1 && !$exact) { + throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find($name) + { + $allCommands = array_keys($this->commands); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + // filter out aliases for commands which are already on the list + if (count($commands) > 1) { + $commandList = $this->commands; + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commandName = $commandList[$nameOrAlias]->getName(); + + return $commandName === $nameOrAlias || !in_array($commandName, $commands); + }); + } + + $exact = in_array($name, $commands, true); + if (count($commands) > 1 && !$exact) { + $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + + throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * Renders a caught exception. + * + * @param \Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException(\Exception $e, OutputInterface $output) + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + do { + $title = sprintf(' [%s] ', get_class($e)); + + $len = $this->stringWidth($title); + + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 + if (defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $formatter = $output->getFormatter(); + $lines = array(); + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4; + $lines[] = array($line, $lineLength); + + $len = max($lineLength, $len); + } + } + + $messages = array(); + $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len))); + $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))))); + foreach ($lines as $line) { + $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]))); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::OUTPUT_RAW | OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + /** + * Tries to figure out the terminal width in which this application runs. + * + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * Tries to figure out the terminal height in which this application runs. + * + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * Tries to figure out the terminal dimensions based on the current environment. + * + * @return array Array containing width and height + */ + public function getTerminalDimensions() + { + if ($this->terminalDimensions) { + return $this->terminalDimensions; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + // extract [w, H] from "wxh (WxH)" + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + // extract [w, h] from "wxh" + if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) { + return array((int) $matches[1], (int) $matches[2]); + } + } + + if ($sttyString = $this->getSttyColumns()) { + // extract [w, h] from "rows h; columns w;" + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + // extract [w, h] from "; h rows; w columns" + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return array((int) $matches[2], (int) $matches[1]); + } + } + + return array(null, null); + } + + /** + * Sets terminal dimensions. + * + * Can be useful to force terminal dimensions for functional tests. + * + * @param int $width The width + * @param int $height The height + * + * @return Application The current application + */ + public function setTerminalDimensions($width, $height) + { + $this->terminalDimensions = array($width, $height); + + return $this; + } + + /** + * Configures the input and output instances based on the user arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(array('--ansi'), true)) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'), true)) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'), true)) { + $input->setInteractive(false); + } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(); + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === 3) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === 2) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + } + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @param Command $command A Command instance + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception when the command being run threw an exception + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + if ($event->commandShouldRun()) { + try { + $exitCode = $command->run($input, $output); + } catch (\Exception $e) { + $event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + $e = $event->getException(); + + $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + throw $e; + } + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + )); + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + * + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return string x or null if it could not be parsed + */ + private function getConsoleMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')); + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true)); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2].'x'.$matches[1]; + } + } + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @param string $name The string + * @param array|\Traversable $collection The collection + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = array(); + + $collectionParts = array(); + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + asort($alternatives); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @param string $commandName The Command name + */ + public function setDefaultCommand($commandName) + { + $this->defaultCommand = $commandName; + } + + private function stringWidth($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + private function splitStringByWidth($string, $width) + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // 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 + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = array(); + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + if ('' !== $line) { + $lines[] = count($lines) ? str_pad($line, $width) : $line; + } + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @param string $name The full name of the command + * + * @return array The namespaces of the command + */ + private function extractAllNamespaces($name) + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = array(); + + foreach ($parts as $part) { + if (count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Console/CHANGELOG.md new file mode 100644 index 0000000..4e29d2e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/CHANGELOG.md @@ -0,0 +1,68 @@ +CHANGELOG +========= + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php new file mode 100644 index 0000000..cb87093 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php @@ -0,0 +1,626 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + */ +class Command +{ + private $application; + private $name; + private $processTitle; + private $aliases = array(); + private $definition; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $applicationDefinitionMerged = false; + private $applicationDefinitionMergedWithArgs = false; + private $code; + private $synopsis = array(); + private $usages = array(); + private $helperSet; + + /** + * Constructor. + * + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return null|int null or 0 if everything went fine, or an error code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return int The command exit code + * + * @throws \Exception + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(true); + $this->getSynopsis(false); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (function_exists('cli_set_process_title')) { + cli_set_process_title($this->processTitle); + } elseif (function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Install the proctitle PECL to be able to change the process title.'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return Command The current instance + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode(callable $code) + { + if ($code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + public function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + return; + } + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; + if ($mergeArgs) { + $this->applicationDefinitionMergedWithArgs = true; + } + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + * + * @return InputDefinition An InputDefinition instance + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param int $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE) + * + * @return Command The current instance + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * PHP 5.5+ or the proctitle PECL library is required + * + * @param string $title The process title + * + * @return Command The current instance + */ + public function setProcessTitle($title) + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%', + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name, + ); + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases($aliases) + { + if (!is_array($aliases) && !$aliases instanceof \Traversable) { + throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * Add a command usage example. + * + * @param string $usage The usage, it'll be prefixed with the command name + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + * + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper($name) + { + return $this->helperSet->get($name); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @param string $name + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Command/HelpCommand.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/HelpCommand.php new file mode 100644 index 0000000..9bb4fab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/HelpCommand.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<%command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help in other formats by using the --format option: + + php %command.full_name% --format=xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + /** + * Sets the command. + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + )); + + $this->command = null; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Command/ListCommand.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/ListCommand.php new file mode 100644 index 0000000..1812073 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Command/ListCommand.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<%command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information in other formats by using the --format option: + + php %command.full_name% --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + )); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/ConsoleEvents.php b/vendor/symfony/symfony/src/Symfony/Component/Console/ConsoleEvents.php new file mode 100644 index 0000000..1ed41b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/ConsoleEvents.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handled to the command. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleCommandEvent + * instance. + * + * @Event + * + * @var string + */ + const COMMAND = 'console.command'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * The event listener method receives a Symfony\Component\Console\Event\ConsoleTerminateEvent + * instance. + * + * @Event + * + * @var string + */ + const TERMINATE = 'console.terminate'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to deal with the exception or + * to modify the thrown exception. The event listener method receives + * a Symfony\Component\Console\Event\ConsoleExceptionEvent + * instance. + * + * @Event + * + * @var string + */ + const EXCEPTION = 'console.exception'; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php new file mode 100644 index 0000000..89961b9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/ApplicationDescription.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon + * + * @internal + */ +class ApplicationDescription +{ + const GLOBAL_NAMESPACE = '_global'; + + /** + * @var Application + */ + private $application; + + /** + * @var null|string + */ + private $namespace; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * Constructor. + * + * @param Application $application + * @param string|null $namespace + */ + public function __construct(Application $application, $namespace = null) + { + $this->application = $application; + $this->namespace = $namespace; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = array(); + $this->namespaces = array(); + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = array(); + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName()) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names); + } + } + + /** + * @param array $commands + * + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = array(); + $globalCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $globalCommands['_global'][$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + ksort($namespacedCommands); + $namespacedCommands = array_merge($globalCommands, $namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/Descriptor.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/Descriptor.php new file mode 100644 index 0000000..50dd86c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/Descriptor.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @param InputArgument $argument + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = array()); + + /** + * Describes an InputOption instance. + * + * @param InputOption $option + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputOption(InputOption $option, array $options = array()); + + /** + * Describes an InputDefinition instance. + * + * @param InputDefinition $definition + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array()); + + /** + * Describes a Command instance. + * + * @param Command $command + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeCommand(Command $command, array $options = array()); + + /** + * Describes an Application instance. + * + * @param Application $application + * @param array $options + * + * @return string|mixed + */ + abstract protected function describeApplication(Application $application, array $options = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php new file mode 100644 index 0000000..3929b6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/DescriptorInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * Describes an InputArgument instance. + * + * @param OutputInterface $output + * @param object $object + * @param array $options + */ + public function describe(OutputInterface $output, $object, array $options = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php new file mode 100644 index 0000000..87e38fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/JsonDescriptor.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeData($this->getInputOptionData($option), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeData($this->getCommandData($command), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $commands = array(); + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command); + } + + $data = $describedNamespace + ? array('commands' => $commands, 'namespace' => $describedNamespace) + : array('commands' => $commands, 'namespaces' => array_values($description->getNamespaces())); + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + * + * @param array $data + * @param array $options + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0)); + } + + /** + * @param InputArgument $argument + * + * @return array + */ + private function getInputArgumentData(InputArgument $argument) + { + return array( + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => $argument->getDefault(), + ); + } + + /** + * @param InputOption $option + * + * @return array + */ + private function getInputOptionData(InputOption $option) + { + return array( + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => $option->getDefault(), + ); + } + + /** + * @param InputDefinition $definition + * + * @return array + */ + private function getInputDefinitionData(InputDefinition $definition) + { + $inputArguments = array(); + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = array(); + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + } + + return array('arguments' => $inputArguments, 'options' => $inputOptions); + } + + /** + * @param Command $command + * + * @return array + */ + private function getCommandData(Command $command) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return array( + 'name' => $command->getName(), + 'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 0000000..d3d76a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->write( + '**'.$argument->getName().':**'."\n\n" + .'* Name: '.($argument->getName() ?: '')."\n" + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $argument->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->write( + '**'.$option->getName().':**'."\n\n" + .'* Name: `--'.$option->getName().'`'."\n" + .'* Shortcut: '.($option->getShortcut() ? '`-'.implode('|-', explode('|', $option->getShortcut())).'`' : '')."\n" + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Description: '.preg_replace('/\s*[\r\n]\s*/', "\n ", $option->getDescription() ?: '')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + if ($showArguments = count($definition->getArguments()) > 0) { + $this->write('### Arguments:'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->write($this->describeInputArgument($argument)); + } + } + + if (count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options:'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->write($this->describeInputOption($option)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $this->write( + $command->getName()."\n" + .str_repeat('-', strlen($command->getName()))."\n\n" + .'* Description: '.($command->getDescription() ?: '')."\n" + .'* Usage:'."\n\n" + .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry .= ' * `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + if ($command->getNativeDefinition()) { + $this->write("\n\n"); + $this->describeInputDefinition($command->getNativeDefinition()); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + $this->write($application->getName()."\n".str_repeat('=', strlen($application->getName()))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) { + return '* '.$commandName; + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->write($this->describeCommand($command)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/TextDescriptor.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/TextDescriptor.php new file mode 100644 index 0000000..fd55900 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/TextDescriptor.php @@ -0,0 +1,283 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; + + $this->writeText(sprintf(' %s%s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '='.strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '['.$value.']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option)); + $synopsis = sprintf('%s%s', + $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', + sprintf('--%s%s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - strlen($synopsis) + 2; + + $this->writeText(sprintf(' %s%s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth))); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = array(); + + $this->writeText('Options:', $options); + foreach ($definition->getOptions() as $option) { + if (strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeApplicationDefinition(false); + + $this->writeText('Usage:', $options); + foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.$usage, $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $width = $this->getColumnWidth($description->getCommands()); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' '.$namespace['id'].'', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - strlen($name); + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + /** + * @param Command[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $widths = array(); + + foreach ($commands as $command) { + $widths[] = strlen($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = strlen($alias); + } + } + + return max($widths) + 2; + } + + /** + * @param InputOption[] $options + * + * @return int + */ + private function calculateTotalWidthForOptions($options) + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName()); + + if ($option->acceptValue()) { + $valueLength = 1 + strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php new file mode 100644 index 0000000..b5676be --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Descriptor/XmlDescriptor.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * @param InputDefinition $definition + * + * @return \DOMDocument + */ + public function getInputDefinitionDocument(InputDefinition $definition) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + /** + * @param Command $command + * + * @return \DOMDocument + */ + public function getCommandDocument(Command $command) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $dom; + } + + /** + * @param Application $application + * @param string|null $namespace + * + * @return \DOMDocument + */ + public function getApplicationDocument(Application $application, $namespace = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ($application->getName() !== 'UNKNOWN') { + $rootXml->setAttribute('name', $application->getName()); + if ($application->getVersion() !== 'UNKNOWN') { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = array()) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = array()) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = array()) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = array()) + { + $this->writeDocument($this->getCommandDocument($command)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = array()) + { + $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + } + + /** + * Appends document children to parent node. + * + * @param \DOMNode $parentNode + * @param \DOMNode $importedParent + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + * + * @param \DOMDocument $dom + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @param InputArgument $argument + * + * @return \DOMDocument + */ + private function getInputArgumentDocument(InputArgument $argument) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + /** + * @param InputOption $option + * + * @return \DOMDocument + */ + private function getInputOptionDocument(InputOption $option) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut(), '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut()))); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $dom; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php new file mode 100644 index 0000000..92adf1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleCommandEvent.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier + */ +class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + * + * @var bool + */ + private $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + * + * @return bool + */ + public function disableCommand() + { + return $this->commandShouldRun = false; + } + + /** + * Enables the command. + * + * @return bool + */ + public function enableCommand() + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + * + * @return bool + */ + public function commandShouldRun() + { + return $this->commandShouldRun; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleEvent.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleEvent.php new file mode 100644 index 0000000..ab620c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * Gets the input instance. + * + * @return InputInterface An InputInterface instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance. + * + * @return OutputInterface An OutputInterface instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleExceptionEvent.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleExceptionEvent.php new file mode 100644 index 0000000..603b7ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleExceptionEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle exception thrown in a command. + * + * @author Fabien Potencier + */ +class ConsoleExceptionEvent extends ConsoleEvent +{ + private $exception; + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setException($exception); + $this->exitCode = (int) $exitCode; + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php new file mode 100644 index 0000000..b6a5d7c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Event/ConsoleTerminateEvent.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var int + */ + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/CommandNotFoundException.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/CommandNotFoundException.php new file mode 100644 index 0000000..ce6fefe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private $alternatives; + + /** + * @param string $message Exception message to throw. + * @param array $alternatives List of similar defined names. + * @param int $code Exception code. + * @param Exception $previous previous exception used for the exception chaining. + */ + public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return array A list of similar defined names. + */ + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/ExceptionInterface.php new file mode 100644 index 0000000..491cc4c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/InvalidArgumentException.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..07cc0b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/InvalidOptionException.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/InvalidOptionException.php new file mode 100644 index 0000000..b2eec61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name typed in the console. + * + * @author Jérôme Tamarelle + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/LogicException.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/LogicException.php new file mode 100644 index 0000000..fc37b8d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/RuntimeException.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/RuntimeException.php new file mode 100644 index 0000000..51d7d80 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatter.php new file mode 100644 index 0000000..244e25c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + */ +class OutputFormatter implements OutputFormatterInterface +{ + private $decorated; + private $styles = array(); + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + return preg_replace('/([^\\\\]?) FormatterStyle" instances + */ + public function __construct($decorated = false, array $styles = array()) + { + $this->decorated = (bool) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style isn't defined + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message) + { + $message = (string) $message; + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9_=;-]*'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + return str_replace('\\<', '<', $output); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|bool false if string is not format string + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + try { + $style->setOption($match[1]); + } catch (\InvalidArgumentException $e) { + return false; + } + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php new file mode 100644 index 0000000..5a52ba0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php new file mode 100644 index 0000000..c7c6b4a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = array( + 'black' => array('set' => 30, 'unset' => 39), + 'red' => array('set' => 31, 'unset' => 39), + 'green' => array('set' => 32, 'unset' => 39), + 'yellow' => array('set' => 33, 'unset' => 39), + 'blue' => array('set' => 34, 'unset' => 39), + 'magenta' => array('set' => 35, 'unset' => 39), + 'cyan' => array('set' => 36, 'unset' => 39), + 'white' => array('set' => 37, 'unset' => 39), + 'default' => array('set' => 39, 'unset' => 39), + ); + private static $availableBackgroundColors = array( + 'black' => array('set' => 40, 'unset' => 49), + 'red' => array('set' => 41, 'unset' => 49), + 'green' => array('set' => 42, 'unset' => 49), + 'yellow' => array('set' => 43, 'unset' => 49), + 'blue' => array('set' => 44, 'unset' => 49), + 'magenta' => array('set' => 45, 'unset' => 49), + 'cyan' => array('set' => 46, 'unset' => 49), + 'white' => array('set' => 47, 'unset' => 49), + 'default' => array('set' => 49, 'unset' => 49), + ); + private static $availableOptions = array( + 'bold' => array('set' => 1, 'unset' => 22), + 'underscore' => array('set' => 4, 'unset' => 24), + 'blink' => array('set' => 5, 'unset' => 25), + 'reverse' => array('set' => 7, 'unset' => 27), + 'conceal' => array('set' => 8, 'unset' => 28), + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + * @param array $options The style options + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string|null $color The color name + * + * @throws InvalidArgumentException When the color name isn't defined + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (!in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option The option name + * + * @throws InvalidArgumentException When the option name isn't defined + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $setCodes = array(); + $unsetCodes = array(); + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 0000000..c36fda8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color The color name + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 0000000..e5d14ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + /** + * @var OutputFormatterStyleInterface + */ + private $emptyStyle; + + /** + * Constructor. + * + * @param OutputFormatterStyleInterface|null $emptyStyle + */ + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface|null $style + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles) - 1]; + } + + /** + * @param OutputFormatterStyleInterface $emptyStyle + * + * @return OutputFormatterStyleStack + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php new file mode 100644 index 0000000..1119b79 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DebugFormatterHelper.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default'); + private $started = array(); + private $count = -1; + + /** + * Starts a debug formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param string $prefix The prefix to use + * + * @return string + */ + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = array('border' => ++$this->count % count($this->colors)); + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + * + * @param string $id The id of the formatting session + * @param string $buffer The message to display + * @param bool $error Whether to consider the buffer as error + * @param string $prefix The prefix for output + * @param string $errorPrefix The prefix for error output + * + * @return string + */ + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param bool $successful Whether to consider the result as success + * @param string $prefix The prefix for the end output + * + * @return string + */ + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + /** + * @param string $id The id of the formatting session + * + * @return string + */ + private function getBorder($id) + { + return sprintf(' ', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DescriptorHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DescriptorHelper.php new file mode 100644 index 0000000..a53b476 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/DescriptorHelper.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private $descriptors = array(); + + /** + * Constructor. + */ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @param OutputInterface $output + * @param object $object + * @param array $options + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $options = array_merge(array( + 'raw_text' => false, + 'format' => 'txt', + ), $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @param string $format + * @param DescriptorInterface $descriptor + * + * @return DescriptorHelper + */ + public function register($format, DescriptorInterface $descriptor) + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'descriptor'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/FormatterHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/FormatterHelper.php new file mode 100644 index 0000000..ac736f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/FormatterHelper.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param bool $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + if (!is_array($messages)) { + $messages = array($messages); + } + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Helper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Helper.php new file mode 100644 index 0000000..b542be3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Helper.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Returns the length of a string, using mb_strwidth if it is available. + * + * @param string $string The string to check its length + * + * @return int The length of the string + */ + public static function strlen($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + public static function formatTime($secs) + { + static $timeFormats = array( + array(0, '< 1 sec'), + array(2, '1 sec'), + array(59, 'secs', 1), + array(60, '1 min'), + array(3600, 'mins', 60), + array(5400, '1 hr'), + array(86400, 'hrs', 3600), + array(129600, '1 day'), + array(604800, 'days', 86400), + ); + + foreach ($timeFormats as $format) { + if ($secs >= $format[0]) { + continue; + } + + if (2 == count($format)) { + return $format[1]; + } + + return ceil($secs / $format[2]).' '.$format[1]; + } + } + + public static function formatMemory($memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string); + $formatter->setDecorated($isDecorated); + + return self::strlen($string); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperInterface.php new file mode 100644 index 0000000..5a923e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperSet.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperSet.php new file mode 100644 index 0000000..f7d703a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/HelperSet.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet implements \IteratorAggregate +{ + private $helpers = array(); + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return bool true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + public function getIterator() + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/InputAwareHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/InputAwareHelper.php new file mode 100644 index 0000000..4261767 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/InputAwareHelper.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputAwareInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProcessHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProcessHelper.php new file mode 100644 index 0000000..2c46a2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProcessHelper.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ProcessBuilder; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * @param int $verbosity The threshold for verbosity + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if (is_array($cmd)) { + $process = ProcessBuilder::create($cmd)->getProcess(); + } elseif ($cmd instanceof Process) { + $process = $cmd; + } else { + $process = new Process($cmd); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s', $this->escapeString($error))); + } + + return $process; + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null) + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param Process $process The Process + * @param callable|null $callback A PHP callable + * + * @return callable + */ + public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + return function ($type, $buffer) use ($output, $process, $callback, $formatter) { + $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + call_user_func($callback, $type, $buffer); + } + }; + } + + private function escapeString($str) + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressBar.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressBar.php new file mode 100644 index 0000000..51083aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressBar.php @@ -0,0 +1,601 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\LogicException; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier + * @author Chris Jones + */ +class ProgressBar +{ + // options + private $barWidth = 28; + private $barChar; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format; + private $internalFormat; + private $redrawFreq = 1; + + /** + * @var OutputInterface + */ + private $output; + private $step = 0; + private $max; + private $startTime; + private $stepWidth; + private $percent = 0.0; + private $lastMessagesLength = 0; + private $formatLineCount; + private $messages; + private $overwrite = true; + + private static $formatters; + private static $formats; + + /** + * Constructor. + * + * @param OutputInterface $output An OutputInterface instance + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, $max = 0) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->setRedrawFrequency($max / 10); + } + + $this->startTime = time(); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, callable $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition($name, $format) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + public function setMessage($message, $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage($name = 'message') + { + return $this->messages[$name]; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the progress bar maximal steps. + * + * @return int The progress bar max steps + */ + public function getMaxSteps() + { + return $this->max; + } + + /** + * Gets the current step position. + * + * @return int The progress bar step + */ + public function getProgress() + { + return $this->step; + } + + /** + * Gets the progress bar step width. + * + * @return int The progress bar step width + */ + private function getStepWidth() + { + return $this->stepWidth; + } + + /** + * Gets the current progress bar percent. + * + * @return float The current progress bar percent + */ + public function getProgressPercent() + { + return $this->percent; + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = (int) $size; + } + + /** + * Gets the progress bar width. + * + * @return int The progress bar size + */ + public function getBarWidth() + { + return $this->barWidth; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Gets the bar character. + * + * @return string A character + */ + public function getBarCharacter() + { + if (null === $this->barChar) { + return $this->max ? '=' : $this->emptyBarChar; + } + + return $this->barChar; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Gets the empty bar character. + * + * @return string A character + */ + public function getEmptyBarCharacter() + { + return $this->emptyBarChar; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Gets the progress bar character. + * + * @return string A character + */ + public function getProgressCharacter() + { + return $this->progressChar; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|float $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = max((int) $freq, 1); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start($max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + * + * @throws LogicException + */ + public function advance($step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + * + * @param bool $overwrite + */ + public function setOverwrite($overwrite) + { + $this->overwrite = (bool) $overwrite; + } + + /** + * Sets the current progress. + * + * @param int $step The current progress + * + * @throws LogicException + */ + public function setProgress($step) + { + $step = (int) $step; + if ($step < $this->step) { + throw new LogicException('You can\'t regress the progress bar.'); + } + + if ($this->max && $step > $this->max) { + $this->max = $step; + } + + $prevPeriod = (int) ($this->step / $this->redrawFreq); + $currPeriod = (int) ($step / $this->redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($prevPeriod !== $currPeriod || $this->max === $step) { + $this->display(); + } + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { + if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { + $text = call_user_func($formatter, $this, $this->output); + } elseif (isset($this->messages[$matches[1]])) { + $text = $this->messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }, $this->format)); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(str_repeat("\n", $this->formatLineCount)); + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + private function setRealFormat($format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + + $this->formatLineCount = substr_count($this->format, "\n"); + } + + /** + * Sets the progress bar maximal steps. + * + * @param int $max The progress bar max steps + */ + private function setMaxSteps($max) + { + $this->max = max(0, (int) $max); + $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + $lines = explode("\n", $message); + + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + foreach ($lines as $i => $line) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) { + $lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + } + + if ($this->overwrite) { + // move back to the beginning of the progress bar before redrawing it + $this->output->write("\x0D"); + } elseif ($this->step > 0) { + // move to new line + $this->output->writeln(''); + } + + if ($this->formatLineCount) { + $this->output->write(sprintf("\033[%dA", $this->formatLineCount)); + } + $this->output->write(implode("\n", $lines)); + + $this->lastMessagesLength = 0; + foreach ($lines as $line) { + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line); + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? 'verbose' : 'verbose_nomax'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? 'debug' : 'debug_nomax'; + default: + return $this->max ? 'normal' : 'normal_nomax'; + } + } + + private static function initPlaceholderFormatters() + { + return array( + 'bar' => function (ProgressBar $bar, OutputInterface $output) { + $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (ProgressBar $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $remaining = 0; + } else { + $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); + } + + return Helper::formatTime($remaining); + }, + 'estimated' => function (ProgressBar $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $estimated = 0; + } else { + $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); + } + + return Helper::formatTime($estimated); + }, + 'memory' => function (ProgressBar $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (ProgressBar $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + }, + 'max' => function (ProgressBar $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (ProgressBar $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', + 'normal_nomax' => ' %current% [%bar%]', + + 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressIndicator.php new file mode 100644 index 0000000..8c8a49c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/ProgressIndicator.php @@ -0,0 +1,322 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond + */ +class ProgressIndicator +{ + private $output; + private $startTime; + private $format; + private $message; + private $indicatorValues; + private $indicatorCurrent; + private $indicatorChangeInterval; + private $indicatorUpdateTime; + private $lastMessagesLength; + private $started = false; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output + * @param string|null $format Indicator format + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = array('-', '\\', '|', '/'); + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > count($indicatorValues)) { + throw new \InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + * + * @param string|null $message + */ + public function setMessage($message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Gets the current indicator message. + * + * @return string|null + * + * @internal for PHP 5.3 compatibility + */ + public function getMessage() + { + return $this->message; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + * + * @internal for PHP 5.3 compatibility + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the current animated indicator character. + * + * @return string + * + * @internal for PHP 5.3 compatibility + */ + public function getCurrentValue() + { + return $this->indicatorValues[$this->indicatorCurrent % count($this->indicatorValues)]; + } + + /** + * Starts the indicator output. + * + * @param $message + */ + public function start($message) + { + if ($this->started) { + throw new \LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->lastMessagesLength = 0; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new \LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish($message) + { + if (!$this->started) { + throw new \LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $self = $this; + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + return call_user_func($formatter, $self); + } + + return $matches[0]; + }, $this->format)); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + // append whitespace to match the line's length + if (null !== $this->lastMessagesLength) { + if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $message)) { + $message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT); + } + } + + if ($this->output->isDecorated()) { + $this->output->write("\x0D"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + + $this->lastMessagesLength = 0; + + $len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $message); + + if ($len > $this->lastMessagesLength) { + $this->lastMessagesLength = $len; + } + } + + private function getCurrentTimeInMilliseconds() + { + return round(microtime(true) * 1000); + } + + private static function initPlaceholderFormatters() + { + return array( + 'indicator' => function (ProgressIndicator $indicator) { + return $indicator->getCurrentValue(); + }, + 'message' => function (ProgressIndicator $indicator) { + return $indicator->getMessage(); + }, + 'elapsed' => function (ProgressIndicator $indicator) { + return Helper::formatTime(time() - $indicator->getStartTime()); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ); + } + + private static function initFormats() + { + return array( + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/QuestionHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/QuestionHelper.php new file mode 100644 index 0000000..13ea0d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -0,0 +1,464 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Question\ChoiceQuestion; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class QuestionHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks a question to the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @param Question $question The question to ask + * + * @return string The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + return $question->getDefault(); + } + + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $interviewer = function () use ($output, $question) { + return $this->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + * + * @throws InvalidArgumentException In case the stream is not a resource + */ + public function setInputStream($stream) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Input stream must be a valid resource.'); + } + + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @return resource + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'question'; + } + + /** + * Asks the question to the user. + * + * @param OutputInterface $output + * @param Question $question + * + * @return bool|mixed|null|string + * + * @throws \Exception + * @throws \RuntimeException + */ + private function doAsk(OutputInterface $output, Question $question) + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: STDIN; + $autocomplete = $question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($output, $inputStream)); + } catch (\RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = $this->readFromInput($inputStream); + } + } else { + $ret = trim($this->autocomplete($output, $question, $inputStream)); + } + + $ret = strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + /** + * Outputs the question prompt. + * + * @param OutputInterface $output + * @param Question $question + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + $messages = (array) $question->getQuestion(); + foreach ($question->getChoices() as $key => $value) { + $messages[] = sprintf(" [%-${width}s] %s", $key, $value); + } + + $output->writeln($messages); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * Outputs an error message. + * + * @param OutputInterface $output + * @param \Exception $error + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = ''.$error->getMessage().''; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param OutputInterface $output + * @param Question $question + * + * @return string + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream) + { + $autocomplete = $question->getAutocompleterValues(); + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // Backspace Character + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + // Move cursor backwards + $output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text + $output->write(''.substr($matches[$ofs], $i).''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + + return $ret; + } + + /** + * Gets a hidden response from user. + * + * @param OutputInterface $output An Output instance + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream) + { + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + throw new RuntimeException('Unable to hide the response.'); + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param Question $question A Question instance + * + * @return string The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question) + { + $error = null; + $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return call_user_func($question->getValidator(), $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Returns a valid unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + /** + * Reads user input. + * + * @param resource $stream The input stream + * + * @return string User input + * + * @throws RuntimeException + */ + private function readFromInput($stream) + { + if (STDIN === $stream && function_exists('readline')) { + $ret = readline(); + } else { + $ret = fgets($stream, 4096); + } + + if (false === $ret) { + throw new RuntimeException('Aborted'); + } + + return trim($ret); + } + + /** + * Returns whether Stty is available or not. + * + * @return bool + */ + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 0000000..942278b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + $validator = $question->getValidator(); + $question->setValidator(function ($value) use ($validator) { + if (null !== $validator) { + $value = $validator($value); + } + + // make required + if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) { + throw new LogicException('A value is required.'); + } + + return $value; + }); + + return parent::ask($input, $output, $question); + } + + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = $question->getQuestion(); + $default = $question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' %s [%s]:', $text, $choices[$default]); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, $default); + } + + $output->writeln($text); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + foreach ($question->getChoices() as $key => $value) { + $output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); + } + } + + $output->write(' > '); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Table.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Table.php new file mode 100644 index 0000000..1f103ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/Table.php @@ -0,0 +1,654 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Abdellatif Ait boudad + * @author Max Grigorian + */ +class Table +{ + /** + * Table headers. + * + * @var array + */ + private $headers = array(); + + /** + * Table rows. + * + * @var array + */ + private $rows = array(); + + /** + * Column widths cache. + * + * @var array + */ + private $columnWidths = array(); + + /** + * Number of columns cache. + * + * @var array + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + /** + * @var TableStyle + */ + private $style; + + /** + * @var array + */ + private $columnStyles = array(); + + private static $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @param string $name The style name + * @param TableStyle $style A TableStyle instance + */ + public static function setStyleDefinition($name, TableStyle $style) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + * + * @param string $name The style name + * + * @return TableStyle A TableStyle instance + */ + public static function getStyleDefinition($name) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + if (!self::$styles[$name]) { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return self::$styles[$name]; + } + + /** + * Sets table style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setStyle($name) + { + if ($name instanceof TableStyle) { + $this->style = $name; + } elseif (isset(self::$styles[$name])) { + $this->style = self::$styles[$name]; + } else { + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current table style. + * + * @return TableStyle + */ + public function getStyle() + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param int $columnIndex Column index + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return Table + */ + public function setColumnStyle($columnIndex, $name) + { + $columnIndex = intval($columnIndex); + + if ($name instanceof TableStyle) { + $this->columnStyles[$columnIndex] = $name; + } elseif (isset(self::$styles[$name])) { + $this->columnStyles[$columnIndex] = self::$styles[$name]; + } else { + throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + * + * @param int $columnIndex Column index + * + * @return TableStyle + */ + public function getColumnStyle($columnIndex) + { + if (isset($this->columnStyles[$columnIndex])) { + return $this->columnStyles[$columnIndex]; + } + + return $this->getStyle(); + } + + public function setHeaders(array $headers) + { + $headers = array_values($headers); + if (!empty($headers) && !is_array($headers[0])) { + $headers = array($headers); + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = array(); + + return $this->addRows($rows); + } + + public function addRows(array $rows) + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + public function addRow($row) + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + if (!is_array($row)) { + throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); + } + + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $this->calculateNumberOfColumns(); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); + + $this->renderRowSeparator(); + if (!empty($headers)) { + foreach ($headers as $header) { + $this->renderRow($header, $this->style->getCellHeaderFormat()); + $this->renderRowSeparator(); + } + } + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + } else { + $this->renderRow($row, $this->style->getCellRowFormat()); + } + } + if (!empty($rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + return; + } + + $markup = $this->style->getCrossingChar(); + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->columnWidths[$column]).$this->style->getCrossingChar(); + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + $this->output->write(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Renders table row. + * + * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * + * @param array $row + * @param string $cellFormat + */ + private function renderRow(array $row, $cellFormat) + { + if (empty($row)) { + return; + } + + $this->renderColumnSeparator(); + foreach ($this->getRowColumns($row) as $column) { + $this->renderCell($row, $column, $cellFormat); + $this->renderColumnSeparator(); + } + $this->output->writeln(''); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param int $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + $width = $this->columnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->columnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + $this->output->write(sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width))); + } else { + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + $this->output->write(sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()))); + } + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return; + } + + $columns = array(0); + foreach (array_merge($this->headers, $this->rows) as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows($rows) + { + $unmergedRows = array(); + for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + if (!strstr($cell, "\n")) { + continue; + } + $lines = explode("\n", $cell); + foreach ($lines as $lineKey => $line) { + if ($cell instanceof TableCell) { + $line = new TableCell($line, array('colspan' => $cell->getColspan())); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + $tableRows = array(); + foreach ($rows as $rowKey => $row) { + $tableRows[] = $this->fillCells($row); + if (isset($unmergedRows[$rowKey])) { + $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + } + } + + return $tableRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @param array $rows + * @param int $line + * + * @return array + */ + private function fillNextRows($rows, $line) + { + $unmergedRows = array(); + foreach ($rows[$line] as $column => $cell) { + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = array($cell); + if (strstr($cell, "\n")) { + $lines = explode("\n", $cell); + $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan())); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, ''), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan())); + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell)); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, array($row)); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + * + * @param array $row + * + * @return array + */ + private function fillCells($row) + { + $newRow = array(); + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + /** + * @param array $rows + * @param int $line + * + * @return array + */ + private function copyRow($rows, $line) + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan())); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + * + * @param array $row + * + * @return int + */ + private function getNumberOfColumns(array $row) + { + $columns = count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + * + * @param array $row + * + * @return array + */ + private function getRowColumns($row) + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + * + * @param array $rows + */ + private function calculateColumnsWidth($rows) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = array(); + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $lengths[] = $this->getCellWidth($row, $column); + } + + $this->columnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2; + } + } + + /** + * Gets column width. + * + * @return int + */ + private function getColumnSeparatorWidth() + { + return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Gets cell width. + * + * @param array $row + * @param int $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // we assume that cell value will be across more than one column. + $cellWidth = $cellWidth / $cell->getColspan(); + } + + return $cellWidth; + } + + return 0; + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->columnWidths = array(); + $this->numberOfColumns = null; + } + + private static function initStyles() + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChar('') + ->setVerticalBorderChar(' ') + ->setCrossingChar('') + ->setCellRowContentFormat('%s') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + return array( + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableCell.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableCell.php new file mode 100644 index 0000000..69442d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableCell.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class TableCell +{ + /** + * @var string + */ + private $value; + + /** + * @var array + */ + private $options = array( + 'rowspan' => 1, + 'colspan' => 1, + ); + + /** + * @param string $value + * @param array $options + */ + public function __construct($value = '', array $options = array()) + { + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + * + * @return string + */ + public function __toString() + { + return $this->value; + } + + /** + * Gets number of colspan. + * + * @return int + */ + public function getColspan() + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + * + * @return int + */ + public function getRowspan() + { + return (int) $this->options['rowspan']; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableSeparator.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableSeparator.php new file mode 100644 index 0000000..8cbbc66 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableSeparator.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier + */ +class TableSeparator extends TableCell +{ + /** + * @param string $value + * @param array $options + */ + public function __construct(array $options = array()) + { + parent::__construct('', $options); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableStyle.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableStyle.php new file mode 100644 index 0000000..d7e28ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Helper/TableStyle.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + */ +class TableStyle +{ + private $paddingChar = ' '; + private $horizontalBorderChar = '-'; + private $verticalBorderChar = '|'; + private $crossingChar = '+'; + private $cellHeaderFormat = '%s'; + private $cellRowFormat = '%s'; + private $cellRowContentFormat = ' %s '; + private $borderFormat = '%s'; + private $padType = STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return TableStyle + */ + public function setPaddingChar($paddingChar) + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + * + * @return string + */ + public function getPaddingChar() + { + return $this->paddingChar; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return TableStyle + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Gets horizontal border character. + * + * @return string + */ + public function getHorizontalBorderChar() + { + return $this->horizontalBorderChar; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return TableStyle + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Gets vertical border character. + * + * @return string + */ + public function getVerticalBorderChar() + { + return $this->verticalBorderChar; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return TableStyle + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Gets crossing character. + * + * @return string $crossingChar + */ + public function getCrossingChar() + { + return $this->crossingChar; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return TableStyle + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + * + * @return string + */ + public function getCellHeaderFormat() + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return TableStyle + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + * + * @return string + */ + public function getCellRowFormat() + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return TableStyle + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + * + * @return string + */ + public function getCellRowContentFormat() + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return TableStyle + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + * + * @return string + */ + public function getBorderFormat() + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return TableStyle + */ + public function setPadType($padType) + { + if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + * + * @return int + */ + public function getPadType() + { + return $this->padType; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArgvInput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArgvInput.php new file mode 100644 index 0000000..fec04e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArgvInput.php @@ -0,0 +1,358 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + // Convert empty values to null + if (!isset($value[0])) { + $value = null; + } + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray()) { + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + if ($onlyParams && $token === '--') { + return false; + } + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + return true; + } + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < count($tokens)) { + $token = array_shift($tokens); + if ($onlyParams && $token === '--') { + return false; + } + + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value.'=')) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $tokens = array_map(function ($token) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1].$this->escapeToken($match[2]); + } + + if ($token && $token[0] !== '-') { + return $this->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArrayInput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArrayInput.php new file mode 100644 index 0000000..6705d4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/ArrayInput.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if ($onlyParams && $v === '--') { + return false; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if ($onlyParams && ($k === '--' || (is_int($k) && $v === '--'))) { + return false; + } + + if (is_int($k)) { + if (in_array($v, $values)) { + return true; + } + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $params = array(); + foreach ($this->parameters as $param => $val) { + if ($param && '-' === $param[0]) { + $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + } else { + $params[] = $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if ($key === '--') { + return; + } + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/Input.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/Input.php new file mode 100644 index 0000000..85499fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/Input.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + /** + * @var InputDefinition + */ + protected $definition; + protected $options = array(); + protected $arguments = array(); + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * Checks if the input is interactive. + * + * @return bool Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + * + * @param string $token + * + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputArgument.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputArgument.php new file mode 100644 index 0000000..048ee4f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputArgument.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputAwareInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputAwareInterface.php new file mode 100644 index 0000000..d0f11e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputAwareInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + * + * @param InputInterface + */ + public function setInput(InputInterface $input); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputDefinition.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputDefinition.php new file mode 100644 index 0000000..e9944db --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputDefinition.php @@ -0,0 +1,411 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|int $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return int The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return int The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { + throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @param bool $short Whether to return the short version (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $elements = array(); + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf( + ' %s%s%s', + $option->isValueOptional() ? '[' : '', + strtoupper($option->getName()), + $option->isValueOptional() ? ']' : '' + ); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if (!$argument->isRequired()) { + $element = '['.$element.']'; + } elseif ($argument->isArray()) { + $element = $element.' ('.$element.')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputInterface.php new file mode 100644 index 0000000..2e1fb9f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputInterface.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|bool $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return bool + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputOption.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputOption.php new file mode 100644 index 0000000..3a752cb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/InputOption.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + * + * @param InputOption $option option to compare + * + * @return bool + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Input/StringInput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/StringInput.php new file mode 100644 index 0000000..9ce0217 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Input/StringInput.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Console/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Logger/ConsoleLogger.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Logger/ConsoleLogger.php new file mode 100644 index 0000000..1f7417e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Logger/ConsoleLogger.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas + * + * @link http://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + const INFO = 'info'; + const ERROR = 'error'; + + /** + * @var OutputInterface + */ + private $output; + /** + * @var array + */ + private $verbosityLevelMap = array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ); + /** + * @var array + */ + private $formatLevelMap = array( + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ); + + /** + * @param OutputInterface $output + * @param array $verbosityLevelMap + * @param array $formatLevelMap + */ + public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array()) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + // Write to the error output if necessary and available + if ($this->formatLevelMap[$level] === self::ERROR && $this->output instanceof ConsoleOutputInterface) { + $output = $this->output->getErrorOutput(); + } else { + $output = $this->output; + } + + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context))); + } + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + * + * @param string $message + * @param array $context + * + * @return string + */ + private function interpolate($message, array $context) + { + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace[sprintf('{%s}', $key)] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/BufferedOutput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/BufferedOutput.php new file mode 100644 index 0000000..5682fc2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/BufferedOutput.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon + */ +class BufferedOutput extends Output +{ + /** + * @var string + */ + private $buffer = ''; + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= "\n"; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutput.php new file mode 100644 index 0000000..8e1f360 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutput.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + /** + * @var StreamOutput + */ + private $stderr; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput() + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + * + * @return bool + */ + private function isRunningOS400() + { + $checks = array( + function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ); + + return false !== stristr(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + $outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output'; + + return @fopen($outputStream, 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + $errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output'; + + return fopen($errorStream, 'w'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php new file mode 100644 index 0000000..5eb4fc7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/ConsoleOutputInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + * + * @return OutputInterface + */ + public function getErrorOutput(); + + /** + * Sets the OutputInterface used for errors. + * + * @param OutputInterface $error + */ + public function setErrorOutput(OutputInterface $error); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/NullOutput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/NullOutput.php new file mode 100644 index 0000000..682f9a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/NullOutput.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class NullOutput implements OutputInterface +{ + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + // to comply with the interface we must return a OutputFormatterInterface + return new OutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return self::VERBOSITY_QUIET; + } + + public function isQuiet() + { + return true; + } + + public function isVerbose() + { + return false; + } + + public function isVeryVerbose() + { + return false; + } + + public function isDebug() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/Output.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/Output.php new file mode 100644 index 0000000..4476ffb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/Output.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = $formatter ?: new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param bool $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.php new file mode 100644 index 0000000..a291ca7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/OutputInterface.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; + + const OUTPUT_NORMAL = 1; + const OUTPUT_RAW = 2; + const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write($messages, $newline = false, $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln($messages, $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param int $level The level of verbosity (one of the VERBOSITY constants) + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return int The current level of verbosity (one of the VERBOSITY constants) + */ + public function getVerbosity(); + + /** + * Returns whether verbosity is quiet (-q). + * + * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise + */ + public function isQuiet(); + + /** + * Returns whether verbosity is verbose (-v). + * + * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise + */ + public function isVerbose(); + + /** + * Returns whether verbosity is very verbose (-vv). + * + * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise + */ + public function isVeryVerbose(); + + /** + * Returns whether verbosity is debug (-vvv). + * + * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise + */ + public function isDebug(); + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + */ + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Output/StreamOutput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/StreamOutput.php new file mode 100644 index 0000000..62d04c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Output/StreamOutput.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + // should never happen + throw new RuntimeException('Unable to write output.'); + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - Windows without Ansicon, ConEmu or Mintty + * - non tty consoles + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + if (DIRECTORY_SEPARATOR === '\\') { + return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Question/ChoiceQuestion.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Question/ChoiceQuestion.php new file mode 100644 index 0000000..2c40638 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Question/ChoiceQuestion.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier + */ +class ChoiceQuestion extends Question +{ + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct($question, array $choices, $default = null) + { + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + * + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @param bool $multiselect + * + * @return ChoiceQuestion The current instance + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Gets the prompt for choices. + * + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @param string $prompt + * + * @return ChoiceQuestion The current instance + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @param string $errorMessage + * + * @return ChoiceQuestion The current instance + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns the default answer validator. + * + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $selected); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = array($selected); + } + + $multiselectChoices = array(); + foreach ($selectedChoices as $value) { + $results = array(); + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + $multiselectChoices[] = (string) $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Question/ConfirmationQuestion.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Question/ConfirmationQuestion.php new file mode 100644 index 0000000..29d9887 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Question/ConfirmationQuestion.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier + */ +class ConfirmationQuestion extends Question +{ + private $trueAnswerRegex; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + * + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return !$answer || $answerIsTrue; + }; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Question/Question.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Question/Question.php new file mode 100644 index 0000000..71cb8f5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Question/Question.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier + */ +class Question +{ + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * Constructor. + * + * @param string $question The question to ask to the user + * @param mixed $default The default answer to return if the user enters nothing + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + * + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * Returns the default answer. + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns whether the user response must be hidden. + * + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @param bool $hidden + * + * @return Question The current instance + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response can not be hidden. + * + * @param bool $fallback + * + * @return Question The current instance + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + * + * @return null|array|\Traversable + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * Sets values for the autocompleter. + * + * @param null|array|\Traversable $values + * + * @return Question The current instance + * + * @throws InvalidArgumentException + * @throws LogicException + */ + public function setAutocompleterValues($values) + { + if (is_array($values) && $this->isAssoc($values)) { + $values = array_merge(array_keys($values), array_values($values)); + } + + if (null !== $values && !is_array($values)) { + if (!$values instanceof \Traversable || $values instanceof \Countable) { + throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); + } + } + + if ($this->hidden) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * Sets a validator for the question. + * + * @param null|callable $validator + * + * @return Question The current instance + */ + public function setValidator(callable $validator = null) + { + $this->validator = $validator; + + return $this; + } + + /** + * Gets the validator for the question. + * + * @return null|callable + */ + public function getValidator() + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @param null|int $attempts + * + * @return Question The current instance + * + * @throws InvalidArgumentException In case the number of attempts is invalid. + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return null|int + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @param callable $normalizer + * + * @return Question The current instance + */ + public function setNormalizer(callable $normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * + * @return callable + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/README.md b/vendor/symfony/symfony/src/Symfony/Component/Console/README.md new file mode 100644 index 0000000..25f700c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/README.md @@ -0,0 +1,67 @@ +Console Component +================= + +Console eases the creation of beautiful and testable command line interfaces. + +The Application object manages the CLI application: + +```php +use Symfony\Component\Console\Application; + +$console = new Application(); +$console->run(); +``` + +The ``run()`` method parses the arguments and options passed on the command +line and executes the right command. + +Registering a new command can easily be done via the ``register()`` method, +which returns a ``Command`` instance: + +```php +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +$console + ->register('ls') + ->setDefinition(array( + new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'), + )) + ->setDescription('Displays the files in the given directory') + ->setCode(function (InputInterface $input, OutputInterface $output) { + $dir = $input->getArgument('dir'); + + $output->writeln(sprintf('Dir listing for %s', $dir)); + }) +; +``` + +You can also register new commands via classes. + +The component provides a lot of features like output coloring, input and +output abstractions (so that you can easily unit-test your commands), +validation, automatic help messages, ... + +Tests +----- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Console/ + $ composer install + $ phpunit + +Third Party +----------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. + +Resources +--------- + +[The Console Component](https://symfony.com/doc/current/components/console.html) + +[How to create a Console Command](https://symfony.com/doc/current/cookbook/console/console_command.html) diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Resources/bin/hiddeninput.exe b/vendor/symfony/symfony/src/Symfony/Component/Console/Resources/bin/hiddeninput.exe new file mode 100644 index 0000000..c8cf65e Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Component/Console/Resources/bin/hiddeninput.exe differ diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Style/OutputStyle.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Style/OutputStyle.php new file mode 100644 index 0000000..de7be1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Style/OutputStyle.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + /** + * @param OutputInterface $output + */ + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $max + * + * @return ProgressBar + */ + public function createProgressBar($max = 0) + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return $this->output->isQuiet(); + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return $this->output->isVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return $this->output->isVeryVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->output->isDebug(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Style/StyleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Style/StyleInterface.php new file mode 100644 index 0000000..2448547 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Style/StyleInterface.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @param string $message + */ + public function title($message); + + /** + * Formats a section title. + * + * @param string $message + */ + public function section($message); + + /** + * Formats a list. + * + * @param array $elements + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @param string|array $message + */ + public function text($message); + + /** + * Formats a success result bar. + * + * @param string|array $message + */ + public function success($message); + + /** + * Formats an error result bar. + * + * @param string|array $message + */ + public function error($message); + + /** + * Formats an warning result bar. + * + * @param string|array $message + */ + public function warning($message); + + /** + * Formats a note admonition. + * + * @param string|array $message + */ + public function note($message); + + /** + * Formats a caution admonition. + * + * @param string|array $message + */ + public function caution($message); + + /** + * Formats a table. + * + * @param array $headers + * @param array $rows + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + * + * @param string $question + * @param string|null $default + * @param callable|null $validator + * + * @return string + */ + public function ask($question, $default = null, $validator = null); + + /** + * Asks a question with the user input hidden. + * + * @param string $question + * @param callable|null $validator + * + * @return string + */ + public function askHidden($question, $validator = null); + + /** + * Asks for confirmation. + * + * @param string $question + * @param bool $default + * + * @return bool + */ + public function confirm($question, $default = true); + + /** + * Asks a choice question. + * + * @param string $question + * @param array $choices + * @param string|int|null $default + * + * @return string + */ + public function choice($question, array $choices, $default = null); + + /** + * Add newline(s). + * + * @param int $count The number of newlines + */ + public function newLine($count = 1); + + /** + * Starts the progress output. + * + * @param int $max Maximum steps (0 if unknown) + */ + public function progressStart($max = 0); + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function progressAdvance($step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Style/SymfonyStyle.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Style/SymfonyStyle.php new file mode 100644 index 0000000..c69b0dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Style/SymfonyStyle.php @@ -0,0 +1,415 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond + */ +class SymfonyStyle extends OutputStyle +{ + const MAX_LINE_LENGTH = 120; + + private $input; + private $questionHelper; + private $progressBar; + private $lineLength; + private $bufferedOutput; + + /** + * @param InputInterface $input + * @param OutputInterface $output + */ + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($output); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string|null $type The block type (added in [] on first line) + * @param string|null $style The style to apply to the whole block + * @param string $prefix The prefix for the block + * @param bool $padding Whether to add vertical padding + */ + public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false) + { + $this->autoPrependBlock(); + $messages = is_array($messages) ? array_values($messages) : array($messages); + $lines = array(); + + // add type + if (null !== $type) { + $messages[0] = sprintf('[%s] %s', $type, $messages[0]); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + $message = OutputFormatter::escape($message); + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix), PHP_EOL, true))); + + if (count($messages) > 1 && $key < count($messages) - 1) { + $lines[] = ''; + } + } + + if ($padding && $this->isDecorated()) { + array_unshift($lines, ''); + $lines[] = ''; + } + + foreach ($lines as &$line) { + $line = sprintf('%s%s', $prefix, $line); + $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + + if ($style) { + $line = sprintf('<%s>%s', $style, $line); + } + } + + $this->writeln($lines); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section($message) + { + $this->autoPrependBlock(); + $this->writeln(array( + sprintf('%s', $message), + sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + )); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * {@inheritdoc} + */ + public function comment($message) + { + $this->autoPrependText(); + + $messages = is_array($message) ? array_values($message) : array($message); + foreach ($messages as $message) { + $this->writeln(sprintf(' // %s', $message)); + } + } + + /** + * {@inheritdoc} + */ + public function success($message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error($message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning($message) + { + $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note($message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * {@inheritdoc} + */ + public function caution($message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + $headers = array_map(function ($value) { return sprintf('%s', $value); }, $headers); + + $table = new Table($this); + $table->setHeaders($headers); + $table->setRows($rows); + $table->setStyle('symfony-style-guide'); + + $table->render(); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function ask($question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden($question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm($question, $default = true) + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice($question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart($max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance($step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + $this->progressBar = null; + } + + /** + * {@inheritdoc} + */ + public function createProgressBar($max = 0) + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== DIRECTORY_SEPARATOR) { + $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('▓'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @param Question $question + * + * @return string + */ + public function askQuestion(Question $question) + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + if (!$this->questionHelper) { + $this->questionHelper = new SymfonyQuestionHelper(); + } + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + parent::writeln($messages, $type); + $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + parent::write($messages, $newline, $type); + $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * @return ProgressBar + */ + private function getProgressBar() + { + if (!$this->progressBar) { + throw new RuntimeException('The ProgressBar is not started.'); + } + + return $this->progressBar; + } + + private function getTerminalWidth() + { + $application = new Application(); + $dimensions = $application->getTerminalDimensions(); + + return $dimensions[0] ?: self::MAX_LINE_LENGTH; + } + + private function autoPrependBlock() + { + $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + return $this->newLine(); //empty history, so we should start with a new line. + } + //Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText() + { + $fetched = $this->bufferedOutput->fetch(); + //Prepend new line if last char isn't EOL: + if ("\n" !== substr($fetched, -1)) { + $this->newLine(); + } + } + + private function reduceBuffer($messages) + { + // We need to know if the two last chars are PHP_EOL + // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer + return array_map(function ($value) { + return substr($value, -4); + }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/ApplicationTester.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/ApplicationTester.php new file mode 100644 index 0000000..da8a19c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/ApplicationTester.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return int The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/CommandTester.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/CommandTester.php new file mode 100644 index 0000000..8d6486e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tester/CommandTester.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + private $statusCode; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = array()) + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(array('command' => $this->command->getName()), $input); + } + + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/ApplicationTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/ApplicationTest.php new file mode 100644 index 0000000..7eaa28d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -0,0 +1,1093 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ApplicationTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + require_once self::$fixturesPath.'/Foo5Command.php'; + require_once self::$fixturesPath.'/FoobarCommand.php'; + require_once self::$fixturesPath.'/BarBucCommand.php'; + require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; + require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); + $this->assertEquals(array('help', 'list'), array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo version bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands(array($foo = new \FooCommand(), $foo1 = new \Foo1Command())); + $commands = $application->all(); + $this->assertEquals(array($foo, $foo1), array($commands['foo:bar'], $commands['foo:bar1']), '->addCommands() registers an array of commands'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. + */ + public function testAddCommandWithEmptyConstructor() + { + $application = new Application(); + $application->add(new \Foo5Command()); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); + } + + public function testSilentHelp() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $tester = new ApplicationTester($application); + $tester->run(array('-h' => true, '-q' => true), array('decorated' => false)); + + $this->assertEmpty($tester->getDisplay(true)); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The command "foofoo" does not exist. + */ + public function testGetInvalidCommand() + { + $application = new Application(); + $application->get('foofoo'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(array('foo'), $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + } + + public function testFindNamespaceWithSubnamespaces() + { + $application = new Application(); + $application->add(new \FooSubnamespaced1Command()); + $application->add(new \FooSubnamespaced2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1). + */ + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \BarBucCommand()); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + $application->findNamespace('f'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo1" is not defined + */ + public function testFindUniqueNameButNamespaceName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($commandName = 'foo1'); + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + /** + * @dataProvider provideAmbiguousAbbreviations + */ + public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) + { + $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); + + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($abbreviation); + } + + public function provideAmbiguousAbbreviations() + { + return array( + array('f', 'Command "f" is not defined.'), + array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'), + array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'), + ); + } + + public function testFindCommandEqualNamespace() + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); + $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); + } + + public function testFindCommandWithAmbiguousNamespacesButUniqueName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \FoobarCommand()); + + $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); + } + + public function testFindCommandWithMissingNamespace() + { + $application = new Application(); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo4Command', $application->find('f::t')); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return array( + array('foo3:baR'), + array('foO3:bar'), + ); + } + + public function testFindAlternativeExceptionMessageMultiple() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1:bar/', $e->getMessage()); + $this->assertRegExp('/foo:bar/', $e->getMessage()); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1/', $e->getMessage()); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + } + + // Test if "bar1" command throw a "CommandNotFoundException" and does not contain + // "foo:bar" as alternative because "bar1" is too far from "foo:bar" + try { + $application->find($commandName = 'bar1'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(array('afoobar1', 'foo:bar1'), $e->getAlternatives()); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); + $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeCommandsWithAnAlias() + { + $fooCommand = new \FooCommand(); + $fooCommand->setAliases(array('foo2')); + + $application = new Application(); + $application->add($fooCommand); + + $result = $application->find('foo'); + + $this->assertSame($fooCommand, $result); + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertSame(array(), $e->getAlternatives()); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertCount(3, $e->getAlternatives()); + $this->assertContains('foo', $e->getAlternatives()); + $this->assertContains('foo1', $e->getAlternatives()); + $this->assertContains('foo3', $e->getAlternatives()); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); + } + } + + 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')); + } + + /** + * @expectedException Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo::bar" is not defined. + */ + public function testFindWithDoubleColonInNameThrowsException() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo4Command()); + $application->find('foo::bar'); + } + + public function testSetCatchExceptions() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + + $application->setCatchExceptions(false); + try { + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + public function testRenderException() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exception'); + + $tester->run(array('command' => 'foo'), array('decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + $this->assertContains('Exception trace', $tester->getDisplay(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(array('command' => 'list', '--foo' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getDisplay(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command()); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo3:bar'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo3:bar'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRenderExceptionWithDoubleWidthCharacters() + { + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(120)); + $application->register('foo')->setCode(function () { + throw new \Exception('エラーメッセージ'); + }); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(array('command' => 'foo'), array('decorated' => true)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth')); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->will($this->returnValue(32)); + $application->register('foo')->setCode(function () { + throw new \Exception('コマンドの実行中にエラーが発生しました。'); + }); + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo'), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal'); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = array('cli.php', 'foo:bar1'); + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); + $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run(array(), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); + + $tester->run(array('--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); + + $tester->run(array('-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); + + $tester->run(array('command' => 'list', '--help' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(array('command' => 'list', '-h' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + + $tester->run(array('--ansi' => true)); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(array('--no-ansi' => true)); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(array('--version' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); + + $tester->run(array('-V' => true), array('decorated' => false)); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); + + $tester->run(array('command' => 'list', '--quiet' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + + $tester->run(array('command' => 'list', '-q' => true)); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + + $tester->run(array('command' => 'list', '--verbose' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 1)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 2)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 3)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); + + $tester->run(array('command' => 'list', '--verbose' => 4)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); + + $tester->run(array('command' => 'list', '-v' => true)); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vv' => true)); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(array('command' => 'list', '-vvv' => true)); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(array('command' => 'foo:bar', '--no-interaction' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(array('command' => 'foo:bar', '-n' => true), array('decorated' => false)); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + /** + * Issue #9285. + * + * If the "verbose" option is just before an argument in ArgvInput, + * an argument value should not be treated as verbosity value. + * This test will fail with "Not enough arguments." if broken + */ + public function testVerboseValueNotBreakArguments() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + + $input = new ArgvInput(array('cli.php', '-v', 'foo:bar')); + $application->run($input, $output); + + $input = new ArgvInput(array('cli.php', '--verbose', 'foo:bar')); + $application->run($input, $output); + } + + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + + public function testRunReturnsExitCodeOneForExceptionCodeZero() + { + $exception = new \Exception('', 0); + + $application = $this->getMock('Symfony\Component\Console\Application', array('doRun')); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->will($this->throwException($exception)); + + $exitCode = $application->run(new ArrayInput(array()), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition(array($def)) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(array('command' => 'foo')); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return array( + array(new InputArgument('command', InputArgument::REQUIRED)), + array(new InputOption('quiet', '', InputOption::VALUE_NONE)), + array(new InputOption('query', 'q', InputOption::VALUE_NONE)), + ); + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet(array(new FormatterHelper()))); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')))); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testRunWithDispatcher() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($this->getDispatcher()); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage caught + */ + public function testRunWithExceptionAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + } + + public function testRunDispatchesAllEventsWithException() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo')); + $this->assertContains('before.foo.caught.after.', $tester->getDisplay()); + } + + public function testRunWithDispatcherSkippingCommand() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher(true)); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $exitCode = $tester->run(array('command' => 'foo')); + $this->assertContains('before.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); + } + + public function testRunWithDispatcherAccessingInputOptions() + { + $noInteractionValue = null; + $quietValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { + $input = $event->getInput(); + + $noInteractionValue = $input->getOption('no-interaction'); + $quietValue = $input->getOption('quiet'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--no-interaction' => true)); + + $this->assertTrue($noInteractionValue); + $this->assertFalse($quietValue); + } + + public function testRunWithDispatcherAddingInputOptions() + { + $extraValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { + $definition = $event->getCommand()->getDefinition(); + $input = $event->getInput(); + + $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); + $input->bind($definition); + + $extraValue = $input->getOption('extra'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'foo', '--extra' => 'some test value')); + + $this->assertEquals('some test value', $extraValue); + } + + public function testTerminalDimensions() + { + $application = new Application(); + $originalDimensions = $application->getTerminalDimensions(); + $this->assertCount(2, $originalDimensions); + + $width = 80; + if ($originalDimensions[0] == $width) { + $width = 100; + } + + $application->setTerminalDimensions($width, 80); + $this->assertSame(array($width, 80), $application->getTerminalDimensions()); + } + + protected function getDispatcher($skipCommand = false) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { + $event->getOutput()->write('before.'); + + if ($skipCommand) { + $event->disableCommand(); + } + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { + $event->getOutput()->writeln('after.'); + + if (!$skipCommand) { + $event->setExitCode(113); + } + }); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->write('caught.'); + + $event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException())); + }); + + return $dispatcher; + } + + public function testSetRunCustomDefaultCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run(array()); + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array()); + + $this->assertEquals('interact called'.PHP_EOL.'called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + /** + * @requires function posix_isatty + */ + public function testCanCheckIfTerminalIsInteractive() + { + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'help')); + + $this->assertFalse($tester->getInput()->hasParameterOption(array('--no-interaction', '-n'))); + + $inputStream = $application->getHelperSet()->get('question')->getInputStream(); + $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array(new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.'))); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array(new FormatterHelper())); + } +} + +class CustomDefaultCommandApplication extends Application +{ + /** + * Overwrites the constructor in order to set a different default command. + */ + public function __construct() + { + parent::__construct(); + + $command = new \FooCommand(); + $this->add($command); + $this->setDefaultCommand($command->getName()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/CommandTest.php new file mode 100644 index 0000000..c39adab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -0,0 +1,350 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. + */ + public function testCommandNameCannotBeEmpty() + { + new Command(); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition(array(new InputArgument('foo'), new InputOption('bar'))); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + } + + /** + * @dataProvider provideInvalidCommandNames + */ + public function testInvalidCommandNames($name) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); + + $command = new \TestCommand(); + $command->setName($name); + } + + public function provideInvalidCommandNames() + { + return array( + array(''), + array('foo:'), + ); + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + $command->setHelp(''); + $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + + $command = new \TestCommand(); + $command->setHelp(''); + $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(array('name'), $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(array('name1')); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(array('name1'), $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('bar'); + $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array(new InputArgument('bar'), new InputOption('foo')))); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments(array(new InputArgument('foo'))); + $application1->getDefinition()->addOptions(array(new InputOption('bar'))); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition(array())); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command, false); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); + $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); + + $m->invoke($command, true); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); + + $m->invoke($command); + $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); + } + + public function testRunInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => true)); + + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + } + + public function testRunNonInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute(array(), array('interactive' => false)); + + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must override the execute() method in the concrete command class. + */ + public function testExecuteMethodNeedsToBeOverridden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException Symfony\Component\Console\Exception\InvalidOptionException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testRunWithInvalidOption() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + $tester->execute(array('--bar' => true)); + } + + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMock('TestCommand', array('execute')); + $command->expects($this->once()) + ->method('execute') + ->will($this->returnValue('2.3')); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + + public function testRunWithApplication() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + + $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function getSetCodeBindToClosureTests() + { + return array( + array(true, 'not bound to the command'), + array(false, 'bound to the command'), + ); + } + + /** + * @dataProvider getSetCodeBindToClosureTests + */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode(array($this, 'callableMethodCommand')); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute(array()); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } +} + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function (InputInterface $input, OutputInterface $output) { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/HelpCommandTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/HelpCommandTest.php new file mode 100644 index 0000000..10bb324 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/HelpCommandTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Application; + +class HelpCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteForCommandAlias() + { + $command = new HelpCommand(); + $command->setApplication(new Application()); + $commandTester = new CommandTester($command); + $commandTester->execute(array('command_name' => 'li'), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + } + + public function testExecuteForCommand() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array(), array('decorated' => false)); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForCommandWithXmlOption() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(array('--format' => 'xml')); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } + + public function testExecuteForApplicationCommand() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list')); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForApplicationCommandWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(array('command_name' => 'list', '--format' => 'xml')); + $this->assertContains('list [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/ListCommandTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/ListCommandTest.php new file mode 100644 index 0000000..36d2380 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Command/ListCommandTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Console\Application; + +class ListCommandTest extends \PHPUnit_Framework_TestCase +{ + public function testExecuteListsCommands() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + + $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + } + + public function testExecuteListsCommandsWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--format' => 'xml')); + $this->assertRegExp('//', $commandTester->getDisplay(), '->execute() returns a list of available commands in XML if --xml is passed'); + } + + public function testExecuteListsCommandsWithRawOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsWithNamespaceArgument() + { + require_once realpath(__DIR__.'/../Fixtures/FooCommand.php'); + $application = new Application(); + $application->add(new \FooCommand()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), 'namespace' => 'foo', '--raw' => true)); + $output = <<assertEquals($output, $commandTester->getDisplay(true)); + } + + public function testExecuteListsCommandsOrder() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $output = <<assertEquals($output, trim($commandTester->getDisplay(true))); + } + + public function testExecuteListsCommandsOrderRaw() + { + require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php'); + $application = new Application(); + $application->add(new \Foo6Command()); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(array('command' => $command->getName(), '--raw' => true)); + $output = <<assertEquals($output, trim($commandTester->getDisplay(true))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 0000000..f582e7f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\BufferedOutput; + +abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getDescribeInputArgumentTestData */ + public function testDescribeInputArgument(InputArgument $argument, $expectedDescription) + { + $this->assertDescription($expectedDescription, $argument); + } + + /** @dataProvider getDescribeInputOptionTestData */ + public function testDescribeInputOption(InputOption $option, $expectedDescription) + { + $this->assertDescription($expectedDescription, $option); + } + + /** @dataProvider getDescribeInputDefinitionTestData */ + public function testDescribeInputDefinition(InputDefinition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + /** @dataProvider getDescribeCommandTestData */ + public function testDescribeCommand(Command $command, $expectedDescription) + { + $this->assertDescription($expectedDescription, $command); + } + + /** @dataProvider getDescribeApplicationTestData */ + public function testDescribeApplication(Application $application, $expectedDescription) + { + // Replaces the dynamic placeholders of the command help text with a static version. + // The placeholder %command.full_name% includes the script path that is not predictable + // and can not be tested against. + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + + $this->assertDescription($expectedDescription, $application); + } + + public function getDescribeInputArgumentTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputArguments()); + } + + public function getDescribeInputOptionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputOptions()); + } + + public function getDescribeInputDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getInputDefinitions()); + } + + public function getDescribeCommandTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCommands()); + } + + public function getDescribeApplicationTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getApplications()); + } + + abstract protected function getDescriptor(); + abstract protected function getFormat(); + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/JsonDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/JsonDescriptorTest.php new file mode 100644 index 0000000..f9a1561 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/JsonDescriptorTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Output\BufferedOutput; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } + + protected function assertDescription($expectedDescription, $describedObject) + { + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + $this->getDescriptor()->describe($output, $describedObject, array('raw_output' => true)); + $this->assertEquals(json_decode(trim($expectedDescription), true), json_decode(trim(str_replace(PHP_EOL, "\n", $output->fetch())), true)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 0000000..c85e8a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/MarkdownDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/ObjectsProvider.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/ObjectsProvider.php new file mode 100644 index 0000000..45b3b2f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/ObjectsProvider.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorApplication2; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand1; +use Symfony\Component\Console\Tests\Fixtures\DescriptorCommand2; + +/** + * @author Jean-François Simon + */ +class ObjectsProvider +{ + public static function getInputArguments() + { + return array( + 'input_argument_1' => new InputArgument('argument_name', InputArgument::REQUIRED), + 'input_argument_2' => new InputArgument('argument_name', InputArgument::IS_ARRAY, 'argument description'), + 'input_argument_3' => new InputArgument('argument_name', InputArgument::OPTIONAL, 'argument description', 'default_value'), + 'input_argument_4' => new InputArgument('argument_name', InputArgument::REQUIRED, "multiline\nargument description"), + ); + } + + public static function getInputOptions() + { + return array( + 'input_option_1' => new InputOption('option_name', 'o', InputOption::VALUE_NONE), + 'input_option_2' => new InputOption('option_name', 'o', InputOption::VALUE_OPTIONAL, 'option description', 'default_value'), + 'input_option_3' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, 'option description'), + 'input_option_4' => new InputOption('option_name', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'option description', array()), + 'input_option_5' => new InputOption('option_name', 'o', InputOption::VALUE_REQUIRED, "multiline\noption description"), + 'input_option_6' => new InputOption('option_name', array('o', 'O'), InputOption::VALUE_REQUIRED, 'option with multiple shortcuts'), + ); + } + + public static function getInputDefinitions() + { + return array( + 'input_definition_1' => new InputDefinition(), + 'input_definition_2' => new InputDefinition(array(new InputArgument('argument_name', InputArgument::REQUIRED))), + 'input_definition_3' => new InputDefinition(array(new InputOption('option_name', 'o', InputOption::VALUE_NONE))), + 'input_definition_4' => new InputDefinition(array( + new InputArgument('argument_name', InputArgument::REQUIRED), + new InputOption('option_name', 'o', InputOption::VALUE_NONE), + )), + ); + } + + public static function getCommands() + { + return array( + 'command_1' => new DescriptorCommand1(), + 'command_2' => new DescriptorCommand2(), + ); + } + + public static function getApplications() + { + return array( + 'application_1' => new DescriptorApplication1(), + 'application_2' => new DescriptorApplication2(), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/TextDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/TextDescriptorTest.php new file mode 100644 index 0000000..350b679 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/TextDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/XmlDescriptorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/XmlDescriptorTest.php new file mode 100644 index 0000000..59a5d1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Descriptor/XmlDescriptorTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Descriptor; + +use Symfony\Component\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/BarBucCommand.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/BarBucCommand.php new file mode 100644 index 0000000..52b619e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/BarBucCommand.php @@ -0,0 +1,11 @@ +setName('bar:buc'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication1.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication1.php new file mode 100644 index 0000000..132b6d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication1.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication1 extends Application +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication2.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication2.php new file mode 100644 index 0000000..ff55135 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorApplication2.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Application; + +class DescriptorApplication2 extends Application +{ + public function __construct() + { + parent::__construct('My Symfony application', 'v1.0'); + $this->add(new DescriptorCommand1()); + $this->add(new DescriptorCommand2()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand1.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand1.php new file mode 100644 index 0000000..ede05d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand1.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; + +class DescriptorCommand1 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command1') + ->setAliases(array('alias1', 'alias2')) + ->setDescription('command 1 description') + ->setHelp('command 1 help') + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand2.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand2.php new file mode 100644 index 0000000..51106b9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DescriptorCommand2.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class DescriptorCommand2 extends Command +{ + protected function configure() + { + $this + ->setName('descriptor:command2') + ->setDescription('command 2 description') + ->setHelp('command 2 help') + ->addUsage('-o|--option_name ') + ->addUsage('') + ->addArgument('argument_name', InputArgument::REQUIRED) + ->addOption('option_name', 'o', InputOption::VALUE_NONE) + ; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php new file mode 100644 index 0000000..0070c0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/DummyOutput.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Fixtures; + +use Symfony\Component\Console\Output\BufferedOutput; + +/** + * Dummy output. + * + * @author Kévin Dunglas + */ +class DummyOutput extends BufferedOutput +{ + /** + * @return array + */ + public function getLogs() + { + $logs = array(); + foreach (explode("\n", trim($this->fetch())) as $message) { + preg_match('/^\[(.*)\] (.*)/', $message, $matches); + $logs[] = sprintf('%s %s', $matches[1], $matches[2]); + } + + return $logs; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php new file mode 100644 index 0000000..254162f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar1') + ->setDescription('The foo:bar1 command') + ->setAliases(array('afoobar1')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php new file mode 100644 index 0000000..8071dc8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo2Command.php @@ -0,0 +1,21 @@ +setName('foo1:bar') + ->setDescription('The foo1:bar command') + ->setAliases(array('afoobar2')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php new file mode 100644 index 0000000..6c890fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo3Command.php @@ -0,0 +1,29 @@ +setName('foo3:bar') + ->setDescription('The foo3:bar command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + try { + throw new \Exception('First exception

    this is html

    '); + } catch (\Exception $e) { + throw new \Exception('Second exception comment', 0, $e); + } + } catch (\Exception $e) { + throw new \Exception('Third exception comment', 0, $e); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php new file mode 100644 index 0000000..1c54639 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo4Command.php @@ -0,0 +1,11 @@ +setName('foo3:bar:toh'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo5Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo5Command.php new file mode 100644 index 0000000..a1c6082 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Foo5Command.php @@ -0,0 +1,10 @@ +setName('0foo:bar')->setDescription('0foo:bar command'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooCommand.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooCommand.php new file mode 100644 index 0000000..355e0ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooCommand.php @@ -0,0 +1,33 @@ +setName('foo:bar') + ->setDescription('The foo:bar command') + ->setAliases(array('afoobar')) + ; + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $output->writeln('called'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooSubnamespaced1Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooSubnamespaced1Command.php new file mode 100644 index 0000000..fc50c72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooSubnamespaced1Command.php @@ -0,0 +1,26 @@ +setName('foo:bar:baz') + ->setDescription('The foo:bar:baz command') + ->setAliases(array('foobarbaz')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooSubnamespaced2Command.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooSubnamespaced2Command.php new file mode 100644 index 0000000..1cf31ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FooSubnamespaced2Command.php @@ -0,0 +1,26 @@ +setName('foo:go:bret') + ->setDescription('The foo:bar:go command') + ->setAliases(array('foobargo')) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FoobarCommand.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FoobarCommand.php new file mode 100644 index 0000000..9681628 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/FoobarCommand.php @@ -0,0 +1,25 @@ +setName('foobar:foo') + ->setDescription('The foobar:foo command') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php new file mode 100644 index 0000000..996fafb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php @@ -0,0 +1,11 @@ +caution('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php new file mode 100644 index 0000000..6634cd5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php @@ -0,0 +1,13 @@ +title('Title'); + $output->warning('Lorem ipsum dolor sit amet'); + $output->title('Title'); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php new file mode 100644 index 0000000..6004e3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php @@ -0,0 +1,16 @@ +warning('Warning'); + $output->caution('Caution'); + $output->error('Error'); + $output->success('Success'); + $output->note('Note'); + $output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php new file mode 100644 index 0000000..c7a08f1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php @@ -0,0 +1,12 @@ +title('First title'); + $output->title('Second title'); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php new file mode 100644 index 0000000..afea70c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php @@ -0,0 +1,34 @@ +write('Lorem ipsum dolor sit amet'); + $output->title('First title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->title('Second title'); + + $output->write('Lorem ipsum dolor sit amet'); + $output->write(''); + $output->title('Third title'); + + //Ensure edge case by appending empty strings to history: + $output->write('Lorem ipsum dolor sit amet'); + $output->write(array('', '', '')); + $output->title('Fourth title'); + + //Ensure have manual control over number of blank lines: + $output->writeln('Lorem ipsum dolor sit amet'); + $output->writeln(array('', '')); //Should append an extra blank line + $output->title('Fifth title'); + + $output->writeln('Lorem ipsum dolor sit amet'); + $output->newLine(2); //Should append an extra blank line + $output->title('Fifth title'); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php new file mode 100644 index 0000000..d2c68a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php @@ -0,0 +1,37 @@ +writeln('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + //Even using write: + $output->write('Lorem ipsum dolor sit amet'); + $output->listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->write('Lorem ipsum dolor sit amet'); + $output->text(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + + $output->newLine(); + + $output->write('Lorem ipsum dolor sit amet'); + $output->comment(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php new file mode 100644 index 0000000..f1d7990 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php @@ -0,0 +1,16 @@ +listing(array( + 'Lorem ipsum dolor sit amet', + 'consectetur adipiscing elit', + )); + $output->success('Lorem ipsum dolor sit amet'); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php new file mode 100644 index 0000000..cbfea73 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php @@ -0,0 +1,15 @@ +title('Title'); + $output->askHidden('Hidden question'); + $output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1'); + $output->confirm('Confirmation with yes default', true); + $output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse'); +}; diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt new file mode 100644 index 0000000..a42e0f7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_0.txt @@ -0,0 +1,3 @@ + + ! [CAUTION] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt new file mode 100644 index 0000000..334875f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_1.txt @@ -0,0 +1,9 @@ + +Title +===== + + [WARNING] Lorem ipsum dolor sit amet + +Title +===== + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt new file mode 100644 index 0000000..ca60976 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt @@ -0,0 +1,13 @@ + + [WARNING] Warning + + ! [CAUTION] Caution + + [ERROR] Error + + [OK] Success + + ! [NOTE] Note + +X [CUSTOM] Custom block + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt new file mode 100644 index 0000000..f4b6d58 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt @@ -0,0 +1,7 @@ + +First title +=========== + +Second title +============ + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt new file mode 100644 index 0000000..2646d85 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt @@ -0,0 +1,32 @@ +Lorem ipsum dolor sit amet + +First title +=========== + +Lorem ipsum dolor sit amet + +Second title +============ + +Lorem ipsum dolor sit amet + +Third title +=========== + +Lorem ipsum dolor sit amet + +Fourth title +============ + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + +Lorem ipsum dolor sit amet + + +Fifth title +=========== + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt new file mode 100644 index 0000000..738eb9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt @@ -0,0 +1,15 @@ +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + +Lorem ipsum dolor sit amet + Lorem ipsum dolor sit amet + consectetur adipiscing elit + +Lorem ipsum dolor sit amet + // Lorem ipsum dolor sit amet + // consectetur adipiscing elit diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt new file mode 100644 index 0000000..5f2d33c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt @@ -0,0 +1,6 @@ + + * Lorem ipsum dolor sit amet + * consectetur adipiscing elit + + [OK] Lorem ipsum dolor sit amet + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt new file mode 100644 index 0000000..ecea977 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt @@ -0,0 +1,5 @@ + +Title +===== + + Duis aute irure dolor in reprehenderit in voluptate velit esse diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/TestCommand.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/TestCommand.php new file mode 100644 index 0000000..dcd3273 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/TestCommand.php @@ -0,0 +1,28 @@ +setName('namespace:name') + ->setAliases(array('name')) + ->setDescription('description') + ->setHelp('help') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('execute called'); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $output->writeln('interact called'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.json new file mode 100644 index 0000000..b3ad97d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}}],"namespaces":[{"id":"_global","commands":["help","list"]}]} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.md new file mode 100644 index 0000000..f1d88c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.md @@ -0,0 +1,181 @@ +UNKNOWN +======= + +* help +* list + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.txt new file mode 100644 index 0000000..c4cf8f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml new file mode 100644 index 0000000..8514f23 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_1.xml @@ -0,0 +1,104 @@ + + + + + + help [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + list [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + + help + list + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.json new file mode 100644 index 0000000..e8870ba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.json @@ -0,0 +1 @@ +{"commands":[{"name":"help","usage":["help [--format FORMAT] [--raw] [--] []"],"description":"Displays help for a command","help":"The help<\/info> command displays help for a given command:\n\n php app\/console help list<\/info>\n\nYou can also output the help in other formats by using the --format<\/comment> option:\n\n php app\/console help --format=xml list<\/info>\n\nTo display the list of available commands, please use the list<\/info> command.","definition":{"arguments":{"command_name":{"name":"command_name","is_required":false,"is_array":false,"description":"The command name","default":"help"}},"options":{"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"},"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command help","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"list","usage":["list [--raw] [--format FORMAT] [--] []"],"description":"Lists commands","help":"The list<\/info> command lists all commands:\n\n php app\/console list<\/info>\n\nYou can also display the commands for a specific namespace:\n\n php app\/console list test<\/info>\n\nYou can also output the information in other formats by using the --format<\/comment> option:\n\n php app\/console list --format=xml<\/info>\n\nIt's also possible to get raw list of commands (useful for embedding command runner):\n\n php app\/console list --raw<\/info>","definition":{"arguments":{"namespace":{"name":"namespace","is_required":false,"is_array":false,"description":"The namespace name","default":null}},"options":{"raw":{"name":"--raw","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"To output raw command list","default":false},"format":{"name":"--format","shortcut":"","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"The output format (txt, xml, json, or md)","default":"txt"}}}},{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":{"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}},{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false},"help":{"name":"--help","shortcut":"-h","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this help message","default":false},"quiet":{"name":"--quiet","shortcut":"-q","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not output any message","default":false},"verbose":{"name":"--verbose","shortcut":"-v|-vv|-vvv","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug","default":false},"version":{"name":"--version","shortcut":"-V","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Display this application version","default":false},"ansi":{"name":"--ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Force ANSI output","default":false},"no-ansi":{"name":"--no-ansi","shortcut":"","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Disable ANSI output","default":false},"no-interaction":{"name":"--no-interaction","shortcut":"-n","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"Do not ask any interactive question","default":false}}}}],"namespaces":[{"id":"_global","commands":["alias1","alias2","help","list"]},{"id":"descriptor","commands":["descriptor:command1","descriptor:command2"]}]} \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.md new file mode 100644 index 0000000..63b2d9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.md @@ -0,0 +1,376 @@ +My Symfony application +====================== + +* alias1 +* alias2 +* help +* list + +**descriptor:** + +* descriptor:command1 +* descriptor:command2 + +help +---- + +* Description: Displays help for a command +* Usage: + + * `help [--format FORMAT] [--raw] [--] []` + +The help command displays help for a given command: + + php app/console help list + +You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + +To display the list of available commands, please use the list command. + +### Arguments: + +**command_name:** + +* Name: command_name +* Is required: no +* Is array: no +* Description: The command name +* Default: `'help'` + +### Options: + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command help +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +list +---- + +* Description: Lists commands +* Usage: + + * `list [--raw] [--format FORMAT] [--] []` + +The list command lists all commands: + + php app/console list + +You can also display the commands for a specific namespace: + + php app/console list test + +You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw + +### Arguments: + +**namespace:** + +* Name: namespace +* Is required: no +* Is array: no +* Description: The namespace name +* Default: `NULL` + +### Options: + +**raw:** + +* Name: `--raw` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: To output raw command list +* Default: `false` + +**format:** + +* Name: `--format` +* Shortcut: +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: The output format (txt, xml, json, or md) +* Default: `'txt'` + +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help + +### Options: + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` + +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` + +**help:** + +* Name: `--help` +* Shortcut: `-h` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this help message +* Default: `false` + +**quiet:** + +* Name: `--quiet` +* Shortcut: `-q` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not output any message +* Default: `false` + +**verbose:** + +* Name: `--verbose` +* Shortcut: `-v|-vv|-vvv` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +* Default: `false` + +**version:** + +* Name: `--version` +* Shortcut: `-V` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Display this application version +* Default: `false` + +**ansi:** + +* Name: `--ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Force ANSI output +* Default: `false` + +**no-ansi:** + +* Name: `--no-ansi` +* Shortcut: +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Disable ANSI output +* Default: `false` + +**no-interaction:** + +* Name: `--no-interaction` +* Shortcut: `-n` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: Do not ask any interactive question +* Default: `false` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.txt new file mode 100644 index 0000000..292aa82 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.txt @@ -0,0 +1,22 @@ +My Symfony application version v1.0 + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + alias1 command 1 description + alias2 command 1 description + help Displays help for a command + list Lists commands + descriptor + descriptor:command1 command 1 description + descriptor:command2 command 2 description diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml new file mode 100644 index 0000000..62e3cfc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_2.xml @@ -0,0 +1,184 @@ + + + + + + help [--format FORMAT] [--raw] [--] [<command_name>] + + Displays help for a command + The <info>help</info> command displays help for a given command: + + <info>php app/console help list</info> + + You can also output the help in other formats by using the <comment>--format</comment> option: + + <info>php app/console help --format=xml list</info> + + To display the list of available commands, please use the <info>list</info> command. + + + The command name + + help + + + + + + + + + + + + + + + + + + list [--raw] [--format FORMAT] [--] [<namespace>] + + Lists commands + The <info>list</info> command lists all commands: + + <info>php app/console list</info> + + You can also display the commands for a specific namespace: + + <info>php app/console list test</info> + + You can also output the information in other formats by using the <comment>--format</comment> option: + + <info>php app/console list --format=xml</info> + + It's also possible to get raw list of commands (useful for embedding command runner): + + <info>php app/console list --raw</info> + + + The namespace name + + + + + + + + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + + + + + + + + + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + + + + + + + + + + + + alias1 + alias2 + help + list + + + descriptor:command1 + descriptor:command2 + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt new file mode 100644 index 0000000..19dacb2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext1.txt @@ -0,0 +1,20 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + afoobar The foo:bar command + help Displays help for a command + list Lists commands + foo + foo:bar The foo:bar command diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt new file mode 100644 index 0000000..c99ccdd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_astext2.txt @@ -0,0 +1,16 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands for the "foo" namespace: + foo:bar The foo:bar command diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt new file mode 100644 index 0000000..0c16e3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_gethelp.txt @@ -0,0 +1 @@ +Console Tool \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt new file mode 100644 index 0000000..919cec4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception1.txt @@ -0,0 +1,6 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not defined. + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt new file mode 100644 index 0000000..6456171 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception2.txt @@ -0,0 +1,8 @@ + + + [Symfony\Component\Console\Exception\InvalidOptionException] + The "--foo" option does not exist. + + +list [--raw] [--format FORMAT] [--] [] + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt new file mode 100644 index 0000000..8276137 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3.txt @@ -0,0 +1,18 @@ + + + [Exception] + Third exception comment + + + + [Exception] + Second exception comment + + + + [Exception] + First exception

    this is html

    + + +foo3:bar + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt new file mode 100644 index 0000000..b4a7b01 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception3decorated.txt @@ -0,0 +1,18 @@ + +  + [Exception]  + Third exception comment  +  + +  + [Exception]  + Second exception comment  +  + +  + [Exception]  + First exception 

    this is html

      +  + +foo3:bar + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt new file mode 100644 index 0000000..cb080e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception4.txt @@ -0,0 +1,7 @@ + + + [Symfony\Component\Console\Exception\CommandNotFoundException] + Command "foo" is not define + d. + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1.txt new file mode 100644 index 0000000..1ba5f8f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1.txt @@ -0,0 +1,8 @@ + + + [Exception] + エラーメッセージ + + +foo + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt new file mode 100644 index 0000000..2064425 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt @@ -0,0 +1,8 @@ + +  + [Exception]  + エラーメッセージ  +  + +foo + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth2.txt new file mode 100644 index 0000000..e41fcfc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth2.txt @@ -0,0 +1,9 @@ + + + [Exception] + コマンドの実行中にエラーが + 発生しました。 + + +foo + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run1.txt new file mode 100644 index 0000000..0dc2730 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run1.txt @@ -0,0 +1,17 @@ +Console Tool + +Usage: + command [options] [arguments] + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Available commands: + help Displays help for a command + list Lists commands diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run2.txt new file mode 100644 index 0000000..0098e9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run2.txt @@ -0,0 +1,28 @@ +Usage: + help [options] [--] [] + +Arguments: + command The command to execute + command_name The command name [default: "help"] + +Options: + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + --raw To output raw command help + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + The help command displays help for a given command: + + php app/console help list + + You can also output the help in other formats by using the --format option: + + php app/console help --format=xml list + + To display the list of available commands, please use the list command. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run3.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run3.txt new file mode 100644 index 0000000..fe566d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run3.txt @@ -0,0 +1,26 @@ +Usage: + list [options] [--] [] + +Arguments: + namespace The namespace name + +Options: + --raw To output raw command list + --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] + +Help: + The list command lists all commands: + + php app/console list + + You can also display the commands for a specific namespace: + + php app/console list test + + You can also output the information in other formats by using the --format option: + + php app/console list --format=xml + + It's also possible to get raw list of commands (useful for embedding command runner): + + php app/console list --raw diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run4.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run4.txt new file mode 100644 index 0000000..47187fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/application_run4.txt @@ -0,0 +1 @@ +Console Tool diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.json new file mode 100644 index 0000000..20f310b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.json @@ -0,0 +1 @@ +{"name":"descriptor:command1","usage":["descriptor:command1", "alias1", "alias2"],"description":"command 1 description","help":"command 1 help","definition":{"arguments":[],"options":[]}} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.md new file mode 100644 index 0000000..34ed3ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.md @@ -0,0 +1,11 @@ +descriptor:command1 +------------------- + +* Description: command 1 description +* Usage: + + * `descriptor:command1` + * `alias1` + * `alias2` + +command 1 help diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.txt new file mode 100644 index 0000000..28e14a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.txt @@ -0,0 +1,7 @@ +Usage: + descriptor:command1 + alias1 + alias2 + +Help: + command 1 help diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.xml new file mode 100644 index 0000000..838b9bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_1.xml @@ -0,0 +1,12 @@ + + + + descriptor:command1 + alias1 + alias2 + + command 1 description + command 1 help + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.json new file mode 100644 index 0000000..38edd1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.json @@ -0,0 +1 @@ +{"name":"descriptor:command2","usage":["descriptor:command2 [-o|--option_name] [--] ", "descriptor:command2 -o|--option_name ", "descriptor:command2 "],"description":"command 2 description","help":"command 2 help","definition":{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}}} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.md new file mode 100644 index 0000000..6f538b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.md @@ -0,0 +1,33 @@ +descriptor:command2 +------------------- + +* Description: command 2 description +* Usage: + + * `descriptor:command2 [-o|--option_name] [--] ` + * `descriptor:command2 -o|--option_name ` + * `descriptor:command2 ` + +command 2 help + +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt new file mode 100644 index 0000000..72f7ce0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.txt @@ -0,0 +1,13 @@ +Usage: + descriptor:command2 [options] [--] + descriptor:command2 -o|--option_name + descriptor:command2 + +Arguments: + argument_name + +Options: + -o, --option_name + +Help: + command 2 help diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.xml new file mode 100644 index 0000000..67364ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_2.xml @@ -0,0 +1,21 @@ + + + + descriptor:command2 [-o|--option_name] [--] <argument_name> + descriptor:command2 -o|--option_name <argument_name> + descriptor:command2 <argument_name> + + command 2 description + command 2 help + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_astext.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_astext.txt new file mode 100644 index 0000000..7e20638 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_astext.txt @@ -0,0 +1,18 @@ +Usage: + namespace:name + name + +Arguments: + command The command to execute + +Options: + -h, --help Display this help message + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi Force ANSI output + --no-ansi Disable ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +Help: + help diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt new file mode 100644 index 0000000..5e77623 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/command_asxml.txt @@ -0,0 +1,38 @@ + + + + namespace:name + name + + description + help + + + The command to execute + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt new file mode 100644 index 0000000..0431c07 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/definition_astext.txt @@ -0,0 +1,11 @@ +Arguments: + foo The foo argument + baz The baz argument [default: true] + bar The bar argument [default: ["http://foo.com/"]] + +Options: + -f, --foo=FOO The foo option + --baz[=BAZ] The baz option [default: false] + -b, --bar[=BAR] The bar option [default: "bar"] + --qux[=QUX] The qux option [default: ["http://foo.com/","bar"]] (multiple values allowed) + --qux2[=QUX2] The qux2 option [default: {"foo":"bar"}] (multiple values allowed) \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt new file mode 100644 index 0000000..eec8c07 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/definition_asxml.txt @@ -0,0 +1,39 @@ + + + + + The foo argument + + + + The baz argument + + true + + + + The bar argument + + bar + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.json new file mode 100644 index 0000000..b8173b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.md new file mode 100644 index 0000000..88f311a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.txt new file mode 100644 index 0000000..5503518 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.txt @@ -0,0 +1 @@ + argument_name diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.xml new file mode 100644 index 0000000..cb37f81 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.json new file mode 100644 index 0000000..ef06b09 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":true,"description":"argument description","default":[]} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.md new file mode 100644 index 0000000..3cdb00c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: yes +* Description: argument description +* Default: `array ()` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.txt new file mode 100644 index 0000000..e713660 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.txt @@ -0,0 +1 @@ + argument_name argument description diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.xml new file mode 100644 index 0000000..629da5a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_2.xml @@ -0,0 +1,5 @@ + + + argument description + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.json new file mode 100644 index 0000000..de8484e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":false,"is_array":false,"description":"argument description","default":"default_value"} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.md new file mode 100644 index 0000000..be1c443 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.md @@ -0,0 +1,7 @@ +**argument_name:** + +* Name: argument_name +* Is required: no +* Is array: no +* Description: argument description +* Default: `'default_value'` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.txt new file mode 100644 index 0000000..6b76639 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.txt @@ -0,0 +1 @@ + argument_name argument description [default: "default_value"] diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.xml new file mode 100644 index 0000000..399a5c8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_3.xml @@ -0,0 +1,7 @@ + + + argument description + + default_value + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.json new file mode 100644 index 0000000..8067a4d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.json @@ -0,0 +1 @@ +{"name":"argument_name","is_required":true,"is_array":false,"description":"multiline argument description","default":null} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.md new file mode 100644 index 0000000..f026ab3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.md @@ -0,0 +1,8 @@ +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: multiline + argument description +* Default: `NULL` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.txt new file mode 100644 index 0000000..aa74e8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.txt @@ -0,0 +1,2 @@ + argument_name multiline + argument description diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.xml new file mode 100644 index 0000000..5ca135e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_argument_4.xml @@ -0,0 +1,6 @@ + + + multiline +argument description + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.json new file mode 100644 index 0000000..c7a7d83 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.json @@ -0,0 +1 @@ +{"arguments":[],"options":[]} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.md new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.xml new file mode 100644 index 0000000..b5481ce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.json new file mode 100644 index 0000000..9964a55 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":[]} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.md new file mode 100644 index 0000000..923191c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.md @@ -0,0 +1,9 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.txt new file mode 100644 index 0000000..73b0f30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.txt @@ -0,0 +1,2 @@ +Arguments: + argument_name diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.xml new file mode 100644 index 0000000..102efc1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.json new file mode 100644 index 0000000..6a86056 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.json @@ -0,0 +1 @@ +{"arguments":[],"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.md new file mode 100644 index 0000000..40fd7b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.md @@ -0,0 +1,11 @@ +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.txt new file mode 100644 index 0000000..c02766f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.txt @@ -0,0 +1,2 @@ +Options: + -o, --option_name diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.xml new file mode 100644 index 0000000..bc95151 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_3.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.json new file mode 100644 index 0000000..c5a0019 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.json @@ -0,0 +1 @@ +{"arguments":{"argument_name":{"name":"argument_name","is_required":true,"is_array":false,"description":"","default":null}},"options":{"option_name":{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false}}} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.md new file mode 100644 index 0000000..a31feea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.md @@ -0,0 +1,21 @@ +### Arguments: + +**argument_name:** + +* Name: argument_name +* Is required: yes +* Is array: no +* Description: +* Default: `NULL` + +### Options: + +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.txt new file mode 100644 index 0000000..63aa81d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.txt @@ -0,0 +1,5 @@ +Arguments: + argument_name + +Options: + -o, --option_name diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.xml new file mode 100644 index 0000000..cffceec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_definition_4.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.json new file mode 100644 index 0000000..60c5b56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":false,"is_value_required":false,"is_multiple":false,"description":"","default":false} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.md new file mode 100644 index 0000000..6f9e9a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: no +* Is value required: no +* Is multiple: no +* Description: +* Default: `false` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.txt new file mode 100644 index 0000000..3a5e4ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.txt @@ -0,0 +1 @@ + -o, --option_name diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.xml new file mode 100644 index 0000000..8a64ea6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_1.xml @@ -0,0 +1,4 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.json new file mode 100644 index 0000000..04e4228 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":false,"description":"option description","default":"default_value"} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.md new file mode 100644 index 0000000..634ac0b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: no +* Description: option description +* Default: `'default_value'` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.txt new file mode 100644 index 0000000..1009eff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description [default: "default_value"] diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.xml new file mode 100644 index 0000000..4afac5b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_2.xml @@ -0,0 +1,7 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.json new file mode 100644 index 0000000..c1ea120 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option description","default":null} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.md new file mode 100644 index 0000000..3428289 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option description +* Default: `NULL` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.txt new file mode 100644 index 0000000..947bb65 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.txt @@ -0,0 +1 @@ + -o, --option_name=OPTION_NAME option description diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.xml new file mode 100644 index 0000000..dcc0631 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_3.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.json new file mode 100644 index 0000000..1b671d8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":false,"is_multiple":true,"description":"option description","default":[]} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.md new file mode 100644 index 0000000..8ffba56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: no +* Is multiple: yes +* Description: option description +* Default: `array ()` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.txt new file mode 100644 index 0000000..27edf77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.txt @@ -0,0 +1 @@ + -o, --option_name[=OPTION_NAME] option description (multiple values allowed) diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.xml new file mode 100644 index 0000000..5e2418b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_4.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.json new file mode 100644 index 0000000..35a1405 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"multiline option description","default":null} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.md new file mode 100644 index 0000000..82f51ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.md @@ -0,0 +1,10 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: multiline + option description +* Default: `NULL` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.txt new file mode 100644 index 0000000..4368883 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.txt @@ -0,0 +1,2 @@ + -o, --option_name=OPTION_NAME multiline + option description diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.xml new file mode 100644 index 0000000..90040cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_5.xml @@ -0,0 +1,6 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.json b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.json new file mode 100644 index 0000000..d84e872 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.json @@ -0,0 +1 @@ +{"name":"--option_name","shortcut":"-o|-O","accept_value":true,"is_value_required":true,"is_multiple":false,"description":"option with multiple shortcuts","default":null} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.md b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.md new file mode 100644 index 0000000..ed1ea1c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.md @@ -0,0 +1,9 @@ +**option_name:** + +* Name: `--option_name` +* Shortcut: `-o|-O` +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Description: option with multiple shortcuts +* Default: `NULL` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.txt b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.txt new file mode 100644 index 0000000..0e6c975 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.txt @@ -0,0 +1 @@ + -o|O, --option_name=OPTION_NAME option with multiple shortcuts diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.xml b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.xml new file mode 100644 index 0000000..06126a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Fixtures/input_option_6.xml @@ -0,0 +1,5 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 0000000..774df26 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleStackTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->getCurrent()); + + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s3, $stack->getCurrent()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + + $this->assertEquals($s2, $stack->pop()); + $this->assertEquals($s1, $stack->pop()); + } + + public function testPopEmpty() + { + $stack = new OutputFormatterStyleStack(); + $style = new OutputFormatterStyle(); + + $this->assertEquals($style, $stack->pop()); + } + + public function testPopNotLast() + { + $stack = new OutputFormatterStyleStack(); + $stack->push($s1 = new OutputFormatterStyle('white', 'black')); + $stack->push($s2 = new OutputFormatterStyle('yellow', 'blue')); + $stack->push($s3 = new OutputFormatterStyle('green', 'red')); + + $this->assertEquals($s2, $stack->pop($s2)); + $this->assertEquals($s1, $stack->pop()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->push(new OutputFormatterStyle('white', 'black')); + $stack->pop(new OutputFormatterStyle('yellow', 'blue')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.php new file mode 100644 index 0000000..0abfb3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterStyleTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $style = new OutputFormatterStyle('green', 'black', array('bold', 'underscore')); + $this->assertEquals("\033[32;40;1;4mfoo\033[39;49;22;24m", $style->apply('foo')); + + $style = new OutputFormatterStyle('red', null, array('blink')); + $this->assertEquals("\033[31;5mfoo\033[39;25m", $style->apply('foo')); + + $style = new OutputFormatterStyle(null, 'white'); + $this->assertEquals("\033[47mfoo\033[49m", $style->apply('foo')); + } + + public function testForeground() + { + $style = new OutputFormatterStyle(); + + $style->setForeground('black'); + $this->assertEquals("\033[30mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('blue'); + $this->assertEquals("\033[34mfoo\033[39m", $style->apply('foo')); + + $style->setForeground('default'); + $this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setForeground('undefined-color'); + } + + public function testBackground() + { + $style = new OutputFormatterStyle(); + + $style->setBackground('black'); + $this->assertEquals("\033[40mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('yellow'); + $this->assertEquals("\033[43mfoo\033[49m", $style->apply('foo')); + + $style->setBackground('default'); + $this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo')); + + $this->setExpectedException('InvalidArgumentException'); + $style->setBackground('undefined-color'); + } + + public function testOptions() + { + $style = new OutputFormatterStyle(); + + $style->setOptions(array('reverse', 'conceal')); + $this->assertEquals("\033[7;8mfoo\033[27;28m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[7;8;1mfoo\033[27;28;22m", $style->apply('foo')); + + $style->unsetOption('reverse'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOption('bold'); + $this->assertEquals("\033[8;1mfoo\033[28;22m", $style->apply('foo')); + + $style->setOptions(array('bold')); + $this->assertEquals("\033[1mfoo\033[22m", $style->apply('foo')); + + try { + $style->setOption('foo'); + $this->fail('->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->setOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + + try { + $style->unsetOption('foo'); + $this->fail('->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + $this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php new file mode 100644 index 0000000..510a4e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function testEmptyTag() + { + $formatter = new OutputFormatter(true); + $this->assertEquals('foo<>bar', $formatter->format('foo<>bar')); + } + + public function testLGCharEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals('fooformat('foo\\assertEquals('some info', $formatter->format('\\some info\\')); + $this->assertEquals('\\some info\\', OutputFormatter::escape('some info')); + + $this->assertEquals( + "\033[33mSymfony\\Component\\Console does work very well!\033[39m", + $formatter->format('Symfony\Component\Console does work very well!') + ); + } + + public function testBundledStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", + $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", + $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", + $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", + $formatter->format('some question') + ); + } + + public function testNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome \033[39;49m\033[32msome info\033[39m\033[37;41m error\033[39;49m", + $formatter->format('some some info error') + ); + } + + public function testAdjacentStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m\033[32msome info\033[39m", + $formatter->format('some errorsome info') + ); + } + + public function testStyleMatchingNotGreedy() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32m>=2.0,<2.3\033[39m)", + $formatter->format('(>=2.0,<2.3)') + ); + } + + public function testStyleEscaping() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "(\033[32mz>=2.0,format('('.$formatter->escape('z>=2.0,)') + ); + + $this->assertEquals( + "\033[32msome error\033[39m", + $formatter->format(''.$formatter->escape('some error').'') + ); + } + + public function testDeepNestedStyles() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals( + "\033[37;41merror\033[39;49m\033[32minfo\033[39m\033[33mcomment\033[39m\033[37;41merror\033[39;49m", + $formatter->format('errorinfocommenterror') + ); + } + + public function testNewStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('test', $style); + + $this->assertEquals($style, $formatter->getStyle('test')); + $this->assertNotEquals($style, $formatter->getStyle('info')); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('b', $style); + + $this->assertEquals("\033[34;47msome \033[39;49m\033[34;47mcustom\033[39;49m\033[34;47m msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testRedefineStyle() + { + $formatter = new OutputFormatter(true); + + $style = new OutputFormatterStyle('blue', 'white'); + $formatter->setStyle('info', $style); + + $this->assertEquals("\033[34;47msome custom msg\033[39;49m", $formatter->format('some custom msg')); + } + + public function testInlineStyle() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + $this->assertEquals("\033[34;41msome text\033[39;49m", $formatter->format('some text')); + } + + public function testNonStyleTag() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals("\033[32msome \033[39m\033[32m\033[39m\033[32m \033[39m\033[32m\033[39m\033[32m styled \033[39m\033[32m

    \033[39m\033[32msingle-char tag\033[39m\033[32m

    \033[39m", $formatter->format('some styled

    single-char tag

    ')); + } + + public function testFormatLongString() + { + $formatter = new OutputFormatter(true); + $long = str_repeat('\\', 14000); + $this->assertEquals("\033[37;41msome error\033[39;49m".$long, $formatter->format('some error'.$long)); + } + + public function testFormatToStringObject() + { + $formatter = new OutputFormatter(false); + $this->assertEquals( + 'some info', $formatter->format(new TableCell()) + ); + } + + public function testNotDecoratedFormatter() + { + $formatter = new OutputFormatter(false); + + $this->assertTrue($formatter->hasStyle('error')); + $this->assertTrue($formatter->hasStyle('info')); + $this->assertTrue($formatter->hasStyle('comment')); + $this->assertTrue($formatter->hasStyle('question')); + + $this->assertEquals( + 'some error', $formatter->format('some error') + ); + $this->assertEquals( + 'some info', $formatter->format('some info') + ); + $this->assertEquals( + 'some comment', $formatter->format('some comment') + ); + $this->assertEquals( + 'some question', $formatter->format('some question') + ); + + $formatter->setDecorated(true); + + $this->assertEquals( + "\033[37;41msome error\033[39;49m", $formatter->format('some error') + ); + $this->assertEquals( + "\033[32msome info\033[39m", $formatter->format('some info') + ); + $this->assertEquals( + "\033[33msome comment\033[39m", $formatter->format('some comment') + ); + $this->assertEquals( + "\033[30;46msome question\033[39;49m", $formatter->format('some question') + ); + } + + public function testContentWithLineBreaks() + { + $formatter = new OutputFormatter(true); + + $this->assertEquals(<<format(<< +some text
    +EOF + )); + + $this->assertEquals(<<format(<<some text +
    +EOF + )); + + $this->assertEquals(<<format(<< +some text +
    +EOF + )); + + $this->assertEquals(<<format(<< +some text +more text +
    +EOF + )); + } +} + +class TableCell +{ + public function __toString() + { + return 'some info'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php new file mode 100644 index 0000000..e0aa921 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\FormatterHelper; + +class FormatterHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testFormatSection() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + '[cli] Some text to display', + $formatter->formatSection('cli', 'Some text to display'), + '::formatSection() formats a message in a section' + ); + } + + public function testFormatBlock() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' Some text to display ', + $formatter->formatBlock('Some text to display', 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' Some text to display '."\n". + ' foo bar ', + $formatter->formatBlock(array('Some text to display', 'foo bar'), 'error'), + '::formatBlock() formats a message in a block' + ); + + $this->assertEquals( + ' '."\n". + ' Some text to display '."\n". + ' ', + $formatter->formatBlock('Some text to display', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDiacriticLetters() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' Du texte à afficher '."\n". + ' ', + $formatter->formatBlock('Du texte à afficher', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockWithDoubleWidthDiacriticLetters() + { + $formatter = new FormatterHelper(); + $this->assertEquals( + ' '."\n". + ' 表示するテキスト '."\n". + ' ', + $formatter->formatBlock('表示するテキスト', 'error', true), + '::formatBlock() formats a message in a block' + ); + } + + public function testFormatBlockLGEscaping() + { + $formatter = new FormatterHelper(); + + $this->assertEquals( + ' '."\n". + ' \some info\ '."\n". + ' ', + $formatter->formatBlock('some info', 'error', true), + '::formatBlock() escapes \'<\' chars' + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/HelperSetTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/HelperSetTest.php new file mode 100644 index 0000000..04edd30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/HelperSetTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Command\Command; + +class HelperSetTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $mock_helper = $this->getGenericMockHelper('fake_helper'); + $helperset = new HelperSet(array('fake_helper_alias' => $mock_helper)); + + $this->assertEquals($mock_helper, $helperset->get('fake_helper_alias'), '__construct sets given helper to helpers'); + $this->assertTrue($helperset->has('fake_helper_alias'), '__construct sets helper alias for given helper'); + } + + public function testSet() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset)); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper to helpers'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + $this->assertTrue($helperset->has('fake_helper_01'), '->set() will set multiple helpers on consecutive calls'); + $this->assertTrue($helperset->has('fake_helper_02'), '->set() will set multiple helpers on consecutive calls'); + + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper', $helperset), 'fake_helper_alias'); + $this->assertTrue($helperset->has('fake_helper'), '->set() adds helper alias when set'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->set() adds helper alias when set'); + } + + public function testHas() + { + $helperset = new HelperSet(array('fake_helper_alias' => $this->getGenericMockHelper('fake_helper'))); + $this->assertTrue($helperset->has('fake_helper'), '->has() finds set helper'); + $this->assertTrue($helperset->has('fake_helper_alias'), '->has() finds set helper by alias'); + } + + public function testGet() + { + $helper_01 = $this->getGenericMockHelper('fake_helper_01'); + $helper_02 = $this->getGenericMockHelper('fake_helper_02'); + $helperset = new HelperSet(array('fake_helper_01_alias' => $helper_01, 'fake_helper_02_alias' => $helper_02)); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01'), '->get() returns correct helper by name'); + $this->assertEquals($helper_01, $helperset->get('fake_helper_01_alias'), '->get() returns correct helper by alias'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02'), '->get() returns correct helper by name'); + $this->assertEquals($helper_02, $helperset->get('fake_helper_02_alias'), '->get() returns correct helper by alias'); + + $helperset = new HelperSet(); + try { + $helperset->get('foo'); + $this->fail('->get() throws InvalidArgumentException when helper not found'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws InvalidArgumentException when helper not found'); + $this->assertInstanceOf('Symfony\Component\Console\Exception\ExceptionInterface', $e, '->get() throws domain specific exception when helper not found'); + $this->assertContains('The helper "foo" is not defined.', $e->getMessage(), '->get() throws InvalidArgumentException when helper not found'); + } + } + + public function testSetCommand() + { + $cmd_01 = new Command('foo'); + $cmd_02 = new Command('bar'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $this->assertEquals($cmd_01, $helperset->getCommand(), '->setCommand() stores given command'); + + $helperset = new HelperSet(); + $helperset->setCommand($cmd_01); + $helperset->setCommand($cmd_02); + $this->assertEquals($cmd_02, $helperset->getCommand(), '->setCommand() overwrites stored command with consecutive calls'); + } + + public function testGetCommand() + { + $cmd = new Command('foo'); + $helperset = new HelperSet(); + $helperset->setCommand($cmd); + $this->assertEquals($cmd, $helperset->getCommand(), '->getCommand() retrieves stored command'); + } + + public function testIteration() + { + $helperset = new HelperSet(); + $helperset->set($this->getGenericMockHelper('fake_helper_01', $helperset)); + $helperset->set($this->getGenericMockHelper('fake_helper_02', $helperset)); + + $helpers = array('fake_helper_01', 'fake_helper_02'); + $i = 0; + + foreach ($helperset as $helper) { + $this->assertEquals($helpers[$i++], $helper->getName()); + } + } + + /** + * Create a generic mock for the helper interface. Optionally check for a call to setHelperSet with a specific + * helperset instance. + * + * @param string $name + * @param HelperSet $helperset allows a mock to verify a particular helperset set is being added to the Helper + */ + private function getGenericMockHelper($name, HelperSet $helperset = null) + { + $mock_helper = $this->getMock('\Symfony\Component\Console\Helper\HelperInterface'); + $mock_helper->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + if ($helperset) { + $mock_helper->expects($this->any()) + ->method('setHelperSet') + ->with($this->equalTo($helperset)); + } + + return $mock_helper; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProcessHelperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProcessHelperTest.php new file mode 100644 index 0000000..a51fb43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProcessHelperTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Process\Process; + +class ProcessHelperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideCommandsAndOutput + */ + public function testVariousProcessRuns($expected, $cmd, $verbosity, $error) + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream($verbosity); + $helper->run($output, $cmd, $error); + $this->assertEquals($expected, $this->getOutput($output)); + } + + public function testPassedCallbackIsExecuted() + { + $helper = new ProcessHelper(); + $helper->setHelperSet(new HelperSet(array(new DebugFormatterHelper()))); + $output = $this->getOutputStream(StreamOutput::VERBOSITY_NORMAL); + + $executed = false; + $callback = function () use (&$executed) { $executed = true; }; + + $helper->run($output, 'php -r "echo 42;"', null, $callback); + $this->assertTrue($executed); + } + + public function provideCommandsAndOutput() + { + $successOutputVerbose = <<42
    ';" + OUT 42 + RES Command ran successfully + +EOT; + $successOutputProcessDebug = <<42
    \';"', StreamOutput::VERBOSITY_DEBUG, null), + array('', 'php -r "syntax error"', StreamOutput::VERBOSITY_VERBOSE, null), + array($syntaxErrorOutputVerbose, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, null), + array($syntaxErrorOutputDebug, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, null), + array($errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERBOSE, $errorMessage), + array($syntaxErrorOutputVerbose.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(50000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_VERY_VERBOSE, $errorMessage), + array($syntaxErrorOutputDebug.$errorMessage.PHP_EOL, 'php -r "fwrite(STDERR, \'error message\');usleep(500000);fwrite(STDOUT, \'out message\');exit(252);"', StreamOutput::VERBOSITY_DEBUG, $errorMessage), + array($successOutputProcessDebug, array('php', '-r', 'echo 42;'), StreamOutput::VERBOSITY_DEBUG, null), + array($successOutputDebug, new Process('php -r "echo 42;"'), StreamOutput::VERBOSITY_DEBUG, null), + ); + } + + private function getOutputStream($verbosity) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, false); + } + + private function getOutput(StreamOutput $output) + { + rewind($output->getStream()); + + return stream_get_contents($output->getStream()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php new file mode 100644 index 0000000..4b25d1b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @group time-sensitive + */ +class ProgressBarTest extends \PHPUnit_Framework_TestCase +{ + public function testMultipleStart() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + $bar->start(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'). + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvance() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 1 [->--------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceWithStep() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(5); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceMultipleTimes() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->advance(3); + $bar->advance(2); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [--->------------------------]'). + $this->generateOutput(' 5 [----->----------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testAdvanceOverMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setProgress(9); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 9/10 [=========================>--] 90%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 11/11 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testFormat() + { + $expected = + $this->generateOutput(' 0/10 [>---------------------------] 0%'). + $this->generateOutput(' 10/10 [============================] 100%'). + $this->generateOutput(' 10/10 [============================] 100%') + ; + + // max in construct, no format + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, no format + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in construct, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + + // max in start, explicit format before + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(10); + $bar->advance(10); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals($expected, stream_get_contents($output->getStream())); + } + + public function testCustomizations() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setBarWidth(10); + $bar->setBarCharacter('_'); + $bar->setEmptyBarCharacter(' '); + $bar->setProgressCharacter('/'); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [/ ] 0%'). + $this->generateOutput(' 1/10 [_/ ] 10%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + public function testDisplayWithQuietVerbosity() + { + $bar = new ProgressBar($output = $this->getOutputStream(true, StreamOutput::VERBOSITY_QUIET), 50); + $bar->display(); + + rewind($output->getStream()); + $this->assertEquals( + '', + stream_get_contents($output->getStream()) + ); + } + + public function testFinishWithoutStart() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 50/50 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testPercent() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] 4%'), + stream_get_contents($output->getStream()) + ); + } + + public function testOverwriteWithShorterLine() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $bar->start(); + $bar->display(); + $bar->advance(); + + // set shorter format + $bar->setFormat(' %current%/%max% [%bar%]'); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 2/50 [=>--------------------------] '), + stream_get_contents($output->getStream()) + ); + } + + public function testStartWithMax() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%]'); + $bar->start(50); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------]'). + $this->generateOutput(' 1/50 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testSetCurrentProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->display(); + $bar->advance(); + $bar->setProgress(15); + $bar->setProgress(25); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 1/50 [>---------------------------] 2%'). + $this->generateOutput(' 15/50 [========>-------------------] 30%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'), + stream_get_contents($output->getStream()) + ); + } + + /** + */ + public function testSetCurrentBeforeStarting() + { + $bar = new ProgressBar($this->getOutputStream()); + $bar->setProgress(15); + $this->assertNotNull($bar->getStartTime()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can't regress the progress bar + */ + public function testRegressProgress() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(15); + $bar->setProgress(10); + } + + public function testRedrawFrequency() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream(), 6)); + $bar->expects($this->exactly(4))->method('display'); + + $bar->setRedrawFrequency(2); + $bar->start(); + $bar->setProgress(1); + $bar->advance(2); + $bar->advance(2); + $bar->advance(1); + } + + public function testRedrawFrequencyIsAtLeastOneIfZeroGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0); + $bar->start(); + $bar->advance(); + } + + public function testRedrawFrequencyIsAtLeastOneIfSmallerOneGiven() + { + $bar = $this->getMock('Symfony\Component\Console\Helper\ProgressBar', array('display'), array($this->getOutputStream())); + + $bar->expects($this->exactly(2))->method('display'); + $bar->setRedrawFrequency(0.9); + $bar->start(); + $bar->advance(); + } + + public function testMultiByteSupport() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->start(); + $bar->setBarCharacter('■'); + $bar->advance(3); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'). + $this->generateOutput(' 3 [■■■>------------------------]'), + stream_get_contents($output->getStream()) + ); + } + + public function testClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/50 [>---------------------------] 0%'). + $this->generateOutput(' 25/50 [==============>-------------] 50%'). + $this->generateOutput(' '), + stream_get_contents($output->getStream()) + ); + } + + public function testPercentNotHundredBeforeComplete() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 200); + $bar->start(); + $bar->display(); + $bar->advance(199); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 0/200 [>---------------------------] 0%'). + $this->generateOutput(' 199/200 [===========================>] 99%'). + $this->generateOutput(' 200/200 [============================] 100%'), + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 200); + $bar->start(); + + for ($i = 0; $i < 200; ++$i) { + $bar->advance(); + } + + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/200 [>---------------------------] 0%'.PHP_EOL. + ' 20/200 [==>-------------------------] 10%'.PHP_EOL. + ' 40/200 [=====>----------------------] 20%'.PHP_EOL. + ' 60/200 [========>-------------------] 30%'.PHP_EOL. + ' 80/200 [===========>----------------] 40%'.PHP_EOL. + ' 100/200 [==============>-------------] 50%'.PHP_EOL. + ' 120/200 [================>-----------] 60%'.PHP_EOL. + ' 140/200 [===================>--------] 70%'.PHP_EOL. + ' 160/200 [======================>-----] 80%'.PHP_EOL. + ' 180/200 [=========================>--] 90%'.PHP_EOL. + ' 200/200 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithClear() + { + $bar = new ProgressBar($output = $this->getOutputStream(false), 50); + $bar->start(); + $bar->setProgress(25); + $bar->clear(); + $bar->setProgress(50); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/50 [>---------------------------] 0%'.PHP_EOL. + ' 25/50 [==============>-------------] 50%'.PHP_EOL. + ' 50/50 [============================] 100%', + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutputWithoutMax() + { + $bar = new ProgressBar($output = $this->getOutputStream(false)); + $bar->start(); + $bar->advance(); + + rewind($output->getStream()); + $this->assertEquals( + ' 0 [>---------------------------]'.PHP_EOL. + ' 1 [->--------------------------]', + stream_get_contents($output->getStream()) + ); + } + + public function testParallelBars() + { + $output = $this->getOutputStream(); + $bar1 = new ProgressBar($output, 2); + $bar2 = new ProgressBar($output, 3); + $bar2->setProgressCharacter('#'); + $bar3 = new ProgressBar($output); + + $bar1->start(); + $output->write("\n"); + $bar2->start(); + $output->write("\n"); + $bar3->start(); + + for ($i = 1; $i <= 3; ++$i) { + // up two lines + $output->write("\033[2A"); + if ($i <= 2) { + $bar1->advance(); + } + $output->write("\n"); + $bar2->advance(); + $output->write("\n"); + $bar3->advance(); + } + $output->write("\033[2A"); + $output->write("\n"); + $output->write("\n"); + $bar3->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/2 [>---------------------------] 0%')."\n". + $this->generateOutput(' 0/3 [#---------------------------] 0%')."\n". + rtrim($this->generateOutput(' 0 [>---------------------------]')). + + "\033[2A". + $this->generateOutput(' 1/2 [==============>-------------] 50%')."\n". + $this->generateOutput(' 1/3 [=========#------------------] 33%')."\n". + rtrim($this->generateOutput(' 1 [->--------------------------]')). + + "\033[2A". + $this->generateOutput(' 2/2 [============================] 100%')."\n". + $this->generateOutput(' 2/3 [==================#---------] 66%')."\n". + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + + "\033[2A". + "\n". + $this->generateOutput(' 3/3 [============================] 100%')."\n". + rtrim($this->generateOutput(' 3 [--->------------------------]')). + + "\033[2A". + "\n". + "\n". + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testWithoutMax() + { + $output = $this->getOutputStream(); + + $bar = new ProgressBar($output); + $bar->start(); + $bar->advance(); + $bar->advance(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + rtrim($this->generateOutput(' 0 [>---------------------------]')). + rtrim($this->generateOutput(' 1 [->--------------------------]')). + rtrim($this->generateOutput(' 2 [-->-------------------------]')). + rtrim($this->generateOutput(' 3 [--->------------------------]')). + rtrim($this->generateOutput(' 3 [============================]')), + stream_get_contents($output->getStream()) + ); + } + + public function testAddingPlaceholderFormatter() + { + ProgressBar::setPlaceholderFormatterDefinition('remaining_steps', function (ProgressBar $bar) { + return $bar->getMaxSteps() - $bar->getProgress(); + }); + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat(' %remaining_steps% [%bar%]'); + + $bar->start(); + $bar->advance(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 3 [>---------------------------]'). + $this->generateOutput(' 2 [=========>------------------]'). + $this->generateOutput(' 0 [============================]'), + stream_get_contents($output->getStream()) + ); + } + + public function testMultilineFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 3); + $bar->setFormat("%bar%\nfoobar"); + + $bar->start(); + $bar->advance(); + $bar->clear(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(">---------------------------\nfoobar"). + $this->generateOutput("=========>------------------\nfoobar "). + $this->generateOutput(" \n "). + $this->generateOutput("============================\nfoobar "), + stream_get_contents($output->getStream()) + ); + } + + public function testAnsiColorsAndEmojis() + { + $bar = new ProgressBar($output = $this->getOutputStream(), 15); + ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) { + static $i = 0; + $mem = 100000 * $i; + $colors = $i++ ? '41;37' : '44;37'; + + return "\033[".$colors.'m '.Helper::formatMemory($mem)." \033[0m"; + }); + $bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%"); + $bar->setBarCharacter($done = "\033[32m●\033[0m"); + $bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m"); + $bar->setProgressCharacter($progress = "\033[32m➤ \033[0m"); + + $bar->setMessage('Starting the demo... fingers crossed', 'title'); + $bar->start(); + $bar->setMessage('Looks good to me...', 'title'); + $bar->advance(4); + $bar->setMessage('Thanks, bye', 'title'); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput( + " \033[44;37m Starting the demo... fingers crossed \033[0m\n". + ' 0/15 '.$progress.str_repeat($empty, 26)." 0%\n". + " \xf0\x9f\x8f\x81 1 sec \033[44;37m 0 B \033[0m" + ). + $this->generateOutput( + " \033[44;37m Looks good to me... \033[0m\n". + ' 4/15 '.str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n". + " \xf0\x9f\x8f\x81 1 sec \033[41;37m 97 KiB \033[0m" + ). + $this->generateOutput( + " \033[44;37m Thanks, bye \033[0m\n". + ' 15/15 '.str_repeat($done, 28)." 100%\n". + " \xf0\x9f\x8f\x81 1 sec \033[41;37m 195 KiB \033[0m" + ), + stream_get_contents($output->getStream()) + ); + } + + public function testSetFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0 [>---------------------------]'), + stream_get_contents($output->getStream()) + ); + + $bar = new ProgressBar($output = $this->getOutputStream(), 10); + $bar->setFormat('normal'); + $bar->start(); + rewind($output->getStream()); + $this->assertEquals( + $this->generateOutput(' 0/10 [>---------------------------] 0%'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @dataProvider provideFormat + */ + public function testFormatsWithoutMax($format) + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat($format); + $bar->start(); + + rewind($output->getStream()); + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php new file mode 100644 index 0000000..6b83081 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/ProgressIndicatorTest.php @@ -0,0 +1,179 @@ +getOutputStream()); + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->setMessage('Advancing...'); + $bar->advance(); + $bar->finish('Done...'); + $bar->start('Starting Again...'); + usleep(101000); + $bar->advance(); + $bar->finish('Done Again...'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' | Starting...'). + $this->generateOutput(' / Starting...'). + $this->generateOutput(' - Starting...'). + $this->generateOutput(' \\ Starting...'). + $this->generateOutput(' \\ Advancing...'). + $this->generateOutput(' | Advancing...'). + $this->generateOutput(' | Done... '). + PHP_EOL. + $this->generateOutput(' - Starting Again...'). + $this->generateOutput(' \\ Starting Again...'). + $this->generateOutput(' \\ Done Again... '). + PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testNonDecoratedOutput() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(false)); + + $bar->start('Starting...'); + $bar->advance(); + $bar->advance(); + $bar->setMessage('Midway...'); + $bar->advance(); + $bar->advance(); + $bar->finish('Done...'); + + rewind($output->getStream()); + + $this->assertEquals( + ' Starting...'.PHP_EOL. + ' Midway... '.PHP_EOL. + ' Done... '.PHP_EOL.PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomIndicatorValues() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, array('a', 'b', 'c')); + + $bar->start('Starting...'); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + usleep(101000); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' b Starting...'). + $this->generateOutput(' c Starting...'). + $this->generateOutput(' a Starting...'), + stream_get_contents($output->getStream()) + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Must have at least 2 indicator value characters. + */ + public function testCannotSetInvalidIndicatorCharacters() + { + $bar = new ProgressIndicator($this->getOutputStream(), null, 100, array('1')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator already started. + */ + public function testCannotStartAlreadyStartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->start('Starting...'); + $bar->start('Starting Again.'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotAdvanceUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->advance(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Progress indicator has not yet been started. + */ + public function testCannotFinishUnstartedIndicator() + { + $bar = new ProgressIndicator($this->getOutputStream()); + $bar->finish('Finished'); + } + + /** + * @dataProvider provideFormat + */ + public function testFormats($format) + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), $format); + $bar->start('Starting...'); + $bar->advance(); + + rewind($output->getStream()); + + $this->assertNotEmpty(stream_get_contents($output->getStream())); + } + + /** + * Provides each defined format. + * + * @return array + */ + public function provideFormat() + { + return array( + array('normal'), + array('verbose'), + array('very_verbose'), + array('debug'), + ); + } + + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) + { + return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); + } + + protected function generateOutput($expected) + { + $count = substr_count($expected, "\n"); + + return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php new file mode 100644 index 0000000..a1f85b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -0,0 +1,383 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * @group tty + */ +class QuestionHelperTest extends \PHPUnit_Framework_TestCase +{ + public function testAskChoice() + { + $questionHelper = new QuestionHelper(); + + $helperSet = new HelperSet(array(new FormatterHelper())); + $questionHelper->setHelperSet($helperSet); + + $heroes = array('Superman', 'Batman', 'Spiderman'); + + $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); + $question->setMaxAttempts(1); + // first answer is an empty answer, we're supposed to receive the default value + $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setMaxAttempts(1); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); + $question->setErrorMessage('Input "%s" is not a superhero!'); + $question->setMaxAttempts(2); + $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $stream = stream_get_contents($output->getStream()); + $this->assertContains('Input "Fabien" is not a superhero!', $stream); + + try { + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); + $question->setMaxAttempts(1); + $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); + } + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); + $question->setMaxAttempts(1); + $question->setMultiselect(true); + + $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAsk() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("\n8AM\n")); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $question = new Question('What time is it?', '2PM'); + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); + + rewind($output->getStream()); + $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); + } + + public function testAskWithAutocomplete() + { + if (!$this->hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + // Acm + // AcsTest + // + // + // Test + // + // S + // F00oo + $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n"); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($inputStream); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new Question('Please select a bundle', 'FrameworkBundle'); + $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')); + + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskHiddenResponse() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test is not supported on Windows'); + } + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("8AM\n")); + + $question = new Question('What time is it?'); + $question->setHidden(true); + + $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + /** + * @dataProvider getAskConfirmationData + */ + public function testAskConfirmation($question, $expected, $default = true) + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream($question."\n")); + $question = new ConfirmationQuestion('Do you like French fries?', $default); + $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); + } + + public function getAskConfirmationData() + { + return array( + array('', true), + array('', false, false), + array('y', true), + array('yes', true), + array('n', false), + array('no', false), + ); + } + + public function testAskConfirmationWithCustomTrueAnswer() + { + $dialog = new QuestionHelper(); + + $dialog->setInputStream($this->getInputStream("j\ny\n")); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); + $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + } + + public function testAskAndValidate() + { + $dialog = new QuestionHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $error = 'This is not a color!'; + $validator = function ($color) use ($error) { + if (!in_array($color, array('white', 'black'))) { + throw new \InvalidArgumentException($error); + } + + return $color; + }; + + $question = new Question('What color was the white horse of Henry IV?', 'white'); + $question->setValidator($validator); + $question->setMaxAttempts(2); + + $dialog->setInputStream($this->getInputStream("\nblack\n")); + $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); + + $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); + try { + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($error, $e->getMessage()); + } + } + + /** + * @dataProvider simpleAnswerProvider + */ + public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'My environment 1', + 'My environment 2', + 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function simpleAnswerProvider() + { + return array( + array(0, 'My environment 1'), + array(1, 'My environment 2'), + array(2, 'My environment 3'), + array('My environment 1', 'My environment 1'), + array('My environment 2', 'My environment 2'), + array('My environment 3', 'My environment 3'), + ); + } + + /** + * @dataProvider mixedKeysChoiceListAnswerProvider + */ + public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) + { + $possibleChoices = array( + '0' => 'No environment', + '1' => 'My environment 1', + 'env_2' => 'My environment 2', + 3 => 'My environment 3', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + public function mixedKeysChoiceListAnswerProvider() + { + return array( + array('0', '0'), + array('No environment', '0'), + array('1', '1'), + array('env_2', 'env_2'), + array(3, '3'), + array('My environment 1', '1'), + ); + } + + /** + * @dataProvider answerProvider + */ + public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) + { + $possibleChoices = array( + 'env_1' => 'My environment 1', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + + $this->assertSame($expectedValue, $answer); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3. + */ + public function testAmbiguousChoiceFromChoicelist() + { + $possibleChoices = array( + 'env_1' => 'My first environment', + 'env_2' => 'My environment', + 'env_3' => 'My environment', + ); + + $dialog = new QuestionHelper(); + $dialog->setInputStream($this->getInputStream("My environment\n")); + $helperSet = new HelperSet(array(new FormatterHelper())); + $dialog->setHelperSet($helperSet); + + $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); + $question->setMaxAttempts(1); + + $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); + } + + public function answerProvider() + { + return array( + array('env_1', 'env_1'), + array('env_2', 'env_2'), + array('env_3', 'env_3'), + array('My environment 1', 'env_1'), + ); + } + + public function testNoInteraction() + { + $dialog = new QuestionHelper(); + $question = new Question('Do you have a job?', 'not yet'); + $this->assertEquals('not yet', $dialog->ask($this->createInputInterfaceMock(false), $this->createOutputInterface(), $question)); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fwrite($stream, $input); + rewind($stream); + + return $stream; + } + + protected function createOutputInterface() + { + return new StreamOutput(fopen('php://memory', 'r+', false)); + } + + protected function createInputInterfaceMock($interactive = true) + { + $mock = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $mock->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue($interactive)); + + return $mock; + } + + private function hasSttyAvailable() + { + exec('stty 2>&1', $output, $exitcode); + + return $exitcode === 0; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php new file mode 100644 index 0000000..587d841 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableStyleTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\TableStyle; + +class TableStyleTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH). + */ + public function testSetPadTypeWithInvalidType() + { + $style = new TableStyle(); + $style->setPadType('TEST'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableTest.php new file mode 100644 index 0000000..d917d69 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Helper/TableTest.php @@ -0,0 +1,639 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Helper; + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableStyle; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Helper\TableCell; +use Symfony\Component\Console\Output\StreamOutput; + +class TableTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'r+'); + } + + protected function tearDown() + { + fclose($this->stream); + $this->stream = null; + } + + /** + * @dataProvider testRenderProvider + */ + public function testRender($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRows($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->addRows($rows) + ->setStyle($style) + ; + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + /** + * @dataProvider testRenderProvider + */ + public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected) + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders($headers) + ->setStyle($style) + ; + foreach ($rows as $row) { + $table->addRow($row); + } + $table->render(); + + $this->assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRenderProvider() + { + $books = array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + ); + + return array( + array( + array('ISBN', 'Title', 'Author'), + $books, + 'default', +<< array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
    array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-700', 'Divine Com', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + ), + 'default', +<<
    99921-58-10-700 | Divine Com | Dante Alighieri | +| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | ++----------------------------------+----------------------+-----------------+ + +TABLE + ), + 'Cell with colspan' => array( + array('ISBN', 'Title', 'Author'), + array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + new TableSeparator(), + array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))), + new TableSeparator(), + array( + new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)), + 'Mark Schmidt', + ), + new TableSeparator(), + array( + '9971-5-0210-0', + new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)), + ), + ), + 'default', +<<
    array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3)), + 'Divine Comedy', + 'Dante Alighieri', + ), + array('A Tale of Two Cities', 'Charles Dickens'), + array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"), + new TableSeparator(), + array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'), + array('80-902734-1-7', 'Test'), + ), + 'default', +<<
    array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)), + ), + array('J. R. R. Tolkien'), + array('J. R. R'), + ), + 'default', +<<
    array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + new TableSeparator(), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + new TableSeparator(), + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)), + ), + ), + 'default', +<<
    array( + array('ISBN', 'Title', 'Author'), + array( + array( + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + 'Dante Alighieri', + ), + array('Charles Dickens'), + array( + 'Dante Alighieri', + new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)), + ), + array('Charles Dickens'), + ), + 'default', +<<
    array( + array('ISBN', 'Author'), + array( + array( + new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)), + 'Dante Alighieri', + ), + array(new TableSeparator()), + array('Charles Dickens'), + ), + 'default', +<<
    array( + array( + array(new TableCell('Main title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + ), + array(), + 'default', +<<
    array( + array(), + array( + array( + new TableCell('1', array('colspan' => 3)), + new TableCell('2', array('colspan' => 2)), + new TableCell('3', array('colspan' => 2)), + new TableCell('4', array('colspan' => 2)), + ), + ), + 'default', +<<
    getOutputStream()); + $table + ->setHeaders(array('■■')) + ->setRows(array(array(1234))) + ->setStyle('default') + ; + $table->render(); + + $expected = +<<
    assertEquals($expected, $this->getOutputContent($output)); + } + + public function testStyle() + { + $style = new TableStyle(); + $style + ->setHorizontalBorderChar('.') + ->setVerticalBorderChar('.') + ->setCrossingChar('.') + ; + + Table::setStyleDefinition('dotfull', $style); + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array(array('Bar'))) + ->setStyle('dotfull'); + $table->render(); + + $expected = +<<
    assertEquals($expected, $this->getOutputContent($output)); + } + + public function testRowSeparator() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('Foo')) + ->setRows(array( + array('Bar1'), + new TableSeparator(), + array('Bar2'), + new TableSeparator(), + array('Bar3'), + )); + $table->render(); + + $expected = +<<
    assertEquals($expected, $this->getOutputContent($output)); + + $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works'); + } + + public function testRenderMultiCalls() + { + $table = new Table($output = $this->getOutputStream()); + $table->setRows(array( + array(new TableCell('foo', array('colspan' => 2))), + )); + $table->render(); + $table->render(); + $table->render(); + + $expected = +<<
    assertEquals($expected, $this->getOutputContent($output)); + } + + public function testColumnStyle() + { + $table = new Table($output = $this->getOutputStream()); + $table + ->setHeaders(array('ISBN', 'Title', 'Author', 'Price')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'), + )); + + $style = new TableStyle(); + $style->setPadType(STR_PAD_LEFT); + $table->setColumnStyle(3, $style); + + $table->render(); + + $expected = + <<
    assertEquals($expected, $this->getOutputContent($output)); + } + + protected function getOutputStream() + { + return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, false); + } + + protected function getOutputContent(StreamOutput $output) + { + rewind($output->getStream()); + + return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php new file mode 100644 index 0000000..dd217ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArgvInputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $_SERVER['argv'] = array('cli.php', 'foo'); + $input = new ArgvInput(); + $r = new \ReflectionObject($input); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + + $this->assertEquals(array('foo'), $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + } + + public function testParseArguments() + { + $input = new ArgvInput(array('cli.php', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + + $input->bind(new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() is stateless'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArgvInput($input); + $input->bind(new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('cli.php', '--foo'), + array(new InputOption('foo')), + array('foo' => true), + '->parse() parses long options without a value', + ), + array( + array('cli.php', '--foo=bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a = separator)', + ), + array( + array('cli.php', '--foo', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses long options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f'), + array(new InputOption('foo', 'f')), + array('foo' => true), + '->parse() parses short options without a value', + ), + array( + array('cli.php', '-fbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with no separator)', + ), + array( + array('cli.php', '-f', 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)), + array('foo' => 'bar'), + '->parse() parses short options with a required value (with a space separator)', + ), + array( + array('cli.php', '-f', ''), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value', + ), + array( + array('cli.php', '-f', '', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)), + array('foo' => ''), + '->parse() parses short options with an optional empty value followed by an argument', + ), + array( + array('cli.php', '-f', '', '-b'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => '', 'bar' => true), + '->parse() parses short options with an optional empty value followed by an option', + ), + array( + array('cli.php', '-f', '-b', 'foo'), + array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')), + array('foo' => null, 'bar' => true), + '->parse() parses short options with an optional value which is not present', + ), + array( + array('cli.php', '-fb'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b')), + array('foo' => true, 'bar' => true), + '->parse() parses short options when they are aggregated as a single one', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has a required value', + ), + array( + array('cli.php', '-fb', 'bar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => true, 'bar' => 'bar'), + '->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator', + ), + array( + array('cli.php', '-fbbar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)), + array('foo' => 'bbar', 'bar' => null), + '->parse() parses short options when they are aggregated as a single one and one of them takes a value', + ), + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testInvalidInput($argv, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('RuntimeException', $expectedExceptionMessage); + + $input = new ArgvInput($argv); + $input->bind($definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('cli.php', '--foo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('cli.php', '-ffoo'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "-o" option does not exist.', + ), + array( + array('cli.php', '--foo=bar'), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))), + 'The "--foo" option does not accept a value.', + ), + array( + array('cli.php', 'foo', 'bar'), + new InputDefinition(), + 'Too many arguments.', + ), + array( + array('cli.php', '--foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('cli.php', '-f'), + new InputDefinition(), + 'The "-f" option does not exist.', + ), + array( + array('cli.php', '-1'), + new InputDefinition(array(new InputArgument('number'))), + 'The "-1" option does not exist.', + ), + ); + } + + public function testParseArrayArgument() + { + $input = new ArgvInput(array('cli.php', 'foo', 'bar', 'baz', 'bat')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz', 'bat')), $input->getArguments(), '->parse() parses array arguments'); + } + + public function testParseArrayOption() + { + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertEquals(array('name' => array('foo', 'bar', 'baz')), $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name=foo', '--name=bar', '--name=')); + $input->bind(new InputDefinition(array(new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)))); + $this->assertSame(array('name' => array('foo', 'bar', null)), $input->getOptions(), '->parse() parses empty array options as null ("--option=value" syntax)'); + + $input = new ArgvInput(array('cli.php', '--name', 'foo', '--name', 'bar', '--name', '--anotherOption')); + $input->bind(new InputDefinition(array( + new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('anotherOption', null, InputOption::VALUE_NONE), + ))); + $this->assertSame(array('name' => array('foo', 'bar', null), 'anotherOption' => true), $input->getOptions(), '->parse() parses empty array options as null ("--option value" syntax)'); + } + + public function testParseNegativeNumberAfterDoubleDash() + { + $input = new ArgvInput(array('cli.php', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number')))); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + + $input = new ArgvInput(array('cli.php', '-f', 'bar', '--', '-1')); + $input->bind(new InputDefinition(array(new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertEquals(array('number' => '-1'), $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + } + + public function testParseEmptyStringArgument() + { + $input = new ArgvInput(array('cli.php', '-f', 'bar', '')); + $input->bind(new InputDefinition(array(new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + + $this->assertEquals(array('empty' => ''), $input->getArguments(), '->parse() parses empty string arguments'); + } + + public function testGetFirstArgument() + { + $input = new ArgvInput(array('cli.php', '-fbbar')); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); + + $input = new ArgvInput(array('cli.php', '-fbbar', 'foo')); + $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + } + + public function testHasParameterOption() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', 'foo')); + $this->assertFalse($input->hasParameterOption('--foo'), '->hasParameterOption() returns false if the given short option is not in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if the given option with provided value is in the raw input'); + } + + public function testHasParameterOptionOnlyOptions() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertTrue($input->hasParameterOption('-f', true), '->hasParameterOption() returns true if the given short option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo', '--', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--foo=bar', 'foo')); + $this->assertTrue($input->hasParameterOption('--foo', true), '->hasParameterOption() returns true if the given long option with provided value is in the raw input'); + + $input = new ArgvInput(array('cli.php', '--', '--foo')); + $this->assertFalse($input->hasParameterOption('--foo', true), '->hasParameterOption() returns false if the given option is in the raw input but after an end of options signal'); + } + + public function testToString() + { + $input = new ArgvInput(array('cli.php', '-f', 'foo')); + $this->assertEquals('-f foo', (string) $input); + + $input = new ArgvInput(array('cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C")); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } + + /** + * @dataProvider provideGetParameterOptionValues + */ + public function testGetParameterOptionEqualSign($argv, $key, $onlyParams, $expected) + { + $input = new ArgvInput($argv); + $this->assertEquals($expected, $input->getParameterOption($key, false, $onlyParams), '->getParameterOption() returns the expected value'); + } + + public function provideGetParameterOptionValues() + { + return array( + array(array('app/console', 'foo:bar', '-e', 'dev'), '-e', false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), '--env', false, 'dev'), + array(array('app/console', 'foo:bar', '-e', 'dev'), array('-e', '--env'), false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev'), array('-e', '--env'), false, 'dev'), + array(array('app/console', 'foo:bar', '--env=dev', '--en=1'), array('--en'), false, '1'), + array(array('app/console', 'foo:bar', '--env=dev', '', '--en=1'), array('--en'), false, '1'), + array(array('app/console', 'foo:bar', '--env', 'val'), '--env', false, 'val'), + array(array('app/console', 'foo:bar', '--env', 'val', '--dummy'), '--env', false, 'val'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', false, 'dev'), + array(array('app/console', 'foo:bar', '--', '--env=dev'), '--env', true, false), + ); + } + + public function testParseSingleDashAsArgument() + { + $input = new ArgvInput(array('cli.php', '-')); + $input->bind(new InputDefinition(array(new InputArgument('file')))); + $this->assertEquals(array('file' => '-'), $input->getArguments(), '->parse() parses single dash as an argument'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php new file mode 100644 index 0000000..e2b6377 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class ArrayInputTest extends \PHPUnit_Framework_TestCase +{ + public function testGetFirstArgument() + { + $input = new ArrayInput(array()); + $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); + $input = new ArrayInput(array('name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $input = new ArrayInput(array('--foo' => 'bar', 'name' => 'Fabien')); + $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + } + + public function testHasParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar'), '->hasParameterOption() returns false if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('--foo')); + $this->assertTrue($input->hasParameterOption('--foo'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + + $input = new ArrayInput(array('--foo', '--', '--bar')); + $this->assertTrue($input->hasParameterOption('--bar'), '->hasParameterOption() returns true if an option is present in the passed parameters'); + $this->assertFalse($input->hasParameterOption('--bar', true), '->hasParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + } + + public function testGetParameterOption() + { + $input = new ArrayInput(array('name' => 'Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + $this->assertFalse($input->getParameterOption('--bar'), '->getParameterOption() returns the default if an option is not present in the passed parameters'); + + $input = new ArrayInput(array('Fabien', '--foo' => 'bar')); + $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + + $input = new ArrayInput(array('--foo', '--', '--bar' => 'woop')); + $this->assertEquals('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); + $this->assertFalse($input->getParameterOption('--bar', false, true), '->getParameterOption() returns false if an option is present in the passed parameters after an end of options signal'); + } + + public function testParseArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + + $this->assertEquals(array('name' => 'foo'), $input->getArguments(), '->parse() parses required arguments'); + } + + /** + * @dataProvider provideOptions + */ + public function testParseOptions($input, $options, $expectedOptions, $message) + { + $input = new ArrayInput($input, new InputDefinition($options)); + + $this->assertEquals($expectedOptions, $input->getOptions(), $message); + } + + public function provideOptions() + { + return array( + array( + array('--foo' => 'bar'), + array(new InputOption('foo')), + array('foo' => 'bar'), + '->parse() parses long options', + ), + array( + array('--foo' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'bar'), + '->parse() parses long options with a default value', + ), + array( + array('--foo' => null), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() parses long options with a default value', + ), + array( + array('-f' => 'bar'), + array(new InputOption('foo', 'f')), + array('foo' => 'bar'), + '->parse() parses short options', + ), + array( + array('--' => null, '-f' => 'bar'), + array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, '', 'default')), + array('foo' => 'default'), + '->parse() does not parse opts after an end of options signal', + ), + array( + array('--' => null), + array(), + array(), + '->parse() does not choke on end of options signal', + ) + ); + } + + /** + * @dataProvider provideInvalidInput + */ + public function testParseInvalidInput($parameters, $definition, $expectedExceptionMessage) + { + $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); + + new ArrayInput($parameters, $definition); + } + + public function provideInvalidInput() + { + return array( + array( + array('foo' => 'foo'), + new InputDefinition(array(new InputArgument('name'))), + 'The "foo" argument does not exist.', + ), + array( + array('--foo' => null), + new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), + 'The "--foo" option requires a value.', + ), + array( + array('--foo' => 'foo'), + new InputDefinition(), + 'The "--foo" option does not exist.', + ), + array( + array('-o' => 'foo'), + new InputDefinition(), + 'The "-o" option does not exist.', + ), + ); + } + + public function testToString() + { + $input = new ArrayInput(array('-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C")); + $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php new file mode 100644 index 0000000..cfb37cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputArgumentTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputArgument; + +class InputArgumentTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $argument = new InputArgument('foo'); + $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + } + + public function testModes() + { + $argument = new InputArgument('foo'); + $this->assertFalse($argument->isRequired(), '__construct() gives a "InputArgument::OPTIONAL" mode by default'); + + $argument = new InputArgument('foo', null); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isRequired(), '__construct() can take "InputArgument::OPTIONAL" as its mode'); + + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $this->assertTrue($argument->isRequired(), '__construct() can take "InputArgument::REQUIRED" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Argument mode "%s" is not valid.', $mode)); + + new InputArgument('foo', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + public function testIsArray() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $this->assertTrue($argument->isArray(), '->isArray() returns true if the argument can be an array'); + $argument = new InputArgument('foo', InputArgument::OPTIONAL); + $this->assertFalse($argument->isArray(), '->isArray() returns false if the argument can not be an array'); + } + + public function testGetDescription() + { + $argument = new InputArgument('foo', null, 'Some description'); + $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + } + + public function testGetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + } + + public function testSetDefault() + { + $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); + $argument->setDefault(null); + $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); + $argument->setDefault('another'); + $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + + $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); + $argument->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $argument->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value except for InputArgument::OPTIONAL mode. + */ + public function testSetDefaultWithRequiredArgument() + { + $argument = new InputArgument('foo', InputArgument::REQUIRED); + $argument->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array argument must be an array. + */ + public function testSetDefaultWithArrayArgument() + { + $argument = new InputArgument('foo', InputArgument::IS_ARRAY); + $argument->setDefault('default'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php new file mode 100644 index 0000000..2b28014 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputDefinitionTest.php @@ -0,0 +1,402 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputDefinitionTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixtures; + + protected $foo, $bar, $foo1, $foo2; + + public static function setUpBeforeClass() + { + self::$fixtures = __DIR__.'/../Fixtures/'; + } + + public function testConstructorArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getArguments(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + } + + public function testConstructorOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $this->assertEquals(array(), $definition->getOptions(), '__construct() creates a new InputDefinition object'); + + $definition = new InputDefinition(array($this->foo, $this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + } + + public function testSetArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->setArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $definition->setArguments(array($this->bar)); + + $this->assertEquals(array('bar' => $this->bar), $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + } + + public function testAddArguments() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $definition->addArguments(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + } + + public function testAddArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $definition->addArgument($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getArguments(), '->addArgument() adds a InputArgument object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An argument with name "foo" already exists. + */ + public function testArgumentsMustHaveDifferentNames() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo1); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add an argument after an array argument. + */ + public function testArrayArgumentHasToBeLast() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument(new InputArgument('fooarray', InputArgument::IS_ARRAY)); + $definition->addArgument(new InputArgument('anotherbar')); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot add a required argument after an optional one. + */ + public function testRequiredArgumentCannotFollowAnOptionalOne() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo); + $definition->addArgument($this->foo2); + } + + public function testGetArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "bar" argument does not exist. + */ + public function testGetInvalidArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + $definition->getArgument('bar'); + } + + public function testHasArgument() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArguments(array($this->foo)); + + $this->assertTrue($definition->hasArgument('foo'), '->hasArgument() returns true if a InputArgument exists for the given name'); + $this->assertFalse($definition->hasArgument('bar'), '->hasArgument() returns false if a InputArgument exists for the given name'); + } + + public function testGetArgumentRequiredCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + } + + public function testGetArgumentCount() + { + $this->initializeArguments(); + + $definition = new InputDefinition(); + $definition->addArgument($this->foo2); + $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $definition->addArgument($this->foo); + $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + } + + public function testGetArgumentDefaults() + { + $definition = new InputDefinition(array( + new InputArgument('foo1', InputArgument::OPTIONAL), + new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), + new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo1' => null, 'foo2' => 'default', 'foo3' => array()), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + + $definition = new InputDefinition(array( + new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', array(1, 2)), + )); + $this->assertEquals(array('foo4' => array(1, 2)), $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + } + + public function testSetOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $definition->setOptions(array($this->bar)); + $this->assertEquals(array('bar' => $this->bar), $definition->getOptions(), '->setOptions() clears all InputOption objects'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-f" option does not exist. + */ + public function testSetOptionsClearsOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->setOptions(array($this->bar)); + $definition->getOptionForShortcut('f'); + } + + public function testAddOptions() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $definition->addOptions(array($this->bar)); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + } + + public function testAddOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $this->assertEquals(array('foo' => $this->foo), $definition->getOptions(), '->addOption() adds a InputOption object'); + $definition->addOption($this->bar); + $this->assertEquals(array('foo' => $this->foo, 'bar' => $this->bar), $definition->getOptions(), '->addOption() adds a InputOption object'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option named "foo" already exists. + */ + public function testAddDuplicateOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo2); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "f" already exists. + */ + public function testAddDuplicateShortcutOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(); + $definition->addOption($this->foo); + $definition->addOption($this->foo1); + } + + public function testGetOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testGetInvalidOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOption('bar'); + } + + public function testHasOption() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasOption('foo'), '->hasOption() returns true if a InputOption exists for the given name'); + $this->assertFalse($definition->hasOption('bar'), '->hasOption() returns false if a InputOption exists for the given name'); + } + + public function testHasShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertTrue($definition->hasShortcut('f'), '->hasShortcut() returns true if a InputOption exists for the given shortcut'); + $this->assertFalse($definition->hasShortcut('b'), '->hasShortcut() returns false if a InputOption exists for the given shortcut'); + } + + public function testGetOptionForShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + public function testGetOptionForMultiShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->multi)); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "-l" option does not exist. + */ + public function testGetOptionForInvalidShortcut() + { + $this->initializeOptions(); + + $definition = new InputDefinition(array($this->foo)); + $definition->getOptionForShortcut('l'); + } + + public function testGetOptionDefaults() + { + $definition = new InputDefinition(array( + new InputOption('foo1', null, InputOption::VALUE_NONE), + new InputOption('foo2', null, InputOption::VALUE_REQUIRED), + new InputOption('foo3', null, InputOption::VALUE_REQUIRED, '', 'default'), + new InputOption('foo4', null, InputOption::VALUE_OPTIONAL), + new InputOption('foo5', null, InputOption::VALUE_OPTIONAL, '', 'default'), + new InputOption('foo6', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY), + new InputOption('foo7', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '', array(1, 2)), + )); + $defaults = array( + 'foo1' => false, + 'foo2' => null, + 'foo3' => 'default', + 'foo4' => null, + 'foo5' => 'default', + 'foo6' => array(), + 'foo7' => array(1, 2), + ); + $this->assertSame($defaults, $definition->getOptionDefaults(), '->getOptionDefaults() returns the default values for all options'); + } + + /** + * @dataProvider getGetSynopsisData + */ + public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) + { + $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); + } + + public function getGetSynopsisData() + { + return array( + array(new InputDefinition(array(new InputOption('foo'))), '[--foo]', 'puts optional options in square brackets'), + array(new InputDefinition(array(new InputOption('foo', 'f'))), '[-f|--foo]', 'separates shortcut with a pipe'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))), '[-f|--foo FOO]', 'uses shortcut as value placeholder'), + array(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL))), '[-f|--foo [FOO]]', 'puts optional values in square brackets'), + + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED))), '', 'puts arguments in angle brackets'), + array(new InputDefinition(array(new InputArgument('foo'))), '[]', 'puts optional arguments in square brackets'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::IS_ARRAY))), '[]...', 'uses an ellipsis for array arguments'), + array(new InputDefinition(array(new InputArgument('foo', InputArgument::REQUIRED | InputArgument::IS_ARRAY))), ' ()...', 'uses parenthesis and ellipsis for required array arguments'), + + array(new InputDefinition(array(new InputOption('foo'), new InputArgument('foo', InputArgument::REQUIRED))), '[--foo] [--] ', 'puts [--] between options and arguments'), + ); + } + + public function testGetShortSynopsis() + { + $definition = new InputDefinition(array(new InputOption('foo'), new InputOption('bar'), new InputArgument('cat'))); + $this->assertEquals('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); + } + + protected function initializeArguments() + { + $this->foo = new InputArgument('foo'); + $this->bar = new InputArgument('bar'); + $this->foo1 = new InputArgument('foo'); + $this->foo2 = new InputArgument('foo2', InputArgument::REQUIRED); + } + + protected function initializeOptions() + { + $this->foo = new InputOption('foo', 'f'); + $this->bar = new InputOption('bar', 'b'); + $this->foo1 = new InputOption('fooBis', 'f'); + $this->foo2 = new InputOption('foo', 'p'); + $this->multi = new InputOption('multi', 'm|mm|mmm'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php new file mode 100644 index 0000000..53ce1df --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputOptionTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputOption; + +class InputOptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $option = new InputOption('foo'); + $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $option = new InputOption('--foo'); + $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value. + */ + public function testArrayModeWithoutValue() + { + new InputOption('foo', 'f', InputOption::VALUE_IS_ARRAY); + } + + public function testShortcut() + { + $option = new InputOption('foo', 'f'); + $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $option = new InputOption('foo', '-f|-ff|fff'); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo', array('f', 'ff', '-fff')); + $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $option = new InputOption('foo'); + $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); + } + + public function testModes() + { + $option = new InputOption('foo', 'f'); + $this->assertFalse($option->acceptValue(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueRequired(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + $this->assertFalse($option->isValueOptional(), '__construct() gives a "InputOption::VALUE_NONE" mode by default'); + + $option = new InputOption('foo', 'f', null); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $this->assertFalse($option->acceptValue(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_NONE" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_REQUIRED); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertTrue($option->isValueRequired(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + $this->assertFalse($option->isValueOptional(), '__construct() can take "InputOption::VALUE_REQUIRED" as its mode'); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL); + $this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + $this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode'); + } + + /** + * @dataProvider provideInvalidModes + */ + public function testInvalidModes($mode) + { + $this->setExpectedException('InvalidArgumentException', sprintf('Option mode "%s" is not valid.', $mode)); + + new InputOption('foo', 'f', $mode); + } + + public function provideInvalidModes() + { + return array( + array('ANOTHER_ONE'), + array(-1), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEmptyNameIsInvalid() + { + new InputOption(''); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDoubleDashNameIsInvalid() + { + new InputOption('--'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSingleDashOptionIsInvalid() + { + new InputOption('foo', '-'); + } + + public function testIsArray() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertTrue($option->isArray(), '->isArray() returns true if the option can be an array'); + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->isArray(), '->isArray() returns false if the option can not be an array'); + } + + public function testGetDescription() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + } + + public function testGetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); + $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); + + $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $this->assertEquals(array(), $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + + $option = new InputOption('foo', null, InputOption::VALUE_NONE); + $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); + } + + public function testSetDefault() + { + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); + $option->setDefault(null); + $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); + $option->setDefault('another'); + $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + + $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); + $option->setDefault(array(1, 2)); + $this->assertEquals(array(1, 2), $option->getDefault(), '->setDefault() changes the default value'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot set a default value when using InputOption::VALUE_NONE mode. + */ + public function testDefaultValueWithValueNoneMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_NONE); + $option->setDefault('default'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage A default value for an array option must be an array. + */ + public function testDefaultValueWithIsArrayMode() + { + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); + $option->setDefault('default'); + } + + public function testEquals() + { + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', null, 'Alternative description'); + $this->assertTrue($option->equals($option2)); + + $option = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description', true); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('bar', 'f', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', '', null, 'Some description'); + $this->assertFalse($option->equals($option2)); + + $option = new InputOption('foo', 'f', null, 'Some description'); + $option2 = new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL, 'Some description'); + $this->assertFalse($option->equals($option2)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputTest.php new file mode 100644 index 0000000..eb1c661 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/InputTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; + +class InputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + } + + public function testOptions() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name')))); + $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + + $input->setOption('name', 'bar'); + $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertEquals(array('name' => 'bar'), $input->getOptions(), '->getOptions() returns all option values'); + + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testSetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->setOption('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" option does not exist. + */ + public function testGetInvalidOption() + { + $input = new ArrayInput(array('--name' => 'foo'), new InputDefinition(array(new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')))); + $input->getOption('foo'); + } + + public function testArguments() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name')))); + $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + + $input->setArgument('name', 'bar'); + $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertEquals(array('name' => 'bar'), $input->getArguments(), '->getArguments() returns all argument values'); + + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertEquals(array('name' => 'foo', 'bar' => 'default'), $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testSetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->setArgument('foo', 'bar'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "foo" argument does not exist. + */ + public function testGetInvalidArgument() + { + $input = new ArrayInput(array('name' => 'foo'), new InputDefinition(array(new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')))); + $input->getArgument('foo'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingArguments() + { + $input = new ArrayInput(array()); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + $input->validate(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Not enough arguments (missing: "name"). + */ + public function testValidateWithMissingRequiredArguments() + { + $input = new ArrayInput(array('bar' => 'baz')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED), new InputArgument('bar', InputArgument::OPTIONAL)))); + $input->validate(); + } + + public function testValidate() + { + $input = new ArrayInput(array('name' => 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name', InputArgument::REQUIRED)))); + + $this->assertNull($input->validate()); + } + + public function testSetGetInteractive() + { + $input = new ArrayInput(array()); + $this->assertTrue($input->isInteractive(), '->isInteractive() returns whether the input should be interactive or not'); + $input->setInteractive(false); + $this->assertFalse($input->isInteractive(), '->setInteractive() changes the interactive flag'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/StringInputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/StringInputTest.php new file mode 100644 index 0000000..7059cf0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Input/StringInputTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Input; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; + +class StringInputTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($input, $tokens, $message) + { + $input = new StringInput($input); + $r = new \ReflectionClass('Symfony\Component\Console\Input\ArgvInput'); + $p = $r->getProperty('tokens'); + $p->setAccessible(true); + $this->assertEquals($tokens, $p->getValue($input), $message); + } + + public function testInputOptionWithGivenString() + { + $definition = new InputDefinition( + array(new InputOption('foo', null, InputOption::VALUE_REQUIRED)) + ); + + // call to bind + $input = new StringInput('--foo=bar'); + $input->bind($definition); + $this->assertEquals('bar', $input->getOption('foo')); + } + + public function getTokenizeData() + { + return array( + array('', array(), '->tokenize() parses an empty string'), + array('foo', array('foo'), '->tokenize() parses arguments'), + array(' foo bar ', array('foo', 'bar'), '->tokenize() ignores whitespaces between arguments'), + array('"quoted"', array('quoted'), '->tokenize() parses quoted arguments'), + array("'quoted'", array('quoted'), '->tokenize() parses quoted arguments'), + array("'a\rb\nc\td'", array("a\rb\nc\td"), '->tokenize() parses whitespace chars in strings'), + array("'a'\r'b'\n'c'\t'd'", array('a', 'b', 'c', 'd'), '->tokenize() parses whitespace chars between args as spaces'), + array('\"quoted\"', array('"quoted"'), '->tokenize() parses escaped-quoted arguments'), + array("\'quoted\'", array('\'quoted\''), '->tokenize() parses escaped-quoted arguments'), + array('-a', array('-a'), '->tokenize() parses short options'), + array('-azc', array('-azc'), '->tokenize() parses aggregated short options'), + array('-awithavalue', array('-awithavalue'), '->tokenize() parses short options with a value'), + array('-a"foo bar"', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a"foo bar""foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'', array('-afoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'\'foo bar\'', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('-a\'foo bar\'"foo bar"', array('-afoo barfoo bar'), '->tokenize() parses short options with a value'), + array('--long-option', array('--long-option'), '->tokenize() parses long options'), + array('--long-option=foo', array('--long-option=foo'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar"', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array('--long-option="foo bar""another"', array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('--long-option=\'foo bar\'', array('--long-option=foo bar'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar''another'", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array("--long-option='foo bar'\"another\"", array('--long-option=foo baranother'), '->tokenize() parses long options with a value'), + array('foo -a -ffoo --long bar', array('foo', '-a', '-ffoo', '--long', 'bar'), '->tokenize() parses when several arguments and options'), + ); + } + + public function testToString() + { + $input = new StringInput('-f foo'); + $this->assertEquals('-f foo', (string) $input); + + $input = new StringInput('-f --bar=foo "a b c d"'); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); + + $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); + $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Logger/ConsoleLoggerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Logger/ConsoleLoggerTest.php new file mode 100644 index 0000000..c5eca2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Logger/ConsoleLoggerTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Logger; + +use Psr\Log\Test\LoggerInterfaceTest; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Logger\ConsoleLogger; +use Symfony\Component\Console\Tests\Fixtures\DummyOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console logger test. + * + * @author Kévin Dunglas + */ +class ConsoleLoggerTest extends LoggerInterfaceTest +{ + /** + * @var DummyOutput + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function getLogger() + { + $this->output = new DummyOutput(OutputInterface::VERBOSITY_VERBOSE); + + return new ConsoleLogger($this->output, array( + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, + LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, + LogLevel::DEBUG => OutputInterface::VERBOSITY_NORMAL, + )); + } + + /** + * {@inheritdoc} + */ + public function getLogs() + { + return $this->output->getLogs(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php new file mode 100644 index 0000000..1afbbb6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\Output; + +class ConsoleOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new ConsoleOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php new file mode 100644 index 0000000..b20ae4e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class NullOutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new NullOutput(); + + ob_start(); + $output->write('foo'); + $buffer = ob_get_clean(); + + $this->assertSame('', $buffer, '->write() does nothing (at least nothing is printed)'); + $this->assertFalse($output->isDecorated(), '->isDecorated() returns false'); + } + + public function testVerbosity() + { + $output = new NullOutput(); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); + + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/OutputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/OutputTest.php new file mode 100644 index 0000000..45e6ddc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/OutputTest.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $output = new TestOutput(Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + public function testSetIsDecorated() + { + $output = new TestOutput(); + $output->setDecorated(true); + $this->assertTrue($output->isDecorated(), 'setDecorated() sets the decorated flag'); + } + + public function testSetGetVerbosity() + { + $output = new TestOutput(); + $output->setVerbosity(Output::VERBOSITY_QUIET); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '->setVerbosity() sets the verbosity'); + + $this->assertTrue($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_NORMAL); + $this->assertFalse($output->isQuiet()); + $this->assertFalse($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertFalse($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertFalse($output->isDebug()); + + $output->setVerbosity(Output::VERBOSITY_DEBUG); + $this->assertFalse($output->isQuiet()); + $this->assertTrue($output->isVerbose()); + $this->assertTrue($output->isVeryVerbose()); + $this->assertTrue($output->isDebug()); + } + + public function testWriteWithVerbosityQuiet() + { + $output = new TestOutput(Output::VERBOSITY_QUIET); + $output->writeln('foo'); + $this->assertEquals('', $output->output, '->writeln() outputs nothing if verbosity is set to VERBOSITY_QUIET'); + } + + public function testWriteAnArrayOfMessages() + { + $output = new TestOutput(); + $output->writeln(array('foo', 'bar')); + $this->assertEquals("foo\nbar\n", $output->output, '->writeln() can take an array of messages to output'); + } + + /** + * @dataProvider provideWriteArguments + */ + public function testWriteRawMessage($message, $type, $expectedOutput) + { + $output = new TestOutput(); + $output->writeln($message, $type); + $this->assertEquals($expectedOutput, $output->output); + } + + public function provideWriteArguments() + { + return array( + array('foo', Output::OUTPUT_RAW, "foo\n"), + array('foo', Output::OUTPUT_PLAIN, "foo\n"), + ); + } + + public function testWriteWithDecorationTurnedOff() + { + $output = new TestOutput(); + $output->setDecorated(false); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() strips decoration tags if decoration is set to false'); + } + + public function testWriteDecoratedMessage() + { + $fooStyle = new OutputFormatterStyle('yellow', 'red', array('blink')); + $output = new TestOutput(); + $output->getFormatter()->setStyle('FOO', $fooStyle); + $output->setDecorated(true); + $output->writeln('foo'); + $this->assertEquals("\033[33;41;5mfoo\033[39;49;25m\n", $output->output, '->writeln() decorates the output'); + } + + public function testWriteWithInvalidStyle() + { + $output = new TestOutput(); + + $output->clear(); + $output->write('foo'); + $this->assertEquals('foo', $output->output, '->write() do nothing when a style does not exist'); + + $output->clear(); + $output->writeln('foo'); + $this->assertEquals("foo\n", $output->output, '->writeln() do nothing when a style does not exist'); + } + + /** + * @dataProvider verbosityProvider + */ + public function testWriteWithVerbosityOption($verbosity, $expected, $msg) + { + $output = new TestOutput(); + + $output->setVerbosity($verbosity); + $output->clear(); + $output->write('1', false); + $output->write('2', false, Output::VERBOSITY_QUIET); + $output->write('3', false, Output::VERBOSITY_NORMAL); + $output->write('4', false, Output::VERBOSITY_VERBOSE); + $output->write('5', false, Output::VERBOSITY_VERY_VERBOSE); + $output->write('6', false, Output::VERBOSITY_DEBUG); + $this->assertEquals($expected, $output->output, $msg); + } + + public function verbosityProvider() + { + return array( + array(Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'), + array(Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'), + array(Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'), + array(Output::VERBOSITY_VERY_VERBOSE, '12345', '->write() in VERY_VERBOSE mode outputs anything below an explicit DEBUG verbosity'), + array(Output::VERBOSITY_DEBUG, '123456', '->write() in DEBUG mode outputs everything'), + ); + } +} + +class TestOutput extends Output +{ + public $output = ''; + + public function clear() + { + $this->output = ''; + } + + protected function doWrite($message, $newline) + { + $this->output .= $message.($newline ? "\n" : ''); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/StreamOutputTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/StreamOutputTest.php new file mode 100644 index 0000000..2fd4f61 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Output/StreamOutputTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Output; + +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\StreamOutput; + +class StreamOutputTest extends \PHPUnit_Framework_TestCase +{ + protected $stream; + + protected function setUp() + { + $this->stream = fopen('php://memory', 'a', false); + } + + protected function tearDown() + { + $this->stream = null; + } + + public function testConstructor() + { + $output = new StreamOutput($this->stream, Output::VERBOSITY_QUIET, true); + $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument'); + $this->assertTrue($output->isDecorated(), '__construct() takes the decorated flag as its second argument'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The StreamOutput class needs a stream as its first argument. + */ + public function testStreamIsRequired() + { + new StreamOutput('foo'); + } + + public function testGetStream() + { + $output = new StreamOutput($this->stream); + $this->assertEquals($this->stream, $output->getStream(), '->getStream() returns the current stream'); + } + + public function testDoWrite() + { + $output = new StreamOutput($this->stream); + $output->writeln('foo'); + rewind($output->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($output->getStream()), '->doWrite() writes to the stream'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php new file mode 100644 index 0000000..6bf6412 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Style; + +use PHPUnit_Framework_TestCase; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Tester\CommandTester; + +class SymfonyStyleTest extends PHPUnit_Framework_TestCase +{ + /** @var Command */ + protected $command; + /** @var CommandTester */ + protected $tester; + + protected function setUp() + { + $this->command = new Command('sfstyle'); + $this->tester = new CommandTester($this->command); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + /** + * @dataProvider inputCommandToOutputFilesProvider + */ + public function testOutputs($inputCommandFilepath, $outputFilepath) + { + $code = require $inputCommandFilepath; + $this->command->setCode($code); + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $this->assertStringEqualsFile($outputFilepath, $this->tester->getDisplay(true)); + } + + public function inputCommandToOutputFilesProvider() + { + $baseDir = __DIR__.'/../Fixtures/Style/SymfonyStyle'; + + return array_map(null, glob($baseDir.'/command/command_*.php'), glob($baseDir.'/output/output_*.txt')); + } + + public function testLongWordsBlockWrapping() + { + $word = 'Lopadotemachoselachogaleokranioleipsanodrimhypotrimmatosilphioparaomelitokatakechymenokichlepikossyphophattoperisteralektryonoptekephalliokigklopeleiolagoiosiraiobaphetraganopterygon'; + $wordLength = strlen($word); + $maxLineLength = SymfonyStyle::MAX_LINE_LENGTH - 3; + + $this->command->setCode(function (InputInterface $input, OutputInterface $output) use ($word) { + $sfStyle = new SymfonyStyleWithForcedLineLength($input, $output); + $sfStyle->block($word, 'CUSTOM', 'fg=white;bg=blue', ' § ', false); + }); + + $this->tester->execute(array(), array('interactive' => false, 'decorated' => false)); + $expectedCount = (int) ceil($wordLength / ($maxLineLength)) + (int) ($wordLength > $maxLineLength - 5); + $this->assertSame($expectedCount, substr_count($this->tester->getDisplay(true), ' § ')); + } +} + +/** + * Use this class in tests to force the line length + * and ensure a consistent output for expectations. + */ +class SymfonyStyleWithForcedLineLength extends SymfonyStyle +{ + public function __construct(InputInterface $input, OutputInterface $output) + { + parent::__construct($input, $output); + + $ref = new \ReflectionProperty(get_parent_class($this), 'lineLength'); + $ref->setAccessible(true); + $ref->setValue($this, 120); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php new file mode 100644 index 0000000..a8389dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/ApplicationTesterTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\ApplicationTester; + +class ApplicationTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $application; + protected $tester; + + protected function setUp() + { + $this->application = new Application(); + $this->application->setAutoExit(false); + $this->application->register('foo') + ->addArgument('foo') + ->setCode(function ($input, $output) { $output->writeln('foo'); }) + ; + + $this->tester = new ApplicationTester($this->application); + $this->tester->run(array('command' => 'foo', 'foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->application = null; + $this->tester = null; + } + + public function testRun() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php new file mode 100644 index 0000000..b54c00e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/Tests/Tester/CommandTesterTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTesterTest extends \PHPUnit_Framework_TestCase +{ + protected $command; + protected $tester; + + protected function setUp() + { + $this->command = new Command('foo'); + $this->command->addArgument('command'); + $this->command->addArgument('foo'); + $this->command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $this->tester = new CommandTester($this->command); + $this->tester->execute(array('foo' => 'bar'), array('interactive' => false, 'decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE)); + } + + protected function tearDown() + { + $this->command = null; + $this->tester = null; + } + + public function testExecute() + { + $this->assertFalse($this->tester->getInput()->isInteractive(), '->execute() takes an interactive option'); + $this->assertFalse($this->tester->getOutput()->isDecorated(), '->execute() takes a decorated option'); + $this->assertEquals(Output::VERBOSITY_VERBOSE, $this->tester->getOutput()->getVerbosity(), '->execute() takes a verbosity option'); + } + + public function testGetInput() + { + $this->assertEquals('bar', $this->tester->getInput()->getArgument('foo'), '->getInput() returns the current input instance'); + } + + public function testGetOutput() + { + rewind($this->tester->getOutput()->getStream()); + $this->assertEquals('foo'.PHP_EOL, stream_get_contents($this->tester->getOutput()->getStream()), '->getOutput() returns the current output instance'); + } + + public function testGetDisplay() + { + $this->assertEquals('foo'.PHP_EOL, $this->tester->getDisplay(), '->getDisplay() returns the display of the last execution'); + } + + public function testGetStatusCode() + { + $this->assertSame(0, $this->tester->getStatusCode(), '->getStatusCode() returns the status code'); + } + + public function testCommandFromApplication() + { + $application = new Application(); + $application->setAutoExit(false); + + $command = new Command('foo'); + $command->setCode(function ($input, $output) { $output->writeln('foo'); }); + + $application->add($command); + + $tester = new CommandTester($application->find('foo')); + + // check that there is no need to pass the command name here + $this->assertEquals(0, $tester->execute(array())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Console/composer.json new file mode 100644 index 0000000..1a316e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/composer.json @@ -0,0 +1,44 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "psr/log": "~1.0" + }, + "suggest": { + "symfony/event-dispatcher": "", + "symfony/process": "", + "psr/log": "For using the console logger" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Console\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Console/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Console/phpunit.xml.dist new file mode 100644 index 0000000..ae0dcbe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Console/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CHANGELOG.md new file mode 100644 index 0000000..4061ff2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CHANGELOG.md @@ -0,0 +1,13 @@ +CHANGELOG +========= + +2.8.0 +----- + + * Added the `CssSelectorConverter` class as a non-static API for the component. + * Deprecated the `CssSelector` static API of the component. + +2.1.0 +----- + + * none diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CssSelectorConverter.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CssSelectorConverter.php new file mode 100644 index 0000000..e31ac19 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/CssSelectorConverter.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector; + +use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser; +use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser; +use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser; +use Symfony\Component\CssSelector\Parser\Shortcut\HashParser; +use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension; +use Symfony\Component\CssSelector\XPath\Translator; + +/** + * CssSelectorConverter is the main entry point of the component and can convert CSS + * selectors to XPath expressions. + * + * @author Christophe Coevoet + */ +class CssSelectorConverter +{ + private $translator; + + /** + * @param bool $html Whether HTML support should be enabled. Disable it for XML documents. + */ + public function __construct($html = true) + { + $this->translator = new Translator(); + + if ($html) { + $this->translator->registerExtension(new HtmlExtension($this->translator)); + } + + $this->translator + ->registerParserShortcut(new EmptyStringParser()) + ->registerParserShortcut(new ElementParser()) + ->registerParserShortcut(new ClassParser()) + ->registerParserShortcut(new HashParser()) + ; + } + + /** + * Translates a CSS expression to its XPath equivalent. + * + * Optionally, a prefix can be added to the resulting XPath + * expression with the $prefix parameter. + * + * @param string $cssExpr The CSS expression. + * @param string $prefix An optional prefix for the XPath expression. + * + * @return string + */ + public function toXPath($cssExpr, $prefix = 'descendant-or-self::') + { + return $this->translator->cssToXPath($cssExpr, $prefix); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ExceptionInterface.php new file mode 100644 index 0000000..e4c5ae1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ExceptionInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * Interface for exceptions. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ExpressionErrorException.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ExpressionErrorException.php new file mode 100644 index 0000000..fd5deea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ExpressionErrorException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class ExpressionErrorException extends ParseException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/InternalErrorException.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/InternalErrorException.php new file mode 100644 index 0000000..e60e5ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/InternalErrorException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class InternalErrorException extends ParseException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ParseException.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ParseException.php new file mode 100644 index 0000000..3b0b0ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/ParseException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Fabien Potencier + */ +class ParseException extends \Exception implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php new file mode 100644 index 0000000..418bc30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Exception/SyntaxErrorException.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Exception; + +use Symfony\Component\CssSelector\Parser\Token; + +/** + * ParseException is thrown when a CSS selector syntax is not valid. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + */ +class SyntaxErrorException extends ParseException +{ + /** + * @param string $expectedValue + * @param Token $foundToken + * + * @return SyntaxErrorException + */ + public static function unexpectedToken($expectedValue, Token $foundToken) + { + return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken)); + } + + /** + * @param string $pseudoElement + * @param string $unexpectedLocation + * + * @return SyntaxErrorException + */ + public static function pseudoElementFound($pseudoElement, $unexpectedLocation) + { + return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation)); + } + + /** + * @param int $position + * + * @return SyntaxErrorException + */ + public static function unclosedString($position) + { + return new self(sprintf('Unclosed/invalid string at %s.', $position)); + } + + /** + * @return SyntaxErrorException + */ + public static function nestedNot() + { + return new self('Got nested ::not().'); + } + + /** + * @return SyntaxErrorException + */ + public static function stringAsFunctionArgument() + { + return new self('String not allowed as function argument.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AbstractNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AbstractNode.php new file mode 100644 index 0000000..7477e91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AbstractNode.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Abstract base node class. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +abstract class AbstractNode implements NodeInterface +{ + /** + * @var string + */ + private $nodeName; + + /** + * @return string + */ + public function getNodeName() + { + if (null === $this->nodeName) { + $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', get_called_class()); + } + + return $this->nodeName; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AttributeNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AttributeNode.php new file mode 100644 index 0000000..af872b7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/AttributeNode.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "[| ]" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class AttributeNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $namespace; + + /** + * @var string + */ + private $attribute; + + /** + * @var string + */ + private $operator; + + /** + * @var string + */ + private $value; + + /** + * @param NodeInterface $selector + * @param string $namespace + * @param string $attribute + * @param string $operator + * @param string $value + */ + public function __construct(NodeInterface $selector, $namespace, $attribute, $operator, $value) + { + $this->selector = $selector; + $this->namespace = $namespace; + $this->attribute = $attribute; + $this->operator = $operator; + $this->value = $value; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return string + */ + public function getAttribute() + { + return $this->attribute; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->operator; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; + + return 'exists' === $this->operator + ? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute) + : sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ClassNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ClassNode.php new file mode 100644 index 0000000..f965e77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ClassNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "." node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ClassNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $name; + + /** + * @param NodeInterface $selector + * @param string $name + */ + public function __construct(NodeInterface $selector, $name) + { + $this->selector = $selector; + $this->name = $name; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php new file mode 100644 index 0000000..39f6599 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/CombinedSelectorNode.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a combined node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class CombinedSelectorNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $combinator; + + /** + * @var NodeInterface + */ + private $subSelector; + + /** + * @param NodeInterface $selector + * @param string $combinator + * @param NodeInterface $subSelector + */ + public function __construct(NodeInterface $selector, $combinator, NodeInterface $subSelector) + { + $this->selector = $selector; + $this->combinator = $combinator; + $this->subSelector = $subSelector; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getCombinator() + { + return $this->combinator; + } + + /** + * @return NodeInterface + */ + public function getSubSelector() + { + return $this->subSelector; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $combinator = ' ' === $this->combinator ? '' : $this->combinator; + + return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ElementNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ElementNode.php new file mode 100644 index 0000000..06e343e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/ElementNode.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "|" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ElementNode extends AbstractNode +{ + /** + * @var string|null + */ + private $namespace; + + /** + * @var string|null + */ + private $element; + + /** + * @param string|null $namespace + * @param string|null $element + */ + public function __construct($namespace = null, $element = null) + { + $this->namespace = $namespace; + $this->element = $element; + } + + /** + * @return null|string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * @return null|string + */ + public function getElement() + { + return $this->element; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return new Specificity(0, 0, $this->element ? 1 : 0); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $element = $this->element ?: '*'; + + return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/FunctionNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/FunctionNode.php new file mode 100644 index 0000000..612f348 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/FunctionNode.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +use Symfony\Component\CssSelector\Parser\Token; + +/** + * Represents a ":()" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class FunctionNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $name; + + /** + * @var Token[] + */ + private $arguments; + + /** + * @param NodeInterface $selector + * @param string $name + * @param Token[] $arguments + */ + public function __construct(NodeInterface $selector, $name, array $arguments = array()) + { + $this->selector = $selector; + $this->name = strtolower($name); + $this->arguments = $arguments; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return Token[] + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $arguments = implode(', ', array_map(function (Token $token) { + return "'".$token->getValue()."'"; + }, $this->arguments)); + + return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/HashNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/HashNode.php new file mode 100644 index 0000000..20db465 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/HashNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "#" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HashNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $id; + + /** + * @param NodeInterface $selector + * @param string $id + */ + public function __construct(NodeInterface $selector, $id) + { + $this->selector = $selector; + $this->id = $id; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NegationNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NegationNode.php new file mode 100644 index 0000000..4b5aa22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NegationNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a ":not()" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class NegationNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var NodeInterface + */ + private $subSelector; + + /** + * @param NodeInterface $selector + * @param NodeInterface $subSelector + */ + public function __construct(NodeInterface $selector, NodeInterface $subSelector) + { + $this->selector = $selector; + $this->subSelector = $subSelector; + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return NodeInterface + */ + public function getSubSelector() + { + return $this->subSelector; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NodeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NodeInterface.php new file mode 100644 index 0000000..d919e20 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/NodeInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Interface for nodes. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface NodeInterface +{ + /** + * Returns node's name. + * + * @return string + */ + public function getNodeName(); + + /** + * Returns node's specificity. + * + * @return Specificity + */ + public function getSpecificity(); + + /** + * Returns node's string representation. + * + * @return string + */ + public function __toString(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/PseudoNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/PseudoNode.php new file mode 100644 index 0000000..c23ddd5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/PseudoNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a ":" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class PseudoNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $selector; + + /** + * @var string + */ + private $identifier; + + /** + * @param NodeInterface $selector + * @param string $identifier + */ + public function __construct(NodeInterface $selector, $identifier) + { + $this->selector = $selector; + $this->identifier = strtolower($identifier); + } + + /** + * @return NodeInterface + */ + public function getSelector() + { + return $this->selector; + } + + /** + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/SelectorNode.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/SelectorNode.php new file mode 100644 index 0000000..729e091 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/SelectorNode.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a "(::|:)" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class SelectorNode extends AbstractNode +{ + /** + * @var NodeInterface + */ + private $tree; + + /** + * @var null|string + */ + private $pseudoElement; + + /** + * @param NodeInterface $tree + * @param null|string $pseudoElement + */ + public function __construct(NodeInterface $tree, $pseudoElement = null) + { + $this->tree = $tree; + $this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null; + } + + /** + * @return NodeInterface + */ + public function getTree() + { + return $this->tree; + } + + /** + * @return null|string + */ + public function getPseudoElement() + { + return $this->pseudoElement; + } + + /** + * {@inheritdoc} + */ + public function getSpecificity() + { + return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/Specificity.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/Specificity.php new file mode 100644 index 0000000..a24b4fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Node/Specificity.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a node specificity. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @see http://www.w3.org/TR/selectors/#specificity + * + * @author Jean-François Simon + * + * @internal + */ +class Specificity +{ + const A_FACTOR = 100; + const B_FACTOR = 10; + const C_FACTOR = 1; + + /** + * @var int + */ + private $a; + + /** + * @var int + */ + private $b; + + /** + * @var int + */ + private $c; + + /** + * Constructor. + * + * @param int $a + * @param int $b + * @param int $c + */ + public function __construct($a, $b, $c) + { + $this->a = $a; + $this->b = $b; + $this->c = $c; + } + + /** + * @param Specificity $specificity + * + * @return Specificity + */ + public function plus(Specificity $specificity) + { + return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); + } + + /** + * Returns global specificity value. + * + * @return int + */ + public function getValue() + { + return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR; + } + + /** + * Returns -1 if the object specificity is lower than the argument, + * 0 if they are equal, and 1 if the argument is lower. + * + * @param Specificity $specificity + * + * @return int + */ + public function compareTo(Specificity $specificity) + { + if ($this->a !== $specificity->a) { + return $this->a > $specificity->a ? 1 : -1; + } + + if ($this->b !== $specificity->b) { + return $this->b > $specificity->b ? 1 : -1; + } + + if ($this->c !== $specificity->c) { + return $this->c > $specificity->c ? 1 : -1; + } + + return 0; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php new file mode 100644 index 0000000..a29775c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/CommentHandler.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class CommentHandler implements HandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + if ('/*' !== $reader->getSubstring(2)) { + return false; + } + + $offset = $reader->getOffset('*/'); + + if (false === $offset) { + $reader->moveToEnd(); + } else { + $reader->moveForward($offset + 2); + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php new file mode 100644 index 0000000..a1297c8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HandlerInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector handler interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface HandlerInterface +{ + /** + * @param Reader $reader + * @param TokenStream $stream + * + * @return bool + */ + public function handle(Reader $reader, TokenStream $stream); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php new file mode 100644 index 0000000..f74bda5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/HashHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HashHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @var TokenizerEscaping + */ + private $escaping; + + /** + * @param TokenizerPatterns $patterns + * @param TokenizerEscaping $escaping + */ + public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) + { + $this->patterns = $patterns; + $this->escaping = $escaping; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern($this->patterns->getHashPattern()); + + if (!$match) { + return false; + } + + $value = $this->escaping->escapeUnicode($match[1]); + $stream->push(new Token(Token::TYPE_HASH, $value, $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php new file mode 100644 index 0000000..358c7c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/IdentifierHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class IdentifierHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @var TokenizerEscaping + */ + private $escaping; + + /** + * @param TokenizerPatterns $patterns + * @param TokenizerEscaping $escaping + */ + public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) + { + $this->patterns = $patterns; + $this->escaping = $escaping; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern($this->patterns->getIdentifierPattern()); + + if (!$match) { + return false; + } + + $value = $this->escaping->escapeUnicode($match[0]); + $stream->push(new Token(Token::TYPE_IDENTIFIER, $value, $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php new file mode 100644 index 0000000..4ea5c48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/NumberHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class NumberHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @param TokenizerPatterns $patterns + */ + public function __construct(TokenizerPatterns $patterns) + { + $this->patterns = $patterns; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern($this->patterns->getNumberPattern()); + + if (!$match) { + return false; + } + + $stream->push(new Token(Token::TYPE_NUMBER, $match[0], $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php new file mode 100644 index 0000000..4205296 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/StringHandler.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Exception\InternalErrorException; +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +/** + * CSS selector comment handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class StringHandler implements HandlerInterface +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @var TokenizerEscaping + */ + private $escaping; + + /** + * @param TokenizerPatterns $patterns + * @param TokenizerEscaping $escaping + */ + public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) + { + $this->patterns = $patterns; + $this->escaping = $escaping; + } + + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $quote = $reader->getSubstring(1); + + if (!in_array($quote, array("'", '"'))) { + return false; + } + + $reader->moveForward(1); + $match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote)); + + if (!$match) { + throw new InternalErrorException(sprintf('Should have found at least an empty match at %s.', $reader->getPosition())); + } + + // check unclosed strings + if (strlen($match[0]) === $reader->getRemainingLength()) { + throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); + } + + // check quotes pairs validity + if ($quote !== $reader->getSubstring(1, strlen($match[0]))) { + throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); + } + + $string = $this->escaping->escapeUnicodeAndNewLine($match[0]); + $stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition())); + $reader->moveForward(strlen($match[0]) + 1); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php new file mode 100644 index 0000000..4c2d335 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Handler/WhitespaceHandler.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector whitespace handler. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class WhitespaceHandler implements HandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(Reader $reader, TokenStream $stream) + { + $match = $reader->findPattern('~^[ \t\r\n\f]+~'); + + if (false === $match) { + return false; + } + + $stream->push(new Token(Token::TYPE_WHITESPACE, $match[0], $reader->getPosition())); + $reader->moveForward(strlen($match[0])); + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Parser.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Parser.php new file mode 100644 index 0000000..f94aea3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Parser.php @@ -0,0 +1,401 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Node; +use Symfony\Component\CssSelector\Parser\Tokenizer\Tokenizer; + +/** + * CSS selector parser. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Parser implements ParserInterface +{ + /** + * @var Tokenizer + */ + private $tokenizer; + + /** + * Constructor. + * + * @param null|Tokenizer $tokenizer + */ + public function __construct(Tokenizer $tokenizer = null) + { + $this->tokenizer = $tokenizer ?: new Tokenizer(); + } + + /** + * {@inheritdoc} + */ + public function parse($source) + { + $reader = new Reader($source); + $stream = $this->tokenizer->tokenize($reader); + + return $this->parseSelectorList($stream); + } + + /** + * Parses the arguments for ":nth-child()" and friends. + * + * @param Token[] $tokens + * + * @throws SyntaxErrorException + * + * @return array + */ + public static function parseSeries(array $tokens) + { + foreach ($tokens as $token) { + if ($token->isString()) { + throw SyntaxErrorException::stringAsFunctionArgument(); + } + } + + $joined = trim(implode('', array_map(function (Token $token) { + return $token->getValue(); + }, $tokens))); + + $int = function ($string) { + if (!is_numeric($string)) { + throw SyntaxErrorException::stringAsFunctionArgument(); + } + + return (int) $string; + }; + + switch (true) { + case 'odd' === $joined: + return array(2, 1); + case 'even' === $joined: + return array(2, 0); + case 'n' === $joined: + return array(1, 0); + case false === strpos($joined, 'n'): + return array(0, $int($joined)); + } + + $split = explode('n', $joined); + $first = isset($split[0]) ? $split[0] : null; + + return array( + $first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1, + isset($split[1]) && $split[1] ? $int($split[1]) : 0, + ); + } + + /** + * Parses selector nodes. + * + * @param TokenStream $stream + * + * @return array + */ + private function parseSelectorList(TokenStream $stream) + { + $stream->skipWhitespace(); + $selectors = array(); + + while (true) { + $selectors[] = $this->parserSelectorNode($stream); + + if ($stream->getPeek()->isDelimiter(array(','))) { + $stream->getNext(); + $stream->skipWhitespace(); + } else { + break; + } + } + + return $selectors; + } + + /** + * Parses next selector or combined node. + * + * @param TokenStream $stream + * + * @throws SyntaxErrorException + * + * @return Node\SelectorNode + */ + private function parserSelectorNode(TokenStream $stream) + { + list($result, $pseudoElement) = $this->parseSimpleSelector($stream); + + while (true) { + $stream->skipWhitespace(); + $peek = $stream->getPeek(); + + if ($peek->isFileEnd() || $peek->isDelimiter(array(','))) { + break; + } + + if (null !== $pseudoElement) { + throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); + } + + if ($peek->isDelimiter(array('+', '>', '~'))) { + $combinator = $stream->getNext()->getValue(); + $stream->skipWhitespace(); + } else { + $combinator = ' '; + } + + list($nextSelector, $pseudoElement) = $this->parseSimpleSelector($stream); + $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector); + } + + return new Node\SelectorNode($result, $pseudoElement); + } + + /** + * Parses next simple node (hash, class, pseudo, negation). + * + * @param TokenStream $stream + * @param bool $insideNegation + * + * @throws SyntaxErrorException + * + * @return array + */ + private function parseSimpleSelector(TokenStream $stream, $insideNegation = false) + { + $stream->skipWhitespace(); + + $selectorStart = count($stream->getUsed()); + $result = $this->parseElementNode($stream); + $pseudoElement = null; + + while (true) { + $peek = $stream->getPeek(); + if ($peek->isWhitespace() + || $peek->isFileEnd() + || $peek->isDelimiter(array(',', '+', '>', '~')) + || ($insideNegation && $peek->isDelimiter(array(')'))) + ) { + break; + } + + if (null !== $pseudoElement) { + throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); + } + + if ($peek->isHash()) { + $result = new Node\HashNode($result, $stream->getNext()->getValue()); + } elseif ($peek->isDelimiter(array('.'))) { + $stream->getNext(); + $result = new Node\ClassNode($result, $stream->getNextIdentifier()); + } elseif ($peek->isDelimiter(array('['))) { + $stream->getNext(); + $result = $this->parseAttributeNode($result, $stream); + } elseif ($peek->isDelimiter(array(':'))) { + $stream->getNext(); + + if ($stream->getPeek()->isDelimiter(array(':'))) { + $stream->getNext(); + $pseudoElement = $stream->getNextIdentifier(); + + continue; + } + + $identifier = $stream->getNextIdentifier(); + if (in_array(strtolower($identifier), array('first-line', 'first-letter', 'before', 'after'))) { + // Special case: CSS 2.1 pseudo-elements can have a single ':'. + // Any new pseudo-element must have two. + $pseudoElement = $identifier; + + continue; + } + + if (!$stream->getPeek()->isDelimiter(array('('))) { + $result = new Node\PseudoNode($result, $identifier); + + continue; + } + + $stream->getNext(); + $stream->skipWhitespace(); + + if ('not' === strtolower($identifier)) { + if ($insideNegation) { + throw SyntaxErrorException::nestedNot(); + } + + list($argument, $argumentPseudoElement) = $this->parseSimpleSelector($stream, true); + $next = $stream->getNext(); + + if (null !== $argumentPseudoElement) { + throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()'); + } + + if (!$next->isDelimiter(array(')'))) { + throw SyntaxErrorException::unexpectedToken('")"', $next); + } + + $result = new Node\NegationNode($result, $argument); + } else { + $arguments = array(); + $next = null; + + while (true) { + $stream->skipWhitespace(); + $next = $stream->getNext(); + + if ($next->isIdentifier() + || $next->isString() + || $next->isNumber() + || $next->isDelimiter(array('+', '-')) + ) { + $arguments[] = $next; + } elseif ($next->isDelimiter(array(')'))) { + break; + } else { + throw SyntaxErrorException::unexpectedToken('an argument', $next); + } + } + + if (empty($arguments)) { + throw SyntaxErrorException::unexpectedToken('at least one argument', $next); + } + + $result = new Node\FunctionNode($result, $identifier, $arguments); + } + } else { + throw SyntaxErrorException::unexpectedToken('selector', $peek); + } + } + + if (count($stream->getUsed()) === $selectorStart) { + throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek()); + } + + return array($result, $pseudoElement); + } + + /** + * Parses next element node. + * + * @param TokenStream $stream + * + * @return Node\ElementNode + */ + private function parseElementNode(TokenStream $stream) + { + $peek = $stream->getPeek(); + + if ($peek->isIdentifier() || $peek->isDelimiter(array('*'))) { + if ($peek->isIdentifier()) { + $namespace = $stream->getNext()->getValue(); + } else { + $stream->getNext(); + $namespace = null; + } + + if ($stream->getPeek()->isDelimiter(array('|'))) { + $stream->getNext(); + $element = $stream->getNextIdentifierOrStar(); + } else { + $element = $namespace; + $namespace = null; + } + } else { + $element = $namespace = null; + } + + return new Node\ElementNode($namespace, $element); + } + + /** + * Parses next attribute node. + * + * @param Node\NodeInterface $selector + * @param TokenStream $stream + * + * @throws SyntaxErrorException + * + * @return Node\AttributeNode + */ + private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream) + { + $stream->skipWhitespace(); + $attribute = $stream->getNextIdentifierOrStar(); + + if (null === $attribute && !$stream->getPeek()->isDelimiter(array('|'))) { + throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek()); + } + + if ($stream->getPeek()->isDelimiter(array('|'))) { + $stream->getNext(); + + if ($stream->getPeek()->isDelimiter(array('='))) { + $namespace = null; + $stream->getNext(); + $operator = '|='; + } else { + $namespace = $attribute; + $attribute = $stream->getNextIdentifier(); + $operator = null; + } + } else { + $namespace = $operator = null; + } + + if (null === $operator) { + $stream->skipWhitespace(); + $next = $stream->getNext(); + + if ($next->isDelimiter(array(']'))) { + return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null); + } elseif ($next->isDelimiter(array('='))) { + $operator = '='; + } elseif ($next->isDelimiter(array('^', '$', '*', '~', '|', '!')) + && $stream->getPeek()->isDelimiter(array('=')) + ) { + $operator = $next->getValue().'='; + $stream->getNext(); + } else { + throw SyntaxErrorException::unexpectedToken('operator', $next); + } + } + + $stream->skipWhitespace(); + $value = $stream->getNext(); + + if ($value->isNumber()) { + // if the value is a number, it's casted into a string + $value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition()); + } + + if (!($value->isIdentifier() || $value->isString())) { + throw SyntaxErrorException::unexpectedToken('string or identifier', $value); + } + + $stream->skipWhitespace(); + $next = $stream->getNext(); + + if (!$next->isDelimiter(array(']'))) { + throw SyntaxErrorException::unexpectedToken('"]"', $next); + } + + return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/ParserInterface.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/ParserInterface.php new file mode 100644 index 0000000..c5af203 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/ParserInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +use Symfony\Component\CssSelector\Node\SelectorNode; + +/** + * CSS selector parser interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface ParserInterface +{ + /** + * Parses given selector source into an array of tokens. + * + * @param string $source + * + * @return SelectorNode[] + */ + public function parse($source); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Reader.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Reader.php new file mode 100644 index 0000000..4113636 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Reader.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +/** + * CSS selector reader. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Reader +{ + /** + * @var string + */ + private $source; + + /** + * @var int + */ + private $length; + + /** + * @var int + */ + private $position = 0; + + /** + * @param string $source + */ + public function __construct($source) + { + $this->source = $source; + $this->length = strlen($source); + } + + /** + * @return bool + */ + public function isEOF() + { + return $this->position >= $this->length; + } + + /** + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * @return int + */ + public function getRemainingLength() + { + return $this->length - $this->position; + } + + /** + * @param int $length + * @param int $offset + * + * @return string + */ + public function getSubstring($length, $offset = 0) + { + return substr($this->source, $this->position + $offset, $length); + } + + /** + * @param string $string + * + * @return int + */ + public function getOffset($string) + { + $position = strpos($this->source, $string, $this->position); + + return false === $position ? false : $position - $this->position; + } + + /** + * @param string $pattern + * + * @return bool + */ + public function findPattern($pattern) + { + $source = substr($this->source, $this->position); + + if (preg_match($pattern, $source, $matches)) { + return $matches; + } + + return false; + } + + /** + * @param int $length + */ + public function moveForward($length) + { + $this->position += $length; + } + + /** + */ + public function moveToEnd() + { + $this->position = $this->length; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php new file mode 100644 index 0000000..c513de5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ClassParser.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector class parser shortcut. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ClassParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, optional element, and required class + // $source = 'test|input.ab6bd_field'; + // $matches = array (size=4) + // 0 => string 'test|input.ab6bd_field' (length=22) + // 1 => string 'test' (length=4) + // 2 => string 'input' (length=5) + // 3 => string 'ab6bd_field' (length=11) + if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+\.([\w-]++)$/i', trim($source), $matches)) { + return array( + new SelectorNode(new ClassNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])), + ); + } + + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.php new file mode 100644 index 0000000..c29f5e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/ElementParser.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\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector element parser shortcut. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class ElementParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, required element or `*` + // $source = 'testns|testel'; + // $matches = array (size=3) + // 0 => string 'testns|testel' (length=13) + // 1 => string 'testns' (length=6) + // 2 => string 'testel' (length=6) + if (preg_match('/^(?:([a-z]++)\|)?([\w-]++|\*)$/i', trim($source), $matches)) { + return array(new SelectorNode(new ElementNode($matches[1] ?: null, $matches[2]))); + } + + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php new file mode 100644 index 0000000..016cf0a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/EmptyStringParser.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector class parser shortcut. + * + * This shortcut ensure compatibility with previous version. + * - The parser fails to parse an empty string. + * - In the previous version, an empty string matches each tags. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class EmptyStringParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an empty string + if ($source == '') { + return array(new SelectorNode(new ElementNode(null, '*'))); + } + + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php new file mode 100644 index 0000000..3f3883b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Shortcut/HashParser.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\HashNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * CSS selector hash parser shortcut. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HashParser implements ParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($source) + { + // Matches an optional namespace, optional element, and required id + // $source = 'test|input#ab6bd_field'; + // $matches = array (size=4) + // 0 => string 'test|input#ab6bd_field' (length=22) + // 1 => string 'test' (length=4) + // 2 => string 'input' (length=5) + // 3 => string 'ab6bd_field' (length=11) + if (preg_match('/^(?:([a-z]++)\|)?+([\w-]++|\*)?+#([\w-]++)$/i', trim($source), $matches)) { + return array( + new SelectorNode(new HashNode(new ElementNode($matches[1] ?: null, $matches[2] ?: null), $matches[3])), + ); + } + + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Token.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Token.php new file mode 100644 index 0000000..68fac59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Token.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +/** + * CSS selector token. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Token +{ + const TYPE_FILE_END = 'eof'; + const TYPE_DELIMITER = 'delimiter'; + const TYPE_WHITESPACE = 'whitespace'; + const TYPE_IDENTIFIER = 'identifier'; + const TYPE_HASH = 'hash'; + const TYPE_NUMBER = 'number'; + const TYPE_STRING = 'string'; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $value; + + /** + * @var int + */ + private $position; + + /** + * @param int $type + * @param string $value + * @param int $position + */ + public function __construct($type, $value, $position) + { + $this->type = $type; + $this->value = $value; + $this->position = $position; + } + + /** + * @return int + */ + public function getType() + { + return $this->type; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * @return bool + */ + public function isFileEnd() + { + return self::TYPE_FILE_END === $this->type; + } + + /** + * @param array $values + * + * @return bool + */ + public function isDelimiter(array $values = array()) + { + if (self::TYPE_DELIMITER !== $this->type) { + return false; + } + + if (empty($values)) { + return true; + } + + return in_array($this->value, $values); + } + + /** + * @return bool + */ + public function isWhitespace() + { + return self::TYPE_WHITESPACE === $this->type; + } + + /** + * @return bool + */ + public function isIdentifier() + { + return self::TYPE_IDENTIFIER === $this->type; + } + + /** + * @return bool + */ + public function isHash() + { + return self::TYPE_HASH === $this->type; + } + + /** + * @return bool + */ + public function isNumber() + { + return self::TYPE_NUMBER === $this->type; + } + + /** + * @return bool + */ + public function isString() + { + return self::TYPE_STRING === $this->type; + } + + /** + * @return string + */ + public function __toString() + { + if ($this->value) { + return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); + } + + return sprintf('<%s at %s>', $this->type, $this->position); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/TokenStream.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/TokenStream.php new file mode 100644 index 0000000..1ec727f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/TokenStream.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser; + +use Symfony\Component\CssSelector\Exception\InternalErrorException; +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; + +/** + * CSS selector token stream. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class TokenStream +{ + /** + * @var Token[] + */ + private $tokens = array(); + + /** + * @var bool + */ + private $frozen = false; + + /** + * @var Token[] + */ + private $used = array(); + + /** + * @var int + */ + private $cursor = 0; + + /** + * @var Token|null + */ + private $peeked = null; + + /** + * @var bool + */ + private $peeking = false; + + /** + * Pushes a token. + * + * @param Token $token + * + * @return TokenStream + */ + public function push(Token $token) + { + $this->tokens[] = $token; + + return $this; + } + + /** + * Freezes stream. + * + * @return TokenStream + */ + public function freeze() + { + $this->frozen = true; + + return $this; + } + + /** + * Returns next token. + * + * @throws InternalErrorException If there is no more token + * + * @return Token + */ + public function getNext() + { + if ($this->peeking) { + $this->peeking = false; + $this->used[] = $this->peeked; + + return $this->peeked; + } + + if (!isset($this->tokens[$this->cursor])) { + throw new InternalErrorException('Unexpected token stream end.'); + } + + return $this->tokens[$this->cursor++]; + } + + /** + * Returns peeked token. + * + * @return Token + */ + public function getPeek() + { + if (!$this->peeking) { + $this->peeked = $this->getNext(); + $this->peeking = true; + } + + return $this->peeked; + } + + /** + * Returns used tokens. + * + * @return Token[] + */ + public function getUsed() + { + return $this->used; + } + + /** + * Returns nex identifier token. + * + * @throws SyntaxErrorException If next token is not an identifier + * + * @return string The identifier token value + */ + public function getNextIdentifier() + { + $next = $this->getNext(); + + if (!$next->isIdentifier()) { + throw SyntaxErrorException::unexpectedToken('identifier', $next); + } + + return $next->getValue(); + } + + /** + * Returns nex identifier or star delimiter token. + * + * @throws SyntaxErrorException If next token is not an identifier or a star delimiter + * + * @return null|string The identifier token value or null if star found + */ + public function getNextIdentifierOrStar() + { + $next = $this->getNext(); + + if ($next->isIdentifier()) { + return $next->getValue(); + } + + if ($next->isDelimiter(array('*'))) { + return; + } + + throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next); + } + + /** + * Skips next whitespace if any. + */ + public function skipWhitespace() + { + $peek = $this->getPeek(); + + if ($peek->isWhitespace()) { + $this->getNext(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php new file mode 100644 index 0000000..aa9fc50 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/Tokenizer.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Tokenizer; + +use Symfony\Component\CssSelector\Parser\Handler; +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * CSS selector tokenizer. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Tokenizer +{ + /** + * @var Handler\HandlerInterface[] + */ + private $handlers; + + /** + * Constructor. + */ + public function __construct() + { + $patterns = new TokenizerPatterns(); + $escaping = new TokenizerEscaping($patterns); + + $this->handlers = array( + new Handler\WhitespaceHandler(), + new Handler\IdentifierHandler($patterns, $escaping), + new Handler\HashHandler($patterns, $escaping), + new Handler\StringHandler($patterns, $escaping), + new Handler\NumberHandler($patterns), + new Handler\CommentHandler(), + ); + } + + /** + * Tokenize selector source code. + * + * @param Reader $reader + * + * @return TokenStream + */ + public function tokenize(Reader $reader) + { + $stream = new TokenStream(); + + while (!$reader->isEOF()) { + foreach ($this->handlers as $handler) { + if ($handler->handle($reader, $stream)) { + continue 2; + } + } + + $stream->push(new Token(Token::TYPE_DELIMITER, $reader->getSubstring(1), $reader->getPosition())); + $reader->moveForward(1); + } + + return $stream + ->push(new Token(Token::TYPE_FILE_END, null, $reader->getPosition())) + ->freeze(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php new file mode 100644 index 0000000..af4c31e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Tokenizer; + +/** + * CSS selector tokenizer escaping applier. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class TokenizerEscaping +{ + /** + * @var TokenizerPatterns + */ + private $patterns; + + /** + * @param TokenizerPatterns $patterns + */ + public function __construct(TokenizerPatterns $patterns) + { + $this->patterns = $patterns; + } + + /** + * @param string $value + * + * @return string + */ + public function escapeUnicode($value) + { + $value = $this->replaceUnicodeSequences($value); + + return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value); + } + + /** + * @param string $value + * + * @return string + */ + public function escapeUnicodeAndNewLine($value) + { + $value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value); + + return $this->escapeUnicode($value); + } + + /** + * @param string $value + * + * @return string + */ + private function replaceUnicodeSequences($value) + { + return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) { + $c = hexdec($match[1]); + + if (0x80 > $c %= 0x200000) { + return chr($c); + } + if (0x800 > $c) { + return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); + } + if (0x10000 > $c) { + return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); + } + }, $value); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php new file mode 100644 index 0000000..5b071cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerPatterns.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Parser\Tokenizer; + +/** + * CSS selector tokenizer patterns builder. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class TokenizerPatterns +{ + /** + * @var string + */ + private $unicodeEscapePattern; + + /** + * @var string + */ + private $simpleEscapePattern; + + /** + * @var string + */ + private $newLineEscapePattern; + + /** + * @var string + */ + private $escapePattern; + + /** + * @var string + */ + private $stringEscapePattern; + + /** + * @var string + */ + private $nonAsciiPattern; + + /** + * @var string + */ + private $nmCharPattern; + + /** + * @var string + */ + private $nmStartPattern; + + /** + * @var string + */ + private $identifierPattern; + + /** + * @var string + */ + private $hashPattern; + + /** + * @var string + */ + private $numberPattern; + + /** + * @var string + */ + private $quotedStringPattern; + + /** + * Constructor. + */ + public function __construct() + { + $this->unicodeEscapePattern = '\\\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?'; + $this->simpleEscapePattern = '\\\\(.)'; + $this->newLineEscapePattern = '\\\\(?:\n|\r\n|\r|\f)'; + $this->escapePattern = $this->unicodeEscapePattern.'|\\\\[^\n\r\f0-9a-f]'; + $this->stringEscapePattern = $this->newLineEscapePattern.'|'.$this->escapePattern; + $this->nonAsciiPattern = '[^\x00-\x7F]'; + $this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; + $this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; + $this->identifierPattern = '(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*'; + $this->hashPattern = '#((?:'.$this->nmCharPattern.')+)'; + $this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)'; + $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*'; + } + + /** + * @return string + */ + public function getNewLineEscapePattern() + { + return '~^'.$this->newLineEscapePattern.'~'; + } + + /** + * @return string + */ + public function getSimpleEscapePattern() + { + return '~^'.$this->simpleEscapePattern.'~'; + } + + /** + * @return string + */ + public function getUnicodeEscapePattern() + { + return '~^'.$this->unicodeEscapePattern.'~i'; + } + + /** + * @return string + */ + public function getIdentifierPattern() + { + return '~^'.$this->identifierPattern.'~i'; + } + + /** + * @return string + */ + public function getHashPattern() + { + return '~^'.$this->hashPattern.'~i'; + } + + /** + * @return string + */ + public function getNumberPattern() + { + return '~^'.$this->numberPattern.'~'; + } + + /** + * @param string $quote + * + * @return string + */ + public function getQuotedStringPattern($quote) + { + return '~^'.sprintf($this->quotedStringPattern, $quote).'~i'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/README.md b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/README.md new file mode 100644 index 0000000..eef2885 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/README.md @@ -0,0 +1,84 @@ +CssSelector Component +===================== + +CssSelector converts CSS selectors to XPath expressions. + +The component only goal is to convert CSS selectors to their XPath +equivalents: + +```php +use Symfony\Component\CssSelector\CssSelectorConverter; + +$converter = new CssSelectorConverter(); +print $converter->toXPath('div.item > h4 > a'); +``` + +HTML and XML are different +-------------------------- + +The `CssSelector` component comes with an `HTML` extension which is enabled by +default. If you need to use this component with `XML` documents, you have to +disable this `HTML` extension. That's because, `HTML` tag & attribute names are +always lower-cased, but case-sensitive in `XML`: + +```php +// disable `HTML` extension: +$converter = new CssSelectorConverter(false); +``` + +When the `HTML` extension is enabled, tag names are lower-cased, attribute +names are lower-cased, the following extra pseudo-classes are supported: +`checked`, `link`, `disabled`, `enabled`, `selected`, `invalid`, `hover`, +`visited`, and the `lang()` function is also added. + +Resources +--------- + +This component is a port of the Python cssselect library +[v0.7.1](https://github.com/SimonSapin/cssselect/releases/tag/v0.7.1), +which is distributed under the BSD license. + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/CssSelector/ + $ composer install + $ phpunit + +License +------- + +This component is a port of the Python cssselect library, +which is copyright Ian Bicking, https://github.com/SimonSapin/cssselect. + +Copyright (c) 2007-2012 Ian Bicking and contributors. See AUTHORS +for more details. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the +distribution. + +3. Neither the name of Ian Bicking nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IAN BICKING OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php new file mode 100644 index 0000000..624909b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/CssSelectorConverterTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests; + +use Symfony\Component\CssSelector\CssSelectorConverter; + +class CssSelectorConverterTest extends \PHPUnit_Framework_TestCase +{ + public function testCssToXPath() + { + $converter = new CssSelectorConverter(); + + $this->assertEquals('descendant-or-self::*', $converter->toXPath('')); + $this->assertEquals('descendant-or-self::h1', $converter->toXPath('h1')); + $this->assertEquals("descendant-or-self::h1[@id = 'foo']", $converter->toXPath('h1#foo')); + $this->assertEquals("descendant-or-self::h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]", $converter->toXPath('h1.foo')); + $this->assertEquals('descendant-or-self::foo:h1', $converter->toXPath('foo|h1')); + $this->assertEquals('descendant-or-self::h1', $converter->toXPath('H1')); + } + + public function testCssToXPathXml() + { + $converter = new CssSelectorConverter(false); + + $this->assertEquals('descendant-or-self::H1', $converter->toXPath('H1')); + } + + /** + * @expectedException \Symfony\Component\CssSelector\Exception\ParseException + * @expectedExceptionMessage Expected identifier, but found. + */ + public function testParseExceptions() + { + $converter = new CssSelectorConverter(); + $converter->toXPath('h1:'); + } + + /** @dataProvider getCssToXPathWithoutPrefixTestData */ + public function testCssToXPathWithoutPrefix($css, $xpath) + { + $converter = new CssSelectorConverter(); + + $this->assertEquals($xpath, $converter->toXPath($css, ''), '->parse() parses an input string and returns a node'); + } + + public function getCssToXPathWithoutPrefixTestData() + { + return array( + array('h1', 'h1'), + array('foo|h1', 'foo:h1'), + array('h1, h2, h3', 'h1 | h2 | h3'), + array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"), + array('h1 > p', 'h1/p'), + array('h1#foo', "h1[@id = 'foo']"), + array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + array('h1[class*="foo bar"]', "h1[@class and contains(@class, 'foo bar')]"), + array('h1[foo|class*="foo bar"]', "h1[@foo:class and contains(@foo:class, 'foo bar')]"), + array('h1[class]', 'h1[@class]'), + array('h1 .foo', "h1/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + array('h1 #foo', "h1/descendant-or-self::*/*[@id = 'foo']"), + array('h1 [class*=foo]', "h1/descendant-or-self::*/*[@class and contains(@class, 'foo')]"), + array('div>.foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + array('div > .foo', "div/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/AbstractNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/AbstractNodeTest.php new file mode 100644 index 0000000..16a3a34 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/AbstractNodeTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\NodeInterface; + +abstract class AbstractNodeTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getToStringConversionTestData */ + public function testToStringConversion(NodeInterface $node, $representation) + { + $this->assertEquals($representation, (string) $node); + } + + /** @dataProvider getSpecificityValueTestData */ + public function testSpecificityValue(NodeInterface $node, $value) + { + $this->assertEquals($value, $node->getSpecificity()->getValue()); + } + + abstract public function getToStringConversionTestData(); + abstract public function getSpecificityValueTestData(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/AttributeNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/AttributeNodeTest.php new file mode 100644 index 0000000..1fd090f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/AttributeNodeTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\AttributeNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class AttributeNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 'Attribute[Element[*][attribute]]'), + array(new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), "Attribute[Element[*][attribute $= 'value']]"), + array(new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), "Attribute[Element[*][namespace|attribute $= 'value']]"), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new AttributeNode(new ElementNode(), null, 'attribute', 'exists', null), 10), + array(new AttributeNode(new ElementNode(null, 'element'), null, 'attribute', 'exists', null), 11), + array(new AttributeNode(new ElementNode(), null, 'attribute', '$=', 'value'), 10), + array(new AttributeNode(new ElementNode(), 'namespace', 'attribute', '$=', 'value'), 10), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/ClassNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/ClassNodeTest.php new file mode 100644 index 0000000..e0ab45a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/ClassNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class ClassNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new ClassNode(new ElementNode(), 'class'), 'Class[Element[*].class]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new ClassNode(new ElementNode(), 'class'), 10), + array(new ClassNode(new ElementNode(null, 'element'), 'class'), 11), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/CombinedSelectorNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/CombinedSelectorNodeTest.php new file mode 100644 index 0000000..9547298 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/CombinedSelectorNodeTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\CombinedSelectorNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class CombinedSelectorNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 'CombinedSelector[Element[*] > Element[*]]'), + array(new CombinedSelectorNode(new ElementNode(), ' ', new ElementNode()), 'CombinedSelector[Element[*] Element[*]]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new CombinedSelectorNode(new ElementNode(), '>', new ElementNode()), 0), + array(new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode()), 1), + array(new CombinedSelectorNode(new ElementNode(null, 'element'), '>', new ElementNode(null, 'element')), 2), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/ElementNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/ElementNodeTest.php new file mode 100644 index 0000000..6d24789 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/ElementNodeTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; + +class ElementNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new ElementNode(), 'Element[*]'), + array(new ElementNode(null, 'element'), 'Element[element]'), + array(new ElementNode('namespace', 'element'), 'Element[namespace|element]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new ElementNode(), 0), + array(new ElementNode(null, 'element'), 1), + array(new ElementNode('namespace', 'element'), 1), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php new file mode 100644 index 0000000..ee3ce51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.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\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Parser\Token; + +class FunctionNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new FunctionNode(new ElementNode(), 'function'), 'Function[Element[*]:function()]'), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_IDENTIFIER, 'value', 0), + )), "Function[Element[*]:function(['value'])]"), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_STRING, 'value1', 0), + new Token(Token::TYPE_NUMBER, 'value2', 0), + )), "Function[Element[*]:function(['value1', 'value2'])]"), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new FunctionNode(new ElementNode(), 'function'), 10), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_IDENTIFIER, 'value', 0), + )), 10), + array(new FunctionNode(new ElementNode(), 'function', array( + new Token(Token::TYPE_STRING, 'value1', 0), + new Token(Token::TYPE_NUMBER, 'value2', 0), + )), 10), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/HashNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/HashNodeTest.php new file mode 100644 index 0000000..8554b22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/HashNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\HashNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class HashNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new HashNode(new ElementNode(), 'id'), 'Hash[Element[*]#id]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new HashNode(new ElementNode(), 'id'), 100), + array(new HashNode(new ElementNode(null, 'id'), 'class'), 101), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/NegationNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/NegationNodeTest.php new file mode 100644 index 0000000..edf4552 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/NegationNodeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\NegationNode; +use Symfony\Component\CssSelector\Node\ElementNode; + +class NegationNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 'Negation[Element[*]:not(Class[Element[*].class])]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new NegationNode(new ElementNode(), new ClassNode(new ElementNode(), 'class')), 10), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/PseudoNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/PseudoNodeTest.php new file mode 100644 index 0000000..bc57813 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/PseudoNodeTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\PseudoNode; + +class PseudoNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new PseudoNode(new ElementNode(), 'pseudo'), 'Pseudo[Element[*]:pseudo]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new PseudoNode(new ElementNode(), 'pseudo'), 10), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SelectorNodeTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SelectorNodeTest.php new file mode 100644 index 0000000..5badf71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SelectorNodeTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\SelectorNode; + +class SelectorNodeTest extends AbstractNodeTest +{ + public function getToStringConversionTestData() + { + return array( + array(new SelectorNode(new ElementNode()), 'Selector[Element[*]]'), + array(new SelectorNode(new ElementNode(), 'pseudo'), 'Selector[Element[*]::pseudo]'), + ); + } + + public function getSpecificityValueTestData() + { + return array( + array(new SelectorNode(new ElementNode()), 0), + array(new SelectorNode(new ElementNode(), 'pseudo'), 1), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SpecificityTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SpecificityTest.php new file mode 100644 index 0000000..c34fe5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Node/SpecificityTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\Specificity; + +class SpecificityTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getValueTestData */ + public function testValue(Specificity $specificity, $value) + { + $this->assertEquals($value, $specificity->getValue()); + } + + /** @dataProvider getValueTestData */ + public function testPlusValue(Specificity $specificity, $value) + { + $this->assertEquals($value + 123, $specificity->plus(new Specificity(1, 2, 3))->getValue()); + } + + public function getValueTestData() + { + return array( + array(new Specificity(0, 0, 0), 0), + array(new Specificity(0, 0, 2), 2), + array(new Specificity(0, 3, 0), 30), + array(new Specificity(4, 0, 0), 400), + array(new Specificity(4, 3, 2), 432), + ); + } + + /** @dataProvider getCompareTestData */ + public function testCompareTo(Specificity $a, Specificity $b, $result) + { + $this->assertEquals($result, $a->compareTo($b)); + } + + public function getCompareTestData() + { + return array( + array(new Specificity(0, 0, 0), new Specificity(0, 0, 0), 0), + array(new Specificity(0, 0, 1), new Specificity(0, 0, 1), 0), + array(new Specificity(0, 0, 2), new Specificity(0, 0, 1), 1), + array(new Specificity(0, 0, 2), new Specificity(0, 0, 3), -1), + array(new Specificity(0, 4, 0), new Specificity(0, 4, 0), 0), + array(new Specificity(0, 6, 0), new Specificity(0, 5, 11), 1), + array(new Specificity(0, 7, 0), new Specificity(0, 8, 0), -1), + array(new Specificity(9, 0, 0), new Specificity(9, 0, 0), 0), + array(new Specificity(11, 0, 0), new Specificity(10, 11, 0), 1), + array(new Specificity(12, 11, 0), new Specificity(13, 0, 0), -1), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/AbstractHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/AbstractHandlerTest.php new file mode 100644 index 0000000..a06dca0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/AbstractHandlerTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +/** + * @author Jean-François Simon + */ +abstract class AbstractHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getHandleValueTestData */ + public function testHandleValue($value, Token $expectedToken, $remainingContent) + { + $reader = new Reader($value); + $stream = new TokenStream(); + + $this->assertTrue($this->generateHandler()->handle($reader, $stream)); + $this->assertEquals($expectedToken, $stream->getNext()); + $this->assertRemainingContent($reader, $remainingContent); + } + + /** @dataProvider getDontHandleValueTestData */ + public function testDontHandleValue($value) + { + $reader = new Reader($value); + $stream = new TokenStream(); + + $this->assertFalse($this->generateHandler()->handle($reader, $stream)); + $this->assertStreamEmpty($stream); + $this->assertRemainingContent($reader, $value); + } + + abstract public function getHandleValueTestData(); + abstract public function getDontHandleValueTestData(); + abstract protected function generateHandler(); + + protected function assertStreamEmpty(TokenStream $stream) + { + $property = new \ReflectionProperty($stream, 'tokens'); + $property->setAccessible(true); + + $this->assertEquals(array(), $property->getValue($stream)); + } + + protected function assertRemainingContent(Reader $reader, $remainingContent) + { + if ('' === $remainingContent) { + $this->assertEquals(0, $reader->getRemainingLength()); + $this->assertTrue($reader->isEOF()); + } else { + $this->assertEquals(strlen($remainingContent), $reader->getRemainingLength()); + $this->assertEquals(0, $reader->getOffset($remainingContent)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/CommentHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/CommentHandlerTest.php new file mode 100644 index 0000000..3961bf7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/CommentHandlerTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\CommentHandler; +use Symfony\Component\CssSelector\Parser\Reader; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +class CommentHandlerTest extends AbstractHandlerTest +{ + /** @dataProvider getHandleValueTestData */ + public function testHandleValue($value, Token $unusedArgument, $remainingContent) + { + $reader = new Reader($value); + $stream = new TokenStream(); + + $this->assertTrue($this->generateHandler()->handle($reader, $stream)); + // comments are ignored (not pushed as token in stream) + $this->assertStreamEmpty($stream); + $this->assertRemainingContent($reader, $remainingContent); + } + + public function getHandleValueTestData() + { + return array( + // 2nd argument only exists for inherited method compatibility + array('/* comment */', new Token(null, null, null), ''), + array('/* comment */foo', new Token(null, null, null), 'foo'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('>'), + array('+'), + array(' '), + ); + } + + protected function generateHandler() + { + return new CommentHandler(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/HashHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/HashHandlerTest.php new file mode 100644 index 0000000..b7fa00a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/HashHandlerTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\HashHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +class HashHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('#id', new Token(Token::TYPE_HASH, 'id', 0), ''), + array('#123', new Token(Token::TYPE_HASH, '123', 0), ''), + + array('#id.class', new Token(Token::TYPE_HASH, 'id', 0), '.class'), + array('#id element', new Token(Token::TYPE_HASH, 'id', 0), ' element'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('id'), + array('123'), + array('<'), + array('<'), + array('#'), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new HashHandler($patterns, new TokenizerEscaping($patterns)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/IdentifierHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/IdentifierHandlerTest.php new file mode 100644 index 0000000..44d3574 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/IdentifierHandlerTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\IdentifierHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +class IdentifierHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('foo', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ''), + array('foo|bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '|bar'), + array('foo.class', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '.class'), + array('foo[attr]', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), '[attr]'), + array('foo bar', new Token(Token::TYPE_IDENTIFIER, 'foo', 0), ' bar'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('>'), + array('+'), + array(' '), + array('*|foo'), + array('/* comment */'), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new IdentifierHandler($patterns, new TokenizerEscaping($patterns)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/NumberHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/NumberHandlerTest.php new file mode 100644 index 0000000..675fd05 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/NumberHandlerTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\NumberHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; + +class NumberHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('12', new Token(Token::TYPE_NUMBER, '12', 0), ''), + array('12.34', new Token(Token::TYPE_NUMBER, '12.34', 0), ''), + array('+12.34', new Token(Token::TYPE_NUMBER, '+12.34', 0), ''), + array('-12.34', new Token(Token::TYPE_NUMBER, '-12.34', 0), ''), + + array('12 arg', new Token(Token::TYPE_NUMBER, '12', 0), ' arg'), + array('12]', new Token(Token::TYPE_NUMBER, '12', 0), ']'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('hello'), + array('>'), + array('+'), + array(' '), + array('/* comment */'), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new NumberHandler($patterns); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/StringHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/StringHandlerTest.php new file mode 100644 index 0000000..89eff8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/StringHandlerTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\StringHandler; +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; +use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; + +class StringHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array('"hello"', new Token(Token::TYPE_STRING, 'hello', 1), ''), + array('"1"', new Token(Token::TYPE_STRING, '1', 1), ''), + array('" "', new Token(Token::TYPE_STRING, ' ', 1), ''), + array('""', new Token(Token::TYPE_STRING, '', 1), ''), + array("'hello'", new Token(Token::TYPE_STRING, 'hello', 1), ''), + + array("'foo'bar", new Token(Token::TYPE_STRING, 'foo', 1), 'bar'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('hello'), + array('>'), + array('1'), + array(' '), + ); + } + + protected function generateHandler() + { + $patterns = new TokenizerPatterns(); + + return new StringHandler($patterns, new TokenizerEscaping($patterns)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/WhitespaceHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/WhitespaceHandlerTest.php new file mode 100644 index 0000000..f5f9e71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Handler/WhitespaceHandlerTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Handler; + +use Symfony\Component\CssSelector\Parser\Handler\WhitespaceHandler; +use Symfony\Component\CssSelector\Parser\Token; + +class WhitespaceHandlerTest extends AbstractHandlerTest +{ + public function getHandleValueTestData() + { + return array( + array(' ', new Token(Token::TYPE_WHITESPACE, ' ', 0), ''), + array("\n", new Token(Token::TYPE_WHITESPACE, "\n", 0), ''), + array("\t", new Token(Token::TYPE_WHITESPACE, "\t", 0), ''), + + array(' foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), 'foo'), + array(' .foo', new Token(Token::TYPE_WHITESPACE, ' ', 0), '.foo'), + ); + } + + public function getDontHandleValueTestData() + { + return array( + array('>'), + array('1'), + array('a'), + ); + } + + protected function generateHandler() + { + return new WhitespaceHandler(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php new file mode 100644 index 0000000..0454d9f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ParserTest.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser; + +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Parser; +use Symfony\Component\CssSelector\Parser\Token; + +class ParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParserTestData */ + public function testParser($source, $representation) + { + $parser = new Parser(); + + $this->assertEquals($representation, array_map(function (SelectorNode $node) { + return (string) $node->getTree(); + }, $parser->parse($source))); + } + + /** @dataProvider getParserExceptionTestData */ + public function testParserException($source, $message) + { + $parser = new Parser(); + + try { + $parser->parse($source); + $this->fail('Parser should throw a SyntaxErrorException.'); + } catch (SyntaxErrorException $e) { + $this->assertEquals($message, $e->getMessage()); + } + } + + /** @dataProvider getPseudoElementsTestData */ + public function testPseudoElements($source, $element, $pseudo) + { + $parser = new Parser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($element, (string) $selector->getTree()); + $this->assertEquals($pseudo, (string) $selector->getPseudoElement()); + } + + /** @dataProvider getSpecificityTestData */ + public function testSpecificity($source, $value) + { + $parser = new Parser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($value, $selector->getSpecificity()->getValue()); + } + + /** @dataProvider getParseSeriesTestData */ + public function testParseSeries($series, $a, $b) + { + $parser = new Parser(); + $selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); + $this->assertCount(1, $selectors); + + /** @var FunctionNode $function */ + $function = $selectors[0]->getTree(); + $this->assertEquals(array($a, $b), Parser::parseSeries($function->getArguments())); + } + + /** @dataProvider getParseSeriesExceptionTestData */ + public function testParseSeriesException($series) + { + $parser = new Parser(); + $selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); + $this->assertCount(1, $selectors); + + /** @var FunctionNode $function */ + $function = $selectors[0]->getTree(); + $this->setExpectedException('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); + Parser::parseSeries($function->getArguments()); + } + + public function getParserTestData() + { + return array( + array('*', array('Element[*]')), + array('*|*', array('Element[*]')), + array('*|foo', array('Element[foo]')), + array('foo|*', array('Element[foo|*]')), + array('foo|bar', array('Element[foo|bar]')), + array('#foo#bar', array('Hash[Hash[Element[*]#foo]#bar]')), + array('div>.foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('div> .foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('div >.foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('div > .foo', array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array("div \n> \t \t .foo", array('CombinedSelector[Element[div] > Class[Element[*].foo]]')), + array('td.foo,.bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('td.foo, .bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array("td.foo\t\r\n\f ,\t\r\n\f .bar", array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('td.foo,.bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('td.foo, .bar', array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array("td.foo\t\r\n\f ,\t\r\n\f .bar", array('Class[Element[td].foo]', 'Class[Element[*].bar]')), + array('div, td.foo, div.bar span', array('Element[div]', 'Class[Element[td].foo]', 'CombinedSelector[Class[Element[div].bar] Element[span]]')), + array('div > p', array('CombinedSelector[Element[div] > Element[p]]')), + array('td:first', array('Pseudo[Element[td]:first]')), + array('td :first', array('CombinedSelector[Element[td] Pseudo[Element[*]:first]]')), + array('a[name]', array('Attribute[Element[a][name]]')), + array("a[ name\t]", array('Attribute[Element[a][name]]')), + array('a [name]', array('CombinedSelector[Element[a] Attribute[Element[*][name]]]')), + array('a[rel="include"]', array("Attribute[Element[a][rel = 'include']]")), + array('a[rel = include]', array("Attribute[Element[a][rel = 'include']]")), + array("a[hreflang |= 'en']", array("Attribute[Element[a][hreflang |= 'en']]")), + array('a[hreflang|=en]', array("Attribute[Element[a][hreflang |= 'en']]")), + array('div:nth-child(10)', array("Function[Element[div]:nth-child(['10'])]")), + array(':nth-child(2n+2)', array("Function[Element[*]:nth-child(['2', 'n', '+2'])]")), + array('div:nth-of-type(10)', array("Function[Element[div]:nth-of-type(['10'])]")), + array('div div:nth-of-type(10) .aclass', array("CombinedSelector[CombinedSelector[Element[div] Function[Element[div]:nth-of-type(['10'])]] Class[Element[*].aclass]]")), + array('label:only', array('Pseudo[Element[label]:only]')), + array('a:lang(fr)', array("Function[Element[a]:lang(['fr'])]")), + array('div:contains("foo")', array("Function[Element[div]:contains(['foo'])]")), + array('div#foobar', array('Hash[Element[div]#foobar]')), + array('div:not(div.foo)', array('Negation[Element[div]:not(Class[Element[div].foo])]')), + array('td ~ th', array('CombinedSelector[Element[td] ~ Element[th]]')), + array('.foo[data-bar][data-baz=0]', array("Attribute[Attribute[Class[Element[*].foo][data-bar]][data-baz = '0']]")), + ); + } + + public function getParserExceptionTestData() + { + return array( + array('attributes(href)/html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()), + array('attributes(href)', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '(', 10))->getMessage()), + array('html/body/a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '/', 4))->getMessage()), + array(' ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 1))->getMessage()), + array('div, ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 5))->getMessage()), + array(' , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 1))->getMessage()), + array('p, , div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, ',', 3))->getMessage()), + array('div > ', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_FILE_END, '', 6))->getMessage()), + array(' > div', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '>', 2))->getMessage()), + array('foo|#bar', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_HASH, 'bar', 4))->getMessage()), + array('#.foo', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '#', 0))->getMessage()), + array('.#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()), + array(':#foo', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_HASH, 'foo', 1))->getMessage()), + array('[*]', SyntaxErrorException::unexpectedToken('"|"', new Token(Token::TYPE_DELIMITER, ']', 2))->getMessage()), + array('[foo|]', SyntaxErrorException::unexpectedToken('identifier', new Token(Token::TYPE_DELIMITER, ']', 5))->getMessage()), + array('[#]', SyntaxErrorException::unexpectedToken('identifier or "*"', new Token(Token::TYPE_DELIMITER, '#', 1))->getMessage()), + array('[foo=#]', SyntaxErrorException::unexpectedToken('string or identifier', new Token(Token::TYPE_DELIMITER, '#', 5))->getMessage()), + array(':nth-child()', SyntaxErrorException::unexpectedToken('at least one argument', new Token(Token::TYPE_DELIMITER, ')', 11))->getMessage()), + array('[href]a', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_IDENTIFIER, 'a', 6))->getMessage()), + array('[rel:stylesheet]', SyntaxErrorException::unexpectedToken('operator', new Token(Token::TYPE_DELIMITER, ':', 4))->getMessage()), + array('[rel=stylesheet', SyntaxErrorException::unexpectedToken('"]"', new Token(Token::TYPE_FILE_END, '', 15))->getMessage()), + array(':lang(fr', SyntaxErrorException::unexpectedToken('an argument', new Token(Token::TYPE_FILE_END, '', 8))->getMessage()), + array(':contains("foo', SyntaxErrorException::unclosedString(10)->getMessage()), + array('foo!', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '!', 3))->getMessage()), + ); + } + + public function getPseudoElementsTestData() + { + return array( + array('foo', 'Element[foo]', ''), + array('*', 'Element[*]', ''), + array(':empty', 'Pseudo[Element[*]:empty]', ''), + array(':BEfore', 'Element[*]', 'before'), + array(':aftER', 'Element[*]', 'after'), + array(':First-Line', 'Element[*]', 'first-line'), + array(':First-Letter', 'Element[*]', 'first-letter'), + array('::befoRE', 'Element[*]', 'before'), + array('::AFter', 'Element[*]', 'after'), + array('::firsT-linE', 'Element[*]', 'first-line'), + array('::firsT-letteR', 'Element[*]', 'first-letter'), + array('::Selection', 'Element[*]', 'selection'), + array('foo:after', 'Element[foo]', 'after'), + array('foo::selection', 'Element[foo]', 'selection'), + array('lorem#ipsum ~ a#b.c[href]:empty::selection', 'CombinedSelector[Hash[Element[lorem]#ipsum] ~ Pseudo[Attribute[Class[Hash[Element[a]#b].c][href]]:empty]]', 'selection'), + ); + } + + public function getSpecificityTestData() + { + return array( + array('*', 0), + array(' foo', 1), + array(':empty ', 10), + array(':before', 1), + array('*:before', 1), + array(':nth-child(2)', 10), + array('.bar', 10), + array('[baz]', 10), + array('[baz="4"]', 10), + array('[baz^="4"]', 10), + array('#lipsum', 100), + array(':not(*)', 0), + array(':not(foo)', 1), + array(':not(.foo)', 10), + array(':not([foo])', 10), + array(':not(:empty)', 10), + array(':not(#foo)', 100), + array('foo:empty', 11), + array('foo:before', 2), + array('foo::before', 2), + array('foo:empty::before', 12), + array('#lorem + foo#ipsum:first-child > bar:first-line', 213), + ); + } + + public function getParseSeriesTestData() + { + return array( + array('1n+3', 1, 3), + array('1n +3', 1, 3), + array('1n + 3', 1, 3), + array('1n+ 3', 1, 3), + array('1n-3', 1, -3), + array('1n -3', 1, -3), + array('1n - 3', 1, -3), + array('1n- 3', 1, -3), + array('n-5', 1, -5), + array('odd', 2, 1), + array('even', 2, 0), + array('3n', 3, 0), + array('n', 1, 0), + array('+n', 1, 0), + array('-n', -1, 0), + array('5', 0, 5), + ); + } + + public function getParseSeriesExceptionTestData() + { + return array( + array('foo'), + array('n+'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ReaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ReaderTest.php new file mode 100644 index 0000000..03c054e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/ReaderTest.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser; + +use Symfony\Component\CssSelector\Parser\Reader; + +class ReaderTest extends \PHPUnit_Framework_TestCase +{ + public function testIsEOF() + { + $reader = new Reader(''); + $this->assertTrue($reader->isEOF()); + + $reader = new Reader('hello'); + $this->assertFalse($reader->isEOF()); + + $this->assignPosition($reader, 2); + $this->assertFalse($reader->isEOF()); + + $this->assignPosition($reader, 5); + $this->assertTrue($reader->isEOF()); + } + + public function testGetRemainingLength() + { + $reader = new Reader('hello'); + $this->assertEquals(5, $reader->getRemainingLength()); + + $this->assignPosition($reader, 2); + $this->assertEquals(3, $reader->getRemainingLength()); + + $this->assignPosition($reader, 5); + $this->assertEquals(0, $reader->getRemainingLength()); + } + + public function testGetSubstring() + { + $reader = new Reader('hello'); + $this->assertEquals('he', $reader->getSubstring(2)); + $this->assertEquals('el', $reader->getSubstring(2, 1)); + + $this->assignPosition($reader, 2); + $this->assertEquals('ll', $reader->getSubstring(2)); + $this->assertEquals('lo', $reader->getSubstring(2, 1)); + } + + public function testGetOffset() + { + $reader = new Reader('hello'); + $this->assertEquals(2, $reader->getOffset('ll')); + $this->assertFalse($reader->getOffset('w')); + + $this->assignPosition($reader, 2); + $this->assertEquals(0, $reader->getOffset('ll')); + $this->assertFalse($reader->getOffset('he')); + } + + public function testFindPattern() + { + $reader = new Reader('hello'); + + $this->assertFalse($reader->findPattern('/world/')); + $this->assertEquals(array('hello', 'h'), $reader->findPattern('/^([a-z]).*/')); + + $this->assignPosition($reader, 2); + $this->assertFalse($reader->findPattern('/^h.*/')); + $this->assertEquals(array('llo'), $reader->findPattern('/^llo$/')); + } + + public function testMoveForward() + { + $reader = new Reader('hello'); + $this->assertEquals(0, $reader->getPosition()); + + $reader->moveForward(2); + $this->assertEquals(2, $reader->getPosition()); + } + + public function testToEnd() + { + $reader = new Reader('hello'); + $reader->moveToEnd(); + $this->assertTrue($reader->isEOF()); + } + + private function assignPosition(Reader $reader, $value) + { + $position = new \ReflectionProperty($reader, 'position'); + $position->setAccessible(true); + $position->setValue($reader, $value); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/ClassParserTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/ClassParserTest.php new file mode 100644 index 0000000..6efdd67 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/ClassParserTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\ClassParser; + +/** + * @author Jean-François Simon + */ +class ClassParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParseTestData */ + public function testParse($source, $representation) + { + $parser = new ClassParser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($representation, (string) $selector->getTree()); + } + + public function getParseTestData() + { + return array( + array('.testclass', 'Class[Element[*].testclass]'), + array('testel.testclass', 'Class[Element[testel].testclass]'), + array('testns|.testclass', 'Class[Element[testns|*].testclass]'), + array('testns|*.testclass', 'Class[Element[testns|*].testclass]'), + array('testns|testel.testclass', 'Class[Element[testns|testel].testclass]'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/ElementParserTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/ElementParserTest.php new file mode 100644 index 0000000..b30b5ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/ElementParserTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\ElementParser; + +/** + * @author Jean-François Simon + */ +class ElementParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParseTestData */ + public function testParse($source, $representation) + { + $parser = new ElementParser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($representation, (string) $selector->getTree()); + } + + public function getParseTestData() + { + return array( + array('*', 'Element[*]'), + array('testel', 'Element[testel]'), + array('testns|*', 'Element[testns|*]'), + array('testns|testel', 'Element[testns|testel]'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/EmptyStringParserTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/EmptyStringParserTest.php new file mode 100644 index 0000000..b7c3539 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/EmptyStringParserTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\EmptyStringParser; + +/** + * @author Jean-François Simon + */ +class EmptyStringParserTest extends \PHPUnit_Framework_TestCase +{ + public function testParse() + { + $parser = new EmptyStringParser(); + $selectors = $parser->parse(''); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals('Element[*]', (string) $selector->getTree()); + + $selectors = $parser->parse('this will produce an empty array'); + $this->assertCount(0, $selectors); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/HashParserTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/HashParserTest.php new file mode 100644 index 0000000..d2ce891 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/Shortcut/HashParserTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser\Shortcut; + +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Shortcut\HashParser; + +/** + * @author Jean-François Simon + */ +class HashParserTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getParseTestData */ + public function testParse($source, $representation) + { + $parser = new HashParser(); + $selectors = $parser->parse($source); + $this->assertCount(1, $selectors); + + /** @var SelectorNode $selector */ + $selector = $selectors[0]; + $this->assertEquals($representation, (string) $selector->getTree()); + } + + public function getParseTestData() + { + return array( + array('#testid', 'Hash[Element[*]#testid]'), + array('testel#testid', 'Hash[Element[testel]#testid]'), + array('testns|#testid', 'Hash[Element[testns|*]#testid]'), + array('testns|*#testid', 'Hash[Element[testns|*]#testid]'), + array('testns|testel#testid', 'Hash[Element[testns|testel]#testid]'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/TokenStreamTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/TokenStreamTest.php new file mode 100644 index 0000000..8f3253a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/Parser/TokenStreamTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Parser; + +use Symfony\Component\CssSelector\Parser\Token; +use Symfony\Component\CssSelector\Parser\TokenStream; + +class TokenStreamTest extends \PHPUnit_Framework_TestCase +{ + public function testGetNext() + { + $stream = new TokenStream(); + $stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3)); + + $this->assertSame($t1, $stream->getNext()); + $this->assertSame($t2, $stream->getNext()); + $this->assertSame($t3, $stream->getNext()); + } + + public function testGetPeek() + { + $stream = new TokenStream(); + $stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $stream->push($t2 = new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'title', 3)); + + $this->assertSame($t1, $stream->getPeek()); + $this->assertSame($t1, $stream->getNext()); + $this->assertSame($t2, $stream->getPeek()); + $this->assertSame($t2, $stream->getPeek()); + $this->assertSame($t2, $stream->getNext()); + } + + public function testGetNextIdentifier() + { + $stream = new TokenStream(); + $stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + + $this->assertEquals('h1', $stream->getNextIdentifier()); + } + + public function testFailToGetNextIdentifier() + { + $this->setExpectedException('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); + + $stream = new TokenStream(); + $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->getNextIdentifier(); + } + + public function testGetNextIdentifierOrStar() + { + $stream = new TokenStream(); + + $stream->push(new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $this->assertEquals('h1', $stream->getNextIdentifierOrStar()); + + $stream->push(new Token(Token::TYPE_DELIMITER, '*', 0)); + $this->assertNull($stream->getNextIdentifierOrStar()); + } + + public function testFailToGetNextIdentifierOrStar() + { + $this->setExpectedException('Symfony\Component\CssSelector\Exception\SyntaxErrorException'); + + $stream = new TokenStream(); + $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); + $stream->getNextIdentifierOrStar(); + } + + public function testSkipWhitespace() + { + $stream = new TokenStream(); + $stream->push($t1 = new Token(Token::TYPE_IDENTIFIER, 'h1', 0)); + $stream->push($t2 = new Token(Token::TYPE_WHITESPACE, ' ', 2)); + $stream->push($t3 = new Token(Token::TYPE_IDENTIFIER, 'h1', 3)); + + $stream->skipWhitespace(); + $this->assertSame($t1, $stream->getNext()); + + $stream->skipWhitespace(); + $this->assertSame($t3, $stream->getNext()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/ids.html b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/ids.html new file mode 100644 index 0000000..5799fad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/ids.html @@ -0,0 +1,48 @@ + + + + +
    + + + + link +
      +
    1. content
    2. +
    3. +
      +
      +
    4. +
    5. +
    6. +
    7. +
    8. +
    9. +
    +

    + hi there + guy + + + + + + + +

    + + +
    +

    +
      +
    + + + + +
    +
    + diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/lang.xml b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/lang.xml new file mode 100644 index 0000000..14f8dbe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/lang.xml @@ -0,0 +1,11 @@ + + a + b + c + d + e + f + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/shakespear.html b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/shakespear.html new file mode 100644 index 0000000..15d1ad3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/Fixtures/shakespear.html @@ -0,0 +1,308 @@ + + + + + + +
    +
    +

    As You Like It

    +
    + by William Shakespeare +
    +
    +

    ACT I, SCENE III. A room in the palace.

    +
    +
    Enter CELIA and ROSALIND
    +
    +
    CELIA
    +
    +
    Why, cousin! why, Rosalind! Cupid have mercy! not a word?
    +
    +
    ROSALIND
    +
    +
    Not one to throw at a dog.
    +
    +
    CELIA
    +
    +
    No, thy words are too precious to be cast away upon
    +
    curs; throw some of them at me; come, lame me with reasons.
    +
    +
    ROSALIND
    +
    CELIA
    +
    +
    But is all this for your father?
    +
    +
    +
    Then there were two cousins laid up; when the one
    +
    should be lamed with reasons and the other mad
    +
    without any.
    +
    +
    ROSALIND
    +
    +
    No, some of it is for my child's father. O, how
    +
    full of briers is this working-day world!
    +
    +
    CELIA
    +
    +
    They are but burs, cousin, thrown upon thee in
    +
    holiday foolery: if we walk not in the trodden
    +
    paths our very petticoats will catch them.
    +
    +
    ROSALIND
    +
    +
    I could shake them off my coat: these burs are in my heart.
    +
    +
    CELIA
    +
    +
    Hem them away.
    +
    +
    ROSALIND
    +
    +
    I would try, if I could cry 'hem' and have him.
    +
    +
    CELIA
    +
    +
    Come, come, wrestle with thy affections.
    +
    +
    ROSALIND
    +
    +
    O, they take the part of a better wrestler than myself!
    +
    +
    CELIA
    +
    +
    O, a good wish upon you! you will try in time, in
    +
    despite of a fall. But, turning these jests out of
    +
    service, let us talk in good earnest: is it
    +
    possible, on such a sudden, you should fall into so
    +
    strong a liking with old Sir Rowland's youngest son?
    +
    +
    ROSALIND
    +
    +
    The duke my father loved his father dearly.
    +
    +
    CELIA
    +
    +
    Doth it therefore ensue that you should love his son
    +
    dearly? By this kind of chase, I should hate him,
    +
    for my father hated his father dearly; yet I hate
    +
    not Orlando.
    +
    +
    ROSALIND
    +
    +
    No, faith, hate him not, for my sake.
    +
    +
    CELIA
    +
    +
    Why should I not? doth he not deserve well?
    +
    +
    ROSALIND
    +
    +
    Let me love him for that, and do you love him
    +
    because I do. Look, here comes the duke.
    +
    +
    CELIA
    +
    +
    With his eyes full of anger.
    +
    Enter DUKE FREDERICK, with Lords
    +
    +
    DUKE FREDERICK
    +
    +
    Mistress, dispatch you with your safest haste
    +
    And get you from our court.
    +
    +
    ROSALIND
    +
    +
    Me, uncle?
    +
    +
    DUKE FREDERICK
    +
    +
    You, cousin
    +
    Within these ten days if that thou be'st found
    +
    So near our public court as twenty miles,
    +
    Thou diest for it.
    +
    +
    ROSALIND
    +
    +
    I do beseech your grace,
    +
    Let me the knowledge of my fault bear with me:
    +
    If with myself I hold intelligence
    +
    Or have acquaintance with mine own desires,
    +
    If that I do not dream or be not frantic,--
    +
    As I do trust I am not--then, dear uncle,
    +
    Never so much as in a thought unborn
    +
    Did I offend your highness.
    +
    +
    DUKE FREDERICK
    +
    +
    Thus do all traitors:
    +
    If their purgation did consist in words,
    +
    They are as innocent as grace itself:
    +
    Let it suffice thee that I trust thee not.
    +
    +
    ROSALIND
    +
    +
    Yet your mistrust cannot make me a traitor:
    +
    Tell me whereon the likelihood depends.
    +
    +
    DUKE FREDERICK
    +
    +
    Thou art thy father's daughter; there's enough.
    +
    +
    ROSALIND
    +
    +
    So was I when your highness took his dukedom;
    +
    So was I when your highness banish'd him:
    +
    Treason is not inherited, my lord;
    +
    Or, if we did derive it from our friends,
    +
    What's that to me? my father was no traitor:
    +
    Then, good my liege, mistake me not so much
    +
    To think my poverty is treacherous.
    +
    +
    CELIA
    +
    +
    Dear sovereign, hear me speak.
    +
    +
    DUKE FREDERICK
    +
    +
    Ay, Celia; we stay'd her for your sake,
    +
    Else had she with her father ranged along.
    +
    +
    CELIA
    +
    +
    I did not then entreat to have her stay;
    +
    It was your pleasure and your own remorse:
    +
    I was too young that time to value her;
    +
    But now I know her: if she be a traitor,
    +
    Why so am I; we still have slept together,
    +
    Rose at an instant, learn'd, play'd, eat together,
    +
    And wheresoever we went, like Juno's swans,
    +
    Still we went coupled and inseparable.
    +
    +
    DUKE FREDERICK
    +
    +
    She is too subtle for thee; and her smoothness,
    +
    Her very silence and her patience
    +
    Speak to the people, and they pity her.
    +
    Thou art a fool: she robs thee of thy name;
    +
    And thou wilt show more bright and seem more virtuous
    +
    When she is gone. Then open not thy lips:
    +
    Firm and irrevocable is my doom
    +
    Which I have pass'd upon her; she is banish'd.
    +
    +
    CELIA
    +
    +
    Pronounce that sentence then on me, my liege:
    +
    I cannot live out of her company.
    +
    +
    DUKE FREDERICK
    +
    +
    You are a fool. You, niece, provide yourself:
    +
    If you outstay the time, upon mine honour,
    +
    And in the greatness of my word, you die.
    +
    Exeunt DUKE FREDERICK and Lords
    +
    +
    CELIA
    +
    +
    O my poor Rosalind, whither wilt thou go?
    +
    Wilt thou change fathers? I will give thee mine.
    +
    I charge thee, be not thou more grieved than I am.
    +
    +
    ROSALIND
    +
    +
    I have more cause.
    +
    +
    CELIA
    +
    +
    Thou hast not, cousin;
    +
    Prithee be cheerful: know'st thou not, the duke
    +
    Hath banish'd me, his daughter?
    +
    +
    ROSALIND
    +
    +
    That he hath not.
    +
    +
    CELIA
    +
    +
    No, hath not? Rosalind lacks then the love
    +
    Which teacheth thee that thou and I am one:
    +
    Shall we be sunder'd? shall we part, sweet girl?
    +
    No: let my father seek another heir.
    +
    Therefore devise with me how we may fly,
    +
    Whither to go and what to bear with us;
    +
    And do not seek to take your change upon you,
    +
    To bear your griefs yourself and leave me out;
    +
    For, by this heaven, now at our sorrows pale,
    +
    Say what thou canst, I'll go along with thee.
    +
    +
    ROSALIND
    +
    +
    Why, whither shall we go?
    +
    +
    CELIA
    +
    +
    To seek my uncle in the forest of Arden.
    +
    +
    ROSALIND
    +
    +
    Alas, what danger will it be to us,
    +
    Maids as we are, to travel forth so far!
    +
    Beauty provoketh thieves sooner than gold.
    +
    +
    CELIA
    +
    +
    I'll put myself in poor and mean attire
    +
    And with a kind of umber smirch my face;
    +
    The like do you: so shall we pass along
    +
    And never stir assailants.
    +
    +
    ROSALIND
    +
    +
    Were it not better,
    +
    Because that I am more than common tall,
    +
    That I did suit me all points like a man?
    +
    A gallant curtle-axe upon my thigh,
    +
    A boar-spear in my hand; and--in my heart
    +
    Lie there what hidden woman's fear there will--
    +
    We'll have a swashing and a martial outside,
    +
    As many other mannish cowards have
    +
    That do outface it with their semblances.
    +
    +
    CELIA
    +
    +
    What shall I call thee when thou art a man?
    +
    +
    ROSALIND
    +
    +
    I'll have no worse a name than Jove's own page;
    +
    And therefore look you call me Ganymede.
    +
    But what will you be call'd?
    +
    +
    CELIA
    +
    +
    Something that hath a reference to my state
    +
    No longer Celia, but Aliena.
    +
    +
    ROSALIND
    +
    +
    But, cousin, what if we assay'd to steal
    +
    The clownish fool out of your father's court?
    +
    Would he not be a comfort to our travel?
    +
    +
    CELIA
    +
    +
    He'll go along o'er the wide world with me;
    +
    Leave me alone to woo him. Let's away,
    +
    And get our jewels and our wealth together,
    +
    Devise the fittest time and safest way
    +
    To hide us from pursuit that will be made
    +
    After my flight. Now go we in content
    +
    To liberty and not to banishment.
    +
    Exeunt
    +
    +
    +
    +
    + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php new file mode 100644 index 0000000..143328f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\XPath; + +use Symfony\Component\CssSelector\XPath\Extension\HtmlExtension; +use Symfony\Component\CssSelector\XPath\Translator; + +class TranslatorTest extends \PHPUnit_Framework_TestCase +{ + /** @dataProvider getXpathLiteralTestData */ + public function testXpathLiteral($value, $literal) + { + $this->assertEquals($literal, Translator::getXpathLiteral($value)); + } + + /** @dataProvider getCssToXPathTestData */ + public function testCssToXPath($css, $xpath) + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $this->assertEquals($xpath, $translator->cssToXPath($css, '')); + } + + /** @dataProvider getXmlLangTestData */ + public function testXmlLang($css, array $elementsId) + { + $translator = new Translator(); + $document = new \SimpleXMLElement(file_get_contents(__DIR__.'/Fixtures/lang.xml')); + $elements = $document->xpath($translator->cssToXPath($css)); + $this->assertEquals(count($elementsId), count($elements)); + foreach ($elements as $element) { + $this->assertTrue(in_array($element->attributes()->id, $elementsId)); + } + } + + /** @dataProvider getHtmlIdsTestData */ + public function testHtmlIds($css, array $elementsId) + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $document = new \DOMDocument(); + $document->strictErrorChecking = false; + $internalErrors = libxml_use_internal_errors(true); + $document->loadHTMLFile(__DIR__.'/Fixtures/ids.html'); + $document = simplexml_import_dom($document); + $elements = $document->xpath($translator->cssToXPath($css)); + $this->assertCount(count($elementsId), $elementsId); + foreach ($elements as $element) { + if (null !== $element->attributes()->id) { + $this->assertTrue(in_array($element->attributes()->id, $elementsId)); + } + } + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** @dataProvider getHtmlShakespearTestData */ + public function testHtmlShakespear($css, $count) + { + $translator = new Translator(); + $translator->registerExtension(new HtmlExtension($translator)); + $document = new \DOMDocument(); + $document->strictErrorChecking = false; + $document->loadHTMLFile(__DIR__.'/Fixtures/shakespear.html'); + $document = simplexml_import_dom($document); + $bodies = $document->xpath('//body'); + $elements = $bodies[0]->xpath($translator->cssToXPath($css)); + $this->assertEquals($count, count($elements)); + } + + public function getXpathLiteralTestData() + { + return array( + array('foo', "'foo'"), + array("foo's bar", '"foo\'s bar"'), + array("foo's \"middle\" bar", 'concat(\'foo\', "\'", \'s "middle" bar\')'), + array("foo's 'middle' \"bar\"", 'concat(\'foo\', "\'", \'s \', "\'", \'middle\', "\'", \' "bar"\')'), + ); + } + + public function getCssToXPathTestData() + { + return array( + array('*', '*'), + array('e', 'e'), + array('*|e', 'e'), + array('e|f', 'e:f'), + array('e[foo]', 'e[@foo]'), + array('e[foo|bar]', 'e[@foo:bar]'), + array('e[foo="bar"]', "e[@foo = 'bar']"), + array('e[foo~="bar"]', "e[@foo and contains(concat(' ', normalize-space(@foo), ' '), ' bar ')]"), + array('e[foo^="bar"]', "e[@foo and starts-with(@foo, 'bar')]"), + array('e[foo$="bar"]', "e[@foo and substring(@foo, string-length(@foo)-2) = 'bar']"), + array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"), + array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"), + array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"), + array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 0)]"), + array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"), + array('e:nth-of-type(1)', '*/e[position() = 1]'), + array('e:nth-last-of-type(1)', '*/e[position() = last() - 0]'), + array('div e:nth-last-of-type(1) .aclass', "div/descendant-or-self::*/e[position() = last() - 0]/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' aclass ')]"), + array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"), + array('e:last-child', "*/*[name() = 'e' and (position() = last())]"), + array('e:first-of-type', '*/e[position() = 1]'), + array('e:last-of-type', '*/e[position() = last()]'), + array('e:only-child', "*/*[name() = 'e' and (last() = 1)]"), + array('e:only-of-type', 'e[last() = 1]'), + array('e:empty', 'e[not(*) and not(string-length())]'), + array('e:EmPTY', 'e[not(*) and not(string-length())]'), + array('e:root', 'e[not(parent::*)]'), + array('e:hover', 'e[0]'), + array('e:contains("foo")', "e[contains(string(.), 'foo')]"), + array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"), + array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"), + array('e#myid', "e[@id = 'myid']"), + array('e:not(:nth-child(odd))', 'e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]'), + array('e:nOT(*)', 'e[0]'), + array('e f', 'e/descendant-or-self::*/f'), + array('e > f', 'e/f'), + array('e + f', "e/following-sibling::*[name() = 'f' and (position() = 1)]"), + array('e ~ f', 'e/following-sibling::f'), + array('div#container p', "div[@id = 'container']/descendant-or-self::*/p"), + ); + } + + public function getXmlLangTestData() + { + return array( + array(':lang("EN")', array('first', 'second', 'third', 'fourth')), + array(':lang("en-us")', array('second', 'fourth')), + array(':lang(en-nz)', array('third')), + array(':lang(fr)', array('fifth')), + array(':lang(ru)', array('sixth')), + array(":lang('ZH')", array('eighth')), + array(':lang(de) :lang(zh)', array('eighth')), + array(':lang(en), :lang(zh)', array('first', 'second', 'third', 'fourth', 'eighth')), + array(':lang(es)', array()), + ); + } + + public function getHtmlIdsTestData() + { + return array( + array('div', array('outer-div', 'li-div', 'foobar-div')), + array('DIV', array('outer-div', 'li-div', 'foobar-div')), // case-insensitive in HTML + array('div div', array('li-div')), + array('div, div div', array('outer-div', 'li-div', 'foobar-div')), + array('a[name]', array('name-anchor')), + array('a[NAme]', array('name-anchor')), // case-insensitive in HTML: + array('a[rel]', array('tag-anchor', 'nofollow-anchor')), + array('a[rel="tag"]', array('tag-anchor')), + array('a[href*="localhost"]', array('tag-anchor')), + array('a[href*=""]', array()), + array('a[href^="http"]', array('tag-anchor', 'nofollow-anchor')), + array('a[href^="http:"]', array('tag-anchor')), + array('a[href^=""]', array()), + array('a[href$="org"]', array('nofollow-anchor')), + array('a[href$=""]', array()), + array('div[foobar~="bc"]', array('foobar-div')), + array('div[foobar~="cde"]', array('foobar-div')), + array('[foobar~="ab bc"]', array('foobar-div')), + array('[foobar~=""]', array()), + array('[foobar~=" \t"]', array()), + array('div[foobar~="cd"]', array()), + array('*[lang|="En"]', array('second-li')), + array('[lang|="En-us"]', array('second-li')), + // Attribute values are case sensitive + array('*[lang|="en"]', array()), + array('[lang|="en-US"]', array()), + array('*[lang|="e"]', array()), + // ... :lang() is not. + array(':lang("EN")', array('second-li', 'li-div')), + array('*:lang(en-US)', array('second-li', 'li-div')), + array(':lang("e")', array()), + array('li:nth-child(3)', array('third-li')), + array('li:nth-child(10)', array()), + array('li:nth-child(2n)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-child(even)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-child(2n+0)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')), + array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')), + array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')), + array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')), + array('li:nth-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(n+3)', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-child(-n)', array()), + array('li:nth-child(-n-1)', array()), + array('li:nth-child(-n+1)', array('first-li')), + array('li:nth-child(-n+3)', array('first-li', 'second-li', 'third-li')), + array('li:nth-last-child(0)', array()), + array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li', 'sixth-li')), + array('li:nth-last-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n-3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')), + array('li:nth-last-child(n+3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li')), + array('li:nth-last-child(-n)', array()), + array('li:nth-last-child(-n-1)', array()), + array('li:nth-last-child(-n+1)', array('seventh-li')), + array('li:nth-last-child(-n+3)', array('fifth-li', 'sixth-li', 'seventh-li')), + array('ol:first-of-type', array('first-ol')), + array('ol:nth-child(1)', array('first-ol')), + array('ol:nth-of-type(2)', array('second-ol')), + array('ol:nth-last-of-type(1)', array('second-ol')), + array('span:only-child', array('foobar-span')), + array('li div:only-child', array('li-div')), + array('div *:only-child', array('li-div', 'foobar-span')), + array('p:only-of-type', array('paragraph')), + array('a:empty', array('name-anchor')), + array('a:EMpty', array('name-anchor')), + array('li:empty', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li')), + array(':root', array('html')), + array('html:root', array('html')), + array('li:root', array()), + array('* :root', array()), + array('*:contains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')), + array(':CONtains("link")', array('html', 'outer-div', 'tag-anchor', 'nofollow-anchor')), + array('*:contains("LInk")', array()), // case sensitive + array('*:contains("e")', array('html', 'nil', 'outer-div', 'first-ol', 'first-li', 'paragraph', 'p-em')), + array('*:contains("E")', array()), // case-sensitive + array('.a', array('first-ol')), + array('.b', array('first-ol')), + array('*.a', array('first-ol')), + array('ol.a', array('first-ol')), + array('.c', array('first-ol', 'third-li', 'fourth-li')), + array('*.c', array('first-ol', 'third-li', 'fourth-li')), + array('ol *.c', array('third-li', 'fourth-li')), + array('ol li.c', array('third-li', 'fourth-li')), + array('li ~ li.c', array('third-li', 'fourth-li')), + array('ol > li.c', array('third-li', 'fourth-li')), + array('#first-li', array('first-li')), + array('li#first-li', array('first-li')), + array('*#first-li', array('first-li')), + array('li div', array('li-div')), + array('li > div', array('li-div')), + array('div div', array('li-div')), + array('div > div', array()), + array('div>.c', array('first-ol')), + array('div > .c', array('first-ol')), + array('div + div', array('foobar-div')), + array('a ~ a', array('tag-anchor', 'nofollow-anchor')), + array('a[rel="tag"] ~ a', array('nofollow-anchor')), + array('ol#first-ol li:last-child', array('seventh-li')), + array('ol#first-ol *:last-child', array('li-div', 'seventh-li')), + array('#outer-div:first-child', array('outer-div')), + array('#outer-div :first-child', array('name-anchor', 'first-li', 'li-div', 'p-b', 'checkbox-fieldset-disabled', 'area-href')), + array('a[href]', array('tag-anchor', 'nofollow-anchor')), + array(':not(*)', array()), + array('a:not([href])', array('name-anchor')), + array('ol :Not(li[class])', array('first-li', 'second-li', 'li-div', 'fifth-li', 'sixth-li', 'seventh-li')), + // HTML-specific + array(':link', array('link-href', 'tag-anchor', 'nofollow-anchor', 'area-href')), + array(':visited', array()), + array(':enabled', array('link-href', 'tag-anchor', 'nofollow-anchor', 'checkbox-unchecked', 'text-checked', 'checkbox-checked', 'area-href')), + array(':disabled', array('checkbox-disabled', 'checkbox-disabled-checked', 'fieldset', 'checkbox-fieldset-disabled')), + array(':checked', array('checkbox-checked', 'checkbox-disabled-checked')), + ); + } + + public function getHtmlShakespearTestData() + { + return array( + array('*', 246), + array('div:contains(CELIA)', 26), + array('div:only-child', 22), // ? + array('div:nth-child(even)', 106), + array('div:nth-child(2n)', 106), + array('div:nth-child(odd)', 137), + array('div:nth-child(2n+1)', 137), + array('div:nth-child(n)', 243), + array('div:last-child', 53), + array('div:first-child', 51), + array('div > div', 242), + array('div + div', 190), + array('div ~ div', 190), + array('body', 1), + array('body div', 243), + array('div', 243), + array('div div', 242), + array('div div div', 241), + array('div, div, div', 243), + array('div, a, span', 243), + array('.dialog', 51), + array('div.dialog', 51), + array('div .dialog', 51), + array('div.character, div.dialog', 99), + array('div.direction.dialog', 0), + array('div.dialog.direction', 0), + array('div.dialog.scene', 1), + array('div.scene.scene', 1), + array('div.scene .scene', 0), + array('div.direction .dialog ', 0), + array('div .dialog .direction', 4), + array('div.dialog .dialog .direction', 4), + array('#speech5', 1), + array('div#speech5', 1), + array('div #speech5', 1), + array('div.scene div.dialog', 49), + array('div#scene1 div.dialog div', 142), + array('#scene1 #speech1', 1), + array('div[class]', 103), + array('div[class=dialog]', 50), + array('div[class^=dia]', 51), + array('div[class$=log]', 50), + array('div[class*=sce]', 1), + array('div[class|=dialog]', 50), // ? Seems right + array('div[class!=madeup]', 243), // ? Seems right + array('div[class~=dialog]', 51), // ? Seems right + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php new file mode 100644 index 0000000..026ac06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AbstractExtension.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +/** + * XPath expression translator abstract extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +abstract class AbstractExtension implements ExtensionInterface +{ + /** + * {@inheritdoc} + */ + public function getNodeTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getCombinationTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getFunctionTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getPseudoClassTranslators() + { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getAttributeMatchingTranslators() + { + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php new file mode 100644 index 0000000..6ace8b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/AttributeMatchingExtension.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator attribute extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class AttributeMatchingExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getAttributeMatchingTranslators() + { + return array( + 'exists' => array($this, 'translateExists'), + '=' => array($this, 'translateEquals'), + '~=' => array($this, 'translateIncludes'), + '|=' => array($this, 'translateDashMatch'), + '^=' => array($this, 'translatePrefixMatch'), + '$=' => array($this, 'translateSuffixMatch'), + '*=' => array($this, 'translateSubstringMatch'), + '!=' => array($this, 'translateDifferent'), + ); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateExists(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($attribute); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateEquals(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateIncludes(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)', + $attribute, + Translator::getXpathLiteral(' '.$value.' ') + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateDashMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition(sprintf( + '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))', + $attribute, + Translator::getXpathLiteral($value), + Translator::getXpathLiteral($value.'-') + )); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translatePrefixMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and starts-with(%1$s, %2$s)', + $attribute, + Translator::getXpathLiteral($value) + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateSuffixMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s', + $attribute, + strlen($value) - 1, + Translator::getXpathLiteral($value) + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateSubstringMatch(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition($value ? sprintf( + '%1$s and contains(%1$s, %2$s)', + $attribute, + Translator::getXpathLiteral($value) + ) : '0'); + } + + /** + * @param XPathExpr $xpath + * @param string $attribute + * @param string $value + * + * @return XPathExpr + */ + public function translateDifferent(XPathExpr $xpath, $attribute, $value) + { + return $xpath->addCondition(sprintf( + $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s', + $attribute, + Translator::getXpathLiteral($value) + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'attribute-matching'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php new file mode 100644 index 0000000..0d2d658 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/CombinationExtension.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator combination extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class CombinationExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getCombinationTranslators() + { + return array( + ' ' => array($this, 'translateDescendant'), + '>' => array($this, 'translateChild'), + '+' => array($this, 'translateDirectAdjacent'), + '~' => array($this, 'translateIndirectAdjacent'), + ); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath->join('/descendant-or-self::*/', $combinedXpath); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath->join('/', $combinedXpath); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath + ->join('/following-sibling::', $combinedXpath) + ->addNameTest() + ->addCondition('position() = 1'); + } + + /** + * @param XPathExpr $xpath + * @param XPathExpr $combinedXpath + * + * @return XPathExpr + */ + public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) + { + return $xpath->join('/following-sibling::', $combinedXpath); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'combination'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php new file mode 100644 index 0000000..3607022 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/ExtensionInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +/** + * XPath expression translator extension interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface ExtensionInterface +{ + /** + * Returns node translators. + * + * These callables will receive the node as first argument and the translator as second argument. + * + * @return callable[] + */ + public function getNodeTranslators(); + + /** + * Returns combination translators. + * + * @return callable[] + */ + public function getCombinationTranslators(); + + /** + * Returns function translators. + * + * @return callable[] + */ + public function getFunctionTranslators(); + + /** + * Returns pseudo-class translators. + * + * @return callable[] + */ + public function getPseudoClassTranslators(); + + /** + * Returns attribute operation translators. + * + * @return callable[] + */ + public function getAttributeMatchingTranslators(); + + /** + * Returns extension name. + * + * @return string + */ + public function getName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php new file mode 100644 index 0000000..ea05523 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\Exception\SyntaxErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Parser\Parser; +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator function extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class FunctionExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getFunctionTranslators() + { + return array( + 'nth-child' => array($this, 'translateNthChild'), + 'nth-last-child' => array($this, 'translateNthLastChild'), + 'nth-of-type' => array($this, 'translateNthOfType'), + 'nth-last-of-type' => array($this, 'translateNthLastOfType'), + 'contains' => array($this, 'translateContains'), + 'lang' => array($this, 'translateLang'), + ); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * @param bool $last + * @param bool $addNameTest + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $last = false, $addNameTest = true) + { + try { + list($a, $b) = Parser::parseSeries($function->getArguments()); + } catch (SyntaxErrorException $e) { + throw new ExpressionErrorException(sprintf('Invalid series: %s', implode(', ', $function->getArguments())), 0, $e); + } + + $xpath->addStarPrefix(); + if ($addNameTest) { + $xpath->addNameTest(); + } + + if (0 === $a) { + return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b)); + } + + if ($a < 0) { + if ($b < 1) { + return $xpath->addCondition('false()'); + } + + $sign = '<='; + } else { + $sign = '>='; + } + + $expr = 'position()'; + + if ($last) { + $expr = 'last() - '.$expr; + --$b; + } + + if (0 !== $b) { + $expr .= ' - '.$b; + } + + $conditions = array(sprintf('%s %s 0', $expr, $sign)); + + if (1 !== $a && -1 !== $a) { + $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); + } + + return $xpath->addCondition(implode(' and ', $conditions)); + + // todo: handle an+b, odd, even + // an+b means every-a, plus b, e.g., 2n+1 means odd + // 0n+b means b + // n+0 means a=1, i.e., all elements + // an means every a elements, i.e., 2n means even + // -n means -1n + // -1n+6 means elements 6 and previous + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + */ + public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function) + { + return $this->translateNthChild($xpath, $function, true); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + */ + public function translateNthOfType(XPathExpr $xpath, FunctionNode $function) + { + return $this->translateNthChild($xpath, $function, false, false); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.'); + } + + return $this->translateNthChild($xpath, $function, true, false); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateContains(XPathExpr $xpath, FunctionNode $function) + { + $arguments = $function->getArguments(); + foreach ($arguments as $token) { + if (!($token->isString() || $token->isIdentifier())) { + throw new ExpressionErrorException( + 'Expected a single string or identifier for :contains(), got ' + .implode(', ', $arguments) + ); + } + } + + return $xpath->addCondition(sprintf( + 'contains(string(.), %s)', + Translator::getXpathLiteral($arguments[0]->getValue()) + )); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateLang(XPathExpr $xpath, FunctionNode $function) + { + $arguments = $function->getArguments(); + foreach ($arguments as $token) { + if (!($token->isString() || $token->isIdentifier())) { + throw new ExpressionErrorException( + 'Expected a single string or identifier for :lang(), got ' + .implode(', ', $arguments) + ); + } + } + + return $xpath->addCondition(sprintf( + 'lang(%s)', + Translator::getXpathLiteral($arguments[0]->getValue()) + )); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'function'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php new file mode 100644 index 0000000..de6ce41 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/HtmlExtension.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator HTML extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class HtmlExtension extends AbstractExtension +{ + /** + * Constructor. + * + * @param Translator $translator + */ + public function __construct(Translator $translator) + { + $translator + ->getExtension('node') + ->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true) + ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true); + } + + /** + * {@inheritdoc} + */ + public function getPseudoClassTranslators() + { + return array( + 'checked' => array($this, 'translateChecked'), + 'link' => array($this, 'translateLink'), + 'disabled' => array($this, 'translateDisabled'), + 'enabled' => array($this, 'translateEnabled'), + 'selected' => array($this, 'translateSelected'), + 'invalid' => array($this, 'translateInvalid'), + 'hover' => array($this, 'translateHover'), + 'visited' => array($this, 'translateVisited'), + ); + } + + /** + * {@inheritdoc} + */ + public function getFunctionTranslators() + { + return array( + 'lang' => array($this, 'translateLang'), + ); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateChecked(XPathExpr $xpath) + { + return $xpath->addCondition( + '(@checked ' + ."and (name(.) = 'input' or name(.) = 'command')" + ."and (@type = 'checkbox' or @type = 'radio'))" + ); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateLink(XPathExpr $xpath) + { + return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')"); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateDisabled(XPathExpr $xpath) + { + return $xpath->addCondition( + '(' + .'@disabled and' + .'(' + ."(name(.) = 'input' and @type != 'hidden')" + ." or name(.) = 'button'" + ." or name(.) = 'select'" + ." or name(.) = 'textarea'" + ." or name(.) = 'command'" + ." or name(.) = 'fieldset'" + ." or name(.) = 'optgroup'" + ." or name(.) = 'option'" + .')' + .') or (' + ."(name(.) = 'input' and @type != 'hidden')" + ." or name(.) = 'button'" + ." or name(.) = 'select'" + ." or name(.) = 'textarea'" + .')' + .' and ancestor::fieldset[@disabled]' + ); + // todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any." + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateEnabled(XPathExpr $xpath) + { + return $xpath->addCondition( + '(' + .'@href and (' + ."name(.) = 'a'" + ." or name(.) = 'link'" + ." or name(.) = 'area'" + .')' + .') or (' + .'(' + ."name(.) = 'command'" + ." or name(.) = 'fieldset'" + ." or name(.) = 'optgroup'" + .')' + .' and not(@disabled)' + .') or (' + .'(' + ."(name(.) = 'input' and @type != 'hidden')" + ." or name(.) = 'button'" + ." or name(.) = 'select'" + ." or name(.) = 'textarea'" + ." or name(.) = 'keygen'" + .')' + .' and not (@disabled or ancestor::fieldset[@disabled])' + .') or (' + ."name(.) = 'option' and not(" + .'@disabled or ancestor::optgroup[@disabled]' + .')' + .')' + ); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateLang(XPathExpr $xpath, FunctionNode $function) + { + $arguments = $function->getArguments(); + foreach ($arguments as $token) { + if (!($token->isString() || $token->isIdentifier())) { + throw new ExpressionErrorException( + 'Expected a single string or identifier for :lang(), got ' + .implode(', ', $arguments) + ); + } + } + + return $xpath->addCondition(sprintf( + 'ancestor-or-self::*[@lang][1][starts-with(concat(' + ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')" + .', %s)]', + 'lang', + Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-') + )); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateSelected(XPathExpr $xpath) + { + return $xpath->addCondition("(@selected and name(.) = 'option')"); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateInvalid(XPathExpr $xpath) + { + return $xpath->addCondition('0'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateHover(XPathExpr $xpath) + { + return $xpath->addCondition('0'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateVisited(XPathExpr $xpath) + { + return $xpath->addCondition('0'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'html'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php new file mode 100644 index 0000000..9d7f8fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/NodeExtension.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Node; +use Symfony\Component\CssSelector\XPath\Translator; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator node extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class NodeExtension extends AbstractExtension +{ + const ELEMENT_NAME_IN_LOWER_CASE = 1; + const ATTRIBUTE_NAME_IN_LOWER_CASE = 2; + const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4; + + /** + * @var int + */ + private $flags; + + /** + * Constructor. + * + * @param int $flags + */ + public function __construct($flags = 0) + { + $this->flags = $flags; + } + + /** + * @param int $flag + * @param bool $on + * + * @return NodeExtension + */ + public function setFlag($flag, $on) + { + if ($on && !$this->hasFlag($flag)) { + $this->flags += $flag; + } + + if (!$on && $this->hasFlag($flag)) { + $this->flags -= $flag; + } + + return $this; + } + + /** + * @param int $flag + * + * @return bool + */ + public function hasFlag($flag) + { + return $this->flags & $flag; + } + + /** + * {@inheritdoc} + */ + public function getNodeTranslators() + { + return array( + 'Selector' => array($this, 'translateSelector'), + 'CombinedSelector' => array($this, 'translateCombinedSelector'), + 'Negation' => array($this, 'translateNegation'), + 'Function' => array($this, 'translateFunction'), + 'Pseudo' => array($this, 'translatePseudo'), + 'Attribute' => array($this, 'translateAttribute'), + 'Class' => array($this, 'translateClass'), + 'Hash' => array($this, 'translateHash'), + 'Element' => array($this, 'translateElement'), + ); + } + + /** + * @param Node\SelectorNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateSelector(Node\SelectorNode $node, Translator $translator) + { + return $translator->nodeToXPath($node->getTree()); + } + + /** + * @param Node\CombinedSelectorNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator) + { + return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); + } + + /** + * @param Node\NegationNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateNegation(Node\NegationNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + $subXpath = $translator->nodeToXPath($node->getSubSelector()); + $subXpath->addNameTest(); + + if ($subXpath->getCondition()) { + return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition())); + } + + return $xpath->addCondition('0'); + } + + /** + * @param Node\FunctionNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateFunction(Node\FunctionNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addFunction($xpath, $node); + } + + /** + * @param Node\PseudoNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translatePseudo(Node\PseudoNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addPseudoClass($xpath, $node->getIdentifier()); + } + + /** + * @param Node\AttributeNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateAttribute(Node\AttributeNode $node, Translator $translator) + { + $name = $node->getAttribute(); + $safe = $this->isSafeName($name); + + if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) { + $name = strtolower($name); + } + + if ($node->getNamespace()) { + $name = sprintf('%s:%s', $node->getNamespace(), $name); + $safe = $safe && $this->isSafeName($node->getNamespace()); + } + + $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); + $value = $node->getValue(); + $xpath = $translator->nodeToXPath($node->getSelector()); + + if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) { + $value = strtolower($value); + } + + return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); + } + + /** + * @param Node\ClassNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateClass(Node\ClassNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); + } + + /** + * @param Node\HashNode $node + * @param Translator $translator + * + * @return XPathExpr + */ + public function translateHash(Node\HashNode $node, Translator $translator) + { + $xpath = $translator->nodeToXPath($node->getSelector()); + + return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); + } + + /** + * @param Node\ElementNode $node + * + * @return XPathExpr + */ + public function translateElement(Node\ElementNode $node) + { + $element = $node->getElement(); + + if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) { + $element = strtolower($element); + } + + if ($element) { + $safe = $this->isSafeName($element); + } else { + $element = '*'; + $safe = true; + } + + if ($node->getNamespace()) { + $element = sprintf('%s:%s', $node->getNamespace(), $element); + $safe = $safe && $this->isSafeName($node->getNamespace()); + } + + $xpath = new XPathExpr('', $element); + + if (!$safe) { + $xpath->addNameTest(); + } + + return $xpath; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'node'; + } + + /** + * Tests if given name is safe. + * + * @param string $name + * + * @return bool + */ + private function isSafeName($name) + { + return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php new file mode 100644 index 0000000..1c8b217 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Extension/PseudoClassExtension.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath\Extension; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\XPath\XPathExpr; + +/** + * XPath expression translator pseudo-class extension. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class PseudoClassExtension extends AbstractExtension +{ + /** + * {@inheritdoc} + */ + public function getPseudoClassTranslators() + { + return array( + 'root' => array($this, 'translateRoot'), + 'first-child' => array($this, 'translateFirstChild'), + 'last-child' => array($this, 'translateLastChild'), + 'first-of-type' => array($this, 'translateFirstOfType'), + 'last-of-type' => array($this, 'translateLastOfType'), + 'only-child' => array($this, 'translateOnlyChild'), + 'only-of-type' => array($this, 'translateOnlyOfType'), + 'empty' => array($this, 'translateEmpty'), + ); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateRoot(XPathExpr $xpath) + { + return $xpath->addCondition('not(parent::*)'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateFirstChild(XPathExpr $xpath) + { + return $xpath + ->addStarPrefix() + ->addNameTest() + ->addCondition('position() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateLastChild(XPathExpr $xpath) + { + return $xpath + ->addStarPrefix() + ->addNameTest() + ->addCondition('position() = last()'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateFirstOfType(XPathExpr $xpath) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:first-of-type" is not implemented.'); + } + + return $xpath + ->addStarPrefix() + ->addCondition('position() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateLastOfType(XPathExpr $xpath) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:last-of-type" is not implemented.'); + } + + return $xpath + ->addStarPrefix() + ->addCondition('position() = last()'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateOnlyChild(XPathExpr $xpath) + { + return $xpath + ->addStarPrefix() + ->addNameTest() + ->addCondition('last() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function translateOnlyOfType(XPathExpr $xpath) + { + if ('*' === $xpath->getElement()) { + throw new ExpressionErrorException('"*:only-of-type" is not implemented.'); + } + + return $xpath->addCondition('last() = 1'); + } + + /** + * @param XPathExpr $xpath + * + * @return XPathExpr + */ + public function translateEmpty(XPathExpr $xpath) + { + return $xpath->addCondition('not(*) and not(string-length())'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pseudo-class'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Translator.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Translator.php new file mode 100644 index 0000000..8c021b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/Translator.php @@ -0,0 +1,301 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath; + +use Symfony\Component\CssSelector\Exception\ExpressionErrorException; +use Symfony\Component\CssSelector\Node\FunctionNode; +use Symfony\Component\CssSelector\Node\NodeInterface; +use Symfony\Component\CssSelector\Node\SelectorNode; +use Symfony\Component\CssSelector\Parser\Parser; +use Symfony\Component\CssSelector\Parser\ParserInterface; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class Translator implements TranslatorInterface +{ + /** + * @var ParserInterface + */ + private $mainParser; + + /** + * @var ParserInterface[] + */ + private $shortcutParsers = array(); + + /** + * @var Extension\ExtensionInterface + */ + private $extensions = array(); + + /** + * @var array + */ + private $nodeTranslators = array(); + + /** + * @var array + */ + private $combinationTranslators = array(); + + /** + * @var array + */ + private $functionTranslators = array(); + + /** + * @var array + */ + private $pseudoClassTranslators = array(); + + /** + * @var array + */ + private $attributeMatchingTranslators = array(); + + /** + * Constructor. + */ + public function __construct(ParserInterface $parser = null) + { + $this->mainParser = $parser ?: new Parser(); + + $this + ->registerExtension(new Extension\NodeExtension()) + ->registerExtension(new Extension\CombinationExtension()) + ->registerExtension(new Extension\FunctionExtension()) + ->registerExtension(new Extension\PseudoClassExtension()) + ->registerExtension(new Extension\AttributeMatchingExtension()) + ; + } + + /** + * @param string $element + * + * @return string + */ + public static function getXpathLiteral($element) + { + if (false === strpos($element, "'")) { + return "'".$element."'"; + } + + if (false === strpos($element, '"')) { + return '"'.$element.'"'; + } + + $string = $element; + $parts = array(); + while (true) { + if (false !== $pos = strpos($string, "'")) { + $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = "\"'\""; + $string = substr($string, $pos + 1); + } else { + $parts[] = "'$string'"; + break; + } + } + + return sprintf('concat(%s)', implode($parts, ', ')); + } + + /** + * {@inheritdoc} + */ + public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') + { + $selectors = $this->parseSelectors($cssExpr); + + /** @var SelectorNode $selector */ + foreach ($selectors as $index => $selector) { + if (null !== $selector->getPseudoElement()) { + throw new ExpressionErrorException('Pseudo-elements are not supported.'); + } + + $selectors[$index] = $this->selectorToXPath($selector, $prefix); + } + + return implode(' | ', $selectors); + } + + /** + * {@inheritdoc} + */ + public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::') + { + return ($prefix ?: '').$this->nodeToXPath($selector); + } + + /** + * Registers an extension. + * + * @param Extension\ExtensionInterface $extension + * + * @return Translator + */ + public function registerExtension(Extension\ExtensionInterface $extension) + { + $this->extensions[$extension->getName()] = $extension; + + $this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators()); + $this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators()); + $this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators()); + $this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators()); + $this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators()); + + return $this; + } + + /** + * @param string $name + * + * @return Extension\ExtensionInterface + * + * @throws ExpressionErrorException + */ + public function getExtension($name) + { + if (!isset($this->extensions[$name])) { + throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); + } + + return $this->extensions[$name]; + } + + /** + * Registers a shortcut parser. + * + * @param ParserInterface $shortcut + * + * @return Translator + */ + public function registerParserShortcut(ParserInterface $shortcut) + { + $this->shortcutParsers[] = $shortcut; + + return $this; + } + + /** + * @param NodeInterface $node + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function nodeToXPath(NodeInterface $node) + { + if (!isset($this->nodeTranslators[$node->getNodeName()])) { + throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); + } + + return call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); + } + + /** + * @param string $combiner + * @param NodeInterface $xpath + * @param NodeInterface $combinedXpath + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function addCombination($combiner, NodeInterface $xpath, NodeInterface $combinedXpath) + { + if (!isset($this->combinationTranslators[$combiner])) { + throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); + } + + return call_user_func($this->combinationTranslators[$combiner], $this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); + } + + /** + * @param XPathExpr $xpath + * @param FunctionNode $function + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function addFunction(XPathExpr $xpath, FunctionNode $function) + { + if (!isset($this->functionTranslators[$function->getName()])) { + throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); + } + + return call_user_func($this->functionTranslators[$function->getName()], $xpath, $function); + } + + /** + * @param XPathExpr $xpath + * @param string $pseudoClass + * + * @return XPathExpr + * + * @throws ExpressionErrorException + */ + public function addPseudoClass(XPathExpr $xpath, $pseudoClass) + { + if (!isset($this->pseudoClassTranslators[$pseudoClass])) { + throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); + } + + return call_user_func($this->pseudoClassTranslators[$pseudoClass], $xpath); + } + + /** + * @param XPathExpr $xpath + * @param string $operator + * @param string $attribute + * @param string $value + * + * @throws ExpressionErrorException + * + * @return XPathExpr + */ + public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value) + { + if (!isset($this->attributeMatchingTranslators[$operator])) { + throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); + } + + return call_user_func($this->attributeMatchingTranslators[$operator], $xpath, $attribute, $value); + } + + /** + * @param string $css + * + * @return SelectorNode[] + */ + private function parseSelectors($css) + { + foreach ($this->shortcutParsers as $shortcut) { + $tokens = $shortcut->parse($css); + + if (!empty($tokens)) { + return $tokens; + } + } + + return $this->mainParser->parse($css); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.php new file mode 100644 index 0000000..0b5de83 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/TranslatorInterface.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\CssSelector\XPath; + +use Symfony\Component\CssSelector\Node\SelectorNode; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +interface TranslatorInterface +{ + /** + * Translates a CSS selector to an XPath expression. + * + * @param string $cssExpr + * @param string $prefix + * + * @return string + */ + public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::'); + + /** + * Translates a parsed selector node to an XPath expression. + * + * @param SelectorNode $selector + * @param string $prefix + * + * @return string + */ + public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::'); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/XPathExpr.php b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/XPathExpr.php new file mode 100644 index 0000000..420ef3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/XPath/XPathExpr.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\XPath; + +/** + * XPath expression translator interface. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Jean-François Simon + * + * @internal + */ +class XPathExpr +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $element; + + /** + * @var string + */ + private $condition; + + /** + * @param string $path + * @param string $element + * @param string $condition + * @param bool $starPrefix + */ + public function __construct($path = '', $element = '*', $condition = '', $starPrefix = false) + { + $this->path = $path; + $this->element = $element; + $this->condition = $condition; + + if ($starPrefix) { + $this->addStarPrefix(); + } + } + + /** + * @return string + */ + public function getElement() + { + return $this->element; + } + + /** + * @param $condition + * + * @return XPathExpr + */ + public function addCondition($condition) + { + $this->condition = $this->condition ? sprintf('%s and (%s)', $this->condition, $condition) : $condition; + + return $this; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return XPathExpr + */ + public function addNameTest() + { + if ('*' !== $this->element) { + $this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); + $this->element = '*'; + } + + return $this; + } + + /** + * @return XPathExpr + */ + public function addStarPrefix() + { + $this->path .= '*/'; + + return $this; + } + + /** + * Joins another XPathExpr with a combiner. + * + * @param string $combiner + * @param XPathExpr $expr + * + * @return XPathExpr + */ + public function join($combiner, XPathExpr $expr) + { + $path = $this->__toString().$combiner; + + if ('*/' !== $expr->path) { + $path .= $expr->path; + } + + $this->path = $path; + $this->element = $expr->element; + $this->condition = $expr->condition; + + return $this; + } + + /** + * @return string + */ + public function __toString() + { + $path = $this->path.$this->element; + $condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']'; + + return $path.$condition; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/composer.json b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/composer.json new file mode 100644 index 0000000..340a363 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/css-selector", + "type": "library", + "description": "Symfony CssSelector Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/CssSelector/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/phpunit.xml.dist new file mode 100644 index 0000000..14a320c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/CssSelector/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/BufferingLogger.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/BufferingLogger.php new file mode 100644 index 0000000..a2ed75b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/BufferingLogger.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Psr\Log\AbstractLogger; + +/** + * A buffering logger that stacks logs for later. + * + * @author Nicolas Grekas + */ +class BufferingLogger extends AbstractLogger +{ + private $logs = array(); + + public function log($level, $message, array $context = array()) + { + $this->logs[] = array($level, $message, $context); + } + + public function cleanLogs() + { + $logs = $this->logs; + $this->logs = array(); + + return $logs; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/Debug/CHANGELOG.md new file mode 100644 index 0000000..c81bccf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/CHANGELOG.md @@ -0,0 +1,47 @@ +CHANGELOG +========= + +3.0.0 +----- + +* removed classes, methods and interfaces deprecated in 2.x + +2.8.0 +----- + +* added BufferingLogger for errors that happen before a proper logger is configured +* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);` +* deprecate ExceptionHandler::createResponse + +2.7.0 +----- + +* added deprecations checking for parent interfaces/classes to DebugClassLoader +* added ZTS support to symfony_debug extension +* added symfony_debug_backtrace() to symfony_debug extension + to track the backtrace of fatal errors + +2.6.0 +----- + +* generalized ErrorHandler and ExceptionHandler, + with some new methods and others deprecated +* enhanced error messages for uncaught exceptions + +2.5.0 +----- + +* added ExceptionHandler::setHandler() +* added UndefinedMethodFatalErrorHandler +* deprecated DummyException + +2.4.0 +----- + + * added a DebugClassLoader able to wrap any autoloader providing a findFile method + * improved error messages for not found classes and functions + +2.3.0 +----- + + * added the component diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Debug.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Debug.php new file mode 100644 index 0000000..e26810c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Debug.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +/** + * Registers all the debug tools. + * + * @author Fabien Potencier + */ +class Debug +{ + private static $enabled = false; + + /** + * Enables the debug tools. + * + * This method registers an error handler and an exception handler. + * + * If the Symfony ClassLoader component is available, a special + * class loader is also registered. + * + * @param int $errorReportingLevel The level of error reporting you want + * @param bool $displayErrors Whether to display errors (for development) or just log them (for production) + */ + public static function enable($errorReportingLevel = E_ALL, $displayErrors = true) + { + if (static::$enabled) { + return; + } + + static::$enabled = true; + + if (null !== $errorReportingLevel) { + error_reporting($errorReportingLevel); + } else { + error_reporting(E_ALL); + } + + if ('cli' !== php_sapi_name()) { + ini_set('display_errors', 0); + ExceptionHandler::register(); + } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { + // CLI - display errors only if they're not already logged to STDERR + ini_set('display_errors', 1); + } + if ($displayErrors) { + ErrorHandler::register(new ErrorHandler(new BufferingLogger())); + } else { + ErrorHandler::register()->throwAt(0, true); + } + + DebugClassLoader::enable(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/DebugClassLoader.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/DebugClassLoader.php new file mode 100644 index 0000000..463af6e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/DebugClassLoader.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +/** + * Autoloader checking if the class is really defined in the file found. + * + * The ClassLoader will wrap all registered autoloaders + * and will throw an exception if a file is found but does + * not declare the class. + * + * @author Fabien Potencier + * @author Christophe Coevoet + * @author Nicolas Grekas + */ +class DebugClassLoader +{ + private $classLoader; + private $isFinder; + private static $caseCheck; + private static $deprecated = array(); + private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null'); + private static $darwinCache = array('/' => array('/', array())); + + /** + * Constructor. + * + * @param callable $classLoader A class loader + */ + public function __construct(callable $classLoader) + { + $this->classLoader = $classLoader; + $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); + + if (!isset(self::$caseCheck)) { + self::$caseCheck = false !== stripos(PHP_OS, 'win') ? (false !== stripos(PHP_OS, 'darwin') ? 2 : 1) : 0; + } + } + + /** + * Gets the wrapped class loader. + * + * @return callable The wrapped class loader + */ + public function getClassLoader() + { + return $this->classLoader; + } + + /** + * Wraps all autoloaders. + */ + public static function enable() + { + // Ensures we don't hit https://bugs.php.net/42098 + class_exists('Symfony\Component\Debug\ErrorHandler'); + class_exists('Psr\Log\LogLevel'); + + if (!is_array($functions = spl_autoload_functions())) { + return; + } + + foreach ($functions as $function) { + spl_autoload_unregister($function); + } + + foreach ($functions as $function) { + if (!is_array($function) || !$function[0] instanceof self) { + $function = array(new static($function), 'loadClass'); + } + + spl_autoload_register($function); + } + } + + /** + * Disables the wrapping. + */ + public static function disable() + { + if (!is_array($functions = spl_autoload_functions())) { + return; + } + + foreach ($functions as $function) { + spl_autoload_unregister($function); + } + + foreach ($functions as $function) { + if (is_array($function) && $function[0] instanceof self) { + $function = $function[0]->getClassLoader(); + } + + spl_autoload_register($function); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + * + * @throws \RuntimeException + */ + public function loadClass($class) + { + ErrorHandler::stackErrors(); + + try { + if ($this->isFinder) { + if ($file = $this->classLoader[0]->findFile($class)) { + require_once $file; + } + } else { + call_user_func($this->classLoader, $class); + $file = false; + } + } finally { + ErrorHandler::unstackErrors(); + } + + $exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + + if ('\\' === $class[0]) { + $class = substr($class, 1); + } + + if ($exists) { + $refl = new \ReflectionClass($class); + $name = $refl->getName(); + + if ($name !== $class && 0 === strcasecmp($name, $class)) { + throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name)); + } + + if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) { + @trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); + } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) { + self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]); + } else { + if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) { + $len = 0; + $ns = ''; + } else { + switch ($ns = substr($name, 0, $len)) { + case 'Symfony\Bridge\\': + case 'Symfony\Bundle\\': + case 'Symfony\Component\\': + $ns = 'Symfony\\'; + $len = strlen($ns); + break; + } + } + $parent = $refl->getParentClass(); + + if (!$parent || strncmp($ns, $parent->name, $len)) { + if ($parent && isset(self::$deprecated[$parent->name]) && strncmp($ns, $parent->name, $len)) { + @trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent->name, self::$deprecated[$parent->name]), E_USER_DEPRECATED); + } + + foreach ($refl->getInterfaceNames() as $interface) { + if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len) && !($parent && $parent->implementsInterface($interface))) { + @trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED); + } + } + } + } + } + + if ($file) { + if (!$exists) { + if (false !== strpos($class, '/')) { + throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); + } + + throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); + } + if (self::$caseCheck) { + $real = explode('\\', $class.strrchr($file, '.')); + $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file)); + + $i = count($tail) - 1; + $j = count($real) - 1; + + while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { + --$i; + --$j; + } + + array_splice($tail, 0, $i + 1); + } + if (self::$caseCheck && $tail) { + $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail); + $tailLen = strlen($tail); + $real = $refl->getFileName(); + + if (2 === self::$caseCheck) { + // realpath() on MacOSX doesn't normalize the case of characters + + $i = 1 + strrpos($real, '/'); + $file = substr($real, $i); + $real = substr($real, 0, $i); + + if (isset(self::$darwinCache[$real])) { + $kDir = $real; + } else { + $kDir = strtolower($real); + + if (isset(self::$darwinCache[$kDir])) { + $real = self::$darwinCache[$kDir][0]; + } else { + $dir = getcwd(); + chdir($real); + $real = getcwd().'/'; + chdir($dir); + + $dir = $real; + $k = $kDir; + $i = strlen($dir) - 1; + while (!isset(self::$darwinCache[$k])) { + self::$darwinCache[$k] = array($dir, array()); + self::$darwinCache[$dir] = &self::$darwinCache[$k]; + + while ('/' !== $dir[--$i]) { + } + $k = substr($k, 0, ++$i); + $dir = substr($dir, 0, $i--); + } + } + } + + $dirFiles = self::$darwinCache[$kDir][1]; + + if (isset($dirFiles[$file])) { + $kFile = $file; + } else { + $kFile = strtolower($file); + + if (!isset($dirFiles[$kFile])) { + foreach (scandir($real, 2) as $f) { + if ('.' !== $f[0]) { + $dirFiles[$f] = $f; + if ($f === $file) { + $kFile = $k = $file; + } elseif ($f !== $k = strtolower($f)) { + $dirFiles[$k] = $f; + } + } + } + self::$darwinCache[$kDir][1] = $dirFiles; + } + } + + $real .= $dirFiles[$kFile]; + } + + if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) + && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) + ) { + throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1))); + } + } + + return true; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/ErrorHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/ErrorHandler.php new file mode 100644 index 0000000..0725687 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/ErrorHandler.php @@ -0,0 +1,652 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Psr\Log\LogLevel; +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\ContextErrorException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\FatalThrowableError; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; + +/** + * A generic ErrorHandler for the PHP engine. + * + * Provides five bit fields that control how errors are handled: + * - thrownErrors: errors thrown as \ErrorException + * - loggedErrors: logged errors, when not @-silenced + * - scopedErrors: errors thrown or logged with their local context + * - tracedErrors: errors logged with their stack trace, only once for repeated errors + * - screamedErrors: never @-silenced errors + * + * Each error level can be logged by a dedicated PSR-3 logger object. + * Screaming only applies to logging. + * Throwing takes precedence over logging. + * Uncaught exceptions are logged as E_ERROR. + * E_DEPRECATED and E_USER_DEPRECATED levels never throw. + * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. + * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. + * As errors have a performance cost, repeated errors are all logged, so that the developer + * can see them and weight them as more important to fix than others of the same level. + * + * @author Nicolas Grekas + */ +class ErrorHandler +{ + private $levels = array( + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice', + E_WARNING => 'Warning', + E_USER_WARNING => 'User Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_CORE_WARNING => 'Core Warning', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Catchable Fatal Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse Error', + E_ERROR => 'Error', + E_CORE_ERROR => 'Core Error', + ); + + private $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array(null, LogLevel::WARNING), + E_USER_NOTICE => array(null, LogLevel::WARNING), + E_STRICT => array(null, LogLevel::WARNING), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), + E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), + E_PARSE => array(null, LogLevel::CRITICAL), + E_ERROR => array(null, LogLevel::CRITICAL), + E_CORE_ERROR => array(null, LogLevel::CRITICAL), + ); + + private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE + private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE + private $loggedErrors = 0; + + private $loggedTraces = array(); + private $isRecursive = 0; + private $exceptionHandler; + private $bootstrappingLogger; + + private static $reservedMemory; + private static $stackedErrors = array(); + private static $stackedErrorLevels = array(); + private static $toStringException = null; + + /** + * Registers the error handler. + * + * @param self|null $handler The handler to register + * @param bool $replace Whether to replace or not any existing handler + * + * @return self The registered error handler + */ + public static function register(self $handler = null, $replace = true) + { + if (null === self::$reservedMemory) { + self::$reservedMemory = str_repeat('x', 10240); + register_shutdown_function(__CLASS__.'::handleFatalError'); + } + + if ($handlerIsNew = null === $handler) { + $handler = new static(); + } + + $prev = set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors); + + if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) { + $handler = $prev[0]; + $replace = false; + } + if ($replace || !$prev) { + $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); + } else { + restore_error_handler(); + } + + $handler->throwAt(E_ALL & $handler->thrownErrors, true); + + return $handler; + } + + public function __construct(BufferingLogger $bootstrappingLogger = null) + { + if ($bootstrappingLogger) { + $this->bootstrappingLogger = $bootstrappingLogger; + $this->setDefaultLogger($bootstrappingLogger); + } + } + + /** + * Sets a logger to non assigned errors levels. + * + * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param bool $replace Whether to replace or not any existing logger + */ + public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false) + { + $loggers = array(); + + if (is_array($levels)) { + foreach ($levels as $type => $logLevel) { + if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { + $loggers[$type] = array($logger, $logLevel); + } + } + } else { + if (null === $levels) { + $levels = E_ALL; + } + foreach ($this->loggers as $type => $log) { + if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { + $log[0] = $logger; + $loggers[$type] = $log; + } + } + } + + $this->setLoggers($loggers); + } + + /** + * Sets a logger for each error level. + * + * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map + * + * @return array The previous map + * + * @throws \InvalidArgumentException + */ + public function setLoggers(array $loggers) + { + $prevLogged = $this->loggedErrors; + $prev = $this->loggers; + $flush = array(); + + foreach ($loggers as $type => $log) { + if (!isset($prev[$type])) { + throw new \InvalidArgumentException('Unknown error type: '.$type); + } + if (!is_array($log)) { + $log = array($log); + } elseif (!array_key_exists(0, $log)) { + throw new \InvalidArgumentException('No logger provided'); + } + if (null === $log[0]) { + $this->loggedErrors &= ~$type; + } elseif ($log[0] instanceof LoggerInterface) { + $this->loggedErrors |= $type; + } else { + throw new \InvalidArgumentException('Invalid logger provided'); + } + $this->loggers[$type] = $log + $prev[$type]; + + if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { + $flush[$type] = $type; + } + } + $this->reRegister($prevLogged | $this->thrownErrors); + + if ($flush) { + foreach ($this->bootstrappingLogger->cleanLogs() as $log) { + $type = $log[2]['type']; + if (!isset($flush[$type])) { + $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); + } elseif ($this->loggers[$type][0]) { + $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); + } + } + } + + return $prev; + } + + /** + * Sets a user exception handler. + * + * @param callable $handler A handler that will be called on Exception + * + * @return callable|null The previous exception handler + */ + public function setExceptionHandler(callable $handler = null) + { + $prev = $this->exceptionHandler; + $this->exceptionHandler = $handler; + + return $prev; + } + + /** + * Sets the PHP error levels that throw an exception when a PHP error occurs. + * + * @param int $levels A bit field of E_* constants for thrown errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function throwAt($levels, $replace = false) + { + $prev = $this->thrownErrors; + $this->thrownErrors = E_ALL & ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; + if (!$replace) { + $this->thrownErrors |= $prev; + } + $this->reRegister($prev | $this->loggedErrors); + + return $prev; + } + + /** + * Sets the PHP error levels for which local variables are preserved. + * + * @param int $levels A bit field of E_* constants for scoped errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function scopeAt($levels, $replace = false) + { + $prev = $this->scopedErrors; + $this->scopedErrors = (int) $levels; + if (!$replace) { + $this->scopedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the PHP error levels for which the stack trace is preserved. + * + * @param int $levels A bit field of E_* constants for traced errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function traceAt($levels, $replace = false) + { + $prev = $this->tracedErrors; + $this->tracedErrors = (int) $levels; + if (!$replace) { + $this->tracedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the error levels where the @-operator is ignored. + * + * @param int $levels A bit field of E_* constants for screamed errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function screamAt($levels, $replace = false) + { + $prev = $this->screamedErrors; + $this->screamedErrors = (int) $levels; + if (!$replace) { + $this->screamedErrors |= $prev; + } + + return $prev; + } + + /** + * Re-registers as a PHP error handler if levels changed. + */ + private function reRegister($prev) + { + if ($prev !== $this->thrownErrors | $this->loggedErrors) { + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler === $this) { + restore_error_handler(); + set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors); + } + } + } + + /** + * Handles errors by filtering then logging them according to the configured bit fields. + * + * @param int $type One of the E_* constants + * @param string $file + * @param int $line + * @param array $context + * + * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself. + * + * @throws \ErrorException When $this->thrownErrors requests so + * + * @internal + */ + public function handleError($type, $message, $file, $line, array $context, array $backtrace = null) + { + $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; + $log = $this->loggedErrors & $type; + $throw = $this->thrownErrors & $type & $level; + $type &= $level | $this->screamedErrors; + + if (!$type || (!$log && !$throw)) { + return $type && $log; + } + + if (null !== $backtrace && $type & E_ERROR) { + // E_ERROR fatal errors are triggered on HHVM when + // hhvm.error_handling.call_user_handler_on_fatals=1 + // which is the way to get their backtrace. + $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); + + return true; + } + + if ($throw) { + if (null !== self::$toStringException) { + $throw = self::$toStringException; + self::$toStringException = null; + } elseif (($this->scopedErrors & $type) && class_exists(ContextErrorException::class)) { + $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context); + } else { + $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line); + } + + if (E_USER_ERROR & $type) { + $backtrace = $backtrace ?: $throw->getTrace(); + + for ($i = 1; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) + && '__toString' === $backtrace[$i]['function'] + && '->' === $backtrace[$i]['type'] + && !isset($backtrace[$i - 1]['class']) + && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) + ) { + // Here, we know trigger_error() has been called from __toString(). + // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. + // A small convention allows working around the limitation: + // given a caught $e exception in __toString(), quitting the method with + // `return trigger_error($e, E_USER_ERROR);` allows this error handler + // to make $e get through the __toString() barrier. + + foreach ($context as $e) { + if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { + if (1 === $i) { + // On HHVM + $throw = $e; + break; + } + self::$toStringException = $e; + + return true; + } + } + + if (1 < $i) { + // On PHP (not on HHVM), display the original error message instead of the default one. + $this->handleException($throw); + + // Stop the process by giving back the error to the native handler. + return false; + } + } + } + } + + throw $throw; + } + + // For duplicated errors, log the trace only once + $e = md5("{$type}/{$line}/{$file}\x00{$message}", true); + $trace = true; + + if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) { + $trace = false; + } else { + $this->loggedTraces[$e] = 1; + } + + $e = compact('type', 'file', 'line', 'level'); + + if ($type & $level) { + if ($this->scopedErrors & $type) { + $e['scope_vars'] = $context; + if ($trace) { + $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); + } + } elseif ($trace) { + if (null === $backtrace) { + $e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } else { + foreach ($backtrace as &$frame) { + unset($frame['args'], $frame); + } + $e['stack'] = $backtrace; + } + } + } + + if ($this->isRecursive) { + $log = 0; + } elseif (self::$stackedErrorLevels) { + self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e); + } else { + try { + $this->isRecursive = true; + $this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e); + } finally { + $this->isRecursive = false; + } + } + + return $type && $log; + } + + /** + * Handles an exception by logging then forwarding it to another handler. + * + * @param \Exception|\Throwable $exception An exception to handle + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public function handleException($exception, array $error = null) + { + if (!$exception instanceof \Exception) { + $exception = new FatalThrowableError($exception); + } + $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; + + if ($this->loggedErrors & $type) { + $e = array( + 'type' => $type, + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'level' => error_reporting(), + 'stack' => $exception->getTrace(), + ); + if ($exception instanceof FatalErrorException) { + if ($exception instanceof FatalThrowableError) { + $error = array( + 'type' => $type, + 'message' => $message = $exception->getMessage(), + 'file' => $e['file'], + 'line' => $e['line'], + ); + } else { + $message = 'Fatal '.$exception->getMessage(); + } + } elseif ($exception instanceof \ErrorException) { + $message = 'Uncaught '.$exception->getMessage(); + if ($exception instanceof ContextErrorException) { + $e['context'] = $exception->getContext(); + } + } else { + $message = 'Uncaught Exception: '.$exception->getMessage(); + } + if ($this->loggedErrors & $e['type']) { + $this->loggers[$e['type']][0]->log($this->loggers[$e['type']][1], $message, $e); + } + } + if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { + foreach ($this->getFatalErrorHandlers() as $handler) { + if ($e = $handler->handleError($error, $exception)) { + $exception = $e; + break; + } + } + } + if (empty($this->exceptionHandler)) { + throw $exception; // Give back $exception to the native handler + } + try { + call_user_func($this->exceptionHandler, $exception); + } catch (\Exception $handlerException) { + } catch (\Throwable $handlerException) { + } + if (isset($handlerException)) { + $this->exceptionHandler = null; + $this->handleException($handlerException); + } + } + + /** + * Shutdown registered function for handling PHP fatal errors. + * + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public static function handleFatalError(array $error = null) + { + if (null === self::$reservedMemory) { + return; + } + + self::$reservedMemory = null; + + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + + if (!$handler instanceof self) { + return; + } + + if (null === $error) { + $error = error_get_last(); + } + + try { + while (self::$stackedErrorLevels) { + static::unstackErrors(); + } + } catch (\Exception $exception) { + // Handled below + } + + if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { + // Let's not throw anymore but keep logging + $handler->throwAt(0, true); + $trace = isset($error['backtrace']) ? $error['backtrace'] : null; + + if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { + $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); + } else { + $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); + } + } elseif (!isset($exception)) { + return; + } + + try { + $handler->handleException($exception, $error); + } catch (FatalErrorException $e) { + // Ignore this re-throw + } + } + + /** + * Configures the error handler for delayed handling. + * Ensures also that non-catchable fatal errors are never silenced. + * + * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 + * PHP has a compile stage where it behaves unusually. To workaround it, + * we plug an error handler that only stacks errors for later. + * + * The most important feature of this is to prevent + * autoloading until unstackErrors() is called. + */ + public static function stackErrors() + { + self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); + } + + /** + * Unstacks stacked errors and forwards to the logger. + */ + public static function unstackErrors() + { + $level = array_pop(self::$stackedErrorLevels); + + if (null !== $level) { + $e = error_reporting($level); + if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) { + // If the user changed the error level, do not overwrite it + error_reporting($e); + } + } + + if (empty(self::$stackedErrorLevels)) { + $errors = self::$stackedErrors; + self::$stackedErrors = array(); + + foreach ($errors as $e) { + $e[0]->log($e[1], $e[2], $e[3]); + } + } + } + + /** + * Gets the fatal error handlers. + * + * Override this method if you want to define more fatal error handlers. + * + * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface + */ + protected function getFatalErrorHandlers() + { + return array( + new UndefinedFunctionFatalErrorHandler(), + new UndefinedMethodFatalErrorHandler(), + new ClassNotFoundFatalErrorHandler(), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php new file mode 100644 index 0000000..b91bf46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ClassNotFoundException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Class (or Trait or Interface) Not Found Exception. + * + * @author Konstanton Myakshin + */ +class ClassNotFoundException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ContextErrorException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ContextErrorException.php new file mode 100644 index 0000000..54f0198 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ContextErrorException.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Error Exception with Variable Context. + * + * @author Christian Sciberras + */ +class ContextErrorException extends \ErrorException +{ + private $context = array(); + + public function __construct($message, $code, $severity, $filename, $lineno, $context = array()) + { + parent::__construct($message, $code, $severity, $filename, $lineno); + $this->context = $context; + } + + /** + * @return array Array of variables that existed when the exception occurred + */ + public function getContext() + { + return $this->context; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalErrorException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalErrorException.php new file mode 100644 index 0000000..f24a54e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalErrorException.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Fatal Error Exception. + * + * @author Konstanton Myakshin + */ +class FatalErrorException extends \ErrorException +{ + public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null) + { + parent::__construct($message, $code, $severity, $filename, $lineno); + + if (null !== $trace) { + if (!$traceArgs) { + foreach ($trace as &$frame) { + unset($frame['args'], $frame['this'], $frame); + } + } + + $this->setTrace($trace); + } elseif (null !== $traceOffset) { + if (function_exists('xdebug_get_function_stack')) { + $trace = xdebug_get_function_stack(); + if (0 < $traceOffset) { + array_splice($trace, -$traceOffset); + } + + foreach ($trace as &$frame) { + if (!isset($frame['type'])) { + // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 + if (isset($frame['class'])) { + $frame['type'] = '::'; + } + } elseif ('dynamic' === $frame['type']) { + $frame['type'] = '->'; + } elseif ('static' === $frame['type']) { + $frame['type'] = '::'; + } + + // XDebug also has a different name for the parameters array + if (!$traceArgs) { + unset($frame['params'], $frame['args']); + } elseif (isset($frame['params']) && !isset($frame['args'])) { + $frame['args'] = $frame['params']; + unset($frame['params']); + } + } + + unset($frame); + $trace = array_reverse($trace); + } elseif (function_exists('symfony_debug_backtrace')) { + $trace = symfony_debug_backtrace(); + if (0 < $traceOffset) { + array_splice($trace, 0, $traceOffset); + } + } else { + $trace = array(); + } + + $this->setTrace($trace); + } + } + + protected function setTrace($trace) + { + $traceReflector = new \ReflectionProperty('Exception', 'trace'); + $traceReflector->setAccessible(true); + $traceReflector->setValue($this, $trace); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalThrowableError.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalThrowableError.php new file mode 100644 index 0000000..6ff5ecd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FatalThrowableError.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Fatal Throwable Error. + * + * @author Nicolas Grekas + */ +class FatalThrowableError extends FatalErrorException +{ + public function __construct(\Throwable $e) + { + if ($e instanceof \ParseError) { + $message = 'Parse error: '.$e->getMessage(); + $severity = E_PARSE; + } elseif ($e instanceof \TypeError) { + $message = 'Type error: '.$e->getMessage(); + $severity = E_RECOVERABLE_ERROR; + } else { + $message = 'Fatal error: '.$e->getMessage(); + $severity = E_ERROR; + } + + \ErrorException::__construct( + $message, + $e->getCode(), + $severity, + $e->getFile(), + $e->getLine() + ); + + $this->setTrace($e->getTrace()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FlattenException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FlattenException.php new file mode 100644 index 0000000..5802c50 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/FlattenException.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + +/** + * FlattenException wraps a PHP Exception to be able to serialize it. + * + * Basically, this class removes all objects from the trace. + * + * @author Fabien Potencier + */ +class FlattenException +{ + private $message; + private $code; + private $previous; + private $trace; + private $class; + private $statusCode; + private $headers; + private $file; + private $line; + + public static function create(\Exception $exception, $statusCode = null, array $headers = array()) + { + $e = new static(); + $e->setMessage($exception->getMessage()); + $e->setCode($exception->getCode()); + + if ($exception instanceof HttpExceptionInterface) { + $statusCode = $exception->getStatusCode(); + $headers = array_merge($headers, $exception->getHeaders()); + } + + if (null === $statusCode) { + $statusCode = 500; + } + + $e->setStatusCode($statusCode); + $e->setHeaders($headers); + $e->setTraceFromException($exception); + $e->setClass(get_class($exception)); + $e->setFile($exception->getFile()); + $e->setLine($exception->getLine()); + + $previous = $exception->getPrevious(); + + if ($previous instanceof \Exception) { + $e->setPrevious(static::create($previous)); + } elseif ($previous instanceof \Throwable) { + $e->setPrevious(static::create(new FatalThrowableError($previous))); + } + + return $e; + } + + public function toArray() + { + $exceptions = array(); + foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) { + $exceptions[] = array( + 'message' => $exception->getMessage(), + 'class' => $exception->getClass(), + 'trace' => $exception->getTrace(), + ); + } + + return $exceptions; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function setStatusCode($code) + { + $this->statusCode = $code; + } + + public function getHeaders() + { + return $this->headers; + } + + public function setHeaders(array $headers) + { + $this->headers = $headers; + } + + public function getClass() + { + return $this->class; + } + + public function setClass($class) + { + $this->class = $class; + } + + public function getFile() + { + return $this->file; + } + + public function setFile($file) + { + $this->file = $file; + } + + public function getLine() + { + return $this->line; + } + + public function setLine($line) + { + $this->line = $line; + } + + public function getMessage() + { + return $this->message; + } + + public function setMessage($message) + { + $this->message = $message; + } + + public function getCode() + { + return $this->code; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function getPrevious() + { + return $this->previous; + } + + public function setPrevious(FlattenException $previous) + { + $this->previous = $previous; + } + + public function getAllPrevious() + { + $exceptions = array(); + $e = $this; + while ($e = $e->getPrevious()) { + $exceptions[] = $e; + } + + return $exceptions; + } + + public function getTrace() + { + return $this->trace; + } + + public function setTraceFromException(\Exception $exception) + { + $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine()); + } + + public function setTrace($trace, $file, $line) + { + $this->trace = array(); + $this->trace[] = array( + 'namespace' => '', + 'short_class' => '', + 'class' => '', + 'type' => '', + 'function' => '', + 'file' => $file, + 'line' => $line, + 'args' => array(), + ); + foreach ($trace as $entry) { + $class = ''; + $namespace = ''; + if (isset($entry['class'])) { + $parts = explode('\\', $entry['class']); + $class = array_pop($parts); + $namespace = implode('\\', $parts); + } + + $this->trace[] = array( + 'namespace' => $namespace, + 'short_class' => $class, + 'class' => isset($entry['class']) ? $entry['class'] : '', + 'type' => isset($entry['type']) ? $entry['type'] : '', + 'function' => isset($entry['function']) ? $entry['function'] : null, + 'file' => isset($entry['file']) ? $entry['file'] : null, + 'line' => isset($entry['line']) ? $entry['line'] : null, + 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(), + ); + } + } + + private function flattenArgs($args, $level = 0, &$count = 0) + { + $result = array(); + foreach ($args as $key => $value) { + if (++$count > 1e4) { + return array('array', '*SKIPPED over 10000 entries*'); + } + if (is_object($value)) { + $result[$key] = array('object', get_class($value)); + } elseif (is_array($value)) { + if ($level > 10) { + $result[$key] = array('array', '*DEEP NESTED ARRAY*'); + } else { + $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count)); + } + } elseif (null === $value) { + $result[$key] = array('null', null); + } elseif (is_bool($value)) { + $result[$key] = array('boolean', $value); + } elseif (is_resource($value)) { + $result[$key] = array('resource', get_resource_type($value)); + } elseif ($value instanceof \__PHP_Incomplete_Class) { + // Special case of object, is_object will return false + $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value)); + } else { + $result[$key] = array('string', (string) $value); + } + } + + return $result; + } + + private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) + { + $array = new \ArrayObject($value); + + return $array['__PHP_Incomplete_Class_Name']; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php new file mode 100644 index 0000000..fec1979 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Out of memory exception. + * + * @author Nicolas Grekas + */ +class OutOfMemoryException extends FatalErrorException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php new file mode 100644 index 0000000..a66ae2a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/UndefinedFunctionException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Undefined Function Exception. + * + * @author Konstanton Myakshin + */ +class UndefinedFunctionException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php new file mode 100644 index 0000000..350dc31 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/UndefinedMethodException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Undefined Method Exception. + * + * @author Grégoire Pineau + */ +class UndefinedMethodException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/ExceptionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/ExceptionHandler.php new file mode 100644 index 0000000..19cdb1f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/ExceptionHandler.php @@ -0,0 +1,414 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\Debug\Exception\OutOfMemoryException; + +/** + * ExceptionHandler converts an exception to a Response object. + * + * It is mostly useful in debug mode to replace the default PHP/XDebug + * output with something prettier and more useful. + * + * As this class is mainly used during Kernel boot, where nothing is yet + * available, the Response content is always HTML. + * + * @author Fabien Potencier + * @author Nicolas Grekas + */ +class ExceptionHandler +{ + private $debug; + private $charset; + private $handler; + private $caughtBuffer; + private $caughtLength; + private $fileLinkFormat; + + public function __construct($debug = true, $charset = null, $fileLinkFormat = null) + { + $this->debug = $debug; + $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + } + + /** + * Registers the exception handler. + * + * @param bool $debug Enable/disable debug mode, where the stack trace is displayed + * @param string|null $charset The charset used by exception messages + * @param string|null $fileLinkFormat The IDE link template + * + * @return ExceptionHandler The registered exception handler + */ + public static function register($debug = true, $charset = null, $fileLinkFormat = null) + { + $handler = new static($debug, $charset, $fileLinkFormat); + + $prev = set_exception_handler(array($handler, 'handle')); + if (is_array($prev) && $prev[0] instanceof ErrorHandler) { + restore_exception_handler(); + $prev[0]->setExceptionHandler(array($handler, 'handle')); + } + + return $handler; + } + + /** + * Sets a user exception handler. + * + * @param callable $handler An handler that will be called on Exception + * + * @return callable|null The previous exception handler if any + */ + public function setHandler(callable $handler = null) + { + $old = $this->handler; + $this->handler = $handler; + + return $old; + } + + /** + * Sets the format for links to source files. + * + * @param string $format The format for links to source files + * + * @return string The previous file link format. + */ + public function setFileLinkFormat($format) + { + $old = $this->fileLinkFormat; + $this->fileLinkFormat = $format; + + return $old; + } + + /** + * Sends a response for the given Exception. + * + * To be as fail-safe as possible, the exception is first handled + * by our simple exception handler, then by the user exception handler. + * The latter takes precedence and any output from the former is cancelled, + * if and only if nothing bad happens in this handling path. + */ + public function handle(\Exception $exception) + { + if (null === $this->handler || $exception instanceof OutOfMemoryException) { + $this->sendPhpResponse($exception); + + return; + } + + $caughtLength = $this->caughtLength = 0; + + ob_start(function ($buffer) { + $this->caughtBuffer = $buffer; + + return ''; + }); + + $this->sendPhpResponse($exception); + while (null === $this->caughtBuffer && ob_end_flush()) { + // Empty loop, everything is in the condition + } + if (isset($this->caughtBuffer[0])) { + ob_start(function ($buffer) { + if ($this->caughtLength) { + // use substr_replace() instead of substr() for mbstring overloading resistance + $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength); + if (isset($cleanBuffer[0])) { + $buffer = $cleanBuffer; + } + } + + return $buffer; + }); + + echo $this->caughtBuffer; + $caughtLength = ob_get_length(); + } + $this->caughtBuffer = null; + + try { + call_user_func($this->handler, $exception); + $this->caughtLength = $caughtLength; + } catch (\Exception $e) { + if (!$caughtLength) { + // All handlers failed. Let PHP handle that now. + throw $exception; + } + } + } + + /** + * Sends the error associated with the given Exception as a plain PHP response. + * + * This method uses plain PHP functions like header() and echo to output + * the response. + * + * @param \Exception|FlattenException $exception An \Exception or FlattenException instance + */ + public function sendPhpResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + if (!headers_sent()) { + header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); + foreach ($exception->getHeaders() as $name => $value) { + header($name.': '.$value, false); + } + header('Content-Type: text/html; charset='.$this->charset); + } + + echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Gets the full HTML content associated with the given exception. + * + * @param \Exception|FlattenException $exception An \Exception or FlattenException instance + * + * @return string The HTML content as a string + */ + public function getHtml($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + return $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Gets the HTML content associated with the given exception. + * + * @param FlattenException $exception A FlattenException instance + * + * @return string The content as a string + */ + public function getContent(FlattenException $exception) + { + switch ($exception->getStatusCode()) { + case 404: + $title = 'Sorry, the page you are looking for could not be found.'; + break; + default: + $title = 'Whoops, looks like something went wrong.'; + } + + $content = ''; + if ($this->debug) { + try { + $count = count($exception->getAllPrevious()); + $total = $count + 1; + foreach ($exception->toArray() as $position => $e) { + $ind = $count - $position + 1; + $class = $this->formatClass($e['class']); + $message = nl2br($this->escapeHtml($e['message'])); + $content .= sprintf(<< + %d/%d + %s%s: + %s + +
    +
      + +EOF + , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message); + foreach ($e['trace'] as $trace) { + $content .= '
    1. '; + if ($trace['function']) { + $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); + } + if (isset($trace['file']) && isset($trace['line'])) { + $content .= $this->formatPath($trace['file'], $trace['line']); + } + $content .= "
    2. \n"; + } + + $content .= "
    \n
    \n"; + } + } catch (\Exception $e) { + // something nasty happened and we cannot throw an exception anymore + if ($this->debug) { + $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); + } else { + $title = 'Whoops, looks like something went wrong.'; + } + } + } + + return << +

    $title

    + $content + +EOF; + } + + /** + * Gets the stylesheet associated with the given exception. + * + * @param FlattenException $exception A FlattenException instance + * + * @return string The stylesheet as a string + */ + public function getStylesheet(FlattenException $exception) + { + return << + + + + + + + + $content + + +EOF; + } + + private function formatClass($class) + { + $parts = explode('\\', $class); + + return sprintf('%s', $class, array_pop($parts)); + } + + private function formatPath($path, $line) + { + $path = $this->escapeHtml($path); + $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path; + + if ($linkFormat = $this->fileLinkFormat) { + $link = strtr($this->escapeHtml($linkFormat), array('%f' => $path, '%l' => (int) $line)); + + return sprintf(' in %s line %d', $link, $file, $line); + } + + return sprintf(' in %s line %d', $path, $file, $line); + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + private function formatArgs(array $args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $formattedValue = sprintf('object(%s)', $this->formatClass($item[1])); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('string' === $item[0]) { + $formattedValue = sprintf("'%s'", $this->escapeHtml($item[1])); + } elseif ('null' === $item[0]) { + $formattedValue = 'null'; + } elseif ('boolean' === $item[0]) { + $formattedValue = ''.strtolower(var_export($item[1], true)).''; + } elseif ('resource' === $item[0]) { + $formattedValue = 'resource'; + } else { + $formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true)); + } + + $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); + } + + return implode(', ', $result); + } + + /** + * HTML-encodes a string. + */ + private function escapeHtml($str) + { + return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php new file mode 100644 index 0000000..c48d0d3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\ClassNotFoundException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; + +/** + * ErrorHandler for classes that do not exist. + * + * @author Fabien Potencier + */ +class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '\' not found'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + foreach (array('class', 'interface', 'trait') as $typeName) { + $prefix = ucfirst($typeName).' \''; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + continue; + } + + $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { + $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); + $tail = ' for another namespace?'; + } else { + $className = $fullyQualifiedClassName; + $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); + $tail = '?'; + } + + if ($candidates = $this->getClassCandidates($className)) { + $tail = array_pop($candidates).'"?'; + if ($candidates) { + $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; + } else { + $tail = ' for "'.$tail; + } + } + $message .= "\nDid you forget a \"use\" statement".$tail; + + return new ClassNotFoundException($message, $exception); + } + } + + /** + * Tries to guess the full namespace for a given class name. + * + * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer + * autoloader (that should cover all common cases). + * + * @param string $class A class name (without its namespace) + * + * @return array An array of possible fully qualified class names + */ + private function getClassCandidates($class) + { + if (!is_array($functions = spl_autoload_functions())) { + return array(); + } + + // find Symfony and Composer autoloaders + $classes = array(); + + foreach ($functions as $function) { + if (!is_array($function)) { + continue; + } + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + + if (!is_array($function)) { + continue; + } + } + + if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) { + foreach ($function[0]->getPrefixes() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + if ($function[0] instanceof ComposerClassLoader) { + foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + } + + return array_unique($classes); + } + + /** + * @param string $path + * @param string $class + * @param string $prefix + * + * @return array + */ + private function findClassInPath($path, $class, $prefix) + { + if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { + return array(); + } + + $classes = array(); + $filename = $class.'.php'; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { + $classes[] = $class; + } + } + + return $classes; + } + + /** + * @param string $path + * @param string $file + * @param string $prefix + * + * @return string|null + */ + private function convertFileToClass($path, $file, $prefix) + { + $candidates = array( + // namespaced class + $namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file), + // namespaced class (with target dir) + $prefix.$namespacedClass, + // namespaced class (with target dir and separator) + $prefix.'\\'.$namespacedClass, + // PEAR class + str_replace('\\', '_', $namespacedClass), + // PEAR class (with target dir) + str_replace('\\', '_', $prefix.$namespacedClass), + // PEAR class (with target dir and separator) + str_replace('\\', '_', $prefix.'\\'.$namespacedClass), + ); + + if ($prefix) { + $candidates = array_filter($candidates, function ($candidate) use ($prefix) {return 0 === strpos($candidate, $prefix);}); + } + + // We cannot use the autoloader here as most of them use require; but if the class + // is not found, the new autoloader call will require the file again leading to a + // "cannot redeclare class" error. + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + + require_once $file; + + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + } + + /** + * @param string $class + * + * @return bool + */ + private function classExists($class) + { + return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php new file mode 100644 index 0000000..6b87eb3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/FatalErrorHandlerInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * Attempts to convert fatal errors to exceptions. + * + * @author Fabien Potencier + */ +interface FatalErrorHandlerInterface +{ + /** + * Attempts to convert an error into an exception. + * + * @param array $error An array as returned by error_get_last() + * @param FatalErrorException $exception A FatalErrorException instance + * + * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise + */ + public function handleError(array $error, FatalErrorException $exception); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php new file mode 100644 index 0000000..c6f391a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\UndefinedFunctionException; +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * ErrorHandler for undefined functions. + * + * @author Fabien Potencier + */ +class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '()'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + $prefix = 'Call to undefined function '; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + return; + } + + $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { + $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); + } else { + $functionName = $fullyQualifiedFunctionName; + $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); + } + + $candidates = array(); + foreach (get_defined_functions() as $type => $definedFunctionNames) { + foreach ($definedFunctionNames as $definedFunctionName) { + if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { + $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); + } else { + $definedFunctionNameBasename = $definedFunctionName; + } + + if ($definedFunctionNameBasename === $functionName) { + $candidates[] = '\\'.$definedFunctionName; + } + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedFunctionException($message, $exception); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php new file mode 100644 index 0000000..f734d6b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\UndefinedMethodException; + +/** + * ErrorHandler for undefined methods. + * + * @author Grégoire Pineau + */ +class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches); + if (!$matches) { + return; + } + + $className = $matches[1]; + $methodName = $matches[2]; + + $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); + + $candidates = array(); + foreach (get_class_methods($className) as $definedMethodName) { + $lev = levenshtein($methodName, $definedMethodName); + if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) { + $candidates[] = $definedMethodName; + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedMethodException($message, $exception); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Debug/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/README.md b/vendor/symfony/symfony/src/Symfony/Component/Debug/README.md new file mode 100644 index 0000000..eec5e1f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/README.md @@ -0,0 +1,42 @@ +Debug Component +=============== + +Debug provides tools to make debugging easier. + +Enabling all debug tools is as easy as calling the `enable()` method on the +main `Debug` class: + +```php +use Symfony\Component\Debug\Debug; + +Debug::enable(); +``` + +You can also use the tools individually: + +```php +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\ExceptionHandler; + +if ('cli' !== php_sapi_name()) { + ini_set('display_errors', 0); + ExceptionHandler::register(); +} elseif (!ini_get('log_errors') || ini_get('error_log')) { + ini_set('display_errors', 1); +} +ErrorHandler::register(); +DebugClassLoader::enable(); +``` + +This component can optionally take advantage of the features of the HttpKernel +component. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Debug/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/README.md b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/README.md new file mode 100644 index 0000000..0672ef8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/README.md @@ -0,0 +1,133 @@ +Symfony Debug Extension +======================= + +This extension publishes several functions to help building powerful debugging tools. + +symfony_zval_info() +------------------- + +- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP, +- does work with references, preventing memory copying. + +Its behavior is about the same as: + +```php + gettype($array[$key]), + 'zval_hash' => /* hashed memory address of $array[$key] */, + 'zval_refcount' => /* internal zval refcount of $array[$key] */, + 'zval_isref' => /* is_ref status of $array[$key] */, + ); + + switch ($info['type']) { + case 'object': + $info += array( + 'object_class' => get_class($array[$key]), + 'object_refcount' => /* internal object refcount of $array[$key] */, + 'object_hash' => spl_object_hash($array[$key]), + 'object_handle' => /* internal object handle $array[$key] */, + ); + break; + + case 'resource': + $info += array( + 'resource_handle' => (int) $array[$key], + 'resource_type' => get_resource_type($array[$key]), + 'resource_refcount' => /* internal resource refcount of $array[$key] */, + ); + break; + + case 'array': + $info += array( + 'array_count' => count($array[$key]), + ); + break; + + case 'string': + $info += array( + 'strlen' => strlen($array[$key]), + ); + break; + } + + return $info; +} +``` + +symfony_debug_backtrace() +------------------------- + +This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors: + +```php +function foo() { fatal(); } +function bar() { foo(); } + +function sd() { var_dump(symfony_debug_backtrace()); } + +register_shutdown_function('sd'); + +bar(); + +/* Will output +Fatal error: Call to undefined function fatal() in foo.php on line 42 +array(3) { + [0]=> + array(2) { + ["function"]=> + string(2) "sd" + ["args"]=> + array(0) { + } + } + [1]=> + array(4) { + ["file"]=> + string(7) "foo.php" + ["line"]=> + int(1) + ["function"]=> + string(3) "foo" + ["args"]=> + array(0) { + } + } + [2]=> + array(4) { + ["file"]=> + string(102) "foo.php" + ["line"]=> + int(2) + ["function"]=> + string(3) "bar" + ["args"]=> + array(0) { + } + } +} +*/ +``` + +Usage +----- + +The extension is compatible with ZTS mode, and should be supported by PHP5.3, 5.4, 5.5 and 5.6. +To enable the extension from source, run: + +``` + phpize + ./configure + make + sudo make install +``` diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/config.m4 b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/config.m4 new file mode 100644 index 0000000..3c56047 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension symfony_debug + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(symfony_debug, for symfony_debug support, +dnl Make sure that the comment is aligned: +dnl [ --with-symfony_debug Include symfony_debug support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(symfony_debug, whether to enable symfony_debug support, +dnl Make sure that the comment is aligned: +[ --enable-symfony_debug Enable symfony_debug support]) + +if test "$PHP_SYMFONY_DEBUG" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-symfony_debug -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/symfony_debug.h" # you most likely want to change this + dnl if test -r $PHP_SYMFONY_DEBUG/$SEARCH_FOR; then # path given as parameter + dnl SYMFONY_DEBUG_DIR=$PHP_SYMFONY_DEBUG + dnl else # search default path list + dnl AC_MSG_CHECKING([for symfony_debug files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl SYMFONY_DEBUG_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$SYMFONY_DEBUG_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the symfony_debug distribution]) + dnl fi + + dnl # --with-symfony_debug -> add include path + dnl PHP_ADD_INCLUDE($SYMFONY_DEBUG_DIR/include) + + dnl # --with-symfony_debug -> check for lib and symbol presence + dnl LIBNAME=symfony_debug # you may want to change this + dnl LIBSYMBOL=symfony_debug # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SYMFONY_DEBUG_DIR/lib, SYMFONY_DEBUG_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_SYMFONY_DEBUGLIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong symfony_debug lib version or lib not found]) + dnl ],[ + dnl -L$SYMFONY_DEBUG_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(SYMFONY_DEBUG_SHARED_LIBADD) + + PHP_NEW_EXTENSION(symfony_debug, symfony_debug.c, $ext_shared) +fi diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/config.w32 b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/config.w32 new file mode 100644 index 0000000..487e691 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("symfony_debug", "for symfony_debug support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("symfony_debug", "enable symfony_debug support", "no"); + +if (PHP_SYMFONY_DEBUG != "no") { + EXTENSION("symfony_debug", "symfony_debug.c"); +} + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h new file mode 100644 index 0000000..26d0e8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/php_symfony_debug.h @@ -0,0 +1,60 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifndef PHP_SYMFONY_DEBUG_H +#define PHP_SYMFONY_DEBUG_H + +extern zend_module_entry symfony_debug_module_entry; +#define phpext_symfony_debug_ptr &symfony_debug_module_entry + +#define PHP_SYMFONY_DEBUG_VERSION "2.7" + +#ifdef PHP_WIN32 +# define PHP_SYMFONY_DEBUG_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_SYMFONY_DEBUG_API __attribute__ ((visibility("default"))) +#else +# define PHP_SYMFONY_DEBUG_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +ZEND_BEGIN_MODULE_GLOBALS(symfony_debug) + intptr_t req_rand_init; + void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); + zval *debug_bt; +ZEND_END_MODULE_GLOBALS(symfony_debug) + +PHP_MINIT_FUNCTION(symfony_debug); +PHP_MSHUTDOWN_FUNCTION(symfony_debug); +PHP_RINIT_FUNCTION(symfony_debug); +PHP_RSHUTDOWN_FUNCTION(symfony_debug); +PHP_MINFO_FUNCTION(symfony_debug); +PHP_GINIT_FUNCTION(symfony_debug); +PHP_GSHUTDOWN_FUNCTION(symfony_debug); + +PHP_FUNCTION(symfony_zval_info); +PHP_FUNCTION(symfony_debug_backtrace); + +static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC); +static const char *_symfony_debug_zval_type(zval *); +static const char* _symfony_debug_get_resource_type(long TSRMLS_DC); +static int _symfony_debug_get_resource_refcount(long TSRMLS_DC); + +void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); + +#ifdef ZTS +#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v) +#else +#define SYMFONY_DEBUG_G(v) (symfony_debug_globals.v) +#endif + +#endif /* PHP_SYMFONY_DEBUG_H */ diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c new file mode 100644 index 0000000..0d7cb60 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/symfony_debug.c @@ -0,0 +1,283 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#ifdef ZTS +#include "TSRM.h" +#endif +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_symfony_debug.h" +#include "ext/standard/php_rand.h" +#include "ext/standard/php_lcg.h" +#include "ext/spl/php_spl.h" +#include "Zend/zend_gc.h" +#include "Zend/zend_builtin_functions.h" +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ +#include "ext/standard/php_array.h" +#include "Zend/zend_interfaces.h" +#include "SAPI.h" + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626 + +ZEND_DECLARE_MODULE_GLOBALS(symfony_debug) + +ZEND_BEGIN_ARG_INFO_EX(symfony_zval_arginfo, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, array, 0) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +const zend_function_entry symfony_debug_functions[] = { + PHP_FE(symfony_zval_info, symfony_zval_arginfo) + PHP_FE(symfony_debug_backtrace, NULL) + PHP_FE_END +}; + +PHP_FUNCTION(symfony_debug_backtrace) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +#if IS_PHP_53 + zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC); +#else + zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC); +#endif + + if (!SYMFONY_DEBUG_G(debug_bt)) { + return; + } + + php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC); +} + +PHP_FUNCTION(symfony_zval_info) +{ + zval *key = NULL, *arg = NULL; + zval **data = NULL; + HashTable *array = NULL; + long options = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) { + return; + } + + switch (Z_TYPE_P(key)) { + case IS_STRING: + if (zend_symtable_find(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&data) == FAILURE) { + return; + } + break; + case IS_LONG: + if (zend_hash_index_find(array, Z_LVAL_P(key), (void **)&data)) { + return; + } + break; + } + + arg = *data; + + array_init(return_value); + + add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1); + add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0); + add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg)); + add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg)); + + if (Z_TYPE_P(arg) == IS_OBJECT) { + char hash[33] = {0}; + + php_spl_object_hash(arg, (char *)hash TSRMLS_CC); + add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1); + add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount); + add_assoc_string(return_value, "object_hash", hash, 1); + add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg)); + } else if (Z_TYPE_P(arg) == IS_ARRAY) { + add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg))); + } else if(Z_TYPE_P(arg) == IS_RESOURCE) { + add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg)); + add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1); + add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC)); + } else if (Z_TYPE_P(arg) == IS_STRING) { + add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg)); + } +} + +void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) +{ + TSRMLS_FETCH(); + zval *retval; + + switch (type) { + case E_ERROR: + case E_PARSE: + case E_CORE_ERROR: + case E_CORE_WARNING: + case E_COMPILE_ERROR: + case E_COMPILE_WARNING: + ALLOC_INIT_ZVAL(retval); +#if IS_PHP_53 + zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC); +#else + zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC); +#endif + SYMFONY_DEBUG_G(debug_bt) = retval; + } + + SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args); +} + +static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC) +{ + const char *res_type; + res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC); + + if (!res_type) { + return "Unknown"; + } + + return res_type; +} + +static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC) +{ + zend_rsrc_list_entry *le; + + if (zend_hash_index_find(&EG(regular_list), rsid, (void **) &le)==SUCCESS) { + return le->refcount; + } + + return 0; +} + +static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC) +{ + char *result = NULL; + intptr_t address_rand; + + if (!SYMFONY_DEBUG_G(req_rand_init)) { + if (!BG(mt_rand_is_seeded)) { + php_mt_srand(GENERATE_SEED() TSRMLS_CC); + } + SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C); + } + + address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init); + + spprintf(&result, 17, "%016zx", address_rand); + + return result; +} + +static const char *_symfony_debug_zval_type(zval *zv) +{ + switch (Z_TYPE_P(zv)) { + case IS_NULL: + return "NULL"; + break; + + case IS_BOOL: + return "boolean"; + break; + + case IS_LONG: + return "integer"; + break; + + case IS_DOUBLE: + return "double"; + break; + + case IS_STRING: + return "string"; + break; + + case IS_ARRAY: + return "array"; + break; + + case IS_OBJECT: + return "object"; + + case IS_RESOURCE: + return "resource"; + + default: + return "unknown type"; + } +} + +zend_module_entry symfony_debug_module_entry = { + STANDARD_MODULE_HEADER, + "symfony_debug", + symfony_debug_functions, + PHP_MINIT(symfony_debug), + PHP_MSHUTDOWN(symfony_debug), + PHP_RINIT(symfony_debug), + PHP_RSHUTDOWN(symfony_debug), + PHP_MINFO(symfony_debug), + PHP_SYMFONY_DEBUG_VERSION, + PHP_MODULE_GLOBALS(symfony_debug), + PHP_GINIT(symfony_debug), + PHP_GSHUTDOWN(symfony_debug), + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; + +#ifdef COMPILE_DL_SYMFONY_DEBUG +ZEND_GET_MODULE(symfony_debug) +#endif + +PHP_GINIT_FUNCTION(symfony_debug) +{ + memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals)); +} + +PHP_GSHUTDOWN_FUNCTION(symfony_debug) +{ + +} + +PHP_MINIT_FUNCTION(symfony_debug) +{ + SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb; + zend_error_cb = symfony_debug_error_cb; + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(symfony_debug) +{ + zend_error_cb = SYMFONY_DEBUG_G(old_error_cb); + + return SUCCESS; +} + +PHP_RINIT_FUNCTION(symfony_debug) +{ + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(symfony_debug) +{ + return SUCCESS; +} + +PHP_MINFO_FUNCTION(symfony_debug) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "Symfony Debug support", "enabled"); + php_info_print_table_header(2, "Symfony Debug version", PHP_SYMFONY_DEBUG_VERSION); + php_info_print_table_end(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt new file mode 100644 index 0000000..4d41417 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/001.phpt @@ -0,0 +1,151 @@ +--TEST-- +Test symfony_zval_info API +--SKIPIF-- + +--FILE-- + $int, + 'float' => $float, + 'str' => $str, + 'object' => $object, + 'array' => $array, + 'resource' => $resource, + 'null' => $null, + 'bool' => $bool, + 'refcount' => &$refcount2); + +var_dump(symfony_zval_info('int', $var)); +var_dump(symfony_zval_info('float', $var)); +var_dump(symfony_zval_info('str', $var)); +var_dump(symfony_zval_info('object', $var)); +var_dump(symfony_zval_info('array', $var)); +var_dump(symfony_zval_info('resource', $var)); +var_dump(symfony_zval_info('null', $var)); +var_dump(symfony_zval_info('bool', $var)); + +var_dump(symfony_zval_info('refcount', $var)); +var_dump(symfony_zval_info('not-exist', $var)); +?> +--EXPECTF-- +array(4) { + ["type"]=> + string(7) "integer" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(6) "double" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(5) { + ["type"]=> + string(6) "string" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["strlen"]=> + int(6) +} +array(8) { + ["type"]=> + string(6) "object" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["object_class"]=> + string(8) "stdClass" + ["object_refcount"]=> + int(1) + ["object_hash"]=> + string(32) "%s" + ["object_handle"]=> + int(%d) +} +array(5) { + ["type"]=> + string(5) "array" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["array_count"]=> + int(2) +} +array(7) { + ["type"]=> + string(8) "resource" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["resource_handle"]=> + int(%d) + ["resource_type"]=> + string(6) "stream" + ["resource_refcount"]=> + int(1) +} +array(4) { + ["type"]=> + string(4) "NULL" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(7) "boolean" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(7) "integer" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(3) + ["zval_isref"]=> + bool(true) +} +NULL diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/002.phpt b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/002.phpt new file mode 100644 index 0000000..ebe2f32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/002.phpt @@ -0,0 +1,64 @@ +--TEST-- +Test symfony_debug_backtrace in case of fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Call to undefined function notexist() in %s on line %d +Array +( + [0] => Array + ( + [function] => bt + [args] => Array + ( + ) + + ) + + [1] => Array + ( + [file] => %s + [line] => %d + [function] => foo + [args] => Array + ( + ) + + ) + + [2] => Array + ( + [file] => %s + [line] => %d + [function] => bar + [args] => Array + ( + ) + + ) + +) diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/002_1.phpt b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/002_1.phpt new file mode 100644 index 0000000..4d52dbf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/002_1.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test symfony_debug_backtrace in case of non fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Array +( + [0] => Array + ( + [file] => %s + [line] => %d + [function] => bt + [args] => Array + ( + ) + + ) + + [1] => Array + ( + [file] => %s + [line] => %d + [function] => bar + [args] => Array + ( + ) + + ) + +) diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/003.phpt b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/003.phpt new file mode 100644 index 0000000..a363333 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Resources/ext/tests/003.phpt @@ -0,0 +1,85 @@ +--TEST-- +Test ErrorHandler in case of fatal error +--SKIPIF-- + +--FILE-- +setExceptionHandler('print_r'); + +if (function_exists('xdebug_disable')) { + xdebug_disable(); +} + +bar(); +?> +--EXPECTF-- +Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d +Symfony\Component\Debug\Exception\UndefinedFunctionException Object +( + [message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug". + [string:Exception:private] => + [code:protected] => 0 + [file:protected] => %s + [line:protected] => %d + [trace:Exception:private] => Array + ( + [0] => Array + ( +%A [function] => Symfony\Component\Debug\foo +%A [args] => Array + ( + ) + + ) + + [1] => Array + ( +%A [function] => Symfony\Component\Debug\bar +%A [args] => Array + ( + ) + + ) +%A + ) + + [previous:Exception:private] => + [severity:protected] => 1 +) diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php new file mode 100644 index 0000000..7b5e2b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -0,0 +1,292 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\ContextErrorException; + +class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var int Error reporting level before running tests. + */ + private $errorReporting; + + private $loader; + + protected function setUp() + { + $this->errorReporting = error_reporting(E_ALL); + $this->loader = new ClassLoader(); + spl_autoload_register(array($this->loader, 'loadClass'), true, true); + DebugClassLoader::enable(); + } + + protected function tearDown() + { + DebugClassLoader::disable(); + spl_autoload_unregister(array($this->loader, 'loadClass')); + error_reporting($this->errorReporting); + } + + public function testIdempotence() + { + DebugClassLoader::enable(); + + $functions = spl_autoload_functions(); + foreach ($functions as $function) { + if (is_array($function) && $function[0] instanceof DebugClassLoader) { + $reflClass = new \ReflectionClass($function[0]); + $reflProp = $reflClass->getProperty('classLoader'); + $reflProp->setAccessible(true); + + $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0])); + + return; + } + } + + $this->fail('DebugClassLoader did not register'); + } + + public function testUnsilencing() + { + if (PHP_VERSION_ID >= 70000) { + $this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.'); + } + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + ob_start(); + + $this->iniSet('log_errors', 0); + $this->iniSet('display_errors', 1); + + // See below: this will fail with parse error + // but this should not be @-silenced. + @class_exists(__NAMESPACE__.'\TestingUnsilencing', true); + + $output = ob_get_clean(); + + $this->assertStringMatchesFormat('%aParse error%a', $output); + } + + public function testStacking() + { + // the ContextErrorException must not be loaded to test the workaround + // for https://bugs.php.net/65322. + if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) { + $this->markTestSkipped('The ContextErrorException class is already loaded.'); + } + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + ErrorHandler::register(); + + try { + // Trigger autoloading + E_STRICT at compile time + // which in turn triggers $errorHandler->handle() + // that again triggers autoloading for ContextErrorException. + // Error stacking works around the bug above and everything is fine. + + eval(' + namespace '.__NAMESPACE__.'; + class ChildTestingStacking extends TestingStacking { function foo($bar) {} } + '); + $this->fail('ContextErrorException expected'); + } catch (\ErrorException $exception) { + // if an exception is thrown, the test passed + $this->assertStringStartsWith(__FILE__, $exception->getFile()); + if (PHP_VERSION_ID < 70000) { + $this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage()); + $this->assertEquals(E_STRICT, $exception->getSeverity()); + } else { + $this->assertRegExp('/^Warning: Declaration/', $exception->getMessage()); + $this->assertEquals(E_WARNING, $exception->getSeverity()); + } + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testNameCaseMismatch() + { + class_exists(__NAMESPACE__.'\TestingCaseMismatch', true); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between class and real file names + */ + public function testFileCaseMismatch() + { + if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + + class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); + } + + /** + * @expectedException \RuntimeException + */ + public function testPsr4CaseMismatch() + { + class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true); + } + + public function testNotPsr0() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true)); + } + + public function testNotPsr0Bis() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true)); + } + + public function testClassAlias() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true)); + } + + /** + * @dataProvider provideDeprecatedSuper + */ + public function testDeprecatedSuper($class, $super, $type) + { + set_error_handler('var_dump', 0); + $e = error_reporting(0); + trigger_error('', E_USER_DEPRECATED); + + class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'The Test\Symfony\Component\Debug\Tests\\'.$class.' class '.$type.' Symfony\Component\Debug\Tests\Fixtures\\'.$super.' that is deprecated but this is a test deprecation notice.', + ); + + $this->assertSame($xError, $lastError); + } + + public function provideDeprecatedSuper() + { + return array( + array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'), + array('DeprecatedParentClass', 'DeprecatedClass', 'extends'), + ); + } + + public function testDeprecatedSuperInSameNamespace() + { + set_error_handler('var_dump', 0); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_NOTICE, + 'message' => '', + ); + + $this->assertSame($xError, $lastError); + } + + public function testReservedForPhp7() + { + if (PHP_VERSION_ID >= 70000) { + $this->markTestSkipped('PHP7 already prevents using reserved names.'); + } + + set_error_handler('var_dump', 0); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\Float', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'Test\Symfony\Component\Debug\Tests\Float uses a reserved class name (Float) that will break on PHP 7 and higher', + ); + + $this->assertSame($xError, $lastError); + } +} + +class ClassLoader +{ + public function loadClass($class) + { + } + + public function getClassMap() + { + return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'); + } + + public function findFile($class) + { + $fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR; + + if (__NAMESPACE__.'\TestingUnsilencing' === $class) { + eval('-- parse error --'); + } elseif (__NAMESPACE__.'\TestingStacking' === $class) { + eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }'); + } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { + eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); + } elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) { + return $fixtureDir.'CaseMismatch.php'; + } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { + return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php'; + } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { + return $fixtureDir.'reallyNotPsr0.php'; + } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { + return $fixtureDir.'notPsr0Bis.php'; + } elseif (__NAMESPACE__.'\Fixtures\DeprecatedInterface' === $class) { + return $fixtureDir.'DeprecatedInterface.php'; + } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) { + eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}'); + } elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class Float {}'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php new file mode 100644 index 0000000..e2f52fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -0,0 +1,491 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Psr\Log\LogLevel; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\BufferingLogger; +use Symfony\Component\Debug\Exception\ContextErrorException; + +/** + * ErrorHandlerTest. + * + * @author Robert Schönthal + * @author Nicolas Grekas + */ +class ErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testRegister() + { + $handler = ErrorHandler::register(); + + try { + $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); + $this->assertSame($handler, ErrorHandler::register()); + + $newHandler = new ErrorHandler(); + + $this->assertSame($newHandler, ErrorHandler::register($newHandler, false)); + $h = set_error_handler('var_dump'); + restore_error_handler(); + $this->assertSame(array($handler, 'handleError'), $h); + + try { + $this->assertSame($newHandler, ErrorHandler::register($newHandler, true)); + $h = set_error_handler('var_dump'); + restore_error_handler(); + $this->assertSame(array($newHandler, 'handleError'), $h); + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } + + public function testNotice() + { + ErrorHandler::register(); + + try { + self::triggerNotice($this); + $this->fail('ContextErrorException expected'); + } catch (ContextErrorException $exception) { + // if an exception is thrown, the test passed + $this->assertEquals(E_NOTICE, $exception->getSeverity()); + $this->assertEquals(__FILE__, $exception->getFile()); + $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); + $this->assertArrayHasKey('foobar', $exception->getContext()); + + $trace = $exception->getTrace(); + $this->assertEquals(__FILE__, $trace[0]['file']); + $this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']); + $this->assertEquals('handleError', $trace[0]['function']); + $this->assertEquals('->', $trace[0]['type']); + + $this->assertEquals(__FILE__, $trace[1]['file']); + $this->assertEquals(__CLASS__, $trace[1]['class']); + $this->assertEquals('triggerNotice', $trace[1]['function']); + $this->assertEquals('::', $trace[1]['type']); + + $this->assertEquals(__FILE__, $trace[1]['file']); + $this->assertEquals(__CLASS__, $trace[2]['class']); + $this->assertEquals(__FUNCTION__, $trace[2]['function']); + $this->assertEquals('->', $trace[2]['type']); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + // dummy function to test trace in error handler. + private static function triggerNotice($that) + { + // dummy variable to check for in error handler. + $foobar = 123; + $that->assertSame('', $foo.$foo.$bar); + } + + public function testConstruct() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0)); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testDefaultLogger() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL)); + + $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array($logger, LogLevel::WARNING), + E_USER_NOTICE => array($logger, LogLevel::CRITICAL), + E_STRICT => array(null, LogLevel::WARNING), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), + E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), + E_PARSE => array(null, LogLevel::CRITICAL), + E_ERROR => array(null, LogLevel::CRITICAL), + E_CORE_ERROR => array(null, LogLevel::CRITICAL), + ); + $this->assertSame($loggers, $handler->setLoggers(array())); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testHandleError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + try { + $handler->handleError(4, 'foo', 'foo.php', 12, array()); + } catch (\ErrorException $e) { + $this->assertSame('Parse Error: foo', $e->getMessage()); + $this->assertSame(4, $e->getSeverity()); + $this->assertSame('foo.php', $e->getFile()); + $this->assertSame(12, $e->getLine()); + } + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(E_USER_DEPRECATED, true); + $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(E_DEPRECATED, true); + $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $warnArgCheck = function ($logLevel, $message, $context) { + $this->assertEquals('info', $logLevel); + $this->assertEquals('foo', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_USER_DEPRECATED); + $this->assertArrayHasKey('stack', $context); + $this->assertInternalType('array', $context['stack']); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($warnArgCheck)) + ; + + $handler = ErrorHandler::register(); + $handler->setDefaultLogger($logger, E_USER_DEPRECATED); + $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Undefined variable: undefVar', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_NOTICE); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = ErrorHandler::register(); + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->screamAt(E_NOTICE); + unset($undefVar); + @$undefVar++; + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleUserError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + + $e = null; + $x = new \Exception('Foo'); + + try { + $f = new Fixtures\ToStringThrower($x); + $f .= ''; // Trigger $f->__toString() + } catch (\Exception $e) { + } + + $this->assertSame($x, $e); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testHandleDeprecation() + { + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals(LogLevel::INFO, $level); + $this->assertArrayHasKey('level', $context); + $this->assertEquals(E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED, $context['level']); + $this->assertArrayHasKey('stack', $context); + }; + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = new ErrorHandler(); + $handler->setDefaultLogger($logger); + @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array()); + } + + public function testHandleException() + { + try { + $handler = ErrorHandler::register(); + + $exception = new \Exception('foo'); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Uncaught Exception: foo', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_ERROR); + }; + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + try { + $handler->handleException($exception); + $this->fail('Exception expected'); + } catch (\Exception $e) { + $this->assertSame($exception, $e); + } + + $handler->setExceptionHandler(function ($e) use ($exception) { + $this->assertSame($exception, $e); + }); + + $handler->handleException($exception); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testErrorStacking() + { + try { + $handler = ErrorHandler::register(); + $handler->screamAt(E_USER_WARNING); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->withConsecutive( + array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')), + array($this->equalTo(LogLevel::DEBUG), $this->equalTo('Silenced warning')) + ) + ; + + $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING)); + + ErrorHandler::stackErrors(); + @trigger_error('Silenced warning', E_USER_WARNING); + $logger->log(LogLevel::WARNING, 'Dummy log'); + ErrorHandler::unstackErrors(); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testBootstrappingLogger() + { + $bootLogger = new BufferingLogger(); + $handler = new ErrorHandler($bootLogger); + + $loggers = array( + E_DEPRECATED => array($bootLogger, LogLevel::INFO), + E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO), + E_NOTICE => array($bootLogger, LogLevel::WARNING), + E_USER_NOTICE => array($bootLogger, LogLevel::WARNING), + E_STRICT => array($bootLogger, LogLevel::WARNING), + E_WARNING => array($bootLogger, LogLevel::WARNING), + E_USER_WARNING => array($bootLogger, LogLevel::WARNING), + E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING), + E_CORE_WARNING => array($bootLogger, LogLevel::WARNING), + E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_PARSE => array($bootLogger, LogLevel::CRITICAL), + E_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL), + ); + + $this->assertSame($loggers, $handler->setLoggers(array())); + + $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array()); + $expectedLog = array(LogLevel::INFO, 'Foo message', array('type' => E_DEPRECATED, 'file' => __FILE__, 'line' => 123, 'level' => error_reporting())); + + $logs = $bootLogger->cleanLogs(); + unset($logs[0][2]['stack']); + + $this->assertSame(array($expectedLog), $logs); + + $bootLogger->log($expectedLog[0], $expectedLog[1], $expectedLog[2]); + + $mockLogger = $this->getMock('Psr\Log\LoggerInterface'); + $mockLogger->expects($this->once()) + ->method('log') + ->with(LogLevel::WARNING, 'Foo message', $expectedLog[2]); + + $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING))); + } + + public function testHandleFatalError() + { + try { + $handler = ErrorHandler::register(); + + $error = array( + 'type' => E_PARSE, + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + ); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Fatal Parse Error: foo', $message); + $this->assertArrayHasKey('type', $context); + $this->assertEquals($context['type'], E_PARSE); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_PARSE); + + $handler->handleFatalError($error); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleFatalErrorOnHHVM() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger + ->expects($this->once()) + ->method('log') + ->with( + $this->equalTo(LogLevel::CRITICAL), + $this->equalTo('Fatal Error: foo'), + $this->equalTo(array( + 'type' => 1, + 'file' => 'bar', + 'line' => 123, + 'level' => -1, + 'stack' => array(456), + )) + ) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + $error = array( + 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + 'context' => array(123), + 'backtrace' => array(456), + ); + + call_user_func_array(array($handler, 'handleError'), $error); + $handler->handleFatalError($error); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php new file mode 100644 index 0000000..6c570e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Exception/FlattenExceptionTest.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\Exception; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\GoneHttpException; +use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; +use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; +use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; +use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; + +class FlattenExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testStatusCode() + { + $flattened = FlattenException::create(new \RuntimeException(), 403); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new \RuntimeException()); + $this->assertEquals('500', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new NotFoundHttpException()); + $this->assertEquals('404', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); + $this->assertEquals('401', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new BadRequestHttpException()); + $this->assertEquals('400', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new NotAcceptableHttpException()); + $this->assertEquals('406', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new ConflictHttpException()); + $this->assertEquals('409', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals('405', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new AccessDeniedHttpException()); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new GoneHttpException()); + $this->assertEquals('410', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new LengthRequiredHttpException()); + $this->assertEquals('411', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new PreconditionFailedHttpException()); + $this->assertEquals('412', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new PreconditionRequiredHttpException()); + $this->assertEquals('428', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException()); + $this->assertEquals('503', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException()); + $this->assertEquals('429', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException()); + $this->assertEquals('415', $flattened->getStatusCode()); + } + + public function testHeadersForHttpException() + { + $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals(array('Allow' => 'POST'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); + $this->assertEquals(array('WWW-Authenticate' => 'Basic realm="My Realm"'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); + $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException(120)); + $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); + $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException(120)); + $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFlattenHttpException(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened2 = FlattenException::create($exception); + + $flattened->setPrevious($flattened2); + + $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.'); + $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.'); + $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testPrevious(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened2 = FlattenException::create($exception); + + $flattened->setPrevious($flattened2); + + $this->assertSame($flattened2, $flattened->getPrevious()); + + $this->assertSame(array($flattened2), $flattened->getAllPrevious()); + } + + /** + * @requires PHP 7.0 + */ + public function testPreviousError() + { + $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); + + $flattened = FlattenException::create($exception)->getPrevious(); + + $this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.'); + $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.'); + $this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testLine(\Exception $exception) + { + $flattened = FlattenException::create($exception); + $this->assertSame($exception->getLine(), $flattened->getLine()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFile(\Exception $exception) + { + $flattened = FlattenException::create($exception); + $this->assertSame($exception->getFile(), $flattened->getFile()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testToArray(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened->setTrace(array(), 'foo.php', 123); + + $this->assertEquals(array( + array( + 'message' => 'test', + 'class' => 'Exception', + 'trace' => array(array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123, + 'args' => array(), + )), + ), + ), $flattened->toArray()); + } + + public function flattenDataProvider() + { + return array( + array(new \Exception('test', 123), 500), + ); + } + + public function testRecursionInArguments() + { + $a = array('foo', array(2, &$a)); + $exception = $this->createException($a); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + $this->assertContains('*DEEP NESTED ARRAY*', serialize($trace)); + } + + public function testTooBigArray() + { + $a = array(); + for ($i = 0; $i < 20; ++$i) { + for ($j = 0; $j < 50; ++$j) { + for ($k = 0; $k < 10; ++$k) { + $a[$i][$j][$k] = 'value'; + } + } + } + $a[20] = 'value'; + $a[21] = 'value1'; + $exception = $this->createException($a); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + $serializeTrace = serialize($trace); + + $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace); + $this->assertNotContains('*value1*', $serializeTrace); + } + + private function createException($foo) + { + return new \Exception(); + } + + public function testSetTraceIncompleteClass() + { + $flattened = FlattenException::create(new \Exception('test', 123)); + $flattened->setTrace( + array( + array( + 'file' => __FILE__, + 'line' => 123, + 'function' => 'test', + 'args' => array( + unserialize('O:14:"BogusTestClass":0:{}'), + ), + ), + ), + 'foo.php', 123 + ); + + $this->assertEquals(array( + array( + 'message' => 'test', + 'class' => 'Exception', + 'trace' => array( + array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', + 'file' => 'foo.php', 'line' => 123, + 'args' => array(), + ), + array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => 'test', + 'file' => __FILE__, 'line' => 123, + 'args' => array( + array( + 'incomplete-object', 'BogusTestClass', + ), + ), + ), + ), + ), + ), $flattened->toArray()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php new file mode 100644 index 0000000..1703da6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; + +require_once __DIR__.'/HeaderMock.php'; + +class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + testHeader(); + } + + protected function tearDown() + { + testHeader(); + } + + public function testDebug() + { + $handler = new ExceptionHandler(false); + + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('

    Whoops, looks like something went wrong.

    ', $response); + $this->assertNotContains('

    ', $response); + + $handler = new ExceptionHandler(true); + + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('

    Whoops, looks like something went wrong.

    ', $response); + $this->assertContains('

    ', $response); + } + + public function testStatusCode() + { + $handler = new ExceptionHandler(false, 'iso8859-1'); + + ob_start(); + $handler->sendPhpResponse(new NotFoundHttpException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('Sorry, the page you are looking for could not be found.', $response); + + $expectedHeaders = array( + array('HTTP/1.0 404', true, null), + array('Content-Type: text/html; charset=iso8859-1', true, null), + ); + + $this->assertSame($expectedHeaders, testHeader()); + } + + public function testHeaders() + { + $handler = new ExceptionHandler(false, 'iso8859-1'); + + ob_start(); + $handler->sendPhpResponse(new MethodNotAllowedHttpException(array('POST'))); + $response = ob_get_clean(); + + $expectedHeaders = array( + array('HTTP/1.0 405', true, null), + array('Allow: POST', false, null), + array('Content-Type: text/html; charset=iso8859-1', true, null), + ); + + $this->assertSame($expectedHeaders, testHeader()); + } + + public function testNestedExceptions() + { + $handler = new ExceptionHandler(true); + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); + $response = ob_get_clean(); + + $this->assertStringMatchesFormat('%AFoo%ABar%A', $response); + } + + public function testHandle() + { + $exception = new \Exception('foo'); + + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); + $handler + ->expects($this->exactly(2)) + ->method('sendPhpResponse'); + + $handler->handle($exception); + + $handler->setHandler(function ($e) use ($exception) { + $this->assertSame($exception, $e); + }); + + $handler->handle($exception); + } + + public function testHandleOutOfMemoryException() + { + $exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__); + + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); + $handler + ->expects($this->once()) + ->method('sendPhpResponse'); + + $handler->setHandler(function ($e) { + $this->fail('OutOfMemoryException should bypass the handler'); + }); + + $handler->handle($exception); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php new file mode 100644 index 0000000..360e6ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; + +class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + foreach (spl_autoload_functions() as $function) { + if (!is_array($function)) { + continue; + } + + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + } + + if ($function[0] instanceof ComposerClassLoader) { + $function[0]->add('Symfony_Component_Debug_Tests_Fixtures', dirname(dirname(dirname(dirname(dirname(__DIR__)))))); + break; + } + } + } + + /** + * @dataProvider provideClassNotFoundData + */ + public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null) + { + if ($autoloader) { + // Unregister all autoloaders to ensure the custom provided + // autoloader is the only one to be used during the test run. + $autoloaders = spl_autoload_functions(); + array_map('spl_autoload_unregister', $autoloaders); + spl_autoload_register($autoloader); + } + + $handler = new ClassNotFoundFatalErrorHandler(); + + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + if ($autoloader) { + spl_autoload_unregister($autoloader); + array_map('spl_autoload_register', $autoloaders); + } + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideClassNotFoundData() + { + $prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception')); + + $symfonyAutoloader = new SymfonyClassLoader(); + $symfonyAutoloader->addPrefixes($prefixes); + + $debugClassLoader = new DebugClassLoader(array($symfonyAutoloader, 'loadClass')); + + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'WhizBangFactory\' not found', + ), + "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found', + ), + "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'PEARClass\' not found', + ), + "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + array($symfonyAutoloader, 'loadClass'), + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + array($debugClassLoader, 'loadClass'), + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", + function ($className) { /* do nothing here */ }, + ), + ); + } + + public function testCannotRedeclareClass() + { + if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + + require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP'; + + $error = array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found', + ); + + $handler = new ClassNotFoundFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php new file mode 100644 index 0000000..795b747 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; + +class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideUndefinedFunctionData + */ + public function testUndefinedFunction($error, $translatedMessage) + { + $handler = new UndefinedFunctionFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); + // class names are case insensitive and PHP/HHVM do not return the same + $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage())); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedFunctionData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function test_namespaced_function()', + ), + "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', + ), + "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function foo()', + ), + 'Attempted to call function "foo" from the global namespace.', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', + ), + 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".', + ), + ); + } +} + +function test_namespaced_function() +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php new file mode 100644 index 0000000..de7b21c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; + +class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideUndefinedMethodData + */ + public function testUndefinedMethod($error, $translatedMessage) + { + $handler = new UndefinedMethodFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedMethodData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::what()', + ), + 'Attempted to call an undefined method named "what" of class "SplObjectStorage".', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::walid()', + ), + "Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::offsetFet()', + ), + "Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?", + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Fixtures/ClassAlias.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Fixtures/ClassAlias.php new file mode 100644 index 0000000..9d6dbaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Fixtures/ClassAlias.php @@ -0,0 +1,3 @@ +exception = $e; + } + + public function __toString() + { + try { + throw $this->exception; + } catch (\Exception $e) { + // Using user_error() here is on purpose so we do not forget + // that this alias also should work alongside with trigger_error(). + return user_error($e, E_USER_ERROR); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Fixtures/casemismatch.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Fixtures/casemismatch.php new file mode 100644 index 0000000..691d660 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/Fixtures/casemismatch.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +function headers_sent() +{ + return false; +} + +function header($str, $replace = true, $status = null) +{ + Tests\testHeader($str, $replace, $status); +} + +namespace Symfony\Component\Debug\Tests; + +function testHeader() +{ + static $headers = array(); + + if (!$h = func_get_args()) { + $h = $headers; + $headers = array(); + + return $h; + } + + $headers[] = func_get_args(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php new file mode 100644 index 0000000..a85d2d1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/Tests/MockExceptionHandler.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; + +class MockExceptionHandler extends Exceptionhandler +{ + public $e; + + public function handle(\Exception $e) + { + $this->e = $e; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Debug/composer.json new file mode 100644 index 0000000..0eef83e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/debug", + "type": "library", + "description": "Symfony Debug Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Debug\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Debug/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Debug/phpunit.xml.dist new file mode 100644 index 0000000..e99c4dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Debug/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + ./Resources/ext/tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Alias.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Alias.php new file mode 100644 index 0000000..5a6c2fe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Alias.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +class Alias +{ + private $id; + private $public; + + /** + * Constructor. + * + * @param string $id Alias identifier + * @param bool $public If this alias is public + */ + public function __construct($id, $public = true) + { + $this->id = strtolower($id); + $this->public = $public; + } + + /** + * Checks if this DI Alias should be public or not. + * + * @return bool + */ + public function isPublic() + { + return $this->public; + } + + /** + * Sets if this Alias is public. + * + * @param bool $boolean If this Alias should be public + */ + public function setPublic($boolean) + { + $this->public = (bool) $boolean; + } + + /** + * Returns the Id of this alias. + * + * @return string The alias id + */ + public function __toString() + { + return $this->id; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/CHANGELOG.md new file mode 100644 index 0000000..5b28ee9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -0,0 +1,61 @@ +CHANGELOG +========= + +3.0.0 +----- + + * removed all deprecated codes from 2.x versions + +2.8.0 +----- + + * deprecated the abstract ContainerAware class in favor of ContainerAwareTrait + * deprecated IntrospectableContainerInterface, to be merged with ContainerInterface in 3.0 + * allowed specifying a directory to recursively load all configuration files it contains + * deprecated the concept of scopes + * added `Definition::setShared()` and `Definition::isShared()` + * added ResettableContainerInterface to be able to reset the container to release memory on shutdown + * added a way to define the priority of service decoration + +2.7.0 +----- + + * deprecated synchronized services + +2.6.0 +----- + + * added new factory syntax and deprecated the old one + +2.5.0 +----- + +* added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService()) +* deprecated SimpleXMLElement class. + +2.4.0 +----- + + * added support for expressions in service definitions + * added ContainerAwareTrait to add default container aware behavior to a class + +2.2.0 +----- + + * added Extension::isConfigEnabled() to ease working with enableable configurations + * added an Extension base class with sensible defaults to be used in conjunction + with the Config component. + * added PrependExtensionInterface (to be able to allow extensions to prepend + application configuration settings for any Bundle) + +2.1.0 +----- + + * added IntrospectableContainerInterface (to be able to check if a service + has been initialized or not) + * added ConfigurationExtensionInterface + * added Definition::clearTag() + * component exceptions that inherit base SPL classes are now used exclusively + (this includes dumped containers) + * [BC BREAK] fixed unescaping of class arguments, method + ParameterBag::unescapeValue() was made public diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php new file mode 100644 index 0000000..9fd3523 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Run this pass before passes that need to know more about the relation of + * your services. + * + * This class will populate the ServiceReferenceGraph with information. You can + * retrieve the graph in other passes from the compiler. + * + * @author Johannes M. Schmitt + */ +class AnalyzeServiceReferencesPass implements RepeatablePassInterface +{ + private $graph; + private $container; + private $currentId; + private $currentDefinition; + private $repeatedPass; + private $onlyConstructorArguments; + + /** + * Constructor. + * + * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls + */ + public function __construct($onlyConstructorArguments = false) + { + $this->onlyConstructorArguments = (bool) $onlyConstructorArguments; + } + + /** + * {@inheritdoc} + */ + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + $this->repeatedPass = $repeatedPass; + } + + /** + * Processes a ContainerBuilder object to populate the service reference graph. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + $this->graph = $container->getCompiler()->getServiceReferenceGraph(); + $this->graph->clear(); + + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isSynthetic() || $definition->isAbstract()) { + continue; + } + + $this->currentId = $id; + $this->currentDefinition = $definition; + + $this->processArguments($definition->getArguments()); + if (is_array($definition->getFactory())) { + $this->processArguments($definition->getFactory()); + } + + if (!$this->onlyConstructorArguments) { + $this->processArguments($definition->getMethodCalls()); + $this->processArguments($definition->getProperties()); + if ($definition->getConfigurator()) { + $this->processArguments(array($definition->getConfigurator())); + } + } + } + + foreach ($container->getAliases() as $id => $alias) { + $this->graph->connect($id, $alias, (string) $alias, $this->getDefinition((string) $alias), null); + } + } + + /** + * Processes service definitions for arguments to find relationships for the service graph. + * + * @param array $arguments An array of Reference or Definition objects relating to service definitions + */ + private function processArguments(array $arguments) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + $this->processArguments($argument); + } elseif ($argument instanceof Reference) { + $this->graph->connect( + $this->currentId, + $this->currentDefinition, + $this->getDefinitionId((string) $argument), + $this->getDefinition((string) $argument), + $argument + ); + } elseif ($argument instanceof Definition) { + $this->processArguments($argument->getArguments()); + $this->processArguments($argument->getMethodCalls()); + $this->processArguments($argument->getProperties()); + + if (is_array($argument->getFactory())) { + $this->processArguments($argument->getFactory()); + } + } + } + } + + /** + * Returns a service definition given the full name or an alias. + * + * @param string $id A full id or alias for a service definition. + * + * @return Definition|null The definition related to the supplied id + */ + private function getDefinition($id) + { + $id = $this->getDefinitionId($id); + + return null === $id ? null : $this->container->getDefinition($id); + } + + private function getDefinitionId($id) + { + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + + if (!$this->container->hasDefinition($id)) { + return; + } + + return $id; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutoAliasServicePass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutoAliasServicePass.php new file mode 100644 index 0000000..c1f05e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutoAliasServicePass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Sets a service to be an alias of another one, given a format pattern. + */ +class AutoAliasServicePass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->findTaggedServiceIds('auto_alias') as $serviceId => $tags) { + foreach ($tags as $tag) { + if (!isset($tag['format'])) { + throw new InvalidArgumentException(sprintf('Missing tag information "format" on auto_alias service "%s".', $serviceId)); + } + + $aliasId = $container->getParameterBag()->resolveValue($tag['format']); + if ($container->hasDefinition($aliasId) || $container->hasAlias($aliasId)) { + $container->setAlias($serviceId, new Alias($aliasId)); + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php new file mode 100644 index 0000000..ec7880b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -0,0 +1,271 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Guesses constructor arguments of services definitions and try to instantiate services if necessary. + * + * @author Kévin Dunglas + */ +class AutowirePass implements CompilerPassInterface +{ + private $container; + private $reflectionClasses = array(); + private $definedTypes = array(); + private $types; + private $notGuessableTypes = array(); + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isAutowired()) { + $this->completeDefinition($id, $definition); + } + } + + // Free memory and remove circular reference to container + $this->container = null; + $this->reflectionClasses = array(); + $this->definedTypes = array(); + $this->types = null; + $this->notGuessableTypes = array(); + } + + /** + * Wires the given definition. + * + * @param string $id + * @param Definition $definition + * + * @throws RuntimeException + */ + private function completeDefinition($id, Definition $definition) + { + if (!$reflectionClass = $this->getReflectionClass($id, $definition)) { + return; + } + + $this->container->addClassResource($reflectionClass); + + if (!$constructor = $reflectionClass->getConstructor()) { + return; + } + + $arguments = $definition->getArguments(); + foreach ($constructor->getParameters() as $index => $parameter) { + $argumentExists = array_key_exists($index, $arguments); + if ($argumentExists && '' !== $arguments[$index]) { + continue; + } + + try { + if (!$typeHint = $parameter->getClass()) { + continue; + } + + if (null === $this->types) { + $this->populateAvailableTypes(); + } + + if (isset($this->types[$typeHint->name])) { + $value = new Reference($this->types[$typeHint->name]); + } else { + try { + $value = $this->createAutowiredDefinition($typeHint, $id); + } catch (RuntimeException $e) { + if ($parameter->allowsNull()) { + $value = null; + } elseif ($parameter->isDefaultValueAvailable()) { + $value = $parameter->getDefaultValue(); + } else { + throw $e; + } + } + } + } catch (\ReflectionException $reflectionException) { + // Typehint against a non-existing class + + if (!$parameter->isDefaultValueAvailable()) { + throw new RuntimeException(sprintf('Cannot autowire argument %s for %s because the type-hinted class does not exist (%s).', $index + 1, $definition->getClass(), $reflectionException->getMessage()), 0, $reflectionException); + } + + $value = $parameter->getDefaultValue(); + } + + if ($argumentExists) { + $definition->replaceArgument($index, $value); + } else { + $definition->addArgument($value); + } + } + } + + /** + * Populates the list of available types. + */ + private function populateAvailableTypes() + { + $this->types = array(); + + foreach ($this->container->getDefinitions() as $id => $definition) { + $this->populateAvailableType($id, $definition); + } + } + + /** + * Populates the list of available types for a given definition. + * + * @param string $id + * @param Definition $definition + */ + private function populateAvailableType($id, Definition $definition) + { + // Never use abstract services + if ($definition->isAbstract()) { + return; + } + + foreach ($definition->getAutowiringTypes() as $type) { + $this->definedTypes[$type] = true; + $this->types[$type] = $id; + } + + // Cannot use reflection if the class isn't set + if (!$definition->getClass()) { + return; + } + + if ($reflectionClass = $this->getReflectionClass($id, $definition)) { + $this->extractInterfaces($id, $reflectionClass); + $this->extractAncestors($id, $reflectionClass); + } + } + + /** + * Extracts the list of all interfaces implemented by a class. + * + * @param string $id + * @param \ReflectionClass $reflectionClass + */ + private function extractInterfaces($id, \ReflectionClass $reflectionClass) + { + foreach ($reflectionClass->getInterfaces() as $interfaceName => $reflectionInterface) { + $this->set($interfaceName, $id); + + $this->extractInterfaces($id, $reflectionInterface); + } + } + + /** + * Extracts all inherited types of a class. + * + * @param string $id + * @param \ReflectionClass $reflectionClass + */ + private function extractAncestors($id, \ReflectionClass $reflectionClass) + { + $this->set($reflectionClass->name, $id); + + if ($reflectionParentClass = $reflectionClass->getParentClass()) { + $this->extractAncestors($id, $reflectionParentClass); + } + } + + /** + * Associates a type and a service id if applicable. + * + * @param string $type + * @param string $id + */ + private function set($type, $id) + { + if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) { + return; + } + + if (isset($this->types[$type])) { + if ($this->types[$type] === $id) { + return; + } + + unset($this->types[$type]); + $this->notGuessableTypes[$type] = true; + + return; + } + + $this->types[$type] = $id; + } + + /** + * Registers a definition for the type if possible or throws an exception. + * + * @param \ReflectionClass $typeHint + * @param string $id + * + * @return Reference A reference to the registered definition + * + * @throws RuntimeException + */ + private function createAutowiredDefinition(\ReflectionClass $typeHint, $id) + { + if (isset($this->notGuessableTypes[$typeHint->name]) || !$typeHint->isInstantiable()) { + throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id)); + } + + $argumentId = sprintf('autowired.%s', $typeHint->name); + + $argumentDefinition = $this->container->register($argumentId, $typeHint->name); + $argumentDefinition->setPublic(false); + + $this->populateAvailableType($argumentId, $argumentDefinition); + $this->completeDefinition($argumentId, $argumentDefinition); + + return new Reference($argumentId); + } + + /** + * Retrieves the reflection class associated with the given service. + * + * @param string $id + * @param Definition $definition + * + * @return \ReflectionClass|null + */ + private function getReflectionClass($id, Definition $definition) + { + if (isset($this->reflectionClasses[$id])) { + return $this->reflectionClasses[$id]; + } + + if (!$class = $definition->getClass()) { + return; + } + + $class = $this->container->getParameterBag()->resolveValue($class); + + try { + return $this->reflectionClasses[$id] = new \ReflectionClass($class); + } catch (\ReflectionException $reflectionException) { + // return null + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php new file mode 100644 index 0000000..d7570dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckCircularReferencesPass.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Checks your services for circular references. + * + * References from method calls are ignored since we might be able to resolve + * these references depending on the order in which services are called. + * + * Circular reference from method calls will only be detected at run-time. + * + * @author Johannes M. Schmitt + */ +class CheckCircularReferencesPass implements CompilerPassInterface +{ + private $currentPath; + private $checkedNodes; + + /** + * Checks the ContainerBuilder object for circular references. + * + * @param ContainerBuilder $container The ContainerBuilder instances + */ + public function process(ContainerBuilder $container) + { + $graph = $container->getCompiler()->getServiceReferenceGraph(); + + $this->checkedNodes = array(); + foreach ($graph->getNodes() as $id => $node) { + $this->currentPath = array($id); + + $this->checkOutEdges($node->getOutEdges()); + } + } + + /** + * Checks for circular references. + * + * @param ServiceReferenceGraphEdge[] $edges An array of Edges + * + * @throws ServiceCircularReferenceException When a circular reference is found. + */ + private function checkOutEdges(array $edges) + { + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $id = $node->getId(); + + if (empty($this->checkedNodes[$id])) { + $searchKey = array_search($id, $this->currentPath); + $this->currentPath[] = $id; + + if (false !== $searchKey) { + throw new ServiceCircularReferenceException($id, array_slice($this->currentPath, $searchKey)); + } + + $this->checkOutEdges($node->getOutEdges()); + + $this->checkedNodes[$id] = true; + array_pop($this->currentPath); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php new file mode 100644 index 0000000..0d21ef2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * This pass validates each definition individually only taking the information + * into account which is contained in the definition itself. + * + * Later passes can rely on the following, and specifically do not need to + * perform these checks themselves: + * + * - non synthetic, non abstract services always have a class set + * - synthetic services are always public + * + * @author Johannes M. Schmitt + */ +class CheckDefinitionValidityPass implements CompilerPassInterface +{ + /** + * Processes the ContainerBuilder to validate the Definition. + * + * @param ContainerBuilder $container + * + * @throws RuntimeException When the Definition is invalid + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + // synthetic service is public + if ($definition->isSynthetic() && !$definition->isPublic()) { + throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id)); + } + + // non-synthetic, non-abstract service has class + if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) { + if ($definition->getFactory()) { + throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id)); + } + + throw new RuntimeException(sprintf( + 'The definition for "%s" has no class. If you intend to inject ' + .'this service dynamically at runtime, please mark it as synthetic=true. ' + .'If this is an abstract definition solely used by child definitions, ' + .'please add abstract=true, otherwise specify a class to get rid of this error.', + $id + )); + } + + // tag attribute values must be scalars + foreach ($definition->getTags() as $name => $tags) { + foreach ($tags as $attributes) { + foreach ($attributes as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $name, $attribute)); + } + } + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php new file mode 100644 index 0000000..304f784 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Checks that all references are pointing to a valid service. + * + * @author Johannes M. Schmitt + */ +class CheckExceptionOnInvalidReferenceBehaviorPass implements CompilerPassInterface +{ + private $container; + private $sourceId; + + public function process(ContainerBuilder $container) + { + $this->container = $container; + + foreach ($container->getDefinitions() as $id => $definition) { + $this->sourceId = $id; + $this->processDefinition($definition); + } + } + + private function processDefinition(Definition $definition) + { + $this->processReferences($definition->getArguments()); + $this->processReferences($definition->getMethodCalls()); + $this->processReferences($definition->getProperties()); + } + + private function processReferences(array $arguments) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + $this->processReferences($argument); + } elseif ($argument instanceof Definition) { + $this->processDefinition($argument); + } elseif ($argument instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $argument->getInvalidBehavior()) { + $destId = (string) $argument; + + if (!$this->container->has($destId)) { + throw new ServiceNotFoundException($destId, $this->sourceId); + } + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php new file mode 100644 index 0000000..df0f55a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CheckReferenceValidityPass.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Checks the validity of references. + * + * The following checks are performed by this pass: + * - target definitions are not abstract + * + * @author Johannes M. Schmitt + */ +class CheckReferenceValidityPass implements CompilerPassInterface +{ + private $container; + private $currentId; + + /** + * Processes the ContainerBuilder to validate References. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + + $ancestors = array(); + + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isSynthetic() || $definition->isAbstract()) { + continue; + } + + $this->currentId = $id; + + $this->validateReferences($definition->getArguments()); + $this->validateReferences($definition->getMethodCalls()); + $this->validateReferences($definition->getProperties()); + } + } + + /** + * Validates an array of References. + * + * @param array $arguments An array of Reference objects + * + * @throws RuntimeException when there is a reference to an abstract definition. + */ + private function validateReferences(array $arguments) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + $this->validateReferences($argument); + } elseif ($argument instanceof Reference) { + $targetDefinition = $this->getDefinition((string) $argument); + + if (null !== $targetDefinition && $targetDefinition->isAbstract()) { + throw new RuntimeException(sprintf( + 'The definition "%s" has a reference to an abstract definition "%s". ' + .'Abstract definitions cannot be the target of references.', + $this->currentId, + $argument + )); + } + } + } + } + + /** + * Returns the Definition given an id. + * + * @param string $id Definition identifier + * + * @return Definition + */ + private function getDefinition($id) + { + if (!$this->container->hasDefinition($id)) { + return; + } + + return $this->container->getDefinition($id); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php new file mode 100644 index 0000000..4e4c2cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/Compiler.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This class is used to remove circular dependencies between individual passes. + * + * @author Johannes M. Schmitt + */ +class Compiler +{ + private $passConfig; + private $log = array(); + private $loggingFormatter; + private $serviceReferenceGraph; + + /** + * Constructor. + */ + public function __construct() + { + $this->passConfig = new PassConfig(); + $this->serviceReferenceGraph = new ServiceReferenceGraph(); + $this->loggingFormatter = new LoggingFormatter(); + } + + /** + * Returns the PassConfig. + * + * @return PassConfig The PassConfig instance + */ + public function getPassConfig() + { + return $this->passConfig; + } + + /** + * Returns the ServiceReferenceGraph. + * + * @return ServiceReferenceGraph The ServiceReferenceGraph instance + */ + public function getServiceReferenceGraph() + { + return $this->serviceReferenceGraph; + } + + /** + * Returns the logging formatter which can be used by compilation passes. + * + * @return LoggingFormatter + */ + public function getLoggingFormatter() + { + return $this->loggingFormatter; + } + + /** + * Adds a pass to the PassConfig. + * + * @param CompilerPassInterface $pass A compiler pass + * @param string $type The type of the pass + */ + public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION) + { + $this->passConfig->addPass($pass, $type); + } + + /** + * Adds a log message. + * + * @param string $string The log message + */ + public function addLogMessage($string) + { + $this->log[] = $string; + } + + /** + * Returns the log. + * + * @return array Log array + */ + public function getLog() + { + return $this->log; + } + + /** + * Run the Compiler and process all Passes. + * + * @param ContainerBuilder $container + */ + public function compile(ContainerBuilder $container) + { + foreach ($this->passConfig->getPasses() as $pass) { + $pass->process($container); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CompilerPassInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CompilerPassInterface.php new file mode 100644 index 0000000..30cb1d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/CompilerPassInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Interface that must be implemented by compilation passes. + * + * @author Johannes M. Schmitt + */ +interface CompilerPassInterface +{ + /** + * You can modify the container here before it is dumped to PHP code. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php new file mode 100644 index 0000000..24008ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Alias; + +/** + * Overwrites a service but keeps the overridden one. + * + * @author Christophe Coevoet + * @author Fabien Potencier + * @author Diego Saint Esteben + */ +class DecoratorServicePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $definitions = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + + foreach ($container->getDefinitions() as $id => $definition) { + if (!$decorated = $definition->getDecoratedService()) { + continue; + } + $definitions->insert(array($id, $definition), array($decorated[2], --$order)); + } + + foreach ($definitions as list($id, $definition)) { + list($inner, $renamedId) = $definition->getDecoratedService(); + + $definition->setDecoratedService(null); + + if (!$renamedId) { + $renamedId = $id.'.inner'; + } + + // we create a new alias/service for the service we are replacing + // to be able to reference it in the new one + if ($container->hasAlias($inner)) { + $alias = $container->getAlias($inner); + $public = $alias->isPublic(); + $container->setAlias($renamedId, new Alias((string) $alias, false)); + } else { + $definition = $container->getDefinition($inner); + $public = $definition->isPublic(); + $definition->setPublic(false); + $container->setDefinition($renamedId, $definition); + } + + $container->setAlias($inner, new Alias($id, $public)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ExtensionCompilerPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ExtensionCompilerPass.php new file mode 100644 index 0000000..b06b497 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ExtensionCompilerPass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * A pass to automatically process extensions if they implement + * CompilerPassInterface. + * + * @author Wouter J + */ +class ExtensionCompilerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getExtensions() as $extension) { + if (!$extension instanceof CompilerPassInterface) { + continue; + } + + $extension->process($container); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php new file mode 100644 index 0000000..6d9a025 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Inline service definitions where this is possible. + * + * @author Johannes M. Schmitt + */ +class InlineServiceDefinitionsPass implements RepeatablePassInterface +{ + private $repeatedPass; + private $graph; + private $compiler; + private $formatter; + private $currentId; + + /** + * {@inheritdoc} + */ + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + $this->repeatedPass = $repeatedPass; + } + + /** + * Processes the ContainerBuilder for inline service definitions. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->compiler = $container->getCompiler(); + $this->formatter = $this->compiler->getLoggingFormatter(); + $this->graph = $this->compiler->getServiceReferenceGraph(); + + $container->setDefinitions($this->inlineArguments($container, $container->getDefinitions(), true)); + } + + /** + * Processes inline arguments. + * + * @param ContainerBuilder $container The ContainerBuilder + * @param array $arguments An array of arguments + * @param bool $isRoot If we are processing the root definitions or not + * + * @return array + */ + private function inlineArguments(ContainerBuilder $container, array $arguments, $isRoot = false) + { + foreach ($arguments as $k => $argument) { + if ($isRoot) { + $this->currentId = $k; + } + if (is_array($argument)) { + $arguments[$k] = $this->inlineArguments($container, $argument); + } elseif ($argument instanceof Reference) { + if (!$container->hasDefinition($id = (string) $argument)) { + continue; + } + + if ($this->isInlineableDefinition($container, $id, $definition = $container->getDefinition($id))) { + $this->compiler->addLogMessage($this->formatter->formatInlineService($this, $id, $this->currentId)); + + if ($definition->isShared()) { + $arguments[$k] = $definition; + } else { + $arguments[$k] = clone $definition; + } + } + } elseif ($argument instanceof Definition) { + $argument->setArguments($this->inlineArguments($container, $argument->getArguments())); + $argument->setMethodCalls($this->inlineArguments($container, $argument->getMethodCalls())); + $argument->setProperties($this->inlineArguments($container, $argument->getProperties())); + + $configurator = $this->inlineArguments($container, array($argument->getConfigurator())); + $argument->setConfigurator($configurator[0]); + + $factory = $this->inlineArguments($container, array($argument->getFactory())); + $argument->setFactory($factory[0]); + } + } + + return $arguments; + } + + /** + * Checks if the definition is inlineable. + * + * @param ContainerBuilder $container + * @param string $id + * @param Definition $definition + * + * @return bool If the definition is inlineable + */ + private function isInlineableDefinition(ContainerBuilder $container, $id, Definition $definition) + { + if (!$definition->isShared()) { + return true; + } + + if ($definition->isPublic() || $definition->isLazy()) { + return false; + } + + if (!$this->graph->hasNode($id)) { + return true; + } + + if ($this->currentId == $id) { + return false; + } + + $ids = array(); + foreach ($this->graph->getNode($id)->getInEdges() as $edge) { + $ids[] = $edge->getSourceNode()->getId(); + } + + if (count(array_unique($ids)) > 1) { + return false; + } + + if (count($ids) > 1 && is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) { + return false; + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php new file mode 100644 index 0000000..db208fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/LoggingFormatter.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +/** + * Used to format logging messages during the compilation. + * + * @author Johannes M. Schmitt + */ +class LoggingFormatter +{ + public function formatRemoveService(CompilerPassInterface $pass, $id, $reason) + { + return $this->format($pass, sprintf('Removed service "%s"; reason: %s.', $id, $reason)); + } + + public function formatInlineService(CompilerPassInterface $pass, $id, $target) + { + return $this->format($pass, sprintf('Inlined service "%s" to "%s".', $id, $target)); + } + + public function formatUpdateReference(CompilerPassInterface $pass, $serviceId, $oldDestId, $newDestId) + { + return $this->format($pass, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $serviceId, $oldDestId, $newDestId)); + } + + public function formatResolveInheritance(CompilerPassInterface $pass, $childId, $parentId) + { + return $this->format($pass, sprintf('Resolving inheritance for "%s" (parent: %s).', $childId, $parentId)); + } + + public function format(CompilerPassInterface $pass, $message) + { + return sprintf('%s: %s', get_class($pass), $message); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php new file mode 100644 index 0000000..f9e6024 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; + +/** + * Merges extension configs into the container builder. + * + * @author Fabien Potencier + */ +class MergeExtensionConfigurationPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $parameters = $container->getParameterBag()->all(); + $definitions = $container->getDefinitions(); + $aliases = $container->getAliases(); + $exprLangProviders = $container->getExpressionLanguageProviders(); + + foreach ($container->getExtensions() as $extension) { + if ($extension instanceof PrependExtensionInterface) { + $extension->prepend($container); + } + } + + foreach ($container->getExtensions() as $name => $extension) { + if (!$config = $container->getExtensionConfig($name)) { + // this extension was not called + continue; + } + $config = $container->getParameterBag()->resolveValue($config); + + $tmpContainer = new ContainerBuilder($container->getParameterBag()); + $tmpContainer->setResourceTracking($container->isTrackingResources()); + $tmpContainer->addObjectResource($extension); + + foreach ($exprLangProviders as $provider) { + $tmpContainer->addExpressionLanguageProvider($provider); + } + + $extension->load($config, $tmpContainer); + + $container->merge($tmpContainer); + $container->getParameterBag()->add($parameters); + } + + $container->addDefinitions($definitions); + $container->addAliases($aliases); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php new file mode 100644 index 0000000..246529d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Compiler Pass Configuration. + * + * This class has a default configuration embedded. + * + * @author Johannes M. Schmitt + */ +class PassConfig +{ + const TYPE_AFTER_REMOVING = 'afterRemoving'; + const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization'; + const TYPE_BEFORE_REMOVING = 'beforeRemoving'; + const TYPE_OPTIMIZE = 'optimization'; + const TYPE_REMOVE = 'removing'; + + private $mergePass; + private $afterRemovingPasses = array(); + private $beforeOptimizationPasses = array(); + private $beforeRemovingPasses = array(); + private $optimizationPasses; + private $removingPasses; + + /** + * Constructor. + */ + public function __construct() + { + $this->mergePass = new MergeExtensionConfigurationPass(); + + $this->optimizationPasses = array( + new ExtensionCompilerPass(), + new ResolveDefinitionTemplatesPass(), + new DecoratorServicePass(), + new ResolveParameterPlaceHoldersPass(), + new CheckDefinitionValidityPass(), + new ResolveReferencesToAliasesPass(), + new ResolveInvalidReferencesPass(), + new AutowirePass(), + new AnalyzeServiceReferencesPass(true), + new CheckCircularReferencesPass(), + new CheckReferenceValidityPass(), + ); + + $this->removingPasses = array( + new RemovePrivateAliasesPass(), + new RemoveAbstractDefinitionsPass(), + new ReplaceAliasByActualDefinitionPass(), + new RepeatedPass(array( + new AnalyzeServiceReferencesPass(), + new InlineServiceDefinitionsPass(), + new AnalyzeServiceReferencesPass(), + new RemoveUnusedDefinitionsPass(), + )), + new CheckExceptionOnInvalidReferenceBehaviorPass(), + ); + } + + /** + * Returns all passes in order to be processed. + * + * @return array An array of all passes to process + */ + public function getPasses() + { + return array_merge( + array($this->mergePass), + $this->beforeOptimizationPasses, + $this->optimizationPasses, + $this->beforeRemovingPasses, + $this->removingPasses, + $this->afterRemovingPasses + ); + } + + /** + * Adds a pass. + * + * @param CompilerPassInterface $pass A Compiler pass + * @param string $type The pass type + * + * @throws InvalidArgumentException when a pass type doesn't exist + */ + public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION) + { + $property = $type.'Passes'; + if (!isset($this->$property)) { + throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); + } + + $passes = &$this->$property; + $passes[] = $pass; + } + + /** + * Gets all passes for the AfterRemoving pass. + * + * @return array An array of passes + */ + public function getAfterRemovingPasses() + { + return $this->afterRemovingPasses; + } + + /** + * Gets all passes for the BeforeOptimization pass. + * + * @return array An array of passes + */ + public function getBeforeOptimizationPasses() + { + return $this->beforeOptimizationPasses; + } + + /** + * Gets all passes for the BeforeRemoving pass. + * + * @return array An array of passes + */ + public function getBeforeRemovingPasses() + { + return $this->beforeRemovingPasses; + } + + /** + * Gets all passes for the Optimization pass. + * + * @return array An array of passes + */ + public function getOptimizationPasses() + { + return $this->optimizationPasses; + } + + /** + * Gets all passes for the Removing pass. + * + * @return array An array of passes + */ + public function getRemovingPasses() + { + return $this->removingPasses; + } + + /** + * Gets all passes for the Merge pass. + * + * @return array An array of passes + */ + public function getMergePass() + { + return $this->mergePass; + } + + /** + * Sets the Merge Pass. + * + * @param CompilerPassInterface $pass The merge pass + */ + public function setMergePass(CompilerPassInterface $pass) + { + $this->mergePass = $pass; + } + + /** + * Sets the AfterRemoving passes. + * + * @param array $passes An array of passes + */ + public function setAfterRemovingPasses(array $passes) + { + $this->afterRemovingPasses = $passes; + } + + /** + * Sets the BeforeOptimization passes. + * + * @param array $passes An array of passes + */ + public function setBeforeOptimizationPasses(array $passes) + { + $this->beforeOptimizationPasses = $passes; + } + + /** + * Sets the BeforeRemoving passes. + * + * @param array $passes An array of passes + */ + public function setBeforeRemovingPasses(array $passes) + { + $this->beforeRemovingPasses = $passes; + } + + /** + * Sets the Optimization passes. + * + * @param array $passes An array of passes + */ + public function setOptimizationPasses(array $passes) + { + $this->optimizationPasses = $passes; + } + + /** + * Sets the Removing passes. + * + * @param array $passes An array of passes + */ + public function setRemovingPasses(array $passes) + { + $this->removingPasses = $passes; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php new file mode 100644 index 0000000..0ef0af0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveAbstractDefinitionsPass.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Removes abstract Definitions. + */ +class RemoveAbstractDefinitionsPass implements CompilerPassInterface +{ + /** + * Removes abstract definitions from the ContainerBuilder. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $compiler = $container->getCompiler(); + $formatter = $compiler->getLoggingFormatter(); + + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isAbstract()) { + $container->removeDefinition($id); + $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'abstract')); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php new file mode 100644 index 0000000..5c53a33 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemovePrivateAliasesPass.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Remove private aliases from the container. They were only used to establish + * dependencies between services, and these dependencies have been resolved in + * one of the previous passes. + * + * @author Johannes M. Schmitt + */ +class RemovePrivateAliasesPass implements CompilerPassInterface +{ + /** + * Removes private aliases from the ContainerBuilder. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $compiler = $container->getCompiler(); + $formatter = $compiler->getLoggingFormatter(); + + foreach ($container->getAliases() as $id => $alias) { + if ($alias->isPublic()) { + continue; + } + + $container->removeAlias($id); + $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'private alias')); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php new file mode 100644 index 0000000..9e18a9e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RemoveUnusedDefinitionsPass.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Removes unused service definitions from the container. + * + * @author Johannes M. Schmitt + */ +class RemoveUnusedDefinitionsPass implements RepeatablePassInterface +{ + private $repeatedPass; + + /** + * {@inheritdoc} + */ + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + $this->repeatedPass = $repeatedPass; + } + + /** + * Processes the ContainerBuilder to remove unused definitions. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $compiler = $container->getCompiler(); + $formatter = $compiler->getLoggingFormatter(); + $graph = $compiler->getServiceReferenceGraph(); + + $hasChanged = false; + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isPublic()) { + continue; + } + + if ($graph->hasNode($id)) { + $edges = $graph->getNode($id)->getInEdges(); + $referencingAliases = array(); + $sourceIds = array(); + foreach ($edges as $edge) { + $node = $edge->getSourceNode(); + $sourceIds[] = $node->getId(); + + if ($node->isAlias()) { + $referencingAliases[] = $node->getValue(); + } + } + $isReferenced = (count(array_unique($sourceIds)) - count($referencingAliases)) > 0; + } else { + $referencingAliases = array(); + $isReferenced = false; + } + + if (1 === count($referencingAliases) && false === $isReferenced) { + $container->setDefinition((string) reset($referencingAliases), $definition); + $definition->setPublic(true); + $container->removeDefinition($id); + $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'replaces alias '.reset($referencingAliases))); + } elseif (0 === count($referencingAliases) && false === $isReferenced) { + $container->removeDefinition($id); + $compiler->addLogMessage($formatter->formatRemoveService($this, $id, 'unused')); + $hasChanged = true; + } + } + + if ($hasChanged) { + $this->repeatedPass->setRepeat(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php new file mode 100644 index 0000000..d60ae35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatablePassInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +/** + * Interface that must be implemented by passes that are run as part of an + * RepeatedPass. + * + * @author Johannes M. Schmitt + */ +interface RepeatablePassInterface extends CompilerPassInterface +{ + /** + * Sets the RepeatedPass interface. + * + * @param RepeatedPass $repeatedPass + */ + public function setRepeatedPass(RepeatedPass $repeatedPass); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php new file mode 100644 index 0000000..e34b068 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/RepeatedPass.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * A pass that might be run repeatedly. + * + * @author Johannes M. Schmitt + */ +class RepeatedPass implements CompilerPassInterface +{ + /** + * @var bool + */ + private $repeat = false; + + /** + * @var RepeatablePassInterface[] + */ + private $passes; + + /** + * Constructor. + * + * @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects + * + * @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface + */ + public function __construct(array $passes) + { + foreach ($passes as $pass) { + if (!$pass instanceof RepeatablePassInterface) { + throw new InvalidArgumentException('$passes must be an array of RepeatablePassInterface.'); + } + + $pass->setRepeatedPass($this); + } + + $this->passes = $passes; + } + + /** + * Process the repeatable passes that run more than once. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->repeat = false; + foreach ($this->passes as $pass) { + $pass->process($container); + } + + if ($this->repeat) { + $this->process($container); + } + } + + /** + * Sets if the pass should repeat. + */ + public function setRepeat() + { + $this->repeat = true; + } + + /** + * Returns the passes. + * + * @return RepeatablePassInterface[] An array of RepeatablePassInterface objects + */ + public function getPasses() + { + return $this->passes; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php new file mode 100644 index 0000000..8308937 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ReplaceAliasByActualDefinitionPass.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Replaces aliases with actual service definitions, effectively removing these + * aliases. + * + * @author Johannes M. Schmitt + */ +class ReplaceAliasByActualDefinitionPass implements CompilerPassInterface +{ + private $compiler; + private $formatter; + private $sourceId; + + /** + * Process the Container to replace aliases with service definitions. + * + * @param ContainerBuilder $container + * + * @throws InvalidArgumentException if the service definition does not exist + */ + public function process(ContainerBuilder $container) + { + $this->compiler = $container->getCompiler(); + $this->formatter = $this->compiler->getLoggingFormatter(); + + foreach ($container->getAliases() as $id => $alias) { + $aliasId = (string) $alias; + + try { + $definition = $container->getDefinition($aliasId); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $id, $alias), null, $e); + } + + if ($definition->isPublic()) { + continue; + } + + $definition->setPublic(true); + $container->setDefinition($id, $definition); + $container->removeDefinition($aliasId); + + $this->updateReferences($container, $aliasId, $id); + + // we have to restart the process due to concurrent modification of + // the container + $this->process($container); + + break; + } + } + + /** + * Updates references to remove aliases. + * + * @param ContainerBuilder $container The container + * @param string $currentId The alias identifier being replaced + * @param string $newId The id of the service the alias points to + */ + private function updateReferences($container, $currentId, $newId) + { + foreach ($container->getAliases() as $id => $alias) { + if ($currentId === (string) $alias) { + $container->setAlias($id, $newId); + } + } + + foreach ($container->getDefinitions() as $id => $definition) { + $this->sourceId = $id; + + $definition->setArguments( + $this->updateArgumentReferences($definition->getArguments(), $currentId, $newId) + ); + + $definition->setMethodCalls( + $this->updateArgumentReferences($definition->getMethodCalls(), $currentId, $newId) + ); + + $definition->setProperties( + $this->updateArgumentReferences($definition->getProperties(), $currentId, $newId) + ); + } + } + + /** + * Updates argument references. + * + * @param array $arguments An array of Arguments + * @param string $currentId The alias identifier + * @param string $newId The identifier the alias points to + * + * @return array + */ + private function updateArgumentReferences(array $arguments, $currentId, $newId) + { + foreach ($arguments as $k => $argument) { + if (is_array($argument)) { + $arguments[$k] = $this->updateArgumentReferences($argument, $currentId, $newId); + } elseif ($argument instanceof Reference) { + if ($currentId === (string) $argument) { + $arguments[$k] = new Reference($newId, $argument->getInvalidBehavior()); + $this->compiler->addLogMessage($this->formatter->formatUpdateReference($this, $this->sourceId, $currentId, $newId)); + } + } + } + + return $arguments; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php new file mode 100644 index 0000000..2f94df9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveDefinitionTemplatesPass.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * This replaces all DefinitionDecorator instances with their equivalent fully + * merged Definition instance. + * + * @author Johannes M. Schmitt + * @author Nicolas Grekas + */ +class ResolveDefinitionTemplatesPass implements CompilerPassInterface +{ + private $compiler; + private $formatter; + private $currentId; + + /** + * Process the ContainerBuilder to replace DefinitionDecorator instances with their real Definition instances. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->compiler = $container->getCompiler(); + $this->formatter = $this->compiler->getLoggingFormatter(); + + $container->setDefinitions($this->resolveArguments($container, $container->getDefinitions(), true)); + } + + /** + * Resolves definition decorator arguments. + * + * @param ContainerBuilder $container The ContainerBuilder + * @param array $arguments An array of arguments + * @param bool $isRoot If we are processing the root definitions or not + * + * @return array + */ + private function resolveArguments(ContainerBuilder $container, array $arguments, $isRoot = false) + { + foreach ($arguments as $k => $argument) { + if ($isRoot) { + // yes, we are specifically fetching the definition from the + // container to ensure we are not operating on stale data + $arguments[$k] = $argument = $container->getDefinition($k); + $this->currentId = $k; + } + if (is_array($argument)) { + $arguments[$k] = $this->resolveArguments($container, $argument); + } elseif ($argument instanceof Definition) { + if ($argument instanceof DefinitionDecorator) { + $arguments[$k] = $argument = $this->resolveDefinition($container, $argument); + if ($isRoot) { + $container->setDefinition($k, $argument); + } + } + $argument->setArguments($this->resolveArguments($container, $argument->getArguments())); + $argument->setMethodCalls($this->resolveArguments($container, $argument->getMethodCalls())); + $argument->setProperties($this->resolveArguments($container, $argument->getProperties())); + + $configurator = $this->resolveArguments($container, array($argument->getConfigurator())); + $argument->setConfigurator($configurator[0]); + + $factory = $this->resolveArguments($container, array($argument->getFactory())); + $argument->setFactory($factory[0]); + } + } + + return $arguments; + } + + /** + * Resolves the definition. + * + * @param ContainerBuilder $container The ContainerBuilder + * @param DefinitionDecorator $definition + * + * @return Definition + * + * @throws \RuntimeException When the definition is invalid + */ + private function resolveDefinition(ContainerBuilder $container, DefinitionDecorator $definition) + { + if (!$container->hasDefinition($parent = $definition->getParent())) { + throw new RuntimeException(sprintf('The parent definition "%s" defined for definition "%s" does not exist.', $parent, $this->currentId)); + } + + $parentDef = $container->getDefinition($parent); + if ($parentDef instanceof DefinitionDecorator) { + $id = $this->currentId; + $this->currentId = $parent; + $parentDef = $this->resolveDefinition($container, $parentDef); + $container->setDefinition($parent, $parentDef); + $this->currentId = $id; + } + + $this->compiler->addLogMessage($this->formatter->formatResolveInheritance($this, $this->currentId, $parent)); + $def = new Definition(); + + // merge in parent definition + // purposely ignored attributes: abstract, tags + $def->setClass($parentDef->getClass()); + $def->setArguments($parentDef->getArguments()); + $def->setMethodCalls($parentDef->getMethodCalls()); + $def->setProperties($parentDef->getProperties()); + $def->setAutowiringTypes($parentDef->getAutowiringTypes()); + if ($parentDef->isDeprecated()) { + $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); + } + $def->setFactory($parentDef->getFactory()); + $def->setConfigurator($parentDef->getConfigurator()); + $def->setFile($parentDef->getFile()); + $def->setPublic($parentDef->isPublic()); + $def->setLazy($parentDef->isLazy()); + + // overwrite with values specified in the decorator + $changes = $definition->getChanges(); + if (isset($changes['class'])) { + $def->setClass($definition->getClass()); + } + if (isset($changes['factory'])) { + $def->setFactory($definition->getFactory()); + } + if (isset($changes['configurator'])) { + $def->setConfigurator($definition->getConfigurator()); + } + if (isset($changes['file'])) { + $def->setFile($definition->getFile()); + } + if (isset($changes['public'])) { + $def->setPublic($definition->isPublic()); + } + if (isset($changes['lazy'])) { + $def->setLazy($definition->isLazy()); + } + if (isset($changes['deprecated'])) { + $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%')); + } + if (isset($changes['decorated_service'])) { + $decoratedService = $definition->getDecoratedService(); + if (null === $decoratedService) { + $def->setDecoratedService($decoratedService); + } else { + $def->setDecoratedService($decoratedService[0], $decoratedService[1]); + } + } + + // merge arguments + foreach ($definition->getArguments() as $k => $v) { + if (is_numeric($k)) { + $def->addArgument($v); + continue; + } + + if (0 !== strpos($k, 'index_')) { + throw new RuntimeException(sprintf('Invalid argument key "%s" found.', $k)); + } + + $index = (int) substr($k, strlen('index_')); + $def->replaceArgument($index, $v); + } + + // merge properties + foreach ($definition->getProperties() as $k => $v) { + $def->setProperty($k, $v); + } + + // append method calls + if (count($calls = $definition->getMethodCalls()) > 0) { + $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); + } + + // merge autowiring types + foreach ($definition->getAutowiringTypes() as $autowiringType) { + $def->addAutowiringType($autowiringType); + } + + // these attributes are always taken from the child + $def->setAbstract($definition->isAbstract()); + $def->setShared($definition->isShared()); + $def->setTags($definition->getTags()); + + return $def; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php new file mode 100644 index 0000000..85dbceb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveInvalidReferencesPass.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Emulates the invalid behavior if the reference is not found within the + * container. + * + * @author Johannes M. Schmitt + */ +class ResolveInvalidReferencesPass implements CompilerPassInterface +{ + private $container; + + /** + * Process the ContainerBuilder to resolve invalid references. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + foreach ($container->getDefinitions() as $definition) { + if ($definition->isSynthetic() || $definition->isAbstract()) { + continue; + } + + $definition->setArguments( + $this->processArguments($definition->getArguments()) + ); + + $calls = array(); + foreach ($definition->getMethodCalls() as $call) { + try { + $calls[] = array($call[0], $this->processArguments($call[1], true)); + } catch (RuntimeException $e) { + // this call is simply removed + } + } + $definition->setMethodCalls($calls); + + $properties = array(); + foreach ($definition->getProperties() as $name => $value) { + try { + $value = $this->processArguments(array($value), true); + $properties[$name] = reset($value); + } catch (RuntimeException $e) { + // ignore property + } + } + $definition->setProperties($properties); + } + } + + /** + * Processes arguments to determine invalid references. + * + * @param array $arguments An array of Reference objects + * @param bool $inMethodCall + * + * @return array + * + * @throws RuntimeException When the config is invalid + */ + private function processArguments(array $arguments, $inMethodCall = false) + { + foreach ($arguments as $k => $argument) { + if (is_array($argument)) { + $arguments[$k] = $this->processArguments($argument, $inMethodCall); + } elseif ($argument instanceof Reference) { + $id = (string) $argument; + + $invalidBehavior = $argument->getInvalidBehavior(); + $exists = $this->container->has($id); + + // resolve invalid behavior + if (!$exists && ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { + $arguments[$k] = null; + } elseif (!$exists && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { + if ($inMethodCall) { + throw new RuntimeException('Method shouldn\'t be called.'); + } + + $arguments[$k] = null; + } + } + } + + return $arguments; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php new file mode 100644 index 0000000..0c5963c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveParameterPlaceHoldersPass.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * Resolves all parameter placeholders "%somevalue%" to their real values. + * + * @author Johannes M. Schmitt + */ +class ResolveParameterPlaceHoldersPass implements CompilerPassInterface +{ + /** + * Processes the ContainerBuilder to resolve parameter placeholders. + * + * @param ContainerBuilder $container + * + * @throws ParameterNotFoundException + */ + public function process(ContainerBuilder $container) + { + $parameterBag = $container->getParameterBag(); + + foreach ($container->getDefinitions() as $id => $definition) { + try { + $definition->setClass($parameterBag->resolveValue($definition->getClass())); + $definition->setFile($parameterBag->resolveValue($definition->getFile())); + $definition->setArguments($parameterBag->resolveValue($definition->getArguments())); + + $factory = $definition->getFactory(); + + if (is_array($factory) && isset($factory[0])) { + $factory[0] = $parameterBag->resolveValue($factory[0]); + $definition->setFactory($factory); + } + + $calls = array(); + foreach ($definition->getMethodCalls() as $name => $arguments) { + $calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments); + } + $definition->setMethodCalls($calls); + + $definition->setProperties($parameterBag->resolveValue($definition->getProperties())); + } catch (ParameterNotFoundException $e) { + $e->setSourceId($id); + + throw $e; + } + } + + $aliases = array(); + foreach ($container->getAliases() as $name => $target) { + $aliases[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($target); + } + $container->setAliases($aliases); + + $parameterBag->resolve(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php new file mode 100644 index 0000000..f670f51 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ResolveReferencesToAliasesPass.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Replaces all references to aliases with references to the actual service. + * + * @author Johannes M. Schmitt + */ +class ResolveReferencesToAliasesPass implements CompilerPassInterface +{ + private $container; + + /** + * Processes the ContainerBuilder to replace references to aliases with actual service references. + * + * @param ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + + foreach ($container->getDefinitions() as $definition) { + if ($definition->isSynthetic() || $definition->isAbstract()) { + continue; + } + + $definition->setArguments($this->processArguments($definition->getArguments())); + $definition->setMethodCalls($this->processArguments($definition->getMethodCalls())); + $definition->setProperties($this->processArguments($definition->getProperties())); + } + + foreach ($container->getAliases() as $id => $alias) { + $aliasId = (string) $alias; + if ($aliasId !== $defId = $this->getDefinitionId($aliasId)) { + $container->setAlias($id, new Alias($defId, $alias->isPublic())); + } + } + } + + /** + * Processes the arguments to replace aliases. + * + * @param array $arguments An array of References + * + * @return array An array of References + */ + private function processArguments(array $arguments) + { + foreach ($arguments as $k => $argument) { + if (is_array($argument)) { + $arguments[$k] = $this->processArguments($argument); + } elseif ($argument instanceof Reference) { + $defId = $this->getDefinitionId($id = (string) $argument); + + if ($defId !== $id) { + $arguments[$k] = new Reference($defId, $argument->getInvalidBehavior()); + } + } + } + + return $arguments; + } + + /** + * Resolves an alias into a definition id. + * + * @param string $id The definition or alias id to resolve + * + * @return string The definition id with aliases resolved + */ + private function getDefinitionId($id) + { + $seen = array(); + while ($this->container->hasAlias($id)) { + if (isset($seen[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($seen)); + } + $seen[$id] = true; + $id = (string) $this->container->getAlias($id); + } + + return $id; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php new file mode 100644 index 0000000..dc9a1a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraph.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * This is a directed graph of your services. + * + * This information can be used by your compiler passes instead of collecting + * it themselves which improves performance quite a lot. + * + * @author Johannes M. Schmitt + */ +class ServiceReferenceGraph +{ + /** + * @var ServiceReferenceGraphNode[] + */ + private $nodes = array(); + + /** + * Checks if the graph has a specific node. + * + * @param string $id Id to check + * + * @return bool + */ + public function hasNode($id) + { + return isset($this->nodes[$id]); + } + + /** + * Gets a node by identifier. + * + * @param string $id The id to retrieve + * + * @return ServiceReferenceGraphNode The node matching the supplied identifier + * + * @throws InvalidArgumentException if no node matches the supplied identifier + */ + public function getNode($id) + { + if (!isset($this->nodes[$id])) { + throw new InvalidArgumentException(sprintf('There is no node with id "%s".', $id)); + } + + return $this->nodes[$id]; + } + + /** + * Returns all nodes. + * + * @return ServiceReferenceGraphNode[] An array of all ServiceReferenceGraphNode objects + */ + public function getNodes() + { + return $this->nodes; + } + + /** + * Clears all nodes. + */ + public function clear() + { + $this->nodes = array(); + } + + /** + * Connects 2 nodes together in the Graph. + * + * @param string $sourceId + * @param string $sourceValue + * @param string $destId + * @param string $destValue + * @param string $reference + */ + public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null) + { + $sourceNode = $this->createNode($sourceId, $sourceValue); + $destNode = $this->createNode($destId, $destValue); + $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference); + + $sourceNode->addOutEdge($edge); + $destNode->addInEdge($edge); + } + + /** + * Creates a graph node. + * + * @param string $id + * @param string $value + * + * @return ServiceReferenceGraphNode + */ + private function createNode($id, $value) + { + if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) { + return $this->nodes[$id]; + } + + return $this->nodes[$id] = new ServiceReferenceGraphNode($id, $value); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php new file mode 100644 index 0000000..6a3e2ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphEdge.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +/** + * Represents an edge in your service graph. + * + * Value is typically a reference. + * + * @author Johannes M. Schmitt + */ +class ServiceReferenceGraphEdge +{ + private $sourceNode; + private $destNode; + private $value; + + /** + * Constructor. + * + * @param ServiceReferenceGraphNode $sourceNode + * @param ServiceReferenceGraphNode $destNode + * @param string $value + */ + public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null) + { + $this->sourceNode = $sourceNode; + $this->destNode = $destNode; + $this->value = $value; + } + + /** + * Returns the value of the edge. + * + * @return ServiceReferenceGraphNode + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the source node. + * + * @return ServiceReferenceGraphNode + */ + public function getSourceNode() + { + return $this->sourceNode; + } + + /** + * Returns the destination node. + * + * @return ServiceReferenceGraphNode + */ + public function getDestNode() + { + return $this->destNode; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php new file mode 100644 index 0000000..c49c932 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/ServiceReferenceGraphNode.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Alias; + +/** + * Represents a node in your service graph. + * + * Value is typically a definition, or an alias. + * + * @author Johannes M. Schmitt + */ +class ServiceReferenceGraphNode +{ + private $id; + private $inEdges = array(); + private $outEdges = array(); + private $value; + + /** + * Constructor. + * + * @param string $id The node identifier + * @param mixed $value The node value + */ + public function __construct($id, $value) + { + $this->id = $id; + $this->value = $value; + } + + /** + * Adds an in edge to this node. + * + * @param ServiceReferenceGraphEdge $edge + */ + public function addInEdge(ServiceReferenceGraphEdge $edge) + { + $this->inEdges[] = $edge; + } + + /** + * Adds an out edge to this node. + * + * @param ServiceReferenceGraphEdge $edge + */ + public function addOutEdge(ServiceReferenceGraphEdge $edge) + { + $this->outEdges[] = $edge; + } + + /** + * Checks if the value of this node is an Alias. + * + * @return bool True if the value is an Alias instance + */ + public function isAlias() + { + return $this->value instanceof Alias; + } + + /** + * Checks if the value of this node is a Definition. + * + * @return bool True if the value is a Definition instance + */ + public function isDefinition() + { + return $this->value instanceof Definition; + } + + /** + * Returns the identifier. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Returns the in edges. + * + * @return array The in ServiceReferenceGraphEdge array + */ + public function getInEdges() + { + return $this->inEdges; + } + + /** + * Returns the out edges. + * + * @return array The out ServiceReferenceGraphEdge array + */ + public function getOutEdges() + { + return $this->outEdges; + } + + /** + * Returns the value of this Node. + * + * @return mixed The value + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php new file mode 100644 index 0000000..3edf124 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php @@ -0,0 +1,362 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; + +/** + * Container is a dependency injection container. + * + * It gives access to object instances (services). + * + * Services and parameters are simple key/pair stores. + * + * Parameter and service keys are case insensitive. + * + * A service id can contain lowercased letters, digits, underscores, and dots. + * Underscores are used to separate words, and dots to group services + * under namespaces: + * + *
      + *
    • request
    • + *
    • mysql_session_storage
    • + *
    • symfony.mysql_session_storage
    • + *
    + * + * A service can also be defined by creating a method named + * getXXXService(), where XXX is the camelized version of the id: + * + *
      + *
    • request -> getRequestService()
    • + *
    • mysql_session_storage -> getMysqlSessionStorageService()
    • + *
    • symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()
    • + *
    + * + * The container can have three possible behaviors when a service does not exist: + * + * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default) + * * NULL_ON_INVALID_REFERENCE: Returns null + * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference + * (for instance, ignore a setter if the service does not exist) + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class Container implements ResettableContainerInterface +{ + /** + * @var ParameterBagInterface + */ + protected $parameterBag; + + protected $services = array(); + protected $methodMap = array(); + protected $aliases = array(); + protected $loading = array(); + + private $underscoreMap = array('_' => '', '.' => '_', '\\' => '_'); + + /** + * Constructor. + * + * @param ParameterBagInterface $parameterBag A ParameterBagInterface instance + */ + public function __construct(ParameterBagInterface $parameterBag = null) + { + $this->parameterBag = $parameterBag ?: new ParameterBag(); + } + + /** + * Compiles the container. + * + * This method does two things: + * + * * Parameter values are resolved; + * * The parameter bag is frozen. + */ + public function compile() + { + $this->parameterBag->resolve(); + + $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + } + + /** + * Returns true if the container parameter bag are frozen. + * + * @return bool true if the container parameter bag are frozen, false otherwise + */ + public function isFrozen() + { + return $this->parameterBag instanceof FrozenParameterBag; + } + + /** + * Gets the service container parameter bag. + * + * @return ParameterBagInterface A ParameterBagInterface instance + */ + public function getParameterBag() + { + return $this->parameterBag; + } + + /** + * Gets a parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws InvalidArgumentException if the parameter is not defined + */ + public function getParameter($name) + { + return $this->parameterBag->get($name); + } + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return bool The presence of parameter in container + */ + public function hasParameter($name) + { + return $this->parameterBag->has($name); + } + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + */ + public function setParameter($name, $value) + { + $this->parameterBag->set($name, $value); + } + + /** + * Sets a service. + * + * Setting a service to null resets the service: has() returns false and get() + * behaves in the same way as if the service was never created. + * + * @param string $id The service identifier + * @param object $service The service instance + */ + public function set($id, $service) + { + $id = strtolower($id); + + if ('service_container' === $id) { + throw new InvalidArgumentException('You cannot set service "service_container".'); + } + + $this->services[$id] = $service; + + if (null === $service) { + unset($this->services[$id]); + } + } + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return bool true if the service is defined, false otherwise + */ + public function has($id) + { + for ($i = 2;;) { + if ('service_container' === $id + || isset($this->aliases[$id]) + || isset($this->services[$id]) + || array_key_exists($id, $this->services) + ) { + return true; + } + if (--$i && $id !== $lcId = strtolower($id)) { + $id = $lcId; + } else { + return method_exists($this, 'get'.strtr($id, $this->underscoreMap).'Service'); + } + } + } + + /** + * Gets a service. + * + * If a service is defined both through a set() method and + * with a get{$id}Service() method, the former has always precedence. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * @throws \Exception if an exception has been thrown when the service has been resolved + * + * @see Reference + */ + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) + { + // Attempt to retrieve the service by checking first aliases then + // available services. Service IDs are case insensitive, however since + // this method can be called thousands of times during a request, avoid + // calling strtolower() unless necessary. + for ($i = 2;;) { + if ('service_container' === $id) { + return $this; + } + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + // Re-use shared service instance if it exists. + if (isset($this->services[$id]) || array_key_exists($id, $this->services)) { + return $this->services[$id]; + } + + if (isset($this->loading[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($this->loading)); + } + + if (isset($this->methodMap[$id])) { + $method = $this->methodMap[$id]; + } elseif (--$i && $id !== $lcId = strtolower($id)) { + $id = $lcId; + continue; + } elseif (method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) { + // $method is set to the right value, proceed + } else { + if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + if (!$id) { + throw new ServiceNotFoundException($id); + } + + $alternatives = array(); + foreach ($this->services as $key => $associatedService) { + $lev = levenshtein($id, $key); + if ($lev <= strlen($id) / 3 || false !== strpos($key, $id)) { + $alternatives[] = $key; + } + } + + throw new ServiceNotFoundException($id, null, null, $alternatives); + } + + return; + } + + $this->loading[$id] = true; + + try { + $service = $this->$method(); + } catch (\Exception $e) { + unset($this->services[$id]); + + throw $e; + } finally { + unset($this->loading[$id]); + } + + return $service; + } + } + + /** + * Returns true if the given service has actually been initialized. + * + * @param string $id The service identifier + * + * @return bool true if service has already been initialized, false otherwise + */ + public function initialized($id) + { + $id = strtolower($id); + + if ('service_container' === $id) { + return false; + } + + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + + return isset($this->services[$id]) || array_key_exists($id, $this->services); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->services = array(); + } + + /** + * Gets all service ids. + * + * @return array An array of all defined service ids + */ + public function getServiceIds() + { + $ids = array(); + $r = new \ReflectionClass($this); + foreach ($r->getMethods() as $method) { + if (preg_match('/^get(.+)Service$/', $method->name, $match)) { + $ids[] = self::underscore($match[1]); + } + } + $ids[] = 'service_container'; + + return array_unique(array_merge($ids, array_keys($this->services))); + } + + /** + * Camelizes a string. + * + * @param string $id A string to camelize + * + * @return string The camelized string + */ + public static function camelize($id) + { + return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => '')); + } + + /** + * A string to underscore. + * + * @param string $id The string to underscore + * + * @return string The underscored string + */ + public static function underscore($id) + { + return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($id, '_', '.'))); + } + + private function __clone() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareInterface.php new file mode 100644 index 0000000..fe301b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * ContainerAwareInterface should be implemented by classes that depends on a Container. + * + * @author Fabien Potencier + */ +interface ContainerAwareInterface +{ + /** + * Sets the container. + * + * @param ContainerInterface|null $container A ContainerInterface instance or null + */ + public function setContainer(ContainerInterface $container = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareTrait.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareTrait.php new file mode 100644 index 0000000..ccf064f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerAwareTrait.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * ContainerAware trait. + * + * @author Fabien Potencier + */ +trait ContainerAwareTrait +{ + /** + * @var ContainerInterface + */ + protected $container; + + /** + * Sets the container. + * + * @param ContainerInterface|null $container A ContainerInterface instance or null + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerBuilder.php new file mode 100644 index 0000000..cd0b64c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -0,0 +1,1069 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * ContainerBuilder is a DI container that provides an API to easily describe services. + * + * @author Fabien Potencier + */ +class ContainerBuilder extends Container implements TaggedContainerInterface +{ + /** + * @var ExtensionInterface[] + */ + private $extensions = array(); + + /** + * @var ExtensionInterface[] + */ + private $extensionsByNs = array(); + + /** + * @var Definition[] + */ + private $definitions = array(); + + /** + * @var Definition[] + */ + private $obsoleteDefinitions = array(); + + /** + * @var Alias[] + */ + private $aliasDefinitions = array(); + + /** + * @var ResourceInterface[] + */ + private $resources = array(); + + private $extensionConfigs = array(); + + /** + * @var Compiler + */ + private $compiler; + + private $trackResources = true; + + /** + * @var InstantiatorInterface|null + */ + private $proxyInstantiator; + + /** + * @var ExpressionLanguage|null + */ + private $expressionLanguage; + + /** + * @var ExpressionFunctionProviderInterface[] + */ + private $expressionLanguageProviders = array(); + + /** + * @var string[] with tag names used by findTaggedServiceIds + */ + private $usedTags = array(); + + /** + * Sets the track resources flag. + * + * If you are not using the loaders and therefore don't want + * to depend on the Config component, set this flag to false. + * + * @param bool $track true if you want to track resources, false otherwise + */ + public function setResourceTracking($track) + { + $this->trackResources = (bool) $track; + } + + /** + * Checks if resources are tracked. + * + * @return bool true if resources are tracked, false otherwise + */ + public function isTrackingResources() + { + return $this->trackResources; + } + + /** + * Sets the instantiator to be used when fetching proxies. + * + * @param InstantiatorInterface $proxyInstantiator + */ + public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator) + { + $this->proxyInstantiator = $proxyInstantiator; + } + + /** + * Registers an extension. + * + * @param ExtensionInterface $extension An extension instance + */ + public function registerExtension(ExtensionInterface $extension) + { + $this->extensions[$extension->getAlias()] = $extension; + + if (false !== $extension->getNamespace()) { + $this->extensionsByNs[$extension->getNamespace()] = $extension; + } + } + + /** + * Returns an extension by alias or namespace. + * + * @param string $name An alias or a namespace + * + * @return ExtensionInterface An extension instance + * + * @throws LogicException if the extension is not registered + */ + public function getExtension($name) + { + if (isset($this->extensions[$name])) { + return $this->extensions[$name]; + } + + if (isset($this->extensionsByNs[$name])) { + return $this->extensionsByNs[$name]; + } + + throw new LogicException(sprintf('Container extension "%s" is not registered', $name)); + } + + /** + * Returns all registered extensions. + * + * @return ExtensionInterface[] An array of ExtensionInterface + */ + public function getExtensions() + { + return $this->extensions; + } + + /** + * Checks if we have an extension. + * + * @param string $name The name of the extension + * + * @return bool If the extension exists + */ + public function hasExtension($name) + { + return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]); + } + + /** + * Returns an array of resources loaded to build this configuration. + * + * @return ResourceInterface[] An array of resources + */ + public function getResources() + { + return array_unique($this->resources); + } + + /** + * Adds a resource for this configuration. + * + * @param ResourceInterface $resource A resource instance + * + * @return ContainerBuilder The current instance + */ + public function addResource(ResourceInterface $resource) + { + if (!$this->trackResources) { + return $this; + } + + $this->resources[] = $resource; + + return $this; + } + + /** + * Sets the resources for this configuration. + * + * @param ResourceInterface[] $resources An array of resources + * + * @return ContainerBuilder The current instance + */ + public function setResources(array $resources) + { + if (!$this->trackResources) { + return $this; + } + + $this->resources = $resources; + + return $this; + } + + /** + * Adds the object class hierarchy as resources. + * + * @param object $object An object instance + * + * @return ContainerBuilder The current instance + */ + public function addObjectResource($object) + { + if ($this->trackResources) { + $this->addClassResource(new \ReflectionClass($object)); + } + + return $this; + } + + /** + * Adds the given class hierarchy as resources. + * + * @param \ReflectionClass $class + * + * @return ContainerBuilder The current instance + */ + public function addClassResource(\ReflectionClass $class) + { + if (!$this->trackResources) { + return $this; + } + + do { + $this->addResource(new FileResource($class->getFileName())); + } while ($class = $class->getParentClass()); + + return $this; + } + + /** + * Loads the configuration for an extension. + * + * @param string $extension The extension alias or namespace + * @param array $values An array of values that customizes the extension + * + * @return ContainerBuilder The current instance + * + * @throws BadMethodCallException When this ContainerBuilder is frozen + * @throws \LogicException if the container is frozen + */ + public function loadFromExtension($extension, array $values = array()) + { + if ($this->isFrozen()) { + throw new BadMethodCallException('Cannot load from an extension on a frozen container.'); + } + + $namespace = $this->getExtension($extension)->getAlias(); + + $this->extensionConfigs[$namespace][] = $values; + + return $this; + } + + /** + * Adds a compiler pass. + * + * @param CompilerPassInterface $pass A compiler pass + * @param string $type The type of compiler pass + * + * @return ContainerBuilder The current instance + */ + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION) + { + $this->getCompiler()->addPass($pass, $type); + + $this->addObjectResource($pass); + + return $this; + } + + /** + * Returns the compiler pass config which can then be modified. + * + * @return PassConfig The compiler pass config + */ + public function getCompilerPassConfig() + { + return $this->getCompiler()->getPassConfig(); + } + + /** + * Returns the compiler. + * + * @return Compiler The compiler + */ + public function getCompiler() + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + return $this->compiler; + } + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + * + * @throws BadMethodCallException When this ContainerBuilder is frozen + */ + public function set($id, $service) + { + $id = strtolower($id); + + if ($this->isFrozen()) { + // setting a synthetic service on a frozen container is alright + if ( + (!isset($this->definitions[$id]) && !isset($this->obsoleteDefinitions[$id])) + || + (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic()) + || + (isset($this->obsoleteDefinitions[$id]) && !$this->obsoleteDefinitions[$id]->isSynthetic()) + ) { + throw new BadMethodCallException(sprintf('Setting service "%s" on a frozen container is not allowed.', $id)); + } + } + + if (isset($this->definitions[$id])) { + $this->obsoleteDefinitions[$id] = $this->definitions[$id]; + } + + unset($this->definitions[$id], $this->aliasDefinitions[$id]); + + parent::set($id, $service); + } + + /** + * Removes a service definition. + * + * @param string $id The service identifier + */ + public function removeDefinition($id) + { + unset($this->definitions[strtolower($id)]); + } + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return bool true if the service is defined, false otherwise + */ + public function has($id) + { + $id = strtolower($id); + + return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); + } + + /** + * Gets a service. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws InvalidArgumentException when no definitions are available + * @throws LogicException when a circular dependency is detected + * @throws \Exception + * + * @see Reference + */ + public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + { + $id = strtolower($id); + + if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { + return $service; + } + + if (!array_key_exists($id, $this->definitions) && isset($this->aliasDefinitions[$id])) { + return $this->get($this->aliasDefinitions[$id]); + } + + try { + $definition = $this->getDefinition($id); + } catch (InvalidArgumentException $e) { + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + return; + } + + throw $e; + } + + $this->loading[$id] = true; + + try { + $service = $this->createService($definition, $id); + } finally { + unset($this->loading[$id]); + } + + return $service; + } + + /** + * Merges a ContainerBuilder with the current ContainerBuilder configuration. + * + * Service definitions overrides the current defined ones. + * + * But for parameters, they are overridden by the current ones. It allows + * the parameters passed to the container constructor to have precedence + * over the loaded ones. + * + * $container = new ContainerBuilder(array('foo' => 'bar')); + * $loader = new LoaderXXX($container); + * $loader->load('resource_name'); + * $container->register('foo', new stdClass()); + * + * In the above example, even if the loaded resource defines a foo + * parameter, the value will still be 'bar' as defined in the ContainerBuilder + * constructor. + * + * @param ContainerBuilder $container The ContainerBuilder instance to merge. + * + * @throws BadMethodCallException When this ContainerBuilder is frozen + */ + public function merge(ContainerBuilder $container) + { + if ($this->isFrozen()) { + throw new BadMethodCallException('Cannot merge on a frozen container.'); + } + + $this->addDefinitions($container->getDefinitions()); + $this->addAliases($container->getAliases()); + $this->getParameterBag()->add($container->getParameterBag()->all()); + + if ($this->trackResources) { + foreach ($container->getResources() as $resource) { + $this->addResource($resource); + } + } + + foreach ($this->extensions as $name => $extension) { + if (!isset($this->extensionConfigs[$name])) { + $this->extensionConfigs[$name] = array(); + } + + $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name)); + } + } + + /** + * Returns the configuration array for the given extension. + * + * @param string $name The name of the extension + * + * @return array An array of configuration + */ + public function getExtensionConfig($name) + { + if (!isset($this->extensionConfigs[$name])) { + $this->extensionConfigs[$name] = array(); + } + + return $this->extensionConfigs[$name]; + } + + /** + * Prepends a config array to the configs of the given extension. + * + * @param string $name The name of the extension + * @param array $config The config to set + */ + public function prependExtensionConfig($name, array $config) + { + if (!isset($this->extensionConfigs[$name])) { + $this->extensionConfigs[$name] = array(); + } + + array_unshift($this->extensionConfigs[$name], $config); + } + + /** + * Compiles the container. + * + * This method passes the container to compiler + * passes whose job is to manipulate and optimize + * the container. + * + * The main compiler passes roughly do four things: + * + * * The extension configurations are merged; + * * Parameter values are resolved; + * * The parameter bag is frozen; + * * Extension loading is disabled. + */ + public function compile() + { + $compiler = $this->getCompiler(); + + if ($this->trackResources) { + foreach ($compiler->getPassConfig()->getPasses() as $pass) { + $this->addObjectResource($pass); + } + } + + $compiler->compile($this); + + if ($this->trackResources) { + foreach ($this->definitions as $definition) { + if ($definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) { + $this->addClassResource(new \ReflectionClass($class)); + } + } + } + + $this->extensionConfigs = array(); + + parent::compile(); + } + + /** + * Gets all service ids. + * + * @return array An array of all defined service ids + */ + public function getServiceIds() + { + return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())); + } + + /** + * Adds the service aliases. + * + * @param array $aliases An array of aliases + */ + public function addAliases(array $aliases) + { + foreach ($aliases as $alias => $id) { + $this->setAlias($alias, $id); + } + } + + /** + * Sets the service aliases. + * + * @param array $aliases An array of aliases + */ + public function setAliases(array $aliases) + { + $this->aliasDefinitions = array(); + $this->addAliases($aliases); + } + + /** + * Sets an alias for an existing service. + * + * @param string $alias The alias to create + * @param string|Alias $id The service to alias + * + * @throws InvalidArgumentException if the id is not a string or an Alias + * @throws InvalidArgumentException if the alias is for itself + */ + public function setAlias($alias, $id) + { + $alias = strtolower($alias); + + if (is_string($id)) { + $id = new Alias($id); + } elseif (!$id instanceof Alias) { + throw new InvalidArgumentException('$id must be a string, or an Alias object.'); + } + + if ($alias === (string) $id) { + throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias)); + } + + unset($this->definitions[$alias]); + + $this->aliasDefinitions[$alias] = $id; + } + + /** + * Removes an alias. + * + * @param string $alias The alias to remove + */ + public function removeAlias($alias) + { + unset($this->aliasDefinitions[strtolower($alias)]); + } + + /** + * Returns true if an alias exists under the given identifier. + * + * @param string $id The service identifier + * + * @return bool true if the alias exists, false otherwise + */ + public function hasAlias($id) + { + return isset($this->aliasDefinitions[strtolower($id)]); + } + + /** + * Gets all defined aliases. + * + * @return Alias[] An array of aliases + */ + public function getAliases() + { + return $this->aliasDefinitions; + } + + /** + * Gets an alias. + * + * @param string $id The service identifier + * + * @return Alias An Alias instance + * + * @throws InvalidArgumentException if the alias does not exist + */ + public function getAlias($id) + { + $id = strtolower($id); + + if (!isset($this->aliasDefinitions[$id])) { + throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); + } + + return $this->aliasDefinitions[$id]; + } + + /** + * Registers a service definition. + * + * This methods allows for simple registration of service definition + * with a fluid interface. + * + * @param string $id The service identifier + * @param string $class The service class + * + * @return Definition A Definition instance + */ + public function register($id, $class = null) + { + return $this->setDefinition($id, new Definition($class)); + } + + /** + * Adds the service definitions. + * + * @param Definition[] $definitions An array of service definitions + */ + public function addDefinitions(array $definitions) + { + foreach ($definitions as $id => $definition) { + $this->setDefinition($id, $definition); + } + } + + /** + * Sets the service definitions. + * + * @param Definition[] $definitions An array of service definitions + */ + public function setDefinitions(array $definitions) + { + $this->definitions = array(); + $this->addDefinitions($definitions); + } + + /** + * Gets all service definitions. + * + * @return Definition[] An array of Definition instances + */ + public function getDefinitions() + { + return $this->definitions; + } + + /** + * Sets a service definition. + * + * @param string $id The service identifier + * @param Definition $definition A Definition instance + * + * @return Definition the service definition + * + * @throws BadMethodCallException When this ContainerBuilder is frozen + */ + public function setDefinition($id, Definition $definition) + { + if ($this->isFrozen()) { + throw new BadMethodCallException('Adding definition to a frozen container is not allowed'); + } + + $id = strtolower($id); + + unset($this->aliasDefinitions[$id]); + + return $this->definitions[$id] = $definition; + } + + /** + * Returns true if a service definition exists under the given identifier. + * + * @param string $id The service identifier + * + * @return bool true if the service definition exists, false otherwise + */ + public function hasDefinition($id) + { + return array_key_exists(strtolower($id), $this->definitions); + } + + /** + * Gets a service definition. + * + * @param string $id The service identifier + * + * @return Definition A Definition instance + * + * @throws InvalidArgumentException if the service definition does not exist + */ + public function getDefinition($id) + { + $id = strtolower($id); + + if (!array_key_exists($id, $this->definitions)) { + throw new InvalidArgumentException(sprintf('The service definition "%s" does not exist.', $id)); + } + + return $this->definitions[$id]; + } + + /** + * Gets a service definition by id or alias. + * + * The method "unaliases" recursively to return a Definition instance. + * + * @param string $id The service identifier or alias + * + * @return Definition A Definition instance + * + * @throws InvalidArgumentException if the service definition does not exist + */ + public function findDefinition($id) + { + $id = strtolower($id); + + while (isset($this->aliasDefinitions[$id])) { + $id = (string) $this->aliasDefinitions[$id]; + } + + return $this->getDefinition($id); + } + + /** + * Creates a service for a service definition. + * + * @param Definition $definition A service definition instance + * @param string $id The service identifier + * @param bool $tryProxy Whether to try proxying the service with a lazy proxy + * + * @return object The service described by the service definition + * + * @throws RuntimeException When the factory definition is incomplete + * @throws RuntimeException When the service is a synthetic service + * @throws InvalidArgumentException When configure callable is not callable + */ + private function createService(Definition $definition, $id, $tryProxy = true) + { + if ($definition->isSynthetic()) { + throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); + } + + if ($definition->isDeprecated()) { + @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); + } + + if ($tryProxy && $definition->isLazy()) { + $proxy = $this + ->getProxyInstantiator() + ->instantiateProxy( + $this, + $definition, + $id, function () use ($definition, $id) { + return $this->createService($definition, $id, false); + } + ); + $this->shareService($definition, $proxy, $id); + + return $proxy; + } + + $parameterBag = $this->getParameterBag(); + + if (null !== $definition->getFile()) { + require_once $parameterBag->resolveValue($definition->getFile()); + } + + $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments()))); + + if (null !== $factory = $definition->getFactory()) { + if (is_array($factory)) { + $factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]); + } elseif (!is_string($factory)) { + throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); + } + + $service = call_user_func_array($factory, $arguments); + + if (!$definition->isDeprecated() && is_array($factory) && is_string($factory[0])) { + $r = new \ReflectionClass($factory[0]); + + if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) { + @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED); + } + } + } else { + $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass())); + + $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); + + if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { + @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED); + } + } + + if ($tryProxy || !$definition->isLazy()) { + // share only if proxying failed, or if not a proxy + $this->shareService($definition, $service, $id); + } + + foreach ($definition->getMethodCalls() as $call) { + $this->callMethod($service, $call); + } + + $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties()))); + foreach ($properties as $name => $value) { + $service->$name = $value; + } + + if ($callable = $definition->getConfigurator()) { + if (is_array($callable)) { + $callable[0] = $parameterBag->resolveValue($callable[0]); + + if ($callable[0] instanceof Reference) { + $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior()); + } elseif ($callable[0] instanceof Definition) { + $callable[0] = $this->createService($callable[0], null); + } + } + + if (!is_callable($callable)) { + throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service))); + } + + call_user_func($callable, $service); + } + + return $service; + } + + /** + * Replaces service references by the real service instance and evaluates expressions. + * + * @param mixed $value A value + * + * @return mixed The same value with all service references replaced by + * the real service instances and all expressions evaluated + */ + public function resolveServices($value) + { + if (is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = $this->resolveServices($v); + } + } elseif ($value instanceof Reference) { + $value = $this->get((string) $value, $value->getInvalidBehavior()); + } elseif ($value instanceof Definition) { + $value = $this->createService($value, null); + } elseif ($value instanceof Expression) { + $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this)); + } + + return $value; + } + + /** + * Returns service ids for a given tag. + * + * Example: + * + * $container->register('foo')->addTag('my.tag', array('hello' => 'world')); + * + * $serviceIds = $container->findTaggedServiceIds('my.tag'); + * foreach ($serviceIds as $serviceId => $tags) { + * foreach ($tags as $tag) { + * echo $tag['hello']; + * } + * } + * + * @param string $name The tag name + * + * @return array An array of tags with the tagged service as key, holding a list of attribute arrays. + */ + public function findTaggedServiceIds($name) + { + $this->usedTags[] = $name; + $tags = array(); + foreach ($this->getDefinitions() as $id => $definition) { + if ($definition->hasTag($name)) { + $tags[$id] = $definition->getTag($name); + } + } + + return $tags; + } + + /** + * Returns all tags the defined services use. + * + * @return array An array of tags + */ + public function findTags() + { + $tags = array(); + foreach ($this->getDefinitions() as $id => $definition) { + $tags = array_merge(array_keys($definition->getTags()), $tags); + } + + return array_unique($tags); + } + + /** + * Returns all tags not queried by findTaggedServiceIds. + * + * @return string[] An array of tags + */ + public function findUnusedTags() + { + return array_values(array_diff($this->findTags(), $this->usedTags)); + } + + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguageProviders[] = $provider; + } + + /** + * @return ExpressionFunctionProviderInterface[] + */ + public function getExpressionLanguageProviders() + { + return $this->expressionLanguageProviders; + } + + /** + * Returns the Service Conditionals. + * + * @param mixed $value An array of conditionals to return. + * + * @return array An array of Service conditionals + */ + public static function getServiceConditionals($value) + { + $services = array(); + + if (is_array($value)) { + foreach ($value as $v) { + $services = array_unique(array_merge($services, self::getServiceConditionals($v))); + } + } elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { + $services[] = (string) $value; + } + + return $services; + } + + /** + * Retrieves the currently set proxy instantiator or instantiates one. + * + * @return InstantiatorInterface + */ + private function getProxyInstantiator() + { + if (!$this->proxyInstantiator) { + $this->proxyInstantiator = new RealServiceInstantiator(); + } + + return $this->proxyInstantiator; + } + + private function callMethod($service, $call) + { + $services = self::getServiceConditionals($call[1]); + + foreach ($services as $s) { + if (!$this->has($s)) { + return; + } + } + + call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])))); + } + + /** + * Shares a given service in the container. + * + * @param Definition $definition + * @param mixed $service + * @param string $id + */ + private function shareService(Definition $definition, $service, $id) + { + if ($definition->isShared()) { + $this->services[$lowerId = strtolower($id)] = $service; + } + } + + private function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); + } + + return $this->expressionLanguage; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerInterface.php new file mode 100644 index 0000000..7e2fbb1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ContainerInterface.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +/** + * ContainerInterface is the interface implemented by service container classes. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +interface ContainerInterface +{ + const EXCEPTION_ON_INVALID_REFERENCE = 1; + const NULL_ON_INVALID_REFERENCE = 2; + const IGNORE_ON_INVALID_REFERENCE = 3; + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + */ + public function set($id, $service); + + /** + * Gets a service. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * + * @see Reference + */ + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return bool true if the service is defined, false otherwise + */ + public function has($id); + + /** + * Check for whether or not a service has been initialized. + * + * @param string $id + * + * @return bool true if the service has been initialized, false otherwise + */ + public function initialized($id); + + /** + * Gets a parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws InvalidArgumentException if the parameter is not defined + */ + public function getParameter($name); + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return bool The presence of parameter in container + */ + public function hasParameter($name); + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + */ + public function setParameter($name, $value); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Definition.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Definition.php new file mode 100644 index 0000000..98d0277 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Definition.php @@ -0,0 +1,729 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +/** + * Definition represents a service definition. + * + * @author Fabien Potencier + */ +class Definition +{ + private $class; + private $file; + private $factory; + private $shared = true; + private $deprecated = false; + private $deprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.'; + private $properties = array(); + private $calls = array(); + private $configurator; + private $tags = array(); + private $public = true; + private $synthetic = false; + private $abstract = false; + private $lazy = false; + private $decoratedService; + private $autowired = false; + private $autowiringTypes = array(); + + protected $arguments; + + /** + * Constructor. + * + * @param string|null $class The service class + * @param array $arguments An array of arguments to pass to the service constructor + */ + public function __construct($class = null, array $arguments = array()) + { + $this->class = $class; + $this->arguments = $arguments; + } + + /** + * Sets a factory. + * + * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call + * + * @return Definition The current instance + */ + public function setFactory($factory) + { + if (is_string($factory) && strpos($factory, '::') !== false) { + $factory = explode('::', $factory, 2); + } + + $this->factory = $factory; + + return $this; + } + + /** + * Gets the factory. + * + * @return string|array The PHP function or an array containing a class/Reference and a method to call + */ + public function getFactory() + { + return $this->factory; + } + + /** + * Sets the service that this service is decorating. + * + * @param null|string $id The decorated service id, use null to remove decoration + * @param null|string $renamedId The new decorated service id + * @param int $priority The priority of decoration + * + * @return Definition The current instance + * + * @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals. + */ + public function setDecoratedService($id, $renamedId = null, $priority = 0) + { + if ($renamedId && $id == $renamedId) { + throw new \InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id)); + } + + if (null === $id) { + $this->decoratedService = null; + } else { + $this->decoratedService = array($id, $renamedId, (int) $priority); + } + + return $this; + } + + /** + * Gets the service that decorates this service. + * + * @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated + */ + public function getDecoratedService() + { + return $this->decoratedService; + } + + /** + * Sets the service class. + * + * @param string $class The service class + * + * @return Definition The current instance + */ + public function setClass($class) + { + $this->class = $class; + + return $this; + } + + /** + * Gets the service class. + * + * @return string|null The service class + */ + public function getClass() + { + return $this->class; + } + + /** + * Sets the arguments to pass to the service constructor/factory method. + * + * @param array $arguments An array of arguments + * + * @return Definition The current instance + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + + return $this; + } + + public function setProperties(array $properties) + { + $this->properties = $properties; + + return $this; + } + + public function getProperties() + { + return $this->properties; + } + + public function setProperty($name, $value) + { + $this->properties[$name] = $value; + + return $this; + } + + /** + * Adds an argument to pass to the service constructor/factory method. + * + * @param mixed $argument An argument + * + * @return Definition The current instance + */ + public function addArgument($argument) + { + $this->arguments[] = $argument; + + return $this; + } + + /** + * Sets a specific argument. + * + * @param int $index + * @param mixed $argument + * + * @return Definition The current instance + * + * @throws OutOfBoundsException When the replaced argument does not exist + */ + public function replaceArgument($index, $argument) + { + if ($index < 0 || $index > count($this->arguments) - 1) { + throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1)); + } + + $this->arguments[$index] = $argument; + + return $this; + } + + /** + * Gets the arguments to pass to the service constructor/factory method. + * + * @return array The array of arguments + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Gets an argument to pass to the service constructor/factory method. + * + * @param int $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + */ + public function getArgument($index) + { + if ($index < 0 || $index > count($this->arguments) - 1) { + throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1)); + } + + return $this->arguments[$index]; + } + + /** + * Sets the methods to call after service initialization. + * + * @param array $calls An array of method calls + * + * @return Definition The current instance + */ + public function setMethodCalls(array $calls = array()) + { + $this->calls = array(); + foreach ($calls as $call) { + $this->addMethodCall($call[0], $call[1]); + } + + return $this; + } + + /** + * Adds a method to call after service initialization. + * + * @param string $method The method name to call + * @param array $arguments An array of arguments to pass to the method call + * + * @return Definition The current instance + * + * @throws InvalidArgumentException on empty $method param + */ + public function addMethodCall($method, array $arguments = array()) + { + if (empty($method)) { + throw new InvalidArgumentException(sprintf('Method name cannot be empty.')); + } + $this->calls[] = array($method, $arguments); + + return $this; + } + + /** + * Removes a method to call after service initialization. + * + * @param string $method The method name to remove + * + * @return Definition The current instance + */ + public function removeMethodCall($method) + { + foreach ($this->calls as $i => $call) { + if ($call[0] === $method) { + unset($this->calls[$i]); + break; + } + } + + return $this; + } + + /** + * Check if the current definition has a given method to call after service initialization. + * + * @param string $method The method name to search for + * + * @return bool + */ + public function hasMethodCall($method) + { + foreach ($this->calls as $call) { + if ($call[0] === $method) { + return true; + } + } + + return false; + } + + /** + * Gets the methods to call after service initialization. + * + * @return array An array of method calls + */ + public function getMethodCalls() + { + return $this->calls; + } + + /** + * Sets tags for this definition. + * + * @param array $tags + * + * @return Definition the current instance + */ + public function setTags(array $tags) + { + $this->tags = $tags; + + return $this; + } + + /** + * Returns all tags. + * + * @return array An array of tags + */ + public function getTags() + { + return $this->tags; + } + + /** + * Gets a tag by name. + * + * @param string $name The tag name + * + * @return array An array of attributes + */ + public function getTag($name) + { + return isset($this->tags[$name]) ? $this->tags[$name] : array(); + } + + /** + * Adds a tag for this definition. + * + * @param string $name The tag name + * @param array $attributes An array of attributes + * + * @return Definition The current instance + */ + public function addTag($name, array $attributes = array()) + { + $this->tags[$name][] = $attributes; + + return $this; + } + + /** + * Whether this definition has a tag with the given name. + * + * @param string $name + * + * @return bool + */ + public function hasTag($name) + { + return isset($this->tags[$name]); + } + + /** + * Clears all tags for a given name. + * + * @param string $name The tag name + * + * @return Definition + */ + public function clearTag($name) + { + unset($this->tags[$name]); + + return $this; + } + + /** + * Clears the tags for this definition. + * + * @return Definition The current instance + */ + public function clearTags() + { + $this->tags = array(); + + return $this; + } + + /** + * Sets a file to require before creating the service. + * + * @param string $file A full pathname to include + * + * @return Definition The current instance + */ + public function setFile($file) + { + $this->file = $file; + + return $this; + } + + /** + * Gets the file to require before creating the service. + * + * @return string|null The full pathname to include + */ + public function getFile() + { + return $this->file; + } + + /** + * Sets if the service must be shared or not. + * + * @param bool $shared Whether the service must be shared or not + * + * @return Definition The current instance + */ + public function setShared($shared) + { + $this->shared = (bool) $shared; + + return $this; + } + + /** + * Whether this service is shared. + * + * @return bool + */ + public function isShared() + { + return $this->shared; + } + + /** + * Sets the visibility of this service. + * + * @param bool $boolean + * + * @return Definition The current instance + */ + public function setPublic($boolean) + { + $this->public = (bool) $boolean; + + return $this; + } + + /** + * Whether this service is public facing. + * + * @return bool + */ + public function isPublic() + { + return $this->public; + } + + /** + * Sets the lazy flag of this service. + * + * @param bool $lazy + * + * @return Definition The current instance + */ + public function setLazy($lazy) + { + $this->lazy = (bool) $lazy; + + return $this; + } + + /** + * Whether this service is lazy. + * + * @return bool + */ + public function isLazy() + { + return $this->lazy; + } + + /** + * Sets whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @param bool $boolean + * + * @return Definition the current instance + */ + public function setSynthetic($boolean) + { + $this->synthetic = (bool) $boolean; + + return $this; + } + + /** + * Whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @return bool + */ + public function isSynthetic() + { + return $this->synthetic; + } + + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @param bool $boolean + * + * @return Definition the current instance + */ + public function setAbstract($boolean) + { + $this->abstract = (bool) $boolean; + + return $this; + } + + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @return bool + */ + public function isAbstract() + { + return $this->abstract; + } + + /** + * Whether this definition is deprecated, that means it should not be called + * anymore. + * + * @param bool $status + * @param string $template Template message to use if the definition is deprecated + * + * @return Definition the current instance + * + * @throws InvalidArgumentException When the message template is invalid. + */ + public function setDeprecated($status = true, $template = null) + { + if (null !== $template) { + if (preg_match('#[\r\n]|\*/#', $template)) { + throw new InvalidArgumentException('Invalid characters found in deprecation template.'); + } + + if (false === strpos($template, '%service_id%')) { + throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.'); + } + + $this->deprecationTemplate = $template; + } + + $this->deprecated = (bool) $status; + + return $this; + } + + /** + * Whether this definition is deprecated, that means it should not be called + * anymore. + * + * @return bool + */ + public function isDeprecated() + { + return $this->deprecated; + } + + /** + * Message to use if this definition is deprecated. + * + * @param string $id Service id relying on this definition + * + * @return string + */ + public function getDeprecationMessage($id) + { + return str_replace('%service_id%', $id, $this->deprecationTemplate); + } + + /** + * Sets a configurator to call after the service is fully initialized. + * + * @param callable $callable A PHP callable + * + * @return Definition The current instance + */ + public function setConfigurator($callable) + { + $this->configurator = $callable; + + return $this; + } + + /** + * Gets the configurator to call after the service is fully initialized. + * + * @return callable|null The PHP callable to call + */ + public function getConfigurator() + { + return $this->configurator; + } + + /** + * Sets types that will default to this definition. + * + * @param string[] $types + * + * @return Definition The current instance + */ + public function setAutowiringTypes(array $types) + { + $this->autowiringTypes = array(); + + foreach ($types as $type) { + $this->autowiringTypes[$type] = true; + } + + return $this; + } + + /** + * Is the definition autowired? + * + * @return bool + */ + public function isAutowired() + { + return $this->autowired; + } + + /** + * Sets autowired. + * + * @param $autowired + * + * @return Definition The current instance + */ + public function setAutowired($autowired) + { + $this->autowired = $autowired; + + return $this; + } + + /** + * Gets autowiring types that will default to this definition. + * + * @return string[] + */ + public function getAutowiringTypes() + { + return array_keys($this->autowiringTypes); + } + + /** + * Adds a type that will default to this definition. + * + * @param string $type + * + * @return Definition The current instance + */ + public function addAutowiringType($type) + { + $this->autowiringTypes[$type] = true; + + return $this; + } + + /** + * Removes a type. + * + * @param string $type + * + * @return Definition The current instance + */ + public function removeAutowiringType($type) + { + unset($this->autowiringTypes[$type]); + + return $this; + } + + /** + * Will this definition default for the given type? + * + * @param string $type + * + * @return bool + */ + public function hasAutowiringType($type) + { + return isset($this->autowiringTypes[$type]); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php new file mode 100644 index 0000000..1ea15fb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/DefinitionDecorator.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +/** + * This definition decorates another definition. + * + * @author Johannes M. Schmitt + */ +class DefinitionDecorator extends Definition +{ + private $parent; + private $changes = array(); + + /** + * Constructor. + * + * @param string $parent The id of Definition instance to decorate. + */ + public function __construct($parent) + { + parent::__construct(); + + $this->parent = $parent; + } + + /** + * Returns the Definition being decorated. + * + * @return string + */ + public function getParent() + { + return $this->parent; + } + + /** + * Returns all changes tracked for the Definition object. + * + * @return array An array of changes for this Definition + */ + public function getChanges() + { + return $this->changes; + } + + /** + * {@inheritdoc} + */ + public function setClass($class) + { + $this->changes['class'] = true; + + return parent::setClass($class); + } + + /** + * {@inheritdoc} + */ + public function setFactory($callable) + { + $this->changes['factory'] = true; + + return parent::setFactory($callable); + } + + /** + * {@inheritdoc} + */ + public function setConfigurator($callable) + { + $this->changes['configurator'] = true; + + return parent::setConfigurator($callable); + } + + /** + * {@inheritdoc} + */ + public function setFile($file) + { + $this->changes['file'] = true; + + return parent::setFile($file); + } + + /** + * {@inheritdoc} + */ + public function setPublic($boolean) + { + $this->changes['public'] = true; + + return parent::setPublic($boolean); + } + + /** + * {@inheritdoc} + */ + public function setLazy($boolean) + { + $this->changes['lazy'] = true; + + return parent::setLazy($boolean); + } + + /** + * {@inheritdoc} + */ + public function setDecoratedService($id, $renamedId = null, $priority = 0) + { + $this->changes['decorated_service'] = true; + + return parent::setDecoratedService($id, $renamedId, $priority); + } + + /** + * {@inheritdoc} + */ + public function setDeprecated($boolean = true, $template = null) + { + $this->changes['deprecated'] = true; + + return parent::setDeprecated($boolean, $template); + } + + /** + * Gets an argument to pass to the service constructor/factory method. + * + * If replaceArgument() has been used to replace an argument, this method + * will return the replacement value. + * + * @param int $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + */ + public function getArgument($index) + { + if (array_key_exists('index_'.$index, $this->arguments)) { + return $this->arguments['index_'.$index]; + } + + $lastIndex = count(array_filter(array_keys($this->arguments), 'is_int')) - 1; + + if ($index < 0 || $index > $lastIndex) { + throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, $lastIndex)); + } + + return $this->arguments[$index]; + } + + /** + * You should always use this method when overwriting existing arguments + * of the parent definition. + * + * If you directly call setArguments() keep in mind that you must follow + * certain conventions when you want to overwrite the arguments of the + * parent definition, otherwise your arguments will only be appended. + * + * @param int $index + * @param mixed $value + * + * @return DefinitionDecorator the current instance + * + * @throws InvalidArgumentException when $index isn't an integer + */ + public function replaceArgument($index, $value) + { + if (!is_int($index)) { + throw new InvalidArgumentException('$index must be an integer.'); + } + + $this->arguments['index_'.$index] = $value; + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php new file mode 100644 index 0000000..4b9d586 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/Dumper.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Dumper is the abstract class for all built-in dumpers. + * + * @author Fabien Potencier + */ +abstract class Dumper implements DumperInterface +{ + protected $container; + + /** + * Constructor. + * + * @param ContainerBuilder $container The service container to dump + */ + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/DumperInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/DumperInterface.php new file mode 100644 index 0000000..dd001e4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/DumperInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +/** + * DumperInterface is the interface implemented by service container dumper classes. + * + * @author Fabien Potencier + */ +interface DumperInterface +{ + /** + * Dumps the service container. + * + * @param array $options An array of options + * + * @return string The representation of the service container + */ + public function dump(array $options = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php new file mode 100644 index 0000000..bf950db --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/GraphvizDumper.php @@ -0,0 +1,302 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +/** + * GraphvizDumper dumps a service container as a graphviz file. + * + * You can convert the generated dot file with the dot utility (http://www.graphviz.org/): + * + * dot -Tpng container.dot > foo.png + * + * @author Fabien Potencier + */ +class GraphvizDumper extends Dumper +{ + private $nodes; + private $edges; + private $options = array( + 'graph' => array('ratio' => 'compress'), + 'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'), + 'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5), + 'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'), + 'node.definition' => array('fillcolor' => '#eeeeee'), + 'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'), + ); + + /** + * Dumps the service container as a graphviz graph. + * + * Available options: + * + * * graph: The default options for the whole graph + * * node: The default options for nodes + * * edge: The default options for edges + * * node.instance: The default options for services that are defined directly by object instances + * * node.definition: The default options for services that are defined via service definition instances + * * node.missing: The default options for missing services + * + * @param array $options An array of options + * + * @return string The dot representation of the service container + */ + public function dump(array $options = array()) + { + foreach (array('graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing') as $key) { + if (isset($options[$key])) { + $this->options[$key] = array_merge($this->options[$key], $options[$key]); + } + } + + $this->nodes = $this->findNodes(); + + $this->edges = array(); + foreach ($this->container->getDefinitions() as $id => $definition) { + $this->edges[$id] = array_merge( + $this->findEdges($id, $definition->getArguments(), true, ''), + $this->findEdges($id, $definition->getProperties(), false, '') + ); + + foreach ($definition->getMethodCalls() as $call) { + $this->edges[$id] = array_merge( + $this->edges[$id], + $this->findEdges($id, $call[1], false, $call[0].'()') + ); + } + } + + return $this->startDot().$this->addNodes().$this->addEdges().$this->endDot(); + } + + /** + * Returns all nodes. + * + * @return string A string representation of all nodes + */ + private function addNodes() + { + $code = ''; + foreach ($this->nodes as $id => $node) { + $aliases = $this->getAliases($id); + + $code .= sprintf(" node_%s [label=\"%s\\n%s\\n\", shape=%s%s];\n", $this->dotize($id), $id.($aliases ? ' ('.implode(', ', $aliases).')' : ''), $node['class'], $this->options['node']['shape'], $this->addAttributes($node['attributes'])); + } + + return $code; + } + + /** + * Returns all edges. + * + * @return string A string representation of all edges + */ + private function addEdges() + { + $code = ''; + foreach ($this->edges as $id => $edges) { + foreach ($edges as $edge) { + $code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed'); + } + } + + return $code; + } + + /** + * Finds all edges belonging to a specific service id. + * + * @param string $id The service id used to find edges + * @param array $arguments An array of arguments + * @param bool $required + * @param string $name + * + * @return array An array of edges + */ + private function findEdges($id, $arguments, $required, $name) + { + $edges = array(); + foreach ($arguments as $argument) { + if ($argument instanceof Parameter) { + $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null; + } elseif (is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) { + $argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null; + } + + if ($argument instanceof Reference) { + if (!$this->container->has((string) $argument)) { + $this->nodes[(string) $argument] = array('name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']); + } + + $edges[] = array('name' => $name, 'required' => $required, 'to' => $argument); + } elseif (is_array($argument)) { + $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name)); + } + } + + return $edges; + } + + /** + * Finds all nodes. + * + * @return array An array of all nodes + */ + private function findNodes() + { + $nodes = array(); + + $container = $this->cloneContainer(); + + foreach ($container->getDefinitions() as $id => $definition) { + $class = $definition->getClass(); + + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + try { + $class = $this->container->getParameterBag()->resolveValue($class); + } catch (ParameterNotFoundException $e) { + } + + $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() ? 'filled' : 'dotted'))); + $container->setDefinition($id, new Definition('stdClass')); + } + + foreach ($container->getServiceIds() as $id) { + $service = $container->get($id); + + if (array_key_exists($id, $container->getAliases())) { + continue; + } + + if (!$container->hasDefinition($id)) { + $class = ('service_container' === $id) ? get_class($this->container) : get_class($service); + $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => $this->options['node.instance']); + } + } + + return $nodes; + } + + private function cloneContainer() + { + $parameterBag = new ParameterBag($this->container->getParameterBag()->all()); + + $container = new ContainerBuilder($parameterBag); + $container->setDefinitions($this->container->getDefinitions()); + $container->setAliases($this->container->getAliases()); + $container->setResources($this->container->getResources()); + foreach ($this->container->getExtensions() as $extension) { + $container->registerExtension($extension); + } + + return $container; + } + + /** + * Returns the start dot. + * + * @return string The string representation of a start dot + */ + private function startDot() + { + return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n", + $this->addOptions($this->options['graph']), + $this->addOptions($this->options['node']), + $this->addOptions($this->options['edge']) + ); + } + + /** + * Returns the end dot. + * + * @return string + */ + private function endDot() + { + return "}\n"; + } + + /** + * Adds attributes. + * + * @param array $attributes An array of attributes + * + * @return string A comma separated list of attributes + */ + private function addAttributes($attributes) + { + $code = array(); + foreach ($attributes as $k => $v) { + $code[] = sprintf('%s="%s"', $k, $v); + } + + return $code ? ', '.implode(', ', $code) : ''; + } + + /** + * Adds options. + * + * @param array $options An array of options + * + * @return string A space separated list of options + */ + private function addOptions($options) + { + $code = array(); + foreach ($options as $k => $v) { + $code[] = sprintf('%s="%s"', $k, $v); + } + + return implode(' ', $code); + } + + /** + * Dotizes an identifier. + * + * @param string $id The identifier to dotize + * + * @return string A dotized string + */ + private function dotize($id) + { + return strtolower(preg_replace('/\W/i', '_', $id)); + } + + /** + * Compiles an array of aliases for a specified service id. + * + * @param string $id A service id + * + * @return array An array of aliases + */ + private function getAliases($id) + { + $aliases = array(); + foreach ($this->container->getAliases() as $alias => $origin) { + if ($id == $origin) { + $aliases[] = $alias; + } + } + + return $aliases; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php new file mode 100644 index 0000000..ffe8ec0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -0,0 +1,1438 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; +use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * PhpDumper dumps a service container as a PHP class. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class PhpDumper extends Dumper +{ + /** + * Characters that might appear in the generated variable name as first character. + * + * @var string + */ + const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; + + /** + * Characters that might appear in the generated variable name as any but the first character. + * + * @var string + */ + const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; + + private $inlinedDefinitions; + private $definitionVariables; + private $referenceVariables; + private $variableCount; + private $reservedVariables = array('instance', 'class'); + private $expressionLanguage; + private $targetDirRegex; + private $targetDirMaxMatches; + + /** + * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface + */ + private $proxyDumper; + + /** + * {@inheritdoc} + */ + public function __construct(ContainerBuilder $container) + { + parent::__construct($container); + + $this->inlinedDefinitions = new \SplObjectStorage(); + } + + /** + * Sets the dumper to be used when dumping proxies in the generated container. + * + * @param ProxyDumper $proxyDumper + */ + public function setProxyDumper(ProxyDumper $proxyDumper) + { + $this->proxyDumper = $proxyDumper; + } + + /** + * Dumps the service container as a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * * namespace: The class namespace + * + * @param array $options An array of options + * + * @return string A PHP class representing of the service container + */ + public function dump(array $options = array()) + { + $this->targetDirRegex = null; + $options = array_merge(array( + 'class' => 'ProjectServiceContainer', + 'base_class' => 'Container', + 'namespace' => '', + ), $options); + + if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) { + // Build a regexp where the first root dirs are mandatory, + // but every other sub-dir is optional up to the full path in $dir + // Mandate at least 2 root dirs and not more that 5 optional dirs. + + $dir = explode(DIRECTORY_SEPARATOR, realpath($dir)); + $i = count($dir); + + if (3 <= $i) { + $regex = ''; + $lastOptionalDir = $i > 8 ? $i - 5 : 3; + $this->targetDirMaxMatches = $i - $lastOptionalDir; + + while (--$i >= $lastOptionalDir) { + $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); + } + + do { + $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; + } while (0 < --$i); + + $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; + } + } + + $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']); + + if ($this->container->isFrozen()) { + $code .= $this->addFrozenConstructor(); + $code .= $this->addFrozenCompile(); + } else { + $code .= $this->addConstructor(); + } + + $code .= + $this->addServices(). + $this->addDefaultParametersMethod(). + $this->endClass(). + $this->addProxyClasses() + ; + $this->targetDirRegex = null; + + return $code; + } + + /** + * Retrieves the currently set proxy dumper or instantiates one. + * + * @return ProxyDumper + */ + private function getProxyDumper() + { + if (!$this->proxyDumper) { + $this->proxyDumper = new NullDumper(); + } + + return $this->proxyDumper; + } + + /** + * Generates Service local temp variables. + * + * @param string $cId + * @param string $definition + * + * @return string + */ + private function addServiceLocalTempVariables($cId, $definition) + { + static $template = " \$%s = %s;\n"; + + $localDefinitions = array_merge( + array($definition), + $this->getInlinedDefinitions($definition) + ); + + $calls = $behavior = array(); + foreach ($localDefinitions as $iDefinition) { + $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior); + $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior); + $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior); + $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior); + $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior); + } + + $code = ''; + foreach ($calls as $id => $callCount) { + if ('service_container' === $id || $id === $cId) { + continue; + } + + if ($callCount > 1) { + $name = $this->getNextVariableName(); + $this->referenceVariables[$id] = new Variable($name); + + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) { + $code .= sprintf($template, $name, $this->getServiceCall($id)); + } else { + $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE))); + } + } + } + + if ('' !== $code) { + $code .= "\n"; + } + + return $code; + } + + /** + * Generates code for the proxies to be attached after the container class. + * + * @return string + */ + private function addProxyClasses() + { + /* @var $definitions Definition[] */ + $definitions = array_filter( + $this->container->getDefinitions(), + array($this->getProxyDumper(), 'isProxyCandidate') + ); + $code = ''; + + foreach ($definitions as $definition) { + $code .= "\n".$this->getProxyDumper()->getProxyCode($definition); + } + + return $code; + } + + /** + * Generates the require_once statement for service includes. + * + * @param string $id The service id + * @param Definition $definition + * + * @return string + */ + private function addServiceInclude($id, $definition) + { + $template = " require_once %s;\n"; + $code = ''; + + if (null !== $file = $definition->getFile()) { + $code .= sprintf($template, $this->dumpValue($file)); + } + + foreach ($this->getInlinedDefinitions($definition) as $definition) { + if (null !== $file = $definition->getFile()) { + $code .= sprintf($template, $this->dumpValue($file)); + } + } + + if ('' !== $code) { + $code .= "\n"; + } + + return $code; + } + + /** + * Generates the inline definition of a service. + * + * @param string $id + * @param Definition $definition + * + * @return string + * + * @throws RuntimeException When the factory definition is incomplete + * @throws ServiceCircularReferenceException When a circular reference is detected + */ + private function addServiceInlinedDefinitions($id, $definition) + { + $code = ''; + $variableMap = $this->definitionVariables; + $nbOccurrences = new \SplObjectStorage(); + $processed = new \SplObjectStorage(); + $inlinedDefinitions = $this->getInlinedDefinitions($definition); + + foreach ($inlinedDefinitions as $definition) { + if (false === $nbOccurrences->contains($definition)) { + $nbOccurrences->offsetSet($definition, 1); + } else { + $i = $nbOccurrences->offsetGet($definition); + $nbOccurrences->offsetSet($definition, $i + 1); + } + } + + foreach ($inlinedDefinitions as $sDefinition) { + if ($processed->contains($sDefinition)) { + continue; + } + $processed->offsetSet($sDefinition); + + $class = $this->dumpValue($sDefinition->getClass()); + if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) { + $name = $this->getNextVariableName(); + $variableMap->offsetSet($sDefinition, new Variable($name)); + + // a construct like: + // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); + // this is an indication for a wrong implementation, you can circumvent this problem + // by setting up your service structure like this: + // $b = new ServiceB(); + // $a = new ServiceA(ServiceB $b); + // $b->setServiceA(ServiceA $a); + if ($this->hasReference($id, $sDefinition->getArguments())) { + throw new ServiceCircularReferenceException($id, array($id)); + } + + $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = '); + + if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) { + $code .= $this->addServiceMethodCalls(null, $sDefinition, $name); + $code .= $this->addServiceProperties(null, $sDefinition, $name); + $code .= $this->addServiceConfigurator(null, $sDefinition, $name); + } + + $code .= "\n"; + } + } + + return $code; + } + + /** + * Adds the service return statement. + * + * @param string $id Service id + * @param Definition $definition + * + * @return string + */ + private function addServiceReturn($id, $definition) + { + if ($this->isSimpleInstance($id, $definition)) { + return " }\n"; + } + + return "\n return \$instance;\n }\n"; + } + + /** + * Generates the service instance. + * + * @param string $id + * @param Definition $definition + * + * @return string + * + * @throws InvalidArgumentException + * @throws RuntimeException + */ + private function addServiceInstance($id, $definition) + { + $class = $definition->getClass(); + + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + $class = $this->dumpValue($class); + + if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); + } + + $simple = $this->isSimpleInstance($id, $definition); + $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); + $instantiation = ''; + + if (!$isProxyCandidate && $definition->isShared()) { + $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance'); + } elseif (!$simple) { + $instantiation = '$instance'; + } + + $return = ''; + if ($simple) { + $return = 'return '; + } else { + $instantiation .= ' = '; + } + + $code = $this->addNewInstance($id, $definition, $return, $instantiation); + + if (!$simple) { + $code .= "\n"; + } + + return $code; + } + + /** + * Checks if the definition is a simple instance. + * + * @param string $id + * @param Definition $definition + * + * @return bool + */ + private function isSimpleInstance($id, $definition) + { + foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) { + if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) { + continue; + } + + if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) { + return false; + } + } + + return true; + } + + /** + * Adds method calls to a service definition. + * + * @param string $id + * @param Definition $definition + * @param string $variableName + * + * @return string + */ + private function addServiceMethodCalls($id, $definition, $variableName = 'instance') + { + $calls = ''; + foreach ($definition->getMethodCalls() as $call) { + $arguments = array(); + foreach ($call[1] as $value) { + $arguments[] = $this->dumpValue($value); + } + + $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments))); + } + + return $calls; + } + + private function addServiceProperties($id, $definition, $variableName = 'instance') + { + $code = ''; + foreach ($definition->getProperties() as $name => $value) { + $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value)); + } + + return $code; + } + + /** + * Generates the inline definition setup. + * + * @param string $id + * @param Definition $definition + * + * @return string + * + * @throws ServiceCircularReferenceException when the container contains a circular reference + */ + private function addServiceInlinedDefinitionsSetup($id, $definition) + { + $this->referenceVariables[$id] = new Variable('instance'); + + $code = ''; + $processed = new \SplObjectStorage(); + foreach ($this->getInlinedDefinitions($definition) as $iDefinition) { + if ($processed->contains($iDefinition)) { + continue; + } + $processed->offsetSet($iDefinition); + + if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) { + continue; + } + + // if the instance is simple, the return statement has already been generated + // so, the only possible way to get there is because of a circular reference + if ($this->isSimpleInstance($id, $definition)) { + throw new ServiceCircularReferenceException($id, array($id)); + } + + $name = (string) $this->definitionVariables->offsetGet($iDefinition); + $code .= $this->addServiceMethodCalls(null, $iDefinition, $name); + $code .= $this->addServiceProperties(null, $iDefinition, $name); + $code .= $this->addServiceConfigurator(null, $iDefinition, $name); + } + + if ('' !== $code) { + $code .= "\n"; + } + + return $code; + } + + /** + * Adds configurator definition. + * + * @param string $id + * @param Definition $definition + * @param string $variableName + * + * @return string + */ + private function addServiceConfigurator($id, $definition, $variableName = 'instance') + { + if (!$callable = $definition->getConfigurator()) { + return ''; + } + + if (is_array($callable)) { + if ($callable[0] instanceof Reference + || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { + return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + } + + $class = $this->dumpValue($callable[0]); + // If the class is a string we can optimize call_user_func away + if (strpos($class, "'") === 0) { + return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName); + } + + return sprintf(" call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + } + + return sprintf(" %s(\$%s);\n", $callable, $variableName); + } + + /** + * Adds a service. + * + * @param string $id + * @param Definition $definition + * + * @return string + */ + private function addService($id, $definition) + { + $this->definitionVariables = new \SplObjectStorage(); + $this->referenceVariables = array(); + $this->variableCount = 0; + + $return = array(); + + if ($definition->isSynthetic()) { + $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically'; + } elseif ($class = $definition->getClass()) { + $return[] = sprintf('@return %s A %s instance.', 0 === strpos($class, '%') ? 'object' : '\\'.ltrim($class, '\\'), ltrim($class, '\\')); + } elseif ($definition->getFactory()) { + $factory = $definition->getFactory(); + if (is_string($factory)) { + $return[] = sprintf('@return object An instance returned by %s().', $factory); + } elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { + if (is_string($factory[0]) || $factory[0] instanceof Reference) { + $return[] = sprintf('@return object An instance returned by %s::%s().', (string) $factory[0], $factory[1]); + } elseif ($factory[0] instanceof Definition) { + $return[] = sprintf('@return object An instance returned by %s::%s().', $factory[0]->getClass(), $factory[1]); + } + } + } + + if ($definition->isDeprecated()) { + if ($return && 0 === strpos($return[count($return) - 1], '@return')) { + $return[] = ''; + } + + $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id)); + } + + $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return)); + + $doc = ''; + if ($definition->isShared()) { + $doc .= <<isPublic()) { + $doc .= <<isAutowired()) { + $doc = <<isLazy()) { + $lazyInitialization = '$lazyLoad = true'; + $lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *"; + } else { + $lazyInitialization = ''; + $lazyInitializationDoc = ''; + } + + // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer + $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); + $visibility = $isProxyCandidate ? 'public' : 'protected'; + $code = <<camelize($id)}Service($lazyInitialization) + { + +EOF; + + $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : ''; + + if ($definition->isSynthetic()) { + $code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id); + } else { + if ($definition->isDeprecated()) { + $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", var_export($definition->getDeprecationMessage($id), true)); + } + + $code .= + $this->addServiceInclude($id, $definition). + $this->addServiceLocalTempVariables($id, $definition). + $this->addServiceInlinedDefinitions($id, $definition). + $this->addServiceInstance($id, $definition). + $this->addServiceInlinedDefinitionsSetup($id, $definition). + $this->addServiceMethodCalls($id, $definition). + $this->addServiceProperties($id, $definition). + $this->addServiceConfigurator($id, $definition). + $this->addServiceReturn($id, $definition) + ; + } + + $this->definitionVariables = null; + $this->referenceVariables = null; + + return $code; + } + + /** + * Adds multiple services. + * + * @return string + */ + private function addServices() + { + $publicServices = $privateServices = ''; + $definitions = $this->container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if ($definition->isPublic()) { + $publicServices .= $this->addService($id, $definition); + } else { + $privateServices .= $this->addService($id, $definition); + } + } + + return $publicServices.$privateServices; + } + + private function addNewInstance($id, Definition $definition, $return, $instantiation) + { + $class = $this->dumpValue($definition->getClass()); + + $arguments = array(); + foreach ($definition->getArguments() as $value) { + $arguments[] = $this->dumpValue($value); + } + + if (null !== $definition->getFactory()) { + $callable = $definition->getFactory(); + if (is_array($callable)) { + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); + } + + if ($callable[0] instanceof Reference + || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { + return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + + $class = $this->dumpValue($callable[0]); + // If the class is a string we can optimize call_user_func away + if (strpos($class, "'") === 0) { + return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + + return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : ''); + } + + return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : ''); + } + + if (false !== strpos($class, '$')) { + return sprintf(" \$class = %s;\n\n $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments)); + } + + return sprintf(" $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); + } + + /** + * Adds the class headers. + * + * @param string $class Class name + * @param string $baseClass The name of the base class + * @param string $namespace The class namespace + * + * @return string + */ + private function startClass($class, $baseClass, $namespace) + { + $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;'; + $namespaceLine = $namespace ? "namespace $namespace;\n" : ''; + + return <<exportTargetDirs(); + $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; + + $code = <<addMethodMap(); + $code .= $this->addAliases(); + + $code .= <<exportTargetDirs(); + + $code = <<container->getParameterBag()->all()) { + $code .= "\n \$this->parameters = \$this->getDefaultParameters();\n"; + } + + $code .= "\n \$this->services = array();\n"; + $code .= $this->addMethodMap(); + $code .= $this->addAliases(); + + $code .= <<container->getDefinitions()) { + return ''; + } + + $code = " \$this->methodMap = array(\n"; + ksort($definitions); + foreach ($definitions as $id => $definition) { + $code .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n"; + } + + return $code." );\n"; + } + + /** + * Adds the aliases property definition. + * + * @return string + */ + private function addAliases() + { + if (!$aliases = $this->container->getAliases()) { + if ($this->container->isFrozen()) { + return "\n \$this->aliases = array();\n"; + } else { + return ''; + } + } + + $code = " \$this->aliases = array(\n"; + ksort($aliases); + foreach ($aliases as $alias => $id) { + $id = (string) $id; + while (isset($aliases[$id])) { + $id = (string) $aliases[$id]; + } + $code .= ' '.var_export($alias, true).' => '.var_export($id, true).",\n"; + } + + return $code." );\n"; + } + + /** + * Adds default parameters method. + * + * @return string + */ + private function addDefaultParametersMethod() + { + if (!$this->container->getParameterBag()->all()) { + return ''; + } + + $parameters = $this->exportParameters($this->container->getParameterBag()->all()); + + $code = ''; + if ($this->container->isFrozen()) { + $code .= <<parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name)); + } + + return \$this->parameters[\$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter(\$name) + { + \$name = strtolower(\$name); + + return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter(\$name, \$value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === \$this->parameterBag) { + \$this->parameterBag = new FrozenParameterBag(\$this->parameters); + } + + return \$this->parameterBag; + } + +EOF; + } + + $code .= << $value) { + if (is_array($value)) { + $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); + } elseif ($value instanceof Variable) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); + } elseif ($value instanceof Definition) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key)); + } elseif ($value instanceof Reference) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key)); + } elseif ($value instanceof Expression) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key)); + } else { + $value = $this->export($value); + } + + $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value); + } + + return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4)); + } + + /** + * Ends the class definition. + * + * @return string + */ + private function endClass() + { + return <<has('%s')", $service); + } + + // re-indent the wrapped code + $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); + + return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code); + } + + /** + * Builds service calls from arguments. + * + * @param array $arguments + * @param array &$calls By reference + * @param array &$behavior By reference + */ + private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + $this->getServiceCallsFromArguments($argument, $calls, $behavior); + } elseif ($argument instanceof Reference) { + $id = (string) $argument; + + if (!isset($calls[$id])) { + $calls[$id] = 0; + } + if (!isset($behavior[$id])) { + $behavior[$id] = $argument->getInvalidBehavior(); + } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) { + $behavior[$id] = $argument->getInvalidBehavior(); + } + + ++$calls[$id]; + } + } + } + + /** + * Returns the inline definition. + * + * @param Definition $definition + * + * @return array + */ + private function getInlinedDefinitions(Definition $definition) + { + if (false === $this->inlinedDefinitions->contains($definition)) { + $definitions = array_merge( + $this->getDefinitionsFromArguments($definition->getArguments()), + $this->getDefinitionsFromArguments($definition->getMethodCalls()), + $this->getDefinitionsFromArguments($definition->getProperties()), + $this->getDefinitionsFromArguments(array($definition->getConfigurator())), + $this->getDefinitionsFromArguments(array($definition->getFactory())) + ); + + $this->inlinedDefinitions->offsetSet($definition, $definitions); + + return $definitions; + } + + return $this->inlinedDefinitions->offsetGet($definition); + } + + /** + * Gets the definition from arguments. + * + * @param array $arguments + * + * @return array + */ + private function getDefinitionsFromArguments(array $arguments) + { + $definitions = array(); + foreach ($arguments as $argument) { + if (is_array($argument)) { + $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument)); + } elseif ($argument instanceof Definition) { + $definitions = array_merge( + $definitions, + $this->getInlinedDefinitions($argument), + array($argument) + ); + } + } + + return $definitions; + } + + /** + * Checks if a service id has a reference. + * + * @param string $id + * @param array $arguments + * @param bool $deep + * @param array $visited + * + * @return bool + */ + private function hasReference($id, array $arguments, $deep = false, array &$visited = array()) + { + foreach ($arguments as $argument) { + if (is_array($argument)) { + if ($this->hasReference($id, $argument, $deep, $visited)) { + return true; + } + } elseif ($argument instanceof Reference) { + $argumentId = (string) $argument; + if ($id === $argumentId) { + return true; + } + + if ($deep && !isset($visited[$argumentId])) { + $visited[$argumentId] = true; + + $service = $this->container->getDefinition($argumentId); + $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties()); + + if ($this->hasReference($id, $arguments, $deep, $visited)) { + return true; + } + } + } + } + + return false; + } + + /** + * Dumps values. + * + * @param mixed $value + * @param bool $interpolate + * + * @return string + * + * @throws RuntimeException + */ + private function dumpValue($value, $interpolate = true) + { + if (is_array($value)) { + $code = array(); + foreach ($value as $k => $v) { + $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); + } + + return sprintf('array(%s)', implode(', ', $code)); + } elseif ($value instanceof Definition) { + if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { + return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate); + } + if (count($value->getMethodCalls()) > 0) { + throw new RuntimeException('Cannot dump definitions which have method calls.'); + } + if (null !== $value->getConfigurator()) { + throw new RuntimeException('Cannot dump definitions which have a configurator.'); + } + + $arguments = array(); + foreach ($value->getArguments() as $argument) { + $arguments[] = $this->dumpValue($argument); + } + + if (null !== $value->getFactory()) { + $factory = $value->getFactory(); + + if (is_string($factory)) { + return sprintf('\\%s(%s)', $factory, implode(', ', $arguments)); + } + + if (is_array($factory)) { + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); + } + + if (is_string($factory[0])) { + return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments)); + } + + if ($factory[0] instanceof Definition) { + return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); + } + + if ($factory[0] instanceof Reference) { + return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments)); + } + } + + throw new RuntimeException('Cannot dump definition because of invalid factory'); + } + + $class = $value->getClass(); + if (null === $class) { + throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); + } + + return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); + } elseif ($value instanceof Variable) { + return '$'.$value; + } elseif ($value instanceof Reference) { + if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) { + return $this->dumpValue($this->referenceVariables[$id], $interpolate); + } + + return $this->getServiceCall((string) $value, $value); + } elseif ($value instanceof Expression) { + return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); + } elseif ($value instanceof Parameter) { + return $this->dumpParameter($value); + } elseif (true === $interpolate && is_string($value)) { + if (preg_match('/^%([^%]+)%$/', $value, $match)) { + // we do this to deal with non string values (Boolean, integer, ...) + // the preg_replace_callback converts them to strings + return $this->dumpParameter(strtolower($match[1])); + } else { + $replaceParameters = function ($match) { + return "'.".$this->dumpParameter(strtolower($match[2])).".'"; + }; + + $code = str_replace('%%', '%', preg_replace_callback('/(?export($value))); + + return $code; + } + } elseif (is_object($value) || is_resource($value)) { + throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); + } else { + return $this->export($value); + } + } + + /** + * Dumps a string to a literal (aka PHP Code) class value. + * + * @param string $class + * + * @return string + * + * @throws RuntimeException + */ + private function dumpLiteralClass($class) + { + if (false !== strpos($class, '$')) { + throw new RuntimeException('Cannot dump definitions which have a variable class name.'); + } + if (0 !== strpos($class, "'") || !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a')); + } + + return '\\'.substr(str_replace('\\\\', '\\', $class), 1, -1); + } + + /** + * Dumps a parameter. + * + * @param string $name + * + * @return string + */ + private function dumpParameter($name) + { + if ($this->container->isFrozen() && $this->container->hasParameter($name)) { + return $this->dumpValue($this->container->getParameter($name), false); + } + + return sprintf("\$this->getParameter('%s')", strtolower($name)); + } + + /** + * Gets a service call. + * + * @param string $id + * @param Reference $reference + * + * @return string + */ + private function getServiceCall($id, Reference $reference = null) + { + if ('service_container' === $id) { + return '$this'; + } + + if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { + return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id); + } else { + if ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + + return sprintf('$this->get(\'%s\')', $id); + } + } + + /** + * Convert a service id to a valid PHP method name. + * + * @param string $id + * + * @return string + * + * @throws InvalidArgumentException + */ + private function camelize($id) + { + $name = Container::camelize($id); + + if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) { + throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id)); + } + + return $name; + } + + /** + * Returns the next name to use. + * + * @return string + */ + private function getNextVariableName() + { + $firstChars = self::FIRST_CHARS; + $firstCharsLength = strlen($firstChars); + $nonFirstChars = self::NON_FIRST_CHARS; + $nonFirstCharsLength = strlen($nonFirstChars); + + while (true) { + $name = ''; + $i = $this->variableCount; + + if ('' === $name) { + $name .= $firstChars[$i % $firstCharsLength]; + $i = (int) ($i / $firstCharsLength); + } + + while ($i > 0) { + --$i; + $name .= $nonFirstChars[$i % $nonFirstCharsLength]; + $i = (int) ($i / $nonFirstCharsLength); + } + + ++$this->variableCount; + + // check that the name is not reserved + if (in_array($name, $this->reservedVariables, true)) { + continue; + } + + return $name; + } + } + + private function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $providers = $this->container->getExpressionLanguageProviders(); + $this->expressionLanguage = new ExpressionLanguage(null, $providers); + + if ($this->container->isTrackingResources()) { + foreach ($providers as $provider) { + $this->container->addObjectResource($provider); + } + } + } + + return $this->expressionLanguage; + } + + private function exportTargetDirs() + { + return null === $this->targetDirRegex ? '' : <<targetDirMaxMatches}; ++\$i) { + \$this->targetDirs[\$i] = \$dir = dirname(\$dir); + } +EOF; + } + + private function export($value) + { + if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { + $prefix = $matches[0][1] ? var_export(substr($value, 0, $matches[0][1]), true).'.' : ''; + $suffix = $matches[0][1] + strlen($matches[0][0]); + $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : ''; + $dirname = '__DIR__'; + + if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) { + $dirname = sprintf('$this->targetDirs[%d]', $offset); + } + + if ($prefix || $suffix) { + return sprintf('(%s%s%s)', $prefix, $dirname, $suffix); + } + + return $dirname; + } + + return var_export($value, true); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php new file mode 100644 index 0000000..68ce500 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php @@ -0,0 +1,362 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * XmlDumper dumps a service container as an XML string. + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class XmlDumper extends Dumper +{ + /** + * @var \DOMDocument + */ + private $document; + + /** + * Dumps the service container as an XML string. + * + * @param array $options An array of options + * + * @return string An xml string representing of the service container + */ + public function dump(array $options = array()) + { + $this->document = new \DOMDocument('1.0', 'utf-8'); + $this->document->formatOutput = true; + + $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container'); + $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd'); + + $this->addParameters($container); + $this->addServices($container); + + $this->document->appendChild($container); + $xml = $this->document->saveXML(); + $this->document = null; + + return $xml; + } + + /** + * Adds parameters. + * + * @param \DOMElement $parent + */ + private function addParameters(\DOMElement $parent) + { + $data = $this->container->getParameterBag()->all(); + if (!$data) { + return; + } + + if ($this->container->isFrozen()) { + $data = $this->escape($data); + } + + $parameters = $this->document->createElement('parameters'); + $parent->appendChild($parameters); + $this->convertParameters($data, 'parameter', $parameters); + } + + /** + * Adds method calls. + * + * @param array $methodcalls + * @param \DOMElement $parent + */ + private function addMethodCalls(array $methodcalls, \DOMElement $parent) + { + foreach ($methodcalls as $methodcall) { + $call = $this->document->createElement('call'); + $call->setAttribute('method', $methodcall[0]); + if (count($methodcall[1])) { + $this->convertParameters($methodcall[1], 'argument', $call); + } + $parent->appendChild($call); + } + } + + /** + * Adds a service. + * + * @param Definition $definition + * @param string $id + * @param \DOMElement $parent + */ + private function addService($definition, $id, \DOMElement $parent) + { + $service = $this->document->createElement('service'); + if (null !== $id) { + $service->setAttribute('id', $id); + } + if ($class = $definition->getClass()) { + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + $service->setAttribute('class', $class); + } + if (!$definition->isShared()) { + $service->setAttribute('shared', 'false'); + } + if (!$definition->isPublic()) { + $service->setAttribute('public', 'false'); + } + if ($definition->isSynthetic()) { + $service->setAttribute('synthetic', 'true'); + } + if ($definition->isLazy()) { + $service->setAttribute('lazy', 'true'); + } + if (null !== $decorated = $definition->getDecoratedService()) { + list($decorated, $renamedId, $priority) = $decorated; + $service->setAttribute('decorates', $decorated); + if (null !== $renamedId) { + $service->setAttribute('decoration-inner-name', $renamedId); + } + if (0 !== $priority) { + $service->setAttribute('decoration-priority', $priority); + } + } + + foreach ($definition->getTags() as $name => $tags) { + foreach ($tags as $attributes) { + $tag = $this->document->createElement('tag'); + $tag->setAttribute('name', $name); + foreach ($attributes as $key => $value) { + $tag->setAttribute($key, $value); + } + $service->appendChild($tag); + } + } + + if ($definition->getFile()) { + $file = $this->document->createElement('file'); + $file->appendChild($this->document->createTextNode($definition->getFile())); + $service->appendChild($file); + } + + if ($parameters = $definition->getArguments()) { + $this->convertParameters($parameters, 'argument', $service); + } + + if ($parameters = $definition->getProperties()) { + $this->convertParameters($parameters, 'property', $service, 'name'); + } + + $this->addMethodCalls($definition->getMethodCalls(), $service); + + if ($callable = $definition->getFactory()) { + $factory = $this->document->createElement('factory'); + + if (is_array($callable) && $callable[0] instanceof Definition) { + $this->addService($callable[0], null, $factory); + $factory->setAttribute('method', $callable[1]); + } elseif (is_array($callable)) { + $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + $factory->setAttribute('method', $callable[1]); + } else { + $factory->setAttribute('function', $callable); + } + $service->appendChild($factory); + } + + if ($definition->isDeprecated()) { + $deprecated = $this->document->createElement('deprecated'); + $deprecated->appendChild($this->document->createTextNode($definition->getDeprecationMessage('%service_id%'))); + + $service->appendChild($deprecated); + } + + if ($definition->isAutowired()) { + $service->setAttribute('autowire', 'true'); + } + + foreach ($definition->getAutowiringTypes() as $autowiringTypeValue) { + $autowiringType = $this->document->createElement('autowiring-type'); + $autowiringType->appendChild($this->document->createTextNode($autowiringTypeValue)); + + $service->appendChild($autowiringType); + } + + if ($callable = $definition->getConfigurator()) { + $configurator = $this->document->createElement('configurator'); + + if (is_array($callable) && $callable[0] instanceof Definition) { + $this->addService($callable[0], null, $configurator); + $configurator->setAttribute('method', $callable[1]); + } elseif (is_array($callable)) { + $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + $configurator->setAttribute('method', $callable[1]); + } else { + $configurator->setAttribute('function', $callable); + } + $service->appendChild($configurator); + } + + $parent->appendChild($service); + } + + /** + * Adds a service alias. + * + * @param string $alias + * @param Alias $id + * @param \DOMElement $parent + */ + private function addServiceAlias($alias, Alias $id, \DOMElement $parent) + { + $service = $this->document->createElement('service'); + $service->setAttribute('id', $alias); + $service->setAttribute('alias', $id); + if (!$id->isPublic()) { + $service->setAttribute('public', 'false'); + } + $parent->appendChild($service); + } + + /** + * Adds services. + * + * @param \DOMElement $parent + */ + private function addServices(\DOMElement $parent) + { + $definitions = $this->container->getDefinitions(); + if (!$definitions) { + return; + } + + $services = $this->document->createElement('services'); + foreach ($definitions as $id => $definition) { + $this->addService($definition, $id, $services); + } + + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $id) { + while (isset($aliases[(string) $id])) { + $id = $aliases[(string) $id]; + } + $this->addServiceAlias($alias, $id, $services); + } + $parent->appendChild($services); + } + + /** + * Converts parameters. + * + * @param array $parameters + * @param string $type + * @param \DOMElement $parent + * @param string $keyAttribute + */ + private function convertParameters($parameters, $type, \DOMElement $parent, $keyAttribute = 'key') + { + $withKeys = array_keys($parameters) !== range(0, count($parameters) - 1); + foreach ($parameters as $key => $value) { + $element = $this->document->createElement($type); + if ($withKeys) { + $element->setAttribute($keyAttribute, $key); + } + + if (is_array($value)) { + $element->setAttribute('type', 'collection'); + $this->convertParameters($value, $type, $element, 'key'); + } elseif ($value instanceof Reference) { + $element->setAttribute('type', 'service'); + $element->setAttribute('id', (string) $value); + $behaviour = $value->getInvalidBehavior(); + if ($behaviour == ContainerInterface::NULL_ON_INVALID_REFERENCE) { + $element->setAttribute('on-invalid', 'null'); + } elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) { + $element->setAttribute('on-invalid', 'ignore'); + } + } elseif ($value instanceof Definition) { + $element->setAttribute('type', 'service'); + $this->addService($value, null, $element); + } elseif ($value instanceof Expression) { + $element->setAttribute('type', 'expression'); + $text = $this->document->createTextNode(self::phpToXml((string) $value)); + $element->appendChild($text); + } else { + if (in_array($value, array('null', 'true', 'false'), true)) { + $element->setAttribute('type', 'string'); + } + $text = $this->document->createTextNode(self::phpToXml($value)); + $element->appendChild($text); + } + $parent->appendChild($element); + } + } + + /** + * Escapes arguments. + * + * @param array $arguments + * + * @return array + */ + private function escape($arguments) + { + $args = array(); + foreach ($arguments as $k => $v) { + if (is_array($v)) { + $args[$k] = $this->escape($v); + } elseif (is_string($v)) { + $args[$k] = str_replace('%', '%%', $v); + } else { + $args[$k] = $v; + } + } + + return $args; + } + + /** + * Converts php types to xml types. + * + * @param mixed $value Value to convert + * + * @return string + * + * @throws RuntimeException When trying to dump object or resource + */ + public static function phpToXml($value) + { + switch (true) { + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case $value instanceof Parameter: + return '%'.$value.'%'; + case is_object($value) || is_resource($value): + throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); + default: + return (string) $value; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php new file mode 100644 index 0000000..14c7022 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -0,0 +1,348 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\Yaml\Dumper as YmlDumper; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * YamlDumper dumps a service container as a YAML string. + * + * @author Fabien Potencier + */ +class YamlDumper extends Dumper +{ + private $dumper; + + /** + * Dumps the service container as an YAML string. + * + * @param array $options An array of options + * + * @return string A YAML string representing of the service container + */ + public function dump(array $options = array()) + { + if (!class_exists('Symfony\Component\Yaml\Dumper')) { + throw new RuntimeException('Unable to dump the container as the Symfony Yaml Component is not installed.'); + } + + if (null === $this->dumper) { + $this->dumper = new YmlDumper(); + } + + return $this->addParameters()."\n".$this->addServices(); + } + + /** + * Adds a service. + * + * @param string $id + * @param Definition $definition + * + * @return string + */ + private function addService($id, $definition) + { + $code = " $id:\n"; + if ($class = $definition->getClass()) { + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + $code .= sprintf(" class: %s\n", $class); + } + + if (!$definition->isPublic()) { + $code .= " public: false\n"; + } + + $tagsCode = ''; + foreach ($definition->getTags() as $name => $tags) { + foreach ($tags as $attributes) { + $att = array(); + foreach ($attributes as $key => $value) { + $att[] = sprintf('%s: %s', $this->dumper->dump($key), $this->dumper->dump($value)); + } + $att = $att ? ', '.implode(', ', $att) : ''; + + $tagsCode .= sprintf(" - { name: %s%s }\n", $this->dumper->dump($name), $att); + } + } + if ($tagsCode) { + $code .= " tags:\n".$tagsCode; + } + + if ($definition->getFile()) { + $code .= sprintf(" file: %s\n", $definition->getFile()); + } + + if ($definition->isSynthetic()) { + $code .= sprintf(" synthetic: true\n"); + } + + if ($definition->isDeprecated()) { + $code .= sprintf(" deprecated: %s\n", $definition->getDeprecationMessage('%service_id%')); + } + + if ($definition->isAutowired()) { + $code .= " autowire: true\n"; + } + + $autowiringTypesCode = ''; + foreach ($definition->getAutowiringTypes() as $autowiringType) { + $autowiringTypesCode .= sprintf(" - %s\n", $this->dumper->dump($autowiringType)); + } + if ($autowiringTypesCode) { + $code .= sprintf(" autowiring_types:\n%s", $autowiringTypesCode); + } + + if ($definition->isLazy()) { + $code .= sprintf(" lazy: true\n"); + } + + if ($definition->getArguments()) { + $code .= sprintf(" arguments: %s\n", $this->dumper->dump($this->dumpValue($definition->getArguments()), 0)); + } + + if ($definition->getProperties()) { + $code .= sprintf(" properties: %s\n", $this->dumper->dump($this->dumpValue($definition->getProperties()), 0)); + } + + if ($definition->getMethodCalls()) { + $code .= sprintf(" calls:\n%s\n", $this->dumper->dump($this->dumpValue($definition->getMethodCalls()), 1, 12)); + } + + if (!$definition->isShared()) { + $code .= " shared: false\n"; + } + + if (null !== $decorated = $definition->getDecoratedService()) { + list($decorated, $renamedId, $priority) = $decorated; + $code .= sprintf(" decorates: %s\n", $decorated); + if (null !== $renamedId) { + $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); + } + if (0 !== $priority) { + $code .= sprintf(" decoration_priority: %s\n", $priority); + } + } + + if ($callable = $definition->getFactory()) { + $code .= sprintf(" factory: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); + } + + if ($callable = $definition->getConfigurator()) { + $code .= sprintf(" configurator: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); + } + + return $code; + } + + /** + * Adds a service alias. + * + * @param string $alias + * @param Alias $id + * + * @return string + */ + private function addServiceAlias($alias, $id) + { + if ($id->isPublic()) { + return sprintf(" %s: '@%s'\n", $alias, $id); + } + + return sprintf(" %s:\n alias: %s\n public: false", $alias, $id); + } + + /** + * Adds services. + * + * @return string + */ + private function addServices() + { + if (!$this->container->getDefinitions()) { + return ''; + } + + $code = "services:\n"; + foreach ($this->container->getDefinitions() as $id => $definition) { + $code .= $this->addService($id, $definition); + } + + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $id) { + while (isset($aliases[(string) $id])) { + $id = $aliases[(string) $id]; + } + $code .= $this->addServiceAlias($alias, $id); + } + + return $code; + } + + /** + * Adds parameters. + * + * @return string + */ + private function addParameters() + { + if (!$this->container->getParameterBag()->all()) { + return ''; + } + + $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isFrozen()); + + return $this->dumper->dump(array('parameters' => $parameters), 2); + } + + /** + * Dumps callable to YAML format. + * + * @param callable $callable + * + * @return callable + */ + private function dumpCallable($callable) + { + if (is_array($callable)) { + if ($callable[0] instanceof Reference) { + $callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]); + } else { + $callable = array($callable[0], $callable[1]); + } + } + + return $callable; + } + + /** + * Dumps the value to YAML format. + * + * @param mixed $value + * + * @return mixed + * + * @throws RuntimeException When trying to dump object or resource + */ + private function dumpValue($value) + { + if (is_array($value)) { + $code = array(); + foreach ($value as $k => $v) { + $code[$k] = $this->dumpValue($v); + } + + return $code; + } elseif ($value instanceof Reference) { + return $this->getServiceCall((string) $value, $value); + } elseif ($value instanceof Parameter) { + return $this->getParameterCall((string) $value); + } elseif ($value instanceof Expression) { + return $this->getExpressionCall((string) $value); + } elseif (is_object($value) || is_resource($value)) { + throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); + } + + return $value; + } + + /** + * Gets the service call. + * + * @param string $id + * @param Reference $reference + * + * @return string + */ + private function getServiceCall($id, Reference $reference = null) + { + if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { + return sprintf('@?%s', $id); + } + + return sprintf('@%s', $id); + } + + /** + * Gets parameter call. + * + * @param string $id + * + * @return string + */ + private function getParameterCall($id) + { + return sprintf('%%%s%%', $id); + } + + private function getExpressionCall($expression) + { + return sprintf('@=%s', $expression); + } + + /** + * Prepares parameters. + * + * @param array $parameters + * @param bool $escape + * + * @return array + */ + private function prepareParameters($parameters, $escape = true) + { + $filtered = array(); + foreach ($parameters as $key => $value) { + if (is_array($value)) { + $value = $this->prepareParameters($value, $escape); + } elseif ($value instanceof Reference || is_string($value) && 0 === strpos($value, '@')) { + $value = '@'.$value; + } + + $filtered[$key] = $value; + } + + return $escape ? $this->escape($filtered) : $filtered; + } + + /** + * Escapes arguments. + * + * @param array $arguments + * + * @return array + */ + private function escape($arguments) + { + $args = array(); + foreach ($arguments as $k => $v) { + if (is_array($v)) { + $args[$k] = $this->escape($v); + } elseif (is_string($v)) { + $args[$k] = str_replace('%', '%%', $v); + } else { + $args[$k] = $v; + } + } + + return $args; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/BadMethodCallException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/BadMethodCallException.php new file mode 100644 index 0000000..959238e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/BadMethodCallException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base BadMethodCallException for Dependency Injection component. + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ExceptionInterface.php new file mode 100644 index 0000000..f5e9099 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ExceptionInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base ExceptionInterface for Dependency Injection component. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..119bb7d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base InvalidArgumentException for Dependency Injection component. + * + * @author Bulat Shakirzyanov + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/LogicException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/LogicException.php new file mode 100644 index 0000000..17a070c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base LogicException for Dependency Injection component. + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/OutOfBoundsException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..a61f143 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/OutOfBoundsException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base OutOfBoundsException for Dependency Injection component. + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php new file mode 100644 index 0000000..2915176 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ParameterCircularReferenceException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * This exception is thrown when a circular reference in a parameter is detected. + * + * @author Fabien Potencier + */ +class ParameterCircularReferenceException extends RuntimeException +{ + private $parameters; + + public function __construct($parameters, \Exception $previous = null) + { + parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]), 0, $previous); + + $this->parameters = $parameters; + } + + public function getParameters() + { + return $this->parameters; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php new file mode 100644 index 0000000..b529f0f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * This exception is thrown when a non-existent parameter is used. + * + * @author Fabien Potencier + */ +class ParameterNotFoundException extends InvalidArgumentException +{ + private $key; + private $sourceId; + private $sourceKey; + private $alternatives; + + /** + * Constructor. + * + * @param string $key The requested parameter key + * @param string $sourceId The service id that references the non-existent parameter + * @param string $sourceKey The parameter key that references the non-existent parameter + * @param \Exception $previous The previous exception + * @param string[] $alternatives Some parameter name alternatives + */ + public function __construct($key, $sourceId = null, $sourceKey = null, \Exception $previous = null, array $alternatives = array()) + { + $this->key = $key; + $this->sourceId = $sourceId; + $this->sourceKey = $sourceKey; + $this->alternatives = $alternatives; + + parent::__construct('', 0, $previous); + + $this->updateRepr(); + } + + public function updateRepr() + { + if (null !== $this->sourceId) { + $this->message = sprintf('The service "%s" has a dependency on a non-existent parameter "%s".', $this->sourceId, $this->key); + } elseif (null !== $this->sourceKey) { + $this->message = sprintf('The parameter "%s" has a dependency on a non-existent parameter "%s".', $this->sourceKey, $this->key); + } else { + $this->message = sprintf('You have requested a non-existent parameter "%s".', $this->key); + } + + if ($this->alternatives) { + if (1 == count($this->alternatives)) { + $this->message .= ' Did you mean this: "'; + } else { + $this->message .= ' Did you mean one of these: "'; + } + $this->message .= implode('", "', $this->alternatives).'"?'; + } + } + + public function getKey() + { + return $this->key; + } + + public function getSourceId() + { + return $this->sourceId; + } + + public function getSourceKey() + { + return $this->sourceKey; + } + + public function setSourceId($sourceId) + { + $this->sourceId = $sourceId; + + $this->updateRepr(); + } + + public function setSourceKey($sourceKey) + { + $this->sourceKey = $sourceKey; + + $this->updateRepr(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/RuntimeException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/RuntimeException.php new file mode 100644 index 0000000..5c24541 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base RuntimeException for Dependency Injection component. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php new file mode 100644 index 0000000..26e3fb3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ServiceCircularReferenceException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * This exception is thrown when a circular reference is detected. + * + * @author Johannes M. Schmitt + */ +class ServiceCircularReferenceException extends RuntimeException +{ + private $serviceId; + private $path; + + public function __construct($serviceId, array $path, \Exception $previous = null) + { + parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path)), 0, $previous); + + $this->serviceId = $serviceId; + $this->path = $path; + } + + public function getServiceId() + { + return $this->serviceId; + } + + public function getPath() + { + return $this->path; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php new file mode 100644 index 0000000..e65da50 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Exception/ServiceNotFoundException.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * This exception is thrown when a non-existent service is requested. + * + * @author Johannes M. Schmitt + */ +class ServiceNotFoundException extends InvalidArgumentException +{ + private $id; + private $sourceId; + + public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = array()) + { + if (null === $sourceId) { + $msg = sprintf('You have requested a non-existent service "%s".', $id); + } else { + $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id); + } + + if ($alternatives) { + if (1 == count($alternatives)) { + $msg .= ' Did you mean this: "'; + } else { + $msg .= ' Did you mean one of these: "'; + } + $msg .= implode('", "', $alternatives).'"?'; + } + + parent::__construct($msg, 0, $previous); + + $this->id = $id; + $this->sourceId = $sourceId; + } + + public function getId() + { + return $this->id; + } + + public function getSourceId() + { + return $this->sourceId; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php new file mode 100644 index 0000000..acc97bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; +use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; + +/** + * Adds some function to the default ExpressionLanguage. + * + * @author Fabien Potencier + * + * @see ExpressionLanguageProvider + */ +class ExpressionLanguage extends BaseExpressionLanguage +{ + public function __construct(ParserCacheInterface $cache = null, array $providers = array()) + { + // prepend the default provider to let users override it easily + array_unshift($providers, new ExpressionLanguageProvider()); + + parent::__construct($cache, $providers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php new file mode 100644 index 0000000..ce6d695 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * Define some ExpressionLanguage functions. + * + * To get a service, use service('request'). + * To get a parameter, use parameter('kernel.debug'). + * + * @author Fabien Potencier + */ +class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface +{ + public function getFunctions() + { + return array( + new ExpressionFunction('service', function ($arg) { + return sprintf('$this->get(%s)', $arg); + }, function (array $variables, $value) { + return $variables['container']->get($value); + }), + + new ExpressionFunction('parameter', function ($arg) { + return sprintf('$this->getParameter(%s)', $arg); + }, function (array $variables, $value) { + return $variables['container']->getParameter($value); + }), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php new file mode 100644 index 0000000..705ba38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * ConfigurationExtensionInterface is the interface implemented by container extension classes. + * + * @author Kevin Bond + */ +interface ConfigurationExtensionInterface +{ + /** + * Returns extension configuration. + * + * @param array $config An array of configuration values + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @return ConfigurationInterface|null The configuration or null + */ + public function getConfiguration(array $config, ContainerBuilder $container); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/Extension.php new file mode 100644 index 0000000..f8e058b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * Provides useful features shared by many extensions. + * + * @author Fabien Potencier + */ +abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface +{ + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return false; + } + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + */ + public function getNamespace() + { + return 'http://example.org/schema/dic/'.$this->getAlias(); + } + + /** + * Returns the recommended alias to use in XML. + * + * This alias is also the mandatory prefix to use when using YAML. + * + * This convention is to remove the "Extension" postfix from the class + * name and then lowercase and underscore the result. So: + * + * AcmeHelloExtension + * + * becomes + * + * acme_hello + * + * This can be overridden in a sub-class to specify the alias manually. + * + * @return string The alias + * + * @throws BadMethodCallException When the extension name does not follow conventions + */ + public function getAlias() + { + $className = get_class($this); + if (substr($className, -9) != 'Extension') { + throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); + } + $classBaseName = substr(strrchr($className, '\\'), 1, -9); + + return Container::underscore($classBaseName); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + $reflected = new \ReflectionClass($this); + $namespace = $reflected->getNamespaceName(); + + $class = $namespace.'\\Configuration'; + if (class_exists($class)) { + $r = new \ReflectionClass($class); + $container->addResource(new FileResource($r->getFileName())); + + if (!method_exists($class, '__construct')) { + $configuration = new $class(); + + return $configuration; + } + } + } + + final protected function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + $processor = new Processor(); + + return $processor->processConfiguration($configuration, $configs); + } + + /** + * @param ContainerBuilder $container + * @param array $config + * + * @return bool Whether the configuration is enabled + * + * @throws InvalidArgumentException When the config is not enableable + */ + protected function isConfigEnabled(ContainerBuilder $container, array $config) + { + if (!array_key_exists('enabled', $config)) { + throw new InvalidArgumentException("The config array has no 'enabled' key."); + } + + return (bool) $container->getParameterBag()->resolveValue($config['enabled']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php new file mode 100644 index 0000000..6e926fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * ExtensionInterface is the interface implemented by container extension classes. + * + * @author Fabien Potencier + */ +interface ExtensionInterface +{ + /** + * Loads a specific configuration. + * + * @param array $configs An array of configuration values + * @param ContainerBuilder $container A ContainerBuilder instance + * + * @throws \InvalidArgumentException When provided tag is not defined in this extension + */ + public function load(array $configs, ContainerBuilder $container); + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + */ + public function getNamespace(); + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath(); + + /** + * Returns the recommended alias to use in XML. + * + * This alias is also the mandatory prefix to use when using YAML. + * + * @return string The alias + */ + public function getAlias(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/PrependExtensionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/PrependExtensionInterface.php new file mode 100644 index 0000000..c666bdb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Extension/PrependExtensionInterface.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +interface PrependExtensionInterface +{ + /** + * Allow an extension to prepend the extension configurations. + * + * @param ContainerBuilder $container + */ + public function prepend(ContainerBuilder $container); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php new file mode 100644 index 0000000..a8dd525 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy\Instantiator; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Lazy proxy instantiator, capable of instantiating a proxy given a container, the + * service definitions and a callback that produces the real service instance. + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * Instantiates a proxy object. + * + * @param ContainerInterface $container the container from which the service is being requested + * @param Definition $definition the definition of the requested service + * @param string $id identifier of the requested service + * @param callable $realInstantiator zero-argument callback that is capable of producing the real + * service instance + * + * @return object + */ + public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php new file mode 100644 index 0000000..cad9320 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/RealServiceInstantiator.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy\Instantiator; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; + +/** + * {@inheritdoc} + * + * Noop proxy instantiator - simply produces the real service instead of a proxy instance. + * + * @author Marco Pivetta + */ +class RealServiceInstantiator implements InstantiatorInterface +{ + /** + * {@inheritdoc} + */ + public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator) + { + return call_user_func($realInstantiator); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php new file mode 100644 index 0000000..878d965 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Lazy proxy dumper capable of generating the instantiation logic PHP code for proxied services. + * + * @author Marco Pivetta + */ +interface DumperInterface +{ + /** + * Inspects whether the given definitions should produce proxy instantiation logic in the dumped container. + * + * @param Definition $definition + * + * @return bool + */ + public function isProxyCandidate(Definition $definition); + + /** + * Generates the code to be used to instantiate a proxy in the dumped factory code. + * + * @param Definition $definition + * @param string $id service identifier + * + * @return string + */ + public function getProxyFactoryCode(Definition $definition, $id); + + /** + * Generates the code for the lazy proxy. + * + * @param Definition $definition + * + * @return string + */ + public function getProxyCode(Definition $definition); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php new file mode 100644 index 0000000..30911d3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/NullDumper.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Null dumper, negates any proxy code generation for any given service definition. + * + * @author Marco Pivetta + */ +class NullDumper implements DumperInterface +{ + /** + * {@inheritdoc} + */ + public function isProxyCandidate(Definition $definition) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function getProxyFactoryCode(Definition $definition, $id) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getProxyCode(Definition $definition) + { + return ''; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php new file mode 100644 index 0000000..a5b4e5a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/ClosureLoader.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\Loader\Loader; + +/** + * ClosureLoader loads service definitions from a PHP closure. + * + * The Closure has access to the container as its first argument. + * + * @author Fabien Potencier + */ +class ClosureLoader extends Loader +{ + private $container; + + /** + * Constructor. + * + * @param ContainerBuilder $container A ContainerBuilder instance + */ + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + call_user_func($resource, $this->container); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return $resource instanceof \Closure; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php new file mode 100644 index 0000000..ffb8853 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/DirectoryLoader.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\Config\Resource\DirectoryResource; + +/** + * DirectoryLoader is a recursive loader to go through directories. + * + * @author Sebastien Lavoie + */ +class DirectoryLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($file, $type = null) + { + $file = rtrim($file, '/'); + $path = $this->locator->locate($file); + $this->container->addResource(new DirectoryResource($path)); + + foreach (scandir($path) as $dir) { + if ('.' !== $dir[0]) { + if (is_dir($path.'/'.$dir)) { + $dir .= '/'; // append / to allow recursion + } + + $this->setCurrentDir($path); + + $this->import($dir, null, false, $path); + } + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if ('directory' === $type) { + return true; + } + + return null === $type && is_string($resource) && '/' === substr($resource, -1); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php new file mode 100644 index 0000000..d71eecf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/FileLoader.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; +use Symfony\Component\Config\FileLocatorInterface; + +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends BaseFileLoader +{ + protected $container; + + /** + * Constructor. + * + * @param ContainerBuilder $container A ContainerBuilder instance + * @param FileLocatorInterface $locator A FileLocator instance + */ + public function __construct(ContainerBuilder $container, FileLocatorInterface $locator) + { + $this->container = $container; + + parent::__construct($locator); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php new file mode 100644 index 0000000..16ddf87 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/IniFileLoader.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * IniFileLoader loads parameters from INI files. + * + * @author Fabien Potencier + */ +class IniFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $path = $this->locator->locate($resource); + + $this->container->addResource(new FileResource($path)); + + $result = parse_ini_file($path, true); + if (false === $result || array() === $result) { + throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource)); + } + + if (isset($result['parameters']) && is_array($result['parameters'])) { + foreach ($result['parameters'] as $key => $value) { + $this->container->setParameter($key, $value); + } + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'ini' === pathinfo($resource, PATHINFO_EXTENSION); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php new file mode 100644 index 0000000..08c1d9a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\Config\Resource\FileResource; + +/** + * PhpFileLoader loads service definitions from a PHP file. + * + * The PHP file is required and the $container variable can be + * used within the file to change the container. + * + * @author Fabien Potencier + */ +class PhpFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + // the container and loader variables are exposed to the included file below + $container = $this->container; + $loader = $this; + + $path = $this->locator->locate($resource); + $this->setCurrentDir(dirname($path)); + $this->container->addResource(new FileResource($path)); + + include $path; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php new file mode 100644 index 0000000..dad4678 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -0,0 +1,566 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * XmlFileLoader loads XML files service definitions. + * + * @author Fabien Potencier + */ +class XmlFileLoader extends FileLoader +{ + const NS = 'http://symfony.com/schema/dic/services'; + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $path = $this->locator->locate($resource); + + $xml = $this->parseFileToDOM($path); + + $this->container->addResource(new FileResource($path)); + + // anonymous services + $this->processAnonymousServices($xml, $path); + + // imports + $this->parseImports($xml, $path); + + // parameters + $this->parseParameters($xml); + + // extensions + $this->loadFromExtensions($xml); + + // services + $this->parseDefinitions($xml, $path); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION); + } + + /** + * Parses parameters. + * + * @param \DOMDocument $xml + */ + private function parseParameters(\DOMDocument $xml) + { + if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { + $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter')); + } + } + + /** + * Parses imports. + * + * @param \DOMDocument $xml + * @param string $file + */ + private function parseImports(\DOMDocument $xml, $file) + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (false === $imports = $xpath->query('//container:imports/container:import')) { + return; + } + + foreach ($imports as $import) { + $this->setCurrentDir(dirname($file)); + $this->import($import->getAttribute('resource'), null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); + } + } + + /** + * Parses multiple definitions. + * + * @param \DOMDocument $xml + * @param string $file + */ + private function parseDefinitions(\DOMDocument $xml, $file) + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (false === $services = $xpath->query('//container:services/container:service')) { + return; + } + + foreach ($services as $service) { + if (null !== $definition = $this->parseDefinition($service, $file)) { + $this->container->setDefinition((string) $service->getAttribute('id'), $definition); + } + } + } + + /** + * Parses an individual Definition. + * + * @param \DOMElement $service + * @param string $file + * + * @return Definition|null + */ + private function parseDefinition(\DOMElement $service, $file) + { + if ($alias = $service->getAttribute('alias')) { + $public = true; + if ($publicAttr = $service->getAttribute('public')) { + $public = XmlUtils::phpize($publicAttr); + } + $this->container->setAlias((string) $service->getAttribute('id'), new Alias($alias, $public)); + + return; + } + + if ($parent = $service->getAttribute('parent')) { + $definition = new DefinitionDecorator($parent); + } else { + $definition = new Definition(); + } + + foreach (array('class', 'shared', 'public', 'synthetic', 'lazy', 'abstract') as $key) { + if ($value = $service->getAttribute($key)) { + $method = 'set'.str_replace('-', '', $key); + $definition->$method(XmlUtils::phpize($value)); + } + } + + if ($value = $service->getAttribute('autowire')) { + $definition->setAutowired(XmlUtils::phpize($value)); + } + + if ($files = $this->getChildren($service, 'file')) { + $definition->setFile($files[0]->nodeValue); + } + + if ($deprecated = $this->getChildren($service, 'deprecated')) { + $definition->setDeprecated(true, $deprecated[0]->nodeValue); + } + + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument')); + $definition->setProperties($this->getArgumentsAsPhp($service, 'property')); + + if ($factories = $this->getChildren($service, 'factory')) { + $factory = $factories[0]; + if ($function = $factory->getAttribute('function')) { + $definition->setFactory($function); + } else { + $factoryService = $this->getChildren($factory, 'service'); + + if (isset($factoryService[0])) { + $class = $this->parseDefinition($factoryService[0], $file); + } elseif ($childService = $factory->getAttribute('service')) { + $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); + } else { + $class = $factory->getAttribute('class'); + } + + $definition->setFactory(array($class, $factory->getAttribute('method'))); + } + } + + if ($configurators = $this->getChildren($service, 'configurator')) { + $configurator = $configurators[0]; + if ($function = $configurator->getAttribute('function')) { + $definition->setConfigurator($function); + } else { + $configuratorService = $this->getChildren($configurator, 'service'); + + if (isset($configuratorService[0])) { + $class = $this->parseDefinition($configuratorService[0], $file); + } elseif ($childService = $configurator->getAttribute('service')) { + $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false); + } else { + $class = $configurator->getAttribute('class'); + } + + $definition->setConfigurator(array($class, $configurator->getAttribute('method'))); + } + } + + foreach ($this->getChildren($service, 'call') as $call) { + $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument')); + } + + foreach ($this->getChildren($service, 'tag') as $tag) { + $parameters = array(); + foreach ($tag->attributes as $name => $node) { + if ('name' === $name) { + continue; + } + + if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { + $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); + } + // keep not normalized key + $parameters[$name] = XmlUtils::phpize($node->nodeValue); + } + + $definition->addTag($tag->getAttribute('name'), $parameters); + } + + foreach ($this->getChildren($service, 'autowiring-type') as $type) { + $definition->addAutowiringType($type->textContent); + } + + if ($value = $service->getAttribute('decorates')) { + $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; + $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; + $definition->setDecoratedService($value, $renameId, $priority); + } + + return $definition; + } + + /** + * Parses a XML file to a \DOMDocument. + * + * @param string $file Path to a file + * + * @return \DOMDocument + * + * @throws InvalidArgumentException When loading of XML file returns error + */ + private function parseFileToDOM($file) + { + try { + $dom = XmlUtils::loadFile($file, array($this, 'validateSchema')); + } catch (\InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e); + } + + $this->validateExtensions($dom, $file); + + return $dom; + } + + /** + * Processes anonymous services. + * + * @param \DOMDocument $xml + * @param string $file + */ + private function processAnonymousServices(\DOMDocument $xml, $file) + { + $definitions = array(); + $count = 0; + + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + // anonymous services as arguments/properties + if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]')) { + foreach ($nodes as $node) { + // give it a unique name + $id = sprintf('%s_%d', hash('sha256', $file), ++$count); + $node->setAttribute('id', $id); + + if ($services = $this->getChildren($node, 'service')) { + $definitions[$id] = array($services[0], $file, false); + $services[0]->setAttribute('id', $id); + } + } + } + + // anonymous services "in the wild" + if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { + foreach ($nodes as $node) { + // give it a unique name + $id = sprintf('%s_%d', hash('sha256', $file), ++$count); + $node->setAttribute('id', $id); + + if ($services = $this->getChildren($node, 'service')) { + $definitions[$id] = array($node, $file, true); + $services[0]->setAttribute('id', $id); + } + } + } + + // resolve definitions + krsort($definitions); + foreach ($definitions as $id => list($domElement, $file, $wild)) { + // anonymous services are always private + // we could not use the constant false here, because of XML parsing + $domElement->setAttribute('public', 'false'); + + if (null !== $definition = $this->parseDefinition($domElement, $file)) { + $this->container->setDefinition($id, $definition); + } + + if (true === $wild) { + $tmpDomElement = new \DOMElement('_services', null, self::NS); + $domElement->parentNode->replaceChild($tmpDomElement, $domElement); + $tmpDomElement->setAttribute('id', $id); + } else { + $domElement->parentNode->removeChild($domElement); + } + } + } + + /** + * Returns arguments as valid php types. + * + * @param \DOMElement $node + * @param string $name + * @param bool $lowercase + * + * @return mixed + */ + private function getArgumentsAsPhp(\DOMElement $node, $name, $lowercase = true) + { + $arguments = array(); + foreach ($this->getChildren($node, $name) as $arg) { + if ($arg->hasAttribute('name')) { + $arg->setAttribute('key', $arg->getAttribute('name')); + } + + if (!$arg->hasAttribute('key')) { + $key = !$arguments ? 0 : max(array_keys($arguments)) + 1; + } else { + $key = $arg->getAttribute('key'); + } + + // parameter keys are case insensitive + if ('parameter' == $name && $lowercase) { + $key = strtolower($key); + } + + // this is used by DefinitionDecorator to overwrite a specific + // argument of the parent definition + if ($arg->hasAttribute('index')) { + $key = 'index_'.$arg->getAttribute('index'); + } + + switch ($arg->getAttribute('type')) { + case 'service': + $onInvalid = $arg->getAttribute('on-invalid'); + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + if ('ignore' == $onInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } elseif ('null' == $onInvalid) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + } + + if ($strict = $arg->getAttribute('strict')) { + $strict = XmlUtils::phpize($strict); + } else { + $strict = true; + } + + $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior, $strict); + break; + case 'expression': + $arguments[$key] = new Expression($arg->nodeValue); + break; + case 'collection': + $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, false); + break; + case 'string': + $arguments[$key] = $arg->nodeValue; + break; + case 'constant': + $arguments[$key] = constant($arg->nodeValue); + break; + default: + $arguments[$key] = XmlUtils::phpize($arg->nodeValue); + } + } + + return $arguments; + } + + /** + * Get child elements by name. + * + * @param \DOMNode $node + * @param mixed $name + * + * @return array + */ + private function getChildren(\DOMNode $node, $name) + { + $children = array(); + foreach ($node->childNodes as $child) { + if ($child instanceof \DOMElement && $child->localName === $name && $child->namespaceURI === self::NS) { + $children[] = $child; + } + } + + return $children; + } + + /** + * Validates a documents XML schema. + * + * @param \DOMDocument $dom + * + * @return bool + * + * @throws RuntimeException When extension references a non-existent XSD file + */ + public function validateSchema(\DOMDocument $dom) + { + $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')); + + if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { + $items = preg_split('/\s+/', $element); + for ($i = 0, $nb = count($items); $i < $nb; $i += 2) { + if (!$this->container->hasExtension($items[$i])) { + continue; + } + + if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) { + $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); + + if (!is_file($path)) { + throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', get_class($extension), $path)); + } + + $schemaLocations[$items[$i]] = $path; + } + } + } + + $tmpfiles = array(); + $imports = ''; + foreach ($schemaLocations as $namespace => $location) { + $parts = explode('/', $location); + if (0 === stripos($location, 'phar://')) { + $tmpfile = tempnam(sys_get_temp_dir(), 'sf2'); + if ($tmpfile) { + copy($location, $tmpfile); + $tmpfiles[] = $tmpfile; + $parts = explode('/', str_replace('\\', '/', $tmpfile)); + } + } + $drive = '\\' === DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; + $location = 'file:///'.$drive.implode('/', array_map('rawurlencode', $parts)); + + $imports .= sprintf(' '."\n", $namespace, $location); + } + + $source = << + + + +$imports + +EOF + ; + + $valid = @$dom->schemaValidateSource($source); + + foreach ($tmpfiles as $tmpfile) { + @unlink($tmpfile); + } + + return $valid; + } + + /** + * Validates an extension. + * + * @param \DOMDocument $dom + * @param string $file + * + * @throws InvalidArgumentException When no extension is found corresponding to a tag + */ + private function validateExtensions(\DOMDocument $dom, $file) + { + foreach ($dom->documentElement->childNodes as $node) { + if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) { + continue; + } + + // can it be handled by an extension? + if (!$this->container->hasExtension($node->namespaceURI)) { + $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf( + 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', + $node->tagName, + $file, + $node->namespaceURI, + $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' + )); + } + } + } + + /** + * Loads from an extension. + * + * @param \DOMDocument $xml + */ + private function loadFromExtensions(\DOMDocument $xml) + { + foreach ($xml->documentElement->childNodes as $node) { + if (!$node instanceof \DOMElement || $node->namespaceURI === self::NS) { + continue; + } + + $values = static::convertDomElementToArray($node); + if (!is_array($values)) { + $values = array(); + } + + $this->container->loadFromExtension($node->namespaceURI, $values); + } + } + + /** + * Converts a \DomElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DomElement $element A \DomElement instance + * + * @return array A PHP array + */ + public static function convertDomElementToArray(\DomElement $element) + { + return XmlUtils::convertDomElementToArray($element); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php new file mode 100644 index 0000000..7fb8c17 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -0,0 +1,435 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * YamlFileLoader loads YAML files service definitions. + * + * The YAML format does not support anonymous services (cf. the XML loader). + * + * @author Fabien Potencier + */ +class YamlFileLoader extends FileLoader +{ + private $yamlParser; + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $path = $this->locator->locate($resource); + + $content = $this->loadFile($path); + + $this->container->addResource(new FileResource($path)); + + // empty file + if (null === $content) { + return; + } + + // imports + $this->parseImports($content, $path); + + // parameters + if (isset($content['parameters'])) { + if (!is_array($content['parameters'])) { + throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $resource)); + } + + foreach ($content['parameters'] as $key => $value) { + $this->container->setParameter($key, $this->resolveServices($value)); + } + } + + // extensions + $this->loadFromExtensions($content); + + // services + $this->parseDefinitions($content, $resource); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yml', 'yaml'), true); + } + + /** + * Parses all imports. + * + * @param array $content + * @param string $file + */ + private function parseImports($content, $file) + { + if (!isset($content['imports'])) { + return; + } + + if (!is_array($content['imports'])) { + throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file)); + } + + foreach ($content['imports'] as $import) { + if (!is_array($import)) { + throw new InvalidArgumentException(sprintf('The values in the "imports" key should be arrays in %s. Check your YAML syntax.', $file)); + } + + $this->setCurrentDir(dirname($file)); + $this->import($import['resource'], null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file); + } + } + + /** + * Parses definitions. + * + * @param array $content + * @param string $file + */ + private function parseDefinitions($content, $file) + { + if (!isset($content['services'])) { + return; + } + + if (!is_array($content['services'])) { + throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file)); + } + + foreach ($content['services'] as $id => $service) { + $this->parseDefinition($id, $service, $file); + } + } + + /** + * Parses a definition. + * + * @param string $id + * @param array $service + * @param string $file + * + * @throws InvalidArgumentException When tags are invalid + */ + private function parseDefinition($id, $service, $file) + { + if (is_string($service) && 0 === strpos($service, '@')) { + $this->container->setAlias($id, substr($service, 1)); + + return; + } + + if (!is_array($service)) { + throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', gettype($service), $id, $file)); + } + + if (isset($service['alias'])) { + $public = !array_key_exists('public', $service) || (bool) $service['public']; + $this->container->setAlias($id, new Alias($service['alias'], $public)); + + return; + } + + if (isset($service['parent'])) { + $definition = new DefinitionDecorator($service['parent']); + } else { + $definition = new Definition(); + } + + if (isset($service['class'])) { + $definition->setClass($service['class']); + } + + if (isset($service['shared'])) { + $definition->setShared($service['shared']); + } + + if (isset($service['synthetic'])) { + $definition->setSynthetic($service['synthetic']); + } + + if (isset($service['lazy'])) { + $definition->setLazy($service['lazy']); + } + + if (isset($service['public'])) { + $definition->setPublic($service['public']); + } + + if (isset($service['abstract'])) { + $definition->setAbstract($service['abstract']); + } + + if (array_key_exists('deprecated', $service)) { + $definition->setDeprecated(true, $service['deprecated']); + } + + if (isset($service['factory'])) { + if (is_string($service['factory'])) { + if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) { + $parts = explode(':', $service['factory']); + $definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1])); + } else { + $definition->setFactory($service['factory']); + } + } else { + $definition->setFactory(array($this->resolveServices($service['factory'][0]), $service['factory'][1])); + } + } + + if (isset($service['file'])) { + $definition->setFile($service['file']); + } + + if (isset($service['arguments'])) { + $definition->setArguments($this->resolveServices($service['arguments'])); + } + + if (isset($service['properties'])) { + $definition->setProperties($this->resolveServices($service['properties'])); + } + + if (isset($service['configurator'])) { + if (is_string($service['configurator'])) { + $definition->setConfigurator($service['configurator']); + } else { + $definition->setConfigurator(array($this->resolveServices($service['configurator'][0]), $service['configurator'][1])); + } + } + + if (isset($service['calls'])) { + if (!is_array($service['calls'])) { + throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + foreach ($service['calls'] as $call) { + if (isset($call['method'])) { + $method = $call['method']; + $args = isset($call['arguments']) ? $this->resolveServices($call['arguments']) : array(); + } else { + $method = $call[0]; + $args = isset($call[1]) ? $this->resolveServices($call[1]) : array(); + } + + $definition->addMethodCall($method, $args); + } + } + + if (isset($service['tags'])) { + if (!is_array($service['tags'])) { + throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + foreach ($service['tags'] as $tag) { + if (!is_array($tag)) { + throw new InvalidArgumentException(sprintf('A "tags" entry must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + if (!isset($tag['name'])) { + throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file)); + } + + $name = $tag['name']; + unset($tag['name']); + + foreach ($tag as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file)); + } + } + + $definition->addTag($name, $tag); + } + } + + if (isset($service['decorates'])) { + $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null; + $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0; + $definition->setDecoratedService($service['decorates'], $renameId, $priority); + } + + if (isset($service['autowire'])) { + $definition->setAutowired($service['autowire']); + } + + if (isset($service['autowiring_types'])) { + if (is_string($service['autowiring_types'])) { + $definition->addAutowiringType($service['autowiring_types']); + } else { + if (!is_array($service['autowiring_types'])) { + throw new InvalidArgumentException(sprintf('Parameter "autowiring_types" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + foreach ($service['autowiring_types'] as $autowiringType) { + if (!is_string($autowiringType)) { + throw new InvalidArgumentException(sprintf('A "autowiring_types" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + $definition->addAutowiringType($autowiringType); + } + } + } + + $this->container->setDefinition($id, $definition); + } + + /** + * Loads a YAML file. + * + * @param string $file + * + * @return array The file content + * + * @throws InvalidArgumentException when the given file is not a local file or when it does not exist + */ + protected function loadFile($file) + { + if (!class_exists('Symfony\Component\Yaml\Parser')) { + throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.'); + } + + if (!stream_is_local($file)) { + throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file)); + } + + if (!file_exists($file)) { + throw new InvalidArgumentException(sprintf('The service file "%s" is not valid.', $file)); + } + + if (null === $this->yamlParser) { + $this->yamlParser = new YamlParser(); + } + + try { + $configuration = $this->yamlParser->parse(file_get_contents($file)); + } catch (ParseException $e) { + throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e); + } + + return $this->validate($configuration, $file); + } + + /** + * Validates a YAML file. + * + * @param mixed $content + * @param string $file + * + * @return array + * + * @throws InvalidArgumentException When service file is not valid + */ + private function validate($content, $file) + { + if (null === $content) { + return $content; + } + + if (!is_array($content)) { + throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file)); + } + + foreach ($content as $namespace => $data) { + if (in_array($namespace, array('imports', 'parameters', 'services'))) { + continue; + } + + if (!$this->container->hasExtension($namespace)) { + $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf( + 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', + $namespace, + $file, + $namespace, + $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' + )); + } + } + + return $content; + } + + /** + * Resolves services. + * + * @param string|array $value + * + * @return array|string|Reference + */ + private function resolveServices($value) + { + if (is_array($value)) { + $value = array_map(array($this, 'resolveServices'), $value); + } elseif (is_string($value) && 0 === strpos($value, '@=')) { + return new Expression(substr($value, 2)); + } elseif (is_string($value) && 0 === strpos($value, '@')) { + if (0 === strpos($value, '@@')) { + $value = substr($value, 1); + $invalidBehavior = null; + } elseif (0 === strpos($value, '@?')) { + $value = substr($value, 2); + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } else { + $value = substr($value, 1); + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + } + + if ('=' === substr($value, -1)) { + $value = substr($value, 0, -1); + $strict = false; + } else { + $strict = true; + } + + if (null !== $invalidBehavior) { + $value = new Reference($value, $invalidBehavior, $strict); + } + } + + return $value; + } + + /** + * Loads from Extensions. + * + * @param array $content + */ + private function loadFromExtensions($content) + { + foreach ($content as $namespace => $values) { + if (in_array($namespace, array('imports', 'parameters', 'services'))) { + continue; + } + + if (!is_array($values)) { + $values = array(); + } + + $this->container->loadFromExtension($namespace, $values); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd new file mode 100644 index 0000000..ad3f57f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Parameter.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Parameter.php new file mode 100644 index 0000000..5431ed8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Parameter.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * Parameter represents a parameter reference. + * + * @author Fabien Potencier + */ +class Parameter +{ + private $id; + + /** + * Constructor. + * + * @param string $id The parameter key + */ + public function __construct($id) + { + $this->id = $id; + } + + /** + * __toString. + * + * @return string The parameter key + */ + public function __toString() + { + return (string) $this->id; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php new file mode 100644 index 0000000..d9fe9ec --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * Holds read-only parameters. + * + * @author Fabien Potencier + */ +class FrozenParameterBag extends ParameterBag +{ + /** + * Constructor. + * + * For performance reasons, the constructor assumes that + * all keys are already lowercased. + * + * This is always the case when used internally. + * + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + $this->resolved = true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + throw new LogicException('Impossible to call clear() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function add(array $parameters) + { + throw new LogicException('Impossible to call add() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + throw new LogicException('Impossible to call remove() on a frozen ParameterBag.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php new file mode 100644 index 0000000..da211aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Holds parameters. + * + * @author Fabien Potencier + */ +class ParameterBag implements ParameterBagInterface +{ + protected $parameters = array(); + protected $resolved = false; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->add($parameters); + } + + /** + * Clears all parameters. + */ + public function clear() + { + $this->parameters = array(); + } + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + */ + public function add(array $parameters) + { + foreach ($parameters as $key => $value) { + $this->parameters[strtolower($key)] = $value; + } + } + + /** + * Gets the service container parameters. + * + * @return array An array of parameters + */ + public function all() + { + return $this->parameters; + } + + /** + * Gets a service container parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws ParameterNotFoundException if the parameter is not defined + */ + public function get($name) + { + $name = strtolower($name); + + if (!array_key_exists($name, $this->parameters)) { + if (!$name) { + throw new ParameterNotFoundException($name); + } + + $alternatives = array(); + foreach ($this->parameters as $key => $parameterValue) { + $lev = levenshtein($name, $key); + if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) { + $alternatives[] = $key; + } + } + + throw new ParameterNotFoundException($name, null, null, null, $alternatives); + } + + return $this->parameters[$name]; + } + + /** + * Sets a service container parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + */ + public function set($name, $value) + { + $this->parameters[strtolower($name)] = $value; + } + + /** + * Returns true if a parameter name is defined. + * + * @param string $name The parameter name + * + * @return bool true if the parameter name is defined, false otherwise + */ + public function has($name) + { + return array_key_exists(strtolower($name), $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $name The parameter name + */ + public function remove($name) + { + unset($this->parameters[strtolower($name)]); + } + + /** + * Replaces parameter placeholders (%name%) by their values for all parameters. + */ + public function resolve() + { + if ($this->resolved) { + return; + } + + $parameters = array(); + foreach ($this->parameters as $key => $value) { + try { + $value = $this->resolveValue($value); + $parameters[$key] = $this->unescapeValue($value); + } catch (ParameterNotFoundException $e) { + $e->setSourceKey($key); + + throw $e; + } + } + + $this->parameters = $parameters; + $this->resolved = true; + } + + /** + * Replaces parameter placeholders (%name%) by their values. + * + * @param mixed $value A value + * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) + * + * @return mixed The resolved value + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + * @throws ParameterCircularReferenceException if a circular reference if detected + * @throws RuntimeException when a given parameter has a type problem. + */ + public function resolveValue($value, array $resolving = array()) + { + if (is_array($value)) { + $args = array(); + foreach ($value as $k => $v) { + $args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving); + } + + return $args; + } + + if (!is_string($value)) { + return $value; + } + + return $this->resolveString($value, $resolving); + } + + /** + * Resolves parameters inside a string. + * + * @param string $value The string to resolve + * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) + * + * @return string The resolved string + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + * @throws ParameterCircularReferenceException if a circular reference if detected + * @throws RuntimeException when a given parameter has a type problem. + */ + public function resolveString($value, array $resolving = array()) + { + // we do this to deal with non string values (Boolean, integer, ...) + // as the preg_replace_callback throw an exception when trying + // a non-string in a parameter value + if (preg_match('/^%([^%\s]+)%$/', $value, $match)) { + $key = strtolower($match[1]); + + if (isset($resolving[$key])) { + throw new ParameterCircularReferenceException(array_keys($resolving)); + } + + $resolving[$key] = true; + + return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving); + } + + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) { + // skip %% + if (!isset($match[1])) { + return '%%'; + } + + $key = strtolower($match[1]); + if (isset($resolving[$key])) { + throw new ParameterCircularReferenceException(array_keys($resolving)); + } + + $resolved = $this->get($key); + + if (!is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type %s inside string value "%s".', $key, gettype($resolved), $value)); + } + + $resolved = (string) $resolved; + $resolving[$key] = true; + + return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); + }, $value); + } + + public function isResolved() + { + return $this->resolved; + } + + /** + * {@inheritdoc} + */ + public function escapeValue($value) + { + if (is_string($value)) { + return str_replace('%', '%%', $value); + } + + if (is_array($value)) { + $result = array(); + foreach ($value as $k => $v) { + $result[$k] = $this->escapeValue($v); + } + + return $result; + } + + return $value; + } + + public function unescapeValue($value) + { + if (is_string($value)) { + return str_replace('%%', '%', $value); + } + + if (is_array($value)) { + $result = array(); + foreach ($value as $k => $v) { + $result[$k] = $this->unescapeValue($v); + } + + return $result; + } + + return $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php new file mode 100644 index 0000000..7386df0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBagInterface.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * ParameterBagInterface. + * + * @author Fabien Potencier + */ +interface ParameterBagInterface +{ + /** + * Clears all parameters. + * + * @throws LogicException if the ParameterBagInterface can not be cleared + */ + public function clear(); + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + * + * @throws LogicException if the parameter can not be added + */ + public function add(array $parameters); + + /** + * Gets the service container parameters. + * + * @return array An array of parameters + */ + public function all(); + + /** + * Gets a service container parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws ParameterNotFoundException if the parameter is not defined + */ + public function get($name); + + /** + * Removes a parameter. + * + * @param string $name The parameter name + */ + public function remove($name); + + /** + * Sets a service container parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @throws LogicException if the parameter can not be set + */ + public function set($name, $value); + + /** + * Returns true if a parameter name is defined. + * + * @param string $name The parameter name + * + * @return bool true if the parameter name is defined, false otherwise + */ + public function has($name); + + /** + * Replaces parameter placeholders (%name%) by their values for all parameters. + */ + public function resolve(); + + /** + * Replaces parameter placeholders (%name%) by their values. + * + * @param mixed $value A value + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + */ + public function resolveValue($value); + + /** + * Escape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function escapeValue($value); + + /** + * Unescape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function unescapeValue($value); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/README.md b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/README.md new file mode 100644 index 0000000..19a1142 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/README.md @@ -0,0 +1,80 @@ +DependencyInjection Component +============================= + +DependencyInjection manages your services via a robust and flexible Dependency +Injection Container. + +Here is a simple example that shows how to register services and parameters: + +```php +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +$sc = new ContainerBuilder(); +$sc + ->register('foo', '%foo.class%') + ->addArgument(new Reference('bar')) +; +$sc->setParameter('foo.class', 'Foo'); + +$sc->get('foo'); +``` + +Method Calls (Setter Injection): + +```php +$sc = new ContainerBuilder(); + +$sc + ->register('bar', '%bar.class%') + ->addMethodCall('setFoo', array(new Reference('foo'))) +; +$sc->setParameter('bar.class', 'Bar'); + +$sc->get('bar'); +``` + +Factory Class: + +If your service is retrieved by calling a static method: + +```php +$sc = new ContainerBuilder(); + +$sc + ->register('bar', '%bar.class%') + ->setFactory(array('%bar.class%', 'getInstance')) + ->addArgument('Aarrg!!!') +; +$sc->setParameter('bar.class', 'Bar'); + +$sc->get('bar'); +``` + +File Include: + +For some services, especially those that are difficult or impossible to +autoload, you may need the container to include a file before +instantiating your class. + +```php +$sc = new ContainerBuilder(); + +$sc + ->register('bar', '%bar.class%') + ->setFile('/path/to/file') + ->addArgument('Aarrg!!!') +; +$sc->setParameter('bar.class', 'Bar'); + +$sc->get('bar'); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/DependencyInjection/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Reference.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Reference.php new file mode 100644 index 0000000..89c95d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Reference.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * Reference represents a service reference. + * + * @author Fabien Potencier + */ +class Reference +{ + private $id; + private $invalidBehavior; + + /** + * Constructor. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @see Container + */ + public function __construct($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + { + $this->id = strtolower($id); + $this->invalidBehavior = $invalidBehavior; + } + + /** + * __toString. + * + * @return string The service identifier + */ + public function __toString() + { + return $this->id; + } + + /** + * Returns the behavior to be used when the service does not exist. + * + * @return int + */ + public function getInvalidBehavior() + { + return $this->invalidBehavior; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php new file mode 100644 index 0000000..b74e676 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/ResettableContainerInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * ResettableContainerInterface defines additional resetting functionality + * for containers, allowing to release shared services when the container is + * not needed anymore. + * + * @author Christophe Coevoet + */ +interface ResettableContainerInterface extends ContainerInterface +{ + /** + * Resets shared services from the container. + * + * The container is not intended to be used again after being reset in a normal workflow. This method is + * meant as a way to release references for ref-counting. + * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service. + */ + public function reset(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php new file mode 100644 index 0000000..90b297f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/TaggedContainerInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * TaggedContainerInterface is the interface implemented when a container knows how to deals with tags. + * + * @author Fabien Potencier + */ +interface TaggedContainerInterface extends ContainerInterface +{ + /** + * Returns service ids for a given tag. + * + * @param string $name The tag name + * + * @return array An array of tags + */ + public function findTaggedServiceIds($name); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php new file mode 100644 index 0000000..04fe7c2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class AnalyzeServiceReferencesPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $a = $container + ->register('a') + ->addArgument($ref1 = new Reference('b')) + ; + + $b = $container + ->register('b') + ->addMethodCall('setA', array($ref2 = new Reference('a'))) + ; + + $c = $container + ->register('c') + ->addArgument($ref3 = new Reference('a')) + ->addArgument($ref4 = new Reference('b')) + ; + + $d = $container + ->register('d') + ->setProperty('foo', $ref5 = new Reference('b')) + ; + + $e = $container + ->register('e') + ->setConfigurator(array($ref6 = new Reference('b'), 'methodName')) + ; + + $graph = $this->process($container); + + $this->assertCount(4, $edges = $graph->getNode('b')->getInEdges()); + + $this->assertSame($ref1, $edges[0]->getValue()); + $this->assertSame($ref4, $edges[1]->getValue()); + $this->assertSame($ref5, $edges[2]->getValue()); + $this->assertSame($ref6, $edges[3]->getValue()); + } + + public function testProcessDetectsReferencesFromInlinedDefinitions() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + + $container + ->register('b') + ->addArgument(new Definition(null, array($ref = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); + $this->assertSame($ref, $refs[0]->getValue()); + } + + public function testProcessDetectsReferencesFromInlinedFactoryDefinitions() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + + $factory = new Definition(); + $factory->setFactory(array(new Reference('a'), 'a')); + + $container + ->register('b') + ->addArgument($factory) + ; + + $graph = $this->process($container); + + $this->assertTrue($graph->hasNode('a')); + $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); + } + + public function testProcessDoesNotSaveDuplicateReferences() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + $container + ->register('b') + ->addArgument(new Definition(null, array($ref1 = new Reference('a')))) + ->addArgument(new Definition(null, array($ref2 = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertCount(2, $graph->getNode('a')->getInEdges()); + } + + public function testProcessDetectsFactoryReferences() + { + $container = new ContainerBuilder(); + + $container + ->register('foo', 'stdClass') + ->setFactory(array('stdClass', 'getInstance')); + + $container + ->register('bar', 'stdClass') + ->setFactory(array(new Reference('foo'), 'getInstance')); + + $graph = $this->process($container); + + $this->assertTrue($graph->hasNode('foo')); + $this->assertCount(1, $graph->getNode('foo')->getInEdges()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new RepeatedPass(array(new AnalyzeServiceReferencesPass())); + $pass->process($container); + + return $container->getCompiler()->getServiceReferenceGraph(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutoAliasServicePassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutoAliasServicePassTest.php new file mode 100644 index 0000000..e3aba6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutoAliasServicePassTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\AutoAliasServicePass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class AutoAliasServicePassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException + */ + public function testProcessWithMissingParameter() + { + $container = new ContainerBuilder(); + + $container->register('example') + ->addTag('auto_alias', array('format' => '%non_existing%.example')); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testProcessWithMissingFormat() + { + $container = new ContainerBuilder(); + + $container->register('example') + ->addTag('auto_alias', array()); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + } + + public function testProcessWithNonExistingAlias() + { + $container = new ContainerBuilder(); + + $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') + ->addTag('auto_alias', array('format' => '%existing%.example')); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + + $this->assertEquals('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->getDefinition('example')->getClass()); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->get('example')); + } + + public function testProcessWithExistingAlias() + { + $container = new ContainerBuilder(); + + $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') + ->addTag('auto_alias', array('format' => '%existing%.example')); + + $container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql'); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + + $this->assertTrue($container->hasAlias('example')); + $this->assertEquals('mysql.example', $container->getAlias('example')); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql', $container->get('example')); + } + + public function testProcessWithManualAlias() + { + $container = new ContainerBuilder(); + + $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') + ->addTag('auto_alias', array('format' => '%existing%.example')); + + $container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql'); + $container->register('mariadb.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariadb'); + $container->setAlias('example', 'mariadb.example'); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + + $this->assertTrue($container->hasAlias('example')); + $this->assertEquals('mariadb.example', $container->getAlias('example')); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb', $container->get('example')); + } +} + +class ServiceClassDefault +{ +} + +class ServiceClassMysql extends ServiceClassDefault +{ +} + +class ServiceClassMariaDb extends ServiceClassMysql +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php new file mode 100644 index 0000000..f1ed72e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -0,0 +1,399 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\AutowirePass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Kévin Dunglas + */ +class AutowirePassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container->register('foo', __NAMESPACE__.'\Foo'); + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('bar')->getArguments()); + $this->assertEquals('foo', (string) $container->getDefinition('bar')->getArgument(0)); + } + + public function testProcessAutowireParent() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $cDefinition = $container->register('c', __NAMESPACE__.'\C'); + $cDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('c')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('c')->getArgument(0)); + } + + public function testProcessAutowireInterface() + { + $container = new ContainerBuilder(); + + $container->register('f', __NAMESPACE__.'\F'); + $gDefinition = $container->register('g', __NAMESPACE__.'\G'); + $gDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(2, $container->getDefinition('g')->getArguments()); + $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(0)); + $this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(1)); + } + + public function testCompleteExistingDefinition() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $container->register('f', __NAMESPACE__.'\F'); + $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b')); + $hDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(2, $container->getDefinition('h')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); + $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1)); + } + + public function testCompleteExistingDefinitionWithNotDefinedArguments() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $container->register('f', __NAMESPACE__.'\F'); + $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument(''); + $hDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(2, $container->getDefinition('h')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); + $this->assertEquals('f', (string) $container->getDefinition('h')->getArgument(1)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". + */ + public function testTypeCollision() + { + $container = new ContainerBuilder(); + + $container->register('c1', __NAMESPACE__.'\CollisionA'); + $container->register('c2', __NAMESPACE__.'\CollisionB'); + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". + */ + public function testTypeNotGuessable() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". + */ + public function testTypeNotGuessableWithSubclass() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\B'); + $container->register('a2', __NAMESPACE__.'\B'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testTypeNotGuessableWithTypeSet() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo')->addAutowiringType(__NAMESPACE__.'\Foo'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('a')->getArguments()); + $this->assertEquals('a2', (string) $container->getDefinition('a')->getArgument(0)); + } + + public function testWithTypeSet() + { + $container = new ContainerBuilder(); + + $container->register('c1', __NAMESPACE__.'\CollisionA'); + $container->register('c2', __NAMESPACE__.'\CollisionB')->addAutowiringType(__NAMESPACE__.'\CollisionInterface'); + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('a')->getArguments()); + $this->assertEquals('c2', (string) $container->getDefinition('a')->getArgument(0)); + } + + public function testCreateDefinition() + { + $container = new ContainerBuilder(); + + $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls'); + $coopTilleulsDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('coop_tilleuls')->getArguments()); + $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas', $container->getDefinition('coop_tilleuls')->getArgument(0)); + + $dunglasDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\dunglas'); + $this->assertEquals(__NAMESPACE__.'\Dunglas', $dunglasDefinition->getClass()); + $this->assertFalse($dunglasDefinition->isPublic()); + $this->assertCount(1, $dunglasDefinition->getArguments()); + $this->assertEquals('autowired.symfony\component\dependencyinjection\tests\compiler\lille', $dunglasDefinition->getArgument(0)); + + $lilleDefinition = $container->getDefinition('autowired.symfony\component\dependencyinjection\tests\compiler\lille'); + $this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass()); + } + + public function testResolveParameter() + { + $container = new ContainerBuilder(); + + $container->setParameter('class_name', __NAMESPACE__.'\Foo'); + $container->register('foo', '%class_name%'); + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertEquals('foo', $container->getDefinition('bar')->getArgument(0)); + } + + public function testOptionalParameter() + { + $container = new ContainerBuilder(); + + $container->register('a', __NAMESPACE__.'\A'); + $container->register('foo', __NAMESPACE__.'\Foo'); + $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter'); + $optDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $definition = $container->getDefinition('opt'); + $this->assertNull($definition->getArgument(0)); + $this->assertEquals('a', $definition->getArgument(1)); + $this->assertEquals('foo', $definition->getArgument(2)); + } + + public function testDontTriggerAutowiring() + { + $container = new ContainerBuilder(); + + $container->register('foo', __NAMESPACE__.'\Foo'); + $container->register('bar', __NAMESPACE__.'\Bar'); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(0, $container->getDefinition('bar')->getArguments()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot autowire argument 2 for Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument because the type-hinted class does not exist (Class Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass does not exist). + */ + public function testClassNotFoundThrowsException() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testDontUseAbstractServices() + { + $container = new ContainerBuilder(); + + $container->register('abstract_foo', __NAMESPACE__.'\Foo')->setAbstract(true); + $container->register('foo', __NAMESPACE__.'\Foo'); + $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $arguments = $container->getDefinition('bar')->getArguments(); + $this->assertSame('foo', (string) $arguments[0]); + } +} + +class Foo +{ +} + +class Bar +{ + public function __construct(Foo $foo) + { + } +} + +class A +{ +} + +class B extends A +{ +} + +class C +{ + public function __construct(A $a) + { + } +} + +interface DInterface +{ +} + +interface EInterface extends DInterface +{ +} + +class F implements EInterface +{ +} + +class G +{ + public function __construct(DInterface $d, EInterface $e) + { + } +} + +class H +{ + public function __construct(B $b, DInterface $d) + { + } +} + +interface CollisionInterface +{ +} + +class CollisionA implements CollisionInterface +{ +} + +class CollisionB implements CollisionInterface +{ +} + +class CannotBeAutowired +{ + public function __construct(CollisionInterface $collision) + { + } +} + +class Lille +{ +} + +class Dunglas +{ + public function __construct(Lille $l) + { + } +} + +class LesTilleuls +{ + public function __construct(Dunglas $k) + { + } +} + +class OptionalParameter +{ + public function __construct(CollisionInterface $c = null, A $a, Foo $f = null) + { + } +} + +class BadTypeHintedArgument +{ + public function __construct(Dunglas $k, NotARealClass $r) + { + } +} +class NotGuessableArgument +{ + public function __construct(Foo $k) + { + } +} +class NotGuessableArgumentForSubclass +{ + public function __construct(A $k) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php new file mode 100644 index 0000000..55351e5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckCircularReferencesPassTest.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CheckCircularReferencesPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('a')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessWithAliases() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->setAlias('b', 'c'); + $container->setAlias('c', 'a'); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessWithFactory() + { + $container = new ContainerBuilder(); + + $container + ->register('a', 'stdClass') + ->setFactory(array(new Reference('b'), 'getInstance')); + + $container + ->register('b', 'stdClass') + ->setFactory(array(new Reference('a'), 'getInstance')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessDetectsIndirectCircularReference() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('c')); + $container->register('c')->addArgument(new Reference('a')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessDetectsIndirectCircularReferenceWithFactory() + { + $container = new ContainerBuilder(); + + $container->register('a')->addArgument(new Reference('b')); + + $container + ->register('b', 'stdClass') + ->setFactory(array(new Reference('c'), 'getInstance')); + + $container->register('c')->addArgument(new Reference('a')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testDeepCircularReference() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('c')); + $container->register('c')->addArgument(new Reference('b')); + + $this->process($container); + } + + public function testProcessIgnoresMethodCalls() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addMethodCall('setA', array(new Reference('a'))); + + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $compiler = new Compiler(); + $passConfig = $compiler->getPassConfig(); + $passConfig->setOptimizationPasses(array( + new AnalyzeServiceReferencesPass(true), + new CheckCircularReferencesPass(), + )); + $passConfig->setRemovingPasses(array()); + + $compiler->compile($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php new file mode 100644 index 0000000..7120947 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CheckDefinitionValidityPass; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testProcessDetectsSyntheticNonPublicDefinitions() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(true)->setPublic(false); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testProcessDetectsNonSyntheticNonAbstractDefinitionWithoutClass() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(false)->setAbstract(false); + + $this->process($container); + } + + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a', 'class'); + $container->register('b', 'class')->setSynthetic(true)->setPublic(true); + $container->register('c', 'class')->setAbstract(true); + $container->register('d', 'class')->setSynthetic(true); + + $this->process($container); + } + + public function testValidTags() + { + $container = new ContainerBuilder(); + $container->register('a', 'class')->addTag('foo', array('bar' => 'baz')); + $container->register('b', 'class')->addTag('foo', array('bar' => null)); + $container->register('c', 'class')->addTag('foo', array('bar' => 1)); + $container->register('d', 'class')->addTag('foo', array('bar' => 1.1)); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testInvalidTags() + { + $container = new ContainerBuilder(); + $container->register('a', 'class')->addTag('foo', array('bar' => array('baz' => 'baz'))); + + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $pass = new CheckDefinitionValidityPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php new file mode 100644 index 0000000..18b605b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CheckExceptionOnInvalidReferenceBehaviorPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ; + $container->register('b', '\stdClass'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + */ + public function testProcessThrowsExceptionOnInvalidReference() + { + $container = new ContainerBuilder(); + + $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ; + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + */ + public function testProcessThrowsExceptionOnInvalidReferenceFromInlinedDefinition() + { + $container = new ContainerBuilder(); + + $def = new Definition(); + $def->addArgument(new Reference('b')); + + $container + ->register('a', '\stdClass') + ->addArgument($def) + ; + + $this->process($container); + } + + private function process(ContainerBuilder $container) + { + $pass = new CheckExceptionOnInvalidReferenceBehaviorPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php new file mode 100644 index 0000000..5b10ae8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckReferenceValidityPassTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CheckReferenceValidityPass; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CheckReferenceValidityPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsReferenceToAbstractDefinition() + { + $container = new ContainerBuilder(); + + $container->register('a')->setAbstract(true); + $container->register('b')->addArgument(new Reference('a')); + + $this->process($container); + } + + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b'); + + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $pass = new CheckReferenceValidityPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php new file mode 100644 index 0000000..2ffa767 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass; + +class DecoratorServicePassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcessWithoutAlias() + { + $container = new ContainerBuilder(); + $fooDefinition = $container + ->register('foo') + ->setPublic(false) + ; + $fooExtendedDefinition = $container + ->register('foo.extended') + ->setPublic(true) + ->setDecoratedService('foo') + ; + $barDefinition = $container + ->register('bar') + ->setPublic(true) + ; + $barExtendedDefinition = $container + ->register('bar.extended') + ->setPublic(true) + ->setDecoratedService('bar', 'bar.yoo') + ; + + $this->process($container); + + $this->assertEquals('foo.extended', $container->getAlias('foo')); + $this->assertFalse($container->getAlias('foo')->isPublic()); + + $this->assertEquals('bar.extended', $container->getAlias('bar')); + $this->assertTrue($container->getAlias('bar')->isPublic()); + + $this->assertSame($fooDefinition, $container->getDefinition('foo.extended.inner')); + $this->assertFalse($container->getDefinition('foo.extended.inner')->isPublic()); + + $this->assertSame($barDefinition, $container->getDefinition('bar.yoo')); + $this->assertFalse($container->getDefinition('bar.yoo')->isPublic()); + + $this->assertNull($fooExtendedDefinition->getDecoratedService()); + $this->assertNull($barExtendedDefinition->getDecoratedService()); + } + + public function testProcessWithAlias() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(true) + ; + $container->setAlias('foo.alias', new Alias('foo', false)); + $fooExtendedDefinition = $container + ->register('foo.extended') + ->setPublic(true) + ->setDecoratedService('foo.alias') + ; + + $this->process($container); + + $this->assertEquals('foo.extended', $container->getAlias('foo.alias')); + $this->assertFalse($container->getAlias('foo.alias')->isPublic()); + + $this->assertEquals('foo', $container->getAlias('foo.extended.inner')); + $this->assertFalse($container->getAlias('foo.extended.inner')->isPublic()); + + $this->assertNull($fooExtendedDefinition->getDecoratedService()); + } + + public function testProcessWithPriority() + { + $container = new ContainerBuilder(); + $fooDefinition = $container + ->register('foo') + ->setPublic(false) + ; + $barDefinition = $container + ->register('bar') + ->setPublic(true) + ->setDecoratedService('foo') + ; + $bazDefinition = $container + ->register('baz') + ->setPublic(true) + ->setDecoratedService('foo', null, 5) + ; + $quxDefinition = $container + ->register('qux') + ->setPublic(true) + ->setDecoratedService('foo', null, 3) + ; + + $this->process($container); + + $this->assertEquals('bar', $container->getAlias('foo')); + $this->assertFalse($container->getAlias('foo')->isPublic()); + + $this->assertSame($fooDefinition, $container->getDefinition('baz.inner')); + $this->assertFalse($container->getDefinition('baz.inner')->isPublic()); + + $this->assertEquals('qux', $container->getAlias('bar.inner')); + $this->assertFalse($container->getAlias('bar.inner')->isPublic()); + + $this->assertEquals('baz', $container->getAlias('qux.inner')); + $this->assertFalse($container->getAlias('qux.inner')->isPublic()); + + $this->assertNull($barDefinition->getDecoratedService()); + $this->assertNull($bazDefinition->getDecoratedService()); + $this->assertNull($quxDefinition->getDecoratedService()); + } + + protected function process(ContainerBuilder $container) + { + $repeatedPass = new DecoratorServicePass(); + $repeatedPass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ExtensionCompilerPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ExtensionCompilerPassTest.php new file mode 100644 index 0000000..637ed7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ExtensionCompilerPassTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\ExtensionCompilerPass; + +/** + * @author Wouter J + */ +class ExtensionCompilerPassTest extends \PHPUnit_Framework_TestCase +{ + private $container; + private $pass; + + public function setUp() + { + $this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder'); + $this->pass = new ExtensionCompilerPass(); + } + + public function testProcess() + { + $extension1 = $this->createExtensionMock(true); + $extension1->expects($this->once())->method('process'); + $extension2 = $this->createExtensionMock(false); + $extension3 = $this->createExtensionMock(false); + $extension4 = $this->createExtensionMock(true); + $extension4->expects($this->once())->method('process'); + + $this->container->expects($this->any()) + ->method('getExtensions') + ->will($this->returnValue(array($extension1, $extension2, $extension3, $extension4))) + ; + + $this->pass->process($this->container); + } + + private function createExtensionMock($hasInlineCompile) + { + return $this->getMock('Symfony\Component\DependencyInjection\\'.( + $hasInlineCompile + ? 'Compiler\CompilerPassInterface' + : 'Extension\ExtensionInterface' + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php new file mode 100644 index 0000000..342a6ba --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class InlineServiceDefinitionsPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container + ->register('inlinable.service') + ->setPublic(false) + ; + + $container + ->register('service') + ->setArguments(array(new Reference('inlinable.service'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $arguments[0]); + $this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]); + } + + public function testProcessDoesNotInlinesWhenAliasedServiceIsShared() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container->setAlias('moo', 'foo'); + + $container + ->register('service') + ->setArguments(array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertSame($ref, $arguments[0]); + } + + public function testProcessDoesInlineNonSharedService() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setShared(false) + ; + $container + ->register('bar') + ->setPublic(false) + ->setShared(false) + ; + $container->setAlias('moo', 'bar'); + + $container + ->register('service') + ->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertEquals($container->getDefinition('foo'), $arguments[0]); + $this->assertNotSame($container->getDefinition('foo'), $arguments[0]); + $this->assertSame($ref, $arguments[1]); + $this->assertEquals($container->getDefinition('bar'), $arguments[2]); + $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); + } + + public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() + { + $container = new ContainerBuilder(); + + $a = $container->register('a')->setPublic(false); + $b = $container + ->register('b') + ->addArgument(new Reference('a')) + ->addArgument(new Definition(null, array(new Reference('a')))) + ; + + $this->process($container); + + $arguments = $b->getArguments(); + $this->assertSame($a, $arguments[0]); + + $inlinedArguments = $arguments[1]->getArguments(); + $this->assertSame($a, $inlinedArguments[0]); + } + + public function testProcessInlinesPrivateFactoryReference() + { + $container = new ContainerBuilder(); + + $container->register('a')->setPublic(false); + $b = $container + ->register('b') + ->setPublic(false) + ->setFactory(array(new Reference('a'), 'a')) + ; + + $container + ->register('foo') + ->setArguments(array( + $ref = new Reference('b'), + )); + + $this->process($container); + + $inlinedArguments = $container->getDefinition('foo')->getArguments(); + $this->assertSame($b, $inlinedArguments[0]); + } + + public function testProcessDoesNotInlinePrivateFactoryIfReferencedMultipleTimesWithinTheSameDefinition() + { + $container = new ContainerBuilder(); + $container + ->register('a') + ; + $container + ->register('b') + ->setPublic(false) + ->setFactory(array(new Reference('a'), 'a')) + ; + + $container + ->register('foo') + ->setArguments(array( + $ref1 = new Reference('b'), + $ref2 = new Reference('b'), + )) + ; + $this->process($container); + + $args = $container->getDefinition('foo')->getArguments(); + $this->assertSame($ref1, $args[0]); + $this->assertSame($ref2, $args[1]); + } + + public function testProcessDoesNotInlineReferenceWhenUsedByInlineFactory() + { + $container = new ContainerBuilder(); + $container + ->register('a') + ; + $container + ->register('b') + ->setPublic(false) + ->setFactory(array(new Reference('a'), 'a')) + ; + + $inlineFactory = new Definition(); + $inlineFactory->setPublic(false); + $inlineFactory->setFactory(array(new Reference('b'), 'b')); + + $container + ->register('foo') + ->setArguments(array( + $ref = new Reference('b'), + $inlineFactory, + )) + ; + $this->process($container); + + $args = $container->getDefinition('foo')->getArguments(); + $this->assertSame($ref, $args[0]); + } + + public function testProcessDoesNotInlineWhenServiceIsPrivateButLazy() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ->setLazy(true) + ; + + $container + ->register('service') + ->setArguments(array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertSame($ref, $arguments[0]); + } + + public function testProcessDoesNotInlineWhenServiceReferencesItself() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ->addMethodCall('foo', array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $calls = $container->getDefinition('foo')->getMethodCalls(); + $this->assertSame($ref, $calls[0][1][0]); + } + + protected function process(ContainerBuilder $container) + { + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass())); + $repeatedPass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php new file mode 100644 index 0000000..c447940 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This class tests the integration of the different compiler passes. + */ +class IntegrationTest extends \PHPUnit_Framework_TestCase +{ + /** + * This tests that dependencies are correctly processed. + * + * We're checking that: + * + * * A is public, B/C are private + * * A -> C + * * B -> C + */ + public function testProcessRemovesAndInlinesRecursively() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $a = $container + ->register('a', '\stdClass') + ->addArgument(new Reference('c')) + ; + + $b = $container + ->register('b', '\stdClass') + ->addArgument(new Reference('c')) + ->setPublic(false) + ; + + $c = $container + ->register('c', '\stdClass') + ->setPublic(false) + ; + + $container->compile(); + + $this->assertTrue($container->hasDefinition('a')); + $arguments = $a->getArguments(); + $this->assertSame($c, $arguments[0]); + $this->assertFalse($container->hasDefinition('b')); + $this->assertFalse($container->hasDefinition('c')); + } + + public function testProcessInlinesReferencesToAliases() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $a = $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ; + + $container->setAlias('b', new Alias('c', false)); + + $c = $container + ->register('c', '\stdClass') + ->setPublic(false) + ; + + $container->compile(); + + $this->assertTrue($container->hasDefinition('a')); + $arguments = $a->getArguments(); + $this->assertSame($c, $arguments[0]); + $this->assertFalse($container->hasAlias('b')); + $this->assertFalse($container->hasDefinition('c')); + } + + public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDefinition() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ->addMethodCall('setC', array(new Reference('c'))) + ; + + $container + ->register('b', '\stdClass') + ->addArgument(new Reference('c')) + ->setPublic(false) + ; + + $container + ->register('c', '\stdClass') + ->setPublic(false) + ; + + $container->compile(); + + $this->assertTrue($container->hasDefinition('a')); + $this->assertFalse($container->hasDefinition('b')); + $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php new file mode 100644 index 0000000..aed8cdf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/MergeExtensionConfigurationPassTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase +{ + public function testExpressionLanguageProviderForwarding() + { + $tmpProviders = array(); + + $extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface'); + $extension->expects($this->any()) + ->method('getXsdValidationBasePath') + ->will($this->returnValue(false)); + $extension->expects($this->any()) + ->method('getNamespace') + ->will($this->returnValue('http://example.org/schema/dic/foo')); + $extension->expects($this->any()) + ->method('getAlias') + ->will($this->returnValue('foo')); + $extension->expects($this->once()) + ->method('load') + ->will($this->returnCallback(function (array $config, ContainerBuilder $container) use (&$tmpProviders) { + $tmpProviders = $container->getExpressionLanguageProviders(); + })); + + $provider = $this->getMock('Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface'); + $container = new ContainerBuilder(new ParameterBag()); + $container->registerExtension($extension); + $container->prependExtensionConfig('foo', array('bar' => true)); + $container->addExpressionLanguageProvider($provider); + + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + + $this->assertEquals(array($provider), $tmpProviders); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php new file mode 100644 index 0000000..82149eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class RemoveUnusedDefinitionsPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container + ->register('bar') + ->setPublic(false) + ; + $container + ->register('moo') + ->setArguments(array(new Reference('bar'))) + ; + + $this->process($container); + + $this->assertFalse($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + $this->assertTrue($container->hasDefinition('moo')); + } + + public function testProcessRemovesUnusedDefinitionsRecursively() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container + ->register('bar') + ->setArguments(array(new Reference('foo'))) + ->setPublic(false) + ; + + $this->process($container); + + $this->assertFalse($container->hasDefinition('foo')); + $this->assertFalse($container->hasDefinition('bar')); + } + + public function testProcessWorksWithInlinedDefinitions() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container + ->register('bar') + ->setArguments(array(new Definition(null, array(new Reference('foo'))))) + ; + + $this->process($container); + + $this->assertTrue($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + } + + public function testProcessWontRemovePrivateFactory() + { + $container = new ContainerBuilder(); + + $container + ->register('foo', 'stdClass') + ->setFactory(array('stdClass', 'getInstance')) + ->setPublic(false); + + $container + ->register('bar', 'stdClass') + ->setFactory(array(new Reference('foo'), 'getInstance')) + ->setPublic(false); + + $container + ->register('foobar') + ->addArgument(new Reference('bar')); + + $this->process($container); + + $this->assertTrue($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + $this->assertTrue($container->hasDefinition('foobar')); + } + + protected function process(ContainerBuilder $container) + { + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass())); + $repeatedPass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php new file mode 100644 index 0000000..e4d2240 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +class ReplaceAliasByActualDefinitionPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container->register('a', '\stdClass'); + + $bDefinition = new Definition('\stdClass'); + $bDefinition->setPublic(false); + $container->setDefinition('b', $bDefinition); + + $container->setAlias('a_alias', 'a'); + $container->setAlias('b_alias', 'b'); + + $this->process($container); + + $this->assertTrue($container->has('a'), '->process() does nothing to public definitions.'); + $this->assertTrue($container->hasAlias('a_alias')); + $this->assertFalse($container->has('b'), '->process() removes non-public definitions.'); + $this->assertTrue( + $container->has('b_alias') && !$container->hasAlias('b_alias'), + '->process() replaces alias to actual.' + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testProcessWithInvalidAlias() + { + $container = new ContainerBuilder(); + $container->setAlias('a_alias', 'a'); + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ReplaceAliasByActualDefinitionPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php new file mode 100644 index 0000000..5109fc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveDefinitionTemplatesPassTest.php @@ -0,0 +1,318 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveDefinitionTemplatesPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('parent', 'foo')->setArguments(array('moo', 'b'))->setProperty('foo', 'moo'); + $container->setDefinition('child', new DefinitionDecorator('parent')) + ->replaceArgument(0, 'a') + ->setProperty('foo', 'bar') + ->setClass('bar') + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertNotInstanceOf('Symfony\Component\DependencyInjection\DefinitionDecorator', $def); + $this->assertEquals('bar', $def->getClass()); + $this->assertEquals(array('a', 'b'), $def->getArguments()); + $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); + } + + public function testProcessAppendsMethodCallsAlways() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->addMethodCall('foo', array('bar')) + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ->addMethodCall('bar', array('foo')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertEquals(array( + array('foo', array('bar')), + array('bar', array('foo')), + ), $def->getMethodCalls()); + } + + public function testProcessDoesNotCopyAbstract() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setAbstract(true) + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertFalse($def->isAbstract()); + } + + public function testProcessDoesNotCopyShared() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setShared(false) + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertTrue($def->isShared()); + } + + public function testProcessDoesNotCopyTags() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->addTag('foo') + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertEquals(array(), $def->getTags()); + } + + public function testProcessDoesNotCopyDecoratedService() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setDecoratedService('foo') + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertNull($def->getDecoratedService()); + } + + public function testProcessDoesNotDropShared() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ->setShared(false) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertFalse($def->isShared()); + } + + public function testProcessHandlesMultipleInheritance() + { + $container = new ContainerBuilder(); + + $container + ->register('parent', 'foo') + ->setArguments(array('foo', 'bar', 'c')) + ; + + $container + ->setDefinition('child2', new DefinitionDecorator('child1')) + ->replaceArgument(1, 'b') + ; + + $container + ->setDefinition('child1', new DefinitionDecorator('parent')) + ->replaceArgument(0, 'a') + ; + + $this->process($container); + + $def = $container->getDefinition('child2'); + $this->assertEquals(array('a', 'b', 'c'), $def->getArguments()); + $this->assertEquals('foo', $def->getClass()); + } + + public function testSetLazyOnServiceHasParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass'); + + $container->setDefinition('child1', new DefinitionDecorator('parent')) + ->setLazy(true) + ; + + $this->process($container); + + $this->assertTrue($container->getDefinition('child1')->isLazy()); + } + + public function testSetLazyOnServiceIsParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setLazy(true) + ; + + $container->setDefinition('child1', new DefinitionDecorator('parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('child1')->isLazy()); + } + + public function testDeepDefinitionsResolving() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'parentClass'); + $container->register('sibling', 'siblingClass') + ->setConfigurator(new DefinitionDecorator('parent'), 'foo') + ->setFactory(array(new DefinitionDecorator('parent'), 'foo')) + ->addArgument(new DefinitionDecorator('parent')) + ->setProperty('prop', new DefinitionDecorator('parent')) + ->addMethodCall('meth', array(new DefinitionDecorator('parent'))) + ; + + $this->process($container); + + $configurator = $container->getDefinition('sibling')->getConfigurator(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($configurator)); + $this->assertSame('parentClass', $configurator->getClass()); + + $factory = $container->getDefinition('sibling')->getFactory(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($factory[0])); + $this->assertSame('parentClass', $factory[0]->getClass()); + + $argument = $container->getDefinition('sibling')->getArgument(0); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($argument)); + $this->assertSame('parentClass', $argument->getClass()); + + $properties = $container->getDefinition('sibling')->getProperties(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($properties['prop'])); + $this->assertSame('parentClass', $properties['prop']->getClass()); + + $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', get_class($methodCalls[0][1][0])); + $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); + } + + public function testSetDecoratedServiceOnServiceHasParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass'); + + $container->setDefinition('child1', new DefinitionDecorator('parent')) + ->setDecoratedService('foo', 'foo_inner') + ; + + $this->assertEquals(array('foo', 'foo_inner', 0), $container->getDefinition('child1')->getDecoratedService()); + } + + public function testDecoratedServiceCopiesDeprecatedStatusFromParent() + { + $container = new ContainerBuilder(); + $container->register('deprecated_parent') + ->setDeprecated(true) + ; + + $container->setDefinition('decorated_deprecated_parent', new DefinitionDecorator('deprecated_parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); + } + + public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() + { + $container = new ContainerBuilder(); + $container->register('deprecated_parent') + ->setDeprecated(true) + ; + + $container->setDefinition('decorated_deprecated_parent', new DefinitionDecorator('deprecated_parent')) + ->setDeprecated(false) + ; + + $this->process($container); + + $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); + } + + public function testProcessMergeAutowiringTypes() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->addAutowiringType('Foo') + ; + + $container + ->setDefinition('child', new DefinitionDecorator('parent')) + ->addAutowiringType('Bar') + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertEquals(array('Foo', 'Bar'), $def->getAutowiringTypes()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveDefinitionTemplatesPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php new file mode 100644 index 0000000..a18ba73 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInvalidReferencesPassTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveInvalidReferencesPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->setArguments(array(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE))) + ->addMethodCall('foo', array(new Reference('moo', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertNull($arguments[0]); + $this->assertCount(0, $def->getMethodCalls()); + } + + public function testProcessIgnoreNonExistentServices() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->setArguments(array(new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertEquals('bar', (string) $arguments[0]); + } + + public function testProcessRemovesPropertiesOnInvalid() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->setProperty('foo', new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)) + ; + + $this->process($container); + + $this->assertEquals(array(), $def->getProperties()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveInvalidReferencesPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php new file mode 100644 index 0000000..1f604c2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveParameterPlaceHoldersPassTest extends \PHPUnit_Framework_TestCase +{ + private $compilerPass; + private $container; + private $fooDefinition; + + protected function setUp() + { + $this->compilerPass = new ResolveParameterPlaceHoldersPass(); + $this->container = $this->createContainerBuilder(); + $this->compilerPass->process($this->container); + $this->fooDefinition = $this->container->getDefinition('foo'); + } + + public function testClassParametersShouldBeResolved() + { + $this->assertSame('Foo', $this->fooDefinition->getClass()); + } + + public function testFactoryParametersShouldBeResolved() + { + $this->assertSame(array('FooFactory', 'getFoo'), $this->fooDefinition->getFactory()); + } + + public function testArgumentParametersShouldBeResolved() + { + $this->assertSame(array('bar', 'baz'), $this->fooDefinition->getArguments()); + } + + public function testMethodCallParametersShouldBeResolved() + { + $this->assertSame(array(array('foobar', array('bar', 'baz'))), $this->fooDefinition->getMethodCalls()); + } + + public function testPropertyParametersShouldBeResolved() + { + $this->assertSame(array('bar' => 'baz'), $this->fooDefinition->getProperties()); + } + + public function testFileParametersShouldBeResolved() + { + $this->assertSame('foo.php', $this->fooDefinition->getFile()); + } + + public function testAliasParametersShouldBeResolved() + { + $this->assertSame('foo', $this->container->getAlias('bar')->__toString()); + } + + private function createContainerBuilder() + { + $containerBuilder = new ContainerBuilder(); + + $containerBuilder->setParameter('foo.class', 'Foo'); + $containerBuilder->setParameter('foo.factory.class', 'FooFactory'); + $containerBuilder->setParameter('foo.arg1', 'bar'); + $containerBuilder->setParameter('foo.arg2', 'baz'); + $containerBuilder->setParameter('foo.method', 'foobar'); + $containerBuilder->setParameter('foo.property.name', 'bar'); + $containerBuilder->setParameter('foo.property.value', 'baz'); + $containerBuilder->setParameter('foo.file', 'foo.php'); + $containerBuilder->setParameter('alias.id', 'bar'); + + $fooDefinition = $containerBuilder->register('foo', '%foo.class%'); + $fooDefinition->setFactory(array('%foo.factory.class%', 'getFoo')); + $fooDefinition->setArguments(array('%foo.arg1%', '%foo.arg2%')); + $fooDefinition->addMethodCall('%foo.method%', array('%foo.arg1%', '%foo.arg2%')); + $fooDefinition->setProperty('%foo.property.name%', '%foo.property.value%'); + $fooDefinition->setFile('%foo.file%'); + + $containerBuilder->setAlias('%alias.id%', 'foo'); + + return $containerBuilder; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php new file mode 100644 index 0000000..6fdc233 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Compiler\ResolveReferencesToAliasesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveReferencesToAliasesPassTest extends \PHPUnit_Framework_TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->setAlias('bar', 'foo'); + $def = $container + ->register('moo') + ->setArguments(array(new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertEquals('foo', (string) $arguments[0]); + } + + public function testProcessRecursively() + { + $container = new ContainerBuilder(); + $container->setAlias('bar', 'foo'); + $container->setAlias('moo', 'bar'); + $def = $container + ->register('foobar') + ->setArguments(array(new Reference('moo'))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertEquals('foo', (string) $arguments[0]); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testAliasCircularReference() + { + $container = new ContainerBuilder(); + $container->setAlias('bar', 'foo'); + $container->setAlias('foo', 'bar'); + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveReferencesToAliasesPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php new file mode 100644 index 0000000..f252b56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -0,0 +1,734 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +require_once __DIR__.'/Fixtures/includes/classes.php'; +require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\ExpressionLanguage\Expression; + +class ContainerBuilderTest extends \PHPUnit_Framework_TestCase +{ + public function testDefinitions() + { + $builder = new ContainerBuilder(); + $definitions = array( + 'foo' => new Definition('Bar\FooClass'), + 'bar' => new Definition('BarClass'), + ); + $builder->setDefinitions($definitions); + $this->assertEquals($definitions, $builder->getDefinitions(), '->setDefinitions() sets the service definitions'); + $this->assertTrue($builder->hasDefinition('foo'), '->hasDefinition() returns true if a service definition exists'); + $this->assertFalse($builder->hasDefinition('foobar'), '->hasDefinition() returns false if a service definition does not exist'); + + $builder->setDefinition('foobar', $foo = new Definition('FooBarClass')); + $this->assertEquals($foo, $builder->getDefinition('foobar'), '->getDefinition() returns a service definition if defined'); + $this->assertTrue($builder->setDefinition('foobar', $foo = new Definition('FooBarClass')) === $foo, '->setDefinition() implements a fluid interface by returning the service reference'); + + $builder->addDefinitions($defs = array('foobar' => new Definition('FooBarClass'))); + $this->assertEquals(array_merge($definitions, $defs), $builder->getDefinitions(), '->addDefinitions() adds the service definitions'); + + try { + $builder->getDefinition('baz'); + $this->fail('->getDefinition() throws an InvalidArgumentException if the service definition does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('The service definition "baz" does not exist.', $e->getMessage(), '->getDefinition() throws an InvalidArgumentException if the service definition does not exist'); + } + } + + public function testCreateDeprecatedService() + { + $definition = new Definition('stdClass'); + $definition->setDeprecated(true); + + $that = $this; + $wasTriggered = false; + + set_error_handler(function ($errno, $errstr) use ($that, &$wasTriggered) { + $that->assertSame(E_USER_DEPRECATED, $errno); + $that->assertSame('The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed.', $errstr); + $wasTriggered = true; + }); + + $builder = new ContainerBuilder(); + $builder->setDefinition('deprecated_foo', $definition); + $builder->get('deprecated_foo'); + + restore_error_handler(); + + $this->assertTrue($wasTriggered); + } + + public function testRegister() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass'); + $this->assertTrue($builder->hasDefinition('foo'), '->register() registers a new service definition'); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $builder->getDefinition('foo'), '->register() returns the newly created Definition instance'); + } + + public function testHas() + { + $builder = new ContainerBuilder(); + $this->assertFalse($builder->has('foo'), '->has() returns false if the service does not exist'); + $builder->register('foo', 'Bar\FooClass'); + $this->assertTrue($builder->has('foo'), '->has() returns true if a service definition exists'); + $builder->set('bar', new \stdClass()); + $this->assertTrue($builder->has('bar'), '->has() returns true if a service exists'); + } + + public function testGet() + { + $builder = new ContainerBuilder(); + try { + $builder->get('foo'); + $this->fail('->get() throws an InvalidArgumentException if the service does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('The service definition "foo" does not exist.', $e->getMessage(), '->get() throws an InvalidArgumentException if the service does not exist'); + } + + $this->assertNull($builder->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service does not exist and NULL_ON_INVALID_REFERENCE is passed as a second argument'); + + $builder->register('foo', 'stdClass'); + $this->assertInternalType('object', $builder->get('foo'), '->get() returns the service definition associated with the id'); + $builder->set('bar', $bar = new \stdClass()); + $this->assertEquals($bar, $builder->get('bar'), '->get() returns the service associated with the id'); + $builder->register('bar', 'stdClass'); + $this->assertEquals($bar, $builder->get('bar'), '->get() returns the service associated with the id even if a definition has been defined'); + + $builder->register('baz', 'stdClass')->setArguments(array(new Reference('baz'))); + try { + @$builder->get('baz'); + $this->fail('->get() throws a ServiceCircularReferenceException if the service has a circular reference to itself'); + } catch (\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException $e) { + $this->assertEquals('Circular reference detected for service "baz", path: "baz".', $e->getMessage(), '->get() throws a LogicException if the service has a circular reference to itself'); + } + + $this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared'); + } + + public function testNonSharedServicesReturnsDifferentInstances() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass')->setShared(false); + + $this->assertNotSame($builder->get('bar'), $builder->get('bar')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage You have requested a synthetic service ("foo"). The DIC does not know how to construct this service. + */ + public function testGetUnsetLoadingServiceWhenCreateServiceThrowsAnException() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass')->setSynthetic(true); + + // we expect a RuntimeException here as foo is synthetic + try { + $builder->get('foo'); + } catch (RuntimeException $e) { + } + + // we must also have the same RuntimeException here + $builder->get('foo'); + } + + public function testGetServiceIds() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass'); + $builder->bar = $bar = new \stdClass(); + $builder->register('bar', 'stdClass'); + $this->assertEquals(array('foo', 'bar', 'service_container'), $builder->getServiceIds(), '->getServiceIds() returns all defined service ids'); + } + + public function testAliases() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass'); + $builder->setAlias('bar', 'foo'); + $this->assertTrue($builder->hasAlias('bar'), '->hasAlias() returns true if the alias exists'); + $this->assertFalse($builder->hasAlias('foobar'), '->hasAlias() returns false if the alias does not exist'); + $this->assertEquals('foo', (string) $builder->getAlias('bar'), '->getAlias() returns the aliased service'); + $this->assertTrue($builder->has('bar'), '->setAlias() defines a new service'); + $this->assertTrue($builder->get('bar') === $builder->get('foo'), '->setAlias() creates a service that is an alias to another one'); + + try { + $builder->setAlias('foobar', 'foobar'); + $this->fail('->setAlias() throws an InvalidArgumentException if the alias references itself'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('An alias can not reference itself, got a circular reference on "foobar".', $e->getMessage(), '->setAlias() throws an InvalidArgumentException if the alias references itself'); + } + + try { + $builder->getAlias('foobar'); + $this->fail('->getAlias() throws an InvalidArgumentException if the alias does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('The service alias "foobar" does not exist.', $e->getMessage(), '->getAlias() throws an InvalidArgumentException if the alias does not exist'); + } + } + + public function testGetAliases() + { + $builder = new ContainerBuilder(); + $builder->setAlias('bar', 'foo'); + $builder->setAlias('foobar', 'foo'); + $builder->setAlias('moo', new Alias('foo', false)); + + $aliases = $builder->getAliases(); + $this->assertEquals('foo', (string) $aliases['bar']); + $this->assertTrue($aliases['bar']->isPublic()); + $this->assertEquals('foo', (string) $aliases['foobar']); + $this->assertEquals('foo', (string) $aliases['moo']); + $this->assertFalse($aliases['moo']->isPublic()); + + $builder->register('bar', 'stdClass'); + $this->assertFalse($builder->hasAlias('bar')); + + $builder->set('foobar', 'stdClass'); + $builder->set('moo', 'stdClass'); + $this->assertCount(0, $builder->getAliases(), '->getAliases() does not return aliased services that have been overridden'); + } + + public function testSetAliases() + { + $builder = new ContainerBuilder(); + $builder->setAliases(array('bar' => 'foo', 'foobar' => 'foo')); + + $aliases = $builder->getAliases(); + $this->assertTrue(isset($aliases['bar'])); + $this->assertTrue(isset($aliases['foobar'])); + } + + public function testAddAliases() + { + $builder = new ContainerBuilder(); + $builder->setAliases(array('bar' => 'foo')); + $builder->addAliases(array('foobar' => 'foo')); + + $aliases = $builder->getAliases(); + $this->assertTrue(isset($aliases['bar'])); + $this->assertTrue(isset($aliases['foobar'])); + } + + public function testAddGetCompilerPass() + { + $builder = new ContainerBuilder(); + $builder->setResourceTracking(false); + $builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses(); + $builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface')); + + $this->assertCount(count($builder->getCompiler()->getPassConfig()->getPasses()) - 1, $builderCompilerPasses); + } + + public function testCreateService() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $this->assertInstanceOf('\Bar\FooClass', $builder->get('foo1'), '->createService() requires the file defined by the service definition'); + $builder->register('foo2', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/%file%.php'); + $builder->setParameter('file', 'foo'); + $this->assertInstanceOf('\Bar\FooClass', $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition'); + } + + public function testCreateProxyWithRealServiceInstantiator() + { + $builder = new ContainerBuilder(); + + $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $builder->getDefinition('foo1')->setLazy(true); + + $foo1 = $builder->get('foo1'); + + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); + $this->assertSame('Bar\FooClass', get_class($foo1)); + } + + public function testCreateServiceClass() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', '%class%'); + $builder->setParameter('class', 'stdClass'); + $this->assertInstanceOf('\stdClass', $builder->get('foo1'), '->createService() replaces parameters in the class provided by the service definition'); + } + + public function testCreateServiceArguments() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'), '%%unescape_it%%')); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->arguments, '->createService() replaces parameters and service references in the arguments provided by the service definition'); + } + + public function testCreateServiceFactory() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass')->setFactory('Bar\FooClass::getInstance'); + $builder->register('qux', 'Bar\FooClass')->setFactory(array('Bar\FooClass', 'getInstance')); + $builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')); + $builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance')); + + $this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance'); + $this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance'); + $this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory'); + $this->assertTrue($builder->get('baz')->called, '->createService() uses another service as factory'); + } + + public function testCreateServiceMethodCalls() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%value%', new Reference('bar')))); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('bar', $builder->get('bar')), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); + } + + public function testCreateServiceMethodCallsWithEscapedParam() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%'))); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); + } + + public function testCreateServiceProperties() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%')); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('bar', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the properties'); + } + + public function testCreateServiceConfigurator() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', 'Bar\FooClass')->setConfigurator('sc_configure'); + $this->assertTrue($builder->get('foo1')->configured, '->createService() calls the configurator'); + + $builder->register('foo2', 'Bar\FooClass')->setConfigurator(array('%class%', 'configureStatic')); + $builder->setParameter('class', 'BazClass'); + $this->assertTrue($builder->get('foo2')->configured, '->createService() calls the configurator'); + + $builder->register('baz', 'BazClass'); + $builder->register('foo3', 'Bar\FooClass')->setConfigurator(array(new Reference('baz'), 'configure')); + $this->assertTrue($builder->get('foo3')->configured, '->createService() calls the configurator'); + + $builder->register('foo4', 'Bar\FooClass')->setConfigurator(array($builder->getDefinition('baz'), 'configure')); + $this->assertTrue($builder->get('foo4')->configured, '->createService() calls the configurator'); + + $builder->register('foo5', 'Bar\FooClass')->setConfigurator('foo'); + try { + $builder->get('foo5'); + $this->fail('->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('The configure callable for class "Bar\FooClass" is not a callable.', $e->getMessage(), '->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); + } + } + + /** + * @expectedException \RuntimeException + */ + public function testCreateSyntheticService() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass')->setSynthetic(true); + $builder->get('foo'); + } + + public function testCreateServiceWithExpression() + { + $builder = new ContainerBuilder(); + $builder->setParameter('bar', 'bar'); + $builder->register('bar', 'BarClass'); + $builder->register('foo', 'Bar\FooClass')->addArgument(array('foo' => new Expression('service("bar").foo ~ parameter("bar")'))); + $this->assertEquals('foobar', $builder->get('foo')->arguments['foo']); + } + + public function testResolveServices() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass'); + $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Reference('foo')), '->resolveServices() resolves service references to service instances'); + $this->assertEquals(array('foo' => array('foo', $builder->get('foo'))), $builder->resolveServices(array('foo' => array('foo', new Reference('foo')))), '->resolveServices() resolves service references to service instances in nested arrays'); + $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions'); + } + + public function testMerge() + { + $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); + $container->setResourceTracking(false); + $config = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); + $container->merge($config); + $this->assertEquals(array('bar' => 'foo', 'foo' => 'bar'), $container->getParameterBag()->all(), '->merge() merges current parameters with the loaded ones'); + + $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); + $container->setResourceTracking(false); + $config = new ContainerBuilder(new ParameterBag(array('foo' => '%bar%'))); + $container->merge($config); + $container->compile(); + $this->assertEquals(array('bar' => 'foo', 'foo' => 'foo'), $container->getParameterBag()->all(), '->merge() evaluates the values of the parameters towards already defined ones'); + + $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); + $container->setResourceTracking(false); + $config = new ContainerBuilder(new ParameterBag(array('foo' => '%bar%', 'baz' => '%foo%'))); + $container->merge($config); + $container->compile(); + $this->assertEquals(array('bar' => 'foo', 'foo' => 'foo', 'baz' => 'foo'), $container->getParameterBag()->all(), '->merge() evaluates the values of the parameters towards already defined ones'); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->register('foo', 'Bar\FooClass'); + $container->register('bar', 'BarClass'); + $config = new ContainerBuilder(); + $config->setDefinition('baz', new Definition('BazClass')); + $config->setAlias('alias_for_foo', 'foo'); + $container->merge($config); + $this->assertEquals(array('foo', 'bar', 'baz'), array_keys($container->getDefinitions()), '->merge() merges definitions already defined ones'); + + $aliases = $container->getAliases(); + $this->assertTrue(isset($aliases['alias_for_foo'])); + $this->assertEquals('foo', (string) $aliases['alias_for_foo']); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->register('foo', 'Bar\FooClass'); + $config->setDefinition('foo', new Definition('BazClass')); + $container->merge($config); + $this->assertEquals('BazClass', $container->getDefinition('foo')->getClass(), '->merge() overrides already defined services'); + } + + /** + * @expectedException \LogicException + */ + public function testMergeLogicException() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->compile(); + $container->merge(new ContainerBuilder()); + } + + public function testfindTaggedServiceIds() + { + $builder = new ContainerBuilder(); + $builder + ->register('foo', 'Bar\FooClass') + ->addTag('foo', array('foo' => 'foo')) + ->addTag('bar', array('bar' => 'bar')) + ->addTag('foo', array('foofoo' => 'foofoo')) + ; + $this->assertEquals($builder->findTaggedServiceIds('foo'), array( + 'foo' => array( + array('foo' => 'foo'), + array('foofoo' => 'foofoo'), + ), + ), '->findTaggedServiceIds() returns an array of service ids and its tag attributes'); + $this->assertEquals(array(), $builder->findTaggedServiceIds('foobar'), '->findTaggedServiceIds() returns an empty array if there is annotated services'); + } + + public function testFindUnusedTags() + { + $builder = new ContainerBuilder(); + $builder + ->register('foo', 'Bar\FooClass') + ->addTag('kernel.event_listener', array('foo' => 'foo')) + ->addTag('kenrel.event_listener', array('bar' => 'bar')) + ; + $builder->findTaggedServiceIds('kernel.event_listener'); + $this->assertEquals(array('kenrel.event_listener'), $builder->findUnusedTags(), '->findUnusedTags() returns an array with unused tags'); + } + + public function testFindDefinition() + { + $container = new ContainerBuilder(); + $container->setDefinition('foo', $definition = new Definition('Bar\FooClass')); + $container->setAlias('bar', 'foo'); + $container->setAlias('foobar', 'bar'); + $this->assertEquals($definition, $container->findDefinition('foobar'), '->findDefinition() returns a Definition'); + } + + public function testAddObjectResource() + { + $container = new ContainerBuilder(); + + $container->setResourceTracking(false); + $container->addObjectResource(new \BarClass()); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->setResourceTracking(true); + $container->addObjectResource(new \BarClass()); + + $resources = $container->getResources(); + + $this->assertCount(1, $resources, '1 resource was registered'); + + /* @var $resource \Symfony\Component\Config\Resource\FileResource */ + $resource = end($resources); + + $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); + $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); + } + + public function testAddClassResource() + { + $container = new ContainerBuilder(); + + $container->setResourceTracking(false); + $container->addClassResource(new \ReflectionClass('BarClass')); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->setResourceTracking(true); + $container->addClassResource(new \ReflectionClass('BarClass')); + + $resources = $container->getResources(); + + $this->assertCount(1, $resources, '1 resource was registered'); + + /* @var $resource \Symfony\Component\Config\Resource\FileResource */ + $resource = end($resources); + + $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); + $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); + } + + public function testCompilesClassDefinitionsOfLazyServices() + { + $container = new ContainerBuilder(); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->register('foo', 'BarClass'); + $container->getDefinition('foo')->setLazy(true); + + $container->compile(); + + $classesPath = realpath(__DIR__.'/Fixtures/includes/classes.php'); + $matchingResources = array_filter( + $container->getResources(), + function (ResourceInterface $resource) use ($classesPath) { + return $resource instanceof FileResource && $classesPath === realpath($resource->getResource()); + } + ); + + $this->assertNotEmpty($matchingResources); + } + + public function testResources() + { + $container = new ContainerBuilder(); + $container->addResource($a = new FileResource(__DIR__.'/Fixtures/xml/services1.xml')); + $container->addResource($b = new FileResource(__DIR__.'/Fixtures/xml/services2.xml')); + $resources = array(); + foreach ($container->getResources() as $resource) { + if (false === strpos($resource, '.php')) { + $resources[] = $resource; + } + } + $this->assertEquals(array($a, $b), $resources, '->getResources() returns an array of resources read for the current configuration'); + $this->assertSame($container, $container->setResources(array())); + $this->assertEquals(array(), $container->getResources()); + } + + public function testExtension() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $container->registerExtension($extension = new \ProjectExtension()); + $this->assertTrue($container->getExtension('project') === $extension, '->registerExtension() registers an extension'); + + $this->setExpectedException('LogicException'); + $container->getExtension('no_registered'); + } + + public function testRegisteredButNotLoadedExtension() + { + $extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface'); + $extension->expects($this->once())->method('getAlias')->will($this->returnValue('project')); + $extension->expects($this->never())->method('load'); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->registerExtension($extension); + $container->compile(); + } + + public function testRegisteredAndLoadedExtension() + { + $extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface'); + $extension->expects($this->exactly(2))->method('getAlias')->will($this->returnValue('project')); + $extension->expects($this->once())->method('load')->with(array(array('foo' => 'bar'))); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->registerExtension($extension); + $container->loadFromExtension('project', array('foo' => 'bar')); + $container->compile(); + } + + public function testPrivateServiceUser() + { + $fooDefinition = new Definition('BarClass'); + $fooUserDefinition = new Definition('BarUserClass', array(new Reference('bar'))); + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $fooDefinition->setPublic(false); + + $container->addDefinitions(array( + 'bar' => $fooDefinition, + 'bar_user' => $fooUserDefinition, + )); + + $container->compile(); + $this->assertInstanceOf('BarClass', $container->get('bar_user')->bar); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsExceptionWhenSetServiceOnAFrozenContainer() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->setDefinition('a', new Definition('stdClass')); + $container->compile(); + $container->set('a', new \stdClass()); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsExceptionWhenAddServiceOnAFrozenContainer() + { + $container = new ContainerBuilder(); + $container->compile(); + $container->set('a', new \stdClass()); + } + + public function testNoExceptionWhenSetSyntheticServiceOnAFrozenContainer() + { + $container = new ContainerBuilder(); + $def = new Definition('stdClass'); + $def->setSynthetic(true); + $container->setDefinition('a', $def); + $container->compile(); + $container->set('a', $a = new \stdClass()); + $this->assertEquals($a, $container->get('a')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsExceptionWhenSetDefinitionOnAFrozenContainer() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->compile(); + $container->setDefinition('a', new Definition()); + } + + public function testExtensionConfig() + { + $container = new ContainerBuilder(); + + $configs = $container->getExtensionConfig('foo'); + $this->assertEmpty($configs); + + $first = array('foo' => 'bar'); + $container->prependExtensionConfig('foo', $first); + $configs = $container->getExtensionConfig('foo'); + $this->assertEquals(array($first), $configs); + + $second = array('ding' => 'dong'); + $container->prependExtensionConfig('foo', $second); + $configs = $container->getExtensionConfig('foo'); + $this->assertEquals(array($second, $first), $configs); + } + + public function testLazyLoadedService() + { + $loader = new ClosureLoader($container = new ContainerBuilder()); + $loader->load(function (ContainerBuilder $container) { + $container->set('a', new \BazClass()); + $definition = new Definition('BazClass'); + $definition->setLazy(true); + $container->setDefinition('a', $definition); + } + ); + + $container->setResourceTracking(true); + + $container->compile(); + + $class = new \BazClass(); + $reflectionClass = new \ReflectionClass($class); + + $r = new \ReflectionProperty($container, 'resources'); + $r->setAccessible(true); + $resources = $r->getValue($container); + + $classInList = false; + foreach ($resources as $resource) { + if ($resource->getResource() === $reflectionClass->getFileName()) { + $classInList = true; + break; + } + } + + $this->assertTrue($classInList); + } + + public function testAutowiring() + { + $container = new ContainerBuilder(); + + $container->register('a', __NAMESPACE__.'\A'); + $bDefinition = $container->register('b', __NAMESPACE__.'\B'); + $bDefinition->setAutowired(true); + + $container->compile(); + + $this->assertEquals('a', (string) $container->getDefinition('b')->getArgument(0)); + } +} + +class FooClass +{ +} + +class A +{ +} + +class B +{ + public function __construct(A $a) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php new file mode 100644 index 0000000..17b6ee6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -0,0 +1,348 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class ContainerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $sc = new Container(); + $this->assertSame($sc, $sc->get('service_container'), '__construct() automatically registers itself as a service'); + + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '__construct() takes an array of parameters as its first argument'); + } + + /** + * @dataProvider dataForTestCamelize + */ + public function testCamelize($id, $expected) + { + $this->assertEquals($expected, Container::camelize($id), sprintf('Container::camelize("%s")', $id)); + } + + public function dataForTestCamelize() + { + return array( + array('foo_bar', 'FooBar'), + array('foo.bar', 'Foo_Bar'), + array('foo.bar_baz', 'Foo_BarBaz'), + array('foo._bar', 'Foo_Bar'), + array('foo_.bar', 'Foo_Bar'), + array('_foo', 'Foo'), + array('.foo', '_Foo'), + array('foo_', 'Foo'), + array('foo.', 'Foo_'), + array('foo\bar', 'Foo_Bar'), + ); + } + + /** + * @dataProvider dataForTestUnderscore + */ + public function testUnderscore($id, $expected) + { + $this->assertEquals($expected, Container::underscore($id), sprintf('Container::underscore("%s")', $id)); + } + + public function dataForTestUnderscore() + { + return array( + array('FooBar', 'foo_bar'), + array('Foo_Bar', 'foo.bar'), + array('Foo_BarBaz', 'foo.bar_baz'), + array('FooBar_BazQux', 'foo_bar.baz_qux'), + array('_Foo', '.foo'), + array('Foo_', 'foo.'), + ); + } + + public function testCompile() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $this->assertFalse($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag'); + $sc->compile(); + $this->assertTrue($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag'); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag', $sc->getParameterBag(), '->compile() changes the parameter bag to a FrozenParameterBag instance'); + $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag'); + } + + public function testIsFrozen() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $this->assertFalse($sc->isFrozen(), '->isFrozen() returns false if the parameters are not frozen'); + $sc->compile(); + $this->assertTrue($sc->isFrozen(), '->isFrozen() returns true if the parameters are frozen'); + } + + public function testGetParameterBag() + { + $sc = new Container(); + $this->assertEquals(array(), $sc->getParameterBag()->all(), '->getParameterBag() returns an empty array if no parameter has been defined'); + } + + public function testGetSetParameter() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $sc->setParameter('bar', 'foo'); + $this->assertEquals('foo', $sc->getParameter('bar'), '->setParameter() sets the value of a new parameter'); + + $sc->setParameter('foo', 'baz'); + $this->assertEquals('baz', $sc->getParameter('foo'), '->setParameter() overrides previously set parameter'); + + $sc->setParameter('Foo', 'baz1'); + $this->assertEquals('baz1', $sc->getParameter('foo'), '->setParameter() converts the key to lowercase'); + $this->assertEquals('baz1', $sc->getParameter('FOO'), '->getParameter() converts the key to lowercase'); + + try { + $sc->getParameter('baba'); + $this->fail('->getParameter() thrown an \InvalidArgumentException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getParameter() thrown an \InvalidArgumentException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->getParameter() thrown an \InvalidArgumentException if the key does not exist'); + } + } + + public function testGetServiceIds() + { + $sc = new Container(); + $sc->set('foo', $obj = new \stdClass()); + $sc->set('bar', $obj = new \stdClass()); + $this->assertEquals(array('service_container', 'foo', 'bar'), $sc->getServiceIds(), '->getServiceIds() returns all defined service ids'); + + $sc = new ProjectServiceContainer(); + $sc->set('foo', $obj = new \stdClass()); + $this->assertEquals(array('bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods, followed by service ids defined by set()'); + } + + public function testSet() + { + $sc = new Container(); + $sc->set('foo', $foo = new \stdClass()); + $this->assertEquals($foo, $sc->get('foo'), '->set() sets a service'); + } + + public function testSetWithNullResetTheService() + { + $sc = new Container(); + $sc->set('foo', null); + $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); + } + + public function testGet() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', $foo = new \stdClass()); + $this->assertEquals($foo, $sc->get('foo'), '->get() returns the service for the given id'); + $this->assertEquals($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase'); + $this->assertEquals($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id'); + $this->assertEquals($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined'); + $this->assertEquals($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined'); + $this->assertEquals($sc->__foo_baz, $sc->get('foo\\baz'), '->get() returns the service if a get*Method() is defined'); + + $sc->set('bar', $bar = new \stdClass()); + $this->assertEquals($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()'); + + try { + $sc->get(''); + $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty'); + } + $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty'); + } + + public function testGetThrowServiceNotFoundException() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', $foo = new \stdClass()); + $sc->set('bar', $foo = new \stdClass()); + $sc->set('baz', $foo = new \stdClass()); + + try { + $sc->get('foo1'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent service "foo1". Did you mean this: "foo"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices'); + } + + try { + $sc->get('bag'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent service "bag". Did you mean one of these: "bar", "baz"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices'); + } + } + + public function testGetCircularReference() + { + $sc = new ProjectServiceContainer(); + try { + $sc->get('circular'); + $this->fail('->get() throws a ServiceCircularReferenceException if it contains circular reference'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException', $e, '->get() throws a ServiceCircularReferenceException if it contains circular reference'); + $this->assertStringStartsWith('Circular reference detected for service "circular"', $e->getMessage(), '->get() throws a \LogicException if it contains circular reference'); + } + } + + public function testHas() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', new \stdClass()); + $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist'); + $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists'); + $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined'); + $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined'); + $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined'); + $this->assertTrue($sc->has('foo\\baz'), '->has() returns true if a get*Method() is defined'); + } + + public function testInitialized() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', new \stdClass()); + $this->assertTrue($sc->initialized('foo'), '->initialized() returns true if service is loaded'); + $this->assertFalse($sc->initialized('foo1'), '->initialized() returns false if service is not loaded'); + $this->assertFalse($sc->initialized('bar'), '->initialized() returns false if a service is defined, but not currently loaded'); + $this->assertFalse($sc->initialized('alias'), '->initialized() returns false if an aliased service is not initialized'); + + $sc->set('bar', new \stdClass()); + $this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized'); + } + + public function testReset() + { + $c = new Container(); + $c->set('bar', new \stdClass()); + + $c->reset(); + + $this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Something went terribly wrong! + */ + public function testGetThrowsException() + { + $c = new ProjectServiceContainer(); + + try { + $c->get('throw_exception'); + } catch (\Exception $e) { + // Do nothing. + } + + // Retry, to make sure that get*Service() will be called. + $c->get('throw_exception'); + } + + public function testGetThrowsExceptionOnServiceConfiguration() + { + $c = new ProjectServiceContainer(); + + try { + $c->get('throws_exception_on_service_configuration'); + } catch (\Exception $e) { + // Do nothing. + } + + $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); + + // Retry, to make sure that get*Service() will be called. + try { + $c->get('throws_exception_on_service_configuration'); + } catch (\Exception $e) { + // Do nothing. + } + $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); + } + + protected function getField($obj, $field) + { + $reflection = new \ReflectionProperty($obj, $field); + $reflection->setAccessible(true); + + return $reflection->getValue($obj); + } + + public function testAlias() + { + $c = new ProjectServiceContainer(); + + $this->assertTrue($c->has('alias')); + $this->assertSame($c->get('alias'), $c->get('bar')); + } + + public function testThatCloningIsNotSupported() + { + $class = new \ReflectionClass('Symfony\Component\DependencyInjection\Container'); + $clone = $class->getMethod('__clone'); + $this->assertFalse($class->isCloneable()); + $this->assertTrue($clone->isPrivate()); + } +} + +class ProjectServiceContainer extends Container +{ + public $__bar, $__foo_bar, $__foo_baz; + + public function __construct() + { + parent::__construct(); + + $this->__bar = new \stdClass(); + $this->__foo_bar = new \stdClass(); + $this->__foo_baz = new \stdClass(); + $this->aliases = array('alias' => 'bar'); + } + + protected function getBarService() + { + return $this->__bar; + } + + protected function getFooBarService() + { + return $this->__foo_bar; + } + + protected function getFoo_BazService() + { + return $this->__foo_baz; + } + + protected function getCircularService() + { + return $this->get('circular'); + } + + protected function getThrowExceptionService() + { + throw new \Exception('Something went terribly wrong!'); + } + + protected function getThrowsExceptionOnServiceConfigurationService() + { + $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass(); + + throw new \Exception('Something was terribly wrong while trying to configure the service!'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php new file mode 100644 index 0000000..692d73d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/CrossCheckTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; + +class CrossCheckTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/Fixtures/'; + + require_once self::$fixturesPath.'/includes/classes.php'; + require_once self::$fixturesPath.'/includes/foo.php'; + } + + /** + * @dataProvider crossCheckLoadersDumpers + */ + public function testCrossCheck($fixture, $type) + { + $loaderClass = 'Symfony\\Component\\DependencyInjection\\Loader\\'.ucfirst($type).'FileLoader'; + $dumperClass = 'Symfony\\Component\\DependencyInjection\\Dumper\\'.ucfirst($type).'Dumper'; + + $tmp = tempnam('sf_service_container', 'sf'); + + file_put_contents($tmp, file_get_contents(self::$fixturesPath.'/'.$type.'/'.$fixture)); + + $container1 = new ContainerBuilder(); + $loader1 = new $loaderClass($container1, new FileLocator()); + $loader1->load($tmp); + + $dumper = new $dumperClass($container1); + file_put_contents($tmp, $dumper->dump()); + + $container2 = new ContainerBuilder(); + $loader2 = new $loaderClass($container2, new FileLocator()); + $loader2->load($tmp); + + unlink($tmp); + + $this->assertEquals($container2->getAliases(), $container1->getAliases(), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container2->getDefinitions(), $container1->getDefinitions(), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container2->getParameterBag()->all(), $container1->getParameterBag()->all(), '->getParameterBag() returns the same value for both containers'); + + $this->assertEquals(serialize($container2), serialize($container1), 'loading a dump from a previously loaded container returns the same container'); + + $services1 = array(); + foreach ($container1 as $id => $service) { + $services1[$id] = serialize($service); + } + $services2 = array(); + foreach ($container2 as $id => $service) { + $services2[$id] = serialize($service); + } + + unset($services1['service_container'], $services2['service_container']); + + $this->assertEquals($services2, $services1, 'Iterator on the containers returns the same services'); + } + + public function crossCheckLoadersDumpers() + { + $tests = array( + array('services1.xml', 'xml'), + array('services2.xml', 'xml'), + array('services6.xml', 'xml'), + array('services8.xml', 'xml'), + array('services9.xml', 'xml'), + ); + + if (class_exists('Symfony\Component\Yaml\Yaml')) { + $tests = array_merge($tests, array( + array('services1.yml', 'yaml'), + array('services2.yml', 'yaml'), + array('services6.yml', 'yaml'), + array('services8.yml', 'yaml'), + array('services9.yml', 'yaml'), + )); + } + + return $tests; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.php new file mode 100644 index 0000000..12122ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionDecoratorTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\DefinitionDecorator; + +class DefinitionDecoratorTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $def = new DefinitionDecorator('foo'); + + $this->assertEquals('foo', $def->getParent()); + $this->assertEquals(array(), $def->getChanges()); + } + + /** + * @dataProvider getPropertyTests + */ + public function testSetProperty($property, $changeKey) + { + $def = new DefinitionDecorator('foo'); + + $getter = 'get'.ucfirst($property); + $setter = 'set'.ucfirst($property); + + $this->assertNull($def->$getter()); + $this->assertSame($def, $def->$setter('foo')); + $this->assertEquals('foo', $def->$getter()); + $this->assertEquals(array($changeKey => true), $def->getChanges()); + } + + public function getPropertyTests() + { + return array( + array('class', 'class'), + array('factory', 'factory'), + array('configurator', 'configurator'), + array('file', 'file'), + ); + } + + public function testSetPublic() + { + $def = new DefinitionDecorator('foo'); + + $this->assertTrue($def->isPublic()); + $this->assertSame($def, $def->setPublic(false)); + $this->assertFalse($def->isPublic()); + $this->assertEquals(array('public' => true), $def->getChanges()); + } + + public function testSetLazy() + { + $def = new DefinitionDecorator('foo'); + + $this->assertFalse($def->isLazy()); + $this->assertSame($def, $def->setLazy(false)); + $this->assertFalse($def->isLazy()); + $this->assertEquals(array('lazy' => true), $def->getChanges()); + } + + public function testSetArgument() + { + $def = new DefinitionDecorator('foo'); + + $this->assertEquals(array(), $def->getArguments()); + $this->assertSame($def, $def->replaceArgument(0, 'foo')); + $this->assertEquals(array('index_0' => 'foo'), $def->getArguments()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testReplaceArgumentShouldRequireIntegerIndex() + { + $def = new DefinitionDecorator('foo'); + + $def->replaceArgument('0', 'foo'); + } + + public function testReplaceArgument() + { + $def = new DefinitionDecorator('foo'); + + $def->setArguments(array(0 => 'foo', 1 => 'bar')); + $this->assertEquals('foo', $def->getArgument(0)); + $this->assertEquals('bar', $def->getArgument(1)); + + $this->assertSame($def, $def->replaceArgument(1, 'baz')); + $this->assertEquals('foo', $def->getArgument(0)); + $this->assertEquals('baz', $def->getArgument(1)); + + $this->assertEquals(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz'), $def->getArguments()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetArgumentShouldCheckBounds() + { + $def = new DefinitionDecorator('foo'); + + $def->setArguments(array(0 => 'foo')); + $def->replaceArgument(0, 'foo'); + + $def->getArgument(1); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php new file mode 100644 index 0000000..3dc669f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php @@ -0,0 +1,308 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class DefinitionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $def = new Definition('stdClass'); + $this->assertEquals('stdClass', $def->getClass(), '__construct() takes the class name as its first argument'); + + $def = new Definition('stdClass', array('foo')); + $this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument'); + } + + public function testSetGetFactory() + { + $def = new Definition('stdClass'); + + $this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface'); + $this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory'); + + $def->setFactory('Foo::bar'); + $this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array'); + } + + public function testSetGetClass() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setClass('foo'), '->setClass() implements a fluent interface'); + $this->assertEquals('foo', $def->getClass(), '->getClass() returns the class name'); + } + + public function testSetGetDecoratedService() + { + $def = new Definition('stdClass'); + $this->assertNull($def->getDecoratedService()); + $def->setDecoratedService('foo', 'foo.renamed', 5); + $this->assertEquals(array('foo', 'foo.renamed', 5), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + + $def = new Definition('stdClass'); + $this->assertNull($def->getDecoratedService()); + $def->setDecoratedService('foo', 'foo.renamed'); + $this->assertEquals(array('foo', 'foo.renamed', 0), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + + $def = new Definition('stdClass'); + $def->setDecoratedService('foo'); + $this->assertEquals(array('foo', null, 0), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + + $def = new Definition('stdClass'); + $this->setExpectedException('InvalidArgumentException', 'The decorated service inner name for "foo" must be different than the service name itself.'); + $def->setDecoratedService('foo', 'foo'); + } + + public function testArguments() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setArguments(array('foo')), '->setArguments() implements a fluent interface'); + $this->assertEquals(array('foo'), $def->getArguments(), '->getArguments() returns the arguments'); + $this->assertSame($def, $def->addArgument('bar'), '->addArgument() implements a fluent interface'); + $this->assertEquals(array('foo', 'bar'), $def->getArguments(), '->addArgument() adds an argument'); + } + + public function testMethodCalls() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setMethodCalls(array(array('foo', array('foo')))), '->setMethodCalls() implements a fluent interface'); + $this->assertEquals(array(array('foo', array('foo'))), $def->getMethodCalls(), '->getMethodCalls() returns the methods to call'); + $this->assertSame($def, $def->addMethodCall('bar', array('bar')), '->addMethodCall() implements a fluent interface'); + $this->assertEquals(array(array('foo', array('foo')), array('bar', array('bar'))), $def->getMethodCalls(), '->addMethodCall() adds a method to call'); + $this->assertTrue($def->hasMethodCall('bar'), '->hasMethodCall() returns true if first argument is a method to call registered'); + $this->assertFalse($def->hasMethodCall('no_registered'), '->hasMethodCall() returns false if first argument is not a method to call registered'); + $this->assertSame($def, $def->removeMethodCall('bar'), '->removeMethodCall() implements a fluent interface'); + $this->assertEquals(array(array('foo', array('foo'))), $def->getMethodCalls(), '->removeMethodCall() removes a method to call'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Method name cannot be empty. + */ + public function testExceptionOnEmptyMethodCall() + { + $def = new Definition('stdClass'); + $def->addMethodCall(''); + } + + public function testSetGetFile() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setFile('foo'), '->setFile() implements a fluent interface'); + $this->assertEquals('foo', $def->getFile(), '->getFile() returns the file to include'); + } + + public function testSetIsShared() + { + $def = new Definition('stdClass'); + $this->assertTrue($def->isShared(), '->isShared() returns true by default'); + $this->assertSame($def, $def->setShared(false), '->setShared() implements a fluent interface'); + $this->assertFalse($def->isShared(), '->isShared() returns false if the instance must not be shared'); + } + + public function testSetIsPublic() + { + $def = new Definition('stdClass'); + $this->assertTrue($def->isPublic(), '->isPublic() returns true by default'); + $this->assertSame($def, $def->setPublic(false), '->setPublic() implements a fluent interface'); + $this->assertFalse($def->isPublic(), '->isPublic() returns false if the instance must not be public.'); + } + + public function testSetIsSynthetic() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isSynthetic(), '->isSynthetic() returns false by default'); + $this->assertSame($def, $def->setSynthetic(true), '->setSynthetic() implements a fluent interface'); + $this->assertTrue($def->isSynthetic(), '->isSynthetic() returns true if the service is synthetic.'); + } + + public function testSetIsLazy() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isLazy(), '->isLazy() returns false by default'); + $this->assertSame($def, $def->setLazy(true), '->setLazy() implements a fluent interface'); + $this->assertTrue($def->isLazy(), '->isLazy() returns true if the service is lazy.'); + } + + public function testSetIsAbstract() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAbstract(), '->isAbstract() returns false by default'); + $this->assertSame($def, $def->setAbstract(true), '->setAbstract() implements a fluent interface'); + $this->assertTrue($def->isAbstract(), '->isAbstract() returns true if the instance must not be public.'); + } + + public function testSetIsDeprecated() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isDeprecated(), '->isDeprecated() returns false by default'); + $this->assertSame($def, $def->setDeprecated(true), '->setDeprecated() implements a fluent interface'); + $this->assertTrue($def->isDeprecated(), '->isDeprecated() returns true if the instance should not be used anymore.'); + $this->assertSame('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', $def->getDeprecationMessage('deprecated_service'), '->getDeprecationMessage() should return a formatted message template'); + } + + /** + * @dataProvider invalidDeprecationMessageProvider + * @expectedException Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testSetDeprecatedWithInvalidDeprecationTemplate($message) + { + $def = new Definition('stdClass'); + $def->setDeprecated(false, $message); + } + + public function invalidDeprecationMessageProvider() + { + return array( + "With \rs" => array("invalid \r message %service_id%"), + "With \ns" => array("invalid \n message %service_id%"), + 'With */s' => array('invalid */ message %service_id%'), + 'message not containing require %service_id% variable' => array('this is deprecated'), + ); + } + + public function testSetGetConfigurator() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setConfigurator('foo'), '->setConfigurator() implements a fluent interface'); + $this->assertEquals('foo', $def->getConfigurator(), '->getConfigurator() returns the configurator'); + } + + public function testClearTags() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->clearTags(), '->clearTags() implements a fluent interface'); + $def->addTag('foo', array('foo' => 'bar')); + $def->clearTags(); + $this->assertEquals(array(), $def->getTags(), '->clearTags() removes all current tags'); + } + + public function testClearTag() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->clearTags(), '->clearTags() implements a fluent interface'); + $def->addTag('1foo1', array('foo1' => 'bar1')); + $def->addTag('2foo2', array('foo2' => 'bar2')); + $def->addTag('3foo3', array('foo3' => 'bar3')); + $def->clearTag('2foo2'); + $this->assertTrue($def->hasTag('1foo1')); + $this->assertFalse($def->hasTag('2foo2')); + $this->assertTrue($def->hasTag('3foo3')); + $def->clearTag('1foo1'); + $this->assertFalse($def->hasTag('1foo1')); + $this->assertTrue($def->hasTag('3foo3')); + } + + public function testTags() + { + $def = new Definition('stdClass'); + $this->assertEquals(array(), $def->getTag('foo'), '->getTag() returns an empty array if the tag is not defined'); + $this->assertFalse($def->hasTag('foo')); + $this->assertSame($def, $def->addTag('foo'), '->addTag() implements a fluent interface'); + $this->assertTrue($def->hasTag('foo')); + $this->assertEquals(array(array()), $def->getTag('foo'), '->getTag() returns attributes for a tag name'); + $def->addTag('foo', array('foo' => 'bar')); + $this->assertEquals(array(array(), array('foo' => 'bar')), $def->getTag('foo'), '->addTag() can adds the same tag several times'); + $def->addTag('bar', array('bar' => 'bar')); + $this->assertEquals($def->getTags(), array( + 'foo' => array(array(), array('foo' => 'bar')), + 'bar' => array(array('bar' => 'bar')), + ), '->getTags() returns all tags'); + } + + public function testSetArgument() + { + $def = new Definition('stdClass'); + + $def->addArgument('foo'); + $this->assertSame(array('foo'), $def->getArguments()); + + $this->assertSame($def, $def->replaceArgument(0, 'moo')); + $this->assertSame(array('moo'), $def->getArguments()); + + $def->addArgument('moo'); + $def + ->replaceArgument(0, 'foo') + ->replaceArgument(1, 'bar') + ; + $this->assertSame(array('foo', 'bar'), $def->getArguments()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetArgumentShouldCheckBounds() + { + $def = new Definition('stdClass'); + + $def->addArgument('foo'); + $def->getArgument(1); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testReplaceArgumentShouldCheckBounds() + { + $def = new Definition('stdClass'); + + $def->addArgument('foo'); + $def->replaceArgument(1, 'bar'); + } + + public function testSetGetProperties() + { + $def = new Definition('stdClass'); + + $this->assertEquals(array(), $def->getProperties()); + $this->assertSame($def, $def->setProperties(array('foo' => 'bar'))); + $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); + } + + public function testSetProperty() + { + $def = new Definition('stdClass'); + + $this->assertEquals(array(), $def->getProperties()); + $this->assertSame($def, $def->setProperty('foo', 'bar')); + $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); + } + + public function testAutowired() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAutowired()); + $def->setAutowired(true); + $this->assertTrue($def->isAutowired()); + } + + public function testTypes() + { + $def = new Definition('stdClass'); + + $this->assertEquals(array(), $def->getAutowiringTypes()); + $this->assertSame($def, $def->setAutowiringTypes(array('Foo'))); + $this->assertEquals(array('Foo'), $def->getAutowiringTypes()); + $this->assertSame($def, $def->addAutowiringType('Bar')); + $this->assertTrue($def->hasAutowiringType('Bar')); + $this->assertSame($def, $def->removeAutowiringType('Foo')); + $this->assertEquals(array('Bar'), $def->getAutowiringTypes()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php new file mode 100644 index 0000000..99c6c71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/GraphvizDumperTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\GraphvizDumper; + +class GraphvizDumperTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + } + + public function testDump() + { + $dumper = new GraphvizDumper($container = new ContainerBuilder()); + + $this->assertStringEqualsFile(self::$fixturesPath.'/graphviz/services1.dot', $dumper->dump(), '->dump() dumps an empty container as an empty dot file'); + + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services9.dot')), $dumper->dump(), '->dump() dumps services'); + + $container = include self::$fixturesPath.'/containers/container10.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services10.dot')), $dumper->dump(), '->dump() dumps services'); + + $container = include self::$fixturesPath.'/containers/container10.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals($dumper->dump(array( + 'graph' => array('ratio' => 'normal'), + 'node' => array('fontsize' => 13, 'fontname' => 'Verdana', 'shape' => 'square'), + 'edge' => array('fontsize' => 12, 'fontname' => 'Verdana', 'color' => 'white', 'arrowhead' => 'closed', 'arrowsize' => 1), + 'node.instance' => array('fillcolor' => 'green', 'style' => 'empty'), + 'node.definition' => array('fillcolor' => 'grey'), + 'node.missing' => array('fillcolor' => 'red', 'style' => 'empty'), + )), str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services10-1.dot')), '->dump() dumps services'); + } + + public function testDumpWithFrozenContainer() + { + $container = include self::$fixturesPath.'/containers/container13.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services13.dot')), $dumper->dump(), '->dump() dumps services'); + } + + public function testDumpWithFrozenCustomClassContainer() + { + $container = include self::$fixturesPath.'/containers/container14.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services14.dot')), $dumper->dump(), '->dump() dumps services'); + } + + public function testDumpWithUnresolvedParameter() + { + $container = include self::$fixturesPath.'/containers/container17.php'; + $dumper = new GraphvizDumper($container); + + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services17.dot')), $dumper->dump(), '->dump() dumps services'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php new file mode 100644 index 0000000..81b633d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -0,0 +1,244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Definition; + +class PhpDumperTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + public function testDump() + { + $dumper = new PhpDumper($container = new ContainerBuilder()); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1-1.php', $dumper->dump(array('class' => 'Container', 'base_class' => 'AbstractContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Dump')), '->dump() takes a class and a base_class options'); + + $container = new ContainerBuilder(); + new PhpDumper($container); + } + + public function testDumpOptimizationString() + { + $definition = new Definition(); + $definition->setClass('stdClass'); + $definition->addArgument(array( + 'only dot' => '.', + 'concatenation as value' => '.\'\'.', + 'concatenation from the start value' => '\'\'.', + '.' => 'dot as a key', + '.\'\'.' => 'concatenation as a key', + '\'\'.' => 'concatenation from the start key', + 'optimize concatenation' => 'string1%some_string%string2', + 'optimize concatenation with empty string' => 'string1%empty_value%string2', + 'optimize concatenation from the start' => '%empty_value%start', + 'optimize concatenation at the end' => 'end%empty_value%', + )); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->setDefinition('test', $definition); + $container->setParameter('empty_value', ''); + $container->setParameter('some_string', '-'); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services10.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); + } + + public function testDumpRelativeDir() + { + $definition = new Definition(); + $definition->setClass('stdClass'); + $definition->addArgument('%foo%'); + $definition->addArgument(array('%foo%' => '%buz%/')); + + $container = new ContainerBuilder(); + $container->setDefinition('test', $definition); + $container->setParameter('foo', 'wiz'.dirname(__DIR__)); + $container->setParameter('bar', __DIR__); + $container->setParameter('baz', '%bar%/PhpDumperTest.php'); + $container->setParameter('buz', dirname(dirname(__DIR__))); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testExportParameters() + { + $dumper = new PhpDumper(new ContainerBuilder(new ParameterBag(array('foo' => new Reference('foo'))))); + $dumper->dump(); + } + + public function testAddParameters() + { + $container = include self::$fixturesPath.'/containers/container8.php'; + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services8.php', $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddService() + { + // without compilation + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new PhpDumper($container); + $this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9.php')), $dumper->dump(), '->dump() dumps services'); + + // with compilation + $container = include self::$fixturesPath.'/containers/container9.php'; + $container->compile(); + $dumper = new PhpDumper($container); + $this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9_compiled.php')), $dumper->dump(), '->dump() dumps services'); + + $dumper = new PhpDumper($container = new ContainerBuilder()); + $container->register('foo', 'FooClass')->addArgument(new \stdClass()); + try { + $dumper->dump(); + $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } + } + + public function testServicesWithAnonymousFactories() + { + $container = include self::$fixturesPath.'/containers/container19.php'; + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Service id "bar$" cannot be converted to a valid PHP method name. + */ + public function testAddServiceInvalidServiceId() + { + $container = new ContainerBuilder(); + $container->register('bar$', 'FooClass'); + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + /** + * @dataProvider provideInvalidFactories + * @expectedException Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot dump definition + */ + public function testInvalidFactories($factory) + { + $container = new ContainerBuilder(); + $def = new Definition('stdClass'); + $def->setFactory($factory); + $container->setDefinition('bar', $def); + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + public function provideInvalidFactories() + { + return array( + array(array('', 'method')), + array(array('class', '')), + array(array('...', 'method')), + array(array('class', '...')), + ); + } + + public function testAliases() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $container->compile(); + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Aliases'))); + + $container = new \Symfony_DI_PhpDumper_Test_Aliases(); + $container->set('foo', $foo = new \stdClass()); + $this->assertSame($foo, $container->get('foo')); + $this->assertSame($foo, $container->get('alias_for_foo')); + $this->assertSame($foo, $container->get('alias_for_alias')); + } + + public function testFrozenContainerWithoutAliases() + { + $container = new ContainerBuilder(); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Frozen_No_Aliases'))); + + $container = new \Symfony_DI_PhpDumper_Test_Frozen_No_Aliases(); + $this->assertFalse($container->has('foo')); + } + + public function testOverrideServiceWhenUsingADumpedContainer() + { + require_once self::$fixturesPath.'/php/services9.php'; + require_once self::$fixturesPath.'/includes/foo.php'; + + $container = new \ProjectServiceContainer(); + $container->set('bar', $bar = new \stdClass()); + $container->setParameter('foo_bar', 'foo_bar'); + + $this->assertEquals($bar, $container->get('bar'), '->set() overrides an already defined service'); + } + + public function testOverrideServiceWhenUsingADumpedContainerAndServiceIsUsedFromAnotherOne() + { + require_once self::$fixturesPath.'/php/services9.php'; + require_once self::$fixturesPath.'/includes/foo.php'; + require_once self::$fixturesPath.'/includes/classes.php'; + + $container = new \ProjectServiceContainer(); + $container->set('bar', $bar = new \stdClass()); + + $this->assertSame($bar, $container->get('foo')->bar, '->set() overrides an already defined service'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testCircularReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->addArgument(new Reference('bar')); + $container->register('bar', 'stdClass')->setPublic(false)->addMethodCall('setA', array(new Reference('baz'))); + $container->register('baz', 'stdClass')->addMethodCall('setA', array(new Reference('foo'))); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + public function testDumpAutowireData() + { + $container = include self::$fixturesPath.'/containers/container24.php'; + $dumper = new PhpDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/php/services24.php'), $dumper->dump()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php new file mode 100644 index 0000000..ead2fc1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/XmlDumperTest.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\XmlDumper; + +class XmlDumperTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + public function testDump() + { + $dumper = new XmlDumper(new ContainerBuilder()); + + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services1.xml', $dumper->dump(), '->dump() dumps an empty container as an empty XML file'); + } + + public function testExportParameters() + { + $container = include self::$fixturesPath.'//containers/container8.php'; + $dumper = new XmlDumper($container); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services8.xml', $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddParameters() + { + $container = include self::$fixturesPath.'//containers/container8.php'; + $dumper = new XmlDumper($container); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services8.xml', $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddService() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(str_replace('%path%', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/xml/services9.xml')), $dumper->dump(), '->dump() dumps services'); + + $dumper = new XmlDumper($container = new ContainerBuilder()); + $container->register('foo', 'FooClass')->addArgument(new \stdClass()); + try { + $dumper->dump(); + $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } + } + + public function testDumpAnonymousServices() + { + $container = include self::$fixturesPath.'/containers/container11.php'; + $dumper = new XmlDumper($container); + $this->assertEquals(' + + + + + + + + + + + + + +', $dumper->dump()); + } + + public function testDumpEntities() + { + $container = include self::$fixturesPath.'/containers/container12.php'; + $dumper = new XmlDumper($container); + $this->assertEquals(" + + + + + foo<>&bar + + + +", $dumper->dump()); + } + + /** + * @dataProvider provideDecoratedServicesData + */ + public function testDumpDecoratedServices($expectedXmlDump, $container) + { + $dumper = new XmlDumper($container); + $this->assertEquals($expectedXmlDump, $dumper->dump()); + } + + public function provideDecoratedServicesData() + { + $fixturesPath = realpath(__DIR__.'/../Fixtures/'); + + return array( + array(" + + + + + +", include $fixturesPath.'/containers/container15.php'), + array(" + + + + + +", include $fixturesPath.'/containers/container16.php'), + ); + } + + /** + * @dataProvider provideCompiledContainerData + */ + public function testCompiledContainerCanBeDumped($containerFile) + { + $fixturesPath = __DIR__.'/../Fixtures'; + $container = require $fixturesPath.'/containers/'.$containerFile.'.php'; + $container->compile(); + $dumper = new XmlDumper($container); + $dumper->dump(); + } + + public function provideCompiledContainerData() + { + return array( + array('container8'), + array('container9'), + array('container11'), + array('container12'), + array('container14'), + ); + } + + public function testDumpInlinedServices() + { + $container = include self::$fixturesPath.'/containers/container21.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services21.xml'), $dumper->dump()); + } + + public function testDumpAutowireData() + { + $container = include self::$fixturesPath.'/containers/container24.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services24.xml'), $dumper->dump()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php new file mode 100644 index 0000000..a2ed68c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\YamlDumper; + +class YamlDumperTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + public function testDump() + { + $dumper = new YamlDumper($container = new ContainerBuilder()); + + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services1.yml', $dumper->dump(), '->dump() dumps an empty container as an empty YAML file'); + + $container = new ContainerBuilder(); + $dumper = new YamlDumper($container); + } + + public function testAddParameters() + { + $container = include self::$fixturesPath.'/containers/container8.php'; + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services8.yml', $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddService() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new YamlDumper($container); + $this->assertEquals(str_replace('%path%', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/yaml/services9.yml')), $dumper->dump(), '->dump() dumps services'); + + $dumper = new YamlDumper($container = new ContainerBuilder()); + $container->register('foo', 'FooClass')->addArgument(new \stdClass()); + try { + $dumper->dump(); + $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } + } + + public function testDumpAutowireData() + { + $container = include self::$fixturesPath.'/containers/container24.php'; + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php new file mode 100644 index 0000000..e7f19a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Extension; + +class ExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getResolvedEnabledFixtures + */ + public function testIsConfigEnabledReturnsTheResolvedValue($enabled) + { + $pb = $this->getMockBuilder('Symfony\Component\DependencyInjection\ParameterBag\ParameterBag') + ->setMethods(array('resolveValue')) + ->getMock() + ; + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder') + ->setMethods(array('getParameterBag')) + ->getMock() + ; + + $pb->expects($this->once()) + ->method('resolveValue') + ->with($this->equalTo($enabled)) + ->will($this->returnValue($enabled)) + ; + + $container->expects($this->once()) + ->method('getParameterBag') + ->will($this->returnValue($pb)) + ; + + $extension = $this->getMockBuilder('Symfony\Component\DependencyInjection\Extension\Extension') + ->setMethods(array()) + ->getMockForAbstractClass() + ; + + $r = new \ReflectionMethod('Symfony\Component\DependencyInjection\Extension\Extension', 'isConfigEnabled'); + $r->setAccessible(true); + + $r->invoke($extension, $container, array('enabled' => $enabled)); + } + + public function getResolvedEnabledFixtures() + { + return array( + array(true), + array(false), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The config array has no 'enabled' key. + */ + public function testIsConfigEnabledOnNonEnableableConfig() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder') + ->getMock() + ; + + $extension = $this->getMockBuilder('Symfony\Component\DependencyInjection\Extension\Extension') + ->setMethods(array()) + ->getMockForAbstractClass() + ; + + $r = new \ReflectionMethod('Symfony\Component\DependencyInjection\Extension\Extension', 'isConfigEnabled'); + $r->setAccessible(true); + + $r->invoke($extension, $container, array()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container10.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container10.php new file mode 100644 index 0000000..a16ca9f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container10.php @@ -0,0 +1,14 @@ + + register('foo', 'FooClass')-> + addArgument(new Reference('bar')) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container11.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container11.php new file mode 100644 index 0000000..3e6cafc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container11.php @@ -0,0 +1,12 @@ + + register('foo', 'FooClass')-> + addArgument(new Definition('BarClass', array(new Definition('BazClass')))) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container12.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container12.php new file mode 100644 index 0000000..73c5b4e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container12.php @@ -0,0 +1,12 @@ + + register('foo', 'FooClass\\Foo')-> + addArgument('foo<>&bar')-> + addTag('foo"bar\\bar', array('foo' => 'foo"barřž€')) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container13.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container13.php new file mode 100644 index 0000000..cc716c7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container13.php @@ -0,0 +1,16 @@ + + register('foo', 'FooClass')-> + addArgument(new Reference('bar')) +; +$container-> + register('bar', 'BarClass') +; +$container->compile(); + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container14.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container14.php new file mode 100644 index 0000000..191afb8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container14.php @@ -0,0 +1,17 @@ +register('foo', 'FooClass\\Foo') + ->setDecoratedService('bar', 'bar.woozy') +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container16.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container16.php new file mode 100644 index 0000000..67b4d35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container16.php @@ -0,0 +1,11 @@ +register('foo', 'FooClass\\Foo') + ->setDecoratedService('bar') +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container17.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container17.php new file mode 100644 index 0000000..d902ec2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container17.php @@ -0,0 +1,10 @@ +register('foo', '%foo.class%') +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php new file mode 100644 index 0000000..64b8f06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php @@ -0,0 +1,22 @@ +register('service_from_anonymous_factory', 'Bar\FooClass') + ->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')) +; + +$anonymousServiceWithFactory = new Definition('Bar\FooClass'); +$anonymousServiceWithFactory->setFactory('Bar\FooClass::getInstance'); +$container + ->register('service_with_method_call_and_factory', 'Bar\FooClass') + ->addMethodCall('setBar', array($anonymousServiceWithFactory)) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container21.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container21.php new file mode 100644 index 0000000..d046738 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container21.php @@ -0,0 +1,20 @@ +setConfigurator(array(new Definition('Baz'), 'configureBar')); + +$fooFactory = new Definition('FooFactory'); +$fooFactory->setFactory(array(new Definition('Foobar'), 'createFooFactory')); + +$container + ->register('foo', 'Foo') + ->setFactory(array($fooFactory, 'createFoo')) + ->setConfigurator(array($bar, 'configureFoo')) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container24.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container24.php new file mode 100644 index 0000000..df45b0d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container24.php @@ -0,0 +1,15 @@ +register('foo', 'Foo') + ->setAutowired(true) + ->addAutowiringType('A') + ->addAutowiringType('B') +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php new file mode 100644 index 0000000..f0b4ca9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container8.php @@ -0,0 +1,14 @@ + '%baz%', + 'baz' => 'bar', + 'bar' => 'foo is %%foo bar', + 'escape' => '@escapeme', + 'values' => array(true, false, null, 0, 1000.3, 'true', 'false', 'null'), +))); + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php new file mode 100644 index 0000000..96f334f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container9.php @@ -0,0 +1,115 @@ +register('foo', '\Bar\FooClass') + ->addTag('foo', array('foo' => 'foo')) + ->addTag('foo', array('bar' => 'bar', 'baz' => 'baz')) + ->setFactory(array('Bar\\FooClass', 'getInstance')) + ->setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container'))) + ->setProperties(array('foo' => 'bar', 'moo' => new Reference('foo.baz'), 'qux' => array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'))) + ->addMethodCall('setBar', array(new Reference('bar'))) + ->addMethodCall('initialize') + ->setConfigurator('sc_configure') +; +$container + ->register('foo.baz', '%baz_class%') + ->setFactory(array('%baz_class%', 'getInstance')) + ->setConfigurator(array('%baz_class%', 'configureStatic1')) +; +$container + ->register('bar', 'Bar\FooClass') + ->setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar'))) + ->setConfigurator(array(new Reference('foo.baz'), 'configure')) +; +$container + ->register('foo_bar', '%foo_class%') + ->setShared(false) +; +$container->getParameterBag()->clear(); +$container->getParameterBag()->add(array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\FooClass', + 'foo' => 'bar', +)); +$container->setAlias('alias_for_foo', 'foo'); +$container->setAlias('alias_for_alias', 'alias_for_foo'); +$container + ->register('method_call1', 'Bar\FooClass') + ->setFile(realpath(__DIR__.'/../includes/foo.php')) + ->addMethodCall('setBar', array(new Reference('foo'))) + ->addMethodCall('setBar', array(new Reference('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE))) + ->addMethodCall('setBar', array(new Reference('foo3', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) + ->addMethodCall('setBar', array(new Reference('foobaz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) + ->addMethodCall('setBar', array(new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'))) +; +$container + ->register('foo_with_inline', 'Foo') + ->addMethodCall('setBar', array(new Reference('inlined'))) +; +$container + ->register('inlined', 'Bar') + ->setProperty('pub', 'pub') + ->addMethodCall('setBaz', array(new Reference('baz'))) + ->setPublic(false) +; +$container + ->register('baz', 'Baz') + ->addMethodCall('setFoo', array(new Reference('foo_with_inline'))) +; +$container + ->register('request', 'Request') + ->setSynthetic(true) +; +$container + ->register('configurator_service', 'ConfClass') + ->setPublic(false) + ->addMethodCall('setFoo', array(new Reference('baz'))) +; +$container + ->register('configured_service', 'stdClass') + ->setConfigurator(array(new Reference('configurator_service'), 'configureStdClass')) +; +$container + ->register('decorated', 'stdClass') +; +$container + ->register('decorator_service', 'stdClass') + ->setDecoratedService('decorated') +; +$container + ->register('decorator_service_with_name', 'stdClass') + ->setDecoratedService('decorated', 'decorated.pif-pouf') +; +$container + ->register('deprecated_service', 'stdClass') + ->setDeprecated(true) +; +$container + ->register('new_factory', 'FactoryClass') + ->setProperty('foo', 'bar') + ->setPublic(false) +; +$container + ->register('factory_service', 'Bar') + ->setFactory(array(new Reference('foo.baz'), 'getInstance')) +; +$container + ->register('new_factory_service', 'FooBarBaz') + ->setProperty('foo', 'bar') + ->setFactory(array(new Reference('new_factory'), 'getInstance')) +; +$container + ->register('service_from_static_method', 'Bar\FooClass') + ->setFactory(array('Bar\FooClass', 'getInstance')) +; + +return $container; diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml new file mode 100644 index 0000000..35ec4a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/import/import.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../recurse/ } diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini new file mode 100644 index 0000000..0984cda --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.ini @@ -0,0 +1,2 @@ +[parameters] + ini = ini diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml new file mode 100644 index 0000000..f98ef12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/recurse/simple.yml @@ -0,0 +1,2 @@ +parameters: + yaml: yaml diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php new file mode 100644 index 0000000..4750324 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/directory/simple.php @@ -0,0 +1,3 @@ +setParameter('php', 'php'); diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services1.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services1.dot new file mode 100644 index 0000000..1bb7c30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services1.dot @@ -0,0 +1,7 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services10-1.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services10-1.dot new file mode 100644 index 0000000..0e578b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services10-1.dot @@ -0,0 +1,10 @@ +digraph sc { + ratio="normal" + node [fontsize="13" fontname="Verdana" shape="square"]; + edge [fontsize="12" fontname="Verdana" color="white" arrowhead="closed" arrowsize="1"]; + + node_foo [label="foo\nFooClass\n", shape=square, fillcolor="grey", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=square, fillcolor="green", style="empty"]; + node_bar [label="bar\n\n", shape=square, fillcolor="red", style="empty"]; + node_foo -> node_bar [label="" style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services10.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services10.dot new file mode 100644 index 0000000..f17857f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services10.dot @@ -0,0 +1,10 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; + node_bar [label="bar\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foo -> node_bar [label="" style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services13.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services13.dot new file mode 100644 index 0000000..bc7f813 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services13.dot @@ -0,0 +1,10 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar [label="bar\nBarClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; + node_foo -> node_bar [label="" style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services14.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services14.dot new file mode 100644 index 0000000..d07dc38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services14.dot @@ -0,0 +1,7 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container\nContainer14\\ProjectServiceContainer\n", shape=record, fillcolor="#9999ff", style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services17.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services17.dot new file mode 100644 index 0000000..a6d04bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services17.dot @@ -0,0 +1,8 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_foo [label="foo\n%foo.class%\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services18.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services18.dot new file mode 100644 index 0000000..4fbccee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services18.dot @@ -0,0 +1,8 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot new file mode 100644 index 0000000..f653698 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/graphviz/services9.dot @@ -0,0 +1,42 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_foo [label="foo (alias_for_foo)\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_baz [label="foo.baz\nBazClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar [label="bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_bar [label="foo_bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"]; + node_method_call1 [label="method_call1\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_with_inline [label="foo_with_inline\nFoo\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_inlined [label="inlined\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_baz [label="baz\nBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_request [label="request\nRequest\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configurator_service [label="configurator_service\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configured_service [label="configured_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_deprecated_service [label="deprecated_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; + node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foo -> node_foo_baz [label="" style="filled"]; + node_foo -> node_service_container [label="" style="filled"]; + node_foo -> node_foo_baz [label="" style="dashed"]; + node_foo -> node_bar [label="setBar()" style="dashed"]; + node_bar -> node_foo_baz [label="" style="filled"]; + node_method_call1 -> node_foo [label="setBar()" style="dashed"]; + node_method_call1 -> node_foo2 [label="setBar()" style="dashed"]; + node_method_call1 -> node_foo3 [label="setBar()" style="dashed"]; + node_method_call1 -> node_foobaz [label="setBar()" style="dashed"]; + node_foo_with_inline -> node_inlined [label="setBar()" style="dashed"]; + node_inlined -> node_baz [label="setBaz()" style="dashed"]; + node_baz -> node_foo_with_inline [label="setFoo()" style="dashed"]; + node_configurator_service -> node_baz [label="setFoo()" style="dashed"]; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php new file mode 100644 index 0000000..c9f8010 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php @@ -0,0 +1,40 @@ +setDefinition('project.service.bar', new Definition('FooClass')); + $configuration->setParameter('project.parameter.bar', isset($config['foo']) ? $config['foo'] : 'foobar'); + + $configuration->setDefinition('project.service.foo', new Definition('FooClass')); + $configuration->setParameter('project.parameter.foo', isset($config['foo']) ? $config['foo'] : 'foobar'); + + return $configuration; + } + + public function getXsdValidationBasePath() + { + return false; + } + + public function getNamespace() + { + return 'http://www.example.com/schema/project'; + } + + public function getAlias() + { + return 'project'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectWithXsdExtension.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectWithXsdExtension.php new file mode 100644 index 0000000..2ee2f12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectWithXsdExtension.php @@ -0,0 +1,19 @@ +configure(); +} + +class BarClass +{ + protected $baz; + public $foo = 'foo'; + + public function setBaz(BazClass $baz) + { + $this->baz = $baz; + } + + public function getBaz() + { + return $this->baz; + } +} + +class BazClass +{ + protected $foo; + + public function setFoo(Foo $foo) + { + $this->foo = $foo; + } + + public function configure($instance) + { + $instance->configure(); + } + + public static function getInstance() + { + return new self(); + } + + public static function configureStatic($instance) + { + $instance->configure(); + } + + public static function configureStatic1() + { + } +} + +class BarUserClass +{ + public $bar; + + public function __construct(BarClass $bar) + { + $this->bar = $bar; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/createphar.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/createphar.php new file mode 100644 index 0000000..5fa06a0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/createphar.php @@ -0,0 +1,47 @@ +addFromString('ProjectWithXsdExtensionInPhar.php', <<addFromString('schema/project-1.0.xsd', << + + + + + + + + + +EOT +); +$phar->setStub(''); diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php new file mode 100644 index 0000000..55968af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/foo.php @@ -0,0 +1,38 @@ +arguments = $arguments; + } + + public static function getInstance($arguments = array()) + { + $obj = new self($arguments); + $obj->called = true; + + return $obj; + } + + public function initialize() + { + $this->initialized = true; + } + + public function configure() + { + $this->configured = true; + } + + public function setBar($value = null) + { + $this->bar = $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/schema/project-1.0.xsd b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/schema/project-1.0.xsd new file mode 100644 index 0000000..282884e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/schema/project-1.0.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/nonvalid.ini b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/nonvalid.ini new file mode 100644 index 0000000..9f84a60 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/nonvalid.ini @@ -0,0 +1,2 @@ +{NOT AN INI FILE} +{JUST A PLAIN TEXT FILE} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters.ini b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters.ini new file mode 100644 index 0000000..df92f75 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters.ini @@ -0,0 +1,3 @@ +[parameters] + foo = bar + bar = %foo% diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters1.ini b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters1.ini new file mode 100644 index 0000000..e50f722 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters1.ini @@ -0,0 +1,3 @@ +[parameters] + FOO = foo + baz = baz diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters2.ini b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters2.ini new file mode 100644 index 0000000..75fbac6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/ini/parameters2.ini @@ -0,0 +1,2 @@ +[parameters] + imported_from_ini = true diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php new file mode 100644 index 0000000..0415d70 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php @@ -0,0 +1,29 @@ +parameters = $this->getDefaultParameters(); + + $this->services = array(); + $this->methodMap = array( + 'test' => 'getTestService', + ); + + $this->aliases = array(); + } + + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + + /** + * Gets the 'test' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getTestService() + { + return $this->services['test'] = new \stdClass(array('only dot' => '.', 'concatenation as value' => '.\'\'.', 'concatenation from the start value' => '\'\'.', '.' => 'dot as a key', '.\'\'.' => 'concatenation as a key', '\'\'.' => 'concatenation from the start key', 'optimize concatenation' => 'string1-string2', 'optimize concatenation with empty string' => 'string1string2', 'optimize concatenation from the start' => 'start', 'optimize concatenation at the end' => 'end')); + } + + /** + * {@inheritdoc} + */ + public function getParameter($name) + { + $name = strtolower($name); + + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + + return $this->parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter($name) + { + $name = strtolower($name); + + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === $this->parameterBag) { + $this->parameterBag = new FrozenParameterBag($this->parameters); + } + + return $this->parameterBag; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'empty_value' => '', + 'some_string' => '-', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php new file mode 100644 index 0000000..03f9f63 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services12.php @@ -0,0 +1,119 @@ +targetDirs[$i] = $dir = dirname($dir); + } + $this->parameters = $this->getDefaultParameters(); + + $this->services = array(); + $this->methodMap = array( + 'test' => 'getTestService', + ); + + $this->aliases = array(); + } + + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + + /** + * Gets the 'test' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getTestService() + { + return $this->services['test'] = new \stdClass(('wiz'.$this->targetDirs[1]), array(('wiz'.$this->targetDirs[1]) => ($this->targetDirs[2].'/'))); + } + + /** + * {@inheritdoc} + */ + public function getParameter($name) + { + $name = strtolower($name); + + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + + return $this->parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter($name) + { + $name = strtolower($name); + + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === $this->parameterBag) { + $this->parameterBag = new FrozenParameterBag($this->parameters); + } + + return $this->parameterBag; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'foo' => ('wiz'.$this->targetDirs[1]), + 'bar' => __DIR__, + 'baz' => (__DIR__.'/PhpDumperTest.php'), + 'buz' => $this->targetDirs[2], + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php new file mode 100644 index 0000000..cc2a18d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -0,0 +1,62 @@ +methodMap = array( + 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', + 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService', + ); + } + + /** + * Gets the 'service_from_anonymous_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceFromAnonymousFactoryService() + { + return $this->services['service_from_anonymous_factory'] = call_user_func(array(new \Bar\FooClass(), 'getInstance')); + } + + /** + * Gets the 'service_with_method_call_and_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceWithMethodCallAndFactoryService() + { + $this->services['service_with_method_call_and_factory'] = $instance = new \Bar\FooClass(); + + $instance->setBar(\Bar\FooClass::getInstance()); + + return $instance; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php new file mode 100644 index 0000000..8b6c1c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services24.php @@ -0,0 +1,43 @@ +methodMap = array( + 'foo' => 'getFooService', + ); + } + + /** + * Gets the 'foo' service. + * + * This service is autowired. + * + * @return \Foo A Foo instance. + */ + protected function getFooService() + { + return $this->services['foo'] = new \Foo(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php new file mode 100644 index 0000000..252a35d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services8.php @@ -0,0 +1,53 @@ +getDefaultParameters())); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'foo' => '%baz%', + 'baz' => 'bar', + 'bar' => 'foo is %%foo bar', + 'escape' => '@escapeme', + 'values' => array( + 0 => true, + 1 => false, + 2 => NULL, + 3 => 0, + 4 => 1000.3, + 5 => 'true', + 6 => 'false', + 7 => 'null', + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php new file mode 100644 index 0000000..a29f7dd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9.php @@ -0,0 +1,393 @@ +getDefaultParameters())); + $this->methodMap = array( + 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'configurator_service' => 'getConfiguratorServiceService', + 'configured_service' => 'getConfiguredServiceService', + 'decorated' => 'getDecoratedService', + 'decorator_service' => 'getDecoratorServiceService', + 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', + 'deprecated_service' => 'getDeprecatedServiceService', + 'factory_service' => 'getFactoryServiceService', + 'foo' => 'getFooService', + 'foo.baz' => 'getFoo_BazService', + 'foo_bar' => 'getFooBarService', + 'foo_with_inline' => 'getFooWithInlineService', + 'inlined' => 'getInlinedService', + 'method_call1' => 'getMethodCall1Service', + 'new_factory' => 'getNewFactoryService', + 'new_factory_service' => 'getNewFactoryServiceService', + 'request' => 'getRequestService', + 'service_from_static_method' => 'getServiceFromStaticMethodService', + ); + $this->aliases = array( + 'alias_for_alias' => 'foo', + 'alias_for_foo' => 'foo', + ); + } + + /** + * Gets the 'bar' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getBarService() + { + $a = $this->get('foo.baz'); + + $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); + + $a->configure($instance); + + return $instance; + } + + /** + * Gets the 'baz' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Baz A Baz instance. + */ + protected function getBazService() + { + $this->services['baz'] = $instance = new \Baz(); + + $instance->setFoo($this->get('foo_with_inline')); + + return $instance; + } + + /** + * Gets the 'configured_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getConfiguredServiceService() + { + $this->services['configured_service'] = $instance = new \stdClass(); + + $this->get('configurator_service')->configureStdClass($instance); + + return $instance; + } + + /** + * Gets the 'decorated' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getDecoratedService() + { + return $this->services['decorated'] = new \stdClass(); + } + + /** + * Gets the 'decorator_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getDecoratorServiceService() + { + return $this->services['decorator_service'] = new \stdClass(); + } + + /** + * Gets the 'decorator_service_with_name' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getDecoratorServiceWithNameService() + { + return $this->services['decorator_service_with_name'] = new \stdClass(); + } + + /** + * Gets the 'deprecated_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + * + * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. + */ + protected function getDeprecatedServiceService() + { + @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); + + return $this->services['deprecated_service'] = new \stdClass(); + } + + /** + * Gets the 'factory_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar A Bar instance. + */ + protected function getFactoryServiceService() + { + return $this->services['factory_service'] = $this->get('foo.baz')->getInstance(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getFooService() + { + $a = $this->get('foo.baz'); + + $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')), true, $this); + + $instance->setBar($this->get('bar')); + $instance->initialize(); + $instance->foo = 'bar'; + $instance->moo = $a; + $instance->qux = array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')); + sc_configure($instance); + + return $instance; + } + + /** + * Gets the 'foo.baz' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return object A %baz_class% instance. + */ + protected function getFoo_BazService() + { + $this->services['foo.baz'] = $instance = call_user_func(array($this->getParameter('baz_class'), 'getInstance')); + + call_user_func(array($this->getParameter('baz_class'), 'configureStatic1'), $instance); + + return $instance; + } + + /** + * Gets the 'foo_bar' service. + * + * @return object A %foo_class% instance. + */ + protected function getFooBarService() + { + $class = $this->getParameter('foo_class'); + + return new $class(); + } + + /** + * Gets the 'foo_with_inline' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Foo A Foo instance. + */ + protected function getFooWithInlineService() + { + $this->services['foo_with_inline'] = $instance = new \Foo(); + + $instance->setBar($this->get('inlined')); + + return $instance; + } + + /** + * Gets the 'method_call1' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getMethodCall1Service() + { + require_once '%path%foo.php'; + + $this->services['method_call1'] = $instance = new \Bar\FooClass(); + + $instance->setBar($this->get('foo')); + $instance->setBar($this->get('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + if ($this->has('foo3')) { + $instance->setBar($this->get('foo3', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + if ($this->has('foobaz')) { + $instance->setBar($this->get('foobaz', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + $instance->setBar(($this->get("foo")->foo() . (($this->hasparameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + + return $instance; + } + + /** + * Gets the 'new_factory_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \FooBarBaz A FooBarBaz instance. + */ + protected function getNewFactoryServiceService() + { + $this->services['new_factory_service'] = $instance = $this->get('new_factory')->getInstance(); + + $instance->foo = 'bar'; + + return $instance; + } + + /** + * Gets the 'request' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @throws RuntimeException always since this service is expected to be injected dynamically + */ + protected function getRequestService() + { + throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); + } + + /** + * Gets the 'service_from_static_method' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceFromStaticMethodService() + { + return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); + } + + /** + * Gets the 'configurator_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \ConfClass A ConfClass instance. + */ + protected function getConfiguratorServiceService() + { + $this->services['configurator_service'] = $instance = new \ConfClass(); + + $instance->setFoo($this->get('baz')); + + return $instance; + } + + /** + * Gets the 'inlined' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \Bar A Bar instance. + */ + protected function getInlinedService() + { + $this->services['inlined'] = $instance = new \Bar(); + + $instance->setBaz($this->get('baz')); + $instance->pub = 'pub'; + + return $instance; + } + + /** + * Gets the 'new_factory' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * This service is private. + * If you want to be able to request this service from the container directly, + * make it public, otherwise you might end up with broken code. + * + * @return \FactoryClass A FactoryClass instance. + */ + protected function getNewFactoryService() + { + $this->services['new_factory'] = $instance = new \FactoryClass(); + + $instance->foo = 'bar'; + + return $instance; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php new file mode 100644 index 0000000..5386b12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php @@ -0,0 +1,370 @@ +parameters = $this->getDefaultParameters(); + + $this->services = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'configured_service' => 'getConfiguredServiceService', + 'decorator_service' => 'getDecoratorServiceService', + 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', + 'deprecated_service' => 'getDeprecatedServiceService', + 'factory_service' => 'getFactoryServiceService', + 'foo' => 'getFooService', + 'foo.baz' => 'getFoo_BazService', + 'foo_bar' => 'getFooBarService', + 'foo_with_inline' => 'getFooWithInlineService', + 'method_call1' => 'getMethodCall1Service', + 'new_factory_service' => 'getNewFactoryServiceService', + 'request' => 'getRequestService', + 'service_from_static_method' => 'getServiceFromStaticMethodService', + ); + $this->aliases = array( + 'alias_for_alias' => 'foo', + 'alias_for_foo' => 'foo', + 'decorated' => 'decorator_service_with_name', + ); + } + + /** + * {@inheritdoc} + */ + public function compile() + { + throw new LogicException('You cannot compile a dumped frozen container.'); + } + + /** + * Gets the 'bar' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getBarService() + { + $a = $this->get('foo.baz'); + + $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); + + $a->configure($instance); + + return $instance; + } + + /** + * Gets the 'baz' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Baz A Baz instance. + */ + protected function getBazService() + { + $this->services['baz'] = $instance = new \Baz(); + + $instance->setFoo($this->get('foo_with_inline')); + + return $instance; + } + + /** + * Gets the 'configured_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getConfiguredServiceService() + { + $a = new \ConfClass(); + $a->setFoo($this->get('baz')); + + $this->services['configured_service'] = $instance = new \stdClass(); + + $a->configureStdClass($instance); + + return $instance; + } + + /** + * Gets the 'decorator_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getDecoratorServiceService() + { + return $this->services['decorator_service'] = new \stdClass(); + } + + /** + * Gets the 'decorator_service_with_name' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + */ + protected function getDecoratorServiceWithNameService() + { + return $this->services['decorator_service_with_name'] = new \stdClass(); + } + + /** + * Gets the 'deprecated_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \stdClass A stdClass instance. + * + * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. + */ + protected function getDeprecatedServiceService() + { + @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); + + return $this->services['deprecated_service'] = new \stdClass(); + } + + /** + * Gets the 'factory_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar A Bar instance. + */ + protected function getFactoryServiceService() + { + return $this->services['factory_service'] = $this->get('foo.baz')->getInstance(); + } + + /** + * Gets the 'foo' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getFooService() + { + $a = $this->get('foo.baz'); + + $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); + + $instance->setBar($this->get('bar')); + $instance->initialize(); + $instance->foo = 'bar'; + $instance->moo = $a; + $instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); + sc_configure($instance); + + return $instance; + } + + /** + * Gets the 'foo.baz' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \BazClass A BazClass instance. + */ + protected function getFoo_BazService() + { + $this->services['foo.baz'] = $instance = \BazClass::getInstance(); + + \BazClass::configureStatic1($instance); + + return $instance; + } + + /** + * Gets the 'foo_bar' service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getFooBarService() + { + return new \Bar\FooClass(); + } + + /** + * Gets the 'foo_with_inline' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Foo A Foo instance. + */ + protected function getFooWithInlineService() + { + $a = new \Bar(); + + $this->services['foo_with_inline'] = $instance = new \Foo(); + + $a->setBaz($this->get('baz')); + $a->pub = 'pub'; + + $instance->setBar($a); + + return $instance; + } + + /** + * Gets the 'method_call1' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getMethodCall1Service() + { + require_once '%path%foo.php'; + + $this->services['method_call1'] = $instance = new \Bar\FooClass(); + + $instance->setBar($this->get('foo')); + $instance->setBar(NULL); + $instance->setBar(($this->get("foo")->foo() . (($this->hasparameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + + return $instance; + } + + /** + * Gets the 'new_factory_service' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \FooBarBaz A FooBarBaz instance. + */ + protected function getNewFactoryServiceService() + { + $a = new \FactoryClass(); + $a->foo = 'bar'; + + $this->services['new_factory_service'] = $instance = $a->getInstance(); + + $instance->foo = 'bar'; + + return $instance; + } + + /** + * Gets the 'request' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @throws RuntimeException always since this service is expected to be injected dynamically + */ + protected function getRequestService() + { + throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); + } + + /** + * Gets the 'service_from_static_method' service. + * + * This service is shared. + * This method always returns the same instance of the service. + * + * @return \Bar\FooClass A Bar\FooClass instance. + */ + protected function getServiceFromStaticMethodService() + { + return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); + } + + /** + * {@inheritdoc} + */ + public function getParameter($name) + { + $name = strtolower($name); + + if (!(isset($this->parameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + + return $this->parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter($name) + { + $name = strtolower($name); + + return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters); + } + + /** + * {@inheritdoc} + */ + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function getParameterBag() + { + if (null === $this->parameterBag) { + $this->parameterBag = new FrozenParameterBag($this->parameters); + } + + return $this->parameterBag; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/simple.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/simple.php new file mode 100644 index 0000000..aa4df99 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/simple.php @@ -0,0 +1,3 @@ +setParameter('foo', 'foo'); diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extension1/services.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extension1/services.xml new file mode 100644 index 0000000..52df38d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extension1/services.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extension2/services.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extension2/services.xml new file mode 100644 index 0000000..21a7ef5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extension2/services.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml new file mode 100644 index 0000000..792fa07 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services1.xml @@ -0,0 +1,19 @@ + + + + + + BAR + + + + + + + + + %project.parameter.foo% + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml new file mode 100644 index 0000000..67d462b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services2.xml @@ -0,0 +1,19 @@ + + + + + + BAR + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml new file mode 100644 index 0000000..c23f02a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services3.xml @@ -0,0 +1,19 @@ + + + + + + BAR + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services4.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services4.xml new file mode 100644 index 0000000..2c33c3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services4.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services5.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services5.xml new file mode 100644 index 0000000..0eaaff2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services5.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services6.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services6.xml new file mode 100644 index 0000000..a9c0103 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services6.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services7.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services7.xml new file mode 100644 index 0000000..e77780d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/extensions/services7.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/namespaces.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/namespaces.xml new file mode 100644 index 0000000..5a05ced --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/namespaces.xml @@ -0,0 +1,17 @@ + + + + + + + + + foo + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/nonvalid.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/nonvalid.xml new file mode 100644 index 0000000..e7b5bc9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/nonvalid.xml @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services1.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services1.xml new file mode 100644 index 0000000..6aa5732 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services1.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml new file mode 100644 index 0000000..a4da1bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services13.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services13.xml new file mode 100644 index 0000000..1ac4938 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services13.xml @@ -0,0 +1,9 @@ + + + + + true + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services14.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services14.xml new file mode 100644 index 0000000..7344621 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services14.xml @@ -0,0 +1,19 @@ + + + + + app + + + + + + app + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml new file mode 100644 index 0000000..f1ac14d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services2.xml @@ -0,0 +1,31 @@ + + + + + a string + bar + + 0 + 4 + null + true + true + false + on + off + 1.3 + 1000.3 + a string + + foo + bar + + + + value + + PHP_EOL + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services21.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services21.xml new file mode 100644 index 0000000..329bc3d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services21.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml new file mode 100644 index 0000000..fa79d38 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services22.xml @@ -0,0 +1,9 @@ + + + + + Bar + Baz + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml new file mode 100644 index 0000000..3f9e15f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services23.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services24.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services24.xml new file mode 100644 index 0000000..476588a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services24.xml @@ -0,0 +1,9 @@ + + + + + A + B + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services3.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services3.xml new file mode 100644 index 0000000..87bf183 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services3.xml @@ -0,0 +1,13 @@ + + + + + foo + + true + false + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4.xml new file mode 100644 index 0000000..03ad9f8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import.xml new file mode 100644 index 0000000..0b7f10a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services4_bad_import.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml new file mode 100644 index 0000000..acb93e7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services5.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml new file mode 100644 index 0000000..ad73880 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services6.xml @@ -0,0 +1,62 @@ + + + + + + + + + %path%/foo.php + + + foo + + + true + false + + + + + + + + + + + + + + + service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default") + + + + + foo + + + true + false + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services7.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services7.xml new file mode 100644 index 0000000..824d8b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services7.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml new file mode 100644 index 0000000..b17e500 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml @@ -0,0 +1,22 @@ + + + + + %baz% + bar + foo is %%foo bar + @escapeme + + true + false + null + 0 + 1000.3 + true + false + null + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml new file mode 100644 index 0000000..4ddb865 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services9.xml @@ -0,0 +1,109 @@ + + + + BazClass + Bar\FooClass + bar + + + + + + foo + + + foo is %foo% + %foo% + + true + + bar + + + foo is %foo% + %foo% + + + + + + + + + + + + + + foo + + %foo_bar% + + + + + %path%foo.php + + + + + + + + + + + + + + service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default") + + + + + + + + + pub + + + + + + + + + + + + + + + + + + + + + + + The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + + + bar + + + + + + bar + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/withdoctype.xml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/withdoctype.xml new file mode 100644 index 0000000..f217d5b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/withdoctype.xml @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_calls.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_calls.yml new file mode 100644 index 0000000..3f34b07 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_calls.yml @@ -0,0 +1,4 @@ +services: + method_call1: + class: FooClass + calls: foo diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_format.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_format.yml new file mode 100644 index 0000000..1f58cac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_format.yml @@ -0,0 +1,2 @@ +parameters: + FOO: bar diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_import.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_import.yml new file mode 100644 index 0000000..0765dc8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_import.yml @@ -0,0 +1,2 @@ +imports: + - foo.yml diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_imports.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_imports.yml new file mode 100644 index 0000000..1ce9d57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_imports.yml @@ -0,0 +1,2 @@ +imports: + foo:bar diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_parameters.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_parameters.yml new file mode 100644 index 0000000..bbd13ac --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_parameters.yml @@ -0,0 +1,2 @@ +parameters: + foo:bar diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_service.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_service.yml new file mode 100644 index 0000000..811af3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_service.yml @@ -0,0 +1,2 @@ +services: + foo: bar diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_services.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_services.yml new file mode 100644 index 0000000..cfbf17c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_services.yml @@ -0,0 +1 @@ +services: foo diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml new file mode 100644 index 0000000..891e014 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types1.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + # types is not an array + autowiring_types: 1 diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml new file mode 100644 index 0000000..fb1d53e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/bad_types2.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + # autowiring_types is not a string + autowiring_types: [ 1 ] diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag1.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag1.yml new file mode 100644 index 0000000..14536fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag1.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + # tags is not an array + tags: string diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag2.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag2.yml new file mode 100644 index 0000000..9028814 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag2.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag is missing the name key + foo_tag: { foo: bar } diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag3.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag3.yml new file mode 100644 index 0000000..72ec4e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag3.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag-attribute is not a scalar + - { name: foo, bar: { foo: foo, bar: bar } } diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag4.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag4.yml new file mode 100644 index 0000000..e8e9939 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/badtag4.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag is not an array + - foo diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/nonvalid1.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/nonvalid1.yml new file mode 100644 index 0000000..4eddb87 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/nonvalid1.yml @@ -0,0 +1,2 @@ +foo: + bar diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/nonvalid2.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/nonvalid2.yml new file mode 100644 index 0000000..c508d53 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/nonvalid2.yml @@ -0,0 +1 @@ +false diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services1.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services1.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services1.yml @@ -0,0 +1 @@ + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml new file mode 100644 index 0000000..f2f8d95 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services10.yml @@ -0,0 +1,9 @@ +parameters: + project.parameter.foo: BAR + +services: + project.service.foo: + class: BAR + +project: + test: %project.parameter.foo% diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services11.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services11.yml new file mode 100644 index 0000000..40126f0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services11.yml @@ -0,0 +1 @@ +foobarfoobar: {} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services13.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services13.yml new file mode 100644 index 0000000..d52d355 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services13.yml @@ -0,0 +1,3 @@ +# used to test imports in XML +parameters: + imported_from_yaml: true diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml new file mode 100644 index 0000000..4c188c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services14.yml @@ -0,0 +1,3 @@ +services: + factory: { class: FooBarClass, factory: baz:getClass} + factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services2.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services2.yml new file mode 100644 index 0000000..b62d5cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services2.yml @@ -0,0 +1,12 @@ +parameters: + FOO: bar + values: + - true + - false + - 0 + - 1000.3 + bar: foo + escape: '@@escapeme' + foo_bar: '@foo_bar' + MixedCase: + MixedCaseKey: value diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services21.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services21.yml new file mode 100644 index 0000000..a2617c1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services21.yml @@ -0,0 +1,15 @@ +services: + manager: + class: UserManager + arguments: + - true + calls: + - method: setLogger + arguments: + - '@logger' + - method: setClass + arguments: + - User + tags: + - name: manager + alias: user diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml new file mode 100644 index 0000000..55d015b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services22.yml @@ -0,0 +1,8 @@ +services: + foo_service: + class: FooClass + autowiring_types: [ Foo, Bar ] + + baz_service: + class: Baz + autowiring_types: Foo diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml new file mode 100644 index 0000000..1984c17 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services23.yml @@ -0,0 +1,4 @@ +services: + bar_service: + class: BarClass + autowire: true diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services24.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services24.yml new file mode 100644 index 0000000..1894077 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services24.yml @@ -0,0 +1,8 @@ + +services: + foo: + class: Foo + autowire: true + autowiring_types: + - A + - B diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services3.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services3.yml new file mode 100644 index 0000000..0e92cdf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services3.yml @@ -0,0 +1,5 @@ +parameters: + foo: foo + values: + - true + - false diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4.yml new file mode 100644 index 0000000..8e0987f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4.yml @@ -0,0 +1,7 @@ +imports: + - { resource: services2.yml } + - { resource: services3.yml } + - { resource: "../php/simple.php" } + - { resource: "../ini/parameters.ini", class: Symfony\Component\DependencyInjection\Loader\IniFileLoader } + - { resource: "../ini/parameters2.ini" } + - { resource: "../xml/services13.xml" } diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import.yml new file mode 100644 index 0000000..f7089fc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services4_bad_import.yml @@ -0,0 +1,2 @@ +imports: + - { resource: foo_fake.yml, ignore_errors: true } diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml new file mode 100644 index 0000000..db554d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services6.yml @@ -0,0 +1,39 @@ +services: + foo: { class: FooClass } + baz: { class: BazClass } + not_shared: { class: FooClass, shared: false } + file: { class: FooClass, file: %path%/foo.php } + arguments: { class: FooClass, arguments: [foo, '@foo', [true, false]] } + configurator1: { class: FooClass, configurator: sc_configure } + configurator2: { class: FooClass, configurator: ['@baz', configure] } + configurator3: { class: FooClass, configurator: [BazClass, configureStatic] } + method_call1: + class: FooClass + calls: + - [ setBar, [] ] + - [ setBar ] + - [ setBar, ['@=service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")'] ] + method_call2: + class: FooClass + calls: + - [ setBar, [ foo, '@foo', [true, false] ] ] + alias_for_foo: '@foo' + another_alias_for_foo: + alias: foo + public: false + request: + class: Request + synthetic: true + lazy: true + decorator_service: + decorates: decorated + decorator_service_with_name: + decorates: decorated + decoration_inner_name: decorated.pif-pouf + decorator_service_with_name_and_priority: + decorates: decorated + decoration_inner_name: decorated.pif-pouf + decoration_priority: 5 + new_factory1: { class: FooBarClass, factory: factory} + new_factory2: { class: FooBarClass, factory: ['@baz', getClass]} + new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services7.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services7.yml new file mode 100644 index 0000000..09064f2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services7.yml @@ -0,0 +1,2 @@ +services: + foo: { class: BarClass } diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml new file mode 100644 index 0000000..a1fb590 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services8.yml @@ -0,0 +1,7 @@ +parameters: + foo: '%baz%' + baz: bar + bar: 'foo is %%foo bar' + escape: '@@escapeme' + values: [true, false, null, 0, 1000.3, 'true', 'false', 'null'] + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml new file mode 100644 index 0000000..37f7e19 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services9.yml @@ -0,0 +1,97 @@ +parameters: + baz_class: BazClass + foo_class: Bar\FooClass + foo: bar + +services: + foo: + class: Bar\FooClass + tags: + - { name: foo, foo: foo } + - { name: foo, bar: bar, baz: baz } + arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container'] + properties: { foo: bar, moo: '@foo.baz', qux: { '%foo%': 'foo is %foo%', foobar: '%foo%' } } + calls: + - [setBar, ['@bar']] + - [initialize, { }] + + factory: [Bar\FooClass, getInstance] + configurator: sc_configure + foo.baz: + class: %baz_class% + factory: ['%baz_class%', getInstance] + configurator: ['%baz_class%', configureStatic1] + bar: + class: Bar\FooClass + arguments: [foo, '@foo.baz', '%foo_bar%'] + configurator: ['@foo.baz', configure] + foo_bar: + class: %foo_class% + shared: false + method_call1: + class: Bar\FooClass + file: %path%foo.php + calls: + - [setBar, ['@foo']] + - [setBar, ['@?foo2']] + - [setBar, ['@?foo3']] + - [setBar, ['@?foobaz']] + - [setBar, ['@=service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")']] + + foo_with_inline: + class: Foo + calls: + - [setBar, ['@inlined']] + + inlined: + class: Bar + public: false + properties: { pub: pub } + calls: + - [setBaz, ['@baz']] + + baz: + class: Baz + calls: + - [setFoo, ['@foo_with_inline']] + + request: + class: Request + synthetic: true + configurator_service: + class: ConfClass + public: false + calls: + - [setFoo, ['@baz']] + + configured_service: + class: stdClass + configurator: ['@configurator_service', configureStdClass] + decorated: + class: stdClass + decorator_service: + class: stdClass + decorates: decorated + decorator_service_with_name: + class: stdClass + decorates: decorated + decoration_inner_name: decorated.pif-pouf + deprecated_service: + class: stdClass + deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + new_factory: + class: FactoryClass + public: false + properties: { foo: bar } + factory_service: + class: Bar + factory: ['@foo.baz', getInstance] + new_factory_service: + class: FooBarBaz + properties: { foo: bar } + factory: ['@new_factory', getInstance] + service_from_static_method: + class: Bar\FooClass + factory: [Bar\FooClass, getInstance] + alias_for_foo: '@foo' + alias_for_alias: '@foo' diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php new file mode 100644 index 0000000..6dcf25d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\LazyProxy\Instantiator; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; + +/** + * Tests for {@see \Symfony\Component\DependencyInjection\Instantiator\RealServiceInstantiator}. + * + * @author Marco Pivetta + */ +class RealServiceInstantiatorTest extends \PHPUnit_Framework_TestCase +{ + public function testInstantiateProxy() + { + $instantiator = new RealServiceInstantiator(); + $instance = new \stdClass(); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $callback = function () use ($instance) { + return $instance; + }; + + $this->assertSame($instance, $instantiator->instantiateProxy($container, new Definition(), 'foo', $callback)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/PhpDumper/NullDumperTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/PhpDumper/NullDumperTest.php new file mode 100644 index 0000000..1fcedca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/LazyProxy/PhpDumper/NullDumperTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; + +/** + * Tests for {@see \Symfony\Component\DependencyInjection\PhpDumper\NullDumper}. + * + * @author Marco Pivetta + */ +class NullDumperTest extends \PHPUnit_Framework_TestCase +{ + public function testNullDumper() + { + $dumper = new NullDumper(); + $definition = new Definition('stdClass'); + + $this->assertFalse($dumper->isProxyCandidate($definition)); + $this->assertSame('', $dumper->getProxyFactoryCode($definition, 'foo')); + $this->assertSame('', $dumper->getProxyCode($definition)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/ClosureLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/ClosureLoaderTest.php new file mode 100644 index 0000000..be05731 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/ClosureLoaderTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; + +class ClosureLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testSupports() + { + $loader = new ClosureLoader(new ContainerBuilder()); + + $this->assertTrue($loader->supports(function ($container) {}), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } + + public function testLoad() + { + $loader = new ClosureLoader($container = new ContainerBuilder()); + + $loader->load(function ($container) { + $container->setParameter('foo', 'foo'); + }); + + $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a \Closure resource'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php new file mode 100644 index 0000000..1049761 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/DirectoryLoaderTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\FileLocator; + +class DirectoryLoaderTest extends \PHPUnit_Framework_TestCase +{ + private static $fixturesPath; + + private $container; + private $loader; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + protected function setUp() + { + $locator = new FileLocator(self::$fixturesPath); + $this->container = new ContainerBuilder(); + $this->loader = new DirectoryLoader($this->container, $locator); + $resolver = new LoaderResolver(array( + new PhpFileLoader($this->container, $locator), + new IniFileLoader($this->container, $locator), + new YamlFileLoader($this->container, $locator), + $this->loader, + )); + $this->loader->setResolver($resolver); + } + + public function testDirectoryCanBeLoadedRecursively() + { + $this->loader->load('directory/'); + $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml', 'php' => 'php'), $this->container->getParameterBag()->all(), '->load() takes a single directory'); + } + + public function testImports() + { + $this->loader->resolve('directory/import/import.yml')->load('directory/import/import.yml'); + $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml'), $this->container->getParameterBag()->all(), '->load() takes a single file that imports a directory'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The file "foo" does not exist (in: + */ + public function testExceptionIsRaisedWhenDirectoryDoesNotExist() + { + $this->loader->load('foo/'); + } + + public function testSupports() + { + $loader = new DirectoryLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('directory/'), '->supports("directory/") returns true'); + $this->assertTrue($loader->supports('directory/', 'directory'), '->supports("directory/", "directory") returns true'); + $this->assertFalse($loader->supports('directory'), '->supports("directory") returns false'); + $this->assertTrue($loader->supports('directory', 'directory'), '->supports("directory", "directory") returns true'); + $this->assertFalse($loader->supports('directory', 'foo'), '->supports("directory", "foo") returns false'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php new file mode 100644 index 0000000..cab44b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/IniFileLoaderTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\Config\FileLocator; + +class IniFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + protected $container; + protected $loader; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->loader = new IniFileLoader($this->container, new FileLocator(self::$fixturesPath.'/ini')); + } + + public function testIniFileCanBeLoaded() + { + $this->loader->load('parameters.ini'); + $this->assertEquals(array('foo' => 'bar', 'bar' => '%foo%'), $this->container->getParameterBag()->all(), '->load() takes a single file name as its first argument'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The file "foo.ini" does not exist (in: + */ + public function testExceptionIsRaisedWhenIniFileDoesNotExist() + { + $this->loader->load('foo.ini'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "nonvalid.ini" file is not valid. + */ + public function testExceptionIsRaisedWhenIniFileCannotBeParsed() + { + @$this->loader->load('nonvalid.ini'); + } + + public function testSupports() + { + $loader = new IniFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.ini'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php new file mode 100644 index 0000000..00b957b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Config\FileLocator; + +class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + public function testSupports() + { + $loader = new PhpFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } + + public function testLoad() + { + $loader = new PhpFileLoader($container = new ContainerBuilder(), new FileLocator()); + + $loader->load(__DIR__.'/../Fixtures/php/simple.php'); + + $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a PHP file resource'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php new file mode 100644 index 0000000..7057d44 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php @@ -0,0 +1,489 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\ExpressionLanguage\Expression; + +class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + require_once self::$fixturesPath.'/includes/foo.php'; + require_once self::$fixturesPath.'/includes/ProjectExtension.php'; + require_once self::$fixturesPath.'/includes/ProjectWithXsdExtension.php'; + } + + public function testLoad() + { + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); + + try { + $loader->load('foo.xml'); + $this->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not exist'); + $this->assertStringStartsWith('The file "foo.xml" does not exist (in:', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); + } + } + + public function testParseFile() + { + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('parseFileToDOM'); + $m->setAccessible(true); + + try { + $m->invoke($loader, self::$fixturesPath.'/ini/parameters.ini'); + $this->fail('->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'parameters.ini'), $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + $this->assertStringStartsWith('[ERROR 4] Start tag expected, \'<\' not found (in', $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + } + + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/xml')); + + try { + $m->invoke($loader, self::$fixturesPath.'/xml/nonvalid.xml'); + $this->fail('->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'nonvalid.xml'), $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $this->assertStringStartsWith('[ERROR 1845] Element \'nonvalid\': No matching global declaration available for the validation root. (in', $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + } + + $xml = $m->invoke($loader, self::$fixturesPath.'/xml/services1.xml'); + $this->assertInstanceOf('DOMDocument', $xml, '->parseFileToDOM() returns an SimpleXMLElement object'); + } + + public function testLoadParameters() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services2.xml'); + + $actual = $container->getParameterBag()->all(); + $expected = array( + 'a string', + 'foo' => 'bar', + 'values' => array( + 0, + 'integer' => 4, + 100 => null, + 'true', + true, + false, + 'on', + 'off', + 'float' => 1.3, + 1000.3, + 'a string', + array('foo', 'bar'), + ), + 'mixedcase' => array('MixedCaseKey' => 'value'), + 'constant' => PHP_EOL, + ); + + $this->assertEquals($expected, $actual, '->load() converts XML values to PHP ones'); + } + + public function testLoadImports() + { + $container = new ContainerBuilder(); + $resolver = new LoaderResolver(array( + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + )); + $loader->setResolver($resolver); + $loader->load('services4.xml'); + + $actual = $container->getParameterBag()->all(); + $expected = array( + 'a string', + 'foo' => 'bar', + 'values' => array( + 0, + 'integer' => 4, + 100 => null, + 'true', + true, + false, + 'on', + 'off', + 'float' => 1.3, + 1000.3, + 'a string', + array('foo', 'bar'), + ), + 'mixedcase' => array('MixedCaseKey' => 'value'), + 'constant' => PHP_EOL, + 'bar' => '%foo%', + 'imported_from_ini' => true, + 'imported_from_yaml' => true, + ); + + $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); + + // Bad import throws no exception due to ignore_errors value. + $loader->load('services4_bad_import.xml'); + } + + public function testLoadAnonymousServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services5.xml'); + $services = $container->getDefinitions(); + $this->assertCount(4, $services, '->load() attributes unique ids to anonymous services'); + + // anonymous service as an argument + $args = $services['foo']->getArguments(); + $this->assertCount(1, $args, '->load() references anonymous services as "normal" ones'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $args[0], '->load() converts anonymous services to references to "normal" services'); + $this->assertTrue(isset($services[(string) $args[0]]), '->load() makes a reference to the created ones'); + $inner = $services[(string) $args[0]]; + $this->assertEquals('BarClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + + // inner anonymous services + $args = $inner->getArguments(); + $this->assertCount(1, $args, '->load() references anonymous services as "normal" ones'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $args[0], '->load() converts anonymous services to references to "normal" services'); + $this->assertTrue(isset($services[(string) $args[0]]), '->load() makes a reference to the created ones'); + $inner = $services[(string) $args[0]]; + $this->assertEquals('BazClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); + + // anonymous service as a property + $properties = $services['foo']->getProperties(); + $property = $properties['p']; + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $property, '->load() converts anonymous services to references to "normal" services'); + $this->assertTrue(isset($services[(string) $property]), '->load() makes a reference to the created ones'); + $inner = $services[(string) $property]; + $this->assertEquals('BazClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + } + + public function testLoadServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services6.xml'); + $services = $container->getDefinitions(); + $this->assertTrue(isset($services['foo']), '->load() parses elements'); + $this->assertFalse($services['not_shared']->isShared(), '->load() parses shared flag'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts element to Definition instances'); + $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); + $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); + $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); + $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(new Reference('baz', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array(new Reference('baz', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag'); + + $aliases = $container->getAliases(); + $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses elements'); + $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); + $this->assertTrue($aliases['alias_for_foo']->isPublic()); + $this->assertTrue(isset($aliases['another_alias_for_foo'])); + $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); + $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); + + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); + } + + public function testParsesTags() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services10.xml'); + + $services = $container->findTaggedServiceIds('foo_tag'); + $this->assertCount(1, $services); + + foreach ($services as $id => $tagAttributes) { + foreach ($tagAttributes as $attributes) { + $this->assertArrayHasKey('other_option', $attributes); + $this->assertEquals('lorem', $attributes['other_option']); + $this->assertArrayHasKey('other-option', $attributes, 'unnormalized tag attributes should not be removed'); + + $this->assertEquals('ciz', $attributes['some_option'], 'no overriding should be done when normalizing'); + $this->assertEquals('cat', $attributes['some-option']); + + $this->assertArrayNotHasKey('an_other_option', $attributes, 'normalization should not be done when an underscore is already found'); + } + } + } + + public function testConvertDomElementToArray() + { + $doc = new \DOMDocument('1.0'); + $doc->loadXML('bar'); + $this->assertEquals('bar', XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => 'bar'), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML('bar'); + $this->assertEquals(array('foo' => 'bar'), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML('barbar'); + $this->assertEquals(array('foo' => array('value' => 'bar', 'foo' => 'bar')), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => null), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => null), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => array(array('foo' => 'bar'), array('foo' => 'bar'))), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + } + + public function testExtensions() + { + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $container->registerExtension(new \ProjectWithXsdExtension()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + // extension without an XSD + $loader->load('extensions/services1.xml'); + $container->compile(); + $services = $container->getDefinitions(); + $parameters = $container->getParameterBag()->all(); + + $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements'); + $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements'); + + $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); + $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); + + // extension with an XSD + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $container->registerExtension(new \ProjectWithXsdExtension()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('extensions/services2.xml'); + $container->compile(); + $services = $container->getDefinitions(); + $parameters = $container->getParameterBag()->all(); + + $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements'); + $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements'); + + $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); + $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); + + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $container->registerExtension(new \ProjectWithXsdExtension()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + // extension with an XSD (does not validate) + try { + $loader->load('extensions/services3.xml'); + $this->fail('->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'services3.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertContains('The attribute \'bar\' is not allowed', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } + + // non-registered extension + try { + $loader->load('extensions/services4.xml'); + $this->fail('->load() throws an InvalidArgumentException if the tag is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid'); + $this->assertStringStartsWith('There is no extension able to load the configuration for "project:bar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid'); + } + } + + public function testExtensionInPhar() + { + if (extension_loaded('suhosin') && false === strpos(ini_get('suhosin.executor.include.whitelist'), 'phar')) { + $this->markTestSkipped('To run this test, add "phar" to the "suhosin.executor.include.whitelist" settings in your php.ini file.'); + } + + require_once self::$fixturesPath.'/includes/ProjectWithXsdExtensionInPhar.phar'; + + // extension with an XSD in PHAR archive + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectWithXsdExtensionInPhar()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('extensions/services6.xml'); + + // extension with an XSD in PHAR archive (does not validate) + try { + $loader->load('extensions/services7.xml'); + $this->fail('->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'services7.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertContains('The attribute \'bar\' is not allowed', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } + } + + public function testSupports() + { + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } + + public function testNoNamingConflictsForAnonymousServices() + { + $container = new ContainerBuilder(); + + $loader1 = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml/extension1')); + $loader1->load('services.xml'); + $services = $container->getDefinitions(); + $this->assertCount(2, $services, '->load() attributes unique ids to anonymous services'); + $loader2 = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml/extension2')); + $loader2->load('services.xml'); + $services = $container->getDefinitions(); + $this->assertCount(4, $services, '->load() attributes unique ids to anonymous services'); + + $services = $container->getDefinitions(); + $args1 = $services['extension1.foo']->getArguments(); + $inner1 = $services[(string) $args1[0]]; + $this->assertEquals('BarClass1', $inner1->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $args2 = $services['extension2.foo']->getArguments(); + $inner2 = $services[(string) $args2[0]]; + $this->assertEquals('BarClass2', $inner2->getClass(), '->load() uses the same configuration as for the anonymous ones'); + } + + public function testDocTypeIsNotAllowed() + { + $container = new ContainerBuilder(); + + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + // document types are not allowed. + try { + $loader->load('withdoctype.xml'); + $this->fail('->load() throws an InvalidArgumentException if the configuration contains a document type'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration contains a document type'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'withdoctype.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration contains a document type'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration contains a document type'); + $this->assertSame('Document types are not allowed.', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration contains a document type'); + } + } + + public function testXmlNamespaces() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('namespaces.xml'); + $services = $container->getDefinitions(); + + $this->assertTrue(isset($services['foo']), '->load() parses elements'); + $this->assertEquals(1, count($services['foo']->getTag('foo.tag')), '->load parses elements'); + $this->assertEquals(array(array('setBar', array('foo'))), $services['foo']->getMethodCalls(), '->load() parses the tag'); + } + + public function testLoadIndexedArguments() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services14.xml'); + + $this->assertEquals(array('index_0' => 'app'), $container->findDefinition('logger')->getArguments()); + } + + public function testLoadInlinedServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services21.xml'); + + $foo = $container->getDefinition('foo'); + + $fooFactory = $foo->getFactory(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $fooFactory[0]); + $this->assertSame('FooFactory', $fooFactory[0]->getClass()); + $this->assertSame('createFoo', $fooFactory[1]); + + $fooFactoryFactory = $fooFactory[0]->getFactory(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $fooFactoryFactory[0]); + $this->assertSame('Foobar', $fooFactoryFactory[0]->getClass()); + $this->assertSame('createFooFactory', $fooFactoryFactory[1]); + + $fooConfigurator = $foo->getConfigurator(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $fooConfigurator[0]); + $this->assertSame('Bar', $fooConfigurator[0]->getClass()); + $this->assertSame('configureFoo', $fooConfigurator[1]); + + $barConfigurator = $fooConfigurator[0]->getConfigurator(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $barConfigurator[0]); + $this->assertSame('Baz', $barConfigurator[0]->getClass()); + $this->assertSame('configureBar', $barConfigurator[1]); + } + + public function testType() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services22.xml'); + + $this->assertEquals(array('Bar', 'Baz'), $container->getDefinition('foo')->getAutowiringTypes()); + } + + public function testAutowire() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services23.xml'); + + $this->assertTrue($container->getDefinition('bar')->isAutowired()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php new file mode 100644 index 0000000..baa0cdd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php @@ -0,0 +1,296 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\ExpressionLanguage\Expression; + +class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + require_once self::$fixturesPath.'/includes/foo.php'; + require_once self::$fixturesPath.'/includes/ProjectExtension.php'; + } + + public function testLoadFile() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('loadFile'); + $m->setAccessible(true); + + try { + $m->invoke($loader, 'foo.yml'); + $this->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not exist'); + $this->assertEquals('The service file "foo.yml" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); + } + + try { + $m->invoke($loader, 'parameters.ini'); + $this->fail('->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); + $this->assertEquals('The service file "parameters.ini" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file is not a valid YAML file'); + } + + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + + foreach (array('nonvalid1', 'nonvalid2') as $fixture) { + try { + $m->invoke($loader, $fixture.'.yml'); + $this->fail('->load() throws an InvalidArgumentException if the loaded file does not validate'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not validate'); + $this->assertStringMatchesFormat('The service file "nonvalid%d.yml" is not valid.', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not validate'); + } + } + } + + /** + * @dataProvider provideInvalidFiles + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testLoadInvalidFile($file) + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + + $loader->load($file.'.yml'); + } + + public function provideInvalidFiles() + { + return array( + array('bad_parameters'), + array('bad_imports'), + array('bad_import'), + array('bad_services'), + array('bad_service'), + array('bad_calls'), + array('bad_format'), + ); + } + + public function testLoadParameters() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services2.yml'); + $this->assertEquals(array('foo' => 'bar', 'mixedcase' => array('MixedCaseKey' => 'value'), 'values' => array(true, false, 0, 1000.3), 'bar' => 'foo', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar')), $container->getParameterBag()->all(), '->load() converts YAML keys to lowercase'); + } + + public function testLoadImports() + { + $container = new ContainerBuilder(); + $resolver = new LoaderResolver(array( + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')), + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + )); + $loader->setResolver($resolver); + $loader->load('services4.yml'); + + $actual = $container->getParameterBag()->all(); + $expected = array('foo' => 'bar', 'values' => array(true, false), 'bar' => '%foo%', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar'), 'mixedcase' => array('MixedCaseKey' => 'value'), 'imported_from_ini' => true, 'imported_from_xml' => true); + $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); + + // Bad import throws no exception due to ignore_errors value. + $loader->load('services4_bad_import.yml'); + } + + public function testLoadServices() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services6.yml'); + $services = $container->getDefinitions(); + $this->assertTrue(isset($services['foo']), '->load() parses service elements'); + $this->assertFalse($services['not_shared']->isShared(), '->load() parses the shared flag'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts service element to Definition instances'); + $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); + $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); + $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); + $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(new Reference('baz'), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(array('setBar', array()), array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasparameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag'); + + $aliases = $container->getAliases(); + $this->assertTrue(isset($aliases['alias_for_foo']), '->load() parses aliases'); + $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); + $this->assertTrue($aliases['alias_for_foo']->isPublic()); + $this->assertTrue(isset($aliases['another_alias_for_foo'])); + $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); + $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); + + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); + } + + public function testLoadFactoryShortSyntax() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services14.yml'); + $services = $container->getDefinitions(); + + $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag with service:method'); + $this->assertEquals(array('FooBacFactory', 'createFooBar'), $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method'); + } + + public function testExtensions() + { + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services10.yml'); + $container->compile(); + $services = $container->getDefinitions(); + $parameters = $container->getParameterBag()->all(); + + $this->assertTrue(isset($services['project.service.bar']), '->load() parses extension elements'); + $this->assertTrue(isset($parameters['project.parameter.bar']), '->load() parses extension elements'); + + $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); + $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); + + try { + $loader->load('services11.yml'); + $this->fail('->load() throws an InvalidArgumentException if the tag is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid'); + $this->assertStringStartsWith('There is no extension able to load the configuration for "foobarfoobar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid'); + } + } + + public function testSupports() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable'); + $this->assertTrue($loader->supports('foo.yaml'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } + + public function testNonArrayTagsThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + try { + $loader->load('badtag1.yml'); + $this->fail('->load() should throw an exception when the tags key of a service is not an array'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tags key is not an array'); + $this->assertStringStartsWith('Parameter "tags" must be an array for service', $e->getMessage(), '->load() throws an InvalidArgumentException if the tags key is not an array'); + } + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage A "tags" entry must be an array for service + */ + public function testNonArrayTagThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('badtag4.yml'); + } + + public function testTagWithoutNameThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + try { + $loader->load('badtag2.yml'); + $this->fail('->load() should throw an exception when a tag is missing the name key'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag is missing the name key'); + $this->assertStringStartsWith('A "tags" entry is missing a "name" key for service ', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag is missing the name key'); + } + } + + public function testTagWithAttributeArrayThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + try { + $loader->load('badtag3.yml'); + $this->fail('->load() should throw an exception when a tag-attribute is not a scalar'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); + $this->assertStringStartsWith('A "tags" attribute must be of a scalar-type for service "foo_service", tag "foo", attribute "bar"', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); + } + } + + public function testLoadYamlOnlyWithKeys() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services21.yml'); + + $definition = $container->getDefinition('manager'); + $this->assertEquals(array(array('setLogger', array(new Reference('logger'))), array('setClass', array('User'))), $definition->getMethodCalls()); + $this->assertEquals(array(true), $definition->getArguments()); + $this->assertEquals(array('manager' => array(array('alias' => 'user'))), $definition->getTags()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testTypesNotArray() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_types1.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testTypeNotString() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_types2.yml'); + } + + public function testTypes() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services22.yml'); + + $this->assertEquals(array('Foo', 'Bar'), $container->getDefinition('foo_service')->getAutowiringTypes()); + $this->assertEquals(array('Foo'), $container->getDefinition('baz_service')->getAutowiringTypes()); + } + + public function testAutowire() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services23.yml'); + + $this->assertTrue($container->getDefinition('bar_service')->isAutowired()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php new file mode 100644 index 0000000..b87ca91 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/FrozenParameterBagTest.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; + +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; + +class FrozenParameterBagTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + ); + $bag = new FrozenParameterBag($parameters); + $this->assertEquals($parameters, $bag->all(), '__construct() takes an array of parameters as its first argument'); + } + + /** + * @expectedException \LogicException + */ + public function testClear() + { + $bag = new FrozenParameterBag(array()); + $bag->clear(); + } + + /** + * @expectedException \LogicException + */ + public function testSet() + { + $bag = new FrozenParameterBag(array()); + $bag->set('foo', 'bar'); + } + + /** + * @expectedException \LogicException + */ + public function testAdd() + { + $bag = new FrozenParameterBag(array()); + $bag->add(array()); + } + + /** + * @expectedException \LogicException + */ + public function testRemove() + { + $bag = new FrozenParameterBag(array('foo' => 'bar')); + $bag->remove('foo'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php new file mode 100644 index 0000000..39dfb48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterBag/ParameterBagTest.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; + +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +class ParameterBagTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $bag = new ParameterBag($parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $this->assertEquals($parameters, $bag->all(), '__construct() takes an array of parameters as its first argument'); + } + + public function testClear() + { + $bag = new ParameterBag($parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $bag->clear(); + $this->assertEquals(array(), $bag->all(), '->clear() removes all parameters'); + } + + public function testRemove() + { + $bag = new ParameterBag(array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $bag->remove('foo'); + $this->assertEquals(array('bar' => 'bar'), $bag->all(), '->remove() removes a parameter'); + $bag->remove('BAR'); + $this->assertEquals(array(), $bag->all(), '->remove() converts key to lowercase before removing'); + } + + public function testGetSet() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $bag->set('bar', 'foo'); + $this->assertEquals('foo', $bag->get('bar'), '->set() sets the value of a new parameter'); + + $bag->set('foo', 'baz'); + $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter'); + + $bag->set('Foo', 'baz1'); + $this->assertEquals('baz1', $bag->get('foo'), '->set() converts the key to lowercase'); + $this->assertEquals('baz1', $bag->get('FOO'), '->get() converts the key to lowercase'); + + try { + $bag->get('baba'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } + } + + public function testGetThrowParameterNotFoundException() + { + $bag = new ParameterBag(array( + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => 'baz', + )); + + try { + $bag->get('foo1'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "foo1". Did you mean this: "foo"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException with some advices'); + } + + try { + $bag->get('bag'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "bag". Did you mean one of these: "bar", "baz"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException with some advices'); + } + + try { + $bag->get(''); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "".', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException with some advices'); + } + } + + public function testHas() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined'); + $this->assertTrue($bag->has('Foo'), '->has() converts the key to lowercase'); + $this->assertFalse($bag->has('bar'), '->has() returns false if a parameter is not defined'); + } + + public function testResolveValue() + { + $bag = new ParameterBag(array()); + $this->assertEquals('foo', $bag->resolveValue('foo'), '->resolveValue() returns its argument unmodified if no placeholders are found'); + + $bag = new ParameterBag(array('foo' => 'bar')); + $this->assertEquals('I\'m a bar', $bag->resolveValue('I\'m a %foo%'), '->resolveValue() replaces placeholders by their values'); + $this->assertEquals(array('bar' => 'bar'), $bag->resolveValue(array('%foo%' => '%foo%')), '->resolveValue() replaces placeholders in keys and values of arrays'); + $this->assertEquals(array('bar' => array('bar' => array('bar' => 'bar'))), $bag->resolveValue(array('%foo%' => array('%foo%' => array('%foo%' => '%foo%')))), '->resolveValue() replaces placeholders in nested arrays'); + $this->assertEquals('I\'m a %%foo%%', $bag->resolveValue('I\'m a %%foo%%'), '->resolveValue() supports % escaping by doubling it'); + $this->assertEquals('I\'m a bar %%foo bar', $bag->resolveValue('I\'m a %foo% %%foo %foo%'), '->resolveValue() supports % escaping by doubling it'); + $this->assertEquals(array('foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar'))), $bag->resolveValue(array('foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar')))), '->resolveValue() supports % escaping by doubling it'); + + $bag = new ParameterBag(array('foo' => true)); + $this->assertTrue($bag->resolveValue('%foo%'), '->resolveValue() replaces arguments that are just a placeholder by their value without casting them to strings'); + $bag = new ParameterBag(array('foo' => null)); + $this->assertNull($bag->resolveValue('%foo%'), '->resolveValue() replaces arguments that are just a placeholder by their value without casting them to strings'); + + $bag = new ParameterBag(array('foo' => 'bar', 'baz' => '%%%foo% %foo%%% %%foo%% %%%foo%%%')); + $this->assertEquals('%%bar bar%% %%foo%% %%bar%%', $bag->resolveValue('%baz%'), '->resolveValue() replaces params placed besides escaped %'); + + $bag = new ParameterBag(array('baz' => '%%s?%%s')); + $this->assertEquals('%%s?%%s', $bag->resolveValue('%baz%'), '->resolveValue() is not replacing greedily'); + + $bag = new ParameterBag(array()); + try { + $bag->resolveValue('%foobar%'); + $this->fail('->resolveValue() throws an InvalidArgumentException if a placeholder references a non-existent parameter'); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('You have requested a non-existent parameter "foobar".', $e->getMessage(), '->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); + } + + try { + $bag->resolveValue('foo %foobar% bar'); + $this->fail('->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('You have requested a non-existent parameter "foobar".', $e->getMessage(), '->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); + } + + $bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => array())); + try { + $bag->resolveValue('%foo%'); + $this->fail('->resolveValue() throws a RuntimeException when a parameter embeds another non-string parameter'); + } catch (RuntimeException $e) { + $this->assertEquals('A string value must be composed of strings and/or numbers, but found parameter "bar" of type array inside string value "a %bar%".', $e->getMessage(), '->resolveValue() throws a RuntimeException when a parameter embeds another non-string parameter'); + } + + $bag = new ParameterBag(array('foo' => '%bar%', 'bar' => '%foobar%', 'foobar' => '%foo%')); + try { + $bag->resolveValue('%foo%'); + $this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } catch (ParameterCircularReferenceException $e) { + $this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } + + $bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => 'a %foobar%', 'foobar' => 'a %foo%')); + try { + $bag->resolveValue('%foo%'); + $this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } catch (ParameterCircularReferenceException $e) { + $this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } + + $bag = new ParameterBag(array('host' => 'foo.bar', 'port' => 1337)); + $this->assertEquals('foo.bar:1337', $bag->resolveValue('%host%:%port%')); + } + + public function testResolveIndicatesWhyAParameterIsNeeded() + { + $bag = new ParameterBag(array('foo' => '%bar%')); + + try { + $bag->resolve(); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('The parameter "foo" has a dependency on a non-existent parameter "bar".', $e->getMessage()); + } + + $bag = new ParameterBag(array('foo' => '%bar%')); + + try { + $bag->resolve(); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('The parameter "foo" has a dependency on a non-existent parameter "bar".', $e->getMessage()); + } + } + + public function testResolveUnescapesValue() + { + $bag = new ParameterBag(array( + 'foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar')), + 'bar' => 'I\'m a %%foo%%', + )); + + $bag->resolve(); + + $this->assertEquals('I\'m a %foo%', $bag->get('bar'), '->resolveValue() supports % escaping by doubling it'); + $this->assertEquals(array('bar' => array('ding' => 'I\'m a bar %foo %bar')), $bag->get('foo'), '->resolveValue() supports % escaping by doubling it'); + } + + public function testEscapeValue() + { + $bag = new ParameterBag(); + + $bag->add(array( + 'foo' => $bag->escapeValue(array('bar' => array('ding' => 'I\'m a bar %foo %bar', 'zero' => null))), + 'bar' => $bag->escapeValue('I\'m a %foo%'), + )); + + $this->assertEquals('I\'m a %%foo%%', $bag->get('bar'), '->escapeValue() escapes % by doubling it'); + $this->assertEquals(array('bar' => array('ding' => 'I\'m a bar %%foo %%bar', 'zero' => null)), $bag->get('foo'), '->escapeValue() escapes % by doubling it'); + } + + /** + * @dataProvider stringsWithSpacesProvider + */ + public function testResolveStringWithSpacesReturnsString($expected, $test, $description) + { + $bag = new ParameterBag(array('foo' => 'bar')); + + try { + $this->assertEquals($expected, $bag->resolveString($test), $description); + } catch (ParameterNotFoundException $e) { + $this->fail(sprintf('%s - "%s"', $description, $expected)); + } + } + + public function stringsWithSpacesProvider() + { + return array( + array('bar', '%foo%', 'Parameters must be wrapped by %.'), + array('% foo %', '% foo %', 'Parameters should not have spaces.'), + array('{% set my_template = "foo" %}', '{% set my_template = "foo" %}', 'Twig-like strings are not parameters.'), + array('50% is less than 100%', '50% is less than 100%', 'Text between % signs is allowed, if there are spaces.'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterTest.php new file mode 100644 index 0000000..571ca02 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ParameterTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\Parameter; + +class ParameterTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $ref = new Parameter('foo'); + $this->assertEquals('foo', (string) $ref, '__construct() sets the id of the parameter, which is used for the __toString() method'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ReferenceTest.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ReferenceTest.php new file mode 100644 index 0000000..6800267 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Tests/ReferenceTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use Symfony\Component\DependencyInjection\Reference; + +class ReferenceTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $ref = new Reference('foo'); + $this->assertEquals('foo', (string) $ref, '__construct() sets the id of the reference, which is used for the __toString() method'); + } + + public function testCaseInsensitive() + { + $ref = new Reference('FooBar'); + $this->assertEquals('foobar', (string) $ref, 'the id is lowercased as the container is case insensitive'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Variable.php b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Variable.php new file mode 100644 index 0000000..e502356 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Variable.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * Represents a variable. + * + * $var = new Variable('a'); + * + * will be dumped as + * + * $a + * + * by the PHP dumper. + * + * @author Johannes M. Schmitt + */ +class Variable +{ + private $name; + + /** + * Constructor. + * + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * Converts the object to a string. + * + * @return string + */ + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/composer.json b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/composer.json new file mode 100644 index 0000000..de83f02 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/composer.json @@ -0,0 +1,43 @@ +{ + "name": "symfony/dependency-injection", + "type": "library", + "description": "Symfony DependencyInjection Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/yaml": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0" + }, + "suggest": { + "symfony/yaml": "", + "symfony/config": "", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/phpunit.xml.dist new file mode 100644 index 0000000..86252d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/CHANGELOG.md new file mode 100644 index 0000000..48fd323 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/CHANGELOG.md @@ -0,0 +1,45 @@ +CHANGELOG +========= + +2.5.0 +----- + +* [BC BREAK] The default value for checkbox and radio inputs without a value attribute have changed + from '1' to 'on' to match the HTML specification. +* [BC BREAK] The typehints on the `Link`, `Form` and `FormField` classes have been changed from + `\DOMNode` to `DOMElement`. Using any other type of `DOMNode` was triggering fatal errors in previous + versions. Code extending these classes will need to update the typehints when overwriting these methods. + +2.4.0 +----- + + * `Crawler::addXmlContent()` removes the default document namespace again if it's an only namespace. + * added support for automatic discovery and explicit registration of document + namespaces for `Crawler::filterXPath()` and `Crawler::filter()` + * improved content type guessing in `Crawler::addContent()` + * [BC BREAK] `Crawler::addXmlContent()` no longer removes the default document + namespace + +2.3.0 +----- + + * added Crawler::html() + * [BC BREAK] Crawler::each() and Crawler::reduce() now return Crawler instances instead of DomElement instances + * added schema relative URL support to links + * added support for HTML5 'form' attribute + +2.2.0 +----- + + * added a way to set raw path to the file in FileFormField - necessary for + simulating HTTP requests + +2.1.0 +----- + + * added support for the HTTP PATCH method + * refactored the Form class internals to support multi-dimensional fields + (the public API is backward compatible) + * added a way to get parsing errors for Crawler::addHtmlContent() and + Crawler::addXmlContent() via libxml functions + * added support for submitting a form without a submit button diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Crawler.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Crawler.php new file mode 100644 index 0000000..7671eed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Crawler.php @@ -0,0 +1,1035 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\CssSelector\CssSelectorConverter; + +/** + * Crawler eases navigation of a list of \DOMNode objects. + * + * @author Fabien Potencier + */ +class Crawler implements \Countable, \IteratorAggregate +{ + /** + * @var string The current URI + */ + protected $uri; + + /** + * @var string The default namespace prefix to be used with XPath and CSS expressions + */ + private $defaultNamespacePrefix = 'default'; + + /** + * @var array A map of manually registered namespaces + */ + private $namespaces = array(); + + /** + * @var string The base href value + */ + private $baseHref; + + /** + * @var \DOMDocument|null + */ + private $document; + + /** + * @var \DOMElement[] + */ + private $nodes = array(); + + /** + * Whether the Crawler contains HTML or XML content (used when converting CSS to XPath). + * + * @var bool + */ + private $isHtml = true; + + /** + * Constructor. + * + * @param mixed $node A Node to use as the base for the crawling + * @param string $currentUri The current URI + * @param string $baseHref The base href value + */ + public function __construct($node = null, $currentUri = null, $baseHref = null) + { + $this->uri = $currentUri; + $this->baseHref = $baseHref ?: $currentUri; + + $this->add($node); + } + + /** + * Removes all the nodes. + */ + public function clear() + { + $this->nodes = array(); + $this->document = null; + } + + /** + * Adds a node to the current list of nodes. + * + * This method uses the appropriate specialized add*() method based + * on the type of the argument. + * + * @param \DOMNodeList|\DOMNode|array|string|null $node A node + * + * @throws \InvalidArgumentException When node is not the expected type. + */ + public function add($node) + { + if ($node instanceof \DOMNodeList) { + $this->addNodeList($node); + } elseif ($node instanceof \DOMNode) { + $this->addNode($node); + } elseif (is_array($node)) { + $this->addNodes($node); + } elseif (is_string($node)) { + $this->addContent($node); + } elseif (null !== $node) { + throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', is_object($node) ? get_class($node) : gettype($node))); + } + } + + /** + * Adds HTML/XML content. + * + * If the charset is not set via the content type, it is assumed + * to be ISO-8859-1, which is the default charset defined by the + * HTTP 1.1 specification. + * + * @param string $content A string to parse as HTML/XML + * @param null|string $type The content type of the string + */ + public function addContent($content, $type = null) + { + if (empty($type)) { + $type = 0 === strpos($content, ']+charset *= *["\']?([a-zA-Z\-0-9_:.]+)/i', $content, $matches)) { + $charset = $matches[1]; + } + + if (null === $charset) { + $charset = 'ISO-8859-1'; + } + + if ('x' === $xmlMatches[1]) { + $this->addXmlContent($content, $charset); + } else { + $this->addHtmlContent($content, $charset); + } + } + + /** + * Adds an HTML content to the list of nodes. + * + * The libxml errors are disabled when the content is parsed. + * + * If you want to get parsing errors, be sure to enable + * internal errors via libxml_use_internal_errors(true) + * and then, get the errors via libxml_get_errors(). Be + * sure to clear errors with libxml_clear_errors() afterward. + * + * @param string $content The HTML content + * @param string $charset The charset + */ + public function addHtmlContent($content, $charset = 'UTF-8') + { + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + set_error_handler(function () {throw new \Exception();}); + + try { + // Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML() + $content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset); + } catch (\Exception $e) { + } + + restore_error_handler(); + + if ('' !== trim($content)) { + @$dom->loadHTML($content); + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + + $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(array('href')); + + $baseHref = current($base); + if (count($base) && !empty($baseHref)) { + if ($this->baseHref) { + $linkNode = $dom->createElement('a'); + $linkNode->setAttribute('href', $baseHref); + $link = new Link($linkNode, $this->baseHref); + $this->baseHref = $link->getUri(); + } else { + $this->baseHref = $baseHref; + } + } + } + + /** + * Adds an XML content to the list of nodes. + * + * The libxml errors are disabled when the content is parsed. + * + * If you want to get parsing errors, be sure to enable + * internal errors via libxml_use_internal_errors(true) + * and then, get the errors via libxml_get_errors(). Be + * sure to clear errors with libxml_clear_errors() afterward. + * + * @param string $content The XML content + * @param string $charset The charset + */ + public function addXmlContent($content, $charset = 'UTF-8') + { + // remove the default namespace if it's the only namespace to make XPath expressions simpler + if (!preg_match('/xmlns:/', $content)) { + $content = str_replace('xmlns', 'ns', $content); + } + + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + + $dom = new \DOMDocument('1.0', $charset); + $dom->validateOnParse = true; + + if ('' !== trim($content)) { + @$dom->loadXML($content, LIBXML_NONET); + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + $this->addDocument($dom); + + $this->isHtml = false; + } + + /** + * Adds a \DOMDocument to the list of nodes. + * + * @param \DOMDocument $dom A \DOMDocument instance + */ + public function addDocument(\DOMDocument $dom) + { + if ($dom->documentElement) { + $this->addNode($dom->documentElement); + } + } + + /** + * Adds a \DOMNodeList to the list of nodes. + * + * @param \DOMNodeList $nodes A \DOMNodeList instance + */ + public function addNodeList(\DOMNodeList $nodes) + { + foreach ($nodes as $node) { + if ($node instanceof \DOMNode) { + $this->addNode($node); + } + } + } + + /** + * Adds an array of \DOMNode instances to the list of nodes. + * + * @param \DOMNode[] $nodes An array of \DOMNode instances + */ + public function addNodes(array $nodes) + { + foreach ($nodes as $node) { + $this->add($node); + } + } + + /** + * Adds a \DOMNode instance to the list of nodes. + * + * @param \DOMNode $node A \DOMNode instance + */ + public function addNode(\DOMNode $node) + { + if ($node instanceof \DOMDocument) { + $node = $node->documentElement; + } + + if (null !== $this->document && $this->document !== $node->ownerDocument) { + throw new \InvalidArgumentException('Attaching DOM nodes from multiple documents in the same crawler is forbidden.'); + } + + if (null === $this->document) { + $this->document = $node->ownerDocument; + } + + // Don't add duplicate nodes in the Crawler + if (in_array($node, $this->nodes, true)) { + return; + } + + $this->nodes[] = $node; + } + + /** + * Returns a node given its position in the node list. + * + * @param int $position The position + * + * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist. + */ + public function eq($position) + { + if (isset($this->nodes[$position])) { + return $this->createSubCrawler($this->nodes[$position]); + } + + return $this->createSubCrawler(null); + } + + /** + * Calls an anonymous function on each node of the list. + * + * The anonymous function receives the position and the node wrapped + * in a Crawler instance as arguments. + * + * Example: + * + * $crawler->filter('h1')->each(function ($node, $i) { + * return $node->text(); + * }); + * + * @param \Closure $closure An anonymous function + * + * @return array An array of values returned by the anonymous function + */ + public function each(\Closure $closure) + { + $data = array(); + foreach ($this->nodes as $i => $node) { + $data[] = $closure($this->createSubCrawler($node), $i); + } + + return $data; + } + + /** + * Slices the list of nodes by $offset and $length. + * + * @param int $offset + * @param int $length + * + * @return Crawler A Crawler instance with the sliced nodes + */ + public function slice($offset = 0, $length = null) + { + return $this->createSubCrawler(array_slice($this->nodes, $offset, $length)); + } + + /** + * Reduces the list of nodes by calling an anonymous function. + * + * To remove a node from the list, the anonymous function must return false. + * + * @param \Closure $closure An anonymous function + * + * @return Crawler A Crawler instance with the selected nodes. + */ + public function reduce(\Closure $closure) + { + $nodes = array(); + foreach ($this->nodes as $i => $node) { + if (false !== $closure($this->createSubCrawler($node), $i)) { + $nodes[] = $node; + } + } + + return $this->createSubCrawler($nodes); + } + + /** + * Returns the first node of the current selection. + * + * @return Crawler A Crawler instance with the first selected node + */ + public function first() + { + return $this->eq(0); + } + + /** + * Returns the last node of the current selection. + * + * @return Crawler A Crawler instance with the last selected node + */ + public function last() + { + return $this->eq(count($this->nodes) - 1); + } + + /** + * Returns the siblings nodes of the current selection. + * + * @return Crawler A Crawler instance with the sibling nodes + * + * @throws \InvalidArgumentException When current node is empty + */ + public function siblings() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild)); + } + + /** + * Returns the next siblings nodes of the current selection. + * + * @return Crawler A Crawler instance with the next sibling nodes + * + * @throws \InvalidArgumentException When current node is empty + */ + public function nextAll() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->createSubCrawler($this->sibling($this->getNode(0))); + } + + /** + * Returns the previous sibling nodes of the current selection. + * + * @return Crawler A Crawler instance with the previous sibling nodes + * + * @throws \InvalidArgumentException + */ + public function previousAll() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling')); + } + + /** + * Returns the parents nodes of the current selection. + * + * @return Crawler A Crawler instance with the parents nodes of the current selection + * + * @throws \InvalidArgumentException When current node is empty + */ + public function parents() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + $nodes = array(); + + while ($node = $node->parentNode) { + if (1 === $node->nodeType) { + $nodes[] = $node; + } + } + + return $this->createSubCrawler($nodes); + } + + /** + * Returns the children nodes of the current selection. + * + * @return Crawler A Crawler instance with the children nodes + * + * @throws \InvalidArgumentException When current node is empty + */ + public function children() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0)->firstChild; + + return $this->createSubCrawler($node ? $this->sibling($node) : array()); + } + + /** + * Returns the attribute value of the first node of the list. + * + * @param string $attribute The attribute name + * + * @return string|null The attribute value or null if the attribute does not exist + * + * @throws \InvalidArgumentException When current node is empty + */ + public function attr($attribute) + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null; + } + + /** + * Returns the node name of the first node of the list. + * + * @return string The node name + * + * @throws \InvalidArgumentException When current node is empty + */ + public function nodeName() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->getNode(0)->nodeName; + } + + /** + * Returns the node value of the first node of the list. + * + * @return string The node value + * + * @throws \InvalidArgumentException When current node is empty + */ + public function text() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + return $this->getNode(0)->nodeValue; + } + + /** + * Returns the first node of the list as HTML. + * + * @return string The node html + * + * @throws \InvalidArgumentException When current node is empty + */ + public function html() + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $html = ''; + foreach ($this->getNode(0)->childNodes as $child) { + $html .= $child->ownerDocument->saveHTML($child); + } + + return $html; + } + + /** + * Extracts information from the list of nodes. + * + * You can extract attributes or/and the node value (_text). + * + * Example: + * + * $crawler->filter('h1 a')->extract(array('_text', 'href')); + * + * @param array $attributes An array of attributes + * + * @return array An array of extracted values + */ + public function extract($attributes) + { + $attributes = (array) $attributes; + $count = count($attributes); + + $data = array(); + foreach ($this->nodes as $node) { + $elements = array(); + foreach ($attributes as $attribute) { + if ('_text' === $attribute) { + $elements[] = $node->nodeValue; + } else { + $elements[] = $node->getAttribute($attribute); + } + } + + $data[] = $count > 1 ? $elements : $elements[0]; + } + + return $data; + } + + /** + * Filters the list of nodes with an XPath expression. + * + * The XPath expression is evaluated in the context of the crawler, which + * is considered as a fake parent of the elements inside it. + * This means that a child selector "div" or "./div" will match only + * the div elements of the current crawler, not their children. + * + * @param string $xpath An XPath expression + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + */ + public function filterXPath($xpath) + { + $xpath = $this->relativize($xpath); + + // If we dropped all expressions in the XPath while preparing it, there would be no match + if ('' === $xpath) { + return $this->createSubCrawler(null); + } + + return $this->filterRelativeXPath($xpath); + } + + /** + * Filters the list of nodes with a CSS selector. + * + * This method only works if you have installed the CssSelector Symfony Component. + * + * @param string $selector A CSS selector + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + * + * @throws \RuntimeException if the CssSelector Component is not available + */ + public function filter($selector) + { + if (!class_exists('Symfony\\Component\\CssSelector\\CssSelectorConverter')) { + throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector 2.8+ is not installed (you can use filterXPath instead).'); + } + + $converter = new CssSelectorConverter($this->isHtml); + + // The CssSelector already prefixes the selector with descendant-or-self:: + return $this->filterRelativeXPath($converter->toXPath($selector)); + } + + /** + * Selects links by name or alt value for clickable images. + * + * @param string $value The link text + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + */ + public function selectLink($value) + { + $xpath = sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) ', static::xpathLiteral(' '.$value.' ')). + sprintf('or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]]', static::xpathLiteral(' '.$value.' ')); + + return $this->filterRelativeXPath($xpath); + } + + /** + * Selects a button by name or alt value for images. + * + * @param string $value The button text + * + * @return Crawler A new instance of Crawler with the filtered list of nodes + */ + public function selectButton($value) + { + $translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'; + $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')). + sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)). + sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)); + + return $this->filterRelativeXPath($xpath); + } + + /** + * Returns a Link object for the first node in the list. + * + * @param string $method The method for the link (get by default) + * + * @return Link A Link instance + * + * @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement + */ + public function link($method = 'get') + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + if (!$node instanceof \DOMElement) { + throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node))); + } + + return new Link($node, $this->baseHref, $method); + } + + /** + * Returns an array of Link objects for the nodes in the list. + * + * @return Link[] An array of Link instances + * + * @throws \InvalidArgumentException If the current node list contains non-DOMElement instances + */ + public function links() + { + $links = array(); + foreach ($this->nodes as $node) { + if (!$node instanceof \DOMElement) { + throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_class($node))); + } + + $links[] = new Link($node, $this->baseHref, 'get'); + } + + return $links; + } + + /** + * Returns a Form object for the first node in the list. + * + * @param array $values An array of values for the form fields + * @param string $method The method for the form + * + * @return Form A Form instance + * + * @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement + */ + public function form(array $values = null, $method = null) + { + if (!$this->nodes) { + throw new \InvalidArgumentException('The current node list is empty.'); + } + + $node = $this->getNode(0); + + if (!$node instanceof \DOMElement) { + throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node))); + } + + $form = new Form($node, $this->uri, $method, $this->baseHref); + + if (null !== $values) { + $form->setValues($values); + } + + return $form; + } + + /** + * Overloads a default namespace prefix to be used with XPath and CSS expressions. + * + * @param string $prefix + */ + public function setDefaultNamespacePrefix($prefix) + { + $this->defaultNamespacePrefix = $prefix; + } + + /** + * @param string $prefix + * @param string $namespace + */ + public function registerNamespace($prefix, $namespace) + { + $this->namespaces[$prefix] = $namespace; + } + + /** + * Converts string for XPath expressions. + * + * Escaped characters are: quotes (") and apostrophe ('). + * + * Examples: + * + * echo Crawler::xpathLiteral('foo " bar'); + * //prints 'foo " bar' + * + * echo Crawler::xpathLiteral("foo ' bar"); + * //prints "foo ' bar" + * + * echo Crawler::xpathLiteral('a\'b"c'); + * //prints concat('a', "'", 'b"c') + * + * + * @param string $s String to be escaped + * + * @return string Converted string + */ + public static function xpathLiteral($s) + { + if (false === strpos($s, "'")) { + return sprintf("'%s'", $s); + } + + if (false === strpos($s, '"')) { + return sprintf('"%s"', $s); + } + + $string = $s; + $parts = array(); + while (true) { + if (false !== $pos = strpos($string, "'")) { + $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = "\"'\""; + $string = substr($string, $pos + 1); + } else { + $parts[] = "'$string'"; + break; + } + } + + return sprintf('concat(%s)', implode($parts, ', ')); + } + + /** + * Filters the list of nodes with an XPath expression. + * + * The XPath expression should already be processed to apply it in the context of each node. + * + * @param string $xpath + * + * @return Crawler + */ + private function filterRelativeXPath($xpath) + { + $prefixes = $this->findNamespacePrefixes($xpath); + + $crawler = $this->createSubCrawler(null); + + foreach ($this->nodes as $node) { + $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); + $crawler->add($domxpath->query($xpath, $node)); + } + + return $crawler; + } + + /** + * Make the XPath relative to the current context. + * + * The returned XPath will match elements matching the XPath inside the current crawler + * when running in the context of a node of the crawler. + * + * @param string $xpath + * + * @return string + */ + private function relativize($xpath) + { + $expressions = array(); + + $unionPattern = '/\|(?![^\[]*\])/'; + // An expression which will never match to replace expressions which cannot match in the crawler + // We cannot simply drop + $nonMatchingExpression = 'a[name() = "b"]'; + + // Split any unions into individual expressions. + foreach (preg_split($unionPattern, $xpath) as $expression) { + $expression = trim($expression); + $parenthesis = ''; + + // If the union is inside some braces, we need to preserve the opening braces and apply + // the change only inside it. + if (preg_match('/^[\(\s*]+/', $expression, $matches)) { + $parenthesis = $matches[0]; + $expression = substr($expression, strlen($parenthesis)); + } + + if (0 === strpos($expression, 'self::*/')) { + $expression = './'.substr($expression, 8); + } + + // add prefix before absolute element selector + if (empty($expression)) { + $expression = $nonMatchingExpression; + } elseif (0 === strpos($expression, '//')) { + $expression = 'descendant-or-self::'.substr($expression, 2); + } elseif (0 === strpos($expression, './/')) { + $expression = 'descendant-or-self::'.substr($expression, 3); + } elseif (0 === strpos($expression, './')) { + $expression = 'self::'.substr($expression, 2); + } elseif (0 === strpos($expression, 'child::')) { + $expression = 'self::'.substr($expression, 7); + } elseif ('/' === $expression[0] || '.' === $expression[0] || 0 === strpos($expression, 'self::')) { + $expression = $nonMatchingExpression; + } elseif (0 === strpos($expression, 'descendant::')) { + $expression = 'descendant-or-self::'.substr($expression, strlen('descendant::')); + } elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) { + // the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes) + $expression = $nonMatchingExpression; + } elseif (0 !== strpos($expression, 'descendant-or-self::')) { + $expression = 'self::'.$expression; + } + $expressions[] = $parenthesis.$expression; + } + + return implode(' | ', $expressions); + } + + /** + * @param int $position + * + * @return \DOMElement|null + */ + public function getNode($position) + { + if (isset($this->nodes[$position])) { + return $this->nodes[$position]; + } + } + + /** + * @return int + */ + public function count() + { + return count($this->nodes); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->nodes); + } + + /** + * @param \DOMElement $node + * @param string $siblingDir + * + * @return array + */ + protected function sibling($node, $siblingDir = 'nextSibling') + { + $nodes = array(); + + do { + if ($node !== $this->getNode(0) && $node->nodeType === 1) { + $nodes[] = $node; + } + } while ($node = $node->$siblingDir); + + return $nodes; + } + + /** + * @param \DOMDocument $document + * @param array $prefixes + * + * @return \DOMXPath + * + * @throws \InvalidArgumentException + */ + private function createDOMXPath(\DOMDocument $document, array $prefixes = array()) + { + $domxpath = new \DOMXPath($document); + + foreach ($prefixes as $prefix) { + $namespace = $this->discoverNamespace($domxpath, $prefix); + if (null !== $namespace) { + $domxpath->registerNamespace($prefix, $namespace); + } + } + + return $domxpath; + } + + /** + * @param \DOMXPath $domxpath + * @param string $prefix + * + * @return string + * + * @throws \InvalidArgumentException + */ + private function discoverNamespace(\DOMXPath $domxpath, $prefix) + { + if (isset($this->namespaces[$prefix])) { + return $this->namespaces[$prefix]; + } + + // ask for one namespace, otherwise we'd get a collection with an item for each node + $namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix)); + + if ($node = $namespaces->item(0)) { + return $node->nodeValue; + } + } + + /** + * @param string $xpath + * + * @return array + */ + private function findNamespacePrefixes($xpath) + { + if (preg_match_all('/(?P[a-z_][a-z_0-9\-\.]*+):[^"\/:]/i', $xpath, $matches)) { + return array_unique($matches['prefix']); + } + + return array(); + } + + /** + * Creates a crawler for some subnodes. + * + * @param \DOMElement|\DOMElement[]|\DOMNodeList|null $nodes + * + * @return static + */ + private function createSubCrawler($nodes) + { + $crawler = new static($nodes, $this->uri, $this->baseHref); + $crawler->isHtml = $this->isHtml; + $crawler->document = $this->document; + + return $crawler; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php new file mode 100644 index 0000000..fcf510c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/ChoiceFormField.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * ChoiceFormField represents a choice form field. + * + * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs. + * + * @author Fabien Potencier + */ +class ChoiceFormField extends FormField +{ + /** + * @var string + */ + private $type; + /** + * @var bool + */ + private $multiple; + /** + * @var array + */ + private $options; + /** + * @var bool + */ + private $validationDisabled = false; + + /** + * Returns true if the field should be included in the submitted values. + * + * @return bool true if the field should be included in the submitted values, false otherwise + */ + public function hasValue() + { + // don't send a value for unchecked checkboxes + if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) { + return false; + } + + return true; + } + + /** + * Check if the current selected option is disabled. + * + * @return bool + */ + public function isDisabled() + { + foreach ($this->options as $option) { + if ($option['value'] == $this->value && $option['disabled']) { + return true; + } + } + + return false; + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function select($value) + { + $this->setValue($value); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + */ + public function tick() + { + if ('checkbox' !== $this->type) { + throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + } + + $this->setValue(true); + } + + /** + * Ticks a checkbox. + * + * @throws \LogicException When the type provided is not correct + */ + public function untick() + { + if ('checkbox' !== $this->type) { + throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); + } + + $this->setValue(false); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + * + * @throws \InvalidArgumentException When value type provided is not correct + */ + public function setValue($value) + { + if ('checkbox' === $this->type && false === $value) { + // uncheck + $this->value = null; + } elseif ('checkbox' === $this->type && true === $value) { + // check + $this->value = $this->options[0]['value']; + } else { + if (is_array($value)) { + if (!$this->multiple) { + throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name)); + } + + foreach ($value as $v) { + if (!$this->containsOption($v, $this->options)) { + throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues()))); + } + } + } elseif (!$this->containsOption($value, $this->options)) { + throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues()))); + } + + if ($this->multiple) { + $value = (array) $value; + } + + if (is_array($value)) { + $this->value = $value; + } else { + parent::setValue($value); + } + } + } + + /** + * Adds a choice to the current ones. + * + * This method should only be used internally. + * + * @param \DOMElement $node + * + * @throws \LogicException When choice provided is not multiple nor radio + */ + public function addChoice(\DOMElement $node) + { + if (!$this->multiple && 'radio' !== $this->type) { + throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); + } + + $option = $this->buildOptionValue($node); + $this->options[] = $option; + + if ($node->hasAttribute('checked')) { + $this->value = $option['value']; + } + } + + /** + * Returns the type of the choice field (radio, select, or checkbox). + * + * @return string The type + */ + public function getType() + { + return $this->type; + } + + /** + * Returns true if the field accepts multiple values. + * + * @return bool true if the field accepts multiple values, false otherwise + */ + public function isMultiple() + { + return $this->multiple; + } + + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); + } + + if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) { + throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type'))); + } + + $this->value = null; + $this->options = array(); + $this->multiple = false; + + if ('input' == $this->node->nodeName) { + $this->type = strtolower($this->node->getAttribute('type')); + $optionValue = $this->buildOptionValue($this->node); + $this->options[] = $optionValue; + + if ($this->node->hasAttribute('checked')) { + $this->value = $optionValue['value']; + } + } else { + $this->type = 'select'; + if ($this->node->hasAttribute('multiple')) { + $this->multiple = true; + $this->value = array(); + $this->name = str_replace('[]', '', $this->name); + } + + $found = false; + foreach ($this->xpath->query('descendant::option', $this->node) as $option) { + $optionValue = $this->buildOptionValue($option); + $this->options[] = $optionValue; + + if ($option->hasAttribute('selected')) { + $found = true; + if ($this->multiple) { + $this->value[] = $optionValue['value']; + } else { + $this->value = $optionValue['value']; + } + } + } + + // if no option is selected and if it is a simple select box, take the first option as the value + if (!$found && !$this->multiple && !empty($this->options)) { + $this->value = $this->options[0]['value']; + } + } + } + + /** + * Returns option value with associated disabled flag. + * + * @param \DOMElement $node + * + * @return array + */ + private function buildOptionValue(\DOMElement $node) + { + $option = array(); + + $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : 'on'; + $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; + $option['disabled'] = $node->hasAttribute('disabled'); + + return $option; + } + + /** + * Checks whether given value is in the existing options. + * + * @param string $optionValue + * @param array $options + * + * @return bool + */ + public function containsOption($optionValue, $options) + { + if ($this->validationDisabled) { + return true; + } + + foreach ($options as $option) { + if ($option['value'] == $optionValue) { + return true; + } + } + + return false; + } + + /** + * Returns list of available field options. + * + * @return array + */ + public function availableOptionValues() + { + $values = array(); + + foreach ($this->options as $option) { + $values[] = $option['value']; + } + + return $values; + } + + /** + * Disables the internal validation of the field. + * + * @return self + */ + public function disableValidation() + { + $this->validationDisabled = true; + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FileFormField.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FileFormField.php new file mode 100644 index 0000000..0e0f943 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FileFormField.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * FileFormField represents a file form field (an HTML file input tag). + * + * @author Fabien Potencier + */ +class FileFormField extends FormField +{ + /** + * Sets the PHP error code associated with the field. + * + * @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION) + * + * @throws \InvalidArgumentException When error code doesn't exist + */ + public function setErrorCode($error) + { + $codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION); + if (!in_array($error, $codes)) { + throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error)); + } + + $this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function upload($value) + { + $this->setValue($value); + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function setValue($value) + { + if (null !== $value && is_readable($value)) { + $error = UPLOAD_ERR_OK; + $size = filesize($value); + $info = pathinfo($value); + $name = $info['basename']; + + // copy to a tmp location + $tmp = sys_get_temp_dir().'/'.sha1(uniqid(mt_rand(), true)); + if (array_key_exists('extension', $info)) { + $tmp .= '.'.$info['extension']; + } + if (is_file($tmp)) { + unlink($tmp); + } + copy($value, $tmp); + $value = $tmp; + } else { + $error = UPLOAD_ERR_NO_FILE; + $size = 0; + $name = ''; + $value = ''; + } + + $this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size); + } + + /** + * Sets path to the file as string for simulating HTTP request. + * + * @param string $path The path to the file + */ + public function setFilePath($path) + { + parent::setValue($path); + } + + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); + } + + if ('file' !== strtolower($this->node->getAttribute('type'))) { + throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type'))); + } + + $this->setValue(null); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FormField.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FormField.php new file mode 100644 index 0000000..a6b33de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/FormField.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * FormField is the abstract class for all form fields. + * + * @author Fabien Potencier + */ +abstract class FormField +{ + /** + * @var \DOMElement + */ + protected $node; + /** + * @var string + */ + protected $name; + /** + * @var string + */ + protected $value; + /** + * @var \DOMDocument + */ + protected $document; + /** + * @var \DOMXPath + */ + protected $xpath; + /** + * @var bool + */ + protected $disabled; + + /** + * Constructor. + * + * @param \DOMElement $node The node associated with this field + */ + public function __construct(\DOMElement $node) + { + $this->node = $node; + $this->name = $node->getAttribute('name'); + $this->xpath = new \DOMXPath($node->ownerDocument); + + $this->initialize(); + } + + /** + * Returns the name of the field. + * + * @return string The name of the field + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the field. + * + * @return string|array The value of the field + */ + public function getValue() + { + return $this->value; + } + + /** + * Sets the value of the field. + * + * @param string $value The value of the field + */ + public function setValue($value) + { + $this->value = (string) $value; + } + + /** + * Returns true if the field should be included in the submitted values. + * + * @return bool true if the field should be included in the submitted values, false otherwise + */ + public function hasValue() + { + return true; + } + + /** + * Check if the current field is disabled. + * + * @return bool + */ + public function isDisabled() + { + return $this->node->hasAttribute('disabled'); + } + + /** + * Initializes the form field. + */ + abstract protected function initialize(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/InputFormField.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/InputFormField.php new file mode 100644 index 0000000..090913e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/InputFormField.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * InputFormField represents an input form field (an HTML input tag). + * + * For inputs with type of file, checkbox, or radio, there are other more + * specialized classes (cf. FileFormField and ChoiceFormField). + * + * @author Fabien Potencier + */ +class InputFormField extends FormField +{ + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) { + throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); + } + + if ('checkbox' === strtolower($this->node->getAttribute('type'))) { + throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); + } + + if ('file' === strtolower($this->node->getAttribute('type'))) { + throw new \LogicException('File inputs should be instances of FileFormField.'); + } + + $this->value = $this->node->getAttribute('value'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/TextareaFormField.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/TextareaFormField.php new file mode 100644 index 0000000..15526e1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Field/TextareaFormField.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Field; + +/** + * TextareaFormField represents a textarea form field (an HTML textarea tag). + * + * @author Fabien Potencier + */ +class TextareaFormField extends FormField +{ + /** + * Initializes the form field. + * + * @throws \LogicException When node type is incorrect + */ + protected function initialize() + { + if ('textarea' !== $this->node->nodeName) { + throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); + } + + $this->value = ''; + foreach ($this->node->childNodes as $node) { + $this->value .= $node->wholeText; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Form.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Form.php new file mode 100644 index 0000000..0c7a3b2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Form.php @@ -0,0 +1,471 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\ChoiceFormField; +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * Form represents an HTML form. + * + * @author Fabien Potencier + */ +class Form extends Link implements \ArrayAccess +{ + /** + * @var \DOMElement + */ + private $button; + + /** + * @var FormFieldRegistry + */ + private $fields; + + /** + * @var string + */ + private $baseHref; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the form is embedded + * @param string $method The method to use for the link (if null, it defaults to the method defined by the form) + * @param string $baseHref The URI of the used for relative links, but not for empty action + * + * @throws \LogicException if the node is not a button inside a form tag + */ + public function __construct(\DOMElement $node, $currentUri, $method = null, $baseHref = null) + { + parent::__construct($node, $currentUri, $method); + $this->baseHref = $baseHref; + + $this->initialize(); + } + + /** + * Gets the form node associated with this form. + * + * @return \DOMElement A \DOMElement instance + */ + public function getFormNode() + { + return $this->node; + } + + /** + * Sets the value of the fields. + * + * @param array $values An array of field values + * + * @return Form + */ + public function setValues(array $values) + { + foreach ($values as $name => $value) { + $this->fields->set($name, $value); + } + + return $this; + } + + /** + * Gets the field values. + * + * The returned array does not include file fields (@see getFiles). + * + * @return array An array of field values. + */ + public function getValues() + { + $values = array(); + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if (!$field instanceof Field\FileFormField && $field->hasValue()) { + $values[$name] = $field->getValue(); + } + } + + return $values; + } + + /** + * Gets the file field values. + * + * @return array An array of file field values. + */ + public function getFiles() + { + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + return array(); + } + + $files = array(); + + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if ($field instanceof Field\FileFormField) { + $files[$name] = $field->getValue(); + } + } + + return $files; + } + + /** + * Gets the field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values. + */ + public function getPhpValues() + { + $values = array(); + foreach ($this->getValues() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the file field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values. + */ + public function getPhpFiles() + { + $values = array(); + foreach ($this->getFiles() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the URI of the form. + * + * The returned URI is not the same as the form "action" attribute. + * This method merges the value if the method is GET to mimics + * browser behavior. + * + * @return string The URI + */ + public function getUri() + { + $uri = parent::getUri(); + + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + $query = parse_url($uri, PHP_URL_QUERY); + $currentParameters = array(); + if ($query) { + parse_str($query, $currentParameters); + } + + $queryString = http_build_query(array_merge($currentParameters, $this->getValues()), null, '&'); + + $pos = strpos($uri, '?'); + $base = false === $pos ? $uri : substr($uri, 0, $pos); + $uri = rtrim($base.'?'.$queryString, '?'); + } + + return $uri; + } + + protected function getRawUri() + { + return $this->node->getAttribute('action'); + } + + /** + * Gets the form method. + * + * If no method is defined in the form, GET is returned. + * + * @return string The method + */ + public function getMethod() + { + if (null !== $this->method) { + return $this->method; + } + + return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function has($name) + { + return $this->fields->has($name); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function remove($name) + { + $this->fields->remove($name); + } + + /** + * Gets a named field. + * + * @param string $name The field name + * + * @return FormField The field instance + * + * @throws \InvalidArgumentException When field is not present in this form + */ + public function get($name) + { + return $this->fields->get($name); + } + + /** + * Sets a named field. + * + * @param FormField $field The field + */ + public function set(FormField $field) + { + $this->fields->add($field); + } + + /** + * Gets all fields. + * + * @return FormField[] An array of fields + */ + public function all() + { + return $this->fields->all(); + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Gets the value of a field. + * + * @param string $name The field name + * + * @return FormField The associated Field instance + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetGet($name) + { + return $this->fields->get($name); + } + + /** + * Sets the value of a field. + * + * @param string $name The field name + * @param string|array $value The value of the field + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetSet($name, $value) + { + $this->fields->set($name, $value); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + */ + public function offsetUnset($name) + { + $this->fields->remove($name); + } + + /** + * Disables validation. + * + * @return self + */ + public function disableValidation() + { + foreach ($this->fields->all() as $field) { + if ($field instanceof Field\ChoiceFormField) { + $field->disableValidation(); + } + } + + return $this; + } + + /** + * Sets the node for the form. + * + * Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not a button or input or does not have a form ancestor + */ + protected function setNode(\DOMElement $node) + { + $this->button = $node; + if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) { + if ($node->hasAttribute('form')) { + // if the node has the HTML5-compliant 'form' attribute, use it + $formId = $node->getAttribute('form'); + $form = $node->ownerDocument->getElementById($formId); + if (null === $form) { + throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); + } + $this->node = $form; + + return; + } + // we loop until we find a form ancestor + do { + if (null === $node = $node->parentNode) { + throw new \LogicException('The selected node does not have a form ancestor.'); + } + } while ('form' !== $node->nodeName); + } elseif ('form' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Adds form elements related to this form. + * + * Creates an internal copy of the submitted 'button' element and + * the form node or the entire document depending on whether we need + * to find non-descendant elements through HTML5 'form' attribute. + */ + private function initialize() + { + $this->fields = new FormFieldRegistry(); + + $xpath = new \DOMXPath($this->node->ownerDocument); + + // add submitted button if it has a valid name + if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { + if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) { + $name = $this->button->getAttribute('name'); + $this->button->setAttribute('value', '0'); + + // temporarily change the name of the input node for the x coordinate + $this->button->setAttribute('name', $name.'.x'); + $this->set(new Field\InputFormField($this->button)); + + // temporarily change the name of the input node for the y coordinate + $this->button->setAttribute('name', $name.'.y'); + $this->set(new Field\InputFormField($this->button)); + + // restore the original name of the input node + $this->button->setAttribute('name', $name); + } else { + $this->set(new Field\InputFormField($this->button)); + } + } + + // find form elements corresponding to the current form + if ($this->node->hasAttribute('id')) { + // corresponding elements are either descendants or have a matching HTML5 form attribute + $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); + + $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId)); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } else { + // do the xpath query with $this->node as the context node, to only find descendant elements + // however, descendant elements with form attribute are not part of this form + $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } + + if ($this->baseHref && '' !== $this->node->getAttribute('action')) { + $this->currentUri = $this->baseHref; + } + } + + private function addField(\DOMElement $node) + { + if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { + return; + } + + $nodeName = $node->nodeName; + if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\ChoiceFormField($node)); + } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { + // there may be other fields with the same name that are no choice + // fields already registered (see https://github.com/symfony/symfony/issues/11689) + if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { + $this->get($node->getAttribute('name'))->addChoice($node); + } else { + $this->set(new Field\ChoiceFormField($node)); + } + } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\FileFormField($node)); + } elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) { + $this->set(new Field\InputFormField($node)); + } elseif ('textarea' == $nodeName) { + $this->set(new Field\TextareaFormField($node)); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/FormFieldRegistry.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/FormFieldRegistry.php new file mode 100644 index 0000000..edb2788 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/FormFieldRegistry.php @@ -0,0 +1,222 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * This is an internal class that must not be used directly. + */ +class FormFieldRegistry +{ + private $fields = array(); + + private $base; + + /** + * Adds a field to the registry. + * + * @param FormField $field The field + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function add(FormField $field) + { + $segments = $this->getSegments($field->getName()); + + $target = &$this->fields; + while ($segments) { + if (!is_array($target)) { + $target = array(); + } + $path = array_shift($segments); + if ('' === $path) { + $target = &$target[]; + } else { + $target = &$target[$path]; + } + } + $target = $field; + } + + /** + * Removes a field and its children from the registry. + * + * @param string $name The fully qualified name of the base field + * + * @throws \InvalidArgumentException when the name is malformed + */ + public function remove($name) + { + $segments = $this->getSegments($name); + $target = &$this->fields; + while (count($segments) > 1) { + $path = array_shift($segments); + if (!array_key_exists($path, $target)) { + return; + } + $target = &$target[$path]; + } + unset($target[array_shift($segments)]); + } + + /** + * Returns the value of the field and its children. + * + * @param string $name The fully qualified name of the field + * + * @return mixed The value of the field + * + * @throws \InvalidArgumentException when the name is malformed + * @throws \InvalidArgumentException if the field does not exist + */ + public function &get($name) + { + $segments = $this->getSegments($name); + $target = &$this->fields; + while ($segments) { + $path = array_shift($segments); + if (!array_key_exists($path, $target)) { + throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path)); + } + $target = &$target[$path]; + } + + return $target; + } + + /** + * Tests whether the form has the given field. + * + * @param string $name The fully qualified name of the field + * + * @return bool Whether the form has the given field + */ + public function has($name) + { + try { + $this->get($name); + + return true; + } catch (\InvalidArgumentException $e) { + return false; + } + } + + /** + * Set the value of a field and its children. + * + * @param string $name The fully qualified name of the field + * @param mixed $value The value + * + * @throws \InvalidArgumentException when the name is malformed + * @throws \InvalidArgumentException if the field does not exist + */ + public function set($name, $value) + { + $target = &$this->get($name); + if ((!is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) { + $target->setValue($value); + } elseif (is_array($value)) { + $fields = self::create($name, $value); + foreach ($fields->all() as $k => $v) { + $this->set($k, $v); + } + } else { + throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name)); + } + } + + /** + * Returns the list of field with their value. + * + * @return FormField[] The list of fields as array((string) Fully qualified name => (mixed) value) + */ + public function all() + { + return $this->walk($this->fields, $this->base); + } + + /** + * Creates an instance of the class. + * + * This function is made private because it allows overriding the $base and + * the $values properties without any type checking. + * + * @param string $base The fully qualified name of the base field + * @param array $values The values of the fields + * + * @return FormFieldRegistry + */ + private static function create($base, array $values) + { + $registry = new static(); + $registry->base = $base; + $registry->fields = $values; + + return $registry; + } + + /** + * Transforms a PHP array in a list of fully qualified name / value. + * + * @param array $array The PHP array + * @param string $base The name of the base field + * @param array $output The initial values + * + * @return array The list of fields as array((string) Fully qualified name => (mixed) value) + */ + private function walk(array $array, $base = '', array &$output = array()) + { + foreach ($array as $k => $v) { + $path = empty($base) ? $k : sprintf('%s[%s]', $base, $k); + if (is_array($v)) { + $this->walk($v, $path, $output); + } else { + $output[$path] = $v; + } + } + + return $output; + } + + /** + * Splits a field name into segments as a web browser would do. + * + * + * getSegments('base[foo][3][]') = array('base', 'foo, '3', ''); + * + * + * @param string $name The name of the field + * + * @return string[] The list of segments + * + * @throws \InvalidArgumentException when the name is malformed + */ + private function getSegments($name) + { + if (preg_match('/^(?P[^[]+)(?P(\[.*)|$)/', $name, $m)) { + $segments = array($m['base']); + while (!empty($m['extra'])) { + if (preg_match('/^\[(?P.*?)\](?P.*)$/', $m['extra'], $m)) { + $segments[] = $m['segment']; + } else { + throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name)); + } + } + + return $segments; + } + + throw new \InvalidArgumentException(sprintf('Malformed field path "%s"', $name)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Link.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Link.php new file mode 100644 index 0000000..ede0991 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Link.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +/** + * Link represents an HTML link (an HTML a, area or link tag). + * + * @author Fabien Potencier + */ +class Link +{ + /** + * @var \DOMElement + */ + protected $node; + + /** + * @var string The method to use for the link + */ + protected $method; + + /** + * @var string The URI of the page where the link is embedded (or the base href) + */ + protected $currentUri; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the link is embedded (or the base href) + * @param string $method The method to use for the link (get by default) + * + * @throws \InvalidArgumentException if the node is not a link + */ + public function __construct(\DOMElement $node, $currentUri, $method = 'GET') + { + if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) { + throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri)); + } + + $this->setNode($node); + $this->method = $method ? strtoupper($method) : null; + $this->currentUri = $currentUri; + } + + /** + * Gets the node associated with this link. + * + * @return \DOMElement A \DOMElement instance + */ + public function getNode() + { + return $this->node; + } + + /** + * Gets the method associated with this link. + * + * @return string The method + */ + public function getMethod() + { + return $this->method; + } + + /** + * Gets the URI associated with this link. + * + * @return string The URI + */ + public function getUri() + { + $uri = trim($this->getRawUri()); + + // absolute URL? + if (null !== parse_url($uri, PHP_URL_SCHEME)) { + return $uri; + } + + // empty URI + if (!$uri) { + return $this->currentUri; + } + + // an anchor + if ('#' === $uri[0]) { + return $this->cleanupAnchor($this->currentUri).$uri; + } + + $baseUri = $this->cleanupUri($this->currentUri); + + if ('?' === $uri[0]) { + return $baseUri.$uri; + } + + // absolute URL with relative schema + if (0 === strpos($uri, '//')) { + return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri; + } + + $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri); + + // absolute path + if ('/' === $uri[0]) { + return $baseUri.$uri; + } + + // relative path + $path = parse_url(substr($this->currentUri, strlen($baseUri)), PHP_URL_PATH); + $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri); + + return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path; + } + + /** + * Returns raw URI data. + * + * @return string + */ + protected function getRawUri() + { + return $this->node->getAttribute('href'); + } + + /** + * Returns the canonicalized URI path (see RFC 3986, section 5.2.4). + * + * @param string $path URI path + * + * @return string + */ + protected function canonicalizePath($path) + { + if ('' === $path || '/' === $path) { + return $path; + } + + if ('.' === substr($path, -1)) { + $path .= '/'; + } + + $output = array(); + + foreach (explode('/', $path) as $segment) { + if ('..' === $segment) { + array_pop($output); + } elseif ('.' !== $segment) { + $output[] = $segment; + } + } + + return implode('/', $output); + } + + /** + * Sets current \DOMElement instance. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not an anchor + */ + protected function setNode(\DOMElement $node) + { + if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Removes the query string and the anchor from the given uri. + * + * @param string $uri The uri to clean + * + * @return string + */ + private function cleanupUri($uri) + { + return $this->cleanupQuery($this->cleanupAnchor($uri)); + } + + /** + * Remove the query string from the uri. + * + * @param string $uri + * + * @return string + */ + private function cleanupQuery($uri) + { + if (false !== $pos = strpos($uri, '?')) { + return substr($uri, 0, $pos); + } + + return $uri; + } + + /** + * Remove the anchor from the uri. + * + * @param string $uri + * + * @return string + */ + private function cleanupAnchor($uri) + { + if (false !== $pos = strpos($uri, '#')) { + return substr($uri, 0, $pos); + } + + return $uri; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/README.md b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/README.md new file mode 100644 index 0000000..d2c8de5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/README.md @@ -0,0 +1,36 @@ +DomCrawler Component +==================== + +DomCrawler eases DOM navigation for HTML and XML documents. + +If you are familiar with jQuery, DomCrawler is a PHP equivalent: + +```php +use Symfony\Component\DomCrawler\Crawler; + +$crawler = new Crawler(); +$crawler->addContent('

    Hello World!

    '); + +print $crawler->filterXPath('descendant-or-self::body/p')->text(); +``` + +If you are also using the CssSelector component, you can use CSS Selectors +instead of XPath expressions: + +```php +use Symfony\Component\DomCrawler\Crawler; + +$crawler = new Crawler(); +$crawler->addContent('

    Hello World!

    '); + +print $crawler->filter('body > p')->text(); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/DomCrawler/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php new file mode 100755 index 0000000..9f3fdb7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -0,0 +1,1097 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler\Tests; + +use Symfony\Component\DomCrawler\Crawler; + +class CrawlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $crawler = new Crawler(); + $this->assertCount(0, $crawler, '__construct() returns an empty crawler'); + + $doc = new \DOMDocument(); + $node = $doc->createElement('test'); + + $crawler = new Crawler($node); + $this->assertCount(1, $crawler, '__construct() takes a node as a first argument'); + } + + public function testAdd() + { + $crawler = new Crawler(); + $crawler->add($this->createDomDocument()); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMDocument'); + + $crawler = new Crawler(); + $crawler->add($this->createNodeList()); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList'); + + $list = array(); + foreach ($this->createNodeList() as $node) { + $list[] = $node; + } + $crawler = new Crawler(); + $crawler->add($list); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from an array of nodes'); + + $crawler = new Crawler(); + $crawler->add($this->createNodeList()->item(0)); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNode'); + + $crawler = new Crawler(); + $crawler->add('Foo'); + $this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testAddInvalidType() + { + $crawler = new Crawler(); + $crawler->add(1); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Attaching DOM nodes from multiple documents in the same crawler is forbidden. + */ + public function testAddMultipleDocumentNode() + { + $crawler = $this->createTestCrawler(); + $crawler->addHtmlContent('
    ', 'UTF-8'); + } + + public function testAddHtmlContent() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
    ', 'UTF-8'); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string'); + } + + public function testAddHtmlContentWithBaseTag() + { + $crawler = new Crawler(); + + $crawler->addHtmlContent('', 'UTF-8'); + + $this->assertEquals('http://symfony.com', $crawler->filterXPath('//base')->attr('href'), '->addHtmlContent() adds nodes from an HTML string'); + $this->assertEquals('http://symfony.com/contact', $crawler->filterXPath('//a')->link()->getUri(), '->addHtmlContent() adds nodes from an HTML string'); + } + + /** + * @requires extension mbstring + */ + public function testAddHtmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent('
    Tiếng Việt', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + public function testAddHtmlContentInvalidBaseTag() + { + $crawler = new Crawler(null, 'http://symfony.com'); + + $crawler->addHtmlContent('', 'UTF-8'); + + $this->assertEquals('http://symfony.com/contact', current($crawler->filterXPath('//a')->links())->getUri(), '->addHtmlContent() correctly handles a non-existent base tag href attribute'); + } + + public function testAddHtmlContentUnsupportedCharset() + { + $crawler = new Crawler(); + $crawler->addHtmlContent(file_get_contents(__DIR__.'/Fixtures/windows-1250.html'), 'Windows-1250'); + + $this->assertEquals('Žťčýů', $crawler->filterXPath('//p')->text()); + } + + /** + * @requires extension mbstring + */ + public function testAddHtmlContentCharsetGbk() + { + $crawler = new Crawler(); + //gbk encode of

    中文

    + $crawler->addHtmlContent(base64_decode('PGh0bWw+PHA+1tDOxDwvcD48L2h0bWw+'), 'gbk'); + + $this->assertEquals('中文', $crawler->filterXPath('//p')->text()); + } + + public function testAddHtmlContentWithErrors() + { + $internalErrors = libxml_use_internal_errors(true); + + $crawler = new Crawler(); + $crawler->addHtmlContent(<< + + + + + + + +EOF + , 'UTF-8'); + + $errors = libxml_get_errors(); + $this->assertCount(1, $errors); + $this->assertEquals("Tag nav invalid\n", $errors[0]->message); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + public function testAddXmlContent() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
    ', 'UTF-8'); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addXmlContent() adds nodes from an XML string'); + } + + public function testAddXmlContentCharset() + { + $crawler = new Crawler(); + $crawler->addXmlContent('
    Tiếng Việt
    ', 'UTF-8'); + + $this->assertEquals('Tiếng Việt', $crawler->filterXPath('//div')->text()); + } + + public function testAddXmlContentWithErrors() + { + $internalErrors = libxml_use_internal_errors(true); + + $crawler = new Crawler(); + $crawler->addXmlContent(<< + + + + +
    + + +EOF + , 'UTF-8'); + + $this->assertTrue(count(libxml_get_errors()) > 1); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + public function testAddContent() + { + $crawler = new Crawler(); + $crawler->addContent('
    ', 'text/html; charset=UTF-8'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string'); + + $crawler = new Crawler(); + $crawler->addContent('
    ', 'text/html; charset=UTF-8; dir=RTL'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an HTML string with extended content type'); + + $crawler = new Crawler(); + $crawler->addContent('
    '); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() uses text/html as the default type'); + + $crawler = new Crawler(); + $crawler->addContent('
    ', 'text/xml; charset=UTF-8'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); + + $crawler = new Crawler(); + $crawler->addContent('
    ', 'text/xml'); + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addContent() adds nodes from an XML string'); + + $crawler = new Crawler(); + $crawler->addContent('foo bar', 'text/plain'); + $this->assertCount(0, $crawler, '->addContent() does nothing if the type is not (x|ht)ml'); + + $crawler = new Crawler(); + $crawler->addContent('中文'); + $this->assertEquals('中文', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset'); + + $crawler = new Crawler(); + $crawler->addContent(iconv('UTF-8', 'SJIS', '日本語')); + $this->assertEquals('日本語', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag'); + } + + public function testAddDocument() + { + $crawler = new Crawler(); + $crawler->addDocument($this->createDomDocument()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument'); + } + + public function testAddNodeList() + { + $crawler = new Crawler(); + $crawler->addNodeList($this->createNodeList()); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList'); + } + + public function testAddNodes() + { + $list = array(); + foreach ($this->createNodeList() as $node) { + $list[] = $node; + } + + $crawler = new Crawler(); + $crawler->addNodes($list); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNodes() adds nodes from an array of nodes'); + } + + public function testAddNode() + { + $crawler = new Crawler(); + $crawler->addNode($this->createNodeList()->item(0)); + + $this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->addNode() adds nodes from a \DOMNode'); + } + + public function testClear() + { + $doc = new \DOMDocument(); + $node = $doc->createElement('test'); + + $crawler = new Crawler($node); + $crawler->clear(); + $this->assertCount(0, $crawler, '->clear() removes all the nodes from the crawler'); + } + + public function testEq() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + $this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler'); + + $this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list'); + $this->assertCount(0, $crawler->eq(100), '->eq() returns an empty crawler if the nth node does not exist'); + } + + public function testEach() + { + $data = $this->createTestCrawler()->filterXPath('//ul[1]/li')->each(function ($node, $i) { + return $i.'-'.$node->text(); + }); + + $this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list'); + } + + public function testIteration() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + + $this->assertInstanceOf('Traversable', $crawler); + $this->assertContainsOnlyInstancesOf('DOMElement', iterator_to_array($crawler), 'Iterating a Crawler gives DOMElement instances'); + } + + public function testSlice() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $this->assertNotSame($crawler->slice(), $crawler, '->slice() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler->slice(), '->slice() returns a new instance of a crawler'); + + $this->assertCount(3, $crawler->slice(), '->slice() does not slice the nodes in the list if any param is entered'); + $this->assertCount(1, $crawler->slice(1, 1), '->slice() slices the nodes in the list'); + } + + public function testReduce() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $nodes = $crawler->reduce(function ($node, $i) { + return $i !== 1; + }); + $this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler'); + + $this->assertCount(2, $nodes, '->reduce() filters the nodes in the list'); + } + + public function testAttr() + { + $this->assertEquals('first', $this->createTestCrawler()->filterXPath('//li')->attr('class'), '->attr() returns the attribute of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->attr('class'); + $this->fail('->attr() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testMissingAttrValueIsNull() + { + $crawler = new Crawler(); + $crawler->addContent('
    ', 'text/html; charset=UTF-8'); + $div = $crawler->filterXPath('//div'); + + $this->assertEquals('sample value', $div->attr('non-empty-attr'), '->attr() reads non-empty attributes correctly'); + $this->assertEquals('', $div->attr('empty-attr'), '->attr() reads empty attributes correctly'); + $this->assertNull($div->attr('missing-attr'), '->attr() reads missing attributes correctly'); + } + + public function testNodeName() + { + $this->assertEquals('li', $this->createTestCrawler()->filterXPath('//li')->nodeName(), '->nodeName() returns the node name of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->nodeName(); + $this->fail('->nodeName() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->nodeName() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testText() + { + $this->assertEquals('One', $this->createTestCrawler()->filterXPath('//li')->text(), '->text() returns the node value of the first element of the node list'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->text(); + $this->fail('->text() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testHtml() + { + $this->assertEquals('Bar', $this->createTestCrawler()->filterXPath('//a[5]')->html()); + $this->assertEquals('', trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html())); + + try { + $this->createTestCrawler()->filterXPath('//ol')->html(); + $this->fail('->html() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->html() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testExtract() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + + $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list'); + $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list'); + + $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty'); + } + + public function testFilterXpathComplexQueries() + { + $crawler = $this->createTestCrawler()->filterXPath('//body'); + + $this->assertCount(0, $crawler->filterXPath('/input')); + $this->assertCount(0, $crawler->filterXPath('/body')); + $this->assertCount(1, $crawler->filterXPath('./body')); + $this->assertCount(1, $crawler->filterXPath('.//body')); + $this->assertCount(5, $crawler->filterXPath('.//input')); + $this->assertCount(4, $crawler->filterXPath('//form')->filterXPath('//button | //input')); + $this->assertCount(1, $crawler->filterXPath('body')); + $this->assertCount(6, $crawler->filterXPath('//button | //input')); + $this->assertCount(1, $crawler->filterXPath('//body')); + $this->assertCount(1, $crawler->filterXPath('descendant-or-self::body')); + $this->assertCount(1, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('./div'), 'A child selection finds only the current div'); + $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('descendant::div'), 'A descendant selector matches the current div and its child'); + $this->assertCount(3, $crawler->filterXPath('//div[@id="parent"]')->filterXPath('//div'), 'A descendant selector matches the current div and its child'); + $this->assertCount(5, $crawler->filterXPath('(//a | //div)//img')); + $this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)')); + $this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )')); + } + + public function testFilterXPath() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler'); + + $crawler = $this->createTestCrawler()->filterXPath('//ul'); + $this->assertCount(6, $crawler->filterXPath('//li'), '->filterXPath() filters the node list with the XPath expression'); + + $crawler = $this->createTestCrawler(); + $this->assertCount(3, $crawler->filterXPath('//body')->filterXPath('//button')->parents(), '->filterXpath() preserves parents when chained'); + } + + public function testFilterRemovesDuplicates() + { + $crawler = $this->createTestCrawler()->filter('html, body')->filter('li'); + $this->assertCount(6, $crawler, 'The crawler removes duplicates when filtering.'); + } + + public function testFilterXPathWithDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//default:entry/default:id'); + $this->assertCount(1, $crawler, '->filterXPath() automatically registers a namespace'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterXPathWithCustomDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->setDefaultNamespacePrefix('x'); + $crawler = $crawler->filterXPath('//x:entry/x:id'); + + $this->assertCount(1, $crawler, '->filterXPath() lets to override the default namespace prefix'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterXPathWithNamespace() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//yt:accessControl'); + $this->assertCount(2, $crawler, '->filterXPath() automatically registers a namespace'); + } + + public function testFilterXPathWithMultipleNamespaces() + { + $crawler = $this->createTestXmlCrawler()->filterXPath('//media:group/yt:aspectRatio'); + $this->assertCount(1, $crawler, '->filterXPath() automatically registers multiple namespaces'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterXPathWithManuallyRegisteredNamespace() + { + $crawler = $this->createTestXmlCrawler(); + $crawler->registerNamespace('m', 'http://search.yahoo.com/mrss/'); + + $crawler = $crawler->filterXPath('//m:group/yt:aspectRatio'); + $this->assertCount(1, $crawler, '->filterXPath() uses manually registered namespace'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterXPathWithAnUrl() + { + $crawler = $this->createTestXmlCrawler(); + + $crawler = $crawler->filterXPath('//media:category[@scheme="http://gdata.youtube.com/schemas/2007/categories.cat"]'); + $this->assertCount(1, $crawler); + $this->assertSame('Music', $crawler->text()); + } + + public function testFilterXPathWithFakeRoot() + { + $crawler = $this->createTestCrawler(); + $this->assertCount(0, $crawler->filterXPath('.'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('self::*'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + $this->assertCount(0, $crawler->filterXPath('self::_root'), '->filterXPath() returns an empty result if the XPath references the fake root node'); + } + + public function testFilterXPathWithAncestorAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('ancestor::*'), 'The fake root node has no ancestor nodes'); + } + + public function testFilterXPathWithAncestorOrSelfAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('ancestor-or-self::*'), 'The fake root node has no ancestor nodes'); + } + + public function testFilterXPathWithAttributeAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('attribute::*'), 'The fake root node has no attribute nodes'); + } + + public function testFilterXPathWithAttributeAxisAfterElementAxis() + { + $this->assertCount(3, $this->createTestCrawler()->filterXPath('//form/button/attribute::*'), '->filterXPath() handles attribute axes properly when they are preceded by an element filtering axis'); + } + + public function testFilterXPathWithChildAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]'); + + $this->assertCount(1, $crawler->filterXPath('child::div'), 'A child selection finds only the current div'); + } + + public function testFilterXPathWithFollowingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('following::div'), 'The fake root node has no following nodes'); + } + + public function testFilterXPathWithFollowingSiblingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('following-sibling::div'), 'The fake root node has no following nodes'); + } + + public function testFilterXPathWithNamespaceAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//button'); + + $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'The fake root node has no namespace nodes'); + } + + public function testFilterXPathWithNamespaceAxisAfterElementAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//div[@id="parent"]/namespace::*'); + + $this->assertCount(0, $crawler->filterXPath('namespace::*'), 'Namespace axes cannot be requested'); + } + + public function testFilterXPathWithParentAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//button'); + + $this->assertCount(0, $crawler->filterXPath('parent::*'), 'The fake root node has no parent nodes'); + } + + public function testFilterXPathWithPrecedingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('preceding::*'), 'The fake root node has no preceding nodes'); + } + + public function testFilterXPathWithPrecedingSiblingAxis() + { + $crawler = $this->createTestCrawler()->filterXPath('//form'); + + $this->assertCount(0, $crawler->filterXPath('preceding-sibling::*'), 'The fake root node has no preceding nodes'); + } + + public function testFilterXPathWithSelfAxes() + { + $crawler = $this->createTestCrawler()->filterXPath('//a'); + + $this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name'); + $this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name'); + $this->assertCount(9, $crawler->filterXPath('self::*/a')); + } + + public function testFilter() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler'); + + $crawler = $this->createTestCrawler()->filter('ul'); + + $this->assertCount(6, $crawler->filter('li'), '->filter() filters the node list with the CSS selector'); + } + + public function testFilterWithDefaultNamespace() + { + $crawler = $this->createTestXmlCrawler()->filter('default|entry default|id'); + $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); + $this->assertSame('tag:youtube.com,2008:video:kgZRZmEc9j4', $crawler->text()); + } + + public function testFilterWithNamespace() + { + $crawler = $this->createTestXmlCrawler()->filter('yt|accessControl'); + $this->assertCount(2, $crawler, '->filter() automatically registers namespaces'); + } + + public function testFilterWithMultipleNamespaces() + { + $crawler = $this->createTestXmlCrawler()->filter('media|group yt|aspectRatio'); + $this->assertCount(1, $crawler, '->filter() automatically registers namespaces'); + $this->assertSame('widescreen', $crawler->text()); + } + + public function testFilterWithDefaultNamespaceOnly() + { + $crawler = new Crawler(' + + + http://localhost/foo + weekly + 0.5 + 2012-11-16 + + + http://localhost/bar + weekly + 0.5 + 2012-11-16 + + + '); + + $this->assertEquals(2, $crawler->filter('url')->count()); + } + + public function testSelectLink() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler'); + + $this->assertCount(1, $crawler->selectLink('Fabien\'s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(1, $crawler->selectLink('Fabien\'s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(2, $crawler->selectLink('Fabien"s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(2, $crawler->selectLink('Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(1, $crawler->selectLink('\' Fabien"s Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(1, $crawler->selectLink('\' Fabien"s Bar'), '->selectLink() selects links by the alt attribute of a clickable image'); + + $this->assertCount(4, $crawler->selectLink('Foo'), '->selectLink() selects links by the node values'); + $this->assertCount(4, $crawler->selectLink('Bar'), '->selectLink() selects links by the node values'); + } + + public function testSelectButton() + { + $crawler = $this->createTestCrawler(); + $this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler'); + + $this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons'); + + $this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons'); + $this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons'); + + $this->assertEquals(1, $crawler->selectButton('FooBarValue')->count(), '->selectButton() selects buttons with form attribute too'); + $this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too'); + } + + public function testSelectButtonWithSingleQuotesInNameAttribute() + { + $html = << + + +
    +
    + + + + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click \'Here\'')); + } + + public function testSelectButtonWithDoubleQuotesInNameAttribute() + { + $html = << + + +
    + Login +
    +
    + + + + +HTML; + + $crawler = new Crawler($html); + + $this->assertCount(1, $crawler->selectButton('Click "Here"')); + } + + public function testLink() + { + $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance'); + + $this->assertEquals('POST', $crawler->link('post')->getMethod(), '->link() takes a method as its argument'); + + $crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink'); + $this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->link(); + $this->fail('->link() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty'); + } + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The selected node should be instance of DOMElement + */ + public function testInvalidLink() + { + $crawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler->filterXPath('//li/text()')->link(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The selected node should be instance of DOMElement + */ + public function testInvalidLinks() + { + $crawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler->filterXPath('//li/text()')->link(); + } + + public function testSelectLinkAndLinkFiltered() + { + $html = << + + +
    + Login +
    +
    + + + + +HTML; + + $crawler = new Crawler($html); + $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'login-form']"); + + $this->assertCount(0, $filtered->selectLink('Login')); + $this->assertCount(1, $filtered->selectButton('Submit')); + + $filtered = $crawler->filterXPath("descendant-or-self::*[@id = 'action']"); + + $this->assertCount(1, $filtered->selectLink('Login')); + $this->assertCount(0, $filtered->selectButton('Submit')); + + $this->assertCount(1, $crawler->selectLink('Login')->selectLink('Login')); + $this->assertCount(1, $crawler->selectButton('Submit')->selectButton('Submit')); + } + + public function testChaining() + { + $crawler = new Crawler('
    '); + + $this->assertEquals('a', $crawler->filterXPath('//div')->filterXPath('div')->filterXPath('div')->attr('name')); + } + + public function testLinks() + { + $crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo'); + $this->assertInternalType('array', $crawler->links(), '->links() returns an array'); + + $this->assertCount(4, $crawler->links(), '->links() returns an array'); + $links = $crawler->links(); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances'); + + $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty'); + } + + public function testForm() + { + $testCrawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler = $testCrawler->selectButton('FooValue'); + $crawler2 = $testCrawler->selectButton('FooBarValue'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler2->form(), '->form() returns a Form instance'); + + $this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute'); + + $this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument'); + $this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->getValues() returns correct form values'); + $this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->getValues() returns correct form values'); + + try { + $this->createTestCrawler()->filterXPath('//ol')->form(); + $this->fail('->form() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty'); + } + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The selected node should be instance of DOMElement + */ + public function testInvalidForm() + { + $crawler = $this->createTestCrawler('http://example.com/bar/'); + $crawler->filterXPath('//li/text()')->form(); + } + + public function testLast() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); + $this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler'); + + $this->assertEquals('Three', $crawler->last()->text()); + } + + public function testFirst() + { + $crawler = $this->createTestCrawler()->filterXPath('//li'); + $this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler'); + + $this->assertEquals('One', $crawler->first()->text()); + } + + public function testSiblings() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); + $this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler'); + + $nodes = $crawler->siblings(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('One', $nodes->eq(0)->text()); + $this->assertEquals('Three', $nodes->eq(1)->text()); + + $nodes = $this->createTestCrawler()->filterXPath('//li')->eq(0)->siblings(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('Two', $nodes->eq(0)->text()); + $this->assertEquals('Three', $nodes->eq(1)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->siblings(); + $this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testNextAll() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(1); + $this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler'); + + $nodes = $crawler->nextAll(); + $this->assertEquals(1, $nodes->count()); + $this->assertEquals('Three', $nodes->eq(0)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->nextAll(); + $this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testPreviousAll() + { + $crawler = $this->createTestCrawler()->filterXPath('//li')->eq(2); + $this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler'); + + $nodes = $crawler->previousAll(); + $this->assertEquals(2, $nodes->count()); + $this->assertEquals('Two', $nodes->eq(0)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->previousAll(); + $this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty'); + } + } + + public function testChildren() + { + $crawler = $this->createTestCrawler()->filterXPath('//ul'); + $this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler'); + + $nodes = $crawler->children(); + $this->assertEquals(3, $nodes->count()); + $this->assertEquals('One', $nodes->eq(0)->text()); + $this->assertEquals('Two', $nodes->eq(1)->text()); + $this->assertEquals('Three', $nodes->eq(2)->text()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->children(); + $this->fail('->children() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty'); + } + + try { + $crawler = new Crawler('

    '); + $crawler->filter('p')->children(); + $this->assertTrue(true, '->children() does not trigger a notice if the node has no children'); + } catch (\PHPUnit_Framework_Error_Notice $e) { + $this->fail('->children() does not trigger a notice if the node has no children'); + } + } + + public function testParents() + { + $crawler = $this->createTestCrawler()->filterXPath('//li[1]'); + $this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler'); + + $nodes = $crawler->parents(); + $this->assertEquals(3, $nodes->count()); + + $nodes = $this->createTestCrawler()->filterXPath('//html')->parents(); + $this->assertEquals(0, $nodes->count()); + + try { + $this->createTestCrawler()->filterXPath('//ol')->parents(); + $this->fail('->parents() throws an \InvalidArgumentException if the node list is empty'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty'); + } + } + + /** + * @dataProvider getBaseTagData + */ + public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri = null, $description = null) + { + $crawler = new Crawler('', $currentUri); + $this->assertEquals($expectedUri, $crawler->filterXPath('//a')->link()->getUri(), $description); + } + + public function getBaseTagData() + { + return array( + array('http://base.com', 'link', 'http://base.com/link'), + array('//base.com', 'link', 'https://base.com/link', 'https://domain.com', ' tag can use a schema-less URL'), + array('path/', 'link', 'https://domain.com/path/link', 'https://domain.com', ' tag can set a path'), + array('http://base.com', '#', 'http://base.com#', 'http://domain.com/path/link', ' tag does work with links to an anchor'), + array('http://base.com', '', 'http://base.com', 'http://domain.com/path/link', ' tag does work with empty links'), + ); + } + + /** + * @dataProvider getBaseTagWithFormData + */ + public function testBaseTagWithForm($baseValue, $actionValue, $expectedUri, $currentUri = null, $description = null) + { + $crawler = new Crawler('
    ', + array('bar' => array('InputFormField', 'bar')), + ), + array( + 'appends the submitted button value but not other submit buttons', + ' + ', + array('foobar' => array('InputFormField', 'foobar')), + ), + array( + 'turns an image input into x and y fields', + '', + array('bar.x' => array('InputFormField', '0'), 'bar.y' => array('InputFormField', '0')), + ), + array( + 'returns textareas', + ' + ', + array('foo' => array('TextareaFormField', 'foo')), + ), + array( + 'returns inputs', + ' + ', + array('foo' => array('InputFormField', 'foo')), + ), + array( + 'returns checkboxes', + ' + ', + array('foo' => array('ChoiceFormField', 'foo')), + ), + array( + 'returns not-checked checkboxes', + ' + ', + array('foo' => array('ChoiceFormField', false)), + ), + array( + 'returns radio buttons', + ' + + ', + array('foo' => array('ChoiceFormField', 'bar')), + ), + array( + 'returns file inputs', + ' + ', + array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), + ), + ); + } + + public function testGetFormNode() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
    '); + + $form = new Form($dom->getElementsByTagName('input')->item(0), 'http://example.com'); + + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testGetFormNodeFromNamedForm() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
    '); + + $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); + + $this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testGetMethod() + { + $form = $this->createForm('
    '); + $this->assertEquals('GET', $form->getMethod(), '->getMethod() returns get if no method is defined'); + + $form = $this->createForm('
    '); + $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form'); + + $form = $this->createForm('
    ', 'put'); + $this->assertEquals('PUT', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + + $form = $this->createForm('
    ', 'delete'); + $this->assertEquals('DELETE', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + + $form = $this->createForm('
    ', 'patch'); + $this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided'); + } + + public function testGetSetValue() + { + $form = $this->createForm('
    '); + + $this->assertEquals('foo', $form['foo']->getValue(), '->offsetGet() returns the value of a form field'); + + $form['foo'] = 'bar'; + + $this->assertEquals('bar', $form['foo']->getValue(), '->offsetSet() changes the value of a form field'); + + try { + $form['foobar'] = 'bar'; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } + + try { + $form['foobar']; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the field does not exist'); + } + } + + public function testSetValueOnMultiValuedFieldsWithMalformedName() + { + $form = $this->createForm('
    '); + + try { + $form['foo[bar'] = 'bar'; + $this->fail('->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->offsetSet() throws an \InvalidArgumentException exception if the name is malformed.'); + } + } + + public function testDisableValidation() + { + $form = $this->createForm('
    + + + + '); + + $form->disableValidation(); + + $form['foo[bar]']->select('foo'); + $form['foo[baz]']->select('bar'); + $this->assertEquals('foo', $form['foo[bar]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); + $this->assertEquals('bar', $form['foo[baz]']->getValue(), '->disableValidation() disables validation of all ChoiceFormField.'); + } + + public function testOffsetUnset() + { + $form = $this->createForm('
    '); + unset($form['foo']); + $this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field'); + } + + public function testOffsetExists() + { + $form = $this->createForm('
    '); + + $this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists'); + $this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist'); + } + + public function testGetValues() + { + $form = $this->createForm('
    '); + $this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => array()), $form->getValues(), '->getValues() returns all form field values'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields'); + } + + public function testSetValues() + { + $form = $this->createForm('
    '); + $form->setValues(array('foo' => false, 'bar' => 'foo')); + $this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields'); + } + + public function testMultiselectSetValues() + { + $form = $this->createForm('
    '); + $form->setValues(array('multi' => array('foo', 'bar'))); + $this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select'); + } + + public function testGetPhpValues() + { + $form = $this->createForm('
    '); + $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), "->getPhpValues() doesn't return empty values"); + } + + public function testGetFiles() + { + $form = $this->createForm('
    '); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST'); + + $form = $this->createForm('
    ', 'put'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT'); + + $form = $this->createForm('
    ', 'delete'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE'); + + $form = $this->createForm('
    ', 'patch'); + $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH'); + + $form = $this->createForm('
    '); + $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields'); + } + + public function testGetPhpFiles() + { + $form = $this->createForm('
    '); + $this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names'); + + $form = $this->createForm('
    '); + $this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively'); + } + + /** + * @dataProvider provideGetUriValues + */ + public function testGetUri($message, $form, $values, $uri, $method = null) + { + $form = $this->createForm($form, $method); + $form->setValues($values); + + $this->assertEquals('http://example.com'.$uri, $form->getUri(), '->getUri() '.$message); + } + + public function testGetBaseUri() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
    '); + + $nodes = $dom->getElementsByTagName('input'); + $form = new Form($nodes->item($nodes->length - 1), 'http://www.foo.com/'); + $this->assertEquals('http://www.foo.com/foo.php', $form->getUri()); + } + + public function testGetUriWithAnchor() + { + $form = $this->createForm('
    ', null, 'http://example.com/id/123'); + + $this->assertEquals('http://example.com/id/123#foo', $form->getUri()); + } + + public function testGetUriActionAbsolute() + { + $formHtml = '
    '; + + $form = $this->createForm($formHtml); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://login.foo.com'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://login.foo.com/bar/'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + // The action URI haven't the same domain Host have an another domain as Host + $form = $this->createForm($formHtml, null, 'https://www.foo.com'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + + $form = $this->createForm($formHtml, null, 'https://www.foo.com/bar/'); + $this->assertEquals('https://login.foo.com/login.php?login_attempt=1', $form->getUri(), '->getUri() returns absolute URIs set in the action form'); + } + + public function testGetUriAbsolute() + { + $form = $this->createForm('
    ', null, 'http://localhost/foo/'); + $this->assertEquals('http://localhost/foo/foo', $form->getUri(), '->getUri() returns absolute URIs'); + + $form = $this->createForm('
    ', null, 'http://localhost/foo/'); + $this->assertEquals('http://localhost/foo', $form->getUri(), '->getUri() returns absolute URIs'); + } + + public function testGetUriWithOnlyQueryString() + { + $form = $this->createForm('
    ', null, 'http://localhost/foo/bar'); + $this->assertEquals('http://localhost/foo/bar?get=param', $form->getUri(), '->getUri() returns absolute URIs only if the host has been defined in the constructor'); + } + + public function testGetUriWithoutAction() + { + $form = $this->createForm('
    ', null, 'http://localhost/foo/bar'); + $this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined'); + } + + public function provideGetUriValues() + { + return array( + array( + 'returns the URI of the form', + '
    ', + array(), + '/foo', + ), + array( + 'appends the form values if the method is get', + '
    ', + array(), + '/foo?foo=foo', + ), + array( + 'appends the form values and merges the submitted values', + '
    ', + array('foo' => 'bar'), + '/foo?foo=bar', + ), + array( + 'does not append values if the method is post', + '
    ', + array(), + '/foo', + ), + array( + 'does not append values if the method is patch', + '
    ', + array(), + '/foo', + 'PUT', + ), + array( + 'does not append values if the method is delete', + '
    ', + array(), + '/foo', + 'DELETE', + ), + array( + 'does not append values if the method is put', + '
    ', + array(), + '/foo', + 'PATCH', + ), + array( + 'appends the form values to an existing query string', + '
    ', + array(), + '/foo?bar=bar&foo=foo', + ), + array( + 'replaces query values with the form values', + '
    ', + array(), + '/foo?bar=foo', + ), + array( + 'returns an empty URI if the action is empty', + '
    ', + array(), + '/', + ), + array( + 'appends the form values even if the action is empty', + '
    ', + array(), + '/?foo=foo', + ), + array( + 'chooses the path if the action attribute value is a sharp (#)', + '
    ', + array(), + '/#', + ), + ); + } + + public function testHas() + { + $form = $this->createForm('
    '); + + $this->assertFalse($form->has('foo'), '->has() returns false if a field is not in the form'); + $this->assertTrue($form->has('bar'), '->has() returns true if a field is in the form'); + } + + public function testRemove() + { + $form = $this->createForm('
    '); + $form->remove('bar'); + $this->assertFalse($form->has('bar'), '->remove() removes a field'); + } + + public function testGet() + { + $form = $this->createForm('
    '); + + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $form->get('bar'), '->get() returns the field object associated with the given name'); + + try { + $form->get('foo'); + $this->fail('->get() throws an \InvalidArgumentException if the field does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertTrue(true, '->get() throws an \InvalidArgumentException if the field does not exist'); + } + } + + public function testAll() + { + $form = $this->createForm('
    '); + + $fields = $form->all(); + $this->assertCount(1, $fields, '->all() return an array of form field objects'); + $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Field\\InputFormField', $fields['bar'], '->all() return an array of form field objects'); + } + + public function testSubmitWithoutAFormButton() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + +
    + + + + '); + + $nodes = $dom->getElementsByTagName('form'); + $form = new Form($nodes->item(0), 'http://example.com'); + $this->assertSame($nodes->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form'); + } + + public function testTypeAttributeIsCaseInsensitive() + { + $form = $this->createForm('
    '); + $this->assertTrue($form->has('example.x'), '->has() returns true if the image input was correctly turned into an x and a y fields'); + $this->assertTrue($form->has('example.y'), '->has() returns true if the image input was correctly turned into an x and a y fields'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryAddThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('[foo]')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryRemoveThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->remove('[foo]'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryGetThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->get('[foo]'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist() + { + $registry = new FormFieldRegistry(); + $registry->get('foo'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistrySetThrowAnExceptionWhenTheNameIsMalformed() + { + $registry = new FormFieldRegistry(); + $registry->set('[foo]', null); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist() + { + $registry = new FormFieldRegistry(); + $registry->set('foo', null); + } + + public function testFormFieldRegistryHasReturnsTrueWhenTheFQNExists() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[bar]')); + + $this->assertTrue($registry->has('foo')); + $this->assertTrue($registry->has('foo[bar]')); + $this->assertFalse($registry->has('bar')); + $this->assertFalse($registry->has('foo[foo]')); + } + + public function testFormRegistryFieldsCanBeRemoved() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo')); + $registry->remove('foo'); + $this->assertFalse($registry->has('foo')); + } + + public function testFormRegistrySupportsMultivaluedFields() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[]')); + $registry->add($this->getFormFieldMock('foo[]')); + $registry->add($this->getFormFieldMock('bar[5]')); + $registry->add($this->getFormFieldMock('bar[]')); + $registry->add($this->getFormFieldMock('bar[baz]')); + + $this->assertEquals( + array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'), + array_keys($registry->all()) + ); + } + + public function testFormRegistrySetValues() + { + $registry = new FormFieldRegistry(); + $registry->add($f2 = $this->getFormFieldMock('foo[2]')); + $registry->add($f3 = $this->getFormFieldMock('foo[3]')); + $registry->add($fbb = $this->getFormFieldMock('foo[bar][baz]')); + + $f2 + ->expects($this->exactly(2)) + ->method('setValue') + ->with(2) + ; + + $f3 + ->expects($this->exactly(2)) + ->method('setValue') + ->with(3) + ; + + $fbb + ->expects($this->exactly(2)) + ->method('setValue') + ->with('fbb') + ; + + $registry->set('foo[2]', 2); + $registry->set('foo[3]', 3); + $registry->set('foo[bar][baz]', 'fbb'); + + $registry->set('foo', array( + 2 => 2, + 3 => 3, + 'bar' => array( + 'baz' => 'fbb', + ), + )); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Cannot set value on a compound field "foo[bar]". + */ + public function testFormRegistrySetValueOnCompoundField() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('foo[bar][baz]')); + + $registry->set('foo[bar]', 'fbb'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unreachable field "0" + */ + public function testFormRegistrySetArrayOnNotCompoundField() + { + $registry = new FormFieldRegistry(); + $registry->add($this->getFormFieldMock('bar')); + + $registry->set('bar', array('baz')); + } + + public function testDifferentFieldTypesWithSameName() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + + +
    + + + + + + + + + + '); + $form = new Form($dom->getElementsByTagName('form')->item(0), 'http://example.com'); + + $this->assertInstanceOf('Symfony\Component\DomCrawler\Field\ChoiceFormField', $form->get('option')); + } + + protected function getFormFieldMock($name, $value = null) + { + $field = $this + ->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField') + ->setMethods(array('getName', 'getValue', 'setValue', 'initialize')) + ->disableOriginalConstructor() + ->getMock() + ; + + $field + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)) + ; + + $field + ->expects($this->any()) + ->method('getValue') + ->will($this->returnValue($value)) + ; + + return $field; + } + + protected function createForm($form, $method = null, $currentUri = null) + { + $dom = new \DOMDocument(); + $dom->loadHTML(''.$form.''); + + $xPath = new \DOMXPath($dom); + $nodes = $xPath->query('//input | //button'); + + if (null === $currentUri) { + $currentUri = 'http://example.com/'; + } + + return new Form($nodes->item($nodes->length - 1), $currentUri, $method); + } + + protected function createTestHtml5Form() + { + $dom = new \DOMDocument(); + $dom->loadHTML(' + +

    Hello form

    +
    +
    + +
    + + + + +
    +
    +
    + + + +
    + +
    + +
    +
    +
    + + +
    + + + +
    +
    +
    + + + + +

    " layout: + * + * + * use Symfony\Component\Form\Extension\Templating\TemplatingExtension; + * + * $formFactory = Forms::createFormFactoryBuilder() + * ->addExtension(new TemplatingExtension($engine, null, array( + * 'FrameworkBundle:Form', + * 'FrameworkBundle:FormTable', + * ))) + * ->getFormFactory(); + * + * + * @author Bernhard Schussek + */ +final class Forms +{ + /** + * Creates a form factory with the default configuration. + * + * @return FormFactoryInterface The form factory. + */ + public static function createFormFactory() + { + return self::createFormFactoryBuilder()->getFormFactory(); + } + + /** + * Creates a form factory builder with the default configuration. + * + * @return FormFactoryBuilderInterface The form factory builder. + */ + public static function createFormFactoryBuilder() + { + $builder = new FormFactoryBuilder(); + $builder->addExtension(new CoreExtension()); + + return $builder; + } + + /** + * This class cannot be instantiated. + */ + private function __construct() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/Guess.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/Guess.php new file mode 100644 index 0000000..0595e7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/Guess.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Guess; + +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * Base class for guesses made by TypeGuesserInterface implementation. + * + * Each instance contains a confidence value about the correctness of the guess. + * Thus an instance with confidence HIGH_CONFIDENCE is more likely to be + * correct than an instance with confidence LOW_CONFIDENCE. + * + * @author Bernhard Schussek + */ +abstract class Guess +{ + /** + * Marks an instance with a value that is extremely likely to be correct. + * + * @var int + */ + const VERY_HIGH_CONFIDENCE = 3; + + /** + * Marks an instance with a value that is very likely to be correct. + * + * @var int + */ + const HIGH_CONFIDENCE = 2; + + /** + * Marks an instance with a value that is likely to be correct. + * + * @var int + */ + const MEDIUM_CONFIDENCE = 1; + + /** + * Marks an instance with a value that may be correct. + * + * @var int + */ + const LOW_CONFIDENCE = 0; + + /** + * The confidence about the correctness of the value. + * + * One of VERY_HIGH_CONFIDENCE, HIGH_CONFIDENCE, MEDIUM_CONFIDENCE + * and LOW_CONFIDENCE. + * + * @var int + */ + private $confidence; + + /** + * Returns the guess most likely to be correct from a list of guesses. + * + * If there are multiple guesses with the same, highest confidence, the + * returned guess is any of them. + * + * @param Guess[] $guesses An array of guesses + * + * @return Guess|null The guess with the highest confidence + */ + public static function getBestGuess(array $guesses) + { + $result = null; + $maxConfidence = -1; + + foreach ($guesses as $guess) { + if ($maxConfidence < $confidence = $guess->getConfidence()) { + $maxConfidence = $confidence; + $result = $guess; + } + } + + return $result; + } + + /** + * Constructor. + * + * @param int $confidence The confidence + * + * @throws InvalidArgumentException if the given value of confidence is unknown + */ + public function __construct($confidence) + { + if (self::VERY_HIGH_CONFIDENCE !== $confidence && self::HIGH_CONFIDENCE !== $confidence && + self::MEDIUM_CONFIDENCE !== $confidence && self::LOW_CONFIDENCE !== $confidence) { + throw new InvalidArgumentException('The confidence should be one of the constants defined in Guess.'); + } + + $this->confidence = $confidence; + } + + /** + * Returns the confidence that the guessed value is correct. + * + * @return int One of the constants VERY_HIGH_CONFIDENCE, HIGH_CONFIDENCE, + * MEDIUM_CONFIDENCE and LOW_CONFIDENCE + */ + public function getConfidence() + { + return $this->confidence; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/TypeGuess.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/TypeGuess.php new file mode 100644 index 0000000..87cc60a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/TypeGuess.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Guess; + +/** + * Contains a guessed class name and a list of options for creating an instance + * of that class. + * + * @author Bernhard Schussek + */ +class TypeGuess extends Guess +{ + /** + * The guessed field type. + * + * @var string + */ + private $type; + + /** + * The guessed options for creating an instance of the guessed class. + * + * @var array + */ + private $options; + + /** + * Constructor. + * + * @param string $type The guessed field type + * @param array $options The options for creating instances of the + * guessed class + * @param int $confidence The confidence that the guessed class name + * is correct + */ + public function __construct($type, array $options, $confidence) + { + parent::__construct($confidence); + + $this->type = $type; + $this->options = $options; + } + + /** + * Returns the guessed field type. + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Returns the guessed options for creating instances of the guessed type. + * + * @return array + */ + public function getOptions() + { + return $this->options; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/ValueGuess.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/ValueGuess.php new file mode 100644 index 0000000..fe40e02 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Guess/ValueGuess.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Guess; + +/** + * Contains a guessed value. + * + * @author Bernhard Schussek + */ +class ValueGuess extends Guess +{ + /** + * The guessed value. + * + * @var array + */ + private $value; + + /** + * Constructor. + * + * @param string $value The guessed value + * @param int $confidence The confidence that the guessed class name + * is correct + */ + public function __construct($value, $confidence) + { + parent::__construct($confidence); + + $this->value = $value; + } + + /** + * Returns the guessed value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/Form/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/NativeRequestHandler.php b/vendor/symfony/symfony/src/Symfony/Component/Form/NativeRequestHandler.php new file mode 100644 index 0000000..c9a7685 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/NativeRequestHandler.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Util\ServerParams; + +/** + * A request handler using PHP's super globals $_GET, $_POST and $_SERVER. + * + * @author Bernhard Schussek + */ +class NativeRequestHandler implements RequestHandlerInterface +{ + /** + * @var ServerParams + */ + private $serverParams; + + /** + * {@inheritdoc} + */ + public function __construct(ServerParams $params = null) + { + $this->serverParams = $params ?: new ServerParams(); + } + + /** + * The allowed keys of the $_FILES array. + * + * @var array + */ + private static $fileKeys = array( + 'error', + 'name', + 'size', + 'tmp_name', + 'type', + ); + + /** + * {@inheritdoc} + */ + public function handleRequest(FormInterface $form, $request = null) + { + if (null !== $request) { + throw new UnexpectedTypeException($request, 'null'); + } + + $name = $form->getName(); + $method = $form->getConfig()->getMethod(); + + if ($method !== self::getRequestMethod()) { + return; + } + + // For request methods that must not have a request body we fetch data + // from the query string. Otherwise we look for data in the request body. + if ('GET' === $method || 'HEAD' === $method || 'TRACE' === $method) { + if ('' === $name) { + $data = $_GET; + } else { + // Don't submit GET requests if the form's name does not exist + // in the request + if (!isset($_GET[$name])) { + return; + } + + $data = $_GET[$name]; + } + } else { + // Mark the form with an error if the uploaded size was too large + // This is done here and not in FormValidator because $_POST is + // empty when that error occurs. Hence the form is never submitted. + $contentLength = $this->serverParams->getContentLength(); + $maxContentLength = $this->serverParams->getPostMaxSize(); + + if (!empty($maxContentLength) && $contentLength > $maxContentLength) { + // Submit the form, but don't clear the default values + $form->submit(null, false); + + $form->addError(new FormError( + $form->getConfig()->getOption('post_max_size_message'), + null, + array('{{ max }}' => $this->serverParams->getNormalizedIniPostMaxSize()) + )); + + return; + } + + $fixedFiles = array(); + foreach ($_FILES as $fileKey => $file) { + $fixedFiles[$fileKey] = self::stripEmptyFiles(self::fixPhpFilesArray($file)); + } + + if ('' === $name) { + $params = $_POST; + $files = $fixedFiles; + } elseif (array_key_exists($name, $_POST) || array_key_exists($name, $fixedFiles)) { + $default = $form->getConfig()->getCompound() ? array() : null; + $params = array_key_exists($name, $_POST) ? $_POST[$name] : $default; + $files = array_key_exists($name, $fixedFiles) ? $fixedFiles[$name] : $default; + } else { + // Don't submit the form if it is not present in the request + return; + } + + if (is_array($params) && is_array($files)) { + $data = array_replace_recursive($params, $files); + } else { + $data = $params ?: $files; + } + } + + // Don't auto-submit the form unless at least one field is present. + if ('' === $name && count(array_intersect_key($data, $form->all())) <= 0) { + return; + } + + $form->submit($data, 'PATCH' !== $method); + } + + /** + * Returns the method used to submit the request to the server. + * + * @return string The request method. + */ + private static function getRequestMethod() + { + $method = isset($_SERVER['REQUEST_METHOD']) + ? strtoupper($_SERVER['REQUEST_METHOD']) + : 'GET'; + + if ('POST' === $method && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { + $method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); + } + + return $method; + } + + /** + * Fixes a malformed PHP $_FILES array. + * + * PHP has a bug that the format of the $_FILES array differs, depending on + * whether the uploaded file fields had normal field names or array-like + * field names ("normal" vs. "parent[child]"). + * + * This method fixes the array to look like the "normal" $_FILES array. + * + * It's safe to pass an already converted array, in which case this method + * just returns the original array unmodified. + * + * This method is identical to {@link \Symfony\Component\HttpFoundation\FileBag::fixPhpFilesArray} + * and should be kept as such in order to port fixes quickly and easily. + * + * @param array $data + * + * @return array + */ + private static function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys !== $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + + foreach ($data['name'] as $key => $name) { + $files[$key] = self::fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $name, + 'type' => $data['type'][$key], + 'tmp_name' => $data['tmp_name'][$key], + 'size' => $data['size'][$key], + )); + } + + return $files; + } + + /** + * Sets empty uploaded files to NULL in the given uploaded files array. + * + * @param mixed $data The file upload data. + * + * @return array|null Returns the stripped upload data. + */ + private static function stripEmptyFiles($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys === $keys) { + if (UPLOAD_ERR_NO_FILE === $data['error']) { + return; + } + + return $data; + } + + foreach ($data as $key => $value) { + $data[$key] = self::stripEmptyFiles($value); + } + + return $data; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/PreloadedExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/PreloadedExtension.php new file mode 100644 index 0000000..5b49e35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/PreloadedExtension.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * A form extension with preloaded types, type exceptions and type guessers. + * + * @author Bernhard Schussek + */ +class PreloadedExtension implements FormExtensionInterface +{ + /** + * @var FormTypeInterface[] + */ + private $types = array(); + + /** + * @var array[FormTypeExtensionInterface[]] + */ + private $typeExtensions = array(); + + /** + * @var FormTypeGuesserInterface + */ + private $typeGuesser; + + /** + * Creates a new preloaded extension. + * + * @param FormTypeInterface[] $types The types that the extension should support + * @param FormTypeExtensionInterface[][] $typeExtensions The type extensions that the extension should support + * @param FormTypeGuesserInterface|null $typeGuesser The guesser that the extension should support + */ + public function __construct(array $types, array $typeExtensions, FormTypeGuesserInterface $typeGuesser = null) + { + $this->typeExtensions = $typeExtensions; + $this->typeGuesser = $typeGuesser; + + foreach ($types as $type) { + $this->types[get_class($type)] = $type; + } + } + + /** + * {@inheritdoc} + */ + public function getType($name) + { + if (!isset($this->types[$name])) { + throw new InvalidArgumentException(sprintf('The type "%s" can not be loaded by this extension', $name)); + } + + return $this->types[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasType($name) + { + return isset($this->types[$name]); + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions($name) + { + return isset($this->typeExtensions[$name]) + ? $this->typeExtensions[$name] + : array(); + } + + /** + * {@inheritdoc} + */ + public function hasTypeExtensions($name) + { + return !empty($this->typeExtensions[$name]); + } + + /** + * {@inheritdoc} + */ + public function getTypeGuesser() + { + return $this->typeGuesser; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/README.md b/vendor/symfony/symfony/src/Symfony/Component/Form/README.md new file mode 100644 index 0000000..efe2809 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/README.md @@ -0,0 +1,26 @@ +Form Component +============== + +Form provides tools for defining forms, rendering and mapping request data to +related models. Furthermore it provides integration with the Validation +component. + +Resources +--------- + +Silex integration: + +https://github.com/silexphp/Silex/blob/master/src/Silex/Provider/FormServiceProvider.php + +Documentation: + +https://symfony.com/doc/3.0/book/forms.html + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Form/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/RequestHandlerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/RequestHandlerInterface.php new file mode 100644 index 0000000..d0a58e6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/RequestHandlerInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Submits forms if they were submitted. + * + * @author Bernhard Schussek + */ +interface RequestHandlerInterface +{ + /** + * Submits a form if it was submitted. + * + * @param FormInterface $form The form to submit. + * @param mixed $request The current request. + */ + public function handleRequest(FormInterface $form, $request = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.php new file mode 100644 index 0000000..42b0352 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormType.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * A wrapper for a form type and its extensions. + * + * @author Bernhard Schussek + */ +class ResolvedFormType implements ResolvedFormTypeInterface +{ + /** + * @var FormTypeInterface + */ + private $innerType; + + /** + * @var FormTypeExtensionInterface[] + */ + private $typeExtensions; + + /** + * @var ResolvedFormTypeInterface|null + */ + private $parent; + + /** + * @var OptionsResolver + */ + private $optionsResolver; + + public function __construct(FormTypeInterface $innerType, array $typeExtensions = array(), ResolvedFormTypeInterface $parent = null) + { + foreach ($typeExtensions as $extension) { + if (!$extension instanceof FormTypeExtensionInterface) { + throw new UnexpectedTypeException($extension, 'Symfony\Component\Form\FormTypeExtensionInterface'); + } + } + + $this->innerType = $innerType; + $this->typeExtensions = $typeExtensions; + $this->parent = $parent; + } + + /** + * {@inheritdoc} + */ + public function getBlockPrefix() + { + return $this->innerType->getBlockPrefix(); + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + public function getInnerType() + { + return $this->innerType; + } + + /** + * {@inheritdoc} + */ + public function getTypeExtensions() + { + return $this->typeExtensions; + } + + /** + * {@inheritdoc} + */ + public function createBuilder(FormFactoryInterface $factory, $name, array $options = array()) + { + $options = $this->getOptionsResolver()->resolve($options); + + // Should be decoupled from the specific option at some point + $dataClass = isset($options['data_class']) ? $options['data_class'] : null; + + $builder = $this->newBuilder($name, $dataClass, $factory, $options); + $builder->setType($this); + + return $builder; + } + + /** + * {@inheritdoc} + */ + public function createView(FormInterface $form, FormView $parent = null) + { + return $this->newView($parent); + } + + /** + * Configures a form builder for the type hierarchy. + * + * @param FormBuilderInterface $builder The builder to configure. + * @param array $options The options used for the configuration. + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + if (null !== $this->parent) { + $this->parent->buildForm($builder, $options); + } + + $this->innerType->buildForm($builder, $options); + + foreach ($this->typeExtensions as $extension) { + $extension->buildForm($builder, $options); + } + } + + /** + * Configures a form view for the type hierarchy. + * + * This method is called before the children of the view are built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + if (null !== $this->parent) { + $this->parent->buildView($view, $form, $options); + } + + $this->innerType->buildView($view, $form, $options); + + foreach ($this->typeExtensions as $extension) { + $extension->buildView($view, $form, $options); + } + } + + /** + * Finishes a form view for the type hierarchy. + * + * This method is called after the children of the view have been built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function finishView(FormView $view, FormInterface $form, array $options) + { + if (null !== $this->parent) { + $this->parent->finishView($view, $form, $options); + } + + $this->innerType->finishView($view, $form, $options); + + foreach ($this->typeExtensions as $extension) { + /* @var FormTypeExtensionInterface $extension */ + $extension->finishView($view, $form, $options); + } + } + + /** + * Returns the configured options resolver used for this type. + * + * @return \Symfony\Component\OptionsResolver\OptionsResolver The options resolver. + */ + public function getOptionsResolver() + { + if (null === $this->optionsResolver) { + if (null !== $this->parent) { + $this->optionsResolver = clone $this->parent->getOptionsResolver(); + } else { + $this->optionsResolver = new OptionsResolver(); + } + + $this->innerType->configureOptions($this->optionsResolver); + + foreach ($this->typeExtensions as $extension) { + $extension->configureOptions($this->optionsResolver); + } + } + + return $this->optionsResolver; + } + + /** + * Creates a new builder instance. + * + * Override this method if you want to customize the builder class. + * + * @param string $name The name of the builder. + * @param string $dataClass The data class. + * @param FormFactoryInterface $factory The current form factory. + * @param array $options The builder options. + * + * @return FormBuilderInterface The new builder instance. + */ + protected function newBuilder($name, $dataClass, FormFactoryInterface $factory, array $options) + { + if ($this->innerType instanceof ButtonTypeInterface) { + return new ButtonBuilder($name, $options); + } + + if ($this->innerType instanceof SubmitButtonTypeInterface) { + return new SubmitButtonBuilder($name, $options); + } + + return new FormBuilder($name, $dataClass, new EventDispatcher(), $factory, $options); + } + + /** + * Creates a new view instance. + * + * Override this method if you want to customize the view class. + * + * @param FormView|null $parent The parent view, if available. + * + * @return FormView A new view instance. + */ + protected function newView(FormView $parent = null) + { + return new FormView($parent); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeFactory.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeFactory.php new file mode 100644 index 0000000..d93d1c0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeFactory.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * @author Bernhard Schussek + */ +class ResolvedFormTypeFactory implements ResolvedFormTypeFactoryInterface +{ + /** + * {@inheritdoc} + */ + public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null) + { + return new ResolvedFormType($type, $typeExtensions, $parent); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php new file mode 100644 index 0000000..3240d77 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeFactoryInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Creates ResolvedFormTypeInterface instances. + * + * This interface allows you to use your custom ResolvedFormTypeInterface + * implementation, within which you can customize the concrete FormBuilderInterface + * implementations or FormView subclasses that are used by the framework. + * + * @author Bernhard Schussek + */ +interface ResolvedFormTypeFactoryInterface +{ + /** + * Resolves a form type. + * + * @param FormTypeInterface $type + * @param FormTypeExtensionInterface[] $typeExtensions + * @param ResolvedFormTypeInterface|null $parent + * + * @return ResolvedFormTypeInterface + * + * @throws Exception\UnexpectedTypeException if the types parent {@link FormTypeInterface::getParent()} is not a string + * @throws Exception\InvalidArgumentException if the types parent can not be retrieved from any extension + */ + public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeInterface.php new file mode 100644 index 0000000..592f459 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ResolvedFormTypeInterface.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * A wrapper for a form type and its extensions. + * + * @author Bernhard Schussek + */ +interface ResolvedFormTypeInterface +{ + /** + * Returns the prefix of the template block name for this type. + * + * @return string The prefix of the template block name + */ + public function getBlockPrefix(); + + /** + * Returns the parent type. + * + * @return ResolvedFormTypeInterface|null The parent type or null. + */ + public function getParent(); + + /** + * Returns the wrapped form type. + * + * @return FormTypeInterface The wrapped form type. + */ + public function getInnerType(); + + /** + * Returns the extensions of the wrapped form type. + * + * @return FormTypeExtensionInterface[] An array of {@link FormTypeExtensionInterface} instances. + */ + public function getTypeExtensions(); + + /** + * Creates a new form builder for this type. + * + * @param FormFactoryInterface $factory The form factory. + * @param string $name The name for the builder. + * @param array $options The builder options. + * + * @return FormBuilderInterface The created form builder. + */ + public function createBuilder(FormFactoryInterface $factory, $name, array $options = array()); + + /** + * Creates a new form view for a form of this type. + * + * @param FormInterface $form The form to create a view for. + * @param FormView $parent The parent view or null. + * + * @return FormView The created form view. + */ + public function createView(FormInterface $form, FormView $parent = null); + + /** + * Configures a form builder for the type hierarchy. + * + * @param FormBuilderInterface $builder The builder to configure. + * @param array $options The options used for the configuration. + */ + public function buildForm(FormBuilderInterface $builder, array $options); + + /** + * Configures a form view for the type hierarchy. + * + * It is called before the children of the view are built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function buildView(FormView $view, FormInterface $form, array $options); + + /** + * Finishes a form view for the type hierarchy. + * + * It is called after the children of the view have been built. + * + * @param FormView $view The form view to configure. + * @param FormInterface $form The form corresponding to the view. + * @param array $options The options used for the configuration. + */ + public function finishView(FormView $view, FormInterface $form, array $options); + + /** + * Returns the configured options resolver used for this type. + * + * @return OptionsResolver The options resolver. + */ + public function getOptionsResolver(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/config/validation.xml b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/config/validation.xml new file mode 100644 index 0000000..2f2364b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/config/validation.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ar.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ar.xlf new file mode 100644 index 0000000..990b039 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ar.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + هذا النموذج يجب الا يحتوى على اى حقول اضافية. + + + The uploaded file was too large. Please try to upload a smaller file. + مساحة الملف المرسل كبيرة. من فضلك حاول ارسال ملف اصغر. + + + The CSRF token is invalid. Please try to resubmit the form. + قيمة رمز الموقع غير صحيحة. من فضلك اعد ارسال النموذج. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.az.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.az.xlf new file mode 100644 index 0000000..69e4473 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.az.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Bu formada əlavə sahə olmamalıdır. + + + The uploaded file was too large. Please try to upload a smaller file. + Yüklənən fayl çox böyükdür. Lütfən daha kiçik fayl yükləyin. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF nişanı yanlışdır. Lütfen formanı yenidən göndərin. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.bg.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.bg.xlf new file mode 100644 index 0000000..5c768c3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.bg.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Тази форма не трябва да съдържа допълнителни полета. + + + The uploaded file was too large. Please try to upload a smaller file. + Каченият файл е твърде голям. Моля, опитайте да качите по-малък файл. + + + The CSRF token is invalid. Please try to resubmit the form. + Невалиден CSRF токен. Моля, опитайте да изпратите формата отново. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf new file mode 100644 index 0000000..3a2fa48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ca.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Aquest formulari no hauria de contenir camps addicionals. + + + The uploaded file was too large. Please try to upload a smaller file. + L'arxiu pujat és massa gran. Per favor, pugi un arxiu més petit. + + + The CSRF token is invalid. Please try to resubmit the form. + El token CSRF no és vàlid. Per favor, provi d'enviar novament el formulari. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf new file mode 100644 index 0000000..776da49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Tato skupina polí nesmí obsahovat další pole. + + + The uploaded file was too large. Please try to upload a smaller file. + Nahraný soubor je příliš velký. Nahrajte prosím menší soubor. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF token je neplatný. Zkuste prosím znovu odeslat formulář. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.da.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.da.xlf new file mode 100644 index 0000000..c2dd460 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.da.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Feltgruppen må ikke indeholde ekstra felter. + + + The uploaded file was too large. Please try to upload a smaller file. + Den oploadede fil var for stor. Opload venligst en mindre fil. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF nøglen er ugyldig. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.de.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.de.xlf new file mode 100644 index 0000000..a9a1831 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.de.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Dieses Formular sollte keine zusätzlichen Felder enthalten. + + + The uploaded file was too large. Please try to upload a smaller file. + Die hochgeladene Datei ist zu groß. Versuchen Sie bitte eine kleinere Datei hochzuladen. + + + The CSRF token is invalid. Please try to resubmit the form. + Der CSRF-Token ist ungültig. Versuchen Sie bitte das Formular erneut zu senden. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.el.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.el.xlf new file mode 100644 index 0000000..ba2ced7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.el.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Αυτή η φόρμα δεν πρέπει να περιέχει επιπλέον πεδία. + + + The uploaded file was too large. Please try to upload a smaller file. + Το αρχείο είναι πολύ μεγάλο. Παρακαλούμε προσπαθήστε να ανεβάσετε ένα μικρότερο αρχείο. + + + The CSRF token is invalid. Please try to resubmit the form. + Το CSRF token δεν είναι έγκυρο. Παρακαλούμε δοκιμάστε να υποβάλετε τη φόρμα ξανά. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.en.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.en.xlf new file mode 100644 index 0000000..b8542d3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.en.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + This form should not contain extra fields. + + + The uploaded file was too large. Please try to upload a smaller file. + The uploaded file was too large. Please try to upload a smaller file. + + + The CSRF token is invalid. Please try to resubmit the form. + The CSRF token is invalid. Please try to resubmit the form. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.es.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.es.xlf new file mode 100644 index 0000000..b609e53 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.es.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulario no debería contener campos adicionales. + + + The uploaded file was too large. Please try to upload a smaller file. + El archivo subido es demasiado grande. Por favor, suba un archivo más pequeño. + + + The CSRF token is invalid. Please try to resubmit the form. + El token CSRF no es válido. Por favor, pruebe a enviar nuevamente el formulario. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.et.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.et.xlf new file mode 100644 index 0000000..1a9867f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.et.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Väljade grupp ei tohiks sisalda lisaväljasid. + + + The uploaded file was too large. Please try to upload a smaller file. + Üleslaaditud fail oli liiga suur. Palun proovi uuesti väiksema failiga. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF-märgis on vigane. Palun proovi vormi uuesti esitada. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.eu.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.eu.xlf new file mode 100644 index 0000000..ba741ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.eu.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Formulario honek ez luke aparteko eremurik eduki behar. + + + The uploaded file was too large. Please try to upload a smaller file. + Igotako fitxategia handiegia da. Mesedez saiatu fitxategi txikiago bat igotzen. + + + The CSRF token is invalid. + CSRF tokena ez da egokia. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf new file mode 100644 index 0000000..468d2f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fa.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + این فرم نباید فیلد اضافی داشته باشد. + + + The uploaded file was too large. Please try to upload a smaller file. + فایل بارگذاری شده بسیار بزرگ است. لطفا فایل کوچکتری را بارگزاری کنید. + + + The CSRF token is invalid. Please try to resubmit the form. + مقدار CSRF نامعتبر است. لطفا فرم را مجددا ارسال فرمایید.. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf new file mode 100644 index 0000000..4eb393a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fi.xlf @@ -0,0 +1,19 @@ + + + + + + This field group should not contain extra fields. + Tämä kenttäryhmä ei voi sisältää ylimääräisiä kenttiä. + + + The uploaded file was too large. Please try to upload a smaller file. + Ladattu tiedosto on liian iso. Ole hyvä ja lataa pienempi tiedosto. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF tarkiste on virheellinen. Ole hyvä ja yritä lähettää lomake uudestaan. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf new file mode 100644 index 0000000..21f9010 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.fr.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ce formulaire ne doit pas contenir des champs supplémentaires. + + + The uploaded file was too large. Please try to upload a smaller file. + Le fichier téléchargé est trop volumineux. Merci d'essayer d'envoyer un fichier plus petit. + + + The CSRF token is invalid. Please try to resubmit the form. + Le jeton CSRF est invalide. Veuillez renvoyer le formulaire. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf new file mode 100644 index 0000000..88cc653 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.gl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulario non debería conter campos adicionais. + + + The uploaded file was too large. Please try to upload a smaller file. + O arquivo subido é demasiado grande. Por favor, suba un arquivo máis pequeno. + + + The CSRF token is invalid. Please try to resubmit the form. + O token CSRF non é válido. Por favor, probe a enviar novamente o formulario. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.he.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.he.xlf new file mode 100644 index 0000000..74a71d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.he.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + הטופס לא צריך להכיל שדות נוספים. + + + The uploaded file was too large. Please try to upload a smaller file. + הקובץ שהועלה גדול מדי. נסה להעלות קובץ קטן יותר. + + + The CSRF token is invalid. Please try to resubmit the form. + אסימון CSRF אינו חוקי. אנא נסה לשלוח שוב את הטופס. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf new file mode 100644 index 0000000..8d0bd29 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hr.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ovaj obrazac ne smije sadržavati dodatna polja. + + + The uploaded file was too large. Please try to upload a smaller file. + Prenesena datoteka je prevelika. Molim pokušajte prenijeti manju datoteku. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF vrijednost nije ispravna. Pokušajte ponovo poslati obrazac. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf new file mode 100644 index 0000000..374cfaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ez a mezőcsoport nem tartalmazhat extra mezőket. + + + The uploaded file was too large. Please try to upload a smaller file. + A feltöltött fájl túl nagy. Kérem, próbáljon egy kisebb fájlt feltölteni. + + + The CSRF token is invalid. Please try to resubmit the form. + Érvénytelen CSRF token. Kérem, próbálja újra elküldeni az űrlapot. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hy.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hy.xlf new file mode 100644 index 0000000..5a6091f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.hy.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Այս ձևը չպետք է պարունակի լրացուցիչ տողեր. + + + The uploaded file was too large. Please try to upload a smaller file. + Վերբեռնված ֆայլը չափազանց մեծ է: Խնդրվում է վերբեռնել ավելի փոքր չափսի ֆայլ. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF արժեքը անթույլատրելի է: Փորձեք նորից ուղարկել ձևը. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.id.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.id.xlf new file mode 100644 index 0000000..b067d98 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.id.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Gabungan kolom tidak boleh mengandung kolom tambahan. + + + The uploaded file was too large. Please try to upload a smaller file. + Berkas yang di unggah terlalu besar. Silahkan coba unggah berkas yang lebih kecil. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF-Token tidak sah. Silahkan coba kirim ulang formulir. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.it.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.it.xlf new file mode 100644 index 0000000..aa15264 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.it.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Questo form non dovrebbe contenere nessun campo extra. + + + The uploaded file was too large. Please try to upload a smaller file. + Il file caricato è troppo grande. Per favore caricare un file più piccolo. + + + The CSRF token is invalid. Please try to resubmit the form. + Il token CSRF non è valido. Provare a reinviare il form. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf new file mode 100644 index 0000000..0db5edd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ja.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + フィールドグループに追加のフィールドを含んではなりません。 + + + The uploaded file was too large. Please try to upload a smaller file. + アップロードされたファイルが大きすぎます。小さなファイルで再度アップロードしてください。 + + + The CSRF token is invalid. Please try to resubmit the form. + CSRFトークンが無効です、再送信してください。 + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lb.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lb.xlf new file mode 100644 index 0000000..a111ffe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lb.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Dës Feldergrupp sollt keng zousätzlech Felder enthalen. + + + The uploaded file was too large. Please try to upload a smaller file. + De geschécktene Fichier ass ze grouss. Versicht wann ech gelift ee méi klenge Fichier eropzelueden. + + + The CSRF token is invalid. Please try to resubmit the form. + Den CSRF-Token ass ongëlteg. Versicht wann ech gelift de Formulaire nach eng Kéier ze schécken. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf new file mode 100644 index 0000000..25f3088 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lt.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Forma negali turėti papildomų laukų. + + + The uploaded file was too large. Please try to upload a smaller file. + Įkelta byla yra per didelė. bandykite įkelti mažesnę. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF kodas nepriimtinas. Bandykite siųsti formos užklausą dar kartą. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lv.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lv.xlf new file mode 100644 index 0000000..9cdfb2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.lv.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Šajā veidlapā nevajadzētu būt papildus ievades laukiem. + + + The uploaded file was too large. Please try to upload a smaller file. + Augšupielādētā faila izmērs bija par lielu. Lūdzu mēģiniet augšupielādēt mazāka izmēra failu. + + + The CSRF token is invalid. Please try to resubmit the form. + Dotais CSRF talons nav derīgs. Lūdzu mēģiniet vēlreiz iesniegt veidlapu. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf new file mode 100644 index 0000000..002b01c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.mn.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Форм нэмэлт талбар багтаах боломжгүй. + + + The uploaded file was too large. Please try to upload a smaller file. + Upload хийсэн файл хэтэрхий том байна. Бага хэмжээтэй файл оруулна уу. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF token буруу байна. Формоо дахин илгээнэ үү. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf new file mode 100644 index 0000000..5e36bd5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.nb.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Feltgruppen må ikke inneholde ekstra felter. + + + The uploaded file was too large. Please try to upload a smaller file. + Den opplastede file var for stor. Vennligst last opp en mindre fil. + + + The CSRF token is invalid. + CSRF nøkkelen er ugyldig. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.nl.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.nl.xlf new file mode 100644 index 0000000..3d737d7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.nl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Dit formulier mag geen extra velden bevatten. + + + The uploaded file was too large. Please try to upload a smaller file. + Het geüploade bestand is te groot. Probeer een kleiner bestand te uploaden. + + + The CSRF token is invalid. Please try to resubmit the form. + De CSRF-token is ongeldig. Probeer het formulier opnieuw te versturen. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pl.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pl.xlf new file mode 100644 index 0000000..64def2a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ten formularz nie powinien zawierać dodatkowych pól. + + + The uploaded file was too large. Please try to upload a smaller file. + Wgrany plik był za duży. Proszę spróbować wgrać mniejszy plik. + + + The CSRF token is invalid. Please try to resubmit the form. + Token CSRF jest nieprawidłowy. Proszę spróbować wysłać formularz ponownie. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf new file mode 100644 index 0000000..554a810 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pt.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulário não deveria conter campos extra. + + + The uploaded file was too large. Please try to upload a smaller file. + O arquivo enviado é muito grande. Por favor, tente enviar um ficheiro mais pequeno. + + + The CSRF token is invalid. Please try to resubmit the form. + O token CSRF é inválido. Por favor submeta o formulário novamente. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf new file mode 100644 index 0000000..9ae4d71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.pt_BR.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Este formulário não deve conter campos adicionais. + + + The uploaded file was too large. Please try to upload a smaller file. + O arquivo enviado é muito grande. Por favor, tente enviar um arquivo menor. + + + The CSRF token is invalid. Please try to resubmit the form. + O token CSRF é inválido. Por favor, tente reenviar o formulário. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf new file mode 100644 index 0000000..25abab3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ro.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Aceast formular nu ar trebui să conțină câmpuri suplimentare. + + + The uploaded file was too large. Please try to upload a smaller file. + Fișierul încărcat a fost prea mare. Vă rugăm sa încărcați un fișier mai mic. + + + The CSRF token is invalid. Please try to resubmit the form. + Token-ul CSRF este invalid. Vă rugăm să trimiteți formularul incă o dată. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ru.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ru.xlf new file mode 100644 index 0000000..8d829f1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.ru.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Эта форма не должна содержать дополнительных полей. + + + The uploaded file was too large. Please try to upload a smaller file. + Загруженный файл слишком большой. Пожалуйста, попробуйте загрузить файл меньшего размера. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF значение недопустимо. Пожалуйста, попробуйте повторить отправку формы. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf new file mode 100644 index 0000000..638d0cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sk.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Polia by nemali obsahovať ďalšie prvky. + + + The uploaded file was too large. Please try to upload a smaller file. + Odoslaný súbor je príliš veľký. Prosím odošlite súbor s menšou veľkosťou. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF token je neplatný. Prosím skúste znovu odoslať formulár. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sl.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sl.xlf new file mode 100644 index 0000000..cd68c1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ta obrazec ne sme vsebovati dodatnih polj. + + + The uploaded file was too large. Please try to upload a smaller file. + Naložena datoteka je prevelika. Prosimo, poizkusite naložiti manjšo. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF vrednost je napačna. Prosimo, ponovno pošljite obrazec. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf new file mode 100644 index 0000000..ff7f550 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sr_Cyrl.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Овај формулар не треба да садржи додатна поља. + + + The uploaded file was too large. Please try to upload a smaller file. + Отпремљена датотека је била превелика. Молим покушајте отпремање мање датотеке. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF вредност је невалидна. Покушајте поново. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf new file mode 100644 index 0000000..6c4be35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sr_Latn.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ovaj formular ne treba da sadrži dodatna polja. + + + The uploaded file was too large. Please try to upload a smaller file. + Otpremljena datoteka je bila prevelika. Molim pokušajte otpremanje manje datoteke. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF vrednost je nevalidna. Pokušajte ponovo. + + + + \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf new file mode 100644 index 0000000..4e2518b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.sv.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Formuläret kan inte innehålla extra fält. + + + The uploaded file was too large. Please try to upload a smaller file. + Den uppladdade filen var för stor. Försök ladda upp en mindre fil. + + + The CSRF token is invalid. + CSRF-symbolen är inte giltig. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.uk.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.uk.xlf new file mode 100644 index 0000000..4c739fa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.uk.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + Ця форма не повинна містити додаткових полів. + + + The uploaded file was too large. Please try to upload a smaller file. + Завантажений файл занадто великий. Будь-ласка, спробуйте завантажити файл меншого розміру. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF значення недопустиме. Будь-ласка, спробуйте відправити форму знову. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf new file mode 100644 index 0000000..8bdf7fb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Resources/translations/validators.zh_CN.xlf @@ -0,0 +1,19 @@ + + + + + + This form should not contain extra fields. + 该表单中不可有额外字段. + + + The uploaded file was too large. Please try to upload a smaller file. + 上传文件太大, 请重新尝试上传一个较小的文件. + + + The CSRF token is invalid. Please try to resubmit the form. + CSRF 验证符无效, 请重新提交. + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/ReversedTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/ReversedTransformer.php new file mode 100644 index 0000000..fc5cd12 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/ReversedTransformer.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Reverses a transformer. + * + * When the transform() method is called, the reversed transformer's + * reverseTransform() method is called and vice versa. + * + * @author Bernhard Schussek + */ +class ReversedTransformer implements DataTransformerInterface +{ + /** + * The reversed transformer. + * + * @var DataTransformerInterface + */ + protected $reversedTransformer; + + /** + * Reverses this transformer. + * + * @param DataTransformerInterface $reversedTransformer + */ + public function __construct(DataTransformerInterface $reversedTransformer) + { + $this->reversedTransformer = $reversedTransformer; + } + + /** + * {@inheritdoc} + */ + public function transform($value) + { + return $this->reversedTransformer->reverseTransform($value); + } + + /** + * {@inheritdoc} + */ + public function reverseTransform($value) + { + return $this->reversedTransformer->transform($value); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButton.php b/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButton.php new file mode 100644 index 0000000..e95cdee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButton.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A button that submits the form. + * + * @author Bernhard Schussek + */ +class SubmitButton extends Button implements ClickableInterface +{ + /** + * @var bool + */ + private $clicked = false; + + /** + * {@inheritdoc} + */ + public function isClicked() + { + return $this->clicked; + } + + /** + * Submits data to the button. + * + * @param null|string $submittedData The data. + * @param bool $clearMissing Not used. + * + * @return SubmitButton The button instance + * + * @throws Exception\AlreadySubmittedException If the form has already been submitted. + */ + public function submit($submittedData, $clearMissing = true) + { + parent::submit($submittedData, $clearMissing); + + $this->clicked = null !== $submittedData; + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButtonBuilder.php b/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButtonBuilder.php new file mode 100644 index 0000000..931f399 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButtonBuilder.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A builder for {@link SubmitButton} instances. + * + * @author Bernhard Schussek + */ +class SubmitButtonBuilder extends ButtonBuilder +{ + /** + * Creates the button. + * + * @return SubmitButton The button + */ + public function getForm() + { + return new SubmitButton($this->getFormConfig()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButtonTypeInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButtonTypeInterface.php new file mode 100644 index 0000000..f7ac13f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/SubmitButtonTypeInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * A type that should be converted into a {@link SubmitButton} instance. + * + * @author Bernhard Schussek + */ +interface SubmitButtonTypeInterface extends FormTypeInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormBuilderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormBuilderInterface.php new file mode 100644 index 0000000..185a8a1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormBuilderInterface.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\FormBuilderInterface as BaseFormBuilderInterface; + +interface FormBuilderInterface extends \Iterator, BaseFormBuilderInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php new file mode 100644 index 0000000..caab269 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormIntegrationTestCase.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\Forms; +use Symfony\Component\Form\FormFactoryInterface; + +/** + * @author Bernhard Schussek + */ +abstract class FormIntegrationTestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormFactoryInterface + */ + protected $factory; + + protected function setUp() + { + $this->factory = Forms::createFormFactoryBuilder() + ->addExtensions($this->getExtensions()) + ->getFormFactory(); + } + + protected function getExtensions() + { + return array(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormInterface.php new file mode 100644 index 0000000..4af4603 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormInterface.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\FormInterface as BaseFormInterface; + +interface FormInterface extends \Iterator, BaseFormInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php new file mode 100644 index 0000000..090eb8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +/** + * Base class for performance tests. + * + * Copied from Doctrine 2's OrmPerformanceTestCase. + * + * @author robo + * @author Bernhard Schussek + */ +abstract class FormPerformanceTestCase extends FormIntegrationTestCase +{ + /** + * @var int + */ + protected $maxRunningTime = 0; + + /** + * {@inheritdoc} + */ + protected function runTest() + { + $s = microtime(true); + parent::runTest(); + $time = microtime(true) - $s; + + if ($this->maxRunningTime != 0 && $time > $this->maxRunningTime) { + $this->fail( + sprintf( + 'expected running time: <= %s but was: %s', + + $this->maxRunningTime, + $time + ) + ); + } + } + + /** + * @param int $maxRunningTime + * + * @throws \InvalidArgumentException + */ + public function setMaxRunningTime($maxRunningTime) + { + if (is_int($maxRunningTime) && $maxRunningTime >= 0) { + $this->maxRunningTime = $maxRunningTime; + } else { + throw new \InvalidArgumentException(); + } + } + + /** + * @since Method available since Release 2.3.0 + * + * @return int + */ + public function getMaxRunningTime() + { + return $this->maxRunningTime; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Test/TypeTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/TypeTestCase.php new file mode 100644 index 0000000..8779775 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Test/TypeTestCase.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Test; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\EventDispatcher\EventDispatcher; + +abstract class TypeTestCase extends FormIntegrationTestCase +{ + /** + * @var FormBuilder + */ + protected $builder; + + /** + * @var EventDispatcher + */ + protected $dispatcher; + + protected function setUp() + { + parent::setUp(); + + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->builder = new FormBuilder(null, null, $this->dispatcher, $this->factory); + } + + public static function assertDateTimeEquals(\DateTime $expected, \DateTime $actual) + { + self::assertEquals($expected->format('c'), $actual->format('c')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php new file mode 100644 index 0000000..1273fa5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractBootstrap3HorizontalLayoutTest.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3LayoutTest +{ + public function testLabelOnForm() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@class="col-sm-2 control-label required"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelDoesNotRenderFieldAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="col-sm-2 control-label required"] +' + ); + } + + public function testLabelWithCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class col-sm-2 control-label required"] +' + ); + } + + public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), 'Custom label', array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class col-sm-2 control-label required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class col-sm-2 control-label required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testStartTag() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('
    ', $html); + } + + public function testStartTagWithOverriddenVars() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'put', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'method' => 'post', + 'action' => 'http://foo.com/directory', + )); + + $this->assertSame('', $html); + } + + public function testStartTagForMultipartForm() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )) + ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType') + ->getForm(); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagWithExtraAttributes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'attr' => array('class' => 'foobar'), + )); + + $this->assertSame('', $html); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php new file mode 100644 index 0000000..32a4af7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -0,0 +1,2150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; + +abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest +{ + public function testLabelOnForm() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@class="control-label required"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelDoesNotRenderFieldAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="control-label required"] +' + ); + } + + public function testLabelWithCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class control-label required"] +' + ); + } + + public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), 'Custom label', array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class control-label required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class control-label required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testErrors() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form->addError(new FormError('[trans]Error 1[/trans]')); + $form->addError(new FormError('[trans]Error 2[/trans]')); + $view = $form->createView(); + $html = $this->renderErrors($view); + + $this->assertMatchesXpath($html, +'/div + [@class="alert alert-danger"] + [ + ./ul + [@class="list-unstyled"] + [ + ./li + [.=" [trans]Error 1[/trans]"] + [ + ./span[@class="glyphicon glyphicon-exclamation-sign"] + ] + /following-sibling::li + [.=" [trans]Error 2[/trans]"] + [ + ./span[@class="glyphicon glyphicon-exclamation-sign"] + ] + ] + [count(./li)=2] + ] +' + ); + } + + public function testOverrideWidgetBlock() + { + // see custom_widgets.html.twig + $form = $this->factory->createNamed('text_id', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderWidget($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./input + [@type="text"] + [@id="text_id"] + [@class="form-control"] + ] + [@id="container"] +' + ); + } + + public function testCheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', true); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="checkbox"] + [ + ./label + [.=" [trans]Name[/trans]"] + [ + ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class"][@checked="checked"][@value="1"] + ] + ] +' + ); + } + + public function testUncheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="checkbox"] + [ + ./label + [.=" [trans]Name[/trans]"] + [ + ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class"][not(@checked)] + ] + ] +' + ); + } + + public function testCheckboxWithValue() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="checkbox"] + [ + ./label + [.=" [trans]Name[/trans]"] + [ + ./input[@type="checkbox"][@name="name"][@id="my&id"][@class="my&class"][@value="foo&bar"] + ] + ] +' + ); + } + + public function testSingleChoice() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + 'choice_translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="Choice&A"] + /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="Placeholder&Not&Translated"] + /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"] + /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPreferred() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '-- sep --', 'attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceWithPreferredAndNoSeparator() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => null, 'attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPreferredAndBlankSeparator() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '', 'attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@disabled="disabled"][not(@selected)][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testChoiceWithOnlyPreferred() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&a', '&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@class="my&class form-control"] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceNonRequired() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value=""][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceNonRequiredNoneSelected() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value=""][.=""] + /following-sibling::option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceNonRequiredWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'placeholder' => 'Select&Anything&Not&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Anything&Not&Me[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceRequiredWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + 'placeholder' => 'Test&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [@required="required"] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Test&Me[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceRequiredWithPlaceholderViaView() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('placeholder' => '', 'attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [@required="required"] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceGrouped() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array( + 'Group&1' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'Group&2' => array('Choice&C' => '&c'), + ), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [./optgroup[@label="[trans]Group&1[/trans]"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] + ] + [./optgroup[@label="[trans]Group&2[/trans]"] + [./option[@value="&c"][not(@selected)][.="[trans]Choice&C[/trans]"]] + [count(./option)=1] + ] + [count(./optgroup)=2] +' + ); + } + + public function testMultipleChoice() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name[]"] + [@class="my&class form-control"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name[]"] + [@class="my&class form-control"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceSkipsPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => true, + 'expanded' => false, + 'placeholder' => 'Test&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name[]"] + [@class="my&class form-control"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceNonRequired() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => false, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name[]"] + [@class="my&class form-control"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceExpanded() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testSingleChoiceExpandedWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + 'choice_translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" Choice&A"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" Choice&B"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testSingleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)][@class="foo&bar"] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testSingleChoiceExpandedWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + 'placeholder' => 'Test&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Test&Me[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" Placeholder&Not&Translated"] + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" Choice&A"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" Choice&B"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testSingleChoiceExpandedWithBooleanValue() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', true, array( + 'choices' => array('Choice&A' => '1', 'Choice&B' => '0'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testMultipleChoiceExpanded() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testMultipleChoiceExpandedWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + 'choice_translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.=" Choice&A"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" Choice&B"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" Choice&C"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testMultipleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)][@class="foo&bar"] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"] + ] +' + ); + } + + public function testCountry() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [./option[@value="AT"][@selected="selected"][.="Austria"]] + [count(./option)>200] +' + ); + } + + public function testCountryWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT', array( + 'placeholder' => 'Select&Country', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Country[/trans]"]] + [./option[@value="AT"][@selected="selected"][.="Austria"]] + [count(./option)>201] +' + ); + } + + public function testDateTime() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'with_seconds' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [ + ./select + [@id="name_date_month"] + [@class="form-control"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [@class="form-control"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [@class="form-control"] + [./option[@value="2011"][@selected="selected"]] + /following-sibling::select + [@id="name_time_hour"] + [@class="form-control"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [@class="form-control"] + [./option[@value="5"][@selected="selected"]] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithPlaceholderGlobal() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'input' => 'string', + 'placeholder' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_date_month"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_date_day"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_date_year"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_time_hour"] + [@class="form-control"] + [./option[@value=""][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_time_minute"] + [@class="form-control"] + [./option[@value=""][.="[trans]Change&Me[/trans]"]] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithHourAndMinute() + { + $data = array('year' => '2011', 'month' => '2', 'day' => '3', 'hour' => '4', 'minute' => '5'); + + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', $data, array( + 'input' => 'array', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_date_month"] + [@class="form-control"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [@class="form-control"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [@class="form-control"] + [./option[@value="2011"][@selected="selected"]] + /following-sibling::select + [@id="name_time_hour"] + [@class="form-control"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [@class="form-control"] + [./option[@value="5"][@selected="selected"]] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'with_seconds' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_date_month"] + [@class="form-control"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [@class="form-control"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [@class="form-control"] + [./option[@value="2011"][@selected="selected"]] + /following-sibling::select + [@id="name_time_hour"] + [@class="form-control"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [@class="form-control"] + [./option[@value="5"][@selected="selected"]] + /following-sibling::select + [@id="name_time_second"] + [@class="form-control"] + [./option[@value="6"][@selected="selected"]] + ] + [count(.//select)=6] +' + ); + } + + public function testDateTimeSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'date_widget' => 'single_text', + 'time_widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./input + [@type="date"] + [@id="name_date"] + [@name="name[date]"] + [@class="form-control"] + [@value="2011-02-03"] + /following-sibling::input + [@type="time"] + [@id="name_time"] + [@name="name[time]"] + [@class="form-control"] + [@value="04:05"] + ] +' + ); + } + + public function testDateTimeWithWidgetSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="datetime"] + [@name="name"] + [@class="my&class form-control"] + [@value="2011-02-03T04:05:06Z"] +' + ); + } + + public function testDateTimeWithWidgetSingleTextIgnoreDateAndTimeWidgets() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="datetime"] + [@name="name"] + [@class="my&class form-control"] + [@value="2011-02-03T04:05:06Z"] +' + ); + } + + public function testDateChoice() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'choice', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_month"] + [@class="form-control"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [@class="form-control"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [@class="form-control"] + [./option[@value="2011"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateChoiceWithPlaceholderGlobal() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'placeholder' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_month"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_day"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_year"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateChoiceWithPlaceholderOnYear() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'required' => false, + 'placeholder' => array('year' => 'Change&Me'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_month"] + [@class="form-control"] + [./option[@value="1"]] + /following-sibling::select + [@id="name_day"] + [@class="form-control"] + [./option[@value="1"]] + /following-sibling::select + [@id="name_year"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./input + [@id="name_month"] + [@type="text"] + [@class="form-control"] + [@value="2"] + /following-sibling::input + [@id="name_day"] + [@type="text"] + [@class="form-control"] + [@value="3"] + /following-sibling::input + [@id="name_year"] + [@type="text"] + [@class="form-control"] + [@value="2011"] + ] + [count(./input)=3] +' + ); + } + + public function testDateSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="date"] + [@name="name"] + [@class="my&class form-control"] + [@value="2011-02-03"] +' + ); + } + + public function testBirthDay() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType', '2000-02-03', array( + 'input' => 'string', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_month"] + [@class="form-control"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [@class="form-control"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [@class="form-control"] + [./option[@value="2000"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testBirthDayWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType', '1950-01-01', array( + 'input' => 'string', + 'placeholder' => '', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_month"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.=""]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.=""]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.=""]] + [./option[@value="1950"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testEmail() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="email"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testEmailWithMaxLength() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType', 'foo&bar', array( + 'attr' => array('maxlength' => 123), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="email"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testHidden() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\HiddenType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="hidden"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] +' + ); + } + + public function testDisabled() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'disabled' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@disabled="disabled"] +' + ); + } + + public function testInteger() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\IntegerType', 123); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="number"] + [@name="name"] + [@class="my&class form-control"] + [@value="123"] +' + ); + } + + public function testLanguage() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LanguageType', 'de'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [./option[@value="de"][@selected="selected"][.="German"]] + [count(./option)>200] +' + ); + } + + public function testLocale() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LocaleType', 'de_AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [./option[@value="de_AT"][@selected="selected"][.="German (Austria)"]] + [count(./option)>200] +' + ); + } + + public function testMoney() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\MoneyType', 1234.56, array( + 'currency' => 'EUR', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="input-group"] + [ + ./span + [@class="input-group-addon"] + [contains(.., "€")] + /following-sibling::input + [@id="my&id"] + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="1234.56"] + ] +' + ); + } + + public function testNumber() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\NumberType', 1234.56); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="1234.56"] +' + ); + } + + public function testPassword() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="password"] + [@name="name"] + [@class="my&class form-control"] +' + ); + } + + public function testPasswordSubmittedWithNotAlwaysEmpty() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', null, array( + 'always_empty' => false, + )); + $form->submit('foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="password"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] +' + ); + } + + public function testPasswordWithMaxLength() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', 'foo&bar', array( + 'attr' => array('maxlength' => 123), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="password"] + [@name="name"] + [@class="my&class form-control"] + [@maxlength="123"] +' + ); + } + + public function testPercent() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PercentType', 0.1); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="input-group"] + [ + ./input + [@id="my&id"] + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="10"] + /following-sibling::span + [@class="input-group-addon"] + [contains(.., "%")] + ] +' + ); + } + + public function testCheckedRadio() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', true); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="radio"] + [ + ./label + [@class="required"] + [ + ./input + [@id="my&id"] + [@type="radio"] + [@name="name"] + [@class="my&class"] + [@checked="checked"] + [@value="1"] + ] + ] +' + ); + } + + public function testUncheckedRadio() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="radio"] + [ + ./label + [@class="required"] + [ + ./input + [@id="my&id"] + [@type="radio"] + [@name="name"] + [@class="my&class"] + [not(@checked)] + ] + ] +' + ); + } + + public function testRadioWithValue() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')), +'/div + [@class="radio"] + [ + ./label + [@class="required"] + [ + ./input + [@id="my&id"] + [@type="radio"] + [@name="name"] + [@class="my&class"] + [@value="foo&bar"] + ] + ] +' + ); + } + + public function testRange() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RangeType', 42, array('attr' => array('min' => 5))); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@class="my&class form-control"] +' + ); + } + + public function testRangeWithMinMaxValues() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RangeType', 42, array('attr' => array('min' => 5, 'max' => 57))); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@max="57"] + [@class="my&class form-control"] +' + ); + } + + public function testTextarea() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextareaType', 'foo&bar', array( + 'attr' => array('pattern' => 'foo'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/textarea + [@name="name"] + [@pattern="foo"] + [@class="my&class form-control"] + [.="foo&bar"] +' + ); + } + + public function testText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testTextWithMaxLength() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'foo&bar', array( + 'attr' => array('maxlength' => 123), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="text"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testSearch() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SearchType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="search"] + [@name="name"] + [@class="my&class form-control"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testTime() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'with_seconds' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_hour"] + [@class="form-control"] + [not(@size)] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_minute"] + [@class="form-control"] + [not(@size)] + [./option[@value="5"][@selected="selected"]] + ] + [count(./select)=2] +' + ); + } + + public function testTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'with_seconds' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_hour"] + [@class="form-control"] + [not(@size)] + [./option[@value="4"][@selected="selected"]] + [count(./option)>23] + /following-sibling::select + [@id="name_minute"] + [@class="form-control"] + [not(@size)] + [./option[@value="5"][@selected="selected"]] + [count(./option)>59] + /following-sibling::select + [@id="name_second"] + [@class="form-control"] + [not(@size)] + [./option[@value="6"][@selected="selected"]] + [count(./option)>59] + ] + [count(./select)=3] +' + ); + } + + public function testTimeText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'widget' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./input + [@type="text"] + [@id="name_hour"] + [@name="name[hour]"] + [@class="form-control"] + [@value="04"] + [@required="required"] + [not(@size)] + /following-sibling::input + [@type="text"] + [@id="name_minute"] + [@name="name[minute]"] + [@class="form-control"] + [@value="05"] + [@required="required"] + [not(@size)] + ] + [count(./input)=2] +' + ); + } + + public function testTimeSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="time"] + [@name="name"] + [@class="my&class form-control"] + [@value="04:05"] + [not(@size)] +' + ); + } + + public function testTimeWithPlaceholderGlobal() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'input' => 'string', + 'placeholder' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_hour"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>24] + /following-sibling::select + [@id="name_minute"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>60] + ] + [count(./select)=2] +' + ); + } + + public function testTimeWithPlaceholderOnYear() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'input' => 'string', + 'required' => false, + 'placeholder' => array('hour' => 'Change&Me'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/div + [@class="my&class form-inline"] + [ + ./select + [@id="name_hour"] + [@class="form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>24] + /following-sibling::select + [@id="name_minute"] + [./option[@value="1"]] + [count(./option)>59] + ] + [count(./select)=2] +' + ); + } + + public function testTimezone() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimezoneType', 'Europe/Vienna'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [./optgroup + [@label="Europe"] + [./option[@value="Europe/Vienna"][@selected="selected"][.="Vienna"]] + ] + [count(./optgroup)>10] + [count(.//option)>200] +' + ); + } + + public function testTimezoneWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimezoneType', null, array( + 'placeholder' => 'Select&Timezone', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@class="my&class form-control"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Timezone[/trans]"]] + [count(./optgroup)>10] + [count(.//option)>201] +' + ); + } + + public function testUrl() + { + $url = 'http://www.google.com?foo1=bar1&foo2=bar2'; + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\UrlType', $url); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/input + [@type="url"] + [@name="name"] + [@class="my&class form-control"] + [@value="http://www.google.com?foo1=bar1&foo2=bar2"] +' + ); + } + + public function testButton() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), + '/button[@type="button"][@name="name"][.="[trans]Name[/trans]"][@class="my&class btn"]' + ); + } + + public function testButtonlabelWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), + '/button[@type="button"][@name="name"][.="Name"][@class="my&class btn"]' + ); + } + + public function testSubmit() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SubmitType'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), + '/button[@type="submit"][@name="name"][@class="my&class btn"]' + ); + } + + public function testReset() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ResetType'); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), + '/button[@type="reset"][@name="name"][@class="my&class btn"]' + ); + } + + public function testWidgetAttributes() + { + $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array( + 'required' => true, + 'disabled' => true, + 'attr' => array('readonly' => true, 'maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'), + )); + + $html = $this->renderWidget($form->createView()); + + // compare plain HTML to check the whitespace + $this->assertSame('', $html); + } + + public function testWidgetAttributeNameRepeatedIfTrue() + { + $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array( + 'attr' => array('foo' => true), + )); + + $html = $this->renderWidget($form->createView()); + + // foo="foo" + $this->assertSame('', $html); + } + + public function testButtonAttributes() + { + $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'disabled' => true, + 'attr' => array('class' => 'foobar', 'data-foo' => 'bar'), + )); + + $html = $this->renderWidget($form->createView()); + + // compare plain HTML to check the whitespace + $this->assertSame('', $html); + } + + public function testButtonAttributeNameRepeatedIfTrue() + { + $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'attr' => array('foo' => true), + )); + + $html = $this->renderWidget($form->createView()); + + // foo="foo" + $this->assertSame('', $html); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php new file mode 100644 index 0000000..b5f6017 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -0,0 +1,780 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Security\Csrf\CsrfToken; + +abstract class AbstractDivLayoutTest extends AbstractLayoutTest +{ + public function testRow() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name"] + /following-sibling::ul + [./li[.="[trans]Error![/trans]"]] + [count(./li)=1] + /following-sibling::input[@id="name"] + ] +' + ); + } + + public function testRowOverrideVariables() + { + $view = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType')->createView(); + $html = $this->renderRow($view, array( + 'attr' => array('class' => 'my&class'), + 'label' => 'foo&bar', + 'label_attr' => array('class' => 'my&label&class'), + )); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name"][@class="my&label&class required"][.="[trans]foo&bar[/trans]"] + /following-sibling::input[@id="name"][@class="my&class"] + ] +' + ); + } + + public function testRepeatedRow() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + // The errors of the form are not rendered by intention! + // In practice, repeated fields cannot have errors as all errors + // on them are mapped to the first child. + // (see RepeatedTypeValidatorExtension) + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@id="name_first"] + ] +/following-sibling::div + [ + ./label[@for="name_second"] + /following-sibling::input[@id="name_second"] + ] +' + ); + } + + public function testButtonRow() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType'); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./button[@type="button"][@name="name"] + ] + [count(//label)=0] +' + ); + } + + public function testRest() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType') + ->add('field3', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field4', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + // Render field2 row -> does not implicitly call renderWidget because + // it is a repeated field! + $this->renderRow($view['field2']); + + // Render field3 widget + $this->renderWidget($view['field3']); + + // Rest should only contain field1 and field4 + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_field1"] + /following-sibling::input[@type="text"][@id="name_field1"] + ] +/following-sibling::div + [ + ./label[@for="name_field4"] + /following-sibling::input[@type="text"][@id="name_field4"] + ] + [count(../div)=2] + [count(..//label)=2] + [count(..//input)=3] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testRestWithChildrenForms() + { + $child1 = $this->factory->createNamedBuilder('child1', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + + $child2 = $this->factory->createNamedBuilder('child2', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($child1) + ->add($child2) + ->getForm() + ->createView(); + + // Render child1.field1 row + $this->renderRow($view['child1']['field1']); + + // Render child2.field2 widget (remember that widget don't render label) + $this->renderWidget($view['child2']['field2']); + + // Rest should only contain child1.field2 and child2.field1 + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[not(@for)] + /following-sibling::div[@id="parent_child1"] + [ + ./div + [ + ./label[@for="parent_child1_field2"] + /following-sibling::input[@id="parent_child1_field2"] + ] + ] + ] + +/following-sibling::div + [ + ./label[not(@for)] + /following-sibling::div[@id="parent_child2"] + [ + ./div + [ + ./label[@for="parent_child2_field1"] + /following-sibling::input[@id="parent_child2_field1"] + ] + ] + ] + [count(//label)=4] + [count(//input[@type="text"])=2] +/following-sibling::input[@type="hidden"][@id="parent__token"] +' + ); + } + + public function testRestAndRepeatedWithRow() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('first', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('password', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType') + ->getForm() + ->createView(); + + $this->renderRow($view['password']); + + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + [count(.//input)=1] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testRestAndRepeatedWithRowPerChild() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('first', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('password', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType') + ->getForm() + ->createView(); + + $this->renderRow($view['password']['first']); + $this->renderRow($view['password']['second']); + + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + [count(.//input)=1] + [count(.//label)=1] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testRestAndRepeatedWithWidgetPerChild() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('first', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('password', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType') + ->getForm() + ->createView(); + + // The password form is considered as rendered as all its children + // are rendered + $this->renderWidget($view['password']['first']); + $this->renderWidget($view['password']['second']); + + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + [count(//input)=2] + [count(//label)=1] +/following-sibling::input + [@type="hidden"] + [@id="name__token"] +' + ); + } + + public function testCollection() + { + $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array('a', 'b'), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./input[@type="text"][@value="a"]] + /following-sibling::div[./input[@type="text"][@value="b"]] + ] + [count(./div[./input])=2] +' + ); + } + + // https://github.com/symfony/symfony/issues/5038 + public function testCollectionWithAlternatingRowTypes() + { + $data = array( + array('title' => 'a'), + array('title' => 'b'), + ); + $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', $data, array( + 'entry_type' => 'Symfony\Component\Form\Tests\Fixtures\AlternatingRowType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./div/div/input[@type="text"][@value="a"]] + /following-sibling::div[./div/div/textarea[.="b"]] + ] + [count(./div[./div/div/input])=1] + [count(./div[./div/div/textarea])=1] +' + ); + } + + public function testEmptyCollection() + { + $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [./input[@type="hidden"][@id="names__token"]] + [count(./div)=0] +' + ); + } + + public function testCollectionRow() + { + $collection = $this->factory->createNamedBuilder( + 'collection', + 'Symfony\Component\Form\Extension\Core\Type\CollectionType', + array('a', 'b'), + array('entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType') + ); + + $form = $this->factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[not(@for)] + /following-sibling::div + [ + ./div + [ + ./label[@for="form_collection_0"] + /following-sibling::input[@type="text"][@value="a"] + ] + /following-sibling::div + [ + ./label[@for="form_collection_1"] + /following-sibling::input[@type="text"][@value="b"] + ] + ] + ] + /following-sibling::input[@type="hidden"][@id="form__token"] + ] + [count(.//input)=3] +' + ); + } + + public function testForm() + { + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->setMethod('PUT') + ->setAction('http://example.com') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + // include ampersands everywhere to validate escaping + $html = $this->renderForm($form->createView(), array( + 'id' => 'my&id', + 'attr' => array('class' => 'my&class'), + )); + + $this->assertMatchesXpath($html, +'/form + [ + ./input[@type="hidden"][@name="_method"][@value="PUT"] + /following-sibling::div + [ + ./div + [ + ./label[@for="name_firstName"] + /following-sibling::input[@type="text"][@id="name_firstName"] + ] + /following-sibling::div + [ + ./label[@for="name_lastName"] + /following-sibling::input[@type="text"][@id="name_lastName"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] + [@id="my&id"] + [@class="my&class"] + ] + [@method="post"] + [@action="http://example.com"] + [@class="my&class"] +' + ); + } + + public function testFormWidget() + { + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="name_firstName"] + /following-sibling::input[@type="text"][@id="name_firstName"] + ] + /following-sibling::div + [ + ./label[@for="name_lastName"] + /following-sibling::input[@type="text"][@id="name_lastName"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] +' + ); + } + + // https://github.com/symfony/symfony/issues/2308 + public function testNestedFormError() + { + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory + ->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array('error_bubbling' => false)) + ->add('grandChild', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ) + ->getForm(); + + $form->get('child')->addError(new FormError('[trans]Error![/trans]')); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div/label + /following-sibling::ul[./li[.="[trans]Error![/trans]"]] + ] + [count(.//li[.="[trans]Error![/trans]"])=1] +' + ); + } + + public function testCsrf() + { + $this->csrfTokenManager->expects($this->any()) + ->method('getToken') + ->will($this->returnValue(new CsrfToken('token_id', 'foo&bar'))); + + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory->createNamedBuilder('grandchild', 'Symfony\Component\Form\Extension\Core\Type\TextType')) + ) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + /following-sibling::input[@type="hidden"][@id="name__token"][@value="foo&bar"] + ] + [count(.//input[@type="hidden"])=1] +' + ); + } + + public function testRepeated() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType', 'foobar', array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="name_first"] + /following-sibling::input[@type="text"][@id="name_first"] + ] + /following-sibling::div + [ + ./label[@for="name_second"] + /following-sibling::input[@type="text"][@id="name_second"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] +' + ); + } + + public function testRepeatedWithCustomOptions() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + // the global required value cannot be overridden + 'first_options' => array('label' => 'Test', 'required' => false), + 'second_options' => array('label' => 'Test2'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="name_first"][.="[trans]Test[/trans]"] + /following-sibling::input[@type="text"][@id="name_first"][@required="required"] + ] + /following-sibling::div + [ + ./label[@for="name_second"][.="[trans]Test2[/trans]"] + /following-sibling::input[@type="text"][@id="name_second"][@required="required"] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(.//input)=3] +' + ); + } + + public function testSearchInputName() + { + $form = $this->factory->createNamedBuilder('full', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('name', 'Symfony\Component\Form\Extension\Core\Type\SearchType') + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [ + ./label[@for="full_name"] + /following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"] + ] + /following-sibling::input[@type="hidden"][@id="full__token"] + ] + [count(//input)=2] +' + ); + } + + public function testLabelHasNoId() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./label[@for="name"][not(@id)] + /following-sibling::input[@id="name"] + ] +' + ); + } + + public function testLabelIsNotRenderedWhenSetToFalse() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => false, + )); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./input[@id="name"] + ] + [count(//label)=0] +' + ); + } + + /** + * @dataProvider themeBlockInheritanceProvider + */ + public function testThemeBlockInheritance($theme) + { + $view = $this->factory + ->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType') + ->createView() + ; + + $this->setTheme($view, $theme); + + $this->assertMatchesXpath( + $this->renderWidget($view), + '/input[@type="email"][@rel="theme"]' + ); + } + + /** + * @dataProvider themeInheritanceProvider + */ + public function testThemeInheritance($parentTheme, $childTheme) + { + $child = $this->factory->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add($child) + ->getForm() + ->createView() + ; + + $this->setTheme($view, $parentTheme); + $this->setTheme($view['child'], $childTheme); + + $this->assertWidgetMatchesXpath($view, array(), +'/div + [ + ./div + [ + ./label[.="parent"] + /following-sibling::input[@type="text"] + ] + /following-sibling::div + [ + ./label[.="child"] + /following-sibling::div + [ + ./div + [ + ./label[.="child"] + /following-sibling::input[@type="text"] + ] + ] + ] + /following-sibling::input[@type="hidden"] + ] +' + ); + } + + /** + * The block "_name_child_label" should be overridden in the theme of the + * implemented driver. + */ + public function testCollectionRowWithCustomBlock() + { + $collection = array('one', 'two', 'three'); + $form = $this->factory->createNamedBuilder('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', $collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./label[.="Custom label: [trans]0[/trans]"]] + /following-sibling::div[./label[.="Custom label: [trans]1[/trans]"]] + /following-sibling::div[./label[.="Custom label: [trans]2[/trans]"]] + ] +' + ); + } + + /** + * The block "_name_c_entry_label" should be overridden in the theme of the + * implemented driver. + */ + public function testChoiceRowWithCustomBlock() + { + $form = $this->factory->createNamedBuilder('name_c', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', 'a', array( + 'choices' => array('ChoiceA' => 'a', 'ChoiceB' => 'b'), + 'expanded' => true, + )) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./label[.="Custom name label: [trans]ChoiceA[/trans]"] + /following-sibling::label[.="Custom name label: [trans]ChoiceB[/trans]"] + ] +' + ); + } + + public function testFormEndWithRest() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2 + $html = $this->renderEnd($view); + + // Insert the start tag, the end tag should be rendered by the helper + $this->assertMatchesXpath(''.$html, +'/form + [ + ./div + [ + ./label[@for="name_field2"] + /following-sibling::input[@type="text"][@id="name_field2"] + ] + /following-sibling::input + [@type="hidden"] + [@id="name__token"] + ] +' + ); + } + + public function testFormEndWithoutRest() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2, but isn't rendered + $html = $this->renderEnd($view, array('render_rest' => false)); + + $this->assertEquals('', $html); + } + + public function testWidgetContainerAttributes() + { + $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'attr' => array('class' => 'foobar', 'data-foo' => 'bar'), + )); + + $form->add('text', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + + $html = $this->renderWidget($form->createView()); + + // compare plain HTML to check the whitespace + $this->assertContains('
    ', $html); + } + + public function testWidgetContainerAttributeNameRepeatedIfTrue() + { + $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'attr' => array('foo' => true), + )); + + $html = $this->renderWidget($form->createView()); + + // foo="foo" + $this->assertContains('
    ', $html); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractExtensionTest.php new file mode 100644 index 0000000..54c3fb5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractExtensionTest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\AbstractExtension; +use Symfony\Component\Form\Tests\Fixtures\FooType; + +class AbstractExtensionTest extends \PHPUnit_Framework_TestCase +{ + public function testHasType() + { + $loader = new ConcreteExtension(); + $this->assertTrue($loader->hasType('Symfony\Component\Form\Tests\Fixtures\FooType')); + $this->assertFalse($loader->hasType('foo')); + } + + public function testGetType() + { + $loader = new ConcreteExtension(); + $this->assertInstanceOf('Symfony\Component\Form\Tests\Fixtures\FooType', $loader->getType('Symfony\Component\Form\Tests\Fixtures\FooType')); + } +} + +class ConcreteExtension extends AbstractExtension +{ + protected function loadTypes() + { + return array(new FooType()); + } + + protected function loadTypeGuesser() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractFormTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractFormTest.php new file mode 100644 index 0000000..dec9df7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractFormTest.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +abstract class AbstractFormTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var EventDispatcherInterface + */ + protected $dispatcher; + + /** + * @var \Symfony\Component\Form\FormFactoryInterface + */ + protected $factory; + + /** + * @var \Symfony\Component\Form\FormInterface + */ + protected $form; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->form = $this->createForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->form = null; + } + + /** + * @return \Symfony\Component\Form\FormInterface + */ + abstract protected function createForm(); + + /** + * @param string $name + * @param EventDispatcherInterface $dispatcher + * @param string $dataClass + * @param array $options + * + * @return FormBuilder + */ + protected function getBuilder($name = 'name', EventDispatcherInterface $dispatcher = null, $dataClass = null, array $options = array()) + { + return new FormBuilder($name, $dataClass, $dispatcher ?: $this->dispatcher, $this->factory, $options); + } + + /** + * @param string $name + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getMockForm($name = 'name') + { + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $config = $this->getMock('Symfony\Component\Form\FormConfigInterface'); + + $form->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $form->expects($this->any()) + ->method('getConfig') + ->will($this->returnValue($config)); + + return $form; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getDataTransformer() + { + return $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getFormValidator() + { + return $this->getMock('Symfony\Component\Form\FormValidatorInterface'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php new file mode 100644 index 0000000..7bade49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -0,0 +1,2394 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + +abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormIntegrationTestCase +{ + protected $csrfTokenManager; + protected $testableFeatures = array(); + + protected function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('Extension intl is required.'); + } + + \Locale::setDefault('en'); + + $this->csrfTokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'); + + parent::setUp(); + } + + protected function getExtensions() + { + return array( + new CsrfExtension($this->csrfTokenManager), + ); + } + + protected function tearDown() + { + $this->csrfTokenManager = null; + + parent::tearDown(); + } + + protected function assertXpathNodeValue(\DomElement $element, $expression, $nodeValue) + { + $xpath = new \DOMXPath($element->ownerDocument); + $nodeList = $xpath->evaluate($expression); + $this->assertEquals(1, $nodeList->length); + $this->assertEquals($nodeValue, $nodeList->item(0)->nodeValue); + } + + protected function assertMatchesXpath($html, $expression, $count = 1) + { + $dom = new \DomDocument('UTF-8'); + try { + // Wrap in node so we can load HTML with multiple tags at + // the top level + $dom->loadXML(''.$html.''); + } catch (\Exception $e) { + $this->fail(sprintf( + "Failed loading HTML:\n\n%s\n\nError: %s", + $html, + $e->getMessage() + )); + } + $xpath = new \DOMXPath($dom); + $nodeList = $xpath->evaluate('/root'.$expression); + + if ($nodeList->length != $count) { + $dom->formatOutput = true; + $this->fail(sprintf( + "Failed asserting that \n\n%s\n\nmatches exactly %s. Matches %s in \n\n%s", + $expression, + $count == 1 ? 'once' : $count.' times', + $nodeList->length == 1 ? 'once' : $nodeList->length.' times', + // strip away and + substr($dom->saveHTML(), 6, -8) + )); + } + } + + protected function assertWidgetMatchesXpath(FormView $view, array $vars, $xpath) + { + // include ampersands everywhere to validate escaping + $html = $this->renderWidget($view, array_merge(array( + 'id' => 'my&id', + 'attr' => array('class' => 'my&class'), + ), $vars)); + + if (!isset($vars['id'])) { + $xpath = trim($xpath).' + [@id="my&id"]'; + } + + if (!isset($vars['attr']['class'])) { + $xpath .= ' + [@class="my&class"]'; + } + + $this->assertMatchesXpath($html, $xpath); + } + + abstract protected function renderForm(FormView $view, array $vars = array()); + + abstract protected function renderLabel(FormView $view, $label = null, array $vars = array()); + + abstract protected function renderErrors(FormView $view); + + abstract protected function renderWidget(FormView $view, array $vars = array()); + + abstract protected function renderRow(FormView $view, array $vars = array()); + + abstract protected function renderRest(FormView $view, array $vars = array()); + + abstract protected function renderStart(FormView $view, array $vars = array()); + + abstract protected function renderEnd(FormView $view, array $vars = array()); + + abstract protected function setTheme(FormView $view, array $themes); + + public function testLabel() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'translation_domain' => false, + )); + + $this->assertMatchesXpath($this->renderLabel($form->createView()), +'/label + [@for="name"] + [.="Name"] +' + ); + } + + public function testLabelOnForm() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType'); + $view = $form->createView(); + $this->renderWidget($view, array('label' => 'foo')); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@class="required"] + [.="[trans]Name[/trans]"] +' + ); + } + + public function testLabelWithCustomTextPassedAsOption() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView()); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), 'Custom label'); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelWithCustomTextPassedAsOptionAndDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), 'Overridden label'); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [.="[trans]Overridden label[/trans]"] +' + ); + } + + public function testLabelDoesNotRenderFieldAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="required"] +' + ); + } + + public function testLabelWithCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class required"] +' + ); + } + + public function testLabelWithCustomTextAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderLabel($form->createView(), 'Custom label', array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, +'/label + [@for="name"] + [@class="my&class required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + // https://github.com/symfony/symfony/issues/5029 + public function testLabelWithCustomTextAsOptionAndCustomAttributesPassedDirectly() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => 'Custom label', + )); + $html = $this->renderLabel($form->createView(), null, array( + 'label_attr' => array( + 'class' => 'my&class', + ), + )); + + $this->assertMatchesXpath($html, + '/label + [@for="name"] + [@class="my&class required"] + [.="[trans]Custom label[/trans]"] +' + ); + } + + public function testLabelFormatName() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view, null, array('label_format' => 'form.%name%')); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]form.myfield[/trans]"] +' + ); + } + + public function testLabelFormatId() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view, null, array('label_format' => 'form.%id%')); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]form.myform_myfield[/trans]"] +' + ); + } + + public function testLabelFormatAsFormOption() + { + $options = array('label_format' => 'form.%name%'); + + $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, $options) + ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]form.myfield[/trans]"] +' + ); + } + + public function testLabelFormatOverriddenOption() + { + $options = array('label_format' => 'form.%name%'); + + $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, $options) + ->add('myfield', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('label_format' => 'field.%name%')) + ->getForm(); + $view = $form->get('myfield')->createView(); + $html = $this->renderLabel($view); + + $this->assertMatchesXpath($html, +'/label + [@for="myform_myfield"] + [.="[trans]field.myfield[/trans]"] +' + ); + } + + public function testLabelWithoutTranslationOnButton() + { + $form = $this->factory->createNamedBuilder('myform', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => false, + )) + ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType') + ->getForm(); + $view = $form->get('mybutton')->createView(); + $html = $this->renderWidget($view); + + $this->assertMatchesXpath($html, +'/button + [@type="button"] + [@name="myform[mybutton]"] + [.="Mybutton"] +' + ); + } + + public function testLabelFormatOnButton() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType') + ->getForm(); + $view = $form->get('mybutton')->createView(); + $html = $this->renderWidget($view, array('label_format' => 'form.%name%')); + + $this->assertMatchesXpath($html, +'/button + [@type="button"] + [@name="myform[mybutton]"] + [.="[trans]form.mybutton[/trans]"] +' + ); + } + + public function testLabelFormatOnButtonId() + { + $form = $this->factory->createNamedBuilder('myform') + ->add('mybutton', 'Symfony\Component\Form\Extension\Core\Type\ButtonType') + ->getForm(); + $view = $form->get('mybutton')->createView(); + $html = $this->renderWidget($view, array('label_format' => 'form.%id%')); + + $this->assertMatchesXpath($html, +'/button + [@type="button"] + [@name="myform[mybutton]"] + [.="[trans]form.myform_mybutton[/trans]"] +' + ); + } + + public function testErrors() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form->addError(new FormError('[trans]Error 1[/trans]')); + $form->addError(new FormError('[trans]Error 2[/trans]')); + $view = $form->createView(); + $html = $this->renderErrors($view); + + $this->assertMatchesXpath($html, +'/ul + [ + ./li[.="[trans]Error 1[/trans]"] + /following-sibling::li[.="[trans]Error 2[/trans]"] + ] + [count(./li)=2] +' + ); + } + + public function testOverrideWidgetBlock() + { + // see custom_widgets.html.twig + $form = $this->factory->createNamed('text_id', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $html = $this->renderWidget($form->createView()); + + $this->assertMatchesXpath($html, +'/div + [ + ./input + [@type="text"] + [@id="text_id"] + ] + [@id="container"] +' + ); + } + + public function testCheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', true); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [@checked="checked"] + [@value="1"] +' + ); + } + + public function testUncheckedCheckbox() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [not(@checked)] +' + ); + } + + public function testCheckboxWithValue() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="checkbox"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testSingleChoice() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + )); + + // If the field is collapsed, has no "multiple" attribute, is required but + // has *no* empty value, the "required" must not be added, otherwise + // the resulting HTML is invalid. + // https://github.com/symfony/symfony/issues/8942 + + // HTML 5 spec + // http://www.w3.org/html/wg/drafts/html/master/forms.html#placeholder-label-option + + // "If a select element has a required attribute specified, does not + // have a multiple attribute specified, and has a display size of 1, + // then the select element must have a placeholder label option." + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + 'choice_translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="Choice&A"] + /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="Placeholder&Not&Translated"] + /following-sibling::option[@value="&a"][@selected="selected"][.="Choice&A"] + /following-sibling::option[@value="&b"][not(@selected)][.="Choice&B"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPreferred() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => '-- sep --'), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceWithPreferredAndNoSeparator() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => null), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceWithPreferredAndBlankSeparator() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('separator' => ''), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@disabled="disabled"][not(@selected)][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testChoiceWithOnlyPreferred() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'preferred_choices' => array('&a', '&b'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [count(./option)=2] +' + ); + } + + public function testSingleChoiceNonRequired() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceNonRequiredNoneSelected() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => false, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][.=""] + /following-sibling::option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceNonRequiredWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => false, + 'required' => false, + 'placeholder' => 'Select&Anything&Not&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Anything&Not&Me[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceRequiredWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + 'placeholder' => 'Test&Me', + )); + + // The "disabled" attribute was removed again due to a bug in the + // BlackBerry 10 browser. + // See https://github.com/symfony/symfony/pull/7678 + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [@required="required"] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Test&Me[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceRequiredWithPlaceholderViaView() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => true, + 'multiple' => false, + 'expanded' => false, + )); + + // The "disabled" attribute was removed again due to a bug in the + // BlackBerry 10 browser. + // See https://github.com/symfony/symfony/pull/7678 + $this->assertWidgetMatchesXpath($form->createView(), array('placeholder' => ''), +'/select + [@name="name"] + [@required="required"] + [ + ./option[@value=""][not(@selected)][not(@disabled)][.=""] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=3] +' + ); + } + + public function testSingleChoiceGrouped() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array( + 'Group&1' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'Group&2' => array('Choice&C' => '&c'), + ), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./optgroup[@label="[trans]Group&1[/trans]"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] + ] + [./optgroup[@label="[trans]Group&2[/trans]"] + [./option[@value="&c"][not(@selected)][.="[trans]Choice&C[/trans]"]] + [count(./option)=1] + ] + [count(./optgroup)=2] +' + ); + } + + public function testMultipleChoice() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceSkipsPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => true, + 'expanded' => false, + 'placeholder' => 'Test&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceNonRequired() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'required' => false, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceExpanded() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testSingleChoiceExpandedWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + 'choice_translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label[@for="name_0"][.="Choice&A"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label[@for="name_1"][.="Choice&B"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testSingleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][@class="foo&bar"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testSingleChoiceExpandedWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + 'placeholder' => 'Test&Me', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + /following-sibling::label[@for="name_placeholder"][.="[trans]Test&Me[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testSingleChoiceExpandedWithPlaceholderWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'multiple' => false, + 'expanded' => true, + 'translation_domain' => false, + 'placeholder' => 'Placeholder&Not&Translated', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_placeholder"][not(@checked)] + /following-sibling::label[@for="name_placeholder"][.="Placeholder&Not&Translated"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label[@for="name_0"][.="Choice&A"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label[@for="name_1"][.="Choice&B"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testSingleChoiceExpandedWithBooleanValue() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', true, array( + 'choices' => array('Choice&A' => '1', 'Choice&B' => '0'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testMultipleChoiceExpanded() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testMultipleChoiceExpandedWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + 'choice_translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][.="Choice&A"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"][.="Choice&B"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][.="Choice&C"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testMultipleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_attr' => array('Choice&B' => array('class' => 'foo&bar')), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@class="foo&bar"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testCountry() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="AT"][@selected="selected"][.="Austria"]] + [count(./option)>200] +' + ); + } + + public function testCountryWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT', array( + 'placeholder' => 'Select&Country', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Country[/trans]"]] + [./option[@value="AT"][@selected="selected"][.="Austria"]] + [count(./option)>201] +' + ); + } + + public function testDateTime() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'with_seconds' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value="2011"][@selected="selected"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value="5"][@selected="selected"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithPlaceholderGlobal() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'input' => 'string', + 'placeholder' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value=""][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value=""][.="[trans]Change&Me[/trans]"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithHourAndMinute() + { + $data = array('year' => '2011', 'month' => '2', 'day' => '3', 'hour' => '4', 'minute' => '5'); + + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', $data, array( + 'input' => 'array', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value="2011"][@selected="selected"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value="5"][@selected="selected"]] + ] + ] + [count(.//select)=5] +' + ); + } + + public function testDateTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'with_seconds' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@id="name_date"] + [ + ./select + [@id="name_date_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_date_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_date_year"] + [./option[@value="2011"][@selected="selected"]] + ] + /following-sibling::div + [@id="name_time"] + [ + ./select + [@id="name_time_hour"] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_time_minute"] + [./option[@value="5"][@selected="selected"]] + /following-sibling::select + [@id="name_time_second"] + [./option[@value="6"][@selected="selected"]] + ] + ] + [count(.//select)=6] +' + ); + } + + public function testDateTimeSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'date_widget' => 'single_text', + 'time_widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input + [@type="date"] + [@id="name_date"] + [@name="name[date]"] + [@value="2011-02-03"] + /following-sibling::input + [@type="time"] + [@id="name_time"] + [@name="name[time]"] + [@value="04:05"] + ] +' + ); + } + + public function testDateTimeWithWidgetSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="datetime"] + [@name="name"] + [@value="2011-02-03T04:05:06Z"] +' + ); + } + + public function testDateTimeWithWidgetSingleTextIgnoreDateAndTimeWidgets() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateTimeType', '2011-02-03 04:05:06', array( + 'input' => 'string', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'widget' => 'single_text', + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="datetime"] + [@name="name"] + [@value="2011-02-03T04:05:06Z"] +' + ); + } + + public function testDateChoice() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'choice', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value="2011"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateChoiceWithPlaceholderGlobal() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'placeholder' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateChoiceWithPlaceholderOnYear() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'input' => 'string', + 'widget' => 'choice', + 'required' => false, + 'placeholder' => array('year' => 'Change&Me'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value="1"]] + /following-sibling::select + [@id="name_day"] + [./option[@value="1"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + ] + [count(./select)=3] +' + ); + } + + public function testDateText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input + [@id="name_month"] + [@type="text"] + [@value="2"] + /following-sibling::input + [@id="name_day"] + [@type="text"] + [@value="3"] + /following-sibling::input + [@id="name_year"] + [@type="text"] + [@value="2011"] + ] + [count(./input)=3] +' + ); + } + + public function testDateSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\DateType', '2011-02-03', array( + 'input' => 'string', + 'widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="date"] + [@name="name"] + [@value="2011-02-03"] +' + ); + } + + public function testDateErrorBubbling() + { + $form = $this->factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('date', 'Symfony\Component\Form\Extension\Core\Type\DateType') + ->getForm(); + $form->get('date')->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + + $this->assertEmpty($this->renderErrors($view)); + $this->assertNotEmpty($this->renderErrors($view['date'])); + } + + public function testBirthDay() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType', '2000-02-03', array( + 'input' => 'string', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value="2"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value="3"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value="2000"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testBirthDayWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType', '1950-01-01', array( + 'input' => 'string', + 'placeholder' => '', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_month"] + [./option[@value=""][not(@selected)][not(@disabled)][.=""]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_day"] + [./option[@value=""][not(@selected)][not(@disabled)][.=""]] + [./option[@value="1"][@selected="selected"]] + /following-sibling::select + [@id="name_year"] + [./option[@value=""][not(@selected)][not(@disabled)][.=""]] + [./option[@value="1950"][@selected="selected"]] + ] + [count(./select)=3] +' + ); + } + + public function testEmail() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="email"] + [@name="name"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testEmailWithMaxLength() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\EmailType', 'foo&bar', array( + 'attr' => array('maxlength' => 123), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="email"] + [@name="name"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testFile() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\FileType'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="file"] +' + ); + } + + public function testHidden() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\HiddenType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="hidden"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testDisabled() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'disabled' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@disabled="disabled"] +' + ); + } + + public function testInteger() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\IntegerType', 123); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="number"] + [@name="name"] + [@value="123"] +' + ); + } + + public function testLanguage() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LanguageType', 'de'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="de"][@selected="selected"][.="German"]] + [count(./option)>200] +' + ); + } + + public function testLocale() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\LocaleType', 'de_AT'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [./option[@value="de_AT"][@selected="selected"][.="German (Austria)"]] + [count(./option)>200] +' + ); + } + + public function testMoney() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\MoneyType', 1234.56, array( + 'currency' => 'EUR', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="1234.56"] + [contains(.., "€")] +' + ); + } + + public function testNumber() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\NumberType', 1234.56); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="1234.56"] +' + ); + } + + public function testPassword() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] +' + ); + } + + public function testPasswordSubmittedWithNotAlwaysEmpty() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', null, array( + 'always_empty' => false, + )); + $form->submit('foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testPasswordWithMaxLength() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', 'foo&bar', array( + 'attr' => array('maxlength' => 123), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="password"] + [@name="name"] + [@maxlength="123"] +' + ); + } + + public function testPercent() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\PercentType', 0.1); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="10"] + [contains(.., "%")] +' + ); + } + + public function testCheckedRadio() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', true); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [@checked="checked"] + [@value="1"] +' + ); + } + + public function testUncheckedRadio() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [not(@checked)] +' + ); + } + + public function testRadioWithValue() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RadioType', false, array( + 'value' => 'foo&bar', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="radio"] + [@name="name"] + [@value="foo&bar"] +' + ); + } + + public function testRange() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RangeType', 42, array('attr' => array('min' => 5))); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] +' + ); + } + + public function testRangeWithMinMaxValues() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RangeType', 42, array('attr' => array('min' => 5, 'max' => 57))); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="range"] + [@name="name"] + [@value="42"] + [@min="5"] + [@max="57"] +' + ); + } + + public function testTextarea() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextareaType', 'foo&bar', array( + 'attr' => array('pattern' => 'foo'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/textarea + [@name="name"] + [@pattern="foo"] + [.="foo&bar"] +' + ); + } + + public function testText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testTextWithMaxLength() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'foo&bar', array( + 'attr' => array('maxlength' => 123), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="text"] + [@name="name"] + [@value="foo&bar"] + [@maxlength="123"] +' + ); + } + + public function testSearch() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SearchType', 'foo&bar'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="search"] + [@name="name"] + [@value="foo&bar"] + [not(@maxlength)] +' + ); + } + + public function testTime() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'with_seconds' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [not(@size)] + [./option[@value="4"][@selected="selected"]] + /following-sibling::select + [@id="name_minute"] + [not(@size)] + [./option[@value="5"][@selected="selected"]] + ] + [count(./select)=2] +' + ); + } + + public function testTimeWithSeconds() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'with_seconds' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [not(@size)] + [./option[@value="4"][@selected="selected"]] + [count(./option)>23] + /following-sibling::select + [@id="name_minute"] + [not(@size)] + [./option[@value="5"][@selected="selected"]] + [count(./option)>59] + /following-sibling::select + [@id="name_second"] + [not(@size)] + [./option[@value="6"][@selected="selected"]] + [count(./option)>59] + ] + [count(./select)=3] +' + ); + } + + public function testTimeText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'widget' => 'text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input + [@type="text"] + [@id="name_hour"] + [@name="name[hour]"] + [@value="04"] + [@size="1"] + [@required="required"] + /following-sibling::input + [@type="text"] + [@id="name_minute"] + [@name="name[minute]"] + [@value="05"] + [@size="1"] + [@required="required"] + ] + [count(./input)=2] +' + ); + } + + public function testTimeSingleText() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', '04:05:06', array( + 'input' => 'string', + 'widget' => 'single_text', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="time"] + [@name="name"] + [@value="04:05"] + [not(@size)] +' + ); + } + + public function testTimeWithPlaceholderGlobal() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'input' => 'string', + 'placeholder' => 'Change&Me', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>24] + /following-sibling::select + [@id="name_minute"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>60] + ] + [count(./select)=2] +' + ); + } + + public function testTimeWithPlaceholderOnYear() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'input' => 'string', + 'required' => false, + 'placeholder' => array('hour' => 'Change&Me'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./select + [@id="name_hour"] + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Change&Me[/trans]"]] + [count(./option)>24] + /following-sibling::select + [@id="name_minute"] + [./option[@value="1"]] + [count(./option)>59] + ] + [count(./select)=2] +' + ); + } + + public function testTimeErrorBubbling() + { + $form = $this->factory->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('time', 'Symfony\Component\Form\Extension\Core\Type\TimeType') + ->getForm(); + $form->get('time')->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + + $this->assertEmpty($this->renderErrors($view)); + $this->assertNotEmpty($this->renderErrors($view['time'])); + } + + public function testTimezone() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimezoneType', 'Europe/Vienna'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [./optgroup + [@label="Europe"] + [./option[@value="Europe/Vienna"][@selected="selected"][.="Vienna"]] + ] + [count(./optgroup)>10] + [count(.//option)>200] +' + ); + } + + public function testTimezoneWithPlaceholder() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TimezoneType', null, array( + 'placeholder' => 'Select&Timezone', + 'required' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [./option[@value=""][not(@selected)][not(@disabled)][.="[trans]Select&Timezone[/trans]"]] + [count(./optgroup)>10] + [count(.//option)>201] +' + ); + } + + public function testUrl() + { + $url = 'http://www.google.com?foo1=bar1&foo2=bar2'; + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\UrlType', $url); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="url"] + [@name="name"] + [@value="http://www.google.com?foo1=bar1&foo2=bar2"] +' + ); + } + + public function testCollectionPrototype() + { + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType', array('items' => array('one', 'two', 'three'))) + ->add('items', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array('allow_add' => true)) + ->getForm() + ->createView(); + + $html = $this->renderWidget($form); + + $this->assertMatchesXpath($html, + '//div[@id="name_items"][@data-prototype] + | + //table[@id="name_items"][@data-prototype]' + ); + } + + public function testEmptyRootFormName() + { + $form = $this->factory->createNamedBuilder('', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $this->assertMatchesXpath($this->renderWidget($form->createView()), + '//input[@type="hidden"][@id="_token"][@name="_token"] + | + //input[@type="text"][@id="child"][@name="child"]', 2); + } + + public function testButton() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="button"][@name="name"][.="[trans]Name[/trans]"]' + ); + } + + public function testButtonLabelIsEmpty() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType'); + + $this->assertSame('', $this->renderLabel($form->createView())); + } + + public function testButtonlabelWithoutTranslation() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'translation_domain' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="button"][@name="name"][.="Name"]' + ); + } + + public function testSubmit() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\SubmitType'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="submit"][@name="name"]' + ); + } + + public function testReset() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ResetType'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), + '/button[@type="reset"][@name="name"]' + ); + } + + public function testStartTag() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('
    ', $html); + } + + public function testStartTagForPutRequest() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'put', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertMatchesXpath($html.'', +'/form + [./input[@type="hidden"][@name="_method"][@value="PUT"]] + [@method="post"] + [@action="http://example.com/directory"]' + ); + } + + public function testStartTagWithOverriddenVars() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'put', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'method' => 'post', + 'action' => 'http://foo.com/directory', + )); + + $this->assertSame('
    ', $html); + } + + public function testStartTagForMultipartForm() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )) + ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType') + ->getForm(); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagWithExtraAttributes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => 'http://example.com/directory', + )); + + $html = $this->renderStart($form->createView(), array( + 'attr' => array('class' => 'foobar'), + )); + + $this->assertSame('', $html); + } + + public function testWidgetAttributes() + { + $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array( + 'required' => true, + 'disabled' => true, + 'attr' => array('readonly' => true, 'maxlength' => 10, 'pattern' => '\d+', 'class' => 'foobar', 'data-foo' => 'bar'), + )); + + $html = $this->renderWidget($form->createView()); + + // compare plain HTML to check the whitespace + $this->assertSame('', $html); + } + + public function testWidgetAttributeNameRepeatedIfTrue() + { + $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array( + 'attr' => array('foo' => true), + )); + + $html = $this->renderWidget($form->createView()); + + // foo="foo" + $this->assertSame('', $html); + } + + public function testWidgetAttributeHiddenIfFalse() + { + $form = $this->factory->createNamed('text', 'Symfony\Component\Form\Extension\Core\Type\TextType', 'value', array( + 'attr' => array('foo' => false), + )); + + $html = $this->renderWidget($form->createView()); + + $this->assertNotContains('foo="', $html); + } + + public function testButtonAttributes() + { + $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'disabled' => true, + 'attr' => array('class' => 'foobar', 'data-foo' => 'bar'), + )); + + $html = $this->renderWidget($form->createView()); + + // compare plain HTML to check the whitespace + $this->assertSame('', $html); + } + + public function testButtonAttributeNameRepeatedIfTrue() + { + $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'attr' => array('foo' => true), + )); + + $html = $this->renderWidget($form->createView()); + + // foo="foo" + $this->assertSame('', $html); + } + + public function testButtonAttributeHiddenIfFalse() + { + $form = $this->factory->createNamed('button', 'Symfony\Component\Form\Extension\Core\Type\ButtonType', null, array( + 'attr' => array('foo' => false), + )); + + $html = $this->renderWidget($form->createView()); + + $this->assertNotContains('foo="', $html); + } + + public function testTextareaWithWhitespaceOnlyContentRetainsValue() + { + $form = $this->factory->createNamed('textarea', 'Symfony\Component\Form\Extension\Core\Type\TextareaType', ' '); + + $html = $this->renderWidget($form->createView()); + + $this->assertContains('> ', $html); + } + + public function testTextareaWithWhitespaceOnlyContentRetainsValueWhenRenderingForm() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array('textarea' => ' ')) + ->add('textarea', 'Symfony\Component\Form\Extension\Core\Type\TextareaType') + ->getForm(); + + $html = $this->renderForm($form->createView()); + + $this->assertContains('> ', $html); + } + + public function testWidgetContainerAttributeHiddenIfFalse() + { + $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'attr' => array('foo' => false), + )); + + $html = $this->renderWidget($form->createView()); + + // no foo + $this->assertNotContains('foo="', $html); + } + + public function testTranslatedAttributes() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('title' => 'Foo'))) + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('placeholder' => 'Bar'))) + ->getForm() + ->createView(); + + $html = $this->renderForm($view); + + $this->assertMatchesXpath($html, '/form//input[@title="[trans]Foo[/trans]"]'); + $this->assertMatchesXpath($html, '/form//input[@placeholder="[trans]Bar[/trans]"]'); + } + + public function testAttributesNotTranslatedWhenTranslationDomainIsFalse() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => false, + )) + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('title' => 'Foo'))) + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('attr' => array('placeholder' => 'Bar'))) + ->getForm() + ->createView(); + + $html = $this->renderForm($view); + + $this->assertMatchesXpath($html, '/form//input[@title="Foo"]'); + $this->assertMatchesXpath($html, '/form//input[@placeholder="Bar"]'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php new file mode 100644 index 0000000..022b514 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php @@ -0,0 +1,384 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\Forms; +use Symfony\Component\Form\RequestHandlerInterface; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractRequestHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var RequestHandlerInterface + */ + protected $requestHandler; + + /** + * @var FormFactory + */ + protected $factory; + + protected $request; + + protected $serverParams; + + protected function setUp() + { + $this->serverParams = $this->getMock( + 'Symfony\Component\Form\Util\ServerParams', + array('getNormalizedIniPostMaxSize', 'getContentLength') + ); + $this->requestHandler = $this->getRequestHandler(); + $this->factory = Forms::createFormFactoryBuilder()->getFormFactory(); + $this->request = null; + } + + public function methodExceptGetProvider() + { + return array( + array('POST'), + array('PUT'), + array('DELETE'), + array('PATCH'), + ); + } + + public function methodProvider() + { + return array_merge(array( + array('GET'), + ), $this->methodExceptGetProvider()); + } + + /** + * @dataProvider methodProvider + */ + public function testSubmitIfNameInRequest($method) + { + $form = $this->getMockForm('param1', $method); + + $this->setRequestData($method, array( + 'param1' => 'DATA', + )); + + $form->expects($this->once()) + ->method('submit') + ->with('DATA', 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodProvider + */ + public function testDoNotSubmitIfWrongRequestMethod($method) + { + $form = $this->getMockForm('param1', $method); + + $otherMethod = 'POST' === $method ? 'PUT' : 'POST'; + + $this->setRequestData($otherMethod, array( + 'param1' => 'DATA', + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testDoNoSubmitSimpleFormIfNameNotInRequestAndNotGetRequest($method) + { + $form = $this->getMockForm('param1', $method, false); + + $this->setRequestData($method, array( + 'paramx' => array(), + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testDoNotSubmitCompoundFormIfNameNotInRequestAndNotGetRequest($method) + { + $form = $this->getMockForm('param1', $method, true); + + $this->setRequestData($method, array( + 'paramx' => array(), + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testDoNotSubmitIfNameNotInRequestAndGetRequest() + { + $form = $this->getMockForm('param1', 'GET'); + + $this->setRequestData('GET', array( + 'paramx' => array(), + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodProvider + */ + public function testSubmitFormWithEmptyNameIfAtLeastOneFieldInRequest($method) + { + $form = $this->getMockForm('', $method); + $form->expects($this->any()) + ->method('all') + ->will($this->returnValue(array( + 'param1' => $this->getMockForm('param1'), + 'param2' => $this->getMockForm('param2'), + ))); + + $this->setRequestData($method, $requestData = array( + 'param1' => 'submitted value', + 'paramx' => 'submitted value', + )); + + $form->expects($this->once()) + ->method('submit') + ->with($requestData, 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodProvider + */ + public function testDoNotSubmitFormWithEmptyNameIfNoFieldInRequest($method) + { + $form = $this->getMockForm('', $method); + $form->expects($this->any()) + ->method('all') + ->will($this->returnValue(array( + 'param1' => $this->getMockForm('param1'), + 'param2' => $this->getMockForm('param2'), + ))); + + $this->setRequestData($method, array( + 'paramx' => 'submitted value', + )); + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testMergeParamsAndFiles($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => array( + 'field1' => 'DATA', + ), + ), array( + 'param1' => array( + 'field2' => $file, + ), + )); + + $form->expects($this->once()) + ->method('submit') + ->with(array( + 'field1' => 'DATA', + 'field2' => $file, + ), 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testParamTakesPrecedenceOverFile($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => 'DATA', + ), array( + 'param1' => $file, + )); + + $form->expects($this->once()) + ->method('submit') + ->with('DATA', 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitFileIfNoParam($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => null, + ), array( + 'param1' => $file, + )); + + $form->expects($this->once()) + ->method('submit') + ->with($file, 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitMultipleFiles($method) + { + $form = $this->getMockForm('param1', $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + 'param1' => null, + ), array( + 'param2' => $this->getMockFile('2'), + 'param1' => $file, + 'param3' => $this->getMockFile('3'), + )); + + $form->expects($this->once()) + ->method('submit') + ->with($file, 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider methodExceptGetProvider + */ + public function testSubmitFileWithNamelessForm($method) + { + $form = $this->getMockForm(null, $method); + $file = $this->getMockFile(); + + $this->setRequestData($method, array( + '' => null, + ), array( + '' => $file, + )); + + $form->expects($this->once()) + ->method('submit') + ->with($file, 'PATCH' !== $method); + + $this->requestHandler->handleRequest($form, $this->request); + } + + /** + * @dataProvider getPostMaxSizeFixtures + */ + public function testAddFormErrorIfPostMaxSizeExceeded($contentLength, $iniMax, $shouldFail, array $errorParams = array()) + { + $this->serverParams->expects($this->once()) + ->method('getContentLength') + ->will($this->returnValue($contentLength)); + $this->serverParams->expects($this->any()) + ->method('getNormalizedIniPostMaxSize') + ->will($this->returnValue($iniMax)); + + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, $options); + $this->setRequestData('POST', array(), array()); + + $this->requestHandler->handleRequest($form, $this->request); + + if ($shouldFail) { + $error = new FormError($options['post_max_size_message'], null, $errorParams); + $error->setOrigin($form); + + $this->assertEquals(array($error), iterator_to_array($form->getErrors())); + $this->assertTrue($form->isSubmitted()); + } else { + $this->assertCount(0, $form->getErrors()); + $this->assertFalse($form->isSubmitted()); + } + } + + public function getPostMaxSizeFixtures() + { + return array( + array(pow(1024, 3) + 1, '1G', true, array('{{ max }}' => '1G')), + array(pow(1024, 3), '1G', false), + array(pow(1024, 2) + 1, '1M', true, array('{{ max }}' => '1M')), + array(pow(1024, 2), '1M', false), + array(1024 + 1, '1K', true, array('{{ max }}' => '1K')), + array(1024, '1K', false), + array(null, '1K', false), + array(1024, '', false), + array(1024, 0, false), + ); + } + + abstract protected function setRequestData($method, $data, $files = array()); + + abstract protected function getRequestHandler(); + + abstract protected function getMockFile($suffix = ''); + + protected function getMockForm($name, $method = null, $compound = true) + { + $config = $this->getMock('Symfony\Component\Form\FormConfigInterface'); + $config->expects($this->any()) + ->method('getMethod') + ->will($this->returnValue($method)); + $config->expects($this->any()) + ->method('getCompound') + ->will($this->returnValue($compound)); + + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $form->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + $form->expects($this->any()) + ->method('getConfig') + ->will($this->returnValue($config)); + + return $form; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php new file mode 100644 index 0000000..9902a6d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -0,0 +1,536 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Security\Csrf\CsrfToken; + +abstract class AbstractTableLayoutTest extends AbstractLayoutTest +{ + public function testRow() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name"]] + /following-sibling::td + [ + ./ul + [./li[.="[trans]Error![/trans]"]] + [count(./li)=1] + /following-sibling::input[@id="name"] + ] + ] +' + ); + } + + public function testLabelIsNotRenderedWhenSetToFalse() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'label' => false, + )); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [count(//label)=0] + /following-sibling::td + [./input[@id="name"]] + ] +' + ); + } + + public function testRepeatedRow() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType'); + $html = $this->renderRow($form->createView()); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@id="name_first"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@id="name_second"]] + ] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + [count(../tr)=3] +' + ); + } + + public function testRepeatedRowWithErrors() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType'); + $form->addError(new FormError('[trans]Error![/trans]')); + $view = $form->createView(); + $html = $this->renderRow($view); + + // The errors of the form are not rendered by intention! + // In practice, repeated fields cannot have errors as all errors + // on them are mapped to the first child. + // (see RepeatedTypeValidatorExtension) + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@id="name_first"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@id="name_second"]] + ] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + [count(../tr)=3] +' + ); + } + + public function testButtonRow() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ButtonType'); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [.=""] + /following-sibling::td + [./button[@type="button"][@name="name"]] + ] + [count(//label)=0] +' + ); + } + + public function testRest() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType') + ->add('field3', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field4', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + // Render field2 row -> does not implicitly call renderWidget because + // it is a repeated field! + $this->renderRow($view['field2']); + + // Render field3 widget + $this->renderWidget($view['field3']); + + // Rest should only contain field1 and field4 + $html = $this->renderRest($view); + + $this->assertMatchesXpath($html, +'/tr + [ + ./td + [./label[@for="name_field1"]] + /following-sibling::td + [./input[@id="name_field1"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_field4"]] + /following-sibling::td + [./input[@id="name_field4"]] + ] + [count(../tr)=3] + [count(..//label)=2] + [count(..//input)=3] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] +' + ); + } + + public function testCollection() + { + $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array('a', 'b'), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[./td/input[@type="text"][@value="a"]] + /following-sibling::tr[./td/input[@type="text"][@value="b"]] + /following-sibling::tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="names__token"]] + ] + [count(./tr[./td/input])=3] +' + ); + } + + public function testEmptyCollection() + { + $form = $this->factory->createNamed('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="names__token"]]] + [count(./tr[./td/input])=1] +' + ); + } + + public function testForm() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->setMethod('PUT') + ->setAction('http://example.com') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + $html = $this->renderForm($view, array( + 'id' => 'my&id', + 'attr' => array('class' => 'my&class'), + )); + + $this->assertMatchesXpath($html, +'/form + [ + ./input[@type="hidden"][@name="_method"][@value="PUT"] + /following-sibling::table + [ + ./tr + [ + ./td + [./label[@for="name_firstName"]] + /following-sibling::td + [./input[@id="name_firstName"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_lastName"]] + /following-sibling::td + [./input[@id="name_lastName"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] + [@id="my&id"] + [@class="my&class"] + ] + [@method="post"] + [@action="http://example.com"] + [@class="my&class"] +' + ); + } + + public function testFormWidget() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + $this->assertWidgetMatchesXpath($view, array(), +'/table + [ + ./tr + [ + ./td + [./label[@for="name_firstName"]] + /following-sibling::td + [./input[@id="name_firstName"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_lastName"]] + /following-sibling::td + [./input[@id="name_lastName"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] +' + ); + } + + // https://github.com/symfony/symfony/issues/2308 + public function testNestedFormError() + { + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory + ->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array('error_bubbling' => false)) + ->add('grandChild', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ) + ->getForm(); + + $form->get('child')->addError(new FormError('[trans]Error![/trans]')); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr/td/ul[./li[.="[trans]Error![/trans]"]] + /following-sibling::table[@id="name_child"] + ] + [count(.//li[.="[trans]Error![/trans]"])=1] +' + ); + } + + public function testCsrf() + { + $this->csrfTokenManager->expects($this->any()) + ->method('getToken') + ->will($this->returnValue(new CsrfToken('token_id', 'foo&bar'))); + + $form = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('child', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory->createNamedBuilder('grandchild', 'Symfony\Component\Form\Extension\Core\Type\TextType')) + ) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input[@type="hidden"])=1] +' + ); + } + + public function testRepeated() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType', 'foobar', array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@type="text"][@id="name_first"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@type="text"][@id="name_second"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] +' + ); + } + + public function testRepeatedWithCustomOptions() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\RepeatedType', 'foobar', array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\PasswordType', + 'first_options' => array('label' => 'Test', 'required' => false), + 'second_options' => array('label' => 'Test2'), + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr + [ + ./td + [./label[@for="name_first"][.="[trans]Test[/trans]"]] + /following-sibling::td + [./input[@type="password"][@id="name_first"][@required="required"]] + ] + /following-sibling::tr + [ + ./td + [./label[@for="name_second"][.="[trans]Test2[/trans]"]] + /following-sibling::td + [./input[@type="password"][@id="name_second"][@required="required"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input)=3] +' + ); + } + + /** + * The block "_name_child_label" should be overridden in the theme of the + * implemented driver. + */ + public function testCollectionRowWithCustomBlock() + { + $collection = array('one', 'two', 'three'); + $form = $this->factory->createNamedBuilder('names', 'Symfony\Component\Form\Extension\Core\Type\CollectionType', $collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[./td/label[.="Custom label: [trans]0[/trans]"]] + /following-sibling::tr[./td/label[.="Custom label: [trans]1[/trans]"]] + /following-sibling::tr[./td/label[.="Custom label: [trans]2[/trans]"]] + ] +' + ); + } + + public function testFormEndWithRest() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2 + $html = $this->renderEnd($view); + + // Insert the start tag, the end tag should be rendered by the helper + // Unfortunately this is not valid HTML, because the surrounding table + // tag is missing. If someone renders a form with table layout + // manually, she should call form_rest() explicitly within the
    + // tag. + $this->assertMatchesXpath(''.$html, +'/form + [ + ./tr + [ + ./td + [./label[@for="name_field2"]] + /following-sibling::td + [./input[@id="name_field2"]] + ] + /following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] +' + ); + } + + public function testFormEndWithoutRest() + { + $view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('field1', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('field2', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm() + ->createView(); + + $this->renderWidget($view['field1']); + + // Rest should only contain field2, but isn't rendered + $html = $this->renderEnd($view, array('render_rest' => false)); + + $this->assertEquals('', $html); + } + + public function testWidgetContainerAttributes() + { + $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'attr' => array('class' => 'foobar', 'data-foo' => 'bar'), + )); + + $form->add('text', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + + $html = $this->renderWidget($form->createView()); + + // compare plain HTML to check the whitespace + $this->assertContains('
    ', $html); + } + + public function testWidgetContainerAttributeNameRepeatedIfTrue() + { + $form = $this->factory->createNamed('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'attr' => array('foo' => true), + )); + + $html = $this->renderWidget($form->createView()); + + // foo="foo" + $this->assertContains('
    ', $html); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ButtonTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ButtonTest.php new file mode 100644 index 0000000..b0c766c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ButtonTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\ButtonBuilder; +use Symfony\Component\Form\FormBuilder; + +/** + * @author Bernhard Schussek + */ +class ButtonTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + + private $factory; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + } + + /** + * @dataProvider getDisabledStates + */ + public function testDisabledIfParentIsDisabled($parentDisabled, $buttonDisabled, $result) + { + $form = $this->getFormBuilder('form') + ->setDisabled($parentDisabled) + ->getForm(); + + $button = $this->getButtonBuilder('button') + ->setDisabled($buttonDisabled) + ->getForm(); + + $button->setParent($form); + + $this->assertSame($result, $button->isDisabled()); + } + + public function getDisabledStates() + { + return array( + // parent, button, result + array(true, true, true), + array(true, false, true), + array(false, true, true), + array(false, false, false), + ); + } + + private function getButtonBuilder($name) + { + return new ButtonBuilder($name); + } + + private function getFormBuilder($name) + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CallbackTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CallbackTransformerTest.php new file mode 100644 index 0000000..8c0469e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CallbackTransformerTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\CallbackTransformer; + +class CallbackTransformerTest extends \PHPUnit_Framework_TestCase +{ + public function testTransform() + { + $transformer = new CallbackTransformer( + function ($value) { return $value.' has been transformed'; }, + function ($value) { return $value.' has reversely been transformed'; } + ); + + $this->assertEquals('foo has been transformed', $transformer->transform('foo')); + $this->assertEquals('bar has reversely been transformed', $transformer->reverseTransform('bar')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/AbstractChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/AbstractChoiceListTest.php new file mode 100644 index 0000000..ca244eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/AbstractChoiceListTest.php @@ -0,0 +1,220 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList; + +/** + * @author Bernhard Schussek + */ +abstract class AbstractChoiceListTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + protected $list; + + /** + * @var array + */ + protected $choices; + + /** + * @var array + */ + protected $values; + + /** + * @var array + */ + protected $structuredValues; + + /** + * @var array + */ + protected $keys; + + /** + * @var mixed + */ + protected $choice1; + + /** + * @var mixed + */ + protected $choice2; + + /** + * @var mixed + */ + protected $choice3; + + /** + * @var mixed + */ + protected $choice4; + + /** + * @var string + */ + protected $value1; + + /** + * @var string + */ + protected $value2; + + /** + * @var string + */ + protected $value3; + + /** + * @var string + */ + protected $value4; + + /** + * @var string + */ + protected $key1; + + /** + * @var string + */ + protected $key2; + + /** + * @var string + */ + protected $key3; + + /** + * @var string + */ + protected $key4; + + protected function setUp() + { + parent::setUp(); + + $this->list = $this->createChoiceList(); + + $choices = $this->getChoices(); + + $this->values = $this->getValues(); + $this->structuredValues = array_combine(array_keys($choices), $this->values); + $this->choices = array_combine($this->values, $choices); + $this->keys = array_combine($this->values, array_keys($choices)); + + // allow access to the individual entries without relying on their indices + reset($this->choices); + reset($this->values); + reset($this->keys); + + for ($i = 1; $i <= 4; ++$i) { + $this->{'choice'.$i} = current($this->choices); + $this->{'value'.$i} = current($this->values); + $this->{'key'.$i} = current($this->keys); + + next($this->choices); + next($this->values); + next($this->keys); + } + } + + public function testGetChoices() + { + $this->assertSame($this->choices, $this->list->getChoices()); + } + + public function testGetValues() + { + $this->assertSame($this->values, $this->list->getValues()); + } + + public function testGetStructuredValues() + { + $this->assertSame($this->values, $this->list->getStructuredValues()); + } + + public function testGetOriginalKeys() + { + $this->assertSame($this->keys, $this->list->getOriginalKeys()); + } + + public function testGetChoicesForValues() + { + $values = array($this->value1, $this->value2); + $this->assertSame(array($this->choice1, $this->choice2), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesPreservesKeys() + { + $values = array(5 => $this->value1, 8 => $this->value2); + $this->assertSame(array(5 => $this->choice1, 8 => $this->choice2), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesPreservesOrder() + { + $values = array($this->value2, $this->value1); + $this->assertSame(array($this->choice2, $this->choice1), $this->list->getChoicesForValues($values)); + } + + public function testGetChoicesForValuesIgnoresNonExistingValues() + { + $values = array($this->value1, $this->value2, 'foobar'); + $this->assertSame(array($this->choice1, $this->choice2), $this->list->getChoicesForValues($values)); + } + + // https://github.com/symfony/symfony/issues/3446 + public function testGetChoicesForValuesEmpty() + { + $this->assertSame(array(), $this->list->getChoicesForValues(array())); + } + + public function testGetValuesForChoices() + { + $choices = array($this->choice1, $this->choice2); + $this->assertSame(array($this->value1, $this->value2), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesPreservesKeys() + { + $choices = array(5 => $this->choice1, 8 => $this->choice2); + $this->assertSame(array(5 => $this->value1, 8 => $this->value2), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesPreservesOrder() + { + $choices = array($this->choice2, $this->choice1); + $this->assertSame(array($this->value2, $this->value1), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesIgnoresNonExistingChoices() + { + $choices = array($this->choice1, $this->choice2, 'foobar'); + $this->assertSame(array($this->value1, $this->value2), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesEmpty() + { + $this->assertSame(array(), $this->list->getValuesForChoices(array())); + } + + /** + * @return \Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface + */ + abstract protected function createChoiceList(); + + abstract protected function getChoices(); + + abstract protected function getValues(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php new file mode 100644 index 0000000..9d144f9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList; + +use Symfony\Component\Form\ChoiceList\ArrayChoiceList; + +/** + * @author Bernhard Schussek + */ +class ArrayChoiceListTest extends AbstractChoiceListTest +{ + private $object; + + protected function setUp() + { + parent::setUp(); + + $this->object = new \stdClass(); + } + + protected function createChoiceList() + { + return new ArrayChoiceList($this->getChoices()); + } + + protected function getChoices() + { + return array(0, 1, '1', 'a', false, true, $this->object); + } + + protected function getValues() + { + return array('0', '1', '2', '3', '4', '5', '6'); + } + + public function testCreateChoiceListWithValueCallback() + { + $callback = function ($choice) { + return ':'.$choice; + }; + + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => 'bar', 10 => 'baz'), $callback); + + $this->assertSame(array(':foo', ':bar', ':baz'), $choiceList->getValues()); + $this->assertSame(array(':foo' => 'foo', ':bar' => 'bar', ':baz' => 'baz'), $choiceList->getChoices()); + $this->assertSame(array(':foo' => 2, ':bar' => 7, ':baz' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 'baz'), $choiceList->getChoicesForValues(array(1 => ':foo', 2 => ':baz'))); + $this->assertSame(array(1 => ':foo', 2 => ':baz'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 'baz'))); + } + + public function testCreateChoiceListWithoutValueCallbackAndDuplicateFreeToStringChoices() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => 'bar', 10 => 123)); + + $this->assertSame(array('foo', 'bar', '123'), $choiceList->getValues()); + $this->assertSame(array('foo' => 'foo', 'bar' => 'bar', '123' => 123), $choiceList->getChoices()); + $this->assertSame(array('foo' => 2, 'bar' => 7, '123' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => 'foo', 2 => '123'))); + $this->assertSame(array(1 => 'foo', 2 => '123'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndToStringDuplicates() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => '123', 10 => 123)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => 123), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndMixedChoices() + { + $object = new \stdClass(); + $choiceList = new ArrayChoiceList(array(2 => 'foo', 5 => array(7 => '123'), 10 => $object)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => $object), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => $object), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => $object))); + } + + public function testCreateChoiceListWithGroupedChoices() + { + $choiceList = new ArrayChoiceList(array( + 'Group 1' => array('A' => 'a', 'B' => 'b'), + 'Group 2' => array('C' => 'c', 'D' => 'd'), + )); + + $this->assertSame(array('a', 'b', 'c', 'd'), $choiceList->getValues()); + $this->assertSame(array( + 'Group 1' => array('A' => 'a', 'B' => 'b'), + 'Group 2' => array('C' => 'c', 'D' => 'd'), + ), $choiceList->getStructuredValues()); + $this->assertSame(array('a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd'), $choiceList->getChoices()); + $this->assertSame(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); + } + + public function testCompareChoicesByIdentityByDefault() + { + $callback = function ($choice) { + return $choice->value; + }; + + $obj1 = (object) array('value' => 'value1'); + $obj2 = (object) array('value' => 'value2'); + + $choiceList = new ArrayChoiceList(array($obj1, $obj2), $callback); + $this->assertSame(array(2 => 'value2'), $choiceList->getValuesForChoices(array(2 => $obj2))); + $this->assertSame(array(2 => 'value2'), $choiceList->getValuesForChoices(array(2 => (object) array('value' => 'value2')))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php new file mode 100644 index 0000000..c27c009 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/CachingFactoryDecoratorTest.php @@ -0,0 +1,531 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList\Factory; + +use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; + +/** + * @author Bernhard Schussek + */ +class CachingFactoryDecoratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $decoratedFactory; + + /** + * @var CachingFactoryDecorator + */ + private $factory; + + protected function setUp() + { + $this->decoratedFactory = $this->getMock('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface'); + $this->factory = new CachingFactoryDecorator($this->decoratedFactory); + } + + public function testCreateFromChoicesEmpty() + { + $list = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with(array()) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromChoices(array())); + $this->assertSame($list, $this->factory->createListFromChoices(array())); + } + + public function testCreateFromChoicesComparesTraversableChoicesAsArray() + { + // The top-most traversable is converted to an array + $choices1 = new \ArrayIterator(array('A' => 'a')); + $choices2 = array('A' => 'a'); + $list = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices2) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromChoices($choices1)); + $this->assertSame($list, $this->factory->createListFromChoices($choices2)); + } + + public function testCreateFromChoicesFlattensChoices() + { + $choices1 = array('key' => array('A' => 'a')); + $choices2 = array('A' => 'a'); + $list = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices1) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromChoices($choices1)); + $this->assertSame($list, $this->factory->createListFromChoices($choices2)); + } + + /** + * @dataProvider provideSameChoices + */ + public function testCreateFromChoicesSameChoices($choice1, $choice2) + { + $choices1 = array($choice1); + $choices2 = array($choice2); + $list = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices1) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromChoices($choices1)); + $this->assertSame($list, $this->factory->createListFromChoices($choices2)); + } + + /** + * @dataProvider provideDistinguishedChoices + */ + public function testCreateFromChoicesDifferentChoices($choice1, $choice2) + { + $choices1 = array($choice1); + $choices2 = array($choice2); + $list1 = new \stdClass(); + $list2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createListFromChoices') + ->with($choices1) + ->will($this->returnValue($list1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createListFromChoices') + ->with($choices2) + ->will($this->returnValue($list2)); + + $this->assertSame($list1, $this->factory->createListFromChoices($choices1)); + $this->assertSame($list2, $this->factory->createListFromChoices($choices2)); + } + + public function testCreateFromChoicesSameValueClosure() + { + $choices = array(1); + $list = new \stdClass(); + $closure = function () {}; + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices, $closure) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromChoices($choices, $closure)); + $this->assertSame($list, $this->factory->createListFromChoices($choices, $closure)); + } + + public function testCreateFromChoicesDifferentValueClosure() + { + $choices = array(1); + $list1 = new \stdClass(); + $list2 = new \stdClass(); + $closure1 = function () {}; + $closure2 = function () {}; + + $this->decoratedFactory->expects($this->at(0)) + ->method('createListFromChoices') + ->with($choices, $closure1) + ->will($this->returnValue($list1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createListFromChoices') + ->with($choices, $closure2) + ->will($this->returnValue($list2)); + + $this->assertSame($list1, $this->factory->createListFromChoices($choices, $closure1)); + $this->assertSame($list2, $this->factory->createListFromChoices($choices, $closure2)); + } + + public function testCreateFromLoaderSameLoader() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + $list = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromLoader') + ->with($loader) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromLoader($loader)); + $this->assertSame($list, $this->factory->createListFromLoader($loader)); + } + + public function testCreateFromLoaderDifferentLoader() + { + $loader1 = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + $loader2 = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + $list1 = new \stdClass(); + $list2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createListFromLoader') + ->with($loader1) + ->will($this->returnValue($list1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createListFromLoader') + ->with($loader2) + ->will($this->returnValue($list2)); + + $this->assertSame($list1, $this->factory->createListFromLoader($loader1)); + $this->assertSame($list2, $this->factory->createListFromLoader($loader2)); + } + + public function testCreateFromLoaderSameValueClosure() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + $list = new \stdClass(); + $closure = function () {}; + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromLoader') + ->with($loader, $closure) + ->will($this->returnValue($list)); + + $this->assertSame($list, $this->factory->createListFromLoader($loader, $closure)); + $this->assertSame($list, $this->factory->createListFromLoader($loader, $closure)); + } + + public function testCreateFromLoaderDifferentValueClosure() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + $list1 = new \stdClass(); + $list2 = new \stdClass(); + $closure1 = function () {}; + $closure2 = function () {}; + + $this->decoratedFactory->expects($this->at(0)) + ->method('createListFromLoader') + ->with($loader, $closure1) + ->will($this->returnValue($list1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createListFromLoader') + ->with($loader, $closure2) + ->will($this->returnValue($list2)); + + $this->assertSame($list1, $this->factory->createListFromLoader($loader, $closure1)); + $this->assertSame($list2, $this->factory->createListFromLoader($loader, $closure2)); + } + + public function testCreateViewSamePreferredChoices() + { + $preferred = array('a'); + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, $preferred) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, $preferred)); + $this->assertSame($view, $this->factory->createView($list, $preferred)); + } + + public function testCreateViewDifferentPreferredChoices() + { + $preferred1 = array('a'); + $preferred2 = array('b'); + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, $preferred1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, $preferred2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, $preferred1)); + $this->assertSame($view2, $this->factory->createView($list, $preferred2)); + } + + public function testCreateViewSamePreferredChoicesClosure() + { + $preferred = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, $preferred) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, $preferred)); + $this->assertSame($view, $this->factory->createView($list, $preferred)); + } + + public function testCreateViewDifferentPreferredChoicesClosure() + { + $preferred1 = function () {}; + $preferred2 = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, $preferred1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, $preferred2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, $preferred1)); + $this->assertSame($view2, $this->factory->createView($list, $preferred2)); + } + + public function testCreateViewSameLabelClosure() + { + $labels = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, $labels) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, null, $labels)); + $this->assertSame($view, $this->factory->createView($list, null, $labels)); + } + + public function testCreateViewDifferentLabelClosure() + { + $labels1 = function () {}; + $labels2 = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, null, $labels1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, null, $labels2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, null, $labels1)); + $this->assertSame($view2, $this->factory->createView($list, null, $labels2)); + } + + public function testCreateViewSameIndexClosure() + { + $index = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, $index) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, null, null, $index)); + $this->assertSame($view, $this->factory->createView($list, null, null, $index)); + } + + public function testCreateViewDifferentIndexClosure() + { + $index1 = function () {}; + $index2 = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, null, null, $index1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, null, null, $index2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, null, null, $index1)); + $this->assertSame($view2, $this->factory->createView($list, null, null, $index2)); + } + + public function testCreateViewSameGroupByClosure() + { + $groupBy = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, $groupBy) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, null, null, null, $groupBy)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, $groupBy)); + } + + public function testCreateViewDifferentGroupByClosure() + { + $groupBy1 = function () {}; + $groupBy2 = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, null, null, null, $groupBy1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, null, null, null, $groupBy2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, null, null, null, $groupBy1)); + $this->assertSame($view2, $this->factory->createView($list, null, null, null, $groupBy2)); + } + + public function testCreateViewSameAttributes() + { + $attr = array('class' => 'foobar'); + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, null, $attr) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); + } + + public function testCreateViewDifferentAttributes() + { + $attr1 = array('class' => 'foobar1'); + $attr2 = array('class' => 'foobar2'); + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, null, null, null, null, $attr1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, null, null, null, null, $attr2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, null, null, null, null, $attr1)); + $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr2)); + } + + public function testCreateViewSameAttributesClosure() + { + $attr = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view = new \stdClass(); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, null, $attr) + ->will($this->returnValue($view)); + + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); + $this->assertSame($view, $this->factory->createView($list, null, null, null, null, $attr)); + } + + public function testCreateViewDifferentAttributesClosure() + { + $attr1 = function () {}; + $attr2 = function () {}; + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $view1 = new \stdClass(); + $view2 = new \stdClass(); + + $this->decoratedFactory->expects($this->at(0)) + ->method('createView') + ->with($list, null, null, null, null, $attr1) + ->will($this->returnValue($view1)); + $this->decoratedFactory->expects($this->at(1)) + ->method('createView') + ->with($list, null, null, null, null, $attr2) + ->will($this->returnValue($view2)); + + $this->assertSame($view1, $this->factory->createView($list, null, null, null, null, $attr1)); + $this->assertSame($view2, $this->factory->createView($list, null, null, null, null, $attr2)); + } + + public function provideSameChoices() + { + $object = (object) array('foo' => 'bar'); + + return array( + array(0, 0), + array('a', 'a'), + // https://github.com/symfony/symfony/issues/10409 + array(chr(181).'meter', chr(181).'meter'), // UTF-8 + array($object, $object), + ); + } + + public function provideDistinguishedChoices() + { + return array( + array(0, false), + array(0, null), + array(0, '0'), + array(0, ''), + array(1, true), + array(1, '1'), + array(1, 'a'), + array('', false), + array('', null), + array(false, null), + // Same properties, but not identical + array((object) array('foo' => 'bar'), (object) array('foo' => 'bar')), + ); + } + + public function provideSameKeyChoices() + { + // Only test types here that can be used as array keys + return array( + array(0, 0), + array(0, '0'), + array('a', 'a'), + array(chr(181).'meter', chr(181).'meter'), + ); + } + + public function provideDistinguishedKeyChoices() + { + // Only test types here that can be used as array keys + return array( + array(0, ''), + array(1, 'a'), + array('', 'a'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php new file mode 100644 index 0000000..4c26841 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -0,0 +1,790 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList\Factory; + +use Symfony\Component\Form\ChoiceList\ArrayChoiceList; +use Symfony\Component\Form\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory; +use Symfony\Component\Form\ChoiceList\LazyChoiceList; +use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; +use Symfony\Component\Form\ChoiceList\View\ChoiceListView; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; + +class DefaultChoiceListFactoryTest extends \PHPUnit_Framework_TestCase +{ + private $obj1; + + private $obj2; + + private $obj3; + + private $obj4; + + private $list; + + /** + * @var DefaultChoiceListFactory + */ + private $factory; + + public function getValue($object) + { + return $object->value; + } + + public function getScalarValue($choice) + { + switch ($choice) { + case 'a': return 'a'; + case 'b': return 'b'; + case 'c': return '1'; + case 'd': return '2'; + } + } + + public function getLabel($object) + { + return $object->label; + } + + public function getFormIndex($object) + { + return $object->index; + } + + public function isPreferred($object) + { + return $this->obj2 === $object || $this->obj3 === $object; + } + + public function getAttr($object) + { + return $object->attr; + } + + public function getGroup($object) + { + return $this->obj1 === $object || $this->obj2 === $object ? 'Group 1' : 'Group 2'; + } + + public function getGroupAsObject($object) + { + return $this->obj1 === $object || $this->obj2 === $object + ? new DefaultChoiceListFactoryTest_Castable('Group 1') + : new DefaultChoiceListFactoryTest_Castable('Group 2'); + } + + protected function setUp() + { + $this->obj1 = (object) array('label' => 'A', 'index' => 'w', 'value' => 'a', 'preferred' => false, 'group' => 'Group 1', 'attr' => array()); + $this->obj2 = (object) array('label' => 'B', 'index' => 'x', 'value' => 'b', 'preferred' => true, 'group' => 'Group 1', 'attr' => array('attr1' => 'value1')); + $this->obj3 = (object) array('label' => 'C', 'index' => 'y', 'value' => 1, 'preferred' => true, 'group' => 'Group 2', 'attr' => array('attr2' => 'value2')); + $this->obj4 = (object) array('label' => 'D', 'index' => 'z', 'value' => 2, 'preferred' => false, 'group' => 'Group 2', 'attr' => array()); + $this->list = new ArrayChoiceList( + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4) + ); + $this->factory = new DefaultChoiceListFactory(); + } + + public function testCreateFromChoicesEmpty() + { + $list = $this->factory->createListFromChoices(array()); + + $this->assertSame(array(), $list->getChoices()); + $this->assertSame(array(), $list->getValues()); + } + + public function testCreateFromChoicesFlat() + { + $list = $this->factory->createListFromChoices( + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4) + ); + + $this->assertObjectListWithGeneratedValues($list); + } + + public function testCreateFromChoicesFlatTraversable() + { + $list = $this->factory->createListFromChoices( + new \ArrayIterator(array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4)) + ); + + $this->assertObjectListWithGeneratedValues($list); + } + + public function testCreateFromChoicesFlatValuesAsCallable() + { + $list = $this->factory->createListFromChoices( + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4), + array($this, 'getValue') + ); + + $this->assertObjectListWithCustomValues($list); + } + + public function testCreateFromChoicesFlatValuesAsClosure() + { + $list = $this->factory->createListFromChoices( + array('A' => $this->obj1, 'B' => $this->obj2, 'C' => $this->obj3, 'D' => $this->obj4), + function ($object) { return $object->value; } + ); + + $this->assertObjectListWithCustomValues($list); + } + + public function testCreateFromChoicesGrouped() + { + $list = $this->factory->createListFromChoices( + array( + 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), + 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + ) + ); + + $this->assertObjectListWithGeneratedValues($list); + } + + public function testCreateFromChoicesGroupedTraversable() + { + $list = $this->factory->createListFromChoices( + new \ArrayIterator(array( + 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), + 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + )) + ); + + $this->assertObjectListWithGeneratedValues($list); + } + + public function testCreateFromChoicesGroupedValuesAsCallable() + { + $list = $this->factory->createListFromChoices( + array( + 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), + 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + ), + array($this, 'getValue') + ); + + $this->assertObjectListWithCustomValues($list); + } + + public function testCreateFromChoicesGroupedValuesAsClosure() + { + $list = $this->factory->createListFromChoices( + array( + 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), + 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + ), + function ($object) { return $object->value; } + ); + + $this->assertObjectListWithCustomValues($list); + } + + public function testCreateFromLoader() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + + $list = $this->factory->createListFromLoader($loader); + + $this->assertEquals(new LazyChoiceList($loader), $list); + } + + public function testCreateFromLoaderWithValues() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + + $value = function () {}; + $list = $this->factory->createListFromLoader($loader, $value); + + $this->assertEquals(new LazyChoiceList($loader, $value), $list); + } + + public function testCreateViewFlat() + { + $view = $this->factory->createView($this->list); + + $this->assertEquals(new ChoiceListView( + array( + 0 => new ChoiceView($this->obj1, '0', 'A'), + 1 => new ChoiceView($this->obj2, '1', 'B'), + 2 => new ChoiceView($this->obj3, '2', 'C'), + 3 => new ChoiceView($this->obj4, '3', 'D'), + ), array() + ), $view); + } + + public function testCreateViewFlatPreferredChoices() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3) + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatPreferredChoicesEmptyArray() + { + $view = $this->factory->createView( + $this->list, + array() + ); + + $this->assertEquals(new ChoiceListView( + array( + 0 => new ChoiceView($this->obj1, '0', 'A'), + 1 => new ChoiceView($this->obj2, '1', 'B'), + 2 => new ChoiceView($this->obj3, '2', 'C'), + 3 => new ChoiceView($this->obj4, '3', 'D'), + ), array() + ), $view); + } + + public function testCreateViewFlatPreferredChoicesAsCallable() + { + $view = $this->factory->createView( + $this->list, + array($this, 'isPreferred') + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatPreferredChoicesAsClosure() + { + $obj2 = $this->obj2; + $obj3 = $this->obj3; + + $view = $this->factory->createView( + $this->list, + function ($object) use ($obj2, $obj3) { + return $obj2 === $object || $obj3 === $object; + } + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatPreferredChoicesClosureReceivesKey() + { + $view = $this->factory->createView( + $this->list, + function ($object, $key) { + return 'B' === $key || 'C' === $key; + } + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatPreferredChoicesClosureReceivesValue() + { + $view = $this->factory->createView( + $this->list, + function ($object, $key, $value) { + return '1' === $value || '2' === $value; + } + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatLabelAsCallable() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + array($this, 'getLabel') + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatLabelAsClosure() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + function ($object) { + return $object->label; + } + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatLabelClosureReceivesKey() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + function ($object, $key) { + return $key; + } + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatLabelClosureReceivesValue() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + function ($object, $key, $value) { + switch ($value) { + case '0': return 'A'; + case '1': return 'B'; + case '2': return 'C'; + case '3': return 'D'; + } + } + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatIndexAsCallable() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + array($this, 'getFormIndex') + ); + + $this->assertFlatViewWithCustomIndices($view); + } + + public function testCreateViewFlatIndexAsClosure() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + function ($object) { + return $object->index; + } + ); + + $this->assertFlatViewWithCustomIndices($view); + } + + public function testCreateViewFlatIndexClosureReceivesKey() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + function ($object, $key) { + switch ($key) { + case 'A': return 'w'; + case 'B': return 'x'; + case 'C': return 'y'; + case 'D': return 'z'; + } + } + ); + + $this->assertFlatViewWithCustomIndices($view); + } + + public function testCreateViewFlatIndexClosureReceivesValue() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + function ($object, $key, $value) { + switch ($value) { + case '0': return 'w'; + case '1': return 'x'; + case '2': return 'y'; + case '3': return 'z'; + } + } + ); + + $this->assertFlatViewWithCustomIndices($view); + } + + public function testCreateViewFlatGroupByOriginalStructure() + { + $list = new ArrayChoiceList(array( + 'Group 1' => array('A' => $this->obj1, 'B' => $this->obj2), + 'Group 2' => array('C' => $this->obj3, 'D' => $this->obj4), + 'Group empty' => array(), + )); + + $view = $this->factory->createView( + $list, + array($this->obj2, $this->obj3) + ); + + $this->assertGroupedView($view); + } + + public function testCreateViewFlatGroupByEmpty() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + array() // ignored + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatGroupByAsCallable() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + array($this, 'getGroup') + ); + + $this->assertGroupedView($view); + } + + public function testCreateViewFlatGroupByObjectThatCanBeCastToString() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + array($this, 'getGroupAsObject') + ); + + $this->assertGroupedView($view); + } + + public function testCreateViewFlatGroupByAsClosure() + { + $obj1 = $this->obj1; + $obj2 = $this->obj2; + + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + function ($object) use ($obj1, $obj2) { + return $obj1 === $object || $obj2 === $object ? 'Group 1' : 'Group 2'; + } + ); + + $this->assertGroupedView($view); + } + + public function testCreateViewFlatGroupByClosureReceivesKey() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + function ($object, $key) { + return 'A' === $key || 'B' === $key ? 'Group 1' : 'Group 2'; + } + ); + + $this->assertGroupedView($view); + } + + public function testCreateViewFlatGroupByClosureReceivesValue() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + function ($object, $key, $value) { + return '0' === $value || '1' === $value ? 'Group 1' : 'Group 2'; + } + ); + + $this->assertGroupedView($view); + } + + public function testCreateViewFlatAttrAsArray() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + array( + 'B' => array('attr1' => 'value1'), + 'C' => array('attr2' => 'value2'), + ) + ); + + $this->assertFlatViewWithAttr($view); + } + + public function testCreateViewFlatAttrEmpty() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + array() + ); + + $this->assertFlatView($view); + } + + public function testCreateViewFlatAttrAsCallable() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + array($this, 'getAttr') + ); + + $this->assertFlatViewWithAttr($view); + } + + public function testCreateViewFlatAttrAsClosure() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + function ($object) { + return $object->attr; + } + ); + + $this->assertFlatViewWithAttr($view); + } + + public function testCreateViewFlatAttrClosureReceivesKey() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + function ($object, $key) { + switch ($key) { + case 'B': return array('attr1' => 'value1'); + case 'C': return array('attr2' => 'value2'); + default: return array(); + } + } + ); + + $this->assertFlatViewWithAttr($view); + } + + public function testCreateViewFlatAttrClosureReceivesValue() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + function ($object, $key, $value) { + switch ($value) { + case '1': return array('attr1' => 'value1'); + case '2': return array('attr2' => 'value2'); + default: return array(); + } + } + ); + + $this->assertFlatViewWithAttr($view); + } + + private function assertScalarListWithChoiceValues(ChoiceListInterface $list) + { + $this->assertSame(array('a', 'b', 'c', 'd'), $list->getValues()); + + $this->assertSame(array( + 'a' => 'a', + 'b' => 'b', + 'c' => 'c', + 'd' => 'd', + ), $list->getChoices()); + + $this->assertSame(array( + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + ), $list->getOriginalKeys()); + } + + private function assertObjectListWithGeneratedValues(ChoiceListInterface $list) + { + $this->assertSame(array('0', '1', '2', '3'), $list->getValues()); + + $this->assertSame(array( + 0 => $this->obj1, + 1 => $this->obj2, + 2 => $this->obj3, + 3 => $this->obj4, + ), $list->getChoices()); + + $this->assertSame(array( + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + ), $list->getOriginalKeys()); + } + + private function assertScalarListWithCustomValues(ChoiceListInterface $list) + { + $this->assertSame(array('a', 'b', '1', '2'), $list->getValues()); + + $this->assertSame(array( + 'a' => 'a', + 'b' => 'b', + 1 => 'c', + 2 => 'd', + ), $list->getChoices()); + + $this->assertSame(array( + 'a' => 'A', + 'b' => 'B', + 1 => 'C', + 2 => 'D', + ), $list->getOriginalKeys()); + } + + private function assertObjectListWithCustomValues(ChoiceListInterface $list) + { + $this->assertSame(array('a', 'b', '1', '2'), $list->getValues()); + + $this->assertSame(array( + 'a' => $this->obj1, + 'b' => $this->obj2, + 1 => $this->obj3, + 2 => $this->obj4, + ), $list->getChoices()); + + $this->assertSame(array( + 'a' => 'A', + 'b' => 'B', + 1 => 'C', + 2 => 'D', + ), $list->getOriginalKeys()); + } + + private function assertFlatView($view) + { + $this->assertEquals(new ChoiceListView( + array( + 0 => new ChoiceView($this->obj1, '0', 'A'), + 3 => new ChoiceView($this->obj4, '3', 'D'), + ), array( + 1 => new ChoiceView($this->obj2, '1', 'B'), + 2 => new ChoiceView($this->obj3, '2', 'C'), + ) + ), $view); + } + + private function assertFlatViewWithCustomIndices($view) + { + $this->assertEquals(new ChoiceListView( + array( + 'w' => new ChoiceView($this->obj1, '0', 'A'), + 'z' => new ChoiceView($this->obj4, '3', 'D'), + ), array( + 'x' => new ChoiceView($this->obj2, '1', 'B'), + 'y' => new ChoiceView($this->obj3, '2', 'C'), + ) + ), $view); + } + + private function assertFlatViewWithAttr($view) + { + $this->assertEquals(new ChoiceListView( + array( + 0 => new ChoiceView($this->obj1, '0', 'A'), + 3 => new ChoiceView($this->obj4, '3', 'D'), + ), array( + 1 => new ChoiceView( + $this->obj2, + '1', + 'B', + array('attr1' => 'value1') + ), + 2 => new ChoiceView( + $this->obj3, + '2', + 'C', + array('attr2' => 'value2') + ), + ) + ), $view); + } + + private function assertGroupedView($view) + { + $this->assertEquals(new ChoiceListView( + array( + 'Group 1' => new ChoiceGroupView( + 'Group 1', + array(0 => new ChoiceView($this->obj1, '0', 'A')) + ), + 'Group 2' => new ChoiceGroupView( + 'Group 2', + array(3 => new ChoiceView($this->obj4, '3', 'D')) + ), + ), array( + 'Group 1' => new ChoiceGroupView( + 'Group 1', + array(1 => new ChoiceView($this->obj2, '1', 'B')) + ), + 'Group 2' => new ChoiceGroupView( + 'Group 2', + array(2 => new ChoiceView($this->obj3, '2', 'C')) + ), + ) + ), $view); + } +} + +class DefaultChoiceListFactoryTest_Castable +{ + private $property; + + public function __construct($property) + { + $this->property = $property; + } + + public function __toString() + { + return $this->property; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php new file mode 100644 index 0000000..b9a77c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php @@ -0,0 +1,352 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList\Factory; + +use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; +use Symfony\Component\PropertyAccess\PropertyPath; + +/** + * @author Bernhard Schussek + */ +class PropertyAccessDecoratorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $decoratedFactory; + + /** + * @var PropertyAccessDecorator + */ + private $factory; + + protected function setUp() + { + $this->decoratedFactory = $this->getMock('Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface'); + $this->factory = new PropertyAccessDecorator($this->decoratedFactory); + } + + public function testCreateFromChoicesPropertyPath() + { + $choices = array((object) array('property' => 'value')); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($choices, $callback) { + return array_map($callback, $choices); + })); + + $this->assertSame(array('value'), $this->factory->createListFromChoices($choices, 'property')); + } + + public function testCreateFromChoicesPropertyPathInstance() + { + $choices = array((object) array('property' => 'value')); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($choices, $callback) { + return array_map($callback, $choices); + })); + + $this->assertSame(array('value'), $this->factory->createListFromChoices($choices, new PropertyPath('property'))); + } + + public function testCreateFromLoaderPropertyPath() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromLoader') + ->with($loader, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($loader, $callback) { + return $callback((object) array('property' => 'value')); + })); + + $this->assertSame('value', $this->factory->createListFromLoader($loader, 'property')); + } + + // https://github.com/symfony/symfony/issues/5494 + public function testCreateFromChoicesAssumeNullIfValuePropertyPathUnreadable() + { + $choices = array(null); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromChoices') + ->with($choices, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($choices, $callback) { + return array_map($callback, $choices); + })); + + $this->assertSame(array(null), $this->factory->createListFromChoices($choices, 'property')); + } + + // https://github.com/symfony/symfony/issues/5494 + public function testCreateFromChoiceLoaderAssumeNullIfValuePropertyPathUnreadable() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromLoader') + ->with($loader, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($loader, $callback) { + return $callback(null); + })); + + $this->assertNull($this->factory->createListFromLoader($loader, 'property')); + } + + public function testCreateFromLoaderPropertyPathInstance() + { + $loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createListFromLoader') + ->with($loader, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($loader, $callback) { + return $callback((object) array('property' => 'value')); + })); + + $this->assertSame('value', $this->factory->createListFromLoader($loader, new PropertyPath('property'))); + } + + public function testCreateViewPreferredChoicesAsPropertyPath() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred) { + return $preferred((object) array('property' => true)); + })); + + $this->assertTrue($this->factory->createView( + $list, + 'property' + )); + } + + public function testCreateViewPreferredChoicesAsPropertyPathInstance() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred) { + return $preferred((object) array('property' => true)); + })); + + $this->assertTrue($this->factory->createView( + $list, + new PropertyPath('property') + )); + } + + // https://github.com/symfony/symfony/issues/5494 + public function testCreateViewAssumeNullIfPreferredChoicesPropertyPathUnreadable() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred) { + return $preferred((object) array('category' => null)); + })); + + $this->assertFalse($this->factory->createView( + $list, + 'category.preferred' + )); + } + + public function testCreateViewLabelsAsPropertyPath() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label) { + return $label((object) array('property' => 'label')); + })); + + $this->assertSame('label', $this->factory->createView( + $list, + null, // preferred choices + 'property' + )); + } + + public function testCreateViewLabelsAsPropertyPathInstance() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label) { + return $label((object) array('property' => 'label')); + })); + + $this->assertSame('label', $this->factory->createView( + $list, + null, // preferred choices + new PropertyPath('property') + )); + } + + public function testCreateViewIndicesAsPropertyPath() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index) { + return $index((object) array('property' => 'index')); + })); + + $this->assertSame('index', $this->factory->createView( + $list, + null, // preferred choices + null, // label + 'property' + )); + } + + public function testCreateViewIndicesAsPropertyPathInstance() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index) { + return $index((object) array('property' => 'index')); + })); + + $this->assertSame('index', $this->factory->createView( + $list, + null, // preferred choices + null, // label + new PropertyPath('property') + )); + } + + public function testCreateViewGroupsAsPropertyPath() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy) { + return $groupBy((object) array('property' => 'group')); + })); + + $this->assertSame('group', $this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + 'property' + )); + } + + public function testCreateViewGroupsAsPropertyPathInstance() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy) { + return $groupBy((object) array('property' => 'group')); + })); + + $this->assertSame('group', $this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + new PropertyPath('property') + )); + } + + // https://github.com/symfony/symfony/issues/5494 + public function testCreateViewAssumeNullIfGroupsPropertyPathUnreadable() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy) { + return $groupBy((object) array('group' => null)); + })); + + $this->assertNull($this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + 'group.name' + )); + } + + public function testCreateViewAttrAsPropertyPath() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy, $attr) { + return $attr((object) array('property' => 'attr')); + })); + + $this->assertSame('attr', $this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + null, // groups + 'property' + )); + } + + public function testCreateViewAttrAsPropertyPathInstance() + { + $list = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + + $this->decoratedFactory->expects($this->once()) + ->method('createView') + ->with($list, null, null, null, null, $this->isInstanceOf('\Closure')) + ->will($this->returnCallback(function ($list, $preferred, $label, $index, $groupBy, $attr) { + return $attr((object) array('property' => 'attr')); + })); + + $this->assertSame('attr', $this->factory->createView( + $list, + null, // preferred choices + null, // label + null, // index + null, // groups + new PropertyPath('property') + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php new file mode 100644 index 0000000..5db96e6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ChoiceList/LazyChoiceListTest.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\ChoiceList; + +use Symfony\Component\Form\ChoiceList\LazyChoiceList; + +/** + * @author Bernhard Schussek + */ +class LazyChoiceListTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var LazyChoiceList + */ + private $list; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerList; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $loader; + + private $value; + + protected function setUp() + { + $this->innerList = $this->getMock('Symfony\Component\Form\ChoiceList\ChoiceListInterface'); + $this->loader = $this->getMock('Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'); + $this->value = function () {}; + $this->list = new LazyChoiceList($this->loader, $this->value); + } + + public function testGetChoicesLoadsInnerListOnFirstCall() + { + $this->loader->expects($this->once()) + ->method('loadChoiceList') + ->with($this->value) + ->will($this->returnValue($this->innerList)); + + $this->innerList->expects($this->exactly(2)) + ->method('getChoices') + ->will($this->returnValue('RESULT')); + + $this->assertSame('RESULT', $this->list->getChoices()); + $this->assertSame('RESULT', $this->list->getChoices()); + } + + public function testGetValuesLoadsInnerListOnFirstCall() + { + $this->loader->expects($this->once()) + ->method('loadChoiceList') + ->with($this->value) + ->will($this->returnValue($this->innerList)); + + $this->innerList->expects($this->exactly(2)) + ->method('getValues') + ->will($this->returnValue('RESULT')); + + $this->assertSame('RESULT', $this->list->getValues()); + $this->assertSame('RESULT', $this->list->getValues()); + } + + public function testGetStructuredValuesLoadsInnerListOnFirstCall() + { + $this->loader->expects($this->once()) + ->method('loadChoiceList') + ->with($this->value) + ->will($this->returnValue($this->innerList)); + + $this->innerList->expects($this->exactly(2)) + ->method('getStructuredValues') + ->will($this->returnValue('RESULT')); + + $this->assertSame('RESULT', $this->list->getStructuredValues()); + $this->assertSame('RESULT', $this->list->getStructuredValues()); + } + + public function testGetOriginalKeysLoadsInnerListOnFirstCall() + { + $this->loader->expects($this->once()) + ->method('loadChoiceList') + ->with($this->value) + ->will($this->returnValue($this->innerList)); + + $this->innerList->expects($this->exactly(2)) + ->method('getOriginalKeys') + ->will($this->returnValue('RESULT')); + + $this->assertSame('RESULT', $this->list->getOriginalKeys()); + $this->assertSame('RESULT', $this->list->getOriginalKeys()); + } + + public function testGetChoicesForValuesForwardsCallIfListNotLoaded() + { + $this->loader->expects($this->exactly(2)) + ->method('loadChoicesForValues') + ->with(array('a', 'b')) + ->will($this->returnValue('RESULT')); + + $this->assertSame('RESULT', $this->list->getChoicesForValues(array('a', 'b'))); + $this->assertSame('RESULT', $this->list->getChoicesForValues(array('a', 'b'))); + } + + public function testGetChoicesForValuesUsesLoadedList() + { + $this->loader->expects($this->once()) + ->method('loadChoiceList') + ->with($this->value) + ->will($this->returnValue($this->innerList)); + + $this->loader->expects($this->never()) + ->method('loadChoicesForValues'); + + $this->innerList->expects($this->exactly(2)) + ->method('getChoicesForValues') + ->with(array('a', 'b')) + ->will($this->returnValue('RESULT')); + + // load choice list + $this->list->getChoices(); + + $this->assertSame('RESULT', $this->list->getChoicesForValues(array('a', 'b'))); + $this->assertSame('RESULT', $this->list->getChoicesForValues(array('a', 'b'))); + } + + public function testGetValuesForChoicesForwardsCallIfListNotLoaded() + { + $this->loader->expects($this->exactly(2)) + ->method('loadValuesForChoices') + ->with(array('a', 'b')) + ->will($this->returnValue('RESULT')); + + $this->assertSame('RESULT', $this->list->getValuesForChoices(array('a', 'b'))); + $this->assertSame('RESULT', $this->list->getValuesForChoices(array('a', 'b'))); + } + + public function testGetValuesForChoicesUsesLoadedList() + { + $this->loader->expects($this->once()) + ->method('loadChoiceList') + ->with($this->value) + ->will($this->returnValue($this->innerList)); + + $this->loader->expects($this->never()) + ->method('loadValuesForChoices'); + + $this->innerList->expects($this->exactly(2)) + ->method('getValuesForChoices') + ->with(array('a', 'b')) + ->will($this->returnValue('RESULT')); + + // load choice list + $this->list->getChoices(); + + $this->assertSame('RESULT', $this->list->getValuesForChoices(array('a', 'b'))); + $this->assertSame('RESULT', $this->list->getValuesForChoices(array('a', 'b'))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php new file mode 100644 index 0000000..f85568f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormPerformanceTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +/** + * @author Bernhard Schussek + */ +class CompoundFormPerformanceTest extends \Symfony\Component\Form\Test\FormPerformanceTestCase +{ + /** + * Create a compound form multiple times, as happens in a collection form. + * + * @group benchmark + */ + public function testArrayBasedForm() + { + $this->setMaxRunningTime(1); + + for ($i = 0; $i < 40; ++$i) { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('gender', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array( + 'choices' => array('male' => 'Male', 'female' => 'Female'), + 'required' => false, + )) + ->add('age', 'Symfony\Component\Form\Extension\Core\Type\NumberType') + ->add('birthDate', 'Symfony\Component\Form\Extension\Core\Type\BirthdayType') + ->add('city', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array( + // simulate 300 different cities + 'choices' => range(1, 300), + )) + ->getForm(); + + // load the form into a view + $form->createView(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormTest.php new file mode 100644 index 0000000..bfe7166 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -0,0 +1,1022 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Forms; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\SubmitButtonBuilder; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; + +class CompoundFormTest extends AbstractFormTest +{ + public function testValidIfAllChildrenAreValid() + { + $this->form->add($this->getBuilder('firstName')->getForm()); + $this->form->add($this->getBuilder('lastName')->getForm()); + + $this->form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + + $this->assertTrue($this->form->isValid()); + } + + public function testInvalidIfChildIsInvalid() + { + $this->form->add($this->getBuilder('firstName')->getForm()); + $this->form->add($this->getBuilder('lastName')->getForm()); + + $this->form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + + $this->form->get('lastName')->addError(new FormError('Invalid')); + + $this->assertFalse($this->form->isValid()); + } + + public function testDisabledFormsValidEvenIfChildrenInvalid() + { + $form = $this->getBuilder('person') + ->setDisabled(true) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('name')) + ->getForm(); + + $form->submit(array('name' => 'Jacques Doe')); + + $form->get('name')->addError(new FormError('Invalid')); + + $this->assertTrue($form->isValid()); + } + + public function testSubmitForwardsNullIfNotClearMissingButValueIsExplicitlyNull() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->once()) + ->method('submit') + ->with($this->equalTo(null)); + + $this->form->submit(array('firstName' => null), false); + } + + public function testSubmitForwardsNullIfValueIsMissing() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->once()) + ->method('submit') + ->with($this->equalTo(null)); + + $this->form->submit(array()); + } + + public function testSubmitDoesNotForwardNullIfNotClearMissing() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->never()) + ->method('submit'); + + $this->form->submit(array(), false); + } + + public function testSubmitDoesNotAddExtraFieldForNullValues() + { + $factory = Forms::createFormFactoryBuilder() + ->getFormFactory(); + + $child = $factory->createNamed('file', 'Symfony\Component\Form\Extension\Core\Type\FileType', null, array('auto_initialize' => false)); + + $this->form->add($child); + $this->form->submit(array('file' => null), false); + + $this->assertCount(0, $this->form->getExtraData()); + } + + public function testClearMissingFlagIsForwarded() + { + $child = $this->getMockForm('firstName'); + + $this->form->add($child); + + $child->expects($this->once()) + ->method('submit') + ->with($this->equalTo('foo'), false); + + $this->form->submit(array('firstName' => 'foo'), false); + } + + public function testCloneChildren() + { + $child = $this->getBuilder('child')->getForm(); + $this->form->add($child); + + $clone = clone $this->form; + + $this->assertNotSame($this->form, $clone); + $this->assertNotSame($child, $clone['child']); + $this->assertNotSame($this->form['child'], $clone['child']); + } + + public function testNotEmptyIfChildNotEmpty() + { + $child = $this->getMockForm(); + $child->expects($this->once()) + ->method('isEmpty') + ->will($this->returnValue(false)); + + $this->form->setData(null); + $this->form->add($child); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testAdd() + { + $child = $this->getBuilder('foo')->getForm(); + $this->form->add($child); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingNameAndType() + { + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + $this->form->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('bar' => 'baz')); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingIntegerNameAndType() + { + $child = $this->getBuilder(0)->getForm(); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with('0', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + // in order to make casting unnecessary + $this->form->add(0, 'Symfony\Component\Form\Extension\Core\Type\TextType', array('bar' => 'baz')); + + $this->assertTrue($this->form->has(0)); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array(0 => $child), $this->form->all()); + } + + public function testAddWithoutType() + { + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->will($this->returnValue($child)); + + $this->form->add('foo'); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingNameButNoType() + { + $this->form = $this->getBuilder('name', null, '\stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createForProperty') + ->with('\stdClass', 'foo') + ->will($this->returnValue($child)); + + $this->form->add('foo'); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + public function testAddUsingNameButNoTypeAndOptions() + { + $this->form = $this->getBuilder('name', null, '\stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + + $child = $this->getBuilder('foo')->getForm(); + + $this->factory->expects($this->once()) + ->method('createForProperty') + ->with('\stdClass', 'foo', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) + ->will($this->returnValue($child)); + + $this->form->add('foo', null, array('bar' => 'baz')); + + $this->assertTrue($this->form->has('foo')); + $this->assertSame($this->form, $child->getParent()); + $this->assertSame(array('foo' => $child), $this->form->all()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testAddThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->add($this->getBuilder('foo')->getForm()); + } + + public function testRemove() + { + $child = $this->getBuilder('foo')->getForm(); + $this->form->add($child); + $this->form->remove('foo'); + + $this->assertNull($child->getParent()); + $this->assertCount(0, $this->form); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testRemoveThrowsExceptionIfAlreadySubmitted() + { + $this->form->add($this->getBuilder('foo')->setCompound(false)->getForm()); + $this->form->submit(array('foo' => 'bar')); + $this->form->remove('foo'); + } + + public function testRemoveIgnoresUnknownName() + { + $this->form->remove('notexisting'); + } + + public function testArrayAccess() + { + $child = $this->getBuilder('foo')->getForm(); + + $this->form[] = $child; + + $this->assertTrue(isset($this->form['foo'])); + $this->assertSame($child, $this->form['foo']); + + unset($this->form['foo']); + + $this->assertFalse(isset($this->form['foo'])); + } + + public function testCountable() + { + $this->form->add($this->getBuilder('foo')->getForm()); + $this->form->add($this->getBuilder('bar')->getForm()); + + $this->assertCount(2, $this->form); + } + + public function testIterator() + { + $this->form->add($this->getBuilder('foo')->getForm()); + $this->form->add($this->getBuilder('bar')->getForm()); + + $this->assertSame($this->form->all(), iterator_to_array($this->form)); + } + + public function testAddMapsViewDataToFormIfInitialized() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->setData('foo') + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->once()) + ->method('mapDataToForms') + ->with('bar', $this->isInstanceOf('\RecursiveIteratorIterator')) + ->will($this->returnCallback(function ($data, \RecursiveIteratorIterator $iterator) use ($child) { + $this->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $this->assertSame(array($child->getName() => $child), iterator_to_array($iterator)); + })); + + $form->initialize(); + $form->add($child); + } + + public function testAddDoesNotMapViewDataToFormIfNotInitialized() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->never()) + ->method('mapDataToForms'); + + $form->add($child); + } + + public function testAddDoesNotMapViewDataToFormIfInheritData() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->setInheritData(true) + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->never()) + ->method('mapDataToForms'); + + $form->initialize(); + $form->add($child); + } + + public function testSetDataSupportsDynamicAdditionAndRemovalOfChildren() + { + $form = $this->getBuilder() + ->setCompound(true) + // We test using PropertyPathMapper on purpose. The traversal logic + // is currently contained in InheritDataAwareIterator, but even + // if that changes, this test should still function. + ->setDataMapper(new PropertyPathMapper()) + ->getForm(); + + $child = $this->getMockForm('child'); + $childToBeRemoved = $this->getMockForm('removed'); + $childToBeAdded = $this->getMockForm('added'); + + $form->add($child); + $form->add($childToBeRemoved); + + $child->expects($this->once()) + ->method('setData') + ->will($this->returnCallback(function () use ($form, $childToBeAdded) { + $form->remove('removed'); + $form->add($childToBeAdded); + })); + + $childToBeRemoved->expects($this->never()) + ->method('setData'); + + // once when it it is created, once when it is added + $childToBeAdded->expects($this->exactly(2)) + ->method('setData'); + + // pass NULL to all children + $form->setData(array()); + } + + public function testSetDataMapsViewDataToChildren() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->getForm()); + + $mapper->expects($this->once()) + ->method('mapDataToForms') + ->with('bar', $this->isInstanceOf('\RecursiveIteratorIterator')) + ->will($this->returnCallback(function ($data, \RecursiveIteratorIterator $iterator) use ($child1, $child2) { + $this->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $this->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator)); + })); + + $form->setData('foo'); + } + + public function testSubmitSupportsDynamicAdditionAndRemovalOfChildren() + { + $child = $this->getMockForm('child'); + $childToBeRemoved = $this->getMockForm('removed'); + $childToBeAdded = $this->getMockForm('added'); + + $this->form->add($child); + $this->form->add($childToBeRemoved); + + $form = $this->form; + + $child->expects($this->once()) + ->method('submit') + ->will($this->returnCallback(function () use ($form, $childToBeAdded) { + $form->remove('removed'); + $form->add($childToBeAdded); + })); + + $childToBeRemoved->expects($this->never()) + ->method('submit'); + + $childToBeAdded->expects($this->once()) + ->method('submit'); + + // pass NULL to all children + $this->form->submit(array()); + } + + public function testSubmitMapsSubmittedChildrenOntoExistingViewData() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->setData('foo') + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->setCompound(false)->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->setCompound(false)->getForm()); + + $mapper->expects($this->once()) + ->method('mapFormsToData') + ->with($this->isInstanceOf('\RecursiveIteratorIterator'), 'bar') + ->will($this->returnCallback(function (\RecursiveIteratorIterator $iterator) use ($child1, $child2) { + $this->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $this->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator)); + $this->assertEquals('Bernhard', $child1->getData()); + $this->assertEquals('Schussek', $child2->getData()); + })); + + $form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + } + + public function testMapFormsToDataIsNotInvokedIfInheritData() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->setInheritData(true) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => 'bar', + ))) + ->getForm(); + + $form->add($child1 = $this->getBuilder('firstName')->setCompound(false)->getForm()); + $form->add($child2 = $this->getBuilder('lastName')->setCompound(false)->getForm()); + + $mapper->expects($this->never()) + ->method('mapFormsToData'); + + $form->submit(array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + )); + } + + /* + * https://github.com/symfony/symfony/issues/4480 + */ + public function testSubmitRestoresViewDataIfCompoundAndEmpty() + { + $mapper = $this->getDataMapper(); + $object = new \stdClass(); + $form = $this->getBuilder('name', null, 'stdClass') + ->setCompound(true) + ->setDataMapper($mapper) + ->setData($object) + ->getForm(); + + $form->submit(array()); + + $this->assertSame($object, $form->getData()); + } + + public function testSubmitMapsSubmittedChildrenOntoEmptyData() + { + $mapper = $this->getDataMapper(); + $object = new \stdClass(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->setEmptyData($object) + ->setData(null) + ->getForm(); + + $form->add($child = $this->getBuilder('name')->setCompound(false)->getForm()); + + $mapper->expects($this->once()) + ->method('mapFormsToData') + ->with($this->isInstanceOf('\RecursiveIteratorIterator'), $object) + ->will($this->returnCallback(function (\RecursiveIteratorIterator $iterator) use ($child) { + $this->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator()); + $this->assertSame(array('name' => $child), iterator_to_array($iterator)); + })); + + $form->submit(array( + 'name' => 'Bernhard', + )); + } + + public function requestMethodProvider() + { + return array( + array('POST'), + array('PUT'), + array('DELETE'), + array('PATCH'), + ); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequest($method) + { + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $values = array( + 'author' => array( + 'name' => 'Bernhard', + 'image' => array('filename' => 'foobar.png'), + ), + ); + + $files = array( + 'author' => array( + 'error' => array('image' => UPLOAD_ERR_OK), + 'name' => array('image' => 'upload.png'), + 'size' => array('image' => 123), + 'tmp_name' => array('image' => $path), + 'type' => array('image' => 'image/png'), + ), + ); + + $request = new Request(array(), $values, array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('author') + ->setMethod($method) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('name')->getForm()); + $form->add($this->getBuilder('image')->getForm()); + + $form->handleRequest($request); + + $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + + $this->assertEquals('Bernhard', $form['name']->getData()); + $this->assertEquals($file, $form['image']->getData()); + + unlink($path); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequestWithEmptyRootFormName($method) + { + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $values = array( + 'name' => 'Bernhard', + 'extra' => 'data', + ); + + $files = array( + 'image' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'upload.png', + 'size' => 123, + 'tmp_name' => $path, + 'type' => 'image/png', + ), + ); + + $request = new Request(array(), $values, array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('') + ->setMethod($method) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('name')->getForm()); + $form->add($this->getBuilder('image')->getForm()); + + $form->handleRequest($request); + + $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + + $this->assertEquals('Bernhard', $form['name']->getData()); + $this->assertEquals($file, $form['image']->getData()); + $this->assertEquals(array('extra' => 'data'), $form->getExtraData()); + + unlink($path); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequestWithSingleChildForm($method) + { + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $files = array( + 'image' => array( + 'error' => UPLOAD_ERR_OK, + 'name' => 'upload.png', + 'size' => 123, + 'tmp_name' => $path, + 'type' => 'image/png', + ), + ); + + $request = new Request(array(), array(), array(), array(), $files, array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('image') + ->setMethod($method) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + + $form->handleRequest($request); + + $file = new UploadedFile($path, 'upload.png', 'image/png', 123, UPLOAD_ERR_OK); + + $this->assertEquals($file, $form->getData()); + + unlink($path); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSubmitPostOrPutRequestWithSingleChildFormUploadedFile($method) + { + $path = tempnam(sys_get_temp_dir(), 'sf2'); + touch($path); + + $values = array( + 'name' => 'Bernhard', + ); + + $request = new Request(array(), $values, array(), array(), array(), array( + 'REQUEST_METHOD' => $method, + )); + + $form = $this->getBuilder('name') + ->setMethod($method) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + + $form->handleRequest($request); + + $this->assertEquals('Bernhard', $form->getData()); + + unlink($path); + } + + public function testSubmitGetRequest() + { + $values = array( + 'author' => array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + ), + ); + + $request = new Request($values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $form = $this->getBuilder('author') + ->setMethod('GET') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('firstName')->getForm()); + $form->add($this->getBuilder('lastName')->getForm()); + + $form->handleRequest($request); + + $this->assertEquals('Bernhard', $form['firstName']->getData()); + $this->assertEquals('Schussek', $form['lastName']->getData()); + } + + public function testSubmitGetRequestWithEmptyRootFormName() + { + $values = array( + 'firstName' => 'Bernhard', + 'lastName' => 'Schussek', + 'extra' => 'data', + ); + + $request = new Request($values, array(), array(), array(), array(), array( + 'REQUEST_METHOD' => 'GET', + )); + + $form = $this->getBuilder('') + ->setMethod('GET') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setRequestHandler(new HttpFoundationRequestHandler()) + ->getForm(); + $form->add($this->getBuilder('firstName')->getForm()); + $form->add($this->getBuilder('lastName')->getForm()); + + $form->handleRequest($request); + + $this->assertEquals('Bernhard', $form['firstName']->getData()); + $this->assertEquals('Schussek', $form['lastName']->getData()); + $this->assertEquals(array('extra' => 'data'), $form->getExtraData()); + } + + public function testGetErrors() + { + $this->form->addError($error1 = new FormError('Error 1')); + $this->form->addError($error2 = new FormError('Error 2')); + + $errors = $this->form->getErrors(); + + $this->assertSame( + "ERROR: Error 1\n". + "ERROR: Error 2\n", + (string) $errors + ); + + $this->assertSame(array($error1, $error2), iterator_to_array($errors)); + } + + public function testGetErrorsDeep() + { + $this->form->addError($error1 = new FormError('Error 1')); + $this->form->addError($error2 = new FormError('Error 2')); + + $childForm = $this->getBuilder('Child')->getForm(); + $childForm->addError($nestedError = new FormError('Nested Error')); + $this->form->add($childForm); + + $errors = $this->form->getErrors(true); + + $this->assertSame( + "ERROR: Error 1\n". + "ERROR: Error 2\n". + "ERROR: Nested Error\n", + (string) $errors + ); + + $this->assertSame( + array($error1, $error2, $nestedError), + iterator_to_array($errors) + ); + } + + public function testGetErrorsDeepRecursive() + { + $this->form->addError($error1 = new FormError('Error 1')); + $this->form->addError($error2 = new FormError('Error 2')); + + $childForm = $this->getBuilder('Child')->getForm(); + $childForm->addError($nestedError = new FormError('Nested Error')); + $this->form->add($childForm); + + $errors = $this->form->getErrors(true, false); + + $this->assertSame( + "ERROR: Error 1\n". + "ERROR: Error 2\n". + "Child:\n". + " ERROR: Nested Error\n", + (string) $errors + ); + + $errorsAsArray = iterator_to_array($errors); + + $this->assertSame($error1, $errorsAsArray[0]); + $this->assertSame($error2, $errorsAsArray[1]); + $this->assertInstanceOf('Symfony\Component\Form\FormErrorIterator', $errorsAsArray[2]); + + $nestedErrorsAsArray = iterator_to_array($errorsAsArray[2]); + + $this->assertCount(1, $nestedErrorsAsArray); + $this->assertSame($nestedError, $nestedErrorsAsArray[0]); + } + + // Basic cases are covered in SimpleFormTest + public function testCreateViewWithChildren() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $options = array('a' => 'Foo', 'b' => 'Bar'); + $field1 = $this->getMockForm('foo'); + $field2 = $this->getMockForm('bar'); + $view = new FormView(); + $field1View = new FormView(); + $field2View = new FormView(); + + $this->form = $this->getBuilder('form', null, null, $options) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setType($type) + ->getForm(); + $this->form->add($field1); + $this->form->add($field2); + + $assertChildViewsEqual = function (array $childViews) { + return function (FormView $view) use ($childViews) { + $this->assertSame($childViews, $view->children); + }; + }; + + // First create the view + $type->expects($this->once()) + ->method('createView') + ->will($this->returnValue($view)); + + // Then build it for the form itself + $type->expects($this->once()) + ->method('buildView') + ->with($view, $this->form, $options) + ->will($this->returnCallback($assertChildViewsEqual(array()))); + + // Then add the first child form + $field1->expects($this->once()) + ->method('createView') + ->will($this->returnValue($field1View)); + + // Then the second child form + $field2->expects($this->once()) + ->method('createView') + ->will($this->returnValue($field2View)); + + // Again build the view for the form itself. This time the child views + // exist. + $type->expects($this->once()) + ->method('finishView') + ->with($view, $this->form, $options) + ->will($this->returnCallback($assertChildViewsEqual(array('foo' => $field1View, 'bar' => $field2View)))); + + $this->assertSame($view, $this->form->createView()); + } + + public function testNoClickedButtonBeforeSubmission() + { + $this->assertNull($this->form->getClickedButton()); + } + + public function testNoClickedButton() + { + $button = $this->getMockBuilder('Symfony\Component\Form\SubmitButton') + ->setConstructorArgs(array(new SubmitButtonBuilder('submit'))) + ->setMethods(array('isClicked')) + ->getMock(); + + $button->expects($this->any()) + ->method('isClicked') + ->will($this->returnValue(false)); + + $parentForm = $this->getBuilder('parent')->getForm(); + $nestedForm = $this->getBuilder('nested')->getForm(); + + $this->form->setParent($parentForm); + $this->form->add($button); + $this->form->add($nestedForm); + $this->form->submit(array()); + + $this->assertNull($this->form->getClickedButton()); + } + + public function testClickedButton() + { + $button = $this->getMockBuilder('Symfony\Component\Form\SubmitButton') + ->setConstructorArgs(array(new SubmitButtonBuilder('submit'))) + ->setMethods(array('isClicked')) + ->getMock(); + + $button->expects($this->any()) + ->method('isClicked') + ->will($this->returnValue(true)); + + $this->form->add($button); + $this->form->submit(array()); + + $this->assertSame($button, $this->form->getClickedButton()); + } + + public function testClickedButtonFromNestedForm() + { + $button = $this->getBuilder('submit')->getForm(); + + $nestedForm = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($this->getBuilder('nested'))) + ->setMethods(array('getClickedButton')) + ->getMock(); + + $nestedForm->expects($this->any()) + ->method('getClickedButton') + ->will($this->returnValue($button)); + + $this->form->add($nestedForm); + $this->form->submit(array()); + + $this->assertSame($button, $this->form->getClickedButton()); + } + + public function testClickedButtonFromParentForm() + { + $button = $this->getBuilder('submit')->getForm(); + + $parentForm = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($this->getBuilder('parent'))) + ->setMethods(array('getClickedButton')) + ->getMock(); + + $parentForm->expects($this->any()) + ->method('getClickedButton') + ->will($this->returnValue($button)); + + $this->form->setParent($parentForm); + $this->form->submit(array()); + + $this->assertSame($button, $this->form->getClickedButton()); + } + + protected function createForm() + { + return $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php new file mode 100644 index 0000000..0597de7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -0,0 +1,361 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataMapper; + +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormConfigInterface; +use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; + +class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var PropertyPathMapper + */ + private $mapper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $propertyAccessor; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->propertyAccessor = $this->getMock('Symfony\Component\PropertyAccess\PropertyAccessorInterface'); + $this->mapper = new PropertyPathMapper($this->propertyAccessor); + } + + /** + * @param $path + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getPropertyPath($path) + { + return $this->getMockBuilder('Symfony\Component\PropertyAccess\PropertyPath') + ->setConstructorArgs(array($path)) + ->setMethods(array('getValue', 'setValue')) + ->getMock(); + } + + /** + * @param FormConfigInterface $config + * @param bool $synchronized + * @param bool $submitted + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getForm(FormConfigInterface $config, $synchronized = true, $submitted = true) + { + $form = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($config)) + ->setMethods(array('isSynchronized', 'isSubmitted')) + ->getMock(); + + $form->expects($this->any()) + ->method('isSynchronized') + ->will($this->returnValue($synchronized)); + + $form->expects($this->any()) + ->method('isSubmitted') + ->will($this->returnValue($submitted)); + + return $form; + } + + public function testMapDataToFormsPassesObjectRefIfByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->with($car, $propertyPath) + ->will($this->returnValue($engine)); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms($car, array($form)); + + // Can't use isIdentical() above because mocks always clone their + // arguments which can't be disabled in PHPUnit 3.6 + $this->assertSame($engine, $form->getData()); + } + + public function testMapDataToFormsPassesObjectCloneIfNotByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->with($car, $propertyPath) + ->will($this->returnValue($engine)); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms($car, array($form)); + + $this->assertNotSame($engine, $form->getData()); + $this->assertEquals($engine, $form->getData()); + } + + public function testMapDataToFormsIgnoresEmptyPropertyPath() + { + $car = new \stdClass(); + + $config = new FormConfigBuilder(null, '\stdClass', $this->dispatcher); + $config->setByReference(true); + $form = $this->getForm($config); + + $this->assertNull($form->getPropertyPath()); + + $this->mapper->mapDataToForms($car, array($form)); + + $this->assertNull($form->getData()); + } + + public function testMapDataToFormsIgnoresUnmapped() + { + $car = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('getValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setMapped(false); + $config->setPropertyPath($propertyPath); + $form = $this->getForm($config); + + $this->mapper->mapDataToForms($car, array($form)); + + $this->assertNull($form->getData()); + } + + public function testMapDataToFormsSetsDefaultDataIfPassedDataIsNull() + { + $default = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('getValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($default); + + $form = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($config)) + ->setMethods(array('setData')) + ->getMock(); + + $form->expects($this->once()) + ->method('setData') + ->with($default); + + $this->mapper->mapDataToForms(null, array($form)); + } + + public function testMapDataToFormsSetsDefaultDataIfPassedDataIsEmptyArray() + { + $default = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('getValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($default); + + $form = $this->getMockBuilder('Symfony\Component\Form\Form') + ->setConstructorArgs(array($config)) + ->setMethods(array('setData')) + ->getMock(); + + $form->expects($this->once()) + ->method('setData') + ->with($default); + + $this->mapper->mapDataToForms(array(), array($form)); + } + + public function testMapFormsToDataWritesBackIfNotByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('setValue') + ->with($car, $propertyPath, $engine); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataWritesBackIfByReferenceButNoReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->once()) + ->method('setValue') + ->with($car, $propertyPath, $engine); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataWritesBackIfByReferenceAndReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + // $car already contains the reference of $engine + $this->propertyAccessor->expects($this->once()) + ->method('getValue') + ->with($car, $propertyPath) + ->will($this->returnValue($engine)); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresUnmapped() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $config->setMapped(false); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresUnsubmittedForms() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config, true, false); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresEmptyData() + { + $car = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData(null); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresUnsynchronized() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = $this->getForm($config, false); + + $this->mapper->mapFormsToData(array($form), $car); + } + + public function testMapFormsToDataIgnoresDisabled() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = $this->getPropertyPath('engine'); + + $this->propertyAccessor->expects($this->never()) + ->method('setValue'); + + $config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $config->setDisabled(true); + $form = $this->getForm($config); + + $this->mapper->mapFormsToData(array($form), $car); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php new file mode 100644 index 0000000..3377926 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ArrayToPartsTransformerTest.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer; + +class ArrayToPartsTransformerTest extends \PHPUnit_Framework_TestCase +{ + private $transformer; + + protected function setUp() + { + $this->transformer = new ArrayToPartsTransformer(array( + 'first' => array('a', 'b', 'c'), + 'second' => array('d', 'e', 'f'), + )); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $input = array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + 'd' => '4', + 'e' => '5', + 'f' => '6', + ); + + $output = array( + 'first' => array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + ), + 'second' => array( + 'd' => '4', + 'e' => '5', + 'f' => '6', + ), + ); + + $this->assertSame($output, $this->transformer->transform($input)); + } + + public function testTransformEmpty() + { + $output = array( + 'first' => null, + 'second' => null, + ); + + $this->assertSame($output, $this->transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresArray() + { + $this->transformer->transform('12345'); + } + + public function testReverseTransform() + { + $input = array( + 'first' => array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + ), + 'second' => array( + 'd' => '4', + 'e' => '5', + 'f' => '6', + ), + ); + + $output = array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + 'd' => '4', + 'e' => '5', + 'f' => '6', + ); + + $this->assertSame($output, $this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmpty() + { + $input = array( + 'first' => '', + 'second' => '', + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyNull() + { + $input = array( + 'first' => null, + 'second' => null, + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyNull() + { + $input = array( + 'first' => array( + 'a' => '1', + 'b' => '2', + 'c' => '3', + ), + 'second' => null, + ); + + $this->transformer->reverseTransform($input); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresArray() + { + $this->transformer->reverseTransform('12345'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BaseDateTimeTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BaseDateTimeTransformerTest.php new file mode 100644 index 0000000..7eb716b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BaseDateTimeTransformerTest.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +class BaseDateTimeTransformerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + * @expectedExceptionMessage this_timezone_does_not_exist + */ + public function testConstructFailsIfInputTimezoneIsInvalid() + { + $this->getMock( + 'Symfony\Component\Form\Extension\Core\DataTransformer\BaseDateTimeTransformer', + array(), + array('this_timezone_does_not_exist') + ); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + * @expectedExceptionMessage that_timezone_does_not_exist + */ + public function testConstructFailsIfOutputTimezoneIsInvalid() + { + $this->getMock( + 'Symfony\Component\Form\Extension\Core\DataTransformer\BaseDateTimeTransformer', + array(), + array(null, 'that_timezone_does_not_exist') + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php new file mode 100644 index 0000000..a121778 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransformer; + +class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + const TRUE_VALUE = '1'; + + /** + * @var BooleanToStringTransformer + */ + protected $transformer; + + protected function setUp() + { + $this->transformer = new BooleanToStringTransformer(self::TRUE_VALUE); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $this->assertEquals(self::TRUE_VALUE, $this->transformer->transform(true)); + $this->assertNull($this->transformer->transform(false)); + } + + // https://github.com/symfony/symfony/issues/8989 + public function testTransformAcceptsNull() + { + $this->assertNull($this->transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformFailsIfString() + { + $this->transformer->transform('1'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformFailsIfInteger() + { + $this->transformer->reverseTransform(1); + } + + public function testReverseTransform() + { + $this->assertTrue($this->transformer->reverseTransform(self::TRUE_VALUE)); + $this->assertTrue($this->transformer->reverseTransform('foobar')); + $this->assertTrue($this->transformer->reverseTransform('')); + $this->assertFalse($this->transformer->reverseTransform(null)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php new file mode 100644 index 0000000..f60ef05 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\ChoiceList\ArrayChoiceList; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; + +class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected $transformer; + + protected function setUp() + { + $list = new ArrayChoiceList(array('', false, 'X')); + + $this->transformer = new ChoiceToValueTransformer($list); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function transformProvider() + { + return array( + // more extensive test set can be found in FormUtilTest + array('', '0'), + array(false, '1'), + ); + } + + /** + * @dataProvider transformProvider + */ + public function testTransform($in, $out) + { + $this->assertSame($out, $this->transformer->transform($in)); + } + + public function reverseTransformProvider() + { + return array( + // values are expected to be valid choice keys already and stay + // the same + array('0', ''), + array('1', false), + array('2', 'X'), + ); + } + + /** + * @dataProvider reverseTransformProvider + */ + public function testReverseTransform($in, $out) + { + $this->assertSame($out, $this->transformer->reverseTransform($in)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsScalar() + { + $this->transformer->reverseTransform(array()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php new file mode 100644 index 0000000..f7747aa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\ChoiceList\ArrayChoiceList; +use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; + +class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected $transformer; + + protected function setUp() + { + $list = new ArrayChoiceList(array('', false, 'X')); + $this->transformer = new ChoicesToValuesTransformer($list); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $in = array('', false, 'X'); + $out = array('0', '1', '2'); + + $this->assertSame($out, $this->transformer->transform($in)); + } + + public function testTransformNull() + { + $this->assertSame(array(), $this->transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformExpectsArray() + { + $this->transformer->transform('foobar'); + } + + public function testReverseTransform() + { + // values are expected to be valid choices and stay the same + $in = array('0', '1', '2'); + $out = array('', false, 'X'); + + $this->assertSame($out, $this->transformer->reverseTransform($in)); + } + + public function testReverseTransformNull() + { + $this->assertSame(array(), $this->transformer->reverseTransform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsArray() + { + $this->transformer->reverseTransform('foobar'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php new file mode 100644 index 0000000..2ee2f22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DataTransformerChainTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; + +class DataTransformerChainTest extends \PHPUnit_Framework_TestCase +{ + public function testTransform() + { + $transformer1 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer1->expects($this->once()) + ->method('transform') + ->with($this->identicalTo('foo')) + ->will($this->returnValue('bar')); + $transformer2 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer2->expects($this->once()) + ->method('transform') + ->with($this->identicalTo('bar')) + ->will($this->returnValue('baz')); + + $chain = new DataTransformerChain(array($transformer1, $transformer2)); + + $this->assertEquals('baz', $chain->transform('foo')); + } + + public function testReverseTransform() + { + $transformer2 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer2->expects($this->once()) + ->method('reverseTransform') + ->with($this->identicalTo('foo')) + ->will($this->returnValue('bar')); + $transformer1 = $this->getMock('Symfony\Component\Form\DataTransformerInterface'); + $transformer1->expects($this->once()) + ->method('reverseTransform') + ->with($this->identicalTo('bar')) + ->will($this->returnValue('baz')); + + $chain = new DataTransformerChain(array($transformer1, $transformer2)); + + $this->assertEquals('baz', $chain->reverseTransform('foo')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php new file mode 100644 index 0000000..200cbc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeTestCase.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +abstract class DateTimeTestCase extends \PHPUnit_Framework_TestCase +{ + public static function assertDateTimeEquals(\DateTime $expected, \DateTime $actual) + { + self::assertEquals($expected->format('U'), $actual->format('U')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php new file mode 100644 index 0000000..7b81f47 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToArrayTransformerTest.php @@ -0,0 +1,580 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; + +class DateTimeToArrayTransformerTest extends DateTimeTestCase +{ + public function testTransform() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC'); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + + $output = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToArrayTransformer(); + + $output = array( + 'year' => '', + 'month' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'second' => '', + ); + + $this->assertSame($output, $transformer->transform(null)); + } + + public function testTransformEmptyWithFields() + { + $transformer = new DateTimeToArrayTransformer(null, null, array('year', 'minute', 'second')); + + $output = array( + 'year' => '', + 'minute' => '', + 'second' => '', + ); + + $this->assertSame($output, $transformer->transform(null)); + } + + public function testTransformWithFields() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC', array('year', 'month', 'minute', 'second')); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + + $output = array( + 'year' => '2010', + 'month' => '2', + 'minute' => '5', + 'second' => '6', + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformWithPadding() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC', null, true); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + + $output = array( + 'year' => '2010', + 'month' => '02', + 'day' => '03', + 'hour' => '04', + 'minute' => '05', + 'second' => '06', + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformDifferentTimezones() + { + $transformer = new DateTimeToArrayTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = new \DateTime('2010-02-03 04:05:06 America/New_York'); + + $dateTime = new \DateTime('2010-02-03 04:05:06 America/New_York'); + $dateTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + $output = array( + 'year' => (string) (int) $dateTime->format('Y'), + 'month' => (string) (int) $dateTime->format('m'), + 'day' => (string) (int) $dateTime->format('d'), + 'hour' => (string) (int) $dateTime->format('H'), + 'minute' => (string) (int) $dateTime->format('i'), + 'second' => (string) (int) $dateTime->format('s'), + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + public function testTransformDateTimeImmutable() + { + $transformer = new DateTimeToArrayTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = new \DateTimeImmutable('2010-02-03 04:05:06 America/New_York'); + + $dateTime = new \DateTimeImmutable('2010-02-03 04:05:06 America/New_York'); + $dateTime = $dateTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + $output = array( + 'year' => (string) (int) $dateTime->format('Y'), + 'month' => (string) (int) $dateTime->format('m'), + 'day' => (string) (int) $dateTime->format('d'), + 'hour' => (string) (int) $dateTime->format('H'), + 'minute' => (string) (int) $dateTime->format('i'), + 'second' => (string) (int) $dateTime->format('s'), + ); + + $this->assertSame($output, $transformer->transform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresDateTime() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform('12345'); + } + + public function testReverseTransform() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $output = new \DateTime('2010-02-03 04:05:06 UTC'); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformWithSomeZero() + { + $transformer = new DateTimeToArrayTransformer('UTC', 'UTC'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '0', + 'second' => '0', + ); + + $output = new \DateTime('2010-02-03 04:00:00 UTC'); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmpty() + { + $transformer = new DateTimeToArrayTransformer(); + + $input = array( + 'year' => '', + 'month' => '', + 'day' => '', + 'hour' => '', + 'minute' => '', + 'second' => '', + ); + + $this->assertNull($transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmptySubsetOfFields() + { + $transformer = new DateTimeToArrayTransformer(null, null, array('year', 'month', 'day')); + + $input = array( + 'year' => '', + 'month' => '', + 'day' => '', + ); + + $this->assertNull($transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyYear() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyHour() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptyMinute() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyEmptySecond() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + )); + } + + public function testReverseTransformNull() + { + $transformer = new DateTimeToArrayTransformer(); + + $this->assertNull($transformer->reverseTransform(null)); + } + + public function testReverseTransformDifferentTimezones() + { + $transformer = new DateTimeToArrayTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $output = new \DateTime('2010-02-03 04:05:06 Asia/Hong_Kong'); + $output->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformToDifferentTimezone() + { + $transformer = new DateTimeToArrayTransformer('Asia/Hong_Kong', 'UTC'); + + $input = array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + ); + + $output = new \DateTime('2010-02-03 04:05:06 UTC'); + $output->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertDateTimeEquals($output, $transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresArray() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform('12345'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeYear() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '-1', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '-1', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '-1', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeHour() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '-1', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeMinute() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '-1', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNegativeSecond() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '-1', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithInvalidMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '13', + 'day' => '3', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithInvalidDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithStringDay() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => 'bazinga', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithStringMonth() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => 'bazinga', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithStringYear() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => 'bazinga', + 'month' => '2', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithEmptyStringHour() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '31', + 'hour' => '', + 'minute' => '5', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithEmptyStringMinute() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '31', + 'hour' => '4', + 'minute' => '', + 'second' => '6', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithEmptyStringSecond() + { + $transformer = new DateTimeToArrayTransformer(); + $transformer->reverseTransform(array( + 'year' => '2010', + 'month' => '2', + 'day' => '31', + 'hour' => '4', + 'minute' => '5', + 'second' => '', + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php new file mode 100644 index 0000000..c60f4d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -0,0 +1,289 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase +{ + protected $dateTime; + protected $dateTimeWithoutSeconds; + + protected function setUp() + { + parent::setUp(); + + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $this->dateTime = new \DateTime('2010-02-03 04:05:06 UTC'); + $this->dateTimeWithoutSeconds = new \DateTime('2010-02-03 04:05:00 UTC'); + } + + protected function tearDown() + { + $this->dateTime = null; + $this->dateTimeWithoutSeconds = null; + } + + public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + if ($expected instanceof \DateTime && $actual instanceof \DateTime) { + $expected = $expected->format('c'); + $actual = $actual->format('c'); + } + + parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + + public function dataProvider() + { + return array( + array(\IntlDateFormatter::SHORT, null, null, '03.02.10 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::MEDIUM, null, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::LONG, null, null, '03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::FULL, null, null, 'Mittwoch, 03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'), + array(\IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, null, '03.02.10', '2010-02-03 00:00:00 UTC'), + array(\IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, '03.02.2010', '2010-02-03 00:00:00 UTC'), + array(\IntlDateFormatter::LONG, \IntlDateFormatter::NONE, null, '03. Februar 2010', '2010-02-03 00:00:00 UTC'), + array(\IntlDateFormatter::FULL, \IntlDateFormatter::NONE, null, 'Mittwoch, 03. Februar 2010', '2010-02-03 00:00:00 UTC'), + array(null, \IntlDateFormatter::SHORT, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'), + array(null, \IntlDateFormatter::MEDIUM, null, '03.02.2010 04:05:06', '2010-02-03 04:05:06 UTC'), + array(null, \IntlDateFormatter::LONG, null, '03.02.2010 04:05:06 GMT', '2010-02-03 04:05:06 UTC'), + // see below for extra test case for time format FULL + array(\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT, null, '04:05', '1970-01-01 04:05:00 UTC'), + array(\IntlDateFormatter::NONE, \IntlDateFormatter::MEDIUM, null, '04:05:06', '1970-01-01 04:05:06 UTC'), + array(\IntlDateFormatter::NONE, \IntlDateFormatter::LONG, null, '04:05:06 GMT', '1970-01-01 04:05:06 UTC'), + array(null, null, 'yyyy-MM-dd HH:mm:00', '2010-02-03 04:05:00', '2010-02-03 04:05:00 UTC'), + array(null, null, 'yyyy-MM-dd HH:mm', '2010-02-03 04:05', '2010-02-03 04:05:00 UTC'), + array(null, null, 'yyyy-MM-dd HH', '2010-02-03 04', '2010-02-03 04:00:00 UTC'), + array(null, null, 'yyyy-MM-dd', '2010-02-03', '2010-02-03 00:00:00 UTC'), + array(null, null, 'yyyy-MM', '2010-02', '2010-02-01 00:00:00 UTC'), + array(null, null, 'yyyy', '2010', '2010-01-01 00:00:00 UTC'), + array(null, null, 'dd-MM-yyyy', '03-02-2010', '2010-02-03 00:00:00 UTC'), + array(null, null, 'HH:mm:ss', '04:05:06', '1970-01-01 04:05:06 UTC'), + array(null, null, 'HH:mm:00', '04:05:00', '1970-01-01 04:05:00 UTC'), + array(null, null, 'HH:mm', '04:05', '1970-01-01 04:05:00 UTC'), + array(null, null, 'HH', '04', '1970-01-01 04:00:00 UTC'), + ); + } + + /** + * @dataProvider dataProvider + */ + public function testTransform($dateFormat, $timeFormat, $pattern, $output, $input) + { + $transformer = new DateTimeToLocalizedStringTransformer( + 'UTC', + 'UTC', + $dateFormat, + $timeFormat, + \IntlDateFormatter::GREGORIAN, + $pattern + ); + + $input = new \DateTime($input); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformFullTime() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::FULL); + + $this->assertEquals('03.02.2010 04:05:06 GMT', $transformer->transform($this->dateTime)); + } + + public function testTransformToDifferentLocale() + { + \Locale::setDefault('en_US'); + + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); + + $this->assertEquals('Feb 3, 2010, 4:05 AM', $transformer->transform($this->dateTime)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + $this->assertSame('', $transformer->transform(null)); + } + + public function testTransformWithDifferentTimezones() + { + $transformer = new DateTimeToLocalizedStringTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = new \DateTime('2010-02-03 04:05:06 America/New_York'); + + $dateTime = clone $input; + $dateTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($dateTime->format('d.m.Y H:i'), $transformer->transform($input)); + } + + public function testTransformWithDifferentPatterns() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'MM*yyyy*dd HH|mm|ss'); + + $this->assertEquals('02*2010*03 04|05|06', $transformer->transform($this->dateTime)); + } + + public function testTransformDateTimeImmutableTimezones() + { + $transformer = new DateTimeToLocalizedStringTransformer('America/New_York', 'Asia/Hong_Kong'); + + $input = new \DateTimeImmutable('2010-02-03 04:05:06 America/New_York'); + + $dateTime = clone $input; + $dateTime = $dateTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($dateTime->format('d.m.Y H:i'), $transformer->transform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresValidDateTime() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->transform('2010-01-01'); + } + + public function testTransformWrapsIntlErrors() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + $this->markTestIncomplete('Checking for intl errors needs to be reimplemented'); + + // HOW TO REPRODUCE? + + //$this->setExpectedException('Symfony\Component\Form\Extension\Core\DataTransformer\TransformationFailedException'); + + //$transformer->transform(1.5); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransform($dateFormat, $timeFormat, $pattern, $input, $output) + { + $transformer = new DateTimeToLocalizedStringTransformer( + 'UTC', + 'UTC', + $dateFormat, + $timeFormat, + \IntlDateFormatter::GREGORIAN, + $pattern + ); + + $output = new \DateTime($output); + + $this->assertEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformFullTime() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::FULL); + + $this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010 04:05:06 GMT+00:00')); + } + + public function testReverseTransformFromDifferentLocale() + { + \Locale::setDefault('en_US'); + + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); + + $this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('Feb 3, 2010, 04:05 AM')); + } + + public function testReverseTransformWithDifferentTimezones() + { + $transformer = new DateTimeToLocalizedStringTransformer('America/New_York', 'Asia/Hong_Kong'); + + $dateTime = new \DateTime('2010-02-03 04:05:00 Asia/Hong_Kong'); + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($dateTime, $transformer->reverseTransform('03.02.2010 04:05')); + } + + public function testReverseTransformWithDifferentPatterns() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'MM*yyyy*dd HH|mm|ss'); + + $this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06')); + } + + public function testReverseTransformEmpty() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresString() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform(12345); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWrapsIntlErrors() + { + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform('12345'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testValidateDateFormatOption() + { + new DateTimeToLocalizedStringTransformer(null, null, 'foobar'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testValidateTimeFormatOption() + { + new DateTimeToLocalizedStringTransformer(null, null, null, 'foobar'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNonExistingDate() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT); + + $this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('31.04.10 04:05')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformOutOfTimestampRange() + { + $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC'); + $transformer->reverseTransform('1789-07-14'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php new file mode 100644 index 0000000..7e9c2e3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer; + +class DateTimeToRfc3339TransformerTest extends DateTimeTestCase +{ + protected $dateTime; + protected $dateTimeWithoutSeconds; + + protected function setUp() + { + parent::setUp(); + + $this->dateTime = new \DateTime('2010-02-03 04:05:06 UTC'); + $this->dateTimeWithoutSeconds = new \DateTime('2010-02-03 04:05:00 UTC'); + } + + protected function tearDown() + { + $this->dateTime = null; + $this->dateTimeWithoutSeconds = null; + } + + public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) + { + if ($expected instanceof \DateTime && $actual instanceof \DateTime) { + $expected = $expected->format('c'); + $actual = $actual->format('c'); + } + + parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + + public function allProvider() + { + return array( + array('UTC', 'UTC', '2010-02-03 04:05:06 UTC', '2010-02-03T04:05:06Z'), + array('UTC', 'UTC', null, ''), + array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:06 America/New_York', '2010-02-03T17:05:06+08:00'), + array('America/New_York', 'Asia/Hong_Kong', null, ''), + array('UTC', 'Asia/Hong_Kong', '2010-02-03 04:05:06 UTC', '2010-02-03T12:05:06+08:00'), + array('America/New_York', 'UTC', '2010-02-03 04:05:06 America/New_York', '2010-02-03T09:05:06Z'), + ); + } + + public function transformProvider() + { + return $this->allProvider(); + } + + public function reverseTransformProvider() + { + return array_merge($this->allProvider(), array( + // format without seconds, as appears in some browsers + array('UTC', 'UTC', '2010-02-03 04:05:00 UTC', '2010-02-03T04:05Z'), + array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:00 America/New_York', '2010-02-03T17:05+08:00'), + array('Europe/Amsterdam', 'Europe/Amsterdam', '2013-08-21 10:30:00 Europe/Amsterdam', '2013-08-21T08:30:00Z'), + )); + } + + /** + * @dataProvider transformProvider + */ + public function testTransform($fromTz, $toTz, $from, $to) + { + $transformer = new DateTimeToRfc3339Transformer($fromTz, $toTz); + + $this->assertSame($to, $transformer->transform(null !== $from ? new \DateTime($from) : null)); + } + + /** + * @dataProvider transformProvider + */ + public function testTransformDateTimeImmutable($fromTz, $toTz, $from, $to) + { + $transformer = new DateTimeToRfc3339Transformer($fromTz, $toTz); + + $this->assertSame($to, $transformer->transform(null !== $from ? new \DateTimeImmutable($from) : null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformRequiresValidDateTime() + { + $transformer = new DateTimeToRfc3339Transformer(); + $transformer->transform('2010-01-01'); + } + + /** + * @dataProvider reverseTransformProvider + */ + public function testReverseTransform($toTz, $fromTz, $to, $from) + { + $transformer = new DateTimeToRfc3339Transformer($toTz, $fromTz); + + if (null !== $to) { + $this->assertDateTimeEquals(new \DateTime($to), $transformer->reverseTransform($from)); + } else { + $this->assertSame($to, $transformer->reverseTransform($from)); + } + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresString() + { + $transformer = new DateTimeToRfc3339Transformer(); + $transformer->reverseTransform(12345); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformWithNonExistingDate() + { + $transformer = new DateTimeToRfc3339Transformer('UTC', 'UTC'); + + $transformer->reverseTransform('2010-04-31T04:05Z'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsValidDateString() + { + $transformer = new DateTimeToRfc3339Transformer('UTC', 'UTC'); + + $transformer->reverseTransform('2010-2010-2010'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php new file mode 100644 index 0000000..3d7d042 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToStringTransformerTest.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; + +class DateTimeToStringTransformerTest extends DateTimeTestCase +{ + public function dataProvider() + { + $data = array( + array('Y-m-d H:i:s', '2010-02-03 16:05:06', '2010-02-03 16:05:06 UTC'), + array('Y-m-d H:i:00', '2010-02-03 16:05:00', '2010-02-03 16:05:00 UTC'), + array('Y-m-d H:i', '2010-02-03 16:05', '2010-02-03 16:05:00 UTC'), + array('Y-m-d H', '2010-02-03 16', '2010-02-03 16:00:00 UTC'), + array('Y-m-d', '2010-02-03', '2010-02-03 00:00:00 UTC'), + array('Y-m', '2010-12', '2010-12-01 00:00:00 UTC'), + array('Y', '2010', '2010-01-01 00:00:00 UTC'), + array('d-m-Y', '03-02-2010', '2010-02-03 00:00:00 UTC'), + array('H:i:s', '16:05:06', '1970-01-01 16:05:06 UTC'), + array('H:i:00', '16:05:00', '1970-01-01 16:05:00 UTC'), + array('H:i', '16:05', '1970-01-01 16:05:00 UTC'), + array('H', '16', '1970-01-01 16:00:00 UTC'), + array('Y-z', '2010-33', '2010-02-03 00:00:00 UTC'), + + // different day representations + array('Y-m-j', '2010-02-3', '2010-02-03 00:00:00 UTC'), + array('z', '33', '1970-02-03 00:00:00 UTC'), + + // not bijective + // this will not work as PHP will use actual date to replace missing info + // and after change of date will lookup for closest Wednesday + // i.e. value: 2010-02, PHP value: 2010-02-(today i.e. 20), parsed date: 2010-02-24 + //array('Y-m-D', '2010-02-Wed', '2010-02-03 00:00:00 UTC'), + //array('Y-m-l', '2010-02-Wednesday', '2010-02-03 00:00:00 UTC'), + + // different month representations + array('Y-n-d', '2010-2-03', '2010-02-03 00:00:00 UTC'), + array('Y-M-d', '2010-Feb-03', '2010-02-03 00:00:00 UTC'), + array('Y-F-d', '2010-February-03', '2010-02-03 00:00:00 UTC'), + + // different year representations + array('y-m-d', '10-02-03', '2010-02-03 00:00:00 UTC'), + + // different time representations + array('G:i:s', '16:05:06', '1970-01-01 16:05:06 UTC'), + array('g:i:s a', '4:05:06 pm', '1970-01-01 16:05:06 UTC'), + array('h:i:s a', '04:05:06 pm', '1970-01-01 16:05:06 UTC'), + + // seconds since Unix + array('U', '1265213106', '2010-02-03 16:05:06 UTC'), + + array('Y-z', '2010-33', '2010-02-03 00:00:00 UTC'), + ); + + return $data; + } + + /** + * @dataProvider dataProvider + */ + public function testTransform($format, $output, $input) + { + $transformer = new DateTimeToStringTransformer('UTC', 'UTC', $format); + + $input = new \DateTime($input); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToStringTransformer(); + + $this->assertSame('', $transformer->transform(null)); + } + + public function testTransformWithDifferentTimezones() + { + $transformer = new DateTimeToStringTransformer('Asia/Hong_Kong', 'America/New_York', 'Y-m-d H:i:s'); + + $input = new \DateTime('2010-02-03 12:05:06 America/New_York'); + $output = $input->format('Y-m-d H:i:s'); + $input->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformDateTimeImmutable() + { + $transformer = new DateTimeToStringTransformer('Asia/Hong_Kong', 'America/New_York', 'Y-m-d H:i:s'); + + $input = new \DateTimeImmutable('2010-02-03 12:05:06 America/New_York'); + $output = $input->format('Y-m-d H:i:s'); + $input = $input->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformExpectsDateTime() + { + $transformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('1234'); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransformUsingPipe($format, $input, $output) + { + $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format, true); + + $output = new \DateTime($output); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + /** + * @dataProvider dataProvider + */ + public function testReverseTransformWithoutUsingPipe($format, $input, $output) + { + $reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format, false); + + $output = new \DateTime($output); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformEmpty() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->assertNull($reverseTransformer->reverseTransform('')); + } + + public function testReverseTransformWithDifferentTimezones() + { + $reverseTransformer = new DateTimeToStringTransformer('America/New_York', 'Asia/Hong_Kong', 'Y-m-d H:i:s'); + + $output = new \DateTime('2010-02-03 16:05:06 Asia/Hong_Kong'); + $input = $output->format('Y-m-d H:i:s'); + $output->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformExpectsString() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform(1234); + } + + public function testReverseTransformExpectsValidDateString() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform('2010-2010-2010'); + } + + public function testReverseTransformWithNonExistingDate() + { + $reverseTransformer = new DateTimeToStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform('2010-04-31'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php new file mode 100644 index 0000000..8f05303 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToTimestampTransformerTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; + +class DateTimeToTimestampTransformerTest extends DateTimeTestCase +{ + public function testTransform() + { + $transformer = new DateTimeToTimestampTransformer('UTC', 'UTC'); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + $output = $input->format('U'); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeToTimestampTransformer(); + + $this->assertNull($transformer->transform(null)); + } + + public function testTransformWithDifferentTimezones() + { + $transformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'America/New_York'); + + $input = new \DateTime('2010-02-03 04:05:06 America/New_York'); + $output = $input->format('U'); + $input->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformFromDifferentTimezone() + { + $transformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'UTC'); + + $input = new \DateTime('2010-02-03 04:05:06 Asia/Hong_Kong'); + + $dateTime = clone $input; + $dateTime->setTimezone(new \DateTimeZone('UTC')); + $output = $dateTime->format('U'); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformDateTimeImmutable() + { + $transformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'America/New_York'); + + $input = new \DateTimeImmutable('2010-02-03 04:05:06 America/New_York'); + $output = $input->format('U'); + $input = $input->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformExpectsDateTime() + { + $transformer = new DateTimeToTimestampTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('1234'); + } + + public function testReverseTransform() + { + $reverseTransformer = new DateTimeToTimestampTransformer('UTC', 'UTC'); + + $output = new \DateTime('2010-02-03 04:05:06 UTC'); + $input = $output->format('U'); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformEmpty() + { + $reverseTransformer = new DateTimeToTimestampTransformer(); + + $this->assertNull($reverseTransformer->reverseTransform(null)); + } + + public function testReverseTransformWithDifferentTimezones() + { + $reverseTransformer = new DateTimeToTimestampTransformer('Asia/Hong_Kong', 'America/New_York'); + + $output = new \DateTime('2010-02-03 04:05:06 America/New_York'); + $input = $output->format('U'); + $output->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input)); + } + + public function testReverseTransformExpectsValidTimestamp() + { + $reverseTransformer = new DateTimeToTimestampTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $reverseTransformer->reverseTransform('2010-2010-2010'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php new file mode 100644 index 0000000..c511325 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformerTest.php @@ -0,0 +1,244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class IntegerToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + \Locale::setDefault('en'); + } + + public function transformWithRoundingProvider() + { + return array( + // towards positive infinity (1.6 -> 2, -1.6 -> -1) + array(1234.5, '1235', IntegerToLocalizedStringTransformer::ROUND_CEILING), + array(1234.4, '1235', IntegerToLocalizedStringTransformer::ROUND_CEILING), + array(-1234.5, '-1234', IntegerToLocalizedStringTransformer::ROUND_CEILING), + array(-1234.4, '-1234', IntegerToLocalizedStringTransformer::ROUND_CEILING), + // towards negative infinity (1.6 -> 1, -1.6 -> -2) + array(1234.5, '1234', IntegerToLocalizedStringTransformer::ROUND_FLOOR), + array(1234.4, '1234', IntegerToLocalizedStringTransformer::ROUND_FLOOR), + array(-1234.5, '-1235', IntegerToLocalizedStringTransformer::ROUND_FLOOR), + array(-1234.4, '-1235', IntegerToLocalizedStringTransformer::ROUND_FLOOR), + // away from zero (1.6 -> 2, -1.6 -> 2) + array(1234.5, '1235', IntegerToLocalizedStringTransformer::ROUND_UP), + array(1234.4, '1235', IntegerToLocalizedStringTransformer::ROUND_UP), + array(-1234.5, '-1235', IntegerToLocalizedStringTransformer::ROUND_UP), + array(-1234.4, '-1235', IntegerToLocalizedStringTransformer::ROUND_UP), + // towards zero (1.6 -> 1, -1.6 -> -1) + array(1234.5, '1234', IntegerToLocalizedStringTransformer::ROUND_DOWN), + array(1234.4, '1234', IntegerToLocalizedStringTransformer::ROUND_DOWN), + array(-1234.5, '-1234', IntegerToLocalizedStringTransformer::ROUND_DOWN), + array(-1234.4, '-1234', IntegerToLocalizedStringTransformer::ROUND_DOWN), + // round halves (.5) to the next even number + array(1234.6, '1235', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1234.5, '1234', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1234.4, '1234', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1233.5, '1234', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1232.5, '1232', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(-1234.6, '-1235', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(-1234.5, '-1234', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(-1234.4, '-1234', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(-1233.5, '-1234', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(-1232.5, '-1232', IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + // round halves (.5) away from zero + array(1234.6, '1235', IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array(1234.5, '1235', IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array(1234.4, '1234', IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array(-1234.6, '-1235', IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array(-1234.5, '-1235', IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array(-1234.4, '-1234', IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + // round halves (.5) towards zero + array(1234.6, '1235', IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1234.5, '1234', IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1234.4, '1234', IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(-1234.6, '-1235', IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(-1234.5, '-1234', IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(-1234.4, '-1234', IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + ); + } + + /** + * @dataProvider transformWithRoundingProvider + */ + public function testTransformWithRounding($input, $output, $roundingMode) + { + $transformer = new IntegerToLocalizedStringTransformer(null, null, $roundingMode); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testReverseTransform() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new IntegerToLocalizedStringTransformer(); + + $this->assertEquals(1, $transformer->reverseTransform('1')); + $this->assertEquals(1, $transformer->reverseTransform('1,5')); + $this->assertEquals(1234, $transformer->reverseTransform('1234,5')); + $this->assertEquals(12345, $transformer->reverseTransform('12345,912')); + } + + public function testReverseTransformEmpty() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } + + public function testReverseTransformWithGrouping() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new IntegerToLocalizedStringTransformer(null, true); + + $this->assertEquals(1234, $transformer->reverseTransform('1.234,5')); + $this->assertEquals(12345, $transformer->reverseTransform('12.345,912')); + $this->assertEquals(1234, $transformer->reverseTransform('1234,5')); + $this->assertEquals(12345, $transformer->reverseTransform('12345,912')); + } + + public function reverseTransformWithRoundingProvider() + { + return array( + // towards positive infinity (1.6 -> 2, -1.6 -> -1) + array('1234,5', 1235, IntegerToLocalizedStringTransformer::ROUND_CEILING), + array('1234,4', 1235, IntegerToLocalizedStringTransformer::ROUND_CEILING), + array('-1234,5', -1234, IntegerToLocalizedStringTransformer::ROUND_CEILING), + array('-1234,4', -1234, IntegerToLocalizedStringTransformer::ROUND_CEILING), + // towards negative infinity (1.6 -> 1, -1.6 -> -2) + array('1234,5', 1234, IntegerToLocalizedStringTransformer::ROUND_FLOOR), + array('1234,4', 1234, IntegerToLocalizedStringTransformer::ROUND_FLOOR), + array('-1234,5', -1235, IntegerToLocalizedStringTransformer::ROUND_FLOOR), + array('-1234,4', -1235, IntegerToLocalizedStringTransformer::ROUND_FLOOR), + // away from zero (1.6 -> 2, -1.6 -> 2) + array('1234,5', 1235, IntegerToLocalizedStringTransformer::ROUND_UP), + array('1234,4', 1235, IntegerToLocalizedStringTransformer::ROUND_UP), + array('-1234,5', -1235, IntegerToLocalizedStringTransformer::ROUND_UP), + array('-1234,4', -1235, IntegerToLocalizedStringTransformer::ROUND_UP), + // towards zero (1.6 -> 1, -1.6 -> -1) + array('1234,5', 1234, IntegerToLocalizedStringTransformer::ROUND_DOWN), + array('1234,4', 1234, IntegerToLocalizedStringTransformer::ROUND_DOWN), + array('-1234,5', -1234, IntegerToLocalizedStringTransformer::ROUND_DOWN), + array('-1234,4', -1234, IntegerToLocalizedStringTransformer::ROUND_DOWN), + // round halves (.5) to the next even number + array('1234,6', 1235, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('1234,5', 1234, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('1234,4', 1234, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('1233,5', 1234, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('1232,5', 1232, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('-1234,6', -1235, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('-1234,5', -1234, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('-1234,4', -1234, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('-1233,5', -1234, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + array('-1232,5', -1232, IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN), + // round halves (.5) away from zero + array('1234,6', 1235, IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array('1234,5', 1235, IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array('1234,4', 1234, IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array('-1234,6', -1235, IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array('-1234,5', -1235, IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + array('-1234,4', -1234, IntegerToLocalizedStringTransformer::ROUND_HALF_UP), + // round halves (.5) towards zero + array('1234,6', 1235, IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array('1234,5', 1234, IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array('1234,4', 1234, IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array('-1234,6', -1235, IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array('-1234,5', -1234, IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + array('-1234,4', -1234, IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN), + ); + } + + /** + * @dataProvider reverseTransformWithRoundingProvider + */ + public function testReverseTransformWithRounding($input, $output, $roundingMode) + { + $transformer = new IntegerToLocalizedStringTransformer(null, null, $roundingMode); + + $this->assertEquals($output, $transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsString() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform(1); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsValidNumber() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNaN() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('NaN'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNaN2() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('nan'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsInfinity() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('∞'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNegativeInfinity() + { + $transformer = new IntegerToLocalizedStringTransformer(); + + $transformer->reverseTransform('-∞'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php new file mode 100644 index 0000000..99e4c8a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class MoneyToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + public function testTransform() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->assertEquals('1,23', $transformer->transform(123)); + } + + public function testTransformExpectsNumeric() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('abcd'); + } + + public function testTransformEmpty() + { + $transformer = new MoneyToLocalizedStringTransformer(); + + $this->assertSame('', $transformer->transform(null)); + } + + public function testReverseTransform() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->assertEquals(123, $transformer->reverseTransform('1,23')); + } + + public function testReverseTransformExpectsString() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->reverseTransform(12345); + } + + public function testReverseTransformEmpty() + { + $transformer = new MoneyToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php new file mode 100644 index 0000000..2a47178 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -0,0 +1,639 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + \Locale::setDefault('en'); + } + + public function provideTransformations() + { + return array( + array(null, '', 'de_AT'), + array(1, '1', 'de_AT'), + array(1.5, '1,5', 'de_AT'), + array(1234.5, '1234,5', 'de_AT'), + array(12345.912, '12345,912', 'de_AT'), + array(1234.5, '1234,5', 'ru'), + array(1234.5, '1234,5', 'fi'), + ); + } + + /** + * @dataProvider provideTransformations + */ + public function testTransform($from, $to, $locale) + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertSame($to, $transformer->transform($from)); + } + + public function provideTransformationsWithGrouping() + { + return array( + array(1234.5, '1.234,5', 'de_AT'), + array(12345.912, '12.345,912', 'de_AT'), + array(1234.5, '1 234,5', 'fr'), + array(1234.5, '1 234,5', 'ru'), + array(1234.5, '1 234,5', 'fi'), + ); + } + + /** + * @dataProvider provideTransformationsWithGrouping + */ + public function testTransformWithGrouping($from, $to, $locale) + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertSame($to, $transformer->transform($from)); + } + + public function testTransformWithScale() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new NumberToLocalizedStringTransformer(2); + + $this->assertEquals('1234,50', $transformer->transform(1234.5)); + $this->assertEquals('678,92', $transformer->transform(678.916)); + } + + public function transformWithRoundingProvider() + { + return array( + // towards positive infinity (1.6 -> 2, -1.6 -> -1) + array(0, 1234.5, '1235', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(0, 1234.4, '1235', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(0, -1234.5, '-1234', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(0, -1234.4, '-1234', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, 123.45, '123,5', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, 123.44, '123,5', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, -123.45, '-123,4', NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, -123.44, '-123,4', NumberToLocalizedStringTransformer::ROUND_CEILING), + // towards negative infinity (1.6 -> 1, -1.6 -> -2) + array(0, 1234.5, '1234', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(0, 1234.4, '1234', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(0, -1234.5, '-1235', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(0, -1234.4, '-1235', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, 123.45, '123,4', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, 123.44, '123,4', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, -123.45, '-123,5', NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, -123.44, '-123,5', NumberToLocalizedStringTransformer::ROUND_FLOOR), + // away from zero (1.6 -> 2, -1.6 -> 2) + array(0, 1234.5, '1235', NumberToLocalizedStringTransformer::ROUND_UP), + array(0, 1234.4, '1235', NumberToLocalizedStringTransformer::ROUND_UP), + array(0, -1234.5, '-1235', NumberToLocalizedStringTransformer::ROUND_UP), + array(0, -1234.4, '-1235', NumberToLocalizedStringTransformer::ROUND_UP), + array(1, 123.45, '123,5', NumberToLocalizedStringTransformer::ROUND_UP), + array(1, 123.44, '123,5', NumberToLocalizedStringTransformer::ROUND_UP), + array(1, -123.45, '-123,5', NumberToLocalizedStringTransformer::ROUND_UP), + array(1, -123.44, '-123,5', NumberToLocalizedStringTransformer::ROUND_UP), + // towards zero (1.6 -> 1, -1.6 -> -1) + array(0, 1234.5, '1234', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(0, 1234.4, '1234', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(0, -1234.5, '-1234', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(0, -1234.4, '-1234', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, 123.45, '123,4', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, 123.44, '123,4', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, -123.45, '-123,4', NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, -123.44, '-123,4', NumberToLocalizedStringTransformer::ROUND_DOWN), + // round halves (.5) to the next even number + array(0, 1234.6, '1235', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, 1234.5, '1234', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, 1234.4, '1234', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, 1233.5, '1234', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, 1232.5, '1232', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, -1234.6, '-1235', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, -1234.5, '-1234', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, -1234.4, '-1234', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, -1233.5, '-1234', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, -1232.5, '-1232', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, 123.46, '123,5', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, 123.45, '123,4', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, 123.44, '123,4', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, 123.35, '123,4', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, 123.25, '123,2', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, -123.46, '-123,5', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, -123.45, '-123,4', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, -123.44, '-123,4', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, -123.35, '-123,4', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, -123.25, '-123,2', NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + // round halves (.5) away from zero + array(0, 1234.6, '1235', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, 1234.5, '1235', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, 1234.4, '1234', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, -1234.6, '-1235', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, -1234.5, '-1235', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, -1234.4, '-1234', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, 123.46, '123,5', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, 123.45, '123,5', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, 123.44, '123,4', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, -123.46, '-123,5', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, -123.45, '-123,5', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, -123.44, '-123,4', NumberToLocalizedStringTransformer::ROUND_HALF_UP), + // round halves (.5) towards zero + array(0, 1234.6, '1235', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, 1234.5, '1234', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, 1234.4, '1234', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, -1234.6, '-1235', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, -1234.5, '-1234', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, -1234.4, '-1234', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, 123.46, '123,5', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, 123.45, '123,4', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, 123.44, '123,4', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, -123.46, '-123,5', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, -123.45, '-123,4', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, -123.44, '-123,4', NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + ); + } + + /** + * @dataProvider transformWithRoundingProvider + */ + public function testTransformWithRounding($scale, $input, $output, $roundingMode) + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new NumberToLocalizedStringTransformer($scale, null, $roundingMode); + + $this->assertEquals($output, $transformer->transform($input)); + } + + public function testTransformDoesNotRoundIfNoScale() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new NumberToLocalizedStringTransformer(null, null, NumberToLocalizedStringTransformer::ROUND_DOWN); + + $this->assertEquals('1234,547', $transformer->transform(1234.547)); + } + + /** + * @dataProvider provideTransformations + */ + public function testReverseTransform($to, $from, $locale) + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals($to, $transformer->reverseTransform($from)); + } + + /** + * @dataProvider provideTransformationsWithGrouping + */ + public function testReverseTransformWithGrouping($to, $from, $locale) + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault($locale); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertEquals($to, $transformer->reverseTransform($from)); + } + + /** + * @see https://github.com/symfony/symfony/issues/7609 + */ + public function testReverseTransformWithGroupingAndFixedSpaces() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $this->assertEquals(1234.5, $transformer->reverseTransform("1\xc2\xa0234,5")); + } + + public function testReverseTransformWithGroupingButWithoutGroupSeparator() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + // omit group separator + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(12345.912, $transformer->reverseTransform('12345,912')); + } + + public function reverseTransformWithRoundingProvider() + { + return array( + // towards positive infinity (1.6 -> 2, -1.6 -> -1) + array(0, '1234,5', 1235, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(0, '1234,4', 1235, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(0, '-1234,5', -1234, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(0, '-1234,4', -1234, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, '123,45', 123.5, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, '123,44', 123.5, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, '-123,45', -123.4, NumberToLocalizedStringTransformer::ROUND_CEILING), + array(1, '-123,44', -123.4, NumberToLocalizedStringTransformer::ROUND_CEILING), + // towards negative infinity (1.6 -> 1, -1.6 -> -2) + array(0, '1234,5', 1234, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(0, '1234,4', 1234, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(0, '-1234,5', -1235, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(0, '-1234,4', -1235, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, '123,45', 123.4, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, '123,44', 123.4, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, '-123,45', -123.5, NumberToLocalizedStringTransformer::ROUND_FLOOR), + array(1, '-123,44', -123.5, NumberToLocalizedStringTransformer::ROUND_FLOOR), + // away from zero (1.6 -> 2, -1.6 -> 2) + array(0, '1234,5', 1235, NumberToLocalizedStringTransformer::ROUND_UP), + array(0, '1234,4', 1235, NumberToLocalizedStringTransformer::ROUND_UP), + array(0, '-1234,5', -1235, NumberToLocalizedStringTransformer::ROUND_UP), + array(0, '-1234,4', -1235, NumberToLocalizedStringTransformer::ROUND_UP), + array(1, '123,45', 123.5, NumberToLocalizedStringTransformer::ROUND_UP), + array(1, '123,44', 123.5, NumberToLocalizedStringTransformer::ROUND_UP), + array(1, '-123,45', -123.5, NumberToLocalizedStringTransformer::ROUND_UP), + array(1, '-123,44', -123.5, NumberToLocalizedStringTransformer::ROUND_UP), + // towards zero (1.6 -> 1, -1.6 -> -1) + array(0, '1234,5', 1234, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(0, '1234,4', 1234, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(0, '-1234,5', -1234, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(0, '-1234,4', -1234, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, '123,45', 123.4, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, '123,44', 123.4, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, '-123,45', -123.4, NumberToLocalizedStringTransformer::ROUND_DOWN), + array(1, '-123,44', -123.4, NumberToLocalizedStringTransformer::ROUND_DOWN), + // round halves (.5) to the next even number + array(0, '1234,6', 1235, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '1234,5', 1234, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '1234,4', 1234, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '1233,5', 1234, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '1232,5', 1232, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '-1234,6', -1235, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '-1234,5', -1234, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '-1234,4', -1234, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '-1233,5', -1234, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(0, '-1232,5', -1232, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '123,46', 123.5, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '123,45', 123.4, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '123,44', 123.4, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '123,35', 123.4, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '123,25', 123.2, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '-123,46', -123.5, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '-123,45', -123.4, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '-123,44', -123.4, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '-123,35', -123.4, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + array(1, '-123,25', -123.2, NumberToLocalizedStringTransformer::ROUND_HALF_EVEN), + // round halves (.5) away from zero + array(0, '1234,6', 1235, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, '1234,5', 1235, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, '1234,4', 1234, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, '-1234,6', -1235, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, '-1234,5', -1235, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(0, '-1234,4', -1234, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, '123,46', 123.5, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, '123,45', 123.5, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, '123,44', 123.4, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, '-123,46', -123.5, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, '-123,45', -123.5, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + array(1, '-123,44', -123.4, NumberToLocalizedStringTransformer::ROUND_HALF_UP), + // round halves (.5) towards zero + array(0, '1234,6', 1235, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, '1234,5', 1234, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, '1234,4', 1234, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, '-1234,6', -1235, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, '-1234,5', -1234, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(0, '-1234,4', -1234, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, '123,46', 123.5, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, '123,45', 123.4, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, '123,44', 123.4, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, '-123,46', -123.5, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, '-123,45', -123.4, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + array(1, '-123,44', -123.4, NumberToLocalizedStringTransformer::ROUND_HALF_DOWN), + ); + } + + /** + * @dataProvider reverseTransformWithRoundingProvider + */ + public function testReverseTransformWithRounding($scale, $input, $output, $roundingMode) + { + $transformer = new NumberToLocalizedStringTransformer($scale, null, $roundingMode); + + $this->assertEquals($output, $transformer->reverseTransform($input)); + } + + public function testReverseTransformDoesNotRoundIfNoScale() + { + $transformer = new NumberToLocalizedStringTransformer(null, null, NumberToLocalizedStringTransformer::ROUND_DOWN); + + $this->assertEquals(1234.547, $transformer->reverseTransform('1234,547')); + } + + public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsNotDot() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('fr'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + // completely valid format + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234,5')); + // accept dots + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234.5')); + // omit group separator + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1.234.5'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDotWithNoGroupSep() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1234.5'); + } + + public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsDotButNoGroupingUsed() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('fr'); + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + } + + public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsNotComma() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('bg'); + $transformer = new NumberToLocalizedStringTransformer(null, true); + + // completely valid format + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234.5')); + // accept commas + $this->assertEquals(1234.5, $transformer->reverseTransform('1 234,5')); + // omit group separator + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsComma() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1,234,5'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsCommaWithNoGroupSep() + { + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform('1234,5'); + } + + public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsCommaButNoGroupingUsed() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $this->assertEquals(1234.5, $transformer->reverseTransform('1234,5')); + $this->assertEquals(1234.5, $transformer->reverseTransform('1234.5')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testTransformExpectsNumeric() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->transform('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsString() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform(1); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformExpectsValidNumber() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * + * @link https://github.com/symfony/symfony/issues/3161 + */ + public function testReverseTransformDisallowsNaN() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('NaN'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNaN2() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('nan'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsInfinity() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('∞'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsInfinity2() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('∞,123'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsNegativeInfinity() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('-∞'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDisallowsLeadingExtraCharacters() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('foo123'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo3" + */ + public function testReverseTransformDisallowsCenteredExtraCharacters() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('12foo3'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo8" + */ + public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform("12\xc2\xa0345,67foo8"); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo8" + */ + public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform("12\xc2\xa0345,67foo8 \xc2\xa0\t"); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo" + */ + public function testReverseTransformDisallowsTrailingExtraCharacters() + { + $transformer = new NumberToLocalizedStringTransformer(); + + $transformer->reverseTransform('123foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage The number contains unrecognized characters: "foo" + */ + public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte() + { + // Since we test against other locales, we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('ru'); + + $transformer = new NumberToLocalizedStringTransformer(null, true); + + $transformer->reverseTransform("12\xc2\xa0345,678foo"); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php new file mode 100644 index 0000000..99f0674 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\PercentToLocalizedStringTransformer; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class PercentToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + parent::setUp(); + + \Locale::setDefault('en'); + } + + public function testTransform() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertEquals('10', $transformer->transform(0.1)); + $this->assertEquals('15', $transformer->transform(0.15)); + $this->assertEquals('12', $transformer->transform(0.1234)); + $this->assertEquals('200', $transformer->transform(2)); + } + + public function testTransformEmpty() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertEquals('', $transformer->transform(null)); + } + + public function testTransformWithInteger() + { + $transformer = new PercentToLocalizedStringTransformer(null, 'integer'); + + $this->assertEquals('0', $transformer->transform(0.1)); + $this->assertEquals('1', $transformer->transform(1)); + $this->assertEquals('15', $transformer->transform(15)); + $this->assertEquals('16', $transformer->transform(15.9)); + } + + public function testTransformWithScale() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new PercentToLocalizedStringTransformer(2); + + $this->assertEquals('12,34', $transformer->transform(0.1234)); + } + + public function testReverseTransform() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertEquals(0.1, $transformer->reverseTransform('10')); + $this->assertEquals(0.15, $transformer->reverseTransform('15')); + $this->assertEquals(0.12, $transformer->reverseTransform('12')); + $this->assertEquals(2, $transformer->reverseTransform('200')); + } + + public function testReverseTransformEmpty() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->assertNull($transformer->reverseTransform('')); + } + + public function testReverseTransformWithInteger() + { + $transformer = new PercentToLocalizedStringTransformer(null, 'integer'); + + $this->assertEquals(10, $transformer->reverseTransform('10')); + $this->assertEquals(15, $transformer->reverseTransform('15')); + $this->assertEquals(12, $transformer->reverseTransform('12')); + $this->assertEquals(200, $transformer->reverseTransform('200')); + } + + public function testReverseTransformWithScale() + { + // Since we test against "de_AT", we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $transformer = new PercentToLocalizedStringTransformer(2); + + $this->assertEquals(0.1234, $transformer->reverseTransform('12,34')); + } + + public function testTransformExpectsNumeric() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->transform('foo'); + } + + public function testReverseTransformExpectsString() + { + $transformer = new PercentToLocalizedStringTransformer(); + + $this->setExpectedException('Symfony\Component\Form\Exception\TransformationFailedException'); + + $transformer->reverseTransform(1); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php new file mode 100644 index 0000000..eb3cf97 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToDuplicatesTransformerTest.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; + +use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToDuplicatesTransformer; + +class ValueToDuplicatesTransformerTest extends \PHPUnit_Framework_TestCase +{ + private $transformer; + + protected function setUp() + { + $this->transformer = new ValueToDuplicatesTransformer(array('a', 'b', 'c')); + } + + protected function tearDown() + { + $this->transformer = null; + } + + public function testTransform() + { + $output = array( + 'a' => 'Foo', + 'b' => 'Foo', + 'c' => 'Foo', + ); + + $this->assertSame($output, $this->transformer->transform('Foo')); + } + + public function testTransformEmpty() + { + $output = array( + 'a' => null, + 'b' => null, + 'c' => null, + ); + + $this->assertSame($output, $this->transformer->transform(null)); + } + + public function testReverseTransform() + { + $input = array( + 'a' => 'Foo', + 'b' => 'Foo', + 'c' => 'Foo', + ); + + $this->assertSame('Foo', $this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyEmpty() + { + $input = array( + 'a' => '', + 'b' => '', + 'c' => '', + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + public function testReverseTransformCompletelyNull() + { + $input = array( + 'a' => null, + 'b' => null, + 'c' => null, + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + public function testReverseTransformEmptyArray() + { + $input = array( + 'a' => array(), + 'b' => array(), + 'c' => array(), + ); + + $this->assertNull($this->transformer->reverseTransform($input)); + } + + public function testReverseTransformZeroString() + { + $input = array( + 'a' => '0', + 'b' => '0', + 'c' => '0', + ); + + $this->assertSame('0', $this->transformer->reverseTransform($input)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformPartiallyNull() + { + $input = array( + 'a' => 'Foo', + 'b' => 'Foo', + 'c' => null, + ); + + $this->transformer->reverseTransform($input); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformDifferences() + { + $input = array( + 'a' => 'Foo', + 'b' => 'Bar', + 'c' => 'Foo', + ); + + $this->transformer->reverseTransform($input); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + */ + public function testReverseTransformRequiresArray() + { + $this->transformer->reverseTransform('12345'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php new file mode 100644 index 0000000..a971ea2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener; + +class FixUrlProtocolListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testFixHttpUrl() + { + $data = 'www.symfony.com'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new FixUrlProtocolListener('http'); + $filter->onSubmit($event); + + $this->assertEquals('http://www.symfony.com', $event->getData()); + } + + public function testSkipKnownUrl() + { + $data = 'http://www.symfony.com'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new FixUrlProtocolListener('http'); + $filter->onSubmit($event); + + $this->assertEquals('http://www.symfony.com', $event->getData()); + } + + public function testSkipOtherProtocol() + { + $data = 'ftp://www.symfony.com'; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new FixUrlProtocolListener('http'); + $filter->onSubmit($event); + + $this->assertEquals('ftp://www.symfony.com', $event->getData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash new file mode 100644 index 0000000..b636f4b Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/Fixtures/randomhash differ diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php new file mode 100644 index 0000000..6f46c9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormBuilder; + +class MergeCollectionListenerArrayObjectTest extends MergeCollectionListenerTest +{ + protected function getData(array $data) + { + return new \ArrayObject($data); + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, '\ArrayObject', $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php new file mode 100644 index 0000000..c0f3d59 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormBuilder; + +class MergeCollectionListenerArrayTest extends MergeCollectionListenerTest +{ + protected function getData(array $data) + { + return $data; + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php new file mode 100644 index 0000000..5eb6c7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\Tests\Fixtures\CustomArrayObject; +use Symfony\Component\Form\FormBuilder; + +class MergeCollectionListenerCustomArrayObjectTest extends MergeCollectionListenerTest +{ + protected function getData(array $data) + { + return new CustomArrayObject($data); + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, 'Symfony\Component\Form\Tests\Fixtures\CustomArrayObject', $this->dispatcher, $this->factory); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php new file mode 100644 index 0000000..2d7ecfe --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; + +abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase +{ + protected $dispatcher; + protected $factory; + protected $form; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->form = $this->getForm('axes'); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->form = null; + } + + abstract protected function getBuilder($name = 'name'); + + protected function getForm($name = 'name', $propertyPath = null) + { + $propertyPath = $propertyPath ?: $name; + + return $this->getBuilder($name)->setAttribute('property_path', $propertyPath)->getForm(); + } + + protected function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + public function getBooleanMatrix1() + { + return array( + array(true), + array(false), + ); + } + + public function getBooleanMatrix2() + { + return array( + array(true, true), + array(true, false), + array(false, true), + array(false, false), + ); + } + + abstract protected function getData(array $data); + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testAddExtraEntriesIfAllowAdd($allowDelete) + { + $originalData = $this->getData(array(1 => 'second')); + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(true, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // The original object was modified + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // The original object matches the new object + $this->assertEquals($newData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testAddExtraEntriesIfAllowAddDontOverwriteExistingIndices($allowDelete) + { + $originalData = $this->getData(array(1 => 'first')); + $newData = $this->getData(array(0 => 'first', 1 => 'second')); + + $listener = new MergeCollectionListener(true, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // The original object was modified + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // The original object matches the new object + $this->assertEquals($this->getData(array(1 => 'first', 2 => 'second')), $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDoNothingIfNotAllowAdd($allowDelete) + { + $originalDataArray = array(1 => 'second'); + $originalData = $this->getData($originalDataArray); + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(false, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // We still have the original object + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // Nothing was removed + $this->assertEquals($this->getData($originalDataArray), $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testRemoveMissingEntriesIfAllowDelete($allowAdd) + { + $originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + $newData = $this->getData(array(1 => 'second')); + + $listener = new MergeCollectionListener($allowAdd, true); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // The original object was modified + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // The original object matches the new object + $this->assertEquals($newData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDoNothingIfNotAllowDelete($allowAdd) + { + $originalDataArray = array(0 => 'first', 1 => 'second', 2 => 'third'); + $originalData = $this->getData($originalDataArray); + $newData = $this->getData(array(1 => 'second')); + + $listener = new MergeCollectionListener($allowAdd, false); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + // We still have the original object + if (is_object($originalData)) { + $this->assertSame($originalData, $event->getData()); + } + + // Nothing was removed + $this->assertEquals($this->getData($originalDataArray), $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix2 + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequireArrayOrTraversable($allowAdd, $allowDelete) + { + $newData = 'no array or traversable'; + $event = new FormEvent($this->form, $newData); + $listener = new MergeCollectionListener($allowAdd, $allowDelete); + $listener->onSubmit($event); + } + + public function testDealWithNullData() + { + $originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + $newData = null; + + $listener = new MergeCollectionListener(false, false); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + $this->assertSame($originalData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDealWithNullOriginalDataIfAllowAdd($allowDelete) + { + $originalData = null; + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(true, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + $this->assertSame($newData, $event->getData()); + } + + /** + * @dataProvider getBooleanMatrix1 + */ + public function testDontDealWithNullOriginalDataIfNotAllowAdd($allowDelete) + { + $originalData = null; + $newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $listener = new MergeCollectionListener(false, $allowDelete); + + $this->form->setData($originalData); + + $event = new FormEvent($this->form, $newData); + $listener->onSubmit($event); + + $this->assertNull($event->getData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php new file mode 100644 index 0000000..e4959a3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php @@ -0,0 +1,277 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormEvent; + +class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + private $factory; + private $form; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->form = null; + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory); + } + + protected function getForm($name = 'name') + { + return $this->getBuilder($name)->getForm(); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + protected function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + public function testPreSetDataResizesForm() + { + $this->form->add($this->getForm('0')); + $this->form->add($this->getForm('1')); + + $this->factory->expects($this->at(0)) + ->method('createNamed') + ->with(1, 'text', null, array('property_path' => '[1]', 'attr' => array('maxlength' => 10), 'auto_initialize' => false)) + ->will($this->returnValue($this->getForm('1'))); + $this->factory->expects($this->at(1)) + ->method('createNamed') + ->with(2, 'text', null, array('property_path' => '[2]', 'attr' => array('maxlength' => 10), 'auto_initialize' => false)) + ->will($this->returnValue($this->getForm('2'))); + + $data = array(1 => 'string', 2 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array('attr' => array('maxlength' => 10)), false, false); + $listener->preSetData($event); + + $this->assertFalse($this->form->has('0')); + $this->assertTrue($this->form->has('1')); + $this->assertTrue($this->form->has('2')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testPreSetDataRequiresArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSetData($event); + } + + public function testPreSetDataDealsWithNullData() + { + $this->factory->expects($this->never())->method('createNamed'); + + $data = null; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSetData($event); + } + + public function testPreSubmitResizesUpIfAllowAdd() + { + $this->form->add($this->getForm('0')); + + $this->factory->expects($this->once()) + ->method('createNamed') + ->with(1, 'text', null, array('property_path' => '[1]', 'attr' => array('maxlength' => 10), 'auto_initialize' => false)) + ->will($this->returnValue($this->getForm('1'))); + + $data = array(0 => 'string', 1 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array('attr' => array('maxlength' => 10)), true, false); + $listener->preSubmit($event); + + $this->assertTrue($this->form->has('0')); + $this->assertTrue($this->form->has('1')); + } + + public function testPreSubmitResizesDownIfAllowDelete() + { + $this->form->add($this->getForm('0')); + $this->form->add($this->getForm('1')); + + $data = array(0 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertTrue($this->form->has('0')); + $this->assertFalse($this->form->has('1')); + } + + // fix for https://github.com/symfony/symfony/pull/493 + public function testPreSubmitRemovesZeroKeys() + { + $this->form->add($this->getForm('0')); + + $data = array(); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('0')); + } + + public function testPreSubmitDoesNothingIfNotAllowAddNorAllowDelete() + { + $this->form->add($this->getForm('0')); + $this->form->add($this->getForm('1')); + + $data = array(0 => 'string', 2 => 'string'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSubmit($event); + + $this->assertTrue($this->form->has('0')); + $this->assertTrue($this->form->has('1')); + $this->assertFalse($this->form->has('2')); + } + + public function testPreSubmitDealsWithNoArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('1')); + } + + public function testPreSubmitDealsWithNullData() + { + $this->form->add($this->getForm('1')); + + $data = null; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('1')); + } + + // fixes https://github.com/symfony/symfony/pull/40 + public function testPreSubmitDealsWithEmptyData() + { + $this->form->add($this->getForm('1')); + + $data = ''; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->preSubmit($event); + + $this->assertFalse($this->form->has('1')); + } + + public function testOnSubmitNormDataRemovesEntriesMissingInTheFormIfAllowDelete() + { + $this->form->add($this->getForm('1')); + + $data = array(0 => 'first', 1 => 'second', 2 => 'third'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->onSubmit($event); + + $this->assertEquals(array(1 => 'second'), $event->getData()); + } + + public function testOnSubmitNormDataDoesNothingIfNotAllowDelete() + { + $this->form->add($this->getForm('1')); + + $data = array(0 => 'first', 1 => 'second', 2 => 'third'); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->onSubmit($event); + + $this->assertEquals($data, $event->getData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testOnSubmitNormDataRequiresArrayOrTraversable() + { + $data = 'no array or traversable'; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, false); + $listener->onSubmit($event); + } + + public function testOnSubmitNormDataDealsWithNullData() + { + $this->form->add($this->getForm('1')); + + $data = null; + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->onSubmit($event); + + $this->assertEquals(array(), $event->getData()); + } + + public function testOnSubmitDealsWithObjectBackedIteratorAggregate() + { + $this->form->add($this->getForm('1')); + + $data = new \ArrayObject(array(0 => 'first', 1 => 'second', 2 => 'third')); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->onSubmit($event); + + $this->assertArrayNotHasKey(0, $event->getData()); + $this->assertArrayNotHasKey(2, $event->getData()); + } + + public function testOnSubmitDealsWithArrayBackedIteratorAggregate() + { + $this->form->add($this->getForm('1')); + + $data = new ArrayCollection(array(0 => 'first', 1 => 'second', 2 => 'third')); + $event = new FormEvent($this->form, $data); + $listener = new ResizeFormListener('text', array(), false, true); + $listener->onSubmit($event); + + $this->assertArrayNotHasKey(0, $event->getData()); + $this->assertArrayNotHasKey(2, $event->getData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php new file mode 100644 index 0000000..959eb92 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; + +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; + +class TrimListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testTrim() + { + $data = ' Foo! '; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new TrimListener(); + $filter->preSubmit($event); + + $this->assertEquals('Foo!', $event->getData()); + } + + public function testTrimSkipNonStrings() + { + $data = 1234; + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $event = new FormEvent($form, $data); + + $filter = new TrimListener(); + $filter->preSubmit($event); + + $this->assertSame(1234, $event->getData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php new file mode 100644 index 0000000..801ffd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +/** + * @author Bernhard Schussek + */ +abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testPassDisabledAsOption() + { + $form = $this->factory->create($this->getTestedType(), null, array('disabled' => true)); + + $this->assertTrue($form->isDisabled()); + } + + public function testPassIdAndNameToView() + { + $view = $this->factory->createNamed('name', $this->getTestedType()) + ->createView(); + + $this->assertEquals('name', $view->vars['id']); + $this->assertEquals('name', $view->vars['name']); + $this->assertEquals('name', $view->vars['full_name']); + } + + public function testStripLeadingUnderscoresAndDigitsFromId() + { + $view = $this->factory->createNamed('_09name', $this->getTestedType()) + ->createView(); + + $this->assertEquals('name', $view->vars['id']); + $this->assertEquals('_09name', $view->vars['name']); + $this->assertEquals('_09name', $view->vars['full_name']); + } + + public function testPassIdAndNameToViewWithParent() + { + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertEquals('parent_child', $view['child']->vars['id']); + $this->assertEquals('child', $view['child']->vars['name']); + $this->assertEquals('parent[child]', $view['child']->vars['full_name']); + } + + public function testPassIdAndNameToViewWithGrandParent() + { + $builder = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + $builder->get('child')->add('grand_child', $this->getTestedType()); + $view = $builder->getForm()->createView(); + + $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->vars['id']); + $this->assertEquals('grand_child', $view['child']['grand_child']->vars['name']); + $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->vars['full_name']); + } + + public function testPassTranslationDomainToView() + { + $form = $this->factory->create($this->getTestedType(), null, array( + 'translation_domain' => 'domain', + )); + $view = $form->createView(); + + $this->assertSame('domain', $view->vars['translation_domain']); + } + + public function testInheritTranslationDomainFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => 'domain', + )) + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['translation_domain']); + } + + public function testPreferOwnTranslationDomain() + { + $view = $this->factory + ->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => 'parent_domain', + )) + ->add('child', $this->getTestedType(), array( + 'translation_domain' => 'domain', + )) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['translation_domain']); + } + + public function testDefaultTranslationDomain() + { + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); + + $this->assertNull($view['child']->vars['translation_domain']); + } + + public function testPassLabelToView() + { + $form = $this->factory->createNamed('__test___field', $this->getTestedType(), null, array('label' => 'My label')); + $view = $form->createView(); + + $this->assertSame('My label', $view->vars['label']); + } + + public function testPassMultipartFalseToView() + { + $form = $this->factory->create($this->getTestedType()); + $view = $form->createView(); + + $this->assertFalse($view->vars['multipart']); + } + + abstract protected function getTestedType(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/BirthdayTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/BirthdayTypeTest.php new file mode 100644 index 0000000..867816b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/BirthdayTypeTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +/** + * @author Stepan Anchugov + */ +class BirthdayTypeTest extends BaseTypeTest +{ + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testSetInvalidYearsOption() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\BirthdayType', null, array( + 'years' => 'bad value', + )); + } + + protected function getTestedType() + { + return 'Symfony\Component\Form\Extension\Core\Type\BirthdayType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php new file mode 100644 index 0000000..4d480d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ButtonTypeTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +/** + * @author Bernhard Schussek + */ +class ButtonTypeTest extends BaseTypeTest +{ + public function testCreateButtonInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\Button', $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ButtonType')); + } + + protected function getTestedType() + { + return 'Symfony\Component\Form\Extension\Core\Type\ButtonType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php new file mode 100644 index 0000000..65971a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\CallbackTransformer; + +class CheckboxTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testDataIsFalseByDefault() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType'); + + $this->assertFalse($form->getData()); + $this->assertFalse($form->getNormData()); + $this->assertNull($form->getViewData()); + } + + public function testPassValueToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array('value' => 'foobar')); + $view = $form->createView(); + + $this->assertEquals('foobar', $view->vars['value']); + } + + public function testCheckedIfDataTrue() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType'); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->vars['checked']); + } + + public function testCheckedIfDataTrueWithEmptyValue() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array('value' => '')); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->vars['checked']); + } + + public function testNotCheckedIfDataFalse() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType'); + $form->setData(false); + $view = $form->createView(); + + $this->assertFalse($view->vars['checked']); + } + + public function testSubmitWithValueChecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => 'foobar', + )); + $form->submit('foobar'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + } + + public function testSubmitWithRandomValueChecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => 'foobar', + )); + $form->submit('krixikraxi'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + } + + public function testSubmitWithValueUnchecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => 'foobar', + )); + $form->submit(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testSubmitWithEmptyValueChecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => '', + )); + $form->submit(''); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitWithEmptyValueUnchecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => '', + )); + $form->submit(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testSubmitWithEmptyValueAndFalseUnchecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => '', + )); + $form->submit(false); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getViewData()); + } + + public function testSubmitWithEmptyValueAndTrueChecked() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CheckboxType', null, array( + 'value' => '', + )); + $form->submit(true); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + /** + * @dataProvider provideCustomModelTransformerData + */ + public function testCustomModelTransformer($data, $checked) + { + // present a binary status field as a checkbox + $transformer = new CallbackTransformer( + function ($value) { + return 'checked' == $value; + }, + function ($value) { + return $value ? 'checked' : 'unchecked'; + } + ); + + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\CheckboxType') + ->addModelTransformer($transformer) + ->getForm(); + + $form->setData($data); + $view = $form->createView(); + + $this->assertSame($data, $form->getData()); + $this->assertSame($checked, $form->getNormData()); + $this->assertEquals($checked, $view->vars['checked']); + } + + public function provideCustomModelTransformerData() + { + return array( + array('checked', true), + array('unchecked', false), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php new file mode 100644 index 0000000..3e0ea22 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypePerformanceTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\FormPerformanceTestCase; + +/** + * @author Bernhard Schussek + */ +class ChoiceTypePerformanceTest extends FormPerformanceTestCase +{ + /** + * This test case is realistic in collection forms where each + * row contains the same choice field. + * + * @group benchmark + */ + public function testSameChoiceFieldCreatedMultipleTimes() + { + $this->setMaxRunningTime(1); + $choices = range(1, 300); + + for ($i = 0; $i < 100; ++$i) { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', mt_rand(1, 400), array( + 'choices' => $choices, + )); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php new file mode 100644 index 0000000..f5fff1e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -0,0 +1,1482 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; + +class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + private $choices = array( + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + 'Jon' => 'd', + 'Roman' => 'e', + ); + + private $objectChoices; + + protected $groupedChoices = array( + 'Symfony' => array( + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + ), + 'Doctrine' => array( + 'Jon' => 'd', + 'Roman' => 'e', + ), + ); + + protected function setUp() + { + parent::setUp(); + + $this->objectChoices = array( + (object) array('id' => 1, 'name' => 'Bernhard'), + (object) array('id' => 2, 'name' => 'Fabien'), + (object) array('id' => 3, 'name' => 'Kris'), + (object) array('id' => 4, 'name' => 'Jon'), + (object) array('id' => 5, 'name' => 'Roman'), + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->objectChoices = null; + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testChoicesOptionExpectsArrayOrTraversable() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => new \stdClass(), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testChoiceLoaderOptionExpectsChoiceLoaderInterface() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choice_loader' => new \stdClass(), + )); + } + + public function testChoiceListAndChoicesCanBeEmpty() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + )); + } + + public function testExpandedChoicesOptionsTurnIntoChildren() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'expanded' => true, + 'choices' => $this->choices, + )); + + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + public function testPlaceholderPresentOnNonRequiredExpandedSingleChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $this->assertTrue(isset($form['placeholder'])); + $this->assertCount(count($this->choices) + 1, $form, 'Each choice should become a new field'); + } + + public function testPlaceholderNotPresentIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + public function testPlaceholderNotPresentIfMultiple() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + public function testPlaceholderNotPresentIfEmptyChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => array( + 'Empty' => '', + 'Not empty' => 1, + ), + )); + + $this->assertFalse(isset($form['placeholder'])); + $this->assertCount(2, $form, 'Each choice should become a new field'); + } + + public function testExpandedChoicesOptionsAreFlattened() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'expanded' => true, + 'choices' => $this->groupedChoices, + )); + + $flattened = array(); + foreach ($this->groupedChoices as $choices) { + $flattened = array_merge($flattened, array_keys($choices)); + } + + $this->assertCount($form->count(), $flattened, 'Each nested choice should become a new field, not the groups'); + + foreach ($flattened as $value => $choice) { + $this->assertTrue($form->has($value), 'Flattened choice is named after it\'s value'); + } + } + + public function testExpandedChoicesOptionsAreFlattenedObjectChoices() + { + $obj1 = (object) array('id' => 1, 'name' => 'Bernhard'); + $obj2 = (object) array('id' => 2, 'name' => 'Fabien'); + $obj3 = (object) array('id' => 3, 'name' => 'Kris'); + $obj4 = (object) array('id' => 4, 'name' => 'Jon'); + $obj5 = (object) array('id' => 5, 'name' => 'Roman'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'expanded' => true, + 'choices' => array( + 'Symfony' => array($obj1, $obj2, $obj3), + 'Doctrine' => array($obj4, $obj5), + ), + 'choice_name' => 'id', + )); + + $this->assertSame(5, $form->count(), 'Each nested choice should become a new field, not the groups'); + $this->assertTrue($form->has(1)); + $this->assertTrue($form->has(2)); + $this->assertTrue($form->has(3)); + $this->assertTrue($form->has(4)); + $this->assertTrue($form->has(5)); + } + + public function testExpandedCheckboxesAreNeverRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + foreach ($form as $child) { + $this->assertFalse($child->isRequired()); + } + } + + public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + foreach ($form as $child) { + $this->assertTrue($child->isRequired()); + } + } + + public function testExpandedRadiosAreNotRequiredIfChoiceChildIsNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + foreach ($form as $child) { + $this->assertFalse($child->isRequired()); + } + } + + public function testSubmitSingleNonExpanded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertEquals('b', $form->getData()); + $this->assertEquals('b', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleNonExpandedInvalidChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit('foobar'); + + $this->assertNull($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + $this->assertFalse($form->isSynchronized()); + } + + public function testSubmitSingleNonExpandedNull() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleNonExpandedNullNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => array(), + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleNonExpandedEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleNonExpandedEmptyExplicitEmptyChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => array( + 'Empty' => 'EMPTY_CHOICE', + ), + 'choice_value' => function () { + return ''; + }, + )); + + $form->submit(''); + + $this->assertSame('EMPTY_CHOICE', $form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleNonExpandedEmptyNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => array(), + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleNonExpandedFalse() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleNonExpandedFalseNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => array(), + )); + + $form->submit(false); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleNonExpandedObjectChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->objectChoices, + 'choice_label' => 'name', + 'choice_value' => 'id', + )); + + // "id" value of the second entry + $form->submit('2'); + + $this->assertEquals($this->objectChoices[1], $form->getData()); + $this->assertEquals('2', $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitMultipleNonExpanded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'b')); + + $this->assertEquals(array('a', 'b'), $form->getData()); + $this->assertEquals(array('a', 'b'), $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitMultipleNonExpandedEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(array()); + + $this->assertSame(array(), $form->getData()); + $this->assertSame(array(), $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitMultipleNonExpandedEmptyNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => array(), + )); + + $form->submit(array()); + + $this->assertSame(array(), $form->getData()); + $this->assertSame(array(), $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitMultipleNonExpandedInvalidScalarChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit('foobar'); + + $this->assertNull($form->getData()); + $this->assertEquals('foobar', $form->getViewData()); + $this->assertFalse($form->isSynchronized()); + } + + public function testSubmitMultipleNonExpandedInvalidArrayChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'foobar')); + + $this->assertNull($form->getData()); + $this->assertEquals(array('a', 'foobar'), $form->getViewData()); + $this->assertFalse($form->isSynchronized()); + } + + public function testSubmitMultipleNonExpandedObjectChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->objectChoices, + 'choice_label' => 'name', + 'choice_value' => 'id', + )); + + $form->submit(array('2', '3')); + + $this->assertEquals(array($this->objectChoices[1], $this->objectChoices[2]), $form->getData()); + $this->assertEquals(array('2', '3'), $form->getViewData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertSame('b', $form->getData()); + $this->assertSame('b', $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('b', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedRequiredInvalidChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit('foobar'); + + $this->assertNull($form->getData()); + $this->assertSame('foobar', $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertFalse($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedNonRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit('b'); + + $this->assertSame('b', $form->getData()); + $this->assertSame('b', $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form['placeholder']->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form['placeholder']->getViewData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('b', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedNonRequiredInvalidChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit('foobar'); + + $this->assertNull($form->getData()); + $this->assertSame('foobar', $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertFalse($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitSingleExpandedRequiredNull() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleExpandedRequiredNullNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => array(), + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedRequiredEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleExpandedRequiredEmptyNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => array(), + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedRequiredFalse() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleExpandedRequiredFalseNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => true, + 'choices' => array(), + )); + + $form->submit(false); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedNonRequiredNull() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form['placeholder']->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('', $form['placeholder']->getViewData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleExpandedNonRequiredNullNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => array(), + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedNonRequiredEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form['placeholder']->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('', $form['placeholder']->getViewData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleExpandedNonRequiredEmptyNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => array(), + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedNonRequiredFalse() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => $this->choices, + )); + + $form->submit(false); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form['placeholder']->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('', $form['placeholder']->getViewData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitSingleExpandedNonRequiredFalseNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'required' => false, + 'choices' => array(), + )); + + $form->submit(false); + + $this->assertNull($form->getData()); + $this->assertNull($form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitSingleExpandedWithEmptyChild() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => array( + 'Empty' => '', + 'Not empty' => 1, + ), + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertSame('', $form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + } + + public function testSubmitSingleExpandedObjectChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => $this->objectChoices, + 'choice_label' => 'name', + 'choice_value' => 'id', + )); + + $form->submit('2'); + + $this->assertSame($this->objectChoices[1], $form->getData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertSame('2', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpanded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'c')); + + $this->assertSame(array('a', 'c'), $form->getData()); + $this->assertSame(array('a', 'c'), $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('a', $form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertSame('c', $form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpandedInvalidScalarChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit('foobar'); + + $this->assertNull($form->getData()); + $this->assertSame('foobar', $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertFalse($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpandedInvalidArrayChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array('a', 'foobar')); + + $this->assertNull($form->getData()); + $this->assertSame(array('a', 'foobar'), $form->getViewData()); + $this->assertEmpty($form->getExtraData()); + $this->assertFalse($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSubmitMultipleExpandedEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->submit(array()); + + $this->assertSame(array(), $form->getData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + // In edge cases (for example, when choices are loaded dynamically by a + // loader), the choices may be empty. Make sure to behave the same as when + // choices are available. + public function testSubmitMultipleExpandedEmptyNoChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array(), + )); + + $form->submit(array()); + + $this->assertSame(array(), $form->getData()); + $this->assertTrue($form->isSynchronized()); + } + + public function testSubmitMultipleExpandedWithEmptyChild() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array( + 'Empty' => '', + 'Not Empty' => 1, + 'Not Empty 2' => 2, + ), + )); + + $form->submit(array('', '2')); + + $this->assertSame(array('', 2), $form->getData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertSame('', $form[0]->getViewData()); + $this->assertNull($form[1]->getViewData()); + $this->assertSame('2', $form[2]->getViewData()); + } + + public function testSubmitMultipleExpandedObjectChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => $this->objectChoices, + 'choice_label' => 'name', + 'choice_value' => 'id', + )); + + $form->submit(array('1', '2')); + + $this->assertSame(array($this->objectChoices[0], $this->objectChoices[1]), $form->getData()); + $this->assertTrue($form->isSynchronized()); + + $this->assertTrue($form[0]->getData()); + $this->assertTrue($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertSame('1', $form[0]->getViewData()); + $this->assertSame('2', $form[1]->getViewData()); + $this->assertNull($form[2]->getViewData()); + $this->assertNull($form[3]->getViewData()); + $this->assertNull($form[4]->getViewData()); + } + + public function testSingleSelectedObjectChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', $this->objectChoices[3], array( + 'multiple' => false, + 'expanded' => false, + 'choices' => $this->objectChoices, + 'choice_label' => 'name', + 'choice_value' => 'id', + )); + + $view = $form->createView(); + $selectedChecker = $view->vars['is_selected']; + + $this->assertTrue($selectedChecker($view->vars['choices'][3]->value, $view->vars['value'])); + $this->assertFalse($selectedChecker($view->vars['choices'][1]->value, $view->vars['value'])); + } + + public function testMultipleSelectedObjectChoices() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', array($this->objectChoices[3]), array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->objectChoices, + 'choice_label' => 'name', + 'choice_value' => 'id', + )); + + $view = $form->createView(); + $selectedChecker = $view->vars['is_selected']; + + $this->assertTrue($selectedChecker($view->vars['choices'][3]->value, $view->vars['value'])); + $this->assertFalse($selectedChecker($view->vars['choices'][1]->value, $view->vars['value'])); + } + + public function testPassRequiredToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['required']); + } + + public function testPassNonRequiredToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'required' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertFalse($view->vars['required']); + } + + public function testPassMultipleToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['multiple']); + } + + public function testPassExpandedToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'expanded' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertTrue($view->vars['expanded']); + } + + public function testPassChoiceTranslationDomainToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertNull($view->vars['choice_translation_domain']); + } + + public function testChoiceTranslationDomainWithTrueValueToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $this->choices, + 'choice_translation_domain' => true, + )); + $view = $form->createView(); + + $this->assertNull($view->vars['choice_translation_domain']); + } + + public function testDefaultChoiceTranslationDomainIsSameAsTranslationDomainToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $this->choices, + 'translation_domain' => 'foo', + )); + $view = $form->createView(); + + $this->assertEquals('foo', $view->vars['choice_translation_domain']); + } + + public function testInheritChoiceTranslationDomainFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'translation_domain' => 'domain', + )) + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array( + 'choices' => array(), + )) + ->getForm() + ->createView(); + + $this->assertEquals('domain', $view['child']->vars['choice_translation_domain']); + } + + public function testPlaceholderIsNullByDefaultIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'required' => true, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertNull($view->vars['placeholder']); + } + + public function testPlaceholderIsEmptyStringByDefaultIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => false, + 'required' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertSame('', $view->vars['placeholder']); + } + + /** + * @dataProvider getOptionsWithPlaceholder + */ + public function testPassPlaceholderToView($multiple, $expanded, $required, $placeholder, $viewValue) + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'required' => $required, + 'placeholder' => $placeholder, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertEquals($viewValue, $view->vars['placeholder']); + $this->assertFalse($view->vars['placeholder_in_choices']); + } + + /** + * @dataProvider getOptionsWithPlaceholder + */ + public function testDontPassPlaceholderIfContainedInChoices($multiple, $expanded, $required, $placeholder, $viewValue) + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'required' => $required, + 'placeholder' => $placeholder, + 'choices' => array('A' => 'a', 'Empty' => ''), + )); + $view = $form->createView(); + + $this->assertNull($view->vars['placeholder']); + $this->assertTrue($view->vars['placeholder_in_choices']); + } + + public function getOptionsWithPlaceholder() + { + return array( + // single non-expanded + array(false, false, false, 'foobar', 'foobar'), + array(false, false, false, '', ''), + array(false, false, false, null, null), + array(false, false, false, false, null), + array(false, false, true, 'foobar', 'foobar'), + array(false, false, true, '', ''), + array(false, false, true, null, null), + array(false, false, true, false, null), + // single expanded + array(false, true, false, 'foobar', 'foobar'), + // radios should never have an empty label + array(false, true, false, '', 'None'), + array(false, true, false, null, null), + array(false, true, false, false, null), + array(false, true, true, 'foobar', 'foobar'), + // radios should never have an empty label + array(false, true, true, '', 'None'), + array(false, true, true, null, null), + array(false, true, true, false, null), + // multiple non-expanded + array(true, false, false, 'foobar', null), + array(true, false, false, '', null), + array(true, false, false, null, null), + array(true, false, false, false, null), + array(true, false, true, 'foobar', null), + array(true, false, true, '', null), + array(true, false, true, null, null), + array(true, false, true, false, null), + // multiple expanded + array(true, true, false, 'foobar', null), + array(true, true, false, '', null), + array(true, true, false, null, null), + array(true, true, false, false, null), + array(true, true, true, 'foobar', null), + array(true, true, true, '', null), + array(true, true, true, null, null), + array(true, true, true, false, null), + ); + } + + public function testPassChoicesToView() + { + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $choices, + )); + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('a', 'a', 'A'), + new ChoiceView('b', 'b', 'B'), + new ChoiceView('c', 'c', 'C'), + new ChoiceView('d', 'd', 'D'), + ), $view->vars['choices']); + } + + public function testPassPreferredChoicesToView() + { + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $choices, + 'preferred_choices' => array('b', 'd'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + 0 => new ChoiceView('a', 'a', 'A'), + 2 => new ChoiceView('c', 'c', 'C'), + ), $view->vars['choices']); + $this->assertEquals(array( + 1 => new ChoiceView('b', 'b', 'B'), + 3 => new ChoiceView('d', 'd', 'D'), + ), $view->vars['preferred_choices']); + } + + public function testPassHierarchicalChoicesToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => $this->groupedChoices, + 'preferred_choices' => array('b', 'd'), + )); + $view = $form->createView(); + + $this->assertEquals(array( + 'Symfony' => new ChoiceGroupView('Symfony', array( + 0 => new ChoiceView('a', 'a', 'Bernhard'), + 2 => new ChoiceView('c', 'c', 'Kris'), + )), + 'Doctrine' => new ChoiceGroupView('Doctrine', array( + 4 => new ChoiceView('e', 'e', 'Roman'), + )), + ), $view->vars['choices']); + $this->assertEquals(array( + 'Symfony' => new ChoiceGroupView('Symfony', array( + 1 => new ChoiceView('b', 'b', 'Fabien'), + )), + 'Doctrine' => new ChoiceGroupView('Doctrine', array( + 3 => new ChoiceView('d', 'd', 'Jon'), + )), + ), $view->vars['preferred_choices']); + } + + public function testPassChoiceDataToView() + { + $obj1 = (object) array('value' => 'a', 'label' => 'A'); + $obj2 = (object) array('value' => 'b', 'label' => 'B'); + $obj3 = (object) array('value' => 'c', 'label' => 'C'); + $obj4 = (object) array('value' => 'd', 'label' => 'D'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => array($obj1, $obj2, $obj3, $obj4), + 'choice_label' => 'label', + 'choice_value' => 'value', + )); + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView($obj1, 'a', 'A'), + new ChoiceView($obj2, 'b', 'B'), + new ChoiceView($obj3, 'c', 'C'), + new ChoiceView($obj4, 'd', 'D'), + ), $view->vars['choices']); + } + + public function testAdjustFullNameForMultipleNonExpanded() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + )); + $view = $form->createView(); + + $this->assertSame('name[]', $view->vars['full_name']); + } + + // https://github.com/symfony/symfony/issues/3298 + public function testInitializeWithEmptyChoices() + { + $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => array(), + )); + } + + public function testInitializeWithDefaultObjectChoice() + { + $obj1 = (object) array('value' => 'a', 'label' => 'A'); + $obj2 = (object) array('value' => 'b', 'label' => 'B'); + $obj3 = (object) array('value' => 'c', 'label' => 'C'); + $obj4 = (object) array('value' => 'd', 'label' => 'D'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices' => array($obj1, $obj2, $obj3, $obj4), + 'choice_label' => 'label', + 'choice_value' => 'value', + // Used to break because "data_class" was inferred, which needs to + // remain null in every case (because it refers to the view format) + 'data' => $obj3, + )); + + // Trigger data initialization + $form->getViewData(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php new file mode 100644 index 0000000..9297d28 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -0,0 +1,317 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Form; +use Symfony\Component\Form\Tests\Fixtures\Author; + +class CollectionTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testContainsNoChildByDefault() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertCount(0, $form); + } + + public function testSetDataAdjustsSize() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'entry_options' => array( + 'attr' => array('maxlength' => 20), + ), + )); + $form->setData(array('foo@foo.com', 'foo@bar.com')); + + $this->assertInstanceOf('Symfony\Component\Form\Form', $form[0]); + $this->assertInstanceOf('Symfony\Component\Form\Form', $form[1]); + $this->assertCount(2, $form); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + $this->assertEquals('foo@bar.com', $form[1]->getData()); + $formAttrs0 = $form[0]->getConfig()->getOption('attr'); + $formAttrs1 = $form[1]->getConfig()->getOption('attr'); + $this->assertEquals(20, $formAttrs0['maxlength']); + $this->assertEquals(20, $formAttrs1['maxlength']); + + $form->setData(array('foo@baz.com')); + $this->assertInstanceOf('Symfony\Component\Form\Form', $form[0]); + $this->assertFalse(isset($form[1])); + $this->assertCount(1, $form); + $this->assertEquals('foo@baz.com', $form[0]->getData()); + $formAttrs0 = $form[0]->getConfig()->getOption('attr'); + $this->assertEquals(20, $formAttrs0['maxlength']); + } + + public function testThrowsExceptionIfObjectIsNotTraversable() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $form->setData(new \stdClass()); + } + + public function testNotResizedIfSubmittedWithMissingData() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + $form->setData(array('foo@foo.com', 'bar@bar.com')); + $form->submit(array('foo@bar.com')); + + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertEquals('foo@bar.com', $form[0]->getData()); + $this->assertEquals('', $form[1]->getData()); + } + + public function testResizedDownIfSubmittedWithMissingDataAndAllowDelete() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'allow_delete' => true, + )); + $form->setData(array('foo@foo.com', 'bar@bar.com')); + $form->submit(array('foo@foo.com')); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + $this->assertEquals(array('foo@foo.com'), $form->getData()); + } + + public function testResizedDownIfSubmittedWithEmptyDataAndDeleteEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'allow_delete' => true, + 'delete_empty' => true, + )); + + $form->setData(array('foo@foo.com', 'bar@bar.com')); + $form->submit(array('foo@foo.com', '')); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + $this->assertEquals(array('foo@foo.com'), $form->getData()); + } + + public function testDontAddEmptyDataIfDeleteEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'allow_add' => true, + 'delete_empty' => true, + )); + + $form->setData(array('foo@foo.com')); + $form->submit(array('foo@foo.com', '')); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + $this->assertEquals(array('foo@foo.com'), $form->getData()); + } + + public function testNoDeleteEmptyIfDeleteNotAllowed() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'allow_delete' => false, + 'delete_empty' => true, + )); + + $form->setData(array('foo@foo.com')); + $form->submit(array('')); + + $this->assertTrue($form->has('0')); + $this->assertEquals('', $form[0]->getData()); + } + + public function testResizedDownIfSubmittedWithCompoundEmptyDataAndDeleteEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Tests\Fixtures\AuthorType', + // If the field is not required, no new Author will be created if the + // form is completely empty + 'entry_options' => array('required' => false), + 'allow_add' => true, + 'delete_empty' => true, + )); + + $form->setData(array(new Author('first', 'last'))); + $form->submit(array( + array('firstName' => 's_first', 'lastName' => 's_last'), + array('firstName' => '', 'lastName' => ''), + )); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals(new Author('s_first', 's_last'), $form[0]->getData()); + $this->assertEquals(array(new Author('s_first', 's_last')), $form->getData()); + } + + public function testNotResizedIfSubmittedWithExtraData() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + $form->setData(array('foo@bar.com')); + $form->submit(array('foo@foo.com', 'bar@bar.com')); + + $this->assertTrue($form->has('0')); + $this->assertFalse($form->has('1')); + $this->assertEquals('foo@foo.com', $form[0]->getData()); + } + + public function testResizedUpIfSubmittedWithExtraDataAndAllowAdd() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'allow_add' => true, + )); + $form->setData(array('foo@bar.com')); + $form->submit(array('foo@bar.com', 'bar@bar.com')); + + $this->assertTrue($form->has('0')); + $this->assertTrue($form->has('1')); + $this->assertEquals('foo@bar.com', $form[0]->getData()); + $this->assertEquals('bar@bar.com', $form[1]->getData()); + $this->assertEquals(array('foo@bar.com', 'bar@bar.com'), $form->getData()); + } + + public function testAllowAddButNoPrototype() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType', + 'allow_add' => true, + 'prototype' => false, + )); + + $this->assertFalse($form->has('__name__')); + } + + public function testPrototypeMultipartPropagation() + { + $form = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType', + 'allow_add' => true, + 'prototype' => true, + )) + ; + + $this->assertTrue($form->createView()->vars['multipart']); + } + + public function testGetDataDoesNotContainsPrototypeNameBeforeDataAreSet() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType', + 'prototype' => true, + 'allow_add' => true, + )); + + $data = $form->getData(); + $this->assertFalse(isset($data['__name__'])); + } + + public function testGetDataDoesNotContainsPrototypeNameAfterDataAreSet() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType', + 'allow_add' => true, + 'prototype' => true, + )); + + $form->setData(array('foobar.png')); + $data = $form->getData(); + $this->assertFalse(isset($data['__name__'])); + } + + public function testPrototypeNameOption() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType', + 'prototype' => true, + 'allow_add' => true, + )); + + $this->assertSame('__name__', $form->getConfig()->getAttribute('prototype')->getName(), '__name__ is the default'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FormType', + 'prototype' => true, + 'allow_add' => true, + 'prototype_name' => '__test__', + )); + + $this->assertSame('__test__', $form->getConfig()->getAttribute('prototype')->getName()); + } + + public function testPrototypeDefaultLabel() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + )); + + $this->assertSame('__test__label__', $form->createView()->vars['prototype']->vars['label']); + } + + public function testPrototypeData() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'allow_add' => true, + 'prototype' => true, + 'prototype_data' => 'foo', + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'entry_options' => array( + 'data' => 'bar', + ), + )); + + $this->assertSame('foo', $form->createView()->vars['prototype']->vars['value']); + } + + public function testPrototypeDefaultRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + )); + + $this->assertTrue($form->createView()->vars['prototype']->vars['required']); + } + + public function testPrototypeSetNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), array( + 'entry_type' => 'Symfony\Component\Form\Extension\Core\Type\FileType', + 'allow_add' => true, + 'prototype' => true, + 'prototype_name' => '__test__', + 'required' => false, + )); + + $this->assertFalse($form->createView()->vars['required'], 'collection is not required'); + $this->assertFalse($form->createView()->vars['prototype']->vars['required'], '"prototype" should not be required'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php new file mode 100644 index 0000000..fb7c4b0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CountryTypeTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class CountryTypeTest extends TestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCountriesAreSelectable() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CountryType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + // Don't check objects for identity + $this->assertContains(new ChoiceView('DE', 'DE', 'Germany'), $choices, '', false, false); + $this->assertContains(new ChoiceView('GB', 'GB', 'United Kingdom'), $choices, '', false, false); + $this->assertContains(new ChoiceView('US', 'US', 'United States'), $choices, '', false, false); + $this->assertContains(new ChoiceView('FR', 'FR', 'France'), $choices, '', false, false); + $this->assertContains(new ChoiceView('MY', 'MY', 'Malaysia'), $choices, '', false, false); + } + + public function testUnknownCountryIsNotIncluded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CountryType', 'Symfony\Component\Form\Extension\Core\Type\CountryType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + foreach ($choices as $choice) { + if ('ZZ' === $choice->value) { + $this->fail('Should not contain choice "ZZ"'); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php new file mode 100644 index 0000000..25e7fdd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/CurrencyTypeTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class CurrencyTypeTest extends TestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCurrenciesAreSelectable() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\CurrencyType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertContains(new ChoiceView('EUR', 'EUR', 'Euro'), $choices, '', false, false); + $this->assertContains(new ChoiceView('USD', 'USD', 'US Dollar'), $choices, '', false, false); + $this->assertContains(new ChoiceView('SIT', 'SIT', 'Slovenian Tolar'), $choices, '', false, false); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php new file mode 100644 index 0000000..766532b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -0,0 +1,539 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Test\TypeTestCase as TestCase; + +class DateTimeTypeTest extends TestCase +{ + protected function setUp() + { + \Locale::setDefault('en'); + + parent::setUp(); + } + + public function testSubmitDateTime() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'datetime', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $dateTime = new \DateTime('2010-06-02 03:04:00 UTC'); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + } + + public function testSubmitString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $this->assertEquals('2010-06-02 03:04:00', $form->getData()); + } + + public function testSubmitTimestamp() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'timestamp', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $dateTime = new \DateTime('2010-06-02 03:04:00 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + } + + public function testSubmitWithoutMinutes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'datetime', + 'with_minutes' => false, + )); + + $form->setData(new \DateTime('2010-06-02 03:04:05 UTC')); + + $input = array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + ), + ); + + $form->submit($input); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 03:00:00 UTC'), $form->getData()); + } + + public function testSubmitWithSeconds() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'datetime', + 'with_seconds' => true, + )); + + $form->setData(new \DateTime('2010-06-02 03:04:05 UTC')); + + $input = array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + 'second' => '5', + ), + ); + + $form->submit($input); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 03:04:05 UTC'), $form->getData()); + } + + public function testSubmitDifferentTimezones() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'date_widget' => 'choice', + 'time_widget' => 'choice', + 'input' => 'string', + 'with_seconds' => true, + )); + + $dateTime = new \DateTime('2010-06-02 03:04:05 Pacific/Tahiti'); + + $form->submit(array( + 'date' => array( + 'day' => (int) $dateTime->format('d'), + 'month' => (int) $dateTime->format('m'), + 'year' => (int) $dateTime->format('Y'), + ), + 'time' => array( + 'hour' => (int) $dateTime->format('H'), + 'minute' => (int) $dateTime->format('i'), + 'second' => (int) $dateTime->format('s'), + ), + )); + + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertEquals($dateTime->format('Y-m-d H:i:s'), $form->getData()); + } + + public function testSubmitDifferentTimezonesDateTime() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $outputTime = new \DateTime('2010-06-02 03:04:00 Pacific/Tahiti'); + + $form->submit('2010-06-02T03:04:00-10:00'); + + $outputTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertDateTimeEquals($outputTime, $form->getData()); + $this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData()); + } + + public function testSubmitStringSingleText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->submit('2010-06-02T03:04:00Z'); + + $this->assertEquals('2010-06-02 03:04:00', $form->getData()); + $this->assertEquals('2010-06-02T03:04:00Z', $form->getViewData()); + } + + public function testSubmitStringSingleTextWithSeconds() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_seconds' => true, + )); + + $form->submit('2010-06-02T03:04:05Z'); + + $this->assertEquals('2010-06-02 03:04:05', $form->getData()); + $this->assertEquals('2010-06-02T03:04:05Z', $form->getViewData()); + } + + public function testSubmitDifferentPattern() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'date_format' => 'MM*yyyy*dd', + 'date_widget' => 'single_text', + 'time_widget' => 'single_text', + 'input' => 'datetime', + )); + + $dateTime = new \DateTime('2010-06-02 03:04'); + + $form->submit(array( + 'date' => '06*2010*02', + 'time' => '03:04', + )); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + } + + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('datetime', $view->vars['type']); + } + + public function testPassDefaultPlaceholderToViewIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'required' => false, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('', $view['date']['year']->vars['placeholder']); + $this->assertSame('', $view['date']['month']->vars['placeholder']); + $this->assertSame('', $view['date']['day']->vars['placeholder']); + $this->assertSame('', $view['time']['hour']->vars['placeholder']); + $this->assertSame('', $view['time']['minute']->vars['placeholder']); + $this->assertSame('', $view['time']['second']->vars['placeholder']); + } + + public function testPassNoPlaceholderToViewIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'required' => true, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['date']['year']->vars['placeholder']); + $this->assertNull($view['date']['month']->vars['placeholder']); + $this->assertNull($view['date']['day']->vars['placeholder']); + $this->assertNull($view['time']['hour']->vars['placeholder']); + $this->assertNull($view['time']['minute']->vars['placeholder']); + $this->assertNull($view['time']['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'placeholder' => 'Empty', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['date']['year']->vars['placeholder']); + $this->assertSame('Empty', $view['date']['month']->vars['placeholder']); + $this->assertSame('Empty', $view['date']['day']->vars['placeholder']); + $this->assertSame('Empty', $view['time']['hour']->vars['placeholder']); + $this->assertSame('Empty', $view['time']['minute']->vars['placeholder']); + $this->assertSame('Empty', $view['time']['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'placeholder' => array( + 'year' => 'Empty year', + 'month' => 'Empty month', + 'day' => 'Empty day', + 'hour' => 'Empty hour', + 'minute' => 'Empty minute', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['date']['year']->vars['placeholder']); + $this->assertSame('Empty month', $view['date']['month']->vars['placeholder']); + $this->assertSame('Empty day', $view['date']['day']->vars['placeholder']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['placeholder']); + $this->assertSame('Empty minute', $view['time']['minute']->vars['placeholder']); + $this->assertSame('Empty second', $view['time']['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'required' => false, + 'placeholder' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['date']['year']->vars['placeholder']); + $this->assertSame('', $view['date']['month']->vars['placeholder']); + $this->assertSame('Empty day', $view['date']['day']->vars['placeholder']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['placeholder']); + $this->assertSame('', $view['time']['minute']->vars['placeholder']); + $this->assertSame('Empty second', $view['time']['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'required' => true, + 'placeholder' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['date']['year']->vars['placeholder']); + $this->assertNull($view['date']['month']->vars['placeholder']); + $this->assertSame('Empty day', $view['date']['day']->vars['placeholder']); + $this->assertSame('Empty hour', $view['time']['hour']->vars['placeholder']); + $this->assertNull($view['time']['minute']->vars['placeholder']); + $this->assertSame('Empty second', $view['time']['second']->vars['placeholder']); + } + + public function testPassHtml5TypeIfSingleTextAndHtml5Format() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertSame('datetime', $view->vars['type']); + } + + public function testDontPassHtml5TypeIfHtml5NotAllowed() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'widget' => 'single_text', + 'html5' => false, + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotHtml5Format() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'widget' => 'single_text', + 'format' => 'yyyy-MM-dd HH:mm', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotSingleText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'widget' => 'text', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDateTypeChoiceErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null); + + $form['date']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['date']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + public function testDateTypeSingleTextErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'date_widget' => 'single_text', + )); + + $form['date']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['date']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + public function testTimeTypeChoiceErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null); + + $form['time']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['time']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + public function testTimeTypeSingleTextErrorsBubbleUp() + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'time_widget' => 'single_text', + )); + + $form['time']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['time']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + public function testPassDefaultChoiceTranslationDomain() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'with_seconds' => true, + )); + + $view = $form->createView(); + + $this->assertFalse($view['date']['year']->vars['choice_translation_domain']); + $this->assertFalse($view['date']['month']->vars['choice_translation_domain']); + $this->assertFalse($view['date']['day']->vars['choice_translation_domain']); + $this->assertFalse($view['time']['hour']->vars['choice_translation_domain']); + $this->assertFalse($view['time']['minute']->vars['choice_translation_domain']); + $this->assertFalse($view['time']['second']->vars['choice_translation_domain']); + } + + public function testPassChoiceTranslationDomainAsString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'choice_translation_domain' => 'messages', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('messages', $view['date']['year']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['date']['month']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['date']['day']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['time']['hour']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['time']['minute']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['time']['second']->vars['choice_translation_domain']); + } + + public function testPassChoiceTranslationDomainAsArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateTimeType', null, array( + 'choice_translation_domain' => array( + 'year' => 'foo', + 'month' => 'test', + 'hour' => 'foo', + 'second' => 'test', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('foo', $view['date']['year']->vars['choice_translation_domain']); + $this->assertSame('test', $view['date']['month']->vars['choice_translation_domain']); + $this->assertFalse($view['date']['day']->vars['choice_translation_domain']); + $this->assertSame('foo', $view['time']['hour']->vars['choice_translation_domain']); + $this->assertFalse($view['time']['minute']->vars['choice_translation_domain']); + $this->assertSame('test', $view['time']['second']->vars['choice_translation_domain']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php new file mode 100644 index 0000000..9159f8c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -0,0 +1,960 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class DateTypeTest extends TestCase +{ + private $defaultTimezone; + + protected function setUp() + { + parent::setUp(); + $this->defaultTimezone = date_default_timezone_get(); + } + + protected function tearDown() + { + date_default_timezone_set($this->defaultTimezone); + \Locale::setDefault('en'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testInvalidWidgetOption() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'fake_widget', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testInvalidInputOption() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'input' => 'fake_input', + )); + } + + public function testSubmitFromSingleTextDateTimeWithDefaultFormat() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $form->submit('2010-06-02'); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('2010-06-02', $form->getViewData()); + } + + public function testSubmitFromSingleTextDateTime() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $form->submit('2.6.2010'); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromSingleTextString() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'string', + )); + + $form->submit('2.6.2010'); + + $this->assertEquals('2010-06-02', $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromSingleTextTimestamp() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'timestamp', + )); + + $form->submit('2.6.2010'); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromSingleTextRaw() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'array', + )); + + $form->submit('2.6.2010'); + + $output = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $this->assertEquals($output, $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + + public function testSubmitFromText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'text', + )); + + $text = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $form->submit($text); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals($text, $form->getViewData()); + } + + public function testSubmitFromChoice() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $text = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $form->submit($text); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals($text, $form->getViewData()); + } + + public function testSubmitFromChoiceEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + 'required' => false, + )); + + $text = array( + 'day' => '', + 'month' => '', + 'year' => '', + ); + + $form->submit($text); + + $this->assertNull($form->getData()); + $this->assertEquals($text, $form->getViewData()); + } + + public function testSubmitFromInputDateTimeDifferentPattern() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'datetime', + )); + + $form->submit('06*2010*02'); + + $this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + public function testSubmitFromInputStringDifferentPattern() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'string', + )); + + $form->submit('06*2010*02'); + + $this->assertEquals('2010-06-02', $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + public function testSubmitFromInputTimestampDifferentPattern() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'timestamp', + )); + + $form->submit('06*2010*02'); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + public function testSubmitFromInputRawDifferentPattern() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'format' => 'MM*yyyy*dd', + 'widget' => 'single_text', + 'input' => 'array', + )); + + $form->submit('06*2010*02'); + + $output = array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ); + + $this->assertEquals($output, $form->getData()); + $this->assertEquals('06*2010*02', $form->getViewData()); + } + + /** + * @dataProvider provideDateFormats + */ + public function testDatePatternWithFormatOption($format, $pattern) + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => $format, + )); + + $view = $form->createView(); + + $this->assertEquals($pattern, $view->vars['date_pattern']); + } + + public function provideDateFormats() + { + return array( + array('dMy', '{{ day }}{{ month }}{{ year }}'), + array('d-M-yyyy', '{{ day }}-{{ month }}-{{ year }}'), + array('M d y', '{{ month }} {{ day }} {{ year }}'), + ); + } + + /** + * This test is to check that the strings '0', '1', '2', '3' are not accepted + * as valid IntlDateFormatter constants for FULL, LONG, MEDIUM or SHORT respectively. + * + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsNoPattern() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => '0', + 'widget' => 'single_text', + 'input' => 'string', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatDoesNotContainYearMonthAndDay() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'months' => array(6, 7), + 'format' => 'yy', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsNoConstant() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => 105, + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfFormatIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => array(), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfYearsIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'years' => 'bad value', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfMonthsIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'months' => 'bad value', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfDaysIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'days' => 'bad value', + )); + } + + public function testSetDataWithNegativeTimezoneOffsetStringInput() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'America/New_York', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->setData('2010-06-02'); + + // 2010-06-02 00:00:00 UTC + // 2010-06-01 20:00:00 UTC-4 + $this->assertEquals('01.06.2010', $form->getViewData()); + } + + public function testSetDataWithNegativeTimezoneOffsetDateTimeInput() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'America/New_York', + 'input' => 'datetime', + 'widget' => 'single_text', + )); + + $dateTime = new \DateTime('2010-06-02 UTC'); + + $form->setData($dateTime); + + // 2010-06-02 00:00:00 UTC + // 2010-06-01 20:00:00 UTC-4 + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals('01.06.2010', $form->getViewData()); + } + + public function testYearsOption() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'years' => array(2010, 2011), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('2010', '2010', '2010'), + new ChoiceView('2011', '2011', '2011'), + ), $view['year']->vars['choices']); + } + + public function testMonthsOption() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'months' => array(6, 7), + 'format' => \IntlDateFormatter::SHORT, + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView(6, '6', '06'), + new ChoiceView(7, '7', '07'), + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionShortFormat() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'months' => array(1, 4), + 'format' => 'dd.MMM.yy', + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView(1, '1', 'Jän'), + new ChoiceView(4, '4', 'Apr.'), + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionLongFormat() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'months' => array(1, 4), + 'format' => 'dd.MMMM.yy', + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView(1, '1', 'Jänner'), + new ChoiceView(4, '4', 'April'), + ), $view['month']->vars['choices']); + } + + public function testMonthsOptionLongFormatWithDifferentTimezone() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'months' => array(1, 4), + 'format' => 'dd.MMMM.yy', + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView(1, '1', 'Jänner'), + new ChoiceView(4, '4', 'April'), + ), $view['month']->vars['choices']); + } + + public function testIsDayWithinRangeReturnsTrueIfWithin() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'days' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView(6, '6', '06'), + new ChoiceView(7, '7', '07'), + ), $view['day']->vars['choices']); + } + + public function testIsPartiallyFilledReturnsFalseIfSingleText() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + )); + + $form->submit('7.6.2010'); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfChoiceAndCompletelyEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $form->submit(array( + 'day' => '', + 'month' => '', + 'year' => '', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfChoiceAndCompletelyFilled() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $form->submit(array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndDayEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + )); + + $form->submit(array( + 'day' => '', + 'month' => '6', + 'year' => '2010', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testPassDatePatternToView() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType'); + $view = $form->createView(); + + $this->assertSame('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentFormat() + { + // we test against "de_AT", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_AT'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => \IntlDateFormatter::LONG, + )); + + $view = $form->createView(); + + $this->assertSame('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentPattern() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => 'MMyyyydd', + )); + + $view = $form->createView(); + + $this->assertSame('{{ month }}{{ year }}{{ day }}', $view->vars['date_pattern']); + } + + public function testPassDatePatternToViewDifferentPatternWithSeparators() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'format' => 'MM*yyyy*dd', + )); + + $view = $form->createView(); + + $this->assertSame('{{ month }}*{{ year }}*{{ day }}', $view->vars['date_pattern']); + } + + public function testDontPassDatePatternIfText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'single_text', + )); + $view = $form->createView(); + + $this->assertFalse(isset($view->vars['date_pattern'])); + } + + public function testDatePatternFormatWithQuotedStrings() + { + // we test against "es_ES", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('es_ES'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + // EEEE, d 'de' MMMM 'de' y + 'format' => \IntlDateFormatter::FULL, + )); + + $view = $form->createView(); + + $this->assertEquals('{{ day }}{{ month }}{{ year }}', $view->vars['date_pattern']); + } + + public function testPassWidgetToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'single_text', + )); + $view = $form->createView(); + + $this->assertSame('single_text', $view->vars['widget']); + } + + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('date', $view->vars['type']); + } + + public function testPassDefaultPlaceholderToViewIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'required' => false, + )); + + $view = $form->createView(); + $this->assertSame('', $view['year']->vars['placeholder']); + $this->assertSame('', $view['month']->vars['placeholder']); + $this->assertSame('', $view['day']->vars['placeholder']); + } + + public function testPassNoPlaceholderToViewIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'required' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['year']->vars['placeholder']); + $this->assertNull($view['month']->vars['placeholder']); + $this->assertNull($view['day']->vars['placeholder']); + } + + public function testPassPlaceholderAsString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'placeholder' => 'Empty', + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['year']->vars['placeholder']); + $this->assertSame('Empty', $view['month']->vars['placeholder']); + $this->assertSame('Empty', $view['day']->vars['placeholder']); + } + + public function testPassPlaceholderAsArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'placeholder' => array( + 'year' => 'Empty year', + 'month' => 'Empty month', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['placeholder']); + $this->assertSame('Empty month', $view['month']->vars['placeholder']); + $this->assertSame('Empty day', $view['day']->vars['placeholder']); + } + + public function testPassPlaceholderAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'required' => false, + 'placeholder' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['placeholder']); + $this->assertSame('', $view['month']->vars['placeholder']); + $this->assertSame('Empty day', $view['day']->vars['placeholder']); + } + + public function testPassPlaceholderAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'required' => true, + 'placeholder' => array( + 'year' => 'Empty year', + 'day' => 'Empty day', + ), + )); + + $view = $form->createView(); + $this->assertSame('Empty year', $view['year']->vars['placeholder']); + $this->assertNull($view['month']->vars['placeholder']); + $this->assertSame('Empty day', $view['day']->vars['placeholder']); + } + + public function testPassHtml5TypeIfSingleTextAndHtml5Format() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertSame('date', $view->vars['type']); + } + + public function testDontPassHtml5TypeIfHtml5NotAllowed() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'single_text', + 'html5' => false, + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotHtml5Format() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'single_text', + 'format' => \IntlDateFormatter::MEDIUM, + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testDontPassHtml5TypeIfNotSingleText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => 'text', + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function provideCompoundWidgets() + { + return array( + array('text'), + array('choice'), + ); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testYearErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => $widget, + )); + $form['year']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['year']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testMonthErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => $widget, + )); + $form['month']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['month']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testDayErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'widget' => $widget, + )); + $form['day']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['day']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + public function testYearsFor32BitsMachines() + { + if (4 !== PHP_INT_SIZE) { + $this->markTestSkipped('PHP 32 bit is required.'); + } + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'years' => range(1900, 2040), + )); + + $view = $form->createView(); + + $listChoices = array(); + foreach (range(1902, 2037) as $y) { + $listChoices[] = new ChoiceView($y, $y, $y); + } + + $this->assertEquals($listChoices, $view['year']->vars['choices']); + } + + public function testPassDefaultChoiceTranslationDomain() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType'); + + $view = $form->createView(); + $this->assertFalse($view['year']->vars['choice_translation_domain']); + $this->assertFalse($view['month']->vars['choice_translation_domain']); + $this->assertFalse($view['day']->vars['choice_translation_domain']); + } + + public function testPassChoiceTranslationDomainAsString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'choice_translation_domain' => 'messages', + )); + + $view = $form->createView(); + $this->assertSame('messages', $view['year']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['month']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['day']->vars['choice_translation_domain']); + } + + public function testPassChoiceTranslationDomainAsArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\DateType', null, array( + 'choice_translation_domain' => array( + 'year' => 'foo', + 'day' => 'test', + ), + )); + + $view = $form->createView(); + $this->assertSame('foo', $view['year']->vars['choice_translation_domain']); + $this->assertFalse($view['month']->vars['choice_translation_domain']); + $this->assertSame('test', $view['day']->vars['choice_translation_domain']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php new file mode 100644 index 0000000..7940552 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FileTypeTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class FileTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + // https://github.com/symfony/symfony/pull/5028 + public function testSetData() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType')->getForm(); + $data = $this->createUploadedFileMock('abcdef', 'original.jpg', true); + + $form->setData($data); + + $this->assertSame($data, $form->getData()); + } + + public function testSubmit() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType')->getForm(); + $data = $this->createUploadedFileMock('abcdef', 'original.jpg', true); + + $form->submit($data); + + $this->assertSame($data, $form->getData()); + } + + // https://github.com/symfony/symfony/issues/6134 + public function testSubmitEmpty() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType')->getForm(); + + $form->submit(null); + + $this->assertNull($form->getData()); + } + + public function testSubmitMultiple() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FileType', null, array( + 'multiple' => true, + ))->getForm(); + + $data = array( + $this->createUploadedFileMock('abcdef', 'first.jpg', true), + $this->createUploadedFileMock('zyxwvu', 'second.jpg', true), + ); + + $form->submit($data); + $this->assertSame($data, $form->getData()); + + $view = $form->createView(); + $this->assertSame('file[]', $view->vars['full_name']); + $this->assertArrayHasKey('multiple', $view->vars['attr']); + } + + public function testDontPassValueToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FileType'); + $form->submit(array( + 'Symfony\Component\Form\Extension\Core\Type\FileType' => $this->createUploadedFileMock('abcdef', 'original.jpg', true), + )); + $view = $form->createView(); + + $this->assertEquals('', $view->vars['value']); + } + + private function createUploadedFileMock($name, $originalName, $valid) + { + $file = $this + ->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->setConstructorArgs(array(__DIR__.'/../../../Fixtures/foo', 'foo')) + ->getMock() + ; + $file + ->expects($this->any()) + ->method('getBasename') + ->will($this->returnValue($name)) + ; + $file + ->expects($this->any()) + ->method('getClientOriginalName') + ->will($this->returnValue($originalName)) + ; + $file + ->expects($this->any()) + ->method('isValid') + ->will($this->returnValue($valid)) + ; + + return $file; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php new file mode 100644 index 0000000..0cde03e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -0,0 +1,636 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Tests\Fixtures\Author; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; +use Symfony\Component\Form\FormError; + +class FormTest_AuthorWithoutRefSetter +{ + protected $reference; + + protected $referenceCopy; + + public function __construct($reference) + { + $this->reference = $reference; + $this->referenceCopy = $reference; + } + + // The returned object should be modified by reference without having + // to provide a setReference() method + public function getReference() + { + return $this->reference; + } + + // The returned object is a copy, so setReferenceCopy() must be used + // to update it + public function getReferenceCopy() + { + return is_object($this->referenceCopy) ? clone $this->referenceCopy : $this->referenceCopy; + } + + public function setReferenceCopy($reference) + { + $this->referenceCopy = $reference; + } +} + +class FormTypeTest extends BaseTypeTest +{ + public function testCreateFormInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\Form', $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType')); + } + + public function testPassRequiredAsOption() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array('required' => false)); + + $this->assertFalse($form->isRequired()); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array('required' => true)); + + $this->assertTrue($form->isRequired()); + } + + public function testSubmittedDataIsTrimmedBeforeTransforming() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->addViewTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[a]' => 'a', + ))) + ->setCompound(false) + ->getForm(); + + $form->submit(' a '); + + $this->assertEquals('a', $form->getViewData()); + $this->assertEquals('reverse[a]', $form->getData()); + } + + public function testSubmittedDataIsNotTrimmedBeforeTransformingIfNoTrimming() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array('trim' => false)) + ->addViewTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[ a ]' => ' a ', + ))) + ->setCompound(false) + ->getForm(); + + $form->submit(' a '); + + $this->assertEquals(' a ', $form->getViewData()); + $this->assertEquals('reverse[ a ]', $form->getData()); + } + + public function testNonReadOnlyFormWithReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array('attr' => array('readonly' => true))) + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['attr']['readonly']); + } + + public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\FormType', array('attr' => array('readonly' => true))) + ->getForm() + ->createView(); + + $this->assertTrue($view['child']->vars['attr']['readonly']); + } + + public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() + { + $view = $this->factory->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->getForm() + ->createView(); + + $this->assertArrayNotHasKey('readonly', $view['child']->vars['attr']); + } + + public function testPassMaxLengthToView() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array('attr' => array('maxlength' => 10))); + $view = $form->createView(); + + $this->assertSame(10, $view->vars['attr']['maxlength']); + } + + public function testDataClassMayBeNull() + { + $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => null, + )); + } + + public function testDataClassMayBeAbstractClass() + { + $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\AbstractAuthor', + )); + } + + public function testDataClassMayBeInterface() + { + $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\AuthorInterface', + )); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testDataClassMustBeValidClassOrInterface() + { + $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'foobar', + )); + } + + public function testSubmitWithEmptyDataCreatesObjectIfClassAvailable() + { + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $builder->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->setData(null); + // partially empty, still an object is created + $form->submit(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testSubmitWithEmptyDataCreatesObjectIfInitiallySubmittedWithObject() + { + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + // data class is inferred from the passed object + 'data' => new Author(), + 'required' => false, + )); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $builder->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->setData(null); + // partially empty, still an object is created + $form->submit(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testSubmitWithEmptyDataCreatesArrayIfDataClassIsNull() + { + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => null, + 'required' => false, + )); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testSubmitEmptyWithEmptyDataCreatesNoObjectIfNotRequired() + { + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $builder->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => '', 'lastName' => '')); + + $this->assertNull($form->getData()); + } + + public function testSubmitEmptyWithEmptyDataCreatesObjectIfRequired() + { + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => true, + )); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $builder->add('lastName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => '', 'lastName' => '')); + + $this->assertEquals(new Author(), $form->getData()); + } + + /* + * We need something to write the field values into + */ + public function testSubmitWithEmptyDataStoresArrayIfNoClassAvailable() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $form->setData(null); + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testSubmitWithEmptyDataPassesEmptyStringToTransformerIfNotCompound() + { + $form = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->addViewTransformer(new FixedDataTransformer(array( + // required for the initial, internal setData(null) + null => 'null', + // required to test that submit(null) is converted to '' + 'empty' => '', + ))) + ->setCompound(false) + ->getForm(); + + $form->submit(null); + + $this->assertSame('empty', $form->getData()); + } + + public function testSubmitWithEmptyDataUsesEmptyDataOption() + { + $author = new Author(); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'empty_data' => $author, + )); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->submit(array('firstName' => 'Bernhard')); + + $this->assertSame($author, $form->getData()); + $this->assertEquals('Bernhard', $author->firstName); + } + + public function provideZeros() + { + return array( + array(0, '0'), + array('0', '0'), + array('00000', '00000'), + ); + } + + /** + * @dataProvider provideZeros + * + * @see https://github.com/symfony/symfony/issues/1986 + */ + public function testSetDataThroughParamsWithZero($data, $dataAsString) + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data' => $data, + 'compound' => false, + )); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame($dataAsString, $view->vars['value']); + $this->assertSame($dataAsString, $form->getData()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testAttributesException() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array('attr' => '')); + } + + public function testNameCanBeEmptyString() + { + $form = $this->factory->createNamed('', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + + $this->assertEquals('', $form->getName()); + } + + public function testSubformDoesntCallSetters() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', $author); + $builder->add('reference', 'Symfony\Component\Form\Extension\Core\Type\FormType', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + $builder->get('reference')->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->submit(array( + // reference has a getter, but not setter + 'reference' => array( + 'firstName' => 'Foo', + ), + )); + + $this->assertEquals('Foo', $author->getReference()->firstName); + } + + public function testSubformCallsSettersIfTheObjectChanged() + { + // no reference + $author = new FormTest_AuthorWithoutRefSetter(null); + $newReference = new Author(); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', $author); + $builder->add('referenceCopy', 'Symfony\Component\Form\Extension\Core\Type\FormType', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + $builder->get('referenceCopy')->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form['referenceCopy']->setData($newReference); // new author object + + $form->submit(array( + // referenceCopy has a getter that returns a copy + 'referenceCopy' => array( + 'firstName' => 'Foo', + ), + )); + + $this->assertEquals('Foo', $author->getReferenceCopy()->firstName); + } + + public function testSubformCallsSettersIfByReferenceIsFalse() + { + $author = new FormTest_AuthorWithoutRefSetter(new Author()); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', $author); + $builder->add('referenceCopy', 'Symfony\Component\Form\Extension\Core\Type\FormType', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'by_reference' => false, + )); + $builder->get('referenceCopy')->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $form = $builder->getForm(); + + $form->submit(array( + // referenceCopy has a getter that returns a copy + 'referenceCopy' => array( + 'firstName' => 'Foo', + ), + )); + + // firstName can only be updated if setReferenceCopy() was called + $this->assertEquals('Foo', $author->getReferenceCopy()->firstName); + } + + public function testSubformCallsSettersIfReferenceIsScalar() + { + $author = new FormTest_AuthorWithoutRefSetter('scalar'); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', $author); + $builder->add('referenceCopy', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + $builder->get('referenceCopy')->addViewTransformer(new CallbackTransformer( + function () {}, + function ($value) { // reverseTransform + + return 'foobar'; + } + )); + $form = $builder->getForm(); + + $form->submit(array( + 'referenceCopy' => array(), // doesn't matter actually + )); + + // firstName can only be updated if setReferenceCopy() was called + $this->assertEquals('foobar', $author->getReferenceCopy()); + } + + public function testSubformAlwaysInsertsIntoArrays() + { + $ref1 = new Author(); + $ref2 = new Author(); + $author = array('referenceCopy' => $ref1); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType'); + $builder->setData($author); + $builder->add('referenceCopy', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + $builder->get('referenceCopy')->addViewTransformer(new CallbackTransformer( + function () {}, + function ($value) use ($ref2) { // reverseTransform + + return $ref2; + } + )); + $form = $builder->getForm(); + + $form->submit(array( + 'referenceCopy' => array('a' => 'b'), // doesn't matter actually + )); + + // the new reference was inserted into the array + $author = $form->getData(); + $this->assertSame($ref2, $author['referenceCopy']); + } + + public function testPassMultipartTrueIfAnyChildIsMultipartToView() + { + $view = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->add('bar', 'Symfony\Component\Form\Extension\Core\Type\FileType') + ->getForm() + ->createView(); + + $this->assertTrue($view->vars['multipart']); + } + + public function testViewIsNotRenderedByDefault() + { + $view = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->add('foo', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->getForm() + ->createView(); + + $this->assertFalse($view->isRendered()); + } + + public function testErrorBubblingIfCompound() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'compound' => true, + )); + + $this->assertTrue($form->getConfig()->getErrorBubbling()); + } + + public function testNoErrorBubblingIfNotCompound() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'compound' => false, + )); + + $this->assertFalse($form->getConfig()->getErrorBubbling()); + } + + public function testOverrideErrorBubbling() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'compound' => false, + 'error_bubbling' => true, + )); + + $this->assertTrue($form->getConfig()->getErrorBubbling()); + } + + public function testPropertyPath() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'property_path' => 'foo', + )); + + $this->assertEquals(new PropertyPath('foo'), $form->getPropertyPath()); + $this->assertTrue($form->getConfig()->getMapped()); + } + + public function testPropertyPathNullImpliesDefault() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'property_path' => null, + )); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + $this->assertTrue($form->getConfig()->getMapped()); + } + + public function testNotMapped() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'property_path' => 'foo', + 'mapped' => false, + )); + + $this->assertEquals(new PropertyPath('foo'), $form->getPropertyPath()); + $this->assertFalse($form->getConfig()->getMapped()); + } + + public function testViewValidNotSubmitted() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType'); + $view = $form->createView(); + $this->assertTrue($view->vars['valid']); + } + + public function testViewNotValidSubmitted() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType'); + $form->submit(array()); + $form->addError(new FormError('An error')); + $view = $form->createView(); + $this->assertFalse($view->vars['valid']); + } + + public function testViewSubmittedNotSubmitted() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType'); + $view = $form->createView(); + $this->assertFalse($view->vars['submitted']); + } + + public function testViewSubmittedSubmitted() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType'); + $form->submit(array()); + $view = $form->createView(); + $this->assertTrue($view->vars['submitted']); + } + + public function testDataOptionSupersedesSetDataCalls() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data' => 'default', + 'compound' => false, + )); + + $form->setData('foobar'); + + $this->assertSame('default', $form->getData()); + } + + public function testDataOptionSupersedesSetDataCallsIfNull() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'data' => null, + 'compound' => false, + )); + + $form->setData('foobar'); + + $this->assertNull($form->getData()); + } + + public function testNormDataIsPassedToView() + { + $view = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->addViewTransformer(new FixedDataTransformer(array( + 'foo' => 'bar', + ))) + ->setData('foo') + ->getForm() + ->createView(); + + $this->assertSame('foo', $view->vars['data']); + $this->assertSame('bar', $view->vars['value']); + } + + // https://github.com/symfony/symfony/issues/6862 + public function testPassZeroLabelToView() + { + $view = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'label' => '0', + )) + ->createView(); + + $this->assertSame('0', $view->vars['label']); + } + + protected function getTestedType() + { + return 'Symfony\Component\Form\Extension\Core\Type\FormType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php new file mode 100644 index 0000000..dd1a7c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class IntegerTypeTest extends TestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testSubmitCastsToInteger() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\IntegerType'); + + $form->submit('1.678'); + + $this->assertSame(1, $form->getData()); + $this->assertSame('1', $form->getViewData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php new file mode 100644 index 0000000..cb59fa2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LanguageTypeTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class LanguageTypeTest extends TestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testCountriesAreSelectable() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\LanguageType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertContains(new ChoiceView('en', 'en', 'English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('en_GB', 'en_GB', 'British English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('en_US', 'en_US', 'American English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('fr', 'fr', 'French'), $choices, '', false, false); + $this->assertContains(new ChoiceView('my', 'my', 'Burmese'), $choices, '', false, false); + } + + public function testMultipleLanguagesIsNotIncluded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\LanguageType', 'Symfony\Component\Form\Extension\Core\Type\LanguageType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertNotContains(new ChoiceView('mul', 'mul', 'Mehrsprachig'), $choices, '', false, false); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php new file mode 100644 index 0000000..8c56bcc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/LocaleTypeTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class LocaleTypeTest extends TestCase +{ + protected function setUp() + { + IntlTestHelper::requireIntl($this); + + parent::setUp(); + } + + public function testLocalesAreSelectable() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\LocaleType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertContains(new ChoiceView('en', 'en', 'English'), $choices, '', false, false); + $this->assertContains(new ChoiceView('en_GB', 'en_GB', 'English (United Kingdom)'), $choices, '', false, false); + $this->assertContains(new ChoiceView('zh_Hant_MO', 'zh_Hant_MO', 'Chinese (Traditional, Macau SAR China)'), $choices, '', false, false); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php new file mode 100644 index 0000000..bd01f8e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class MoneyTypeTest extends TestCase +{ + protected function setUp() + { + // we test against different locales, so we need the full + // implementation + IntlTestHelper::requireFullIntl($this); + + parent::setUp(); + } + + public function testPassMoneyPatternToView() + { + \Locale::setDefault('de_DE'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\MoneyType'); + $view = $form->createView(); + + $this->assertSame('{{ widget }} €', $view->vars['money_pattern']); + } + + public function testMoneyPatternWorksForYen() + { + \Locale::setDefault('en_US'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\MoneyType', null, array('currency' => 'JPY')); + $view = $form->createView(); + $this->assertTrue((bool) strstr($view->vars['money_pattern'], '¥')); + } + + // https://github.com/symfony/symfony/issues/5458 + public function testPassDifferentPatternsForDifferentCurrencies() + { + \Locale::setDefault('de_DE'); + + $form1 = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\MoneyType', null, array('currency' => 'GBP')); + $form2 = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\MoneyType', null, array('currency' => 'EUR')); + $view1 = $form1->createView(); + $view2 = $form2->createView(); + + $this->assertSame('{{ widget }} £', $view1->vars['money_pattern']); + $this->assertSame('{{ widget }} €', $view2->vars['money_pattern']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php new file mode 100644 index 0000000..1020260 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/NumberTypeTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; +use Symfony\Component\Intl\Util\IntlTestHelper; + +class NumberTypeTest extends TestCase +{ + protected function setUp() + { + parent::setUp(); + + // we test against "de_DE", so we need the full implementation + IntlTestHelper::requireFullIntl($this); + + \Locale::setDefault('de_DE'); + } + + public function testDefaultFormatting() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\NumberType'); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12345,679', $view->vars['value']); + } + + public function testDefaultFormattingWithGrouping() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\NumberType', null, array('grouping' => true)); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12.345,679', $view->vars['value']); + } + + public function testDefaultFormattingWithScale() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\NumberType', null, array('scale' => 2)); + $form->setData('12345.67890'); + $view = $form->createView(); + + $this->assertSame('12345,68', $view->vars['value']); + } + + public function testDefaultFormattingWithRounding() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\NumberType', null, array('scale' => 0, 'rounding_mode' => \NumberFormatter::ROUND_UP)); + $form->setData('12345.54321'); + $view = $form->createView(); + + $this->assertSame('12346', $view->vars['value']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php new file mode 100644 index 0000000..380d572 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/PasswordTypeTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class PasswordTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testEmptyIfNotSubmitted() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\PasswordType'); + $form->setData('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('', $view->vars['value']); + } + + public function testEmptyIfSubmitted() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\PasswordType'); + $form->submit('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('', $view->vars['value']); + } + + public function testNotEmptyIfSubmittedAndNotAlwaysEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\PasswordType', null, array('always_empty' => false)); + $form->submit('pAs5w0rd'); + $view = $form->createView(); + + $this->assertSame('pAs5w0rd', $view->vars['value']); + } + + public function testNotTrimmed() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\PasswordType', null); + $form->submit(' pAs5w0rd '); + $data = $form->getData(); + + $this->assertSame(' pAs5w0rd ', $data); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php new file mode 100644 index 0000000..1384971 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +class RepeatedTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + protected $form; + + protected function setUp() + { + parent::setUp(); + + $this->form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + $this->form->setData(null); + } + + public function testSetData() + { + $this->form->setData('foobar'); + + $this->assertEquals('foobar', $this->form['first']->getData()); + $this->assertEquals('foobar', $this->form['second']->getData()); + } + + public function testSetOptions() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'options' => array('label' => 'Global'), + )); + + $this->assertEquals('Global', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Global', $form['second']->getConfig()->getOption('label')); + $this->assertTrue($form['first']->isRequired()); + $this->assertTrue($form['second']->isRequired()); + } + + public function testSetOptionsPerChild() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + // the global required value cannot be overridden + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'first_options' => array('label' => 'Test', 'required' => false), + 'second_options' => array('label' => 'Test2'), + )); + + $this->assertEquals('Test', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Test2', $form['second']->getConfig()->getOption('label')); + $this->assertTrue($form['first']->isRequired()); + $this->assertTrue($form['second']->isRequired()); + } + + public function testSetRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'required' => false, + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + )); + + $this->assertFalse($form['first']->isRequired()); + $this->assertFalse($form['second']->isRequired()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testSetInvalidOptions() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'options' => 'bad value', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testSetInvalidFirstOptions() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'first_options' => 'bad value', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testSetInvalidSecondOptions() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'second_options' => 'bad value', + )); + } + + public function testSetErrorBubblingToTrue() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'error_bubbling' => true, + )); + + $this->assertTrue($form->getConfig()->getOption('error_bubbling')); + $this->assertTrue($form['first']->getConfig()->getOption('error_bubbling')); + $this->assertTrue($form['second']->getConfig()->getOption('error_bubbling')); + } + + public function testSetErrorBubblingToFalse() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'error_bubbling' => false, + )); + + $this->assertFalse($form->getConfig()->getOption('error_bubbling')); + $this->assertFalse($form['first']->getConfig()->getOption('error_bubbling')); + $this->assertFalse($form['second']->getConfig()->getOption('error_bubbling')); + } + + public function testSetErrorBubblingIndividually() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'error_bubbling' => true, + 'options' => array('error_bubbling' => false), + 'second_options' => array('error_bubbling' => true), + )); + + $this->assertTrue($form->getConfig()->getOption('error_bubbling')); + $this->assertFalse($form['first']->getConfig()->getOption('error_bubbling')); + $this->assertTrue($form['second']->getConfig()->getOption('error_bubbling')); + } + + public function testSetOptionsPerChildAndOverwrite() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\RepeatedType', null, array( + 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextType', + 'options' => array('label' => 'Label'), + 'second_options' => array('label' => 'Second label'), + )); + + $this->assertEquals('Label', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Second label', $form['second']->getConfig()->getOption('label')); + $this->assertTrue($form['first']->isRequired()); + $this->assertTrue($form['second']->isRequired()); + } + + public function testSubmitUnequal() + { + $input = array('first' => 'foo', 'second' => 'bar'); + + $this->form->submit($input); + + $this->assertEquals('foo', $this->form['first']->getViewData()); + $this->assertEquals('bar', $this->form['second']->getViewData()); + $this->assertFalse($this->form->isSynchronized()); + $this->assertEquals($input, $this->form->getViewData()); + $this->assertNull($this->form->getData()); + } + + public function testSubmitEqual() + { + $input = array('first' => 'foo', 'second' => 'foo'); + + $this->form->submit($input); + + $this->assertEquals('foo', $this->form['first']->getViewData()); + $this->assertEquals('foo', $this->form['second']->getViewData()); + $this->assertTrue($this->form->isSynchronized()); + $this->assertEquals($input, $this->form->getViewData()); + $this->assertEquals('foo', $this->form->getData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php new file mode 100644 index 0000000..8652d1b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/SubmitTypeTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; + +/** + * @author Bernhard Schussek + */ +class SubmitTypeTest extends TestCase +{ + public function testCreateSubmitButtonInstances() + { + $this->assertInstanceOf('Symfony\Component\Form\SubmitButton', $this->factory->create('Symfony\Component\Form\Extension\Core\Type\SubmitType')); + } + + public function testNotClickedByDefault() + { + $button = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\SubmitType'); + + $this->assertFalse($button->isClicked()); + } + + public function testNotClickedIfSubmittedWithNull() + { + $button = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\SubmitType'); + $button->submit(null); + + $this->assertFalse($button->isClicked()); + } + + public function testClickedIfSubmittedWithEmptyString() + { + $button = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\SubmitType'); + $button->submit(''); + + $this->assertTrue($button->isClicked()); + } + + public function testClickedIfSubmittedWithUnemptyString() + { + $button = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\SubmitType'); + $button->submit('foo'); + + $this->assertTrue($button->isClicked()); + } + + public function testSubmitCanBeAddedToForm() + { + $form = $this->factory + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType') + ->getForm(); + + $this->assertSame($form, $form->add('send', 'Symfony\Component\Form\Extension\Core\Type\SubmitType')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php new file mode 100644 index 0000000..7e10d32 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -0,0 +1,747 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\ChoiceList\View\ChoiceView; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Test\TypeTestCase as TestCase; + +class TimeTypeTest extends TestCase +{ + public function testSubmitDateTime() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $dateTime = new \DateTime('1970-01-01 03:04:00 UTC'); + + $this->assertEquals($dateTime, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitTimestamp() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'timestamp', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $dateTime = new \DateTime('1970-01-01 03:04:00 UTC'); + + $this->assertEquals($dateTime->format('U'), $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $this->assertEquals($input, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + + public function testSubmitDatetimeSingleText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'widget' => 'single_text', + )); + + $form->submit('03:04'); + + $this->assertEquals(new \DateTime('1970-01-01 03:04:00 UTC'), $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitDatetimeSingleTextWithoutMinutes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'widget' => 'single_text', + 'with_minutes' => false, + )); + + $form->submit('03'); + + $this->assertEquals(new \DateTime('1970-01-01 03:00:00 UTC'), $form->getData()); + $this->assertEquals('03', $form->getViewData()); + } + + public function testSubmitArraySingleText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + 'widget' => 'single_text', + )); + + $data = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit('03:04'); + + $this->assertEquals($data, $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitArraySingleTextWithoutMinutes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + 'widget' => 'single_text', + 'with_minutes' => false, + )); + + $data = array( + 'hour' => '3', + ); + + $form->submit('03'); + + $this->assertEquals($data, $form->getData()); + $this->assertEquals('03', $form->getViewData()); + } + + public function testSubmitArraySingleTextWithSeconds() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'array', + 'widget' => 'single_text', + 'with_seconds' => true, + )); + + $data = array( + 'hour' => '3', + 'minute' => '4', + 'second' => '5', + ); + + $form->submit('03:04:05'); + + $this->assertEquals($data, $form->getData()); + $this->assertEquals('03:04:05', $form->getViewData()); + } + + public function testSubmitStringSingleText() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + )); + + $form->submit('03:04'); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitStringSingleTextWithoutMinutes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_minutes' => false, + )); + + $form->submit('03'); + + $this->assertEquals('03:00:00', $form->getData()); + $this->assertEquals('03', $form->getViewData()); + } + + public function testSetDataWithoutMinutes() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'with_minutes' => false, + )); + + $form->setData(new \DateTime('03:04:05 UTC')); + + $this->assertEquals(array('hour' => 3), $form->getViewData()); + } + + public function testSetDataWithSeconds() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime', + 'with_seconds' => true, + )); + + $form->setData(new \DateTime('03:04:05 UTC')); + + $this->assertEquals(array('hour' => 3, 'minute' => 4, 'second' => 5), $form->getViewData()); + } + + public function testSetDataDifferentTimezones() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Asia/Hong_Kong', + 'input' => 'string', + 'with_seconds' => true, + )); + + $dateTime = new \DateTime('2013-01-01 12:04:05'); + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $form->setData($dateTime->format('H:i:s')); + + $outputTime = clone $dateTime; + $outputTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $displayedData = array( + 'hour' => (int) $outputTime->format('H'), + 'minute' => (int) $outputTime->format('i'), + 'second' => (int) $outputTime->format('s'), + ); + + $this->assertEquals($displayedData, $form->getViewData()); + } + + public function testSetDataDifferentTimezonesDateTime() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Asia/Hong_Kong', + 'input' => 'datetime', + 'with_seconds' => true, + )); + + $dateTime = new \DateTime('12:04:05'); + $dateTime->setTimezone(new \DateTimeZone('America/New_York')); + + $form->setData($dateTime); + + $outputTime = clone $dateTime; + $outputTime->setTimezone(new \DateTimeZone('Asia/Hong_Kong')); + + $displayedData = array( + 'hour' => (int) $outputTime->format('H'), + 'minute' => (int) $outputTime->format('i'), + 'second' => (int) $outputTime->format('s'), + ); + + $this->assertDateTimeEquals($dateTime, $form->getData()); + $this->assertEquals($displayedData, $form->getViewData()); + } + + public function testHoursOption() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'hours' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['hour']->vars['choices']); + } + + public function testIsMinuteWithinRangeReturnsTrueIfWithin() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'minutes' => array(6, 7), + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['minute']->vars['choices']); + } + + public function testIsSecondWithinRangeReturnsTrueIfWithin() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'seconds' => array(6, 7), + 'with_seconds' => true, + )); + + $view = $form->createView(); + + $this->assertEquals(array( + new ChoiceView('6', '6', '06'), + new ChoiceView('7', '7', '07'), + ), $view['second']->vars['choices']); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + )); + + $form->submit(array( + 'hour' => '', + 'minute' => '', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyEmptyWithSeconds() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '', + 'minute' => '', + 'second' => '', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyFilled() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsFalseIfCompletelyFilledWithSeconds() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + 'second' => '0', + )); + + $this->assertFalse($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndHourEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '', + 'minute' => '0', + 'second' => '0', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndMinuteEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '', + 'second' => '0', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testIsPartiallyFilledReturnsTrueIfChoiceAndSecondsEmpty() + { + $this->markTestIncomplete('Needs to be reimplemented using validators'); + + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'choice', + 'with_seconds' => true, + )); + + $form->submit(array( + 'hour' => '0', + 'minute' => '0', + 'second' => '', + )); + + $this->assertTrue($form->isPartiallyFilled()); + } + + public function testInitializeWithDateTime() + { + // Throws an exception if "data_class" option is not explicitly set + // to null in the type + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', new \DateTime()); + } + + public function testSingleTextWidgetShouldUseTheRightInputType() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'single_text', + )); + + $view = $form->createView(); + $this->assertEquals('time', $view->vars['type']); + } + + public function testSingleTextWidgetWithSecondsShouldHaveRightStepAttribute() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'single_text', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertArrayHasKey('step', $view->vars['attr']); + $this->assertEquals(1, $view->vars['attr']['step']); + } + + public function testSingleTextWidgetWithSecondsShouldNotOverrideStepAttribute() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'single_text', + 'with_seconds' => true, + 'attr' => array( + 'step' => 30, + ), + )); + + $view = $form->createView(); + $this->assertArrayHasKey('step', $view->vars['attr']); + $this->assertEquals(30, $view->vars['attr']['step']); + } + + public function testDontPassHtml5TypeIfHtml5NotAllowed() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => 'single_text', + 'html5' => false, + )); + + $view = $form->createView(); + $this->assertFalse(isset($view->vars['type'])); + } + + public function testPassDefaultPlaceholderToViewIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'required' => false, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('', $view['hour']->vars['placeholder']); + $this->assertSame('', $view['minute']->vars['placeholder']); + $this->assertSame('', $view['second']->vars['placeholder']); + } + + public function testPassNoPlaceholderToViewIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'required' => true, + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertNull($view['hour']->vars['placeholder']); + $this->assertNull($view['minute']->vars['placeholder']); + $this->assertNull($view['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'placeholder' => 'Empty', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty', $view['hour']->vars['placeholder']); + $this->assertSame('Empty', $view['minute']->vars['placeholder']); + $this->assertSame('Empty', $view['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'placeholder' => array( + 'hour' => 'Empty hour', + 'minute' => 'Empty minute', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['placeholder']); + $this->assertSame('Empty minute', $view['minute']->vars['placeholder']); + $this->assertSame('Empty second', $view['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsPartialArrayAddEmptyIfNotRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'required' => false, + 'placeholder' => array( + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['placeholder']); + $this->assertSame('', $view['minute']->vars['placeholder']); + $this->assertSame('Empty second', $view['second']->vars['placeholder']); + } + + public function testPassPlaceholderAsPartialArrayAddNullIfRequired() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'required' => true, + 'placeholder' => array( + 'hour' => 'Empty hour', + 'second' => 'Empty second', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('Empty hour', $view['hour']->vars['placeholder']); + $this->assertNull($view['minute']->vars['placeholder']); + $this->assertSame('Empty second', $view['second']->vars['placeholder']); + } + + public function provideCompoundWidgets() + { + return array( + array('text'), + array('choice'), + ); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testHourErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => $widget, + )); + $form['hour']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['hour']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testMinuteErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => $widget, + )); + $form['minute']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['minute']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + /** + * @dataProvider provideCompoundWidgets + */ + public function testSecondErrorsBubbleUp($widget) + { + $error = new FormError('Invalid!'); + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'widget' => $widget, + 'with_seconds' => true, + )); + $form['second']->addError($error); + + $this->assertSame(array(), iterator_to_array($form['second']->getErrors())); + $this->assertSame(array($error), iterator_to_array($form->getErrors())); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidConfigurationException + */ + public function testInitializeWithSecondsAndWithoutMinutes() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'with_minutes' => false, + 'with_seconds' => true, + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfHoursIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'hours' => 'bad value', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfMinutesIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'minutes' => 'bad value', + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfSecondsIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'seconds' => 'bad value', + )); + } + + public function testPassDefaultChoiceTranslationDomain() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType'); + + $view = $form->createView(); + $this->assertFalse($view['hour']->vars['choice_translation_domain']); + $this->assertFalse($view['minute']->vars['choice_translation_domain']); + } + + public function testPassChoiceTranslationDomainAsString() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'choice_translation_domain' => 'messages', + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('messages', $view['hour']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['minute']->vars['choice_translation_domain']); + $this->assertSame('messages', $view['second']->vars['choice_translation_domain']); + } + + public function testPassChoiceTranslationDomainAsArray() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimeType', null, array( + 'choice_translation_domain' => array( + 'hour' => 'foo', + 'second' => 'test', + ), + 'with_seconds' => true, + )); + + $view = $form->createView(); + $this->assertSame('foo', $view['hour']->vars['choice_translation_domain']); + $this->assertFalse($view['minute']->vars['choice_translation_domain']); + $this->assertSame('test', $view['second']->vars['choice_translation_domain']); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php new file mode 100644 index 0000000..5327379 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\ChoiceList\View\ChoiceView; + +class TimezoneTypeTest extends \Symfony\Component\Form\Test\TypeTestCase +{ + public function testTimezonesAreSelectable() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\TimezoneType'); + $view = $form->createView(); + $choices = $view->vars['choices']; + + $this->assertArrayHasKey('Africa', $choices); + $this->assertContains(new ChoiceView('Africa/Kinshasa', 'Africa/Kinshasa', 'Kinshasa'), $choices['Africa'], '', false, false); + + $this->assertArrayHasKey('America', $choices); + $this->assertContains(new ChoiceView('America/New_York', 'America/New_York', 'New York'), $choices['America'], '', false, false); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php new file mode 100644 index 0000000..641f9a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Test\TypeTestCase as TestCase; + +class UrlTypeTest extends TestCase +{ + public function testSubmitAddsDefaultProtocolIfNoneIsIncluded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\UrlType', 'name'); + + $form->submit('www.domain.com'); + + $this->assertSame('http://www.domain.com', $form->getData()); + $this->assertSame('http://www.domain.com', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfAlreadyIncluded() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\UrlType', null, array( + 'default_protocol' => 'http', + )); + + $form->submit('ftp://www.domain.com'); + + $this->assertSame('ftp://www.domain.com', $form->getData()); + $this->assertSame('ftp://www.domain.com', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\UrlType', null, array( + 'default_protocol' => 'http', + )); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfNull() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\UrlType', null, array( + 'default_protocol' => 'http', + )); + + $form->submit(null); + + $this->assertNull($form->getData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitAddsNoDefaultProtocolIfSetToNull() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\UrlType', null, array( + 'default_protocol' => null, + )); + + $form->submit('www.domain.com'); + + $this->assertSame('www.domain.com', $form->getData()); + $this->assertSame('www.domain.com', $form->getViewData()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testThrowExceptionIfDefaultProtocolIsInvalid() + { + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\UrlType', null, array( + 'default_protocol' => array(), + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php new file mode 100644 index 0000000..7206cee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; + +class CsrfValidationListenerTest extends \PHPUnit_Framework_TestCase +{ + protected $dispatcher; + protected $factory; + protected $tokenManager; + protected $form; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->tokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'); + $this->form = $this->getBuilder('post') + ->setDataMapper($this->getDataMapper()) + ->getForm(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->tokenManager = null; + $this->form = null; + } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory, array('compound' => true)); + } + + protected function getForm($name = 'name') + { + return $this->getBuilder($name)->getForm(); + } + + protected function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + protected function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + // https://github.com/symfony/symfony/pull/5838 + public function testStringFormData() + { + $data = 'XP4HUzmHPi'; + $event = new FormEvent($this->form, $data); + + $validation = new CsrfValidationListener('csrf', $this->tokenManager, 'unknown', 'Invalid.'); + $validation->preSubmit($event); + + // Validate accordingly + $this->assertSame($data, $event->getData()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php new file mode 100644 index 0000000..3a34394 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -0,0 +1,397 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Component\Form\Extension\Csrf\CsrfExtension; +use Symfony\Component\Security\Csrf\CsrfToken; + +class FormTypeCsrfExtensionTest_ChildType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + // The form needs a child in order to trigger CSRF protection by + // default + $builder->add('name', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + } +} + +class FormTypeCsrfExtensionTest extends TypeTestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $tokenManager; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $translator; + + protected function setUp() + { + $this->tokenManager = $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface'); + $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); + + parent::setUp(); + } + + protected function tearDown() + { + $this->tokenManager = null; + $this->translator = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new CsrfExtension($this->tokenManager, $this->translator), + )); + } + + public function testCsrfProtectionByDefaultIfRootAndCompound() + { + $view = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => true, + )) + ->createView(); + + $this->assertTrue(isset($view['csrf'])); + } + + public function testNoCsrfProtectionByDefaultIfCompoundButNotRoot() + { + $view = $this->factory + ->createNamedBuilder('root', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory + ->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => true, + )) + ) + ->getForm() + ->get('form') + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testNoCsrfProtectionByDefaultIfRootButNotCompound() + { + $view = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'compound' => false, + )) + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testCsrfProtectionCanBeDisabled() + { + $view = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_protection' => false, + 'compound' => true, + )) + ->createView(); + + $this->assertFalse(isset($view['csrf'])); + } + + public function testGenerateCsrfToken() + { + $this->tokenManager->expects($this->once()) + ->method('getToken') + ->with('TOKEN_ID') + ->will($this->returnValue(new CsrfToken('TOKEN_ID', 'token'))); + + $view = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'csrf_token_id' => 'TOKEN_ID', + 'compound' => true, + )) + ->createView(); + + $this->assertEquals('token', $view['csrf']->vars['value']); + } + + public function testGenerateCsrfTokenUsesFormNameAsIntentionByDefault() + { + $this->tokenManager->expects($this->once()) + ->method('getToken') + ->with('FORM_NAME') + ->will($this->returnValue('token')); + + $view = $this->factory + ->createNamed('FORM_NAME', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'compound' => true, + )) + ->createView(); + + $this->assertEquals('token', $view['csrf']->vars['value']); + } + + public function testGenerateCsrfTokenUsesTypeClassAsIntentionIfEmptyFormName() + { + $this->tokenManager->expects($this->once()) + ->method('getToken') + ->with('Symfony\Component\Form\Extension\Core\Type\FormType') + ->will($this->returnValue('token')); + + $view = $this->factory + ->createNamed('', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'compound' => true, + )) + ->createView(); + + $this->assertEquals('token', $view['csrf']->vars['value']); + } + + public function provideBoolean() + { + return array( + array(true), + array(false), + ); + } + + /** + * @dataProvider provideBoolean + */ + public function testValidateTokenOnSubmitIfRootAndCompound($valid) + { + $this->tokenManager->expects($this->once()) + ->method('isTokenValid') + ->with(new CsrfToken('TOKEN_ID', 'token')) + ->will($this->returnValue($valid)); + + $form = $this->factory + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'csrf_token_id' => 'TOKEN_ID', + 'compound' => true, + )) + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertSame($valid, $form->isValid()); + } + + /** + * @dataProvider provideBoolean + */ + public function testValidateTokenOnSubmitIfRootAndCompoundUsesFormNameAsIntentionByDefault($valid) + { + $this->tokenManager->expects($this->once()) + ->method('isTokenValid') + ->with(new CsrfToken('FORM_NAME', 'token')) + ->will($this->returnValue($valid)); + + $form = $this->factory + ->createNamedBuilder('FORM_NAME', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'compound' => true, + )) + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertSame($valid, $form->isValid()); + } + + /** + * @dataProvider provideBoolean + */ + public function testValidateTokenOnSubmitIfRootAndCompoundUsesTypeClassAsIntentionIfEmptyFormName($valid) + { + $this->tokenManager->expects($this->once()) + ->method('isTokenValid') + ->with(new CsrfToken('Symfony\Component\Form\Extension\Core\Type\FormType', 'token')) + ->will($this->returnValue($valid)); + + $form = $this->factory + ->createNamedBuilder('', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'compound' => true, + )) + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertSame($valid, $form->isValid()); + } + + public function testFailIfRootAndCompoundAndTokenMissing() + { + $this->tokenManager->expects($this->never()) + ->method('isTokenValid'); + + $form = $this->factory + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'csrf_token_id' => 'TOKEN_ID', + 'compound' => true, + )) + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->getForm(); + + $form->submit(array( + 'child' => 'foobar', + // token is missing + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertFalse($form->isValid()); + } + + public function testDontValidateTokenIfCompoundButNoRoot() + { + $this->tokenManager->expects($this->never()) + ->method('isTokenValid'); + + $form = $this->factory + ->createNamedBuilder('root', 'Symfony\Component\Form\Extension\Core\Type\FormType') + ->add($this->factory + ->createNamedBuilder('form', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'csrf_token_id' => 'TOKEN_ID', + 'compound' => true, + )) + ) + ->getForm() + ->get('form'); + + $form->submit(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + } + + public function testDontValidateTokenIfRootButNotCompound() + { + $this->tokenManager->expects($this->never()) + ->method('isTokenValid'); + + $form = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'csrf_token_id' => 'TOKEN_ID', + 'compound' => false, + )); + + $form->submit(array( + 'csrf' => 'token', + )); + } + + public function testNoCsrfProtectionOnPrototype() + { + $prototypeView = $this->factory + ->create('Symfony\Component\Form\Extension\Core\Type\CollectionType', null, array( + 'entry_type' => __CLASS__.'_ChildType', + 'entry_options' => array( + 'csrf_field_name' => 'csrf', + ), + 'prototype' => true, + 'allow_add' => true, + )) + ->createView() + ->vars['prototype']; + + $this->assertFalse(isset($prototypeView['csrf'])); + $this->assertCount(1, $prototypeView); + } + + public function testsTranslateCustomErrorMessage() + { + $this->tokenManager->expects($this->once()) + ->method('isTokenValid') + ->with(new CsrfToken('TOKEN_ID', 'token')) + ->will($this->returnValue(false)); + + $this->translator->expects($this->once()) + ->method('trans') + ->with('Foobar') + ->will($this->returnValue('[trans]Foobar[/trans]')); + + $form = $this->factory + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_token_manager' => $this->tokenManager, + 'csrf_message' => 'Foobar', + 'csrf_token_id' => 'TOKEN_ID', + 'compound' => true, + )) + ->getForm(); + + $form->submit(array( + 'csrf' => 'token', + )); + + $errors = $form->getErrors(); + $expected = new FormError('[trans]Foobar[/trans]'); + $expected->setOrigin($form); + + $this->assertGreaterThan(0, count($errors)); + $this->assertEquals($expected, $errors[0]); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/DataCollectorExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/DataCollectorExtensionTest.php new file mode 100644 index 0000000..da34d4a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/DataCollectorExtensionTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\DataCollector; + +use Symfony\Component\Form\Extension\DataCollector\DataCollectorExtension; + +class DataCollectorExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var DataCollectorExtension + */ + private $extension; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dataCollector; + + protected function setUp() + { + $this->dataCollector = $this->getMock('Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface'); + $this->extension = new DataCollectorExtension($this->dataCollector); + } + + public function testLoadTypeExtensions() + { + $typeExtensions = $this->extension->getTypeExtensions('Symfony\Component\Form\Extension\Core\Type\FormType'); + + $this->assertInternalType('array', $typeExtensions); + $this->assertCount(1, $typeExtensions); + $this->assertInstanceOf('Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension', array_shift($typeExtensions)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php new file mode 100644 index 0000000..f6fa249 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -0,0 +1,539 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\DataCollector; + +use Symfony\Component\Form\Extension\DataCollector\FormDataCollector; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormView; + +class FormDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dataExtractor; + + /** + * @var FormDataCollector + */ + private $dataCollector; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dataMapper; + + /** + * @var Form + */ + private $form; + + /** + * @var Form + */ + private $childForm; + + /** + * @var FormView + */ + private $view; + + /** + * @var FormView + */ + private $childView; + + protected function setUp() + { + $this->dataExtractor = $this->getMock('Symfony\Component\Form\Extension\DataCollector\FormDataExtractorInterface'); + $this->dataCollector = new FormDataCollector($this->dataExtractor); + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->dataMapper = $this->getMock('Symfony\Component\Form\DataMapperInterface'); + $this->form = $this->createForm('name'); + $this->childForm = $this->createForm('child'); + $this->view = new FormView(); + $this->childView = new FormView(); + } + + public function testBuildPreliminaryFormTree() + { + $this->form->add($this->childForm); + + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($this->form) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(1)) + ->method('extractConfiguration') + ->with($this->childForm) + ->will($this->returnValue(array('config' => 'bar'))); + + $this->dataExtractor->expects($this->at(2)) + ->method('extractDefaultData') + ->with($this->form) + ->will($this->returnValue(array('default_data' => 'foo'))); + $this->dataExtractor->expects($this->at(3)) + ->method('extractDefaultData') + ->with($this->childForm) + ->will($this->returnValue(array('default_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(4)) + ->method('extractSubmittedData') + ->with($this->form) + ->will($this->returnValue(array('submitted_data' => 'foo'))); + $this->dataExtractor->expects($this->at(5)) + ->method('extractSubmittedData') + ->with($this->childForm) + ->will($this->returnValue(array('submitted_data' => 'bar'))); + + $this->dataCollector->collectConfiguration($this->form); + $this->dataCollector->collectDefaultData($this->form); + $this->dataCollector->collectSubmittedData($this->form); + $this->dataCollector->buildPreliminaryFormTree($this->form); + + $childFormData = array( + 'config' => 'bar', + 'default_data' => 'bar', + 'submitted_data' => 'bar', + 'children' => array(), + ); + + $formData = array( + 'config' => 'foo', + 'default_data' => 'foo', + 'submitted_data' => 'foo', + 'children' => array( + 'child' => $childFormData, + ), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($this->childForm) => $childFormData, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testBuildMultiplePreliminaryFormTrees() + { + $form1 = $this->createForm('form1'); + $form2 = $this->createForm('form2'); + + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($form1) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(1)) + ->method('extractConfiguration') + ->with($form2) + ->will($this->returnValue(array('config' => 'bar'))); + + $this->dataCollector->collectConfiguration($form1); + $this->dataCollector->collectConfiguration($form2); + $this->dataCollector->buildPreliminaryFormTree($form1); + + $form1Data = array( + 'config' => 'foo', + 'children' => array(), + ); + + $this->assertSame(array( + 'forms' => array( + 'form1' => $form1Data, + ), + 'forms_by_hash' => array( + spl_object_hash($form1) => $form1Data, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + + $this->dataCollector->buildPreliminaryFormTree($form2); + + $form2Data = array( + 'config' => 'bar', + 'children' => array(), + ); + + $this->assertSame(array( + 'forms' => array( + 'form1' => $form1Data, + 'form2' => $form2Data, + ), + 'forms_by_hash' => array( + spl_object_hash($form1) => $form1Data, + spl_object_hash($form2) => $form2Data, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testBuildSamePreliminaryFormTreeMultipleTimes() + { + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($this->form) + ->will($this->returnValue(array('config' => 'foo'))); + + $this->dataExtractor->expects($this->at(1)) + ->method('extractDefaultData') + ->with($this->form) + ->will($this->returnValue(array('default_data' => 'foo'))); + + $this->dataCollector->collectConfiguration($this->form); + $this->dataCollector->buildPreliminaryFormTree($this->form); + + $formData = array( + 'config' => 'foo', + 'children' => array(), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + + $this->dataCollector->collectDefaultData($this->form); + $this->dataCollector->buildPreliminaryFormTree($this->form); + + $formData = array( + 'config' => 'foo', + 'default_data' => 'foo', + 'children' => array(), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testBuildPreliminaryFormTreeWithoutCollectingAnyData() + { + $this->dataCollector->buildPreliminaryFormTree($this->form); + + $formData = array( + 'children' => array(), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testBuildFinalFormTree() + { + $this->form->add($this->childForm); + $this->view->children['child'] = $this->childView; + + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($this->form) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(1)) + ->method('extractConfiguration') + ->with($this->childForm) + ->will($this->returnValue(array('config' => 'bar'))); + + $this->dataExtractor->expects($this->at(2)) + ->method('extractDefaultData') + ->with($this->form) + ->will($this->returnValue(array('default_data' => 'foo'))); + $this->dataExtractor->expects($this->at(3)) + ->method('extractDefaultData') + ->with($this->childForm) + ->will($this->returnValue(array('default_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(4)) + ->method('extractSubmittedData') + ->with($this->form) + ->will($this->returnValue(array('submitted_data' => 'foo'))); + $this->dataExtractor->expects($this->at(5)) + ->method('extractSubmittedData') + ->with($this->childForm) + ->will($this->returnValue(array('submitted_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(6)) + ->method('extractViewVariables') + ->with($this->view) + ->will($this->returnValue(array('view_vars' => 'foo'))); + + $this->dataExtractor->expects($this->at(7)) + ->method('extractViewVariables') + ->with($this->childView) + ->will($this->returnValue(array('view_vars' => 'bar'))); + + $this->dataCollector->collectConfiguration($this->form); + $this->dataCollector->collectDefaultData($this->form); + $this->dataCollector->collectSubmittedData($this->form); + $this->dataCollector->collectViewVariables($this->view); + $this->dataCollector->buildFinalFormTree($this->form, $this->view); + + $childFormData = array( + 'view_vars' => 'bar', + 'config' => 'bar', + 'default_data' => 'bar', + 'submitted_data' => 'bar', + 'children' => array(), + ); + + $formData = array( + 'view_vars' => 'foo', + 'config' => 'foo', + 'default_data' => 'foo', + 'submitted_data' => 'foo', + 'children' => array( + 'child' => $childFormData, + ), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($this->childForm) => $childFormData, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testFinalFormReliesOnFormViewStructure() + { + $this->form->add($child1 = $this->createForm('first')); + $this->form->add($child2 = $this->createForm('second')); + + $this->view->children['second'] = $this->childView; + + $this->dataCollector->buildPreliminaryFormTree($this->form); + + $child1Data = array( + 'children' => array(), + ); + + $child2Data = array( + 'children' => array(), + ); + + $formData = array( + 'children' => array( + 'first' => $child1Data, + 'second' => $child2Data, + ), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($child1) => $child1Data, + spl_object_hash($child2) => $child2Data, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + + $this->dataCollector->buildFinalFormTree($this->form, $this->view); + + $formData = array( + 'children' => array( + // "first" not present in FormView + 'second' => $child2Data, + ), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($child1) => $child1Data, + spl_object_hash($child2) => $child2Data, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testChildViewsCanBeWithoutCorrespondingChildForms() + { + // don't add $this->childForm to $this->form! + + $this->view->children['child'] = $this->childView; + + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($this->form) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(1)) + ->method('extractConfiguration') + ->with($this->childForm) + ->will($this->returnValue(array('config' => 'bar'))); + + // explicitly call collectConfiguration(), since $this->childForm is not + // contained in the form tree + $this->dataCollector->collectConfiguration($this->form); + $this->dataCollector->collectConfiguration($this->childForm); + $this->dataCollector->buildFinalFormTree($this->form, $this->view); + + $childFormData = array( + // no "config" key + 'children' => array(), + ); + + $formData = array( + 'config' => 'foo', + 'children' => array( + 'child' => $childFormData, + ), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + // no child entry + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testChildViewsWithoutCorrespondingChildFormsMayBeExplicitlyAssociated() + { + // don't add $this->childForm to $this->form! + + $this->view->children['child'] = $this->childView; + + // but associate the two + $this->dataCollector->associateFormWithView($this->childForm, $this->childView); + + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($this->form) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(1)) + ->method('extractConfiguration') + ->with($this->childForm) + ->will($this->returnValue(array('config' => 'bar'))); + + // explicitly call collectConfiguration(), since $this->childForm is not + // contained in the form tree + $this->dataCollector->collectConfiguration($this->form); + $this->dataCollector->collectConfiguration($this->childForm); + $this->dataCollector->buildFinalFormTree($this->form, $this->view); + + $childFormData = array( + 'config' => 'bar', + 'children' => array(), + ); + + $formData = array( + 'config' => 'foo', + 'children' => array( + 'child' => $childFormData, + ), + ); + + $this->assertSame(array( + 'forms' => array( + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($this->childForm) => $childFormData, + ), + 'nb_errors' => 0, + ), $this->dataCollector->getData()); + } + + public function testCollectSubmittedDataCountsErrors() + { + $form1 = $this->createForm('form1'); + $childForm1 = $this->createForm('child1'); + $form2 = $this->createForm('form2'); + + $form1->add($childForm1); + $this->dataExtractor + ->method('extractConfiguration') + ->will($this->returnValue(array())); + $this->dataExtractor + ->method('extractDefaultData') + ->will($this->returnValue(array())); + $this->dataExtractor->expects($this->at(4)) + ->method('extractSubmittedData') + ->with($form1) + ->will($this->returnValue(array('errors' => array('foo')))); + $this->dataExtractor->expects($this->at(5)) + ->method('extractSubmittedData') + ->with($childForm1) + ->will($this->returnValue(array('errors' => array('bar', 'bam')))); + $this->dataExtractor->expects($this->at(8)) + ->method('extractSubmittedData') + ->with($form2) + ->will($this->returnValue(array('errors' => array('baz')))); + + $this->dataCollector->collectSubmittedData($form1); + + $data = $this->dataCollector->getData(); + $this->assertSame(3, $data['nb_errors']); + + $this->dataCollector->collectSubmittedData($form2); + + $data = $this->dataCollector->getData(); + $this->assertSame(4, $data['nb_errors']); + } + + private function createForm($name) + { + $builder = new FormBuilder($name, null, $this->dispatcher, $this->factory); + $builder->setCompound(true); + $builder->setDataMapper($this->dataMapper); + + return $builder->getForm(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php new file mode 100644 index 0000000..690e4bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -0,0 +1,416 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\DataCollector; + +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\DataCollector\FormDataExtractor; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; + +class FormDataExtractorTest_SimpleValueExporter extends ValueExporter +{ + /** + * {@inheritdoc} + */ + public function exportValue($value, $depth = 1, $deep = false) + { + return is_object($value) ? sprintf('object(%s)', get_class($value)) : var_export($value, true); + } +} + +/** + * @author Bernhard Schussek + */ +class FormDataExtractorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormDataExtractorTest_SimpleValueExporter + */ + private $valueExporter; + + /** + * @var FormDataExtractor + */ + private $dataExtractor; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + protected function setUp() + { + $this->valueExporter = new FormDataExtractorTest_SimpleValueExporter(); + $this->dataExtractor = new FormDataExtractor($this->valueExporter); + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + } + + public function testExtractConfiguration() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $type->expects($this->any()) + ->method('getInnerType') + ->will($this->returnValue(new \stdClass())); + + $form = $this->createBuilder('name') + ->setType($type) + ->getForm(); + + $this->assertSame(array( + 'id' => 'name', + 'name' => 'name', + 'type_class' => 'stdClass', + 'synchronized' => 'true', + 'passed_options' => array(), + 'resolved_options' => array(), + ), $this->dataExtractor->extractConfiguration($form)); + } + + public function testExtractConfigurationSortsPassedOptions() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $type->expects($this->any()) + ->method('getInnerType') + ->will($this->returnValue(new \stdClass())); + + $options = array( + 'b' => 'foo', + 'a' => 'bar', + 'c' => 'baz', + ); + + $form = $this->createBuilder('name') + ->setType($type) + // passed options are stored in an attribute by + // ResolvedTypeDataCollectorProxy + ->setAttribute('data_collector/passed_options', $options) + ->getForm(); + + $this->assertSame(array( + 'id' => 'name', + 'name' => 'name', + 'type_class' => 'stdClass', + 'synchronized' => 'true', + 'passed_options' => array( + 'a' => "'bar'", + 'b' => "'foo'", + 'c' => "'baz'", + ), + 'resolved_options' => array(), + ), $this->dataExtractor->extractConfiguration($form)); + } + + public function testExtractConfigurationSortsResolvedOptions() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $type->expects($this->any()) + ->method('getInnerType') + ->will($this->returnValue(new \stdClass())); + + $options = array( + 'b' => 'foo', + 'a' => 'bar', + 'c' => 'baz', + ); + + $form = $this->createBuilder('name', $options) + ->setType($type) + ->getForm(); + + $this->assertSame(array( + 'id' => 'name', + 'name' => 'name', + 'type_class' => 'stdClass', + 'synchronized' => 'true', + 'passed_options' => array(), + 'resolved_options' => array( + 'a' => "'bar'", + 'b' => "'foo'", + 'c' => "'baz'", + ), + ), $this->dataExtractor->extractConfiguration($form)); + } + + public function testExtractConfigurationBuildsIdRecursively() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $type->expects($this->any()) + ->method('getInnerType') + ->will($this->returnValue(new \stdClass())); + + $grandParent = $this->createBuilder('grandParent') + ->setCompound(true) + ->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface')) + ->getForm(); + $parent = $this->createBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getMock('Symfony\Component\Form\DataMapperInterface')) + ->getForm(); + $form = $this->createBuilder('name') + ->setType($type) + ->getForm(); + + $grandParent->add($parent); + $parent->add($form); + + $this->assertSame(array( + 'id' => 'grandParent_parent_name', + 'name' => 'name', + 'type_class' => 'stdClass', + 'synchronized' => 'true', + 'passed_options' => array(), + 'resolved_options' => array(), + ), $this->dataExtractor->extractConfiguration($form)); + } + + public function testExtractDefaultData() + { + $form = $this->createBuilder('name')->getForm(); + + $form->setData('Foobar'); + + $this->assertSame(array( + 'default_data' => array( + 'norm' => "'Foobar'", + ), + 'submitted_data' => array(), + ), $this->dataExtractor->extractDefaultData($form)); + } + + public function testExtractDefaultDataStoresModelDataIfDifferent() + { + $form = $this->createBuilder('name') + ->addModelTransformer(new FixedDataTransformer(array( + 'Foo' => 'Bar', + ))) + ->getForm(); + + $form->setData('Foo'); + + $this->assertSame(array( + 'default_data' => array( + 'norm' => "'Bar'", + 'model' => "'Foo'", + ), + 'submitted_data' => array(), + ), $this->dataExtractor->extractDefaultData($form)); + } + + public function testExtractDefaultDataStoresViewDataIfDifferent() + { + $form = $this->createBuilder('name') + ->addViewTransformer(new FixedDataTransformer(array( + 'Foo' => 'Bar', + ))) + ->getForm(); + + $form->setData('Foo'); + + $this->assertSame(array( + 'default_data' => array( + 'norm' => "'Foo'", + 'view' => "'Bar'", + ), + 'submitted_data' => array(), + ), $this->dataExtractor->extractDefaultData($form)); + } + + public function testExtractSubmittedData() + { + $form = $this->createBuilder('name')->getForm(); + + $form->submit('Foobar'); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Foobar'", + ), + 'errors' => array(), + 'synchronized' => 'true', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractSubmittedDataStoresModelDataIfDifferent() + { + $form = $this->createBuilder('name') + ->addModelTransformer(new FixedDataTransformer(array( + 'Foo' => 'Bar', + '' => '', + ))) + ->getForm(); + + $form->submit('Bar'); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Bar'", + 'model' => "'Foo'", + ), + 'errors' => array(), + 'synchronized' => 'true', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractSubmittedDataStoresViewDataIfDifferent() + { + $form = $this->createBuilder('name') + ->addViewTransformer(new FixedDataTransformer(array( + 'Foo' => 'Bar', + '' => '', + ))) + ->getForm(); + + $form->submit('Bar'); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Foo'", + 'view' => "'Bar'", + ), + 'errors' => array(), + 'synchronized' => 'true', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractSubmittedDataStoresErrors() + { + $form = $this->createBuilder('name')->getForm(); + + $form->submit('Foobar'); + $form->addError(new FormError('Invalid!')); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Foobar'", + ), + 'errors' => array( + array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()), + ), + 'synchronized' => 'true', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractSubmittedDataStoresErrorOrigin() + { + $form = $this->createBuilder('name')->getForm(); + + $error = new FormError('Invalid!'); + $error->setOrigin($form); + + $form->submit('Foobar'); + $form->addError($error); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Foobar'", + ), + 'errors' => array( + array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array()), + ), + 'synchronized' => 'true', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractSubmittedDataStoresErrorCause() + { + $form = $this->createBuilder('name')->getForm(); + + $exception = new \Exception(); + + $form->submit('Foobar'); + $form->addError(new FormError('Invalid!', null, array(), null, $exception)); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Foobar'", + ), + 'errors' => array( + array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'trace' => array( + array( + 'class' => "'Exception'", + 'message' => "''", + ), + )), + ), + 'synchronized' => 'true', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractSubmittedDataRemembersIfNonSynchronized() + { + $form = $this->createBuilder('name') + ->addModelTransformer(new CallbackTransformer( + function () {}, + function () { + throw new TransformationFailedException('Fail!'); + } + )) + ->getForm(); + + $form->submit('Foobar'); + + $this->assertSame(array( + 'submitted_data' => array( + 'norm' => "'Foobar'", + 'model' => 'NULL', + ), + 'errors' => array(), + 'synchronized' => 'false', + ), $this->dataExtractor->extractSubmittedData($form)); + } + + public function testExtractViewVariables() + { + $view = new FormView(); + + $view->vars = array( + 'b' => 'foo', + 'a' => 'bar', + 'c' => 'baz', + 'id' => 'foo_bar', + 'name' => 'bar', + ); + + $this->assertSame(array( + 'id' => 'foo_bar', + 'name' => 'bar', + 'view_vars' => array( + 'a' => "'bar'", + 'b' => "'foo'", + 'c' => "'baz'", + 'id' => "'foo_bar'", + 'name' => "'bar'", + ), + ), $this->dataExtractor->extractViewVariables($view)); + } + + /** + * @param string $name + * @param array $options + * + * @return FormBuilder + */ + private function createBuilder($name, array $options = array()) + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory, $options); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php new file mode 100644 index 0000000..9c53387 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DataCollector/Type/DataCollectorTypeExtensionTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\DataCollector\Type; + +use Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension; + +class DataCollectorTypeExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var DataCollectorTypeExtension + */ + private $extension; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dataCollector; + + protected function setUp() + { + $this->dataCollector = $this->getMock('Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface'); + $this->extension = new DataCollectorTypeExtension($this->dataCollector); + } + + public function testGetExtendedType() + { + $this->assertEquals('Symfony\Component\Form\Extension\Core\Type\FormType', $this->extension->getExtendedType()); + } + + public function testBuildForm() + { + $builder = $this->getMock('Symfony\Component\Form\Test\FormBuilderInterface'); + $builder->expects($this->atLeastOnce()) + ->method('addEventSubscriber') + ->with($this->isInstanceOf('Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener')); + + $this->extension->buildForm($builder, array()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php new file mode 100644 index 0000000..77fb370 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/DependencyInjection/DependencyInjectionExtensionTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension; + +class DependencyInjectionExtensionTest extends \PHPUnit_Framework_TestCase +{ + public function testGetTypeExtensions() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $typeExtension1 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface'); + $typeExtension1->expects($this->any()) + ->method('getExtendedType') + ->willReturn('test'); + $typeExtension2 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface'); + $typeExtension2->expects($this->any()) + ->method('getExtendedType') + ->willReturn('test'); + $typeExtension3 = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface'); + $typeExtension3->expects($this->any()) + ->method('getExtendedType') + ->willReturn('other'); + + $services = array( + 'extension1' => $typeExtension1, + 'extension2' => $typeExtension2, + 'extension3' => $typeExtension3, + ); + + $container->expects($this->any()) + ->method('get') + ->willReturnCallback(function ($id) use ($services) { + if (isset($services[$id])) { + return $services[$id]; + } + + throw new ServiceNotFoundException($id); + }); + + $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension1', 'extension2'), 'other' => array('extension3')), array()); + + $this->assertTrue($extension->hasTypeExtensions('test')); + $this->assertFalse($extension->hasTypeExtensions('unknown')); + $this->assertSame(array($typeExtension1, $typeExtension2), $extension->getTypeExtensions('test')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testThrowExceptionForInvalidExtendedType() + { + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $typeExtension = $this->getMock('Symfony\Component\Form\FormTypeExtensionInterface'); + $typeExtension->expects($this->any()) + ->method('getExtendedType') + ->willReturn('unmatched'); + + $container->expects($this->any()) + ->method('get') + ->with('extension') + ->willReturn($typeExtension); + + $extension = new DependencyInjectionExtension($container, array(), array('test' => array('extension')), array()); + + $extension->getTypeExtensions('test'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php new file mode 100644 index 0000000..4d4ac86 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/HttpFoundation/HttpFoundationRequestHandlerTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\HttpFoundation; + +use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; +use Symfony\Component\Form\Tests\AbstractRequestHandlerTest; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * @author Bernhard Schussek + */ +class HttpFoundationRequestHandlerTest extends AbstractRequestHandlerTest +{ + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequestShouldNotBeNull() + { + $this->requestHandler->handleRequest($this->getMockForm('name', 'GET')); + } + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequestShouldBeInstanceOfRequest() + { + $this->requestHandler->handleRequest($this->getMockForm('name', 'GET'), new \stdClass()); + } + + protected function setRequestData($method, $data, $files = array()) + { + $this->request = Request::create('http://localhost', $method, $data, array(), $files); + } + + protected function getRequestHandler() + { + return new HttpFoundationRequestHandler($this->serverParams); + } + + protected function getMockFile($suffix = '') + { + return new UploadedFile(__DIR__.'/../../Fixtures/foo'.$suffix, 'foo'.$suffix); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php new file mode 100644 index 0000000..9e957d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorPerformanceTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints; + +use Symfony\Component\Form\Extension\Validator\ValidatorExtension; +use Symfony\Component\Form\Test\FormPerformanceTestCase; +use Symfony\Component\Validator\Validation; + +/** + * @author Bernhard Schussek + */ +class FormValidatorPerformanceTest extends FormPerformanceTestCase +{ + protected function getExtensions() + { + return array( + new ValidatorExtension(Validation::createValidator()), + ); + } + + /** + * findClickedButton() used to have an exponential number of calls. + * + * @group benchmark + */ + public function testValidationPerformance() + { + $this->setMaxRunningTime(1); + + $builder = $this->factory->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType'); + + for ($i = 0; $i < 40; ++$i) { + $builder->add($i, 'Symfony\Component\Form\Extension\Core\Type\FormType'); + + $builder->get($i) + ->add('a') + ->add('b') + ->add('c'); + } + + $form = $builder->getForm(); + + $form->submit(null); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php new file mode 100644 index 0000000..cf9252f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -0,0 +1,684 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Constraints; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; +use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator; +use Symfony\Component\Form\SubmitButtonBuilder; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Valid; +use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; + +/** + * @author Bernhard Schussek + */ +class FormValidatorTest extends AbstractConstraintValidatorTest +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $serverParams; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->serverParams = $this->getMock( + 'Symfony\Component\Form\Extension\Validator\Util\ServerParams', + array('getNormalizedIniPostMaxSize', 'getContentLength') + ); + + parent::setUp(); + } + + protected function createValidator() + { + return new FormValidator($this->serverParams); + } + + public function testValidate() + { + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testValidateConstraints() + { + $object = $this->getMock('\stdClass'); + $constraint1 = new NotNull(array('groups' => array('group1', 'group2'))); + $constraint2 = new NotBlank(array('groups' => 'group2')); + + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + // First default constraints + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + // Then custom constraints + $this->expectValidateValueAt(2, 'data', $object, $constraint1, 'group1'); + $this->expectValidateValueAt(3, 'data', $object, $constraint2, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testValidateIfParentWithCascadeValidation() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testValidateIfChildWithValidConstraint() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array(new Valid()), + ); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontValidateIfParentWithoutCascadeValidation() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => false)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testValidateConstraintsEvenIfNoCascadeValidation() + { + $object = $this->getMock('\stdClass'); + $constraint1 = new NotNull(array('groups' => array('group1', 'group2'))); + $constraint2 = new NotBlank(array('groups' => 'group2')); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => false)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + $parent->add($form); + + $this->expectValidateValueAt(0, 'data', $object, $constraint1, 'group1'); + $this->expectValidateValueAt(1, 'data', $object, $constraint2, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontValidateIfNoValidationGroups() + { + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'validation_groups' => array(), + )) + ->setData($object) + ->getForm(); + + $form->setData($object); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontValidateConstraintsIfNoValidationGroups() + { + $object = $this->getMock('\stdClass'); + $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); + $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); + + $options = array( + 'validation_groups' => array(), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + // Launch transformer + $form->submit(array()); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontValidateIfNotSynchronized() + { + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'invalid_message' => 'invalid_message_key', + // Invalid message parameters must be supported, because the + // invalid message can be a translation key + // see https://github.com/symfony/symfony/issues/5144 + 'invalid_message_parameters' => array('{{ foo }}' => 'bar'), + )) + ->setData($object) + ->addViewTransformer(new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + )) + ->getForm(); + + // Launch transformer + $form->submit('foo'); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->buildViolation('invalid_message_key') + ->setParameter('{{ value }}', 'foo') + ->setParameter('{{ foo }}', 'bar') + ->setInvalidValue('foo') + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) + ->setCause($form->getTransformationFailure()) + ->assertRaised(); + } + + public function testAddInvalidErrorEvenIfNoValidationGroups() + { + $object = $this->getMock('\stdClass'); + + $form = $this->getBuilder('name', '\stdClass', array( + 'invalid_message' => 'invalid_message_key', + // Invalid message parameters must be supported, because the + // invalid message can be a translation key + // see https://github.com/symfony/symfony/issues/5144 + 'invalid_message_parameters' => array('{{ foo }}' => 'bar'), + 'validation_groups' => array(), + )) + ->setData($object) + ->addViewTransformer(new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + )) + ->getForm(); + + // Launch transformer + $form->submit('foo'); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->buildViolation('invalid_message_key') + ->setParameter('{{ value }}', 'foo') + ->setParameter('{{ foo }}', 'bar') + ->setInvalidValue('foo') + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) + ->setCause($form->getTransformationFailure()) + ->assertRaised(); + } + + public function testDontValidateConstraintsIfNotSynchronized() + { + $object = $this->getMock('\stdClass'); + $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); + $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); + + $options = array( + 'invalid_message' => 'invalid_message_key', + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->addViewTransformer(new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + )) + ->getForm(); + + // Launch transformer + $form->submit('foo'); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->buildViolation('invalid_message_key') + ->setParameter('{{ value }}', 'foo') + ->setInvalidValue('foo') + ->setCode(Form::NOT_SYNCHRONIZED_ERROR) + ->setCause($form->getTransformationFailure()) + ->assertRaised(); + } + + // https://github.com/symfony/symfony/issues/4359 + public function testDontMarkInvalidIfAnyChildIsNotSynchronized() + { + $object = $this->getMock('\stdClass'); + + $failingTransformer = new CallbackTransformer( + function ($data) { return $data; }, + function () { throw new TransformationFailedException(); } + ); + + $form = $this->getBuilder('name', '\stdClass') + ->setData($object) + ->addViewTransformer($failingTransformer) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add( + $this->getBuilder('child') + ->addViewTransformer($failingTransformer) + ) + ->getForm(); + + // Launch transformer + $form->submit(array('child' => 'foo')); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testHandleCallbackValidationGroups() + { + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => array($this, 'getValidationGroups')); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontExecuteFunctionNames() + { + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => 'header'); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $this->expectValidateAt(0, 'data', $object, 'header'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testHandleClosureValidationGroups() + { + $object = $this->getMock('\stdClass'); + $options = array('validation_groups' => function (FormInterface $form) { + return array('group1', 'group2'); + }); + $form = $this->getBuilder('name', '\stdClass', $options) + ->setData($object) + ->getForm(); + + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testUseValidationGroupOfClickedButton() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm('name', '\stdClass', array( + 'validation_groups' => 'form_group', + 'constraints' => array(new Valid()), + )); + + $parent->add($form); + $parent->add($this->getSubmitButton('submit', array( + 'validation_groups' => 'button_group', + ))); + + $parent->submit(array('name' => $object, 'submit' => '')); + + $this->expectValidateAt(0, 'data', $object, array('button_group')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontUseValidationGroupOfUnclickedButton() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getForm('name', '\stdClass', array( + 'validation_groups' => 'form_group', + 'constraints' => array(new Valid()), + )); + + $parent->add($form); + $parent->add($this->getSubmitButton('submit', array( + 'validation_groups' => 'button_group', + ))); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('form_group')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testUseInheritedValidationGroup() + { + $object = $this->getMock('\stdClass'); + + $parentOptions = array('validation_groups' => 'group'); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('group')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testUseInheritedCallbackValidationGroup() + { + $object = $this->getMock('\stdClass'); + + $parentOptions = array('validation_groups' => array($this, 'getValidationGroups')); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testUseInheritedClosureValidationGroup() + { + $object = $this->getMock('\stdClass'); + + $parentOptions = array( + 'validation_groups' => function (FormInterface $form) { + return array('group1', 'group2'); + }, + ); + $parent = $this->getBuilder('parent', null, $parentOptions) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testAppendPropertyPath() + { + $object = $this->getMock('\stdClass'); + $form = $this->getBuilder('name', '\stdClass') + ->setData($object) + ->getForm(); + + $this->expectValidateAt(0, 'data', $object, 'Default'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testDontWalkScalars() + { + $form = $this->getBuilder() + ->setData('scalar') + ->getForm(); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testViolationIfExtraData() + { + $form = $this->getBuilder('parent', null, array('extra_fields_message' => 'Extra!')) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('child')) + ->getForm(); + + $form->submit(array('foo' => 'bar')); + + $this->expectNoValidate(); + + $this->validator->validate($form, new Form()); + + $this->buildViolation('Extra!') + ->setParameter('{{ extra_fields }}', 'foo') + ->setInvalidValue(array('foo' => 'bar')) + ->setCode(Form::NO_SUCH_FIELD_ERROR) + ->assertRaised(); + } + + public function testNoViolationIfAllowExtraData() + { + $context = $this->getMockExecutionContext(); + + $form = $this + ->getBuilder('parent', null, array('allow_extra_fields' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('child')) + ->getForm(); + + $form->submit(array('foo' => 'bar')); + + $context->expects($this->never()) + ->method('addViolation'); + $context->expects($this->never()) + ->method('addViolationAt'); + + $this->validator->initialize($context); + $this->validator->validate($form, new Form()); + } + + /** + * Access has to be public, as this method is called via callback array + * in {@link testValidateFormDataCanHandleCallbackValidationGroups()} + * and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()}. + */ + public function getValidationGroups(FormInterface $form) + { + return array('group1', 'group2'); + } + + private function getMockExecutionContext() + { + $context = $this->getMock('Symfony\Component\Validator\Context\ExecutionContextInterface'); + $validator = $this->getMock('Symfony\Component\Validator\Validator\ValidatorInterface'); + $contextualValidator = $this->getMock('Symfony\Component\Validator\Validator\ContextualValidatorInterface'); + + $validator->expects($this->any()) + ->method('inContext') + ->with($context) + ->will($this->returnValue($contextualValidator)); + + $context->expects($this->any()) + ->method('getValidator') + ->will($this->returnValue($validator)); + + return $context; + } + + /** + * @param string $name + * @param string $dataClass + * @param array $options + * + * @return FormBuilder + */ + private function getBuilder($name = 'name', $dataClass = null, array $options = array()) + { + $options = array_replace(array( + 'constraints' => array(), + 'invalid_message_parameters' => array(), + ), $options); + + return new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory, $options); + } + + private function getForm($name = 'name', $dataClass = null, array $options = array()) + { + return $this->getBuilder($name, $dataClass, $options)->getForm(); + } + + private function getSubmitButton($name = 'name', array $options = array()) + { + $builder = new SubmitButtonBuilder($name, $options); + + return $builder->getForm(); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php new file mode 100644 index 0000000..6859074 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\EventListener; + +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\Extension\Validator\Constraints\Form; +use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationList; + +class ValidationListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $validator; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $violationMapper; + + /** + * @var ValidationListener + */ + private $listener; + + private $message; + + private $messageTemplate; + + private $params; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->validator = $this->getMock('Symfony\Component\Validator\Validator\ValidatorInterface'); + $this->violationMapper = $this->getMock('Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface'); + $this->listener = new ValidationListener($this->validator, $this->violationMapper); + $this->message = 'Message'; + $this->messageTemplate = 'Message template'; + $this->params = array('foo' => 'bar'); + } + + private function getConstraintViolation($code = null) + { + return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, 'prop.path', null, null, $code, new Form()); + } + + private function getBuilder($name = 'name', $propertyPath = null, $dataClass = null) + { + $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory); + $builder->setPropertyPath(new PropertyPath($propertyPath ?: $name)); + $builder->setAttribute('error_mapping', array()); + $builder->setErrorBubbling(false); + $builder->setMapped(true); + + return $builder; + } + + private function getForm($name = 'name', $propertyPath = null, $dataClass = null) + { + return $this->getBuilder($name, $propertyPath, $dataClass)->getForm(); + } + + private function getMockForm() + { + return $this->getMock('Symfony\Component\Form\Test\FormInterface'); + } + + // More specific mapping tests can be found in ViolationMapperTest + public function testMapViolation() + { + $violation = $this->getConstraintViolation(); + $form = $this->getForm('street'); + + $this->validator->expects($this->once()) + ->method('validate') + ->will($this->returnValue(array($violation))); + + $this->violationMapper->expects($this->once()) + ->method('mapViolation') + ->with($violation, $form, false); + + $this->listener->validateForm(new FormEvent($form, null)); + } + + public function testMapViolationAllowsNonSyncIfInvalid() + { + $violation = $this->getConstraintViolation(Form::NOT_SYNCHRONIZED_ERROR); + $form = $this->getForm('street'); + + $this->validator->expects($this->once()) + ->method('validate') + ->will($this->returnValue(array($violation))); + + $this->violationMapper->expects($this->once()) + ->method('mapViolation') + // pass true now + ->with($violation, $form, true); + + $this->listener->validateForm(new FormEvent($form, null)); + } + + public function testValidateIgnoresNonRoot() + { + $form = $this->getMockForm(); + $form->expects($this->once()) + ->method('isRoot') + ->will($this->returnValue(false)); + + $this->validator->expects($this->never()) + ->method('validate'); + + $this->violationMapper->expects($this->never()) + ->method('mapViolation'); + + $this->listener->validateForm(new FormEvent($form, null)); + } + + public function testValidateWithEmptyViolationList() + { + $form = $this->getMockForm(); + $form->expects($this->once()) + ->method('isRoot') + ->will($this->returnValue(true)); + + $this->validator + ->expects($this->once()) + ->method('validate') + ->will($this->returnValue(new ConstraintViolationList())); + + $this->violationMapper + ->expects($this->never()) + ->method('mapViolation'); + + $this->listener->validateForm(new FormEvent($form, null)); + } + + public function testValidatorInterface() + { + $validator = $this->getMock('Symfony\Component\Validator\Validator\ValidatorInterface'); + + $listener = new ValidationListener($validator, $this->violationMapper); + $this->assertAttributeSame($validator, 'validator', $listener); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php new file mode 100644 index 0000000..e26bf7f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/BaseValidatorExtensionTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Type; + +use Symfony\Component\Form\Test\FormInterface; + +/** + * @author Bernhard Schussek + */ +abstract class BaseValidatorExtensionTest extends TypeTestCase +{ + public function testValidationGroupNullByDefault() + { + $form = $this->createForm(); + + $this->assertNull($form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsTransformedToArray() + { + $form = $this->createForm(array( + 'validation_groups' => 'group', + )); + + $this->assertEquals(array('group'), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToArray() + { + $form = $this->createForm(array( + 'validation_groups' => array('group1', 'group2'), + )); + + $this->assertEquals(array('group1', 'group2'), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToFalse() + { + $form = $this->createForm(array( + 'validation_groups' => false, + )); + + $this->assertEquals(array(), $form->getConfig()->getOption('validation_groups')); + } + + public function testValidationGroupsCanBeSetToCallback() + { + $form = $this->createForm(array( + 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), + )); + + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + } + + public function testValidationGroupsCanBeSetToClosure() + { + $form = $this->createForm(array( + 'validation_groups' => function (FormInterface $form) { return; }, + )); + + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); + } + + abstract protected function createForm(array $options = array()); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php new file mode 100644 index 0000000..9f92000 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Type; + +use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension; +use Symfony\Component\Validator\Constraints\Valid; +use Symfony\Component\Validator\ConstraintViolationList; + +class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest +{ + public function testSubmitValidatesData() + { + $builder = $this->factory->createBuilder( + 'Symfony\Component\Form\Extension\Core\Type\FormType', + null, + array( + 'validation_groups' => 'group', + ) + ); + $builder->add('firstName', 'Symfony\Component\Form\Extension\Core\Type\FormType'); + $form = $builder->getForm(); + + $this->validator->expects($this->once()) + ->method('validate') + ->with($this->equalTo($form)) + ->will($this->returnValue(new ConstraintViolationList())); + + // specific data is irrelevant + $form->submit(array()); + } + + public function testValidConstraint() + { + $form = $this->createForm(array('constraints' => $valid = new Valid())); + + $this->assertSame(array($valid), $form->getConfig()->getOption('constraints')); + } + + public function testValidatorInterface() + { + $validator = $this->getMock('Symfony\Component\Validator\Validator\ValidatorInterface'); + + $formTypeValidatorExtension = new FormTypeValidatorExtension($validator); + $this->assertAttributeSame($validator, 'validator', $formTypeValidatorExtension); + } + + protected function createForm(array $options = array()) + { + return $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, $options); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/SubmitTypeValidatorExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/SubmitTypeValidatorExtensionTest.php new file mode 100644 index 0000000..48fc8de --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/SubmitTypeValidatorExtensionTest.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Type; + +class SubmitTypeValidatorExtensionTest extends BaseValidatorExtensionTest +{ + protected function createForm(array $options = array()) + { + return $this->factory->create('Symfony\Component\Form\Extension\Core\Type\SubmitType', null, $options); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php new file mode 100644 index 0000000..19b6c1c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Type/TypeTestCase.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Type; + +use Symfony\Component\Form\Test\TypeTestCase as BaseTypeTestCase; +use Symfony\Component\Form\Extension\Validator\ValidatorExtension; + +abstract class TypeTestCase extends BaseTypeTestCase +{ + protected $validator; + + protected function setUp() + { + $this->validator = $this->getMock('Symfony\Component\Validator\Validator\ValidatorInterface'); + $metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); + $this->validator->expects($this->once())->method('getMetadataFor')->will($this->returnValue($metadata)); + + parent::setUp(); + } + + protected function tearDown() + { + $this->validator = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new ValidatorExtension($this->validator), + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php new file mode 100644 index 0000000..957b597 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/Util/ServerParamsTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\Util; + +use Symfony\Component\Form\Extension\Validator\Util\ServerParams; +use Symfony\Component\HttpFoundation\Request; + +class ServerParamsTest extends \PHPUnit_Framework_TestCase +{ + public function testGetContentLengthFromSuperglobals() + { + $serverParams = new ServerParams(); + $this->assertNull($serverParams->getContentLength()); + + $_SERVER['CONTENT_LENGTH'] = 1024; + + $this->assertEquals(1024, $serverParams->getContentLength()); + + unset($_SERVER['CONTENT_LENGTH']); + } + + public function testGetContentLengthFromRequest() + { + $request = Request::create('http://foo', 'GET', array(), array(), array(), array('CONTENT_LENGTH' => 1024)); + $requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array('getCurrentRequest')); + $requestStack->expects($this->once())->method('getCurrentRequest')->will($this->returnValue($request)); + $serverParams = new ServerParams($requestStack); + + $this->assertEquals(1024, $serverParams->getContentLength()); + } + + /** @dataProvider getGetPostMaxSizeTestData */ + public function testGetPostMaxSize($size, $bytes) + { + $serverParams = $this->getMock('Symfony\Component\Form\Extension\Validator\Util\ServerParams', array('getNormalizedIniPostMaxSize')); + $serverParams + ->expects($this->any()) + ->method('getNormalizedIniPostMaxSize') + ->will($this->returnValue(strtoupper($size))); + + $this->assertEquals($bytes, $serverParams->getPostMaxSize()); + } + + public function getGetPostMaxSizeTestData() + { + return array( + array('2k', 2048), + array('2 k', 2048), + array('8m', 8 * 1024 * 1024), + array('+2 k', 2048), + array('+2???k', 2048), + array('0x10', 16), + array('0xf', 15), + array('010', 8), + array('+0x10 k', 16 * 1024), + array('1g', 1024 * 1024 * 1024), + array('-1', -1), + array('0', 0), + array('2mk', 2048), // the unit must be the last char, so in this case 'k', not 'm' + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php new file mode 100644 index 0000000..6a1484b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator; + +use Symfony\Component\Form\Extension\Validator\ValidatorExtension; + +class ValidatorExtensionTest extends \PHPUnit_Framework_TestCase +{ + public function test2Dot5ValidationApi() + { + $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\RecursiveValidator') + ->disableOriginalConstructor() + ->getMock(); + $metadata = $this->getMockBuilder('Symfony\Component\Validator\Mapping\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + + $validator->expects($this->once()) + ->method('getMetadataFor') + ->with($this->identicalTo('Symfony\Component\Form\Form')) + ->will($this->returnValue($metadata)); + + // Verify that the constraints are added + $metadata->expects($this->once()) + ->method('addConstraint') + ->with($this->isInstanceOf('Symfony\Component\Form\Extension\Validator\Constraints\Form')); + + $metadata->expects($this->once()) + ->method('addPropertyConstraint') + ->with('children', $this->isInstanceOf('Symfony\Component\Validator\Constraints\Valid')); + + $validator + ->expects($this->never()) + ->method('getMetadataFactory'); + + $extension = new ValidatorExtension($validator); + $guesser = $extension->loadTypeGuesser(); + + $this->assertInstanceOf('Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser', $guesser); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php new file mode 100644 index 0000000..a5e1cb1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorTypeGuesserTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator; + +use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\ValueGuess; +use Symfony\Component\Validator\Constraints\Email; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\Constraints\IsTrue; +use Symfony\Component\Validator\Constraints\Type; +use Symfony\Component\Validator\Mapping\ClassMetadata; + +/** + * @author franek + * @author Bernhard Schussek + */ +class ValidatorTypeGuesserTest extends \PHPUnit_Framework_TestCase +{ + const TEST_CLASS = 'Symfony\Component\Form\Tests\Extension\Validator\ValidatorTypeGuesserTest_TestClass'; + + const TEST_PROPERTY = 'property'; + + /** + * @var ValidatorTypeGuesser + */ + private $guesser; + + /** + * @var ClassMetadata + */ + private $metadata; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $metadataFactory; + + protected function setUp() + { + $this->metadata = new ClassMetadata(self::TEST_CLASS); + $this->metadataFactory = $this->getMock('Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface'); + $this->metadataFactory->expects($this->any()) + ->method('getMetadataFor') + ->with(self::TEST_CLASS) + ->will($this->returnValue($this->metadata)); + $this->guesser = new ValidatorTypeGuesser($this->metadataFactory); + } + + public function guessRequiredProvider() + { + return array( + array(new NotNull(), new ValueGuess(true, Guess::HIGH_CONFIDENCE)), + array(new NotBlank(), new ValueGuess(true, Guess::HIGH_CONFIDENCE)), + array(new IsTrue(), new ValueGuess(true, Guess::HIGH_CONFIDENCE)), + array(new Length(10), new ValueGuess(false, Guess::LOW_CONFIDENCE)), + array(new Range(array('min' => 1, 'max' => 20)), new ValueGuess(false, Guess::LOW_CONFIDENCE)), + ); + } + + /** + * @dataProvider guessRequiredProvider + */ + public function testGuessRequired($constraint, $guess) + { + // add distracting constraint + $this->metadata->addPropertyConstraint(self::TEST_PROPERTY, new Email()); + + // add constraint under test + $this->metadata->addPropertyConstraint(self::TEST_PROPERTY, $constraint); + + $this->assertEquals($guess, $this->guesser->guessRequired(self::TEST_CLASS, self::TEST_PROPERTY)); + } + + public function testGuessRequiredReturnsFalseForUnmappedProperties() + { + $this->assertEquals(new ValueGuess(false, Guess::LOW_CONFIDENCE), $this->guesser->guessRequired(self::TEST_CLASS, self::TEST_PROPERTY)); + } + + public function testGuessMaxLengthForConstraintWithMaxValue() + { + $constraint = new Length(array('max' => '2')); + + $result = $this->guesser->guessMaxLengthForConstraint($constraint); + $this->assertInstanceOf('Symfony\Component\Form\Guess\ValueGuess', $result); + $this->assertEquals(2, $result->getValue()); + $this->assertEquals(Guess::HIGH_CONFIDENCE, $result->getConfidence()); + } + + public function testGuessMaxLengthForConstraintWithMinValue() + { + $constraint = new Length(array('min' => '2')); + + $result = $this->guesser->guessMaxLengthForConstraint($constraint); + $this->assertNull($result); + } + + public function maxLengthTypeProvider() + { + return array( + array('double'), + array('float'), + array('numeric'), + array('real'), + ); + } + + /** + * @dataProvider maxLengthTypeProvider + */ + public function testGuessMaxLengthForConstraintWithType($type) + { + $constraint = new Type($type); + + $result = $this->guesser->guessMaxLengthForConstraint($constraint); + $this->assertInstanceOf('Symfony\Component\Form\Guess\ValueGuess', $result); + $this->assertNull($result->getValue()); + $this->assertEquals(Guess::MEDIUM_CONFIDENCE, $result->getConfidence()); + } +} + +class ValidatorTypeGuesserTest_TestClass +{ + private $property; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php new file mode 100644 index 0000000..0934ef6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -0,0 +1,1542 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\CallbackTransformer; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormError; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationInterface; + +/** + * @author Bernhard Schussek + */ +class ViolationMapperTest extends \PHPUnit_Framework_TestCase +{ + const LEVEL_0 = 0; + + const LEVEL_1 = 1; + + const LEVEL_1B = 2; + + const LEVEL_2 = 3; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var ViolationMapper + */ + private $mapper; + + /** + * @var string + */ + private $message; + + /** + * @var string + */ + private $messageTemplate; + + /** + * @var array + */ + private $params; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->mapper = new ViolationMapper(); + $this->message = 'Message'; + $this->messageTemplate = 'Message template'; + $this->params = array('foo' => 'bar'); + } + + protected function getForm($name = 'name', $propertyPath = null, $dataClass = null, $errorMapping = array(), $inheritData = false, $synchronized = true) + { + $config = new FormConfigBuilder($name, $dataClass, $this->dispatcher, array( + 'error_mapping' => $errorMapping, + )); + $config->setMapped(true); + $config->setInheritData($inheritData); + $config->setPropertyPath($propertyPath); + $config->setCompound(true); + $config->setDataMapper($this->getDataMapper()); + + if (!$synchronized) { + $config->addViewTransformer(new CallbackTransformer( + function ($normData) { return $normData; }, + function () { throw new TransformationFailedException(); } + )); + } + + return new Form($config); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getDataMapper() + { + return $this->getMock('Symfony\Component\Form\DataMapperInterface'); + } + + /** + * @param $propertyPath + * + * @return ConstraintViolation + */ + protected function getConstraintViolation($propertyPath) + { + return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, $propertyPath, null); + } + + /** + * @return FormError + */ + protected function getFormError(ConstraintViolationInterface $violation, FormInterface $form) + { + $error = new FormError($this->message, $this->messageTemplate, $this->params, null, $violation); + $error->setOrigin($form); + + return $error; + } + + public function testMapToFormInheritingParentDataIfDataDoesNotMatch() + { + $violation = $this->getConstraintViolation('children[address].data.foo'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array(), true); + $grandChild = $this->getForm('street'); + + $parent->add($child); + $child->add($grandChild); + + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $child)), iterator_to_array($child->getErrors()), $child->getName().' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function testFollowDotRules() + { + $violation = $this->getConstraintViolation('data.foo'); + $parent = $this->getForm('parent', null, null, array( + 'foo' => 'address', + )); + $child = $this->getForm('address', null, null, array( + '.' => 'street', + )); + $grandChild = $this->getForm('street', null, null, array( + '.' => 'name', + )); + $grandGrandChild = $this->getForm('name'); + + $parent->add($child); + $child->add($grandChild); + $grandChild->add($grandGrandChild); + + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $grandGrandChild)), iterator_to_array($grandGrandChild->getErrors()), $grandGrandChild->getName().' should have an error, but has none'); + } + + public function testAbortMappingIfNotSynchronized() + { + $violation = $this->getConstraintViolation('children[address].data.street'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array(), false, false); + // even though "street" is synchronized, it should not have any errors + // due to its parent not being synchronized + $grandChild = $this->getForm('street', 'street'); + + $parent->add($child); + $child->add($grandChild); + + // invoke the transformer and mark the form unsynchronized + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function testAbortDotRuleMappingIfNotSynchronized() + { + $violation = $this->getConstraintViolation('data.address'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array( + '.' => 'street', + ), false, false); + // even though "street" is synchronized, it should not have any errors + // due to its parent not being synchronized + $grandChild = $this->getForm('street'); + + $parent->add($child); + $child->add($grandChild); + + // invoke the transformer and mark the form unsynchronized + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function testAbortMappingIfNotSubmitted() + { + $violation = $this->getConstraintViolation('children[address].data.street'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address'); + $grandChild = $this->getForm('street', 'street'); + + $parent->add($child); + $child->add($grandChild); + + // Disable automatic submission of missing fields + $parent->submit(array(), false); + $child->submit(array(), false); + + // $grandChild is not submitted + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function testAbortDotRuleMappingIfNotSubmitted() + { + $violation = $this->getConstraintViolation('data.address'); + $parent = $this->getForm('parent'); + $child = $this->getForm('address', 'address', null, array( + '.' => 'street', + )); + $grandChild = $this->getForm('street'); + + $parent->add($child); + $child->add($grandChild); + + // Disable automatic submission of missing fields + $parent->submit(array(), false); + $child->submit(array(), false); + + // $grandChild is not submitted + + $this->mapper->mapViolation($violation, $parent); + + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); + } + + public function provideDefaultTests() + { + // The mapping must be deterministic! If a child has the property path "[street]", + // "data[street]" should be mapped, but "data.street" should not! + return array( + // mapping target, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_0, 'address', 'address', 'street', 'street', ''), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data'), + + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'data[address].street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_2, 'address', 'person.address', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_1, 'address', 'person.address', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_1, 'address', 'person.address', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_2, 'address', 'person.address', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_1, 'address', 'person[address]', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_2, 'address', 'person[address]', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', 'person[address]', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_2, 'address', '[person].address', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_1, 'address', '[person].address', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_1, 'address', '[person].address', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_2, 'address', '[person].address', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_0, 'address', '[person].address', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address]'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address].data'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', 'street', 'data[person].address[street].prop'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'data[person][address].street'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', 'street', 'data[person][address].street.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'data[person][address][street]'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', 'street', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'children[address].data[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person.address[street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address].street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address].street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address][street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data.person[address][street].prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address.street'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address.street.prop'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address[street]'), + array(self::LEVEL_0, 'address', '[person][address]', 'street', '[street]', 'data[person].address[street].prop'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'data[person][address].street'), + array(self::LEVEL_1, 'address', '[person][address]', 'street', '[street]', 'data[person][address].street.prop'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'data[person][address][street]'), + array(self::LEVEL_2, 'address', '[person][address]', 'street', '[street]', 'data[person][address][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.office'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].data.office.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data[office][street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'data.address.office.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office.street', 'data.address.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office.street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data.office'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].data.office.street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office.street', 'data.address[office][street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'data[address].office.street'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office.street', 'data[address].office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address].office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address].office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office.street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data.office'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].data.office[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address.office.street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'data.address.office[street]'), + array(self::LEVEL_2, 'address', 'address', 'street', 'office[street]', 'data.address.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office[street]', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'office[street]', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office[street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', 'office[street]', 'data.address[office][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address].office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address].office.street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'data[address].office[street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'office[street]', 'data[address].office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', 'office[street]', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data[office]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].data[office].street'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'children[address].data[office][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address.office[street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'data.address[office].street'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office].street', 'data.address[office].street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address[office][street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office].street', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office].street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data[office]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].data[office].street'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'children[address].data[office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data[office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office].street', 'data.address[office][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address].office[street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'data[address][office].street'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office].street', 'data[address][office].street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address][office][street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office].street', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].data[office][street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office.street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address.office[street].prop'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address[office].street'), + array(self::LEVEL_1, 'address', 'address', 'street', '[office][street]', 'data.address[office].street.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'data.address[office][street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[office][street]', 'data.address[office][street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office.street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address].office[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office].street'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', '[office][street]', 'data[address][office][street].prop'), + + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data.office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office].street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'children[address].data[office][street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office.street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office.street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office[street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address.office[street].prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office].street'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office].street.prop'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office][street]'), + array(self::LEVEL_0, 'address', '[address]', 'street', '[office][street]', 'data.address[office][street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office.street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office.street.prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office[street]'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address].office[street].prop'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address][office].street'), + array(self::LEVEL_1, 'address', '[address]', 'street', '[office][street]', 'data[address][office].street.prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'data[address][office][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[office][street]', 'data[address][office][street].prop'), + + // Edge cases which must not occur + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address][street]'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address][street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address][street]'), + array(self::LEVEL_2, 'address', 'address', 'street', '[street]', 'children[address][street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', 'street', 'children[address][street].prop'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address][street]'), + array(self::LEVEL_2, 'address', '[address]', 'street', '[street]', 'children[address][street].prop'), + + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].children[address].children[street]'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].children[address].data.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'children[person].data.address.street'), + array(self::LEVEL_0, 'address', 'person.address', 'street', 'street', 'data.address.street'), + + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].children[office].children[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].children[office].data.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'children[address].data.street'), + array(self::LEVEL_1, 'address', 'address', 'street', 'office.street', 'data.address.street'), + ); + } + + /** + * @dataProvider provideDefaultTests + */ + public function testDefaultErrorMapping($target, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent'); + $child = $this->getForm($childName, $childPath); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + + $parent->add($child); + $child->add($grandChild); + + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError($violation, $parent)), iterator_to_array($parent->getErrors()), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $child)), iterator_to_array($child->getErrors()), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChildName.' should have an error, but has none'); + } + } + + public function provideCustomDataErrorTests() + { + return array( + // mapping target, error mapping, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_1, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', 'address', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address].street'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', 'street', 'data[address][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.foo[street].prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo].street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[foo][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data.address[street].prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street'), + array(self::LEVEL_1, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address].street.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street]'), + array(self::LEVEL_2, '[foo]', 'address', 'address', '[address]', 'street', '[street]', 'data[address][street].prop'), + + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_1, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_2, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo.bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_1, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_2, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_0, 'foo[bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_1, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_2, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_0, '[foo].bar', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo].bar[street].prop'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar].street.prop'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street]'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', 'street', 'data[foo][bar][street].prop'), + + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo.bar[street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar].street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data.foo[bar][street].prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar.street.prop'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street]'), + array(self::LEVEL_0, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo].bar[street].prop'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street'), + array(self::LEVEL_1, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar].street.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street]'), + array(self::LEVEL_2, '[foo][bar]', 'address', 'address', 'address', 'street', '[street]', 'data[foo][bar][street].prop'), + + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', 'street', 'data.foo'), + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo]'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo'), + array(self::LEVEL_2, 'foo', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo.prop'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo]'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo].prop'), + + array(self::LEVEL_2, 'foo', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo'), + array(self::LEVEL_2, 'foo', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo.prop'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo]'), + array(self::LEVEL_2, '[foo]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', 'street', 'data[foo][bar].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo.bar'), + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo.bar.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo[bar]'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data.foo[bar].prop'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo].bar'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo].bar.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo][bar]'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', 'address', 'street', '[street]', 'data[foo][bar].prop'), + + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo.bar'), + array(self::LEVEL_2, 'foo.bar', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo.bar.prop'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo[bar]'), + array(self::LEVEL_2, 'foo[bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data.foo[bar].prop'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo].bar'), + array(self::LEVEL_2, '[foo].bar', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo].bar.prop'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo][bar]'), + array(self::LEVEL_2, '[foo][bar]', 'address.street', 'address', '[address]', 'street', 'street', 'data[foo][bar].prop'), + + // Edge cases + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_2, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_1, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_0, 'foo', 'address', 'address', '[address]', 'street', 'street', 'data[foo][street].prop'), + + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo.street.prop'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street]'), + array(self::LEVEL_0, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data.foo[street].prop'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street'), + array(self::LEVEL_2, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo].street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street]'), + array(self::LEVEL_1, '[foo]', 'address', 'address', 'address', 'street', 'street', 'data[foo][street].prop'), + ); + } + + /** + * @dataProvider provideCustomDataErrorTests + */ + public function testCustomDataErrorMapping($target, $mapFrom, $mapTo, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent', null, null, array($mapFrom => $mapTo)); + $child = $this->getForm($childName, $childPath); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + + $parent->add($child); + $child->add($grandChild); + + // Add a field mapped to the first element of $mapFrom + // to try to distract the algorithm + // Only add it if we expect the error to come up on a different + // level than LEVEL_0, because in this case the error would + // (correctly) be mapped to the distraction field + if ($target !== self::LEVEL_0) { + $mapFromPath = new PropertyPath($mapFrom); + $mapFromPrefix = $mapFromPath->isIndex(0) + ? '['.$mapFromPath->getElement(0).']' + : $mapFromPath->getElement(0); + $distraction = $this->getForm('distraction', $mapFromPrefix); + + $parent->add($distraction); + } + + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + if ($target !== self::LEVEL_0) { + $this->assertCount(0, $distraction->getErrors(), 'distraction should not have an error, but has one'); + } + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError($violation, $parent)), iterator_to_array($parent->getErrors()), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $child)), iterator_to_array($child->getErrors()), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChildName.' should have an error, but has none'); + } + } + + public function provideCustomFormErrorTests() + { + // This case is different than the data errors, because here the + // left side of the mapping refers to the property path of the actual + // children. In other words, a child error only works if + // 1) the error actually maps to an existing child and + // 2) the property path of that child (relative to the form providing + // the mapping) matches the left side of the mapping + return array( + // mapping target, map from, map to, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street].prop'), + + // Property path of the erroneous field and mapping must match exactly + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1B, 'foo', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1B, '[foo]', 'address', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_2, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_2, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1, '[foo]', 'address', 'foo', '[foo]', 'address', 'address', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street].prop'), + + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].children[street].data'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo].data[street].prop'), + + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street].data'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street].data.prop'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street.prop'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'foo', 'address', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street].prop'), + + // Map to a nested child + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[foo]'), + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[foo]'), + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[foo]'), + array(self::LEVEL_2, 'foo', 'address.street', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[foo]'), + + // Map from a nested child + array(self::LEVEL_1B, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_1B, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1B, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, 'address.street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1B, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1B, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1B, 'address[street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, 'address[street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_1B, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_1B, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1B, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, '[address].street', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', 'address', 'street', '[street]', 'children[address].data[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].children[street]'), + array(self::LEVEL_2, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_1B, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1B, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].children[street]'), + array(self::LEVEL_1, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data.street'), + array(self::LEVEL_1B, '[address][street]', 'foo', 'foo', 'foo', 'address', '[address]', 'street', '[street]', 'children[address].data[street]'), + ); + } + + /** + * @dataProvider provideCustomFormErrorTests + */ + public function testCustomFormErrorMapping($target, $mapFrom, $mapTo, $errorName, $errorPath, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent', null, null, array($mapFrom => $mapTo)); + $child = $this->getForm($childName, $childPath); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + $errorChild = $this->getForm($errorName, $errorPath); + + $parent->add($child); + $parent->add($errorChild); + $child->add($grandChild); + + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $parent)), iterator_to_array($parent->getErrors()), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $child)), iterator_to_array($child->getErrors()), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1B === $target) { + $this->assertEquals(array($this->getFormError($violation, $errorChild)), iterator_to_array($errorChild->getErrors()), $errorName.' should have an error, but has none'); + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChildName.' should have an error, but has none'); + } + } + + public function provideErrorTestsForFormInheritingParentData() + { + return array( + // mapping target, child name, its property path, grand child name, its property path, violation path + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].children[street].data.prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'children[address].data.street.prop'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street]'), + array(self::LEVEL_1, 'address', 'address', 'street', 'street', 'children[address].data[street].prop'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.street'), + array(self::LEVEL_2, 'address', 'address', 'street', 'street', 'data.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address.street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address.street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address[street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data.address[street].prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address].street.prop'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street]'), + array(self::LEVEL_0, 'address', 'address', 'street', 'street', 'data[address][street].prop'), + ); + } + + /** + * @dataProvider provideErrorTestsForFormInheritingParentData + */ + public function testErrorMappingForFormInheritingParentData($target, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath) + { + $violation = $this->getConstraintViolation($violationPath); + $parent = $this->getForm('parent'); + $child = $this->getForm($childName, $childPath, null, array(), true); + $grandChild = $this->getForm($grandChildName, $grandChildPath); + + $parent->add($child); + $child->add($grandChild); + + $parent->submit(array()); + + $this->mapper->mapViolation($violation, $parent); + + if (self::LEVEL_0 === $target) { + $this->assertEquals(array($this->getFormError($violation, $parent)), iterator_to_array($parent->getErrors()), $parent->getName().' should have an error, but has none'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } elseif (self::LEVEL_1 === $target) { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $child)), iterator_to_array($child->getErrors()), $childName.' should have an error, but has none'); + $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); + } else { + $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); + $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); + $this->assertEquals(array($this->getFormError($violation, $grandChild)), iterator_to_array($grandChild->getErrors()), $grandChildName.' should have an error, but has none'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php new file mode 100644 index 0000000..fdbf747 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationPathTest.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper; + +use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPath; + +/** + * @author Bernhard Schussek + */ +class ViolationPathTest extends \PHPUnit_Framework_TestCase +{ + public function providePaths() + { + return array( + array('children[address]', array( + array('address', true, true), + )), + array('children[address].children[street]', array( + array('address', true, true), + array('street', true, true), + )), + array('children[address][street]', array( + array('address', true, true), + array('street', true, true), + ), 'children[address].children[street]'), + array('children[address].data', array( + array('address', true, true), + ), 'children[address]'), + array('children[address].data.street', array( + array('address', true, true), + array('street', false, false), + )), + array('children[address].data[street]', array( + array('address', true, true), + array('street', false, true), + )), + array('children[address].children[street].data.name', array( + array('address', true, true), + array('street', true, true), + array('name', false, false), + )), + array('children[address].children[street].data[name]', array( + array('address', true, true), + array('street', true, true), + array('name', false, true), + )), + array('data.address', array( + array('address', false, false), + )), + array('data[address]', array( + array('address', false, true), + )), + array('data.address.street', array( + array('address', false, false), + array('street', false, false), + )), + array('data[address].street', array( + array('address', false, true), + array('street', false, false), + )), + array('data.address[street]', array( + array('address', false, false), + array('street', false, true), + )), + array('data[address][street]', array( + array('address', false, true), + array('street', false, true), + )), + // A few invalid examples + array('data', array(), ''), + array('children', array(), ''), + array('children.address', array(), ''), + array('children.address[street]', array(), ''), + ); + } + + /** + * @dataProvider providePaths + */ + public function testCreatePath($string, $entries, $slicedPath = null) + { + if (null === $slicedPath) { + $slicedPath = $string; + } + + $path = new ViolationPath($string); + + $this->assertSame($slicedPath, $path->__toString()); + $this->assertSame(count($entries), count($path->getElements())); + $this->assertSame(count($entries), $path->getLength()); + + foreach ($entries as $index => $entry) { + $this->assertEquals($entry[0], $path->getElement($index)); + $this->assertSame($entry[1], $path->mapsForm($index)); + $this->assertSame($entry[2], $path->isIndex($index)); + $this->assertSame(!$entry[2], $path->isProperty($index)); + } + } + + public function provideParents() + { + return array( + array('children[address]', null), + array('children[address].children[street]', 'children[address]'), + array('children[address].data.street', 'children[address]'), + array('children[address].data[street]', 'children[address]'), + array('data.address', null), + array('data.address.street', 'data.address'), + array('data.address[street]', 'data.address'), + array('data[address].street', 'data[address]'), + array('data[address][street]', 'data[address]'), + ); + } + + /** + * @dataProvider provideParents + */ + public function testGetParent($violationPath, $parentPath) + { + $path = new ViolationPath($violationPath); + $parent = null === $parentPath ? null : new ViolationPath($parentPath); + + $this->assertEquals($parent, $path->getParent()); + } + + public function testGetElement() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertEquals('street', $path->getElement(1)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetElementDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->getElement(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetElementDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->getElement(-1); + } + + public function testIsProperty() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertFalse($path->isProperty(1)); + $this->assertTrue($path->isProperty(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsPropertyDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isProperty(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsPropertyDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isProperty(-1); + } + + public function testIsIndex() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertTrue($path->isIndex(1)); + $this->assertFalse($path->isIndex(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsIndexDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isIndex(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsIndexDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->isIndex(-1); + } + + public function testMapsForm() + { + $path = new ViolationPath('children[address].data[street].name'); + + $this->assertTrue($path->mapsForm(0)); + $this->assertFalse($path->mapsForm(1)); + $this->assertFalse($path->mapsForm(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testMapsFormDoesNotAcceptInvalidIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->mapsForm(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testMapsFormDoesNotAcceptNegativeIndices() + { + $path = new ViolationPath('children[address].data[street].name'); + + $path->mapsForm(-1); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AbstractAuthor.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AbstractAuthor.php new file mode 100644 index 0000000..03a6b72 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AbstractAuthor.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +abstract class AbstractAuthor +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php new file mode 100644 index 0000000..131b3fd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php @@ -0,0 +1,24 @@ +getFormFactory(); + + $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) { + $form = $event->getForm(); + $type = $form->getName() % 2 === 0 + ? 'Symfony\Component\Form\Extension\Core\Type\TextType' + : 'Symfony\Component\Form\Extension\Core\Type\TextareaType'; + $form->add('title', $type); + }); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Author.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Author.php new file mode 100644 index 0000000..39765d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Author.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +class Author +{ + public $firstName; + private $lastName; + private $australian; + public $child; + private $readPermissions; + + private $privateProperty; + + public function __construct($firstName = null, $lastName = null) + { + $this->firstName = $firstName; + $this->lastName = $lastName; + } + + public function setLastName($lastName) + { + $this->lastName = $lastName; + } + + public function getLastName() + { + return $this->lastName; + } + + private function getPrivateGetter() + { + return 'foobar'; + } + + public function setAustralian($australian) + { + $this->australian = $australian; + } + + public function isAustralian() + { + return $this->australian; + } + + public function setReadPermissions($bool) + { + $this->readPermissions = $bool; + } + + public function hasReadPermissions() + { + return $this->readPermissions; + } + + private function isPrivateIsser() + { + return true; + } + + public function getPrivateSetter() + { + } + + private function setPrivateSetter($data) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorInterface.php new file mode 100644 index 0000000..984cb54 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +interface AuthorInterface +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorType.php new file mode 100644 index 0000000..504f812 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/AuthorType.php @@ -0,0 +1,25 @@ +add('firstName') + ->add('lastName') + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php new file mode 100644 index 0000000..950f677 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/CustomArrayObject.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +/** + * This class is a hand written simplified version of PHP native `ArrayObject` + * class, to show that it behaves differently than the PHP native implementation. + */ +class CustomArrayObject implements \ArrayAccess, \IteratorAggregate, \Countable, \Serializable +{ + private $array; + + public function __construct(array $array = null) + { + $this->array = $array ?: array(); + } + + public function offsetExists($offset) + { + return array_key_exists($offset, $this->array); + } + + public function offsetGet($offset) + { + return $this->array[$offset]; + } + + public function offsetSet($offset, $value) + { + if (null === $offset) { + $this->array[] = $value; + } else { + $this->array[$offset] = $value; + } + } + + public function offsetUnset($offset) + { + unset($this->array[$offset]); + } + + public function getIterator() + { + return new \ArrayIterator($this->array); + } + + public function count() + { + return count($this->array); + } + + public function serialize() + { + return serialize($this->array); + } + + public function unserialize($serialized) + { + $this->array = (array) unserialize((string) $serialized); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FBooType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FBooType.php new file mode 100644 index 0000000..fd9ca41 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FBooType.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class FBooType extends AbstractType +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php new file mode 100644 index 0000000..f7fba56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedDataTransformer.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +class FixedDataTransformer implements DataTransformerInterface +{ + private $mapping; + + public function __construct(array $mapping) + { + $this->mapping = $mapping; + } + + public function transform($value) + { + if (!array_key_exists($value, $this->mapping)) { + throw new TransformationFailedException(sprintf('No mapping for value "%s"', $value)); + } + + return $this->mapping[$value]; + } + + public function reverseTransform($value) + { + $result = array_search($value, $this->mapping, true); + + if ($result === false) { + throw new TransformationFailedException(sprintf('No reverse mapping for value "%s"', $value)); + } + + return $result; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php new file mode 100644 index 0000000..762a10b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FixedFilterListener.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class FixedFilterListener implements EventSubscriberInterface +{ + private $mapping; + + public function __construct(array $mapping) + { + $this->mapping = array_merge(array( + 'preSubmit' => array(), + 'onSubmit' => array(), + 'preSetData' => array(), + ), $mapping); + } + + public function preSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (isset($this->mapping['preSubmit'][$data])) { + $event->setData($this->mapping['preSubmit'][$data]); + } + } + + public function onSubmit(FormEvent $event) + { + $data = $event->getData(); + + if (isset($this->mapping['onSubmit'][$data])) { + $event->setData($this->mapping['onSubmit'][$data]); + } + } + + public function preSetData(FormEvent $event) + { + $data = $event->getData(); + + if (isset($this->mapping['preSetData'][$data])) { + $event->setData($this->mapping['preSetData'][$data]); + } + } + + public static function getSubscribedEvents() + { + return array( + FormEvents::PRE_SUBMIT => 'preSubmit', + FormEvents::SUBMIT => 'onSubmit', + FormEvents::PRE_SET_DATA => 'preSetData', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Foo.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Foo.php new file mode 100644 index 0000000..0920bc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Foo.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class Foo extends AbstractType +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Foo1Bar2Type.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Foo1Bar2Type.php new file mode 100644 index 0000000..17bef7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Foo1Bar2Type.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class Foo1Bar2Type extends AbstractType +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooBarHTMLType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooBarHTMLType.php new file mode 100644 index 0000000..c996927 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooBarHTMLType.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class FooBarHTMLType extends AbstractType +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubType.php new file mode 100644 index 0000000..e4a4f36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooSubType.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class FooSubType extends AbstractType +{ + public function getParent() + { + return __NAMESPACE__.'\FooType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooType.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooType.php new file mode 100644 index 0000000..2e144ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooType.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class FooType extends AbstractType +{ + public function getParent() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php new file mode 100644 index 0000000..8d72132 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBarExtension.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\FormBuilderInterface; + +class FooTypeBarExtension extends AbstractTypeExtension +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setAttribute('bar', 'x'); + } + + public function getAllowedOptionValues() + { + return array( + 'a_or_b' => array('c'), + ); + } + + public function getExtendedType() + { + return __NAMESPACE__.'\FooType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php new file mode 100644 index 0000000..646d41f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/FooTypeBazExtension.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\FormBuilderInterface; + +class FooTypeBazExtension extends AbstractTypeExtension +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setAttribute('baz', 'x'); + } + + public function getExtendedType() + { + return __NAMESPACE__.'\FooType'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php new file mode 100644 index 0000000..042bee5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/TestExtension.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\Form\FormTypeExtensionInterface; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\FormExtensionInterface; + +class TestExtension implements FormExtensionInterface +{ + private $types = array(); + + private $extensions = array(); + + private $guesser; + + public function __construct(FormTypeGuesserInterface $guesser) + { + $this->guesser = $guesser; + } + + public function addType(FormTypeInterface $type) + { + $this->types[get_class($type)] = $type; + } + + public function getType($name) + { + return isset($this->types[$name]) ? $this->types[$name] : null; + } + + public function hasType($name) + { + return isset($this->types[$name]); + } + + public function addTypeExtension(FormTypeExtensionInterface $extension) + { + $type = $extension->getExtendedType(); + + if (!isset($this->extensions[$type])) { + $this->extensions[$type] = array(); + } + + $this->extensions[$type][] = $extension; + } + + public function getTypeExtensions($name) + { + return isset($this->extensions[$name]) ? $this->extensions[$name] : array(); + } + + public function hasTypeExtensions($name) + { + return isset($this->extensions[$name]); + } + + public function getTypeGuesser() + { + return $this->guesser; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Type.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Type.php new file mode 100644 index 0000000..61ca7d1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/Type.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; + +class Type extends AbstractType +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/foo b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/foo new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/foo2 b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/foo2 new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/foo3 b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Fixtures/foo3 new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormBuilderTest.php new file mode 100644 index 0000000..86f50b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormBuilderTest.php @@ -0,0 +1,234 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\ButtonBuilder; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\SubmitButtonBuilder; + +class FormBuilderTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + private $factory; + private $builder; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->factory = null; + $this->builder = null; + } + + /** + * Changing the name is not allowed, otherwise the name and property path + * are not synchronized anymore. + * + * @see FormType::buildForm() + */ + public function testNoSetName() + { + $this->assertFalse(method_exists($this->builder, 'setName')); + } + + public function testAddNameNoStringAndNoInteger() + { + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $this->builder->add(true); + } + + public function testAddTypeNoString() + { + $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); + $this->builder->add('foo', 1234); + } + + public function testAddWithGuessFluent() + { + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); + $builder = $this->builder->add('foo'); + $this->assertSame($builder, $this->builder); + } + + public function testAddIsFluent() + { + $builder = $this->builder->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType', array('bar' => 'baz')); + $this->assertSame($builder, $this->builder); + } + + public function testAdd() + { + $this->assertFalse($this->builder->has('foo')); + $this->builder->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $this->assertTrue($this->builder->has('foo')); + } + + public function testAddIntegerName() + { + $this->assertFalse($this->builder->has(0)); + $this->builder->add(0, 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $this->assertTrue($this->builder->has(0)); + } + + public function testAll() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->will($this->returnValue(new FormBuilder('foo', null, $this->dispatcher, $this->factory))); + + $this->assertCount(0, $this->builder->all()); + $this->assertFalse($this->builder->has('foo')); + + $this->builder->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $children = $this->builder->all(); + + $this->assertTrue($this->builder->has('foo')); + $this->assertCount(1, $children); + $this->assertArrayHasKey('foo', $children); + } + + /* + * https://github.com/symfony/symfony/issues/4693 + */ + public function testMaintainOrderOfLazyAndExplicitChildren() + { + $this->builder->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $this->builder->add($this->getFormBuilder('bar')); + $this->builder->add('baz', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + + $children = $this->builder->all(); + + $this->assertSame(array('foo', 'bar', 'baz'), array_keys($children)); + } + + public function testAddFormType() + { + $this->assertFalse($this->builder->has('foo')); + $this->builder->add('foo', $this->getMock('Symfony\Component\Form\FormTypeInterface')); + $this->assertTrue($this->builder->has('foo')); + } + + public function testRemove() + { + $this->builder->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $this->builder->remove('foo'); + $this->assertFalse($this->builder->has('foo')); + } + + public function testRemoveUnknown() + { + $this->builder->remove('foo'); + $this->assertFalse($this->builder->has('foo')); + } + + // https://github.com/symfony/symfony/pull/4826 + public function testRemoveAndGetForm() + { + $this->builder->add('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType'); + $this->builder->remove('foo'); + $form = $this->builder->getForm(); + $this->assertInstanceOf('Symfony\Component\Form\Form', $form); + } + + public function testCreateNoTypeNo() + { + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('foo', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array()) + ; + + $this->builder->create('foo'); + } + + public function testAddButton() + { + $this->builder->add(new ButtonBuilder('reset')); + $this->builder->add(new SubmitButtonBuilder('submit')); + } + + public function testGetUnknown() + { + $this->setExpectedException('Symfony\Component\Form\Exception\InvalidArgumentException', 'The child with the name "foo" does not exist.'); + $this->builder->get('foo'); + } + + public function testGetExplicitType() + { + $expectedType = 'Symfony\Component\Form\Extension\Core\Type\TextType'; + $expectedName = 'foo'; + $expectedOptions = array('bar' => 'baz'); + + $this->factory->expects($this->once()) + ->method('createNamedBuilder') + ->with($expectedName, $expectedType, null, $expectedOptions) + ->will($this->returnValue($this->getFormBuilder())); + + $this->builder->add($expectedName, $expectedType, $expectedOptions); + $builder = $this->builder->get($expectedName); + + $this->assertNotSame($builder, $this->builder); + } + + public function testGetGuessedType() + { + $expectedName = 'foo'; + $expectedOptions = array('bar' => 'baz'); + + $this->factory->expects($this->once()) + ->method('createBuilderForProperty') + ->with('stdClass', $expectedName, null, $expectedOptions) + ->will($this->returnValue($this->getFormBuilder())); + + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); + $this->builder->add($expectedName, null, $expectedOptions); + $builder = $this->builder->get($expectedName); + + $this->assertNotSame($builder, $this->builder); + } + + public function testGetFormConfigErasesReferences() + { + $builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); + $builder->add(new FormBuilder('child', null, $this->dispatcher, $this->factory)); + + $config = $builder->getFormConfig(); + $reflClass = new \ReflectionClass($config); + $children = $reflClass->getProperty('children'); + $unresolvedChildren = $reflClass->getProperty('unresolvedChildren'); + + $children->setAccessible(true); + $unresolvedChildren->setAccessible(true); + + $this->assertEmpty($children->getValue($config)); + $this->assertEmpty($unresolvedChildren->getValue($config)); + } + + private function getFormBuilder($name = 'name') + { + $mock = $this->getMockBuilder('Symfony\Component\Form\FormBuilder') + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects($this->any()) + ->method('getName') + ->will($this->returnValue($name)); + + return $mock; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormConfigTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormConfigTest.php new file mode 100644 index 0000000..b77632c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormConfigTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\Exception\InvalidArgumentException; + +/** + * @author Bernhard Schussek + */ +class FormConfigTest extends \PHPUnit_Framework_TestCase +{ + public function getHtml4Ids() + { + return array( + array('z0', true), + array('A0', true), + array('A9', true), + array('Z0', true), + array('#', false), + array('a#', false), + array('a$', false), + array('a%', false), + array('a ', false), + array("a\t", false), + array("a\n", false), + array('a-', true), + array('a_', true), + array('a:', true), + // Periods are allowed by the HTML4 spec, but disallowed by us + // because they break the generated property paths + array('a.', false), + // Contrary to the HTML4 spec, we allow names starting with a + // number, otherwise naming fields by collection indices is not + // possible. + // For root forms, leading digits will be stripped from the + // "id" attribute to produce valid HTML4. + array('0', true), + array('9', true), + // Contrary to the HTML4 spec, we allow names starting with an + // underscore, since this is already a widely used practice in + // Symfony. + // For root forms, leading underscores will be stripped from the + // "id" attribute to produce valid HTML4. + array('_', true), + // Integers are allowed + array(0, true), + array(123, true), + // NULL is allowed + array(null, true), + // Other types are not + array(1.23, false), + array(5., false), + array(true, false), + array(new \stdClass(), false), + ); + } + + /** + * @dataProvider getHtml4Ids + */ + public function testNameAcceptsOnlyNamesValidAsIdsInHtml4($name, $accepted) + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + + try { + new FormConfigBuilder($name, null, $dispatcher); + if (!$accepted) { + $this->fail(sprintf('The value "%s" should not be accepted', $name)); + } + } catch (UnexpectedTypeException $e) { + // if the value was not accepted, but should be, rethrow exception + if ($accepted) { + throw $e; + } + } catch (InvalidArgumentException $e) { + // if the value was not accepted, but should be, rethrow exception + if ($accepted) { + throw $e; + } + } + } + + public function testGetRequestHandlerCreatesNativeRequestHandlerIfNotSet() + { + $config = $this->getConfigBuilder()->getFormConfig(); + + $this->assertInstanceOf('Symfony\Component\Form\NativeRequestHandler', $config->getRequestHandler()); + } + + public function testGetRequestHandlerReusesNativeRequestHandlerInstance() + { + $config1 = $this->getConfigBuilder()->getFormConfig(); + $config2 = $this->getConfigBuilder()->getFormConfig(); + + $this->assertSame($config1->getRequestHandler(), $config2->getRequestHandler()); + } + + public function testSetMethodAllowsGet() + { + $this->getConfigBuilder()->setMethod('GET'); + } + + public function testSetMethodAllowsPost() + { + $this->getConfigBuilder()->setMethod('POST'); + } + + public function testSetMethodAllowsPut() + { + $this->getConfigBuilder()->setMethod('PUT'); + } + + public function testSetMethodAllowsDelete() + { + $this->getConfigBuilder()->setMethod('DELETE'); + } + + public function testSetMethodAllowsPatch() + { + $this->getConfigBuilder()->setMethod('PATCH'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testSetMethodDoesNotAllowOtherValues() + { + $this->getConfigBuilder()->setMethod('foo'); + } + + private function getConfigBuilder($name = 'name') + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + + return new FormConfigBuilder($name, null, $dispatcher); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php new file mode 100644 index 0000000..9d5cb47 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryBuilderTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormFactoryBuilder; +use Symfony\Component\Form\Tests\Fixtures\FooType; + +class FormFactoryBuilderTest extends \PHPUnit_Framework_TestCase +{ + private $registry; + private $guesser; + private $type; + + protected function setUp() + { + $factory = new \ReflectionClass('Symfony\Component\Form\FormFactory'); + $this->registry = $factory->getProperty('registry'); + $this->registry->setAccessible(true); + + $this->guesser = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->type = new FooType(); + } + + public function testAddType() + { + $factoryBuilder = new FormFactoryBuilder(); + $factoryBuilder->addType($this->type); + + $factory = $factoryBuilder->getFormFactory(); + $registry = $this->registry->getValue($factory); + $extensions = $registry->getExtensions(); + + $this->assertCount(1, $extensions); + $this->assertTrue($extensions[0]->hasType(get_class($this->type))); + $this->assertNull($extensions[0]->getTypeGuesser()); + } + + public function testAddTypeGuesser() + { + $factoryBuilder = new FormFactoryBuilder(); + $factoryBuilder->addTypeGuesser($this->guesser); + + $factory = $factoryBuilder->getFormFactory(); + $registry = $this->registry->getValue($factory); + $extensions = $registry->getExtensions(); + + $this->assertCount(1, $extensions); + $this->assertNotNull($extensions[0]->getTypeGuesser()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryTest.php new file mode 100644 index 0000000..22e8884 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -0,0 +1,485 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormTypeGuesserChain; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\Guess\Guess; +use Symfony\Component\Form\Guess\ValueGuess; +use Symfony\Component\Form\Guess\TypeGuess; + +/** + * @author Bernhard Schussek + */ +class FormFactoryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser1; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser2; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $registry; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $resolvedTypeFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $builder; + + /** + * @var FormFactory + */ + private $factory; + + protected function setUp() + { + $this->resolvedTypeFactory = $this->getMock('Symfony\Component\Form\ResolvedFormTypeFactoryInterface'); + $this->guesser1 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->guesser2 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->registry = $this->getMock('Symfony\Component\Form\FormRegistryInterface'); + $this->builder = $this->getMock('Symfony\Component\Form\Test\FormBuilderInterface'); + $this->factory = new FormFactory($this->registry, $this->resolvedTypeFactory); + + $this->registry->expects($this->any()) + ->method('getTypeGuesser') + ->will($this->returnValue(new FormTypeGuesserChain(array( + $this->guesser1, + $this->guesser2, + )))); + } + + public function testCreateNamedBuilderWithTypeName() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedOptions = array('a' => '2', 'b' => '3'); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue($this->builder)); + + $this->builder->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($resolvedOptions)); + + $resolvedType->expects($this->once()) + ->method('buildForm') + ->with($this->builder, $resolvedOptions); + + $this->assertSame($this->builder, $this->factory->createNamedBuilder('name', 'type', null, $options)); + } + + public function testCreateNamedBuilderFillsDataOption() + { + $givenOptions = array('a' => '1', 'b' => '2'); + $expectedOptions = array_merge($givenOptions, array('data' => 'DATA')); + $resolvedOptions = array('a' => '2', 'b' => '3', 'data' => 'DATA'); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $expectedOptions) + ->will($this->returnValue($this->builder)); + + $this->builder->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($resolvedOptions)); + + $resolvedType->expects($this->once()) + ->method('buildForm') + ->with($this->builder, $resolvedOptions); + + $this->assertSame($this->builder, $this->factory->createNamedBuilder('name', 'type', 'DATA', $givenOptions)); + } + + public function testCreateNamedBuilderDoesNotOverrideExistingDataOption() + { + $options = array('a' => '1', 'b' => '2', 'data' => 'CUSTOM'); + $resolvedOptions = array('a' => '2', 'b' => '3', 'data' => 'CUSTOM'); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue($this->builder)); + + $this->builder->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($resolvedOptions)); + + $resolvedType->expects($this->once()) + ->method('buildForm') + ->with($this->builder, $resolvedOptions); + + $this->assertSame($this->builder, $this->factory->createNamedBuilder('name', 'type', 'DATA', $options)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + * @expectedExceptionMessage Expected argument of type "string", "stdClass" given + */ + public function testCreateNamedBuilderThrowsUnderstandableException() + { + $this->factory->createNamedBuilder('name', new \stdClass()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + * @expectedExceptionMessage Expected argument of type "string", "stdClass" given + */ + public function testCreateThrowsUnderstandableException() + { + $this->factory->create(new \stdClass()); + } + + public function testCreateUsesBlockPrefixIfTypeGivenAsString() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedOptions = array('a' => '2', 'b' => '3'); + + // the interface does not have the method, so use the real class + $resolvedType = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormType') + ->disableOriginalConstructor() + ->getMock(); + + $resolvedType->expects($this->any()) + ->method('getBlockPrefix') + ->willReturn('TYPE_PREFIX'); + + $this->registry->expects($this->any()) + ->method('getType') + ->with('TYPE') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'TYPE_PREFIX', $options) + ->will($this->returnValue($this->builder)); + + $this->builder->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($resolvedOptions)); + + $resolvedType->expects($this->once()) + ->method('buildForm') + ->with($this->builder, $resolvedOptions); + + $this->builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->create('TYPE', null, $options)); + } + + public function testCreateNamed() + { + $options = array('a' => '1', 'b' => '2'); + $resolvedOptions = array('a' => '2', 'b' => '3'); + $resolvedType = $this->getMockResolvedType(); + + $this->registry->expects($this->once()) + ->method('getType') + ->with('type') + ->will($this->returnValue($resolvedType)); + + $resolvedType->expects($this->once()) + ->method('createBuilder') + ->with($this->factory, 'name', $options) + ->will($this->returnValue($this->builder)); + + $this->builder->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($resolvedOptions)); + + $resolvedType->expects($this->once()) + ->method('buildForm') + ->with($this->builder, $resolvedOptions); + + $this->builder->expects($this->once()) + ->method('getForm') + ->will($this->returnValue('FORM')); + + $this->assertSame('FORM', $this->factory->createNamed('name', 'type', null, $options)); + } + + public function testCreateBuilderForPropertyWithoutTypeGuesser() + { + $registry = $this->getMock('Symfony\Component\Form\FormRegistryInterface'); + $factory = $this->getMockBuilder('Symfony\Component\Form\FormFactory') + ->setMethods(array('createNamedBuilder')) + ->setConstructorArgs(array($registry, $this->resolvedTypeFactory)) + ->getMock(); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array()) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testCreateBuilderForPropertyCreatesFormWithHighestConfidence() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'Symfony\Component\Form\Extension\Core\Type\TextType', + array('attr' => array('maxlength' => 10)), + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'Symfony\Component\Form\Extension\Core\Type\PasswordType', + array('attr' => array('maxlength' => 7)), + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\PasswordType', null, array('attr' => array('maxlength' => 7))) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testCreateBuilderCreatesTextFormIfNoGuess() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(null)); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType') + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty('Application\Author', 'firstName'); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testOptionsCanBeOverridden() + { + $this->guesser1->expects($this->once()) + ->method('guessType') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new TypeGuess( + 'Symfony\Component\Form\Extension\Core\Type\TextType', + array('attr' => array('maxlength' => 10)), + Guess::MEDIUM_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('attr' => array('maxlength' => 11))) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName', + null, + array('attr' => array('maxlength' => 11)) + ); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testCreateBuilderUsesMaxLengthIfFound() + { + $this->guesser1->expects($this->once()) + ->method('guessMaxLength') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + 15, + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessMaxLength') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + 20, + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('attr' => array('maxlength' => 20))) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testCreateBuilderUsesMaxLengthAndPattern() + { + $this->guesser1->expects($this->once()) + ->method('guessMaxLength') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + 20, + Guess::HIGH_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '.{5,}', + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('attr' => array('maxlength' => 20, 'pattern' => '.{5,}', 'class' => 'tinymce'))) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName', + null, + array('attr' => array('class' => 'tinymce')) + ); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testCreateBuilderUsesRequiredSettingWithHighestConfidence() + { + $this->guesser1->expects($this->once()) + ->method('guessRequired') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + true, + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessRequired') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + false, + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('required' => false)) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $this->builder); + } + + public function testCreateBuilderUsesPatternIfFound() + { + $this->guesser1->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '[a-z]', + Guess::MEDIUM_CONFIDENCE + ))); + + $this->guesser2->expects($this->once()) + ->method('guessPattern') + ->with('Application\Author', 'firstName') + ->will($this->returnValue(new ValueGuess( + '[a-zA-Z]', + Guess::HIGH_CONFIDENCE + ))); + + $factory = $this->getMockFactory(array('createNamedBuilder')); + + $factory->expects($this->once()) + ->method('createNamedBuilder') + ->with('firstName', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, array('attr' => array('pattern' => '[a-zA-Z]'))) + ->will($this->returnValue('builderInstance')); + + $this->builder = $factory->createBuilderForProperty( + 'Application\Author', + 'firstName' + ); + + $this->assertEquals('builderInstance', $this->builder); + } + + private function getMockFactory(array $methods = array()) + { + return $this->getMockBuilder('Symfony\Component\Form\FormFactory') + ->setMethods($methods) + ->setConstructorArgs(array($this->registry, $this->resolvedTypeFactory)) + ->getMock(); + } + + private function getMockResolvedType() + { + return $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRegistryTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRegistryTest.php new file mode 100644 index 0000000..cf3cfdc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRegistryTest.php @@ -0,0 +1,216 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormRegistry; +use Symfony\Component\Form\FormTypeGuesserChain; +use Symfony\Component\Form\ResolvedFormType; +use Symfony\Component\Form\ResolvedFormTypeFactoryInterface; +use Symfony\Component\Form\Tests\Fixtures\FooSubType; +use Symfony\Component\Form\Tests\Fixtures\FooType; +use Symfony\Component\Form\Tests\Fixtures\FooTypeBarExtension; +use Symfony\Component\Form\Tests\Fixtures\FooTypeBazExtension; +use Symfony\Component\Form\Tests\Fixtures\TestExtension; + +/** + * @author Bernhard Schussek + */ +class FormRegistryTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FormRegistry + */ + private $registry; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ResolvedFormTypeFactoryInterface + */ + private $resolvedTypeFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser1; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $guesser2; + + /** + * @var TestExtension + */ + private $extension1; + + /** + * @var TestExtension + */ + private $extension2; + + protected function setUp() + { + $this->resolvedTypeFactory = $this->getMock('Symfony\Component\Form\ResolvedFormTypeFactory'); + $this->guesser1 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->guesser2 = $this->getMock('Symfony\Component\Form\FormTypeGuesserInterface'); + $this->extension1 = new TestExtension($this->guesser1); + $this->extension2 = new TestExtension($this->guesser2); + $this->registry = new FormRegistry(array( + $this->extension1, + $this->extension2, + ), $this->resolvedTypeFactory); + } + + public function testGetTypeFromExtension() + { + $type = new FooType(); + $resolvedType = new ResolvedFormType($type); + + $this->extension2->addType($type); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->willReturn($resolvedType); + + $this->assertSame($resolvedType, $this->registry->getType(get_class($type))); + } + + public function testLoadUnregisteredType() + { + $type = new FooType(); + $resolvedType = new ResolvedFormType($type); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->willReturn($resolvedType); + + $this->assertSame($resolvedType, $this->registry->getType('Symfony\Component\Form\Tests\Fixtures\FooType')); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testFailIfUnregisteredTypeNoClass() + { + $this->registry->getType('Symfony\Blubb'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testFailIfUnregisteredTypeNoFormType() + { + $this->registry->getType('stdClass'); + } + + public function testGetTypeWithTypeExtensions() + { + $type = new FooType(); + $ext1 = new FooTypeBarExtension(); + $ext2 = new FooTypeBazExtension(); + $resolvedType = new ResolvedFormType($type, array($ext1, $ext2)); + + $this->extension2->addType($type); + $this->extension1->addTypeExtension($ext1); + $this->extension2->addTypeExtension($ext2); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type, array($ext1, $ext2)) + ->willReturn($resolvedType); + + $this->assertSame($resolvedType, $this->registry->getType(get_class($type))); + } + + public function testGetTypeConnectsParent() + { + $parentType = new FooType(); + $type = new FooSubType(); + $parentResolvedType = new ResolvedFormType($parentType); + $resolvedType = new ResolvedFormType($type); + + $this->extension1->addType($parentType); + $this->extension2->addType($type); + + $this->resolvedTypeFactory->expects($this->at(0)) + ->method('createResolvedType') + ->with($parentType) + ->willReturn($parentResolvedType); + + $this->resolvedTypeFactory->expects($this->at(1)) + ->method('createResolvedType') + ->with($type, array(), $parentResolvedType) + ->willReturn($resolvedType); + + $this->assertSame($resolvedType, $this->registry->getType(get_class($type))); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testGetTypeThrowsExceptionIfTypeNotFound() + { + $this->registry->getType('bar'); + } + + public function testHasTypeAfterLoadingFromExtension() + { + $type = new FooType(); + $resolvedType = new ResolvedFormType($type); + + $this->resolvedTypeFactory->expects($this->once()) + ->method('createResolvedType') + ->with($type) + ->willReturn($resolvedType); + + $this->extension2->addType($type); + + $this->assertTrue($this->registry->hasType(get_class($type))); + } + + public function testHasTypeIfFQCN() + { + $this->assertTrue($this->registry->hasType('Symfony\Component\Form\Tests\Fixtures\FooType')); + } + + public function testDoesNotHaveTypeIfNonExistingClass() + { + $this->assertFalse($this->registry->hasType('Symfony\Blubb')); + } + + public function testDoesNotHaveTypeIfNoFormType() + { + $this->assertFalse($this->registry->hasType('stdClass')); + } + + public function testGetTypeGuesser() + { + $expectedGuesser = new FormTypeGuesserChain(array($this->guesser1, $this->guesser2)); + + $this->assertEquals($expectedGuesser, $this->registry->getTypeGuesser()); + + $registry = new FormRegistry( + array($this->getMock('Symfony\Component\Form\FormExtensionInterface')), + $this->resolvedTypeFactory + ); + + $this->assertNull($registry->getTypeGuesser()); + } + + public function testGetExtensions() + { + $expectedExtensions = array($this->extension1, $this->extension2); + + $this->assertEquals($expectedExtensions, $this->registry->getExtensions()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRendererTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRendererTest.php new file mode 100644 index 0000000..eda35e3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/FormRendererTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +class FormRendererTest extends \PHPUnit_Framework_TestCase +{ + public function testHumanize() + { + $renderer = $this->getMockBuilder('Symfony\Component\Form\FormRenderer') + ->setMethods(null) + ->disableOriginalConstructor() + ->getMock() + ; + + $this->assertEquals('Is active', $renderer->humanize('is_active')); + $this->assertEquals('Is active', $renderer->humanize('isActive')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Guess/GuessTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Guess/GuessTest.php new file mode 100644 index 0000000..2422663 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Guess/GuessTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Guess; + +use Symfony\Component\Form\Guess\Guess; + +class TestGuess extends Guess +{ +} + +class GuessTest extends \PHPUnit_Framework_TestCase +{ + public function testGetBestGuessReturnsGuessWithHighestConfidence() + { + $guess1 = new TestGuess(Guess::MEDIUM_CONFIDENCE); + $guess2 = new TestGuess(Guess::LOW_CONFIDENCE); + $guess3 = new TestGuess(Guess::HIGH_CONFIDENCE); + + $this->assertSame($guess3, Guess::getBestGuess(array($guess1, $guess2, $guess3))); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGuessExpectsValidConfidence() + { + new TestGuess(5); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php new file mode 100644 index 0000000..3858006 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/NativeRequestHandlerTest.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\NativeRequestHandler; + +/** + * @author Bernhard Schussek + */ +class NativeRequestHandlerTest extends AbstractRequestHandlerTest +{ + private static $serverBackup; + + public static function setUpBeforeClass() + { + self::$serverBackup = $_SERVER; + } + + protected function setUp() + { + parent::setUp(); + + $_GET = array(); + $_POST = array(); + $_FILES = array(); + $_SERVER = array( + // PHPUnit needs this entry + 'SCRIPT_NAME' => self::$serverBackup['SCRIPT_NAME'], + ); + } + + protected function tearDown() + { + parent::tearDown(); + + $_GET = array(); + $_POST = array(); + $_FILES = array(); + $_SERVER = self::$serverBackup; + } + + /** + * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException + */ + public function testRequestShouldBeNull() + { + $this->requestHandler->handleRequest($this->getMockForm('name', 'GET'), 'request'); + } + + public function testMethodOverrideHeaderTakesPrecedenceIfPost() + { + $form = $this->getMockForm('param1', 'PUT'); + + $this->setRequestData('POST', array( + 'param1' => 'DATA', + )); + + $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; + + $form->expects($this->once()) + ->method('submit') + ->with('DATA'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testConvertEmptyUploadedFilesToNull() + { + $form = $this->getMockForm('param1', 'POST', false); + + $this->setRequestData('POST', array(), array('param1' => array( + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => UPLOAD_ERR_NO_FILE, + 'size' => 0, + ))); + + $form->expects($this->once()) + ->method('submit') + ->with($this->identicalTo(null)); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testFixBuggyFilesArray() + { + $form = $this->getMockForm('param1', 'POST', false); + + $this->setRequestData('POST', array(), array('param1' => array( + 'name' => array( + 'field' => 'upload.txt', + ), + 'type' => array( + 'field' => 'text/plain', + ), + 'tmp_name' => array( + 'field' => 'owfdskjasdfsa', + ), + 'error' => array( + 'field' => UPLOAD_ERR_OK, + ), + 'size' => array( + 'field' => 100, + ), + ))); + + $form->expects($this->once()) + ->method('submit') + ->with(array( + 'field' => array( + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => UPLOAD_ERR_OK, + 'size' => 100, + ), + )); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testFixBuggyNestedFilesArray() + { + $form = $this->getMockForm('param1', 'POST'); + + $this->setRequestData('POST', array(), array('param1' => array( + 'name' => array( + 'field' => array('subfield' => 'upload.txt'), + ), + 'type' => array( + 'field' => array('subfield' => 'text/plain'), + ), + 'tmp_name' => array( + 'field' => array('subfield' => 'owfdskjasdfsa'), + ), + 'error' => array( + 'field' => array('subfield' => UPLOAD_ERR_OK), + ), + 'size' => array( + 'field' => array('subfield' => 100), + ), + ))); + + $form->expects($this->once()) + ->method('submit') + ->with(array( + 'field' => array( + 'subfield' => array( + 'name' => 'upload.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa', + 'error' => UPLOAD_ERR_OK, + 'size' => 100, + ), + ), + )); + + $this->requestHandler->handleRequest($form, $this->request); + } + + public function testMethodOverrideHeaderIgnoredIfNotPost() + { + $form = $this->getMockForm('param1', 'POST'); + + $this->setRequestData('GET', array( + 'param1' => 'DATA', + )); + + $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; + + $form->expects($this->never()) + ->method('submit'); + + $this->requestHandler->handleRequest($form, $this->request); + } + + protected function setRequestData($method, $data, $files = array()) + { + if ('GET' === $method) { + $_GET = $data; + $_FILES = array(); + } else { + $_POST = $data; + $_FILES = $files; + } + + $_SERVER = array( + 'REQUEST_METHOD' => $method, + // PHPUnit needs this entry + 'SCRIPT_NAME' => self::$serverBackup['SCRIPT_NAME'], + ); + } + + protected function getRequestHandler() + { + return new NativeRequestHandler($this->serverParams); + } + + protected function getMockFile($suffix = '') + { + return array( + 'name' => 'upload'.$suffix.'.txt', + 'type' => 'text/plain', + 'tmp_name' => 'owfdskjasdfsa'.$suffix, + 'error' => UPLOAD_ERR_OK, + 'size' => 100, + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php new file mode 100644 index 0000000..15025e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/ResolvedFormTypeTest.php @@ -0,0 +1,393 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\FormTypeExtensionInterface; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\Form\ResolvedFormType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * @author Bernhard Schussek + */ +class ResolvedFormTypeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $factory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dataMapper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|FormTypeInterface + */ + private $parentType; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|FormTypeInterface + */ + private $type; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|FormTypeExtensionInterface + */ + private $extension1; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|FormTypeExtensionInterface + */ + private $extension2; + + /** + * @var ResolvedFormType + */ + private $parentResolvedType; + + /** + * @var ResolvedFormType + */ + private $resolvedType; + + protected function setUp() + { + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + $this->dataMapper = $this->getMock('Symfony\Component\Form\DataMapperInterface'); + $this->parentType = $this->getMockFormType(); + $this->type = $this->getMockFormType(); + $this->extension1 = $this->getMockFormTypeExtension(); + $this->extension2 = $this->getMockFormTypeExtension(); + $this->parentResolvedType = new ResolvedFormType($this->parentType); + $this->resolvedType = new ResolvedFormType($this->type, array($this->extension1, $this->extension2), $this->parentResolvedType); + } + + public function testGetOptionsResolver() + { + $i = 0; + + $assertIndexAndAddOption = function ($index, $option, $default) use (&$i) { + return function (OptionsResolver $resolver) use (&$i, $index, $option, $default) { + $this->assertEquals($index, $i, 'Executed at index '.$index); + + ++$i; + + $resolver->setDefaults(array($option => $default)); + }; + }; + + // First the default options are generated for the super type + $this->parentType->expects($this->once()) + ->method('configureOptions') + ->will($this->returnCallback($assertIndexAndAddOption(0, 'a', 'a_default'))); + + // The form type itself + $this->type->expects($this->once()) + ->method('configureOptions') + ->will($this->returnCallback($assertIndexAndAddOption(1, 'b', 'b_default'))); + + // And its extensions + $this->extension1->expects($this->once()) + ->method('configureOptions') + ->will($this->returnCallback($assertIndexAndAddOption(2, 'c', 'c_default'))); + + $this->extension2->expects($this->once()) + ->method('configureOptions') + ->will($this->returnCallback($assertIndexAndAddOption(3, 'd', 'd_default'))); + + $givenOptions = array('a' => 'a_custom', 'c' => 'c_custom'); + $resolvedOptions = array('a' => 'a_custom', 'b' => 'b_default', 'c' => 'c_custom', 'd' => 'd_default'); + + $resolver = $this->resolvedType->getOptionsResolver(); + + $this->assertEquals($resolvedOptions, $resolver->resolve($givenOptions)); + } + + public function testCreateBuilder() + { + $givenOptions = array('a' => 'a_custom', 'c' => 'c_custom'); + $resolvedOptions = array('a' => 'a_custom', 'b' => 'b_default', 'c' => 'c_custom', 'd' => 'd_default'); + $optionsResolver = $this->getMock('Symfony\Component\OptionsResolver\OptionsResolver'); + + $this->resolvedType = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormType') + ->setConstructorArgs(array($this->type, array($this->extension1, $this->extension2), $this->parentResolvedType)) + ->setMethods(array('getOptionsResolver')) + ->getMock(); + + $this->resolvedType->expects($this->once()) + ->method('getOptionsResolver') + ->will($this->returnValue($optionsResolver)); + + $optionsResolver->expects($this->once()) + ->method('resolve') + ->with($givenOptions) + ->will($this->returnValue($resolvedOptions)); + + $factory = $this->getMockFormFactory(); + $builder = $this->resolvedType->createBuilder($factory, 'name', $givenOptions); + + $this->assertSame($this->resolvedType, $builder->getType()); + $this->assertSame($resolvedOptions, $builder->getOptions()); + $this->assertNull($builder->getDataClass()); + } + + public function testCreateBuilderWithDataClassOption() + { + $givenOptions = array('data_class' => 'Foo'); + $resolvedOptions = array('data_class' => '\stdClass'); + $optionsResolver = $this->getMock('Symfony\Component\OptionsResolver\OptionsResolver'); + + $this->resolvedType = $this->getMockBuilder('Symfony\Component\Form\ResolvedFormType') + ->setConstructorArgs(array($this->type, array($this->extension1, $this->extension2), $this->parentResolvedType)) + ->setMethods(array('getOptionsResolver')) + ->getMock(); + + $this->resolvedType->expects($this->once()) + ->method('getOptionsResolver') + ->will($this->returnValue($optionsResolver)); + + $optionsResolver->expects($this->once()) + ->method('resolve') + ->with($givenOptions) + ->will($this->returnValue($resolvedOptions)); + + $factory = $this->getMockFormFactory(); + $builder = $this->resolvedType->createBuilder($factory, 'name', $givenOptions); + + $this->assertSame($this->resolvedType, $builder->getType()); + $this->assertSame($resolvedOptions, $builder->getOptions()); + $this->assertSame('\stdClass', $builder->getDataClass()); + } + + public function testBuildForm() + { + $i = 0; + + $assertIndex = function ($index) use (&$i) { + return function () use (&$i, $index) { + $this->assertEquals($index, $i, 'Executed at index '.$index); + + ++$i; + }; + }; + + $options = array('a' => 'Foo', 'b' => 'Bar'); + $builder = $this->getMock('Symfony\Component\Form\Test\FormBuilderInterface'); + + // First the form is built for the super type + $this->parentType->expects($this->once()) + ->method('buildForm') + ->with($builder, $options) + ->will($this->returnCallback($assertIndex(0))); + + // Then the type itself + $this->type->expects($this->once()) + ->method('buildForm') + ->with($builder, $options) + ->will($this->returnCallback($assertIndex(1))); + + // Then its extensions + $this->extension1->expects($this->once()) + ->method('buildForm') + ->with($builder, $options) + ->will($this->returnCallback($assertIndex(2))); + + $this->extension2->expects($this->once()) + ->method('buildForm') + ->with($builder, $options) + ->will($this->returnCallback($assertIndex(3))); + + $this->resolvedType->buildForm($builder, $options); + } + + public function testCreateView() + { + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + + $view = $this->resolvedType->createView($form); + + $this->assertInstanceOf('Symfony\Component\Form\FormView', $view); + $this->assertNull($view->parent); + } + + public function testCreateViewWithParent() + { + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $parentView = $this->getMock('Symfony\Component\Form\FormView'); + + $view = $this->resolvedType->createView($form, $parentView); + + $this->assertInstanceOf('Symfony\Component\Form\FormView', $view); + $this->assertSame($parentView, $view->parent); + } + + public function testBuildView() + { + $options = array('a' => '1', 'b' => '2'); + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + + $i = 0; + + $assertIndex = function ($index) use (&$i) { + return function () use (&$i, $index) { + $this->assertEquals($index, $i, 'Executed at index '.$index); + + ++$i; + }; + }; + + // First the super type + $this->parentType->expects($this->once()) + ->method('buildView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(0))); + + // Then the type itself + $this->type->expects($this->once()) + ->method('buildView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(1))); + + // Then its extensions + $this->extension1->expects($this->once()) + ->method('buildView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(2))); + + $this->extension2->expects($this->once()) + ->method('buildView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(3))); + + $this->resolvedType->buildView($view, $form, $options); + } + + public function testFinishView() + { + $options = array('a' => '1', 'b' => '2'); + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + + $i = 0; + + $assertIndex = function ($index) use (&$i) { + return function () use (&$i, $index) { + $this->assertEquals($index, $i, 'Executed at index '.$index); + + ++$i; + }; + }; + + // First the super type + $this->parentType->expects($this->once()) + ->method('finishView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(0))); + + // Then the type itself + $this->type->expects($this->once()) + ->method('finishView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(1))); + + // Then its extensions + $this->extension1->expects($this->once()) + ->method('finishView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(2))); + + $this->extension2->expects($this->once()) + ->method('finishView') + ->with($view, $form, $options) + ->will($this->returnCallback($assertIndex(3))); + + $this->resolvedType->finishView($view, $form, $options); + } + + public function testGetBlockPrefix() + { + $this->type->expects($this->once()) + ->method('getBlockPrefix') + ->willReturn('my_prefix'); + + $resolvedType = new ResolvedFormType($this->type); + + $this->assertSame('my_prefix', $resolvedType->getBlockPrefix()); + } + + /** + * @dataProvider provideTypeClassBlockPrefixTuples + */ + public function testBlockPrefixDefaultsToFQCNIfNoName($typeClass, $blockPrefix) + { + $resolvedType = new ResolvedFormType(new $typeClass()); + + $this->assertSame($blockPrefix, $resolvedType->getBlockPrefix()); + } + + public function provideTypeClassBlockPrefixTuples() + { + return array( + array(__NAMESPACE__.'\Fixtures\FooType', 'foo'), + array(__NAMESPACE__.'\Fixtures\Foo', 'foo'), + array(__NAMESPACE__.'\Fixtures\Type', 'type'), + array(__NAMESPACE__.'\Fixtures\FooBarHTMLType', 'foo_bar_html'), + array(__NAMESPACE__.'\Fixtures\Foo1Bar2Type', 'foo1_bar2'), + array(__NAMESPACE__.'\Fixtures\FBooType', 'f_boo'), + ); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormType($typeClass = 'Symfony\Component\Form\AbstractType') + { + return $this->getMock($typeClass, array('getBlockPrefix', 'configureOptions', 'finishView', 'buildView', 'buildForm')); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormTypeExtension() + { + return $this->getMock('Symfony\Component\Form\AbstractTypeExtension', array('getExtendedType', 'configureOptions', 'finishView', 'buildView', 'buildForm')); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFormFactory() + { + return $this->getMock('Symfony\Component\Form\FormFactoryInterface'); + } + + /** + * @param string $name + * @param array $options + * + * @return FormBuilder + */ + protected function getBuilder($name = 'name', array $options = array()) + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory, $options); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/SimpleFormTest.php new file mode 100644 index 0000000..36edea5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -0,0 +1,1049 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests; + +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; +use Symfony\Component\Form\Tests\Fixtures\FixedFilterListener; + +class SimpleFormTest_Countable implements \Countable +{ + private $count; + + public function __construct($count) + { + $this->count = $count; + } + + public function count() + { + return $this->count; + } +} + +class SimpleFormTest_Traversable implements \IteratorAggregate +{ + private $iterator; + + public function __construct($count) + { + $this->iterator = new \ArrayIterator($count > 0 ? array_fill(0, $count, 'Foo') : array()); + } + + public function getIterator() + { + return $this->iterator; + } +} + +class SimpleFormTest extends AbstractFormTest +{ + public function testDataIsInitializedToConfiguredValue() + { + $model = new FixedDataTransformer(array( + 'default' => 'foo', + )); + $view = new FixedDataTransformer(array( + 'foo' => 'bar', + )); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer($view); + $config->addModelTransformer($model); + $config->setData('default'); + $form = new Form($config); + + $this->assertSame('default', $form->getData()); + $this->assertSame('foo', $form->getNormData()); + $this->assertSame('bar', $form->getViewData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage Unable to transform value for property path "name": No mapping for value "arg" + */ + public function testDataTransformationFailure() + { + $model = new FixedDataTransformer(array( + 'default' => 'foo', + )); + $view = new FixedDataTransformer(array( + 'foo' => 'bar', + )); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer($view); + $config->addModelTransformer($model); + $config->setData('arg'); + $form = new Form($config); + + $form->getData(); + } + + // https://github.com/symfony/symfony/commit/d4f4038f6daf7cf88ca7c7ab089473cce5ebf7d8#commitcomment-1632879 + public function testDataIsInitializedFromSubmit() + { + $mock = $this->getMockBuilder('\stdClass') + ->setMethods(array('preSetData', 'preSubmit')) + ->getMock(); + $mock->expects($this->at(0)) + ->method('preSetData'); + $mock->expects($this->at(1)) + ->method('preSubmit'); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, array($mock, 'preSetData')); + $config->addEventListener(FormEvents::PRE_SUBMIT, array($mock, 'preSubmit')); + $form = new Form($config); + + // no call to setData() or similar where the object would be + // initialized otherwise + + $form->submit('foobar'); + } + + // https://github.com/symfony/symfony/pull/7789 + public function testFalseIsConvertedToNull() + { + $mock = $this->getMockBuilder('\stdClass') + ->setMethods(array('preSubmit')) + ->getMock(); + $mock->expects($this->once()) + ->method('preSubmit') + ->with($this->callback(function ($event) { + return null === $event->getData(); + })); + + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SUBMIT, array($mock, 'preSubmit')); + $form = new Form($config); + + $form->submit(false); + + $this->assertTrue($form->isValid()); + $this->assertNull($form->getData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testSubmitThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->submit(array()); + } + + public function testSubmitIsIgnoredIfDisabled() + { + $form = $this->getBuilder() + ->setDisabled(true) + ->setData('initial') + ->getForm(); + + $form->submit('new'); + + $this->assertEquals('initial', $form->getData()); + $this->assertTrue($form->isSubmitted()); + } + + public function testNeverRequiredIfParentNotRequired() + { + $parent = $this->getBuilder()->setRequired(false)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $this->assertFalse($child->isRequired()); + } + + public function testRequired() + { + $parent = $this->getBuilder()->setRequired(true)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $this->assertTrue($child->isRequired()); + } + + public function testNotRequired() + { + $parent = $this->getBuilder()->setRequired(true)->getForm(); + $child = $this->getBuilder()->setRequired(false)->getForm(); + + $child->setParent($parent); + + $this->assertFalse($child->isRequired()); + } + + /** + * @dataProvider getDisabledStates + */ + public function testAlwaysDisabledIfParentDisabled($parentDisabled, $disabled, $result) + { + $parent = $this->getBuilder()->setDisabled($parentDisabled)->getForm(); + $child = $this->getBuilder()->setDisabled($disabled)->getForm(); + + $child->setParent($parent); + + $this->assertSame($result, $child->isDisabled()); + } + + public function getDisabledStates() + { + return array( + // parent, button, result + array(true, true, true), + array(true, false, true), + array(false, true, true), + array(false, false, false), + ); + } + + public function testGetRootReturnsRootOfParent() + { + $parent = $this->getMockForm(); + $parent->expects($this->once()) + ->method('getRoot') + ->will($this->returnValue('ROOT')); + + $this->form->setParent($parent); + + $this->assertEquals('ROOT', $this->form->getRoot()); + } + + public function testGetRootReturnsSelfIfNoParent() + { + $this->assertSame($this->form, $this->form->getRoot()); + } + + public function testEmptyIfEmptyArray() + { + $this->form->setData(array()); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testEmptyIfEmptyCountable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Countable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Countable(0)); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testNotEmptyIfFilledCountable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Countable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Countable(1)); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testEmptyIfEmptyTraversable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Traversable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Traversable(0)); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testNotEmptyIfFilledTraversable() + { + $this->form = new Form(new FormConfigBuilder('name', __NAMESPACE__.'\SimpleFormTest_Traversable', $this->dispatcher)); + + $this->form->setData(new SimpleFormTest_Traversable(1)); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testEmptyIfNull() + { + $this->form->setData(null); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testEmptyIfEmptyString() + { + $this->form->setData(''); + + $this->assertTrue($this->form->isEmpty()); + } + + public function testNotEmptyIfText() + { + $this->form->setData('foobar'); + + $this->assertFalse($this->form->isEmpty()); + } + + public function testValidIfSubmitted() + { + $form = $this->getBuilder()->getForm(); + $form->submit('foobar'); + + $this->assertTrue($form->isValid()); + } + + public function testValidIfSubmittedAndDisabled() + { + $form = $this->getBuilder()->setDisabled(true)->getForm(); + $form->submit('foobar'); + + $this->assertTrue($form->isValid()); + } + + public function testNotValidIfNotSubmitted() + { + $this->assertFalse($this->form->isValid()); + } + + public function testNotValidIfErrors() + { + $form = $this->getBuilder()->getForm(); + $form->submit('foobar'); + $form->addError(new FormError('Error!')); + + $this->assertFalse($form->isValid()); + } + + public function testHasErrors() + { + $this->form->addError(new FormError('Error!')); + + $this->assertCount(1, $this->form->getErrors()); + } + + public function testHasNoErrors() + { + $this->assertCount(0, $this->form->getErrors()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testSetParentThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->setParent($this->getBuilder('parent')->getForm()); + } + + public function testSubmitted() + { + $form = $this->getBuilder()->getForm(); + $form->submit('foobar'); + + $this->assertTrue($form->isSubmitted()); + } + + public function testNotSubmitted() + { + $this->assertFalse($this->form->isSubmitted()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\AlreadySubmittedException + */ + public function testSetDataThrowsExceptionIfAlreadySubmitted() + { + $this->form->submit(array()); + $this->form->setData(null); + } + + public function testSetDataClonesObjectIfNotByReference() + { + $data = new \stdClass(); + $form = $this->getBuilder('name', null, '\stdClass')->setByReference(false)->getForm(); + $form->setData($data); + + $this->assertNotSame($data, $form->getData()); + $this->assertEquals($data, $form->getData()); + } + + public function testSetDataDoesNotCloneObjectIfByReference() + { + $data = new \stdClass(); + $form = $this->getBuilder('name', null, '\stdClass')->setByReference(true)->getForm(); + $form->setData($data); + + $this->assertSame($data, $form->getData()); + } + + public function testSetDataExecutesTransformationChain() + { + // use real event dispatcher now + $form = $this->getBuilder('name', new EventDispatcher()) + ->addEventSubscriber(new FixedFilterListener(array( + 'preSetData' => array( + 'app' => 'filtered', + ), + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'filtered' => 'norm', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'norm' => 'client', + ))) + ->getForm(); + + $form->setData('app'); + + $this->assertEquals('filtered', $form->getData()); + $this->assertEquals('norm', $form->getNormData()); + $this->assertEquals('client', $form->getViewData()); + } + + public function testSetDataExecutesViewTransformersInOrder() + { + $form = $this->getBuilder() + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'first' => 'second', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'third', + ))) + ->getForm(); + + $form->setData('first'); + + $this->assertEquals('third', $form->getViewData()); + } + + public function testSetDataExecutesModelTransformersInReverseOrder() + { + $form = $this->getBuilder() + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'third', + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'first' => 'second', + ))) + ->getForm(); + + $form->setData('first'); + + $this->assertEquals('third', $form->getNormData()); + } + + /* + * When there is no data transformer, the data must have the same format + * in all three representations + */ + public function testSetDataConvertsScalarToStringIfNoTransformer() + { + $form = $this->getBuilder()->getForm(); + + $form->setData(1); + + $this->assertSame('1', $form->getData()); + $this->assertSame('1', $form->getNormData()); + $this->assertSame('1', $form->getViewData()); + } + + /* + * Data in client format should, if possible, always be a string to + * facilitate differentiation between '0' and '' + */ + public function testSetDataConvertsScalarToStringIfOnlyModelTransformer() + { + $form = $this->getBuilder() + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 1 => 23, + ))) + ->getForm(); + + $form->setData(1); + + $this->assertSame(1, $form->getData()); + $this->assertSame(23, $form->getNormData()); + $this->assertSame('23', $form->getViewData()); + } + + /* + * NULL remains NULL in app and norm format to remove the need to treat + * empty values and NULL explicitly in the application + */ + public function testSetDataConvertsNullToStringIfNoTransformer() + { + $form = $this->getBuilder()->getForm(); + + $form->setData(null); + + $this->assertNull($form->getData()); + $this->assertNull($form->getNormData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSetDataIsIgnoredIfDataIsLocked() + { + $form = $this->getBuilder() + ->setData('default') + ->setDataLocked(true) + ->getForm(); + + $form->setData('foobar'); + + $this->assertSame('default', $form->getData()); + } + + public function testSubmitConvertsEmptyToNullIfNoTransformer() + { + $form = $this->getBuilder()->getForm(); + + $form->submit(''); + + $this->assertNull($form->getData()); + $this->assertNull($form->getNormData()); + $this->assertSame('', $form->getViewData()); + } + + public function testSubmitExecutesTransformationChain() + { + // use real event dispatcher now + $form = $this->getBuilder('name', new EventDispatcher()) + ->addEventSubscriber(new FixedFilterListener(array( + 'preSubmit' => array( + 'client' => 'filteredclient', + ), + 'onSubmit' => array( + 'norm' => 'filterednorm', + ), + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'norm' => 'filteredclient', + 'filterednorm' => 'cleanedclient', + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'app' => 'filterednorm', + ))) + ->getForm(); + + $form->submit('client'); + + $this->assertEquals('app', $form->getData()); + $this->assertEquals('filterednorm', $form->getNormData()); + $this->assertEquals('cleanedclient', $form->getViewData()); + } + + public function testSubmitExecutesViewTransformersInReverseOrder() + { + $form = $this->getBuilder() + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'third' => 'second', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'first', + ))) + ->getForm(); + + $form->submit('first'); + + $this->assertEquals('third', $form->getNormData()); + } + + public function testSubmitExecutesModelTransformersInOrder() + { + $form = $this->getBuilder() + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'second' => 'first', + ))) + ->addModelTransformer(new FixedDataTransformer(array( + '' => '', + 'third' => 'second', + ))) + ->getForm(); + + $form->submit('first'); + + $this->assertEquals('third', $form->getData()); + } + + public function testSynchronizedByDefault() + { + $this->assertTrue($this->form->isSynchronized()); + } + + public function testSynchronizedAfterSubmission() + { + $this->form->submit('foobar'); + + $this->assertTrue($this->form->isSynchronized()); + } + + public function testNotSynchronizedIfViewReverseTransformationFailed() + { + $transformer = $this->getDataTransformer(); + $transformer->expects($this->once()) + ->method('reverseTransform') + ->will($this->throwException(new TransformationFailedException())); + + $form = $this->getBuilder() + ->addViewTransformer($transformer) + ->getForm(); + + $form->submit('foobar'); + + $this->assertFalse($form->isSynchronized()); + } + + public function testNotSynchronizedIfModelReverseTransformationFailed() + { + $transformer = $this->getDataTransformer(); + $transformer->expects($this->once()) + ->method('reverseTransform') + ->will($this->throwException(new TransformationFailedException())); + + $form = $this->getBuilder() + ->addModelTransformer($transformer) + ->getForm(); + + $form->submit('foobar'); + + $this->assertFalse($form->isSynchronized()); + } + + public function testEmptyDataCreatedBeforeTransforming() + { + $form = $this->getBuilder() + ->setEmptyData('foo') + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'bar' => 'foo', + ))) + ->getForm(); + + $form->submit(''); + + $this->assertEquals('bar', $form->getData()); + } + + public function testEmptyDataFromClosure() + { + $form = $this->getBuilder() + ->setEmptyData(function ($form) { + // the form instance is passed to the closure to allow use + // of form data when creating the empty value + $this->assertInstanceOf('Symfony\Component\Form\FormInterface', $form); + + return 'foo'; + }) + ->addViewTransformer(new FixedDataTransformer(array( + '' => '', + // direction is reversed! + 'bar' => 'foo', + ))) + ->getForm(); + + $form->submit(''); + + $this->assertEquals('bar', $form->getData()); + } + + public function testSubmitResetsErrors() + { + $this->form->addError(new FormError('Error!')); + $this->form->submit('foobar'); + + $this->assertCount(0, $this->form->getErrors()); + } + + public function testCreateView() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + $form = $this->getBuilder()->setType($type)->getForm(); + + $type->expects($this->once()) + ->method('createView') + ->with($form) + ->will($this->returnValue($view)); + + $this->assertSame($view, $form->createView()); + } + + public function testCreateViewWithParent() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + $parentForm = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $parentView = $this->getMock('Symfony\Component\Form\FormView'); + $form = $this->getBuilder()->setType($type)->getForm(); + $form->setParent($parentForm); + + $parentForm->expects($this->once()) + ->method('createView') + ->will($this->returnValue($parentView)); + + $type->expects($this->once()) + ->method('createView') + ->with($form, $parentView) + ->will($this->returnValue($view)); + + $this->assertSame($view, $form->createView()); + } + + public function testCreateViewWithExplicitParent() + { + $type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface'); + $view = $this->getMock('Symfony\Component\Form\FormView'); + $parentView = $this->getMock('Symfony\Component\Form\FormView'); + $form = $this->getBuilder()->setType($type)->getForm(); + + $type->expects($this->once()) + ->method('createView') + ->with($form, $parentView) + ->will($this->returnValue($view)); + + $this->assertSame($view, $form->createView($parentView)); + } + + public function testFormCanHaveEmptyName() + { + $form = $this->getBuilder('')->getForm(); + + $this->assertEquals('', $form->getName()); + } + + public function testSetNullParentWorksWithEmptyName() + { + $form = $this->getBuilder('')->getForm(); + $form->setParent(null); + + $this->assertNull($form->getParent()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\LogicException + * @expectedExceptionMessage A form with an empty name cannot have a parent form. + */ + public function testFormCannotHaveEmptyNameNotInRootLevel() + { + $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($this->getBuilder('')) + ->getForm(); + } + + public function testGetPropertyPathReturnsConfiguredPath() + { + $form = $this->getBuilder()->setPropertyPath('address.street')->getForm(); + + $this->assertEquals(new PropertyPath('address.street'), $form->getPropertyPath()); + } + + // see https://github.com/symfony/symfony/issues/3903 + public function testGetPropertyPathDefaultsToNameIfParentHasDataClass() + { + $parent = $this->getBuilder(null, null, 'stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $parent->add($form); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + } + + // see https://github.com/symfony/symfony/issues/3903 + public function testGetPropertyPathDefaultsToIndexedNameIfParentDataClassIsNull() + { + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $parent->add($form); + + $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath()); + } + + public function testGetPropertyPathDefaultsToNameIfFirstParentWithoutInheritDataHasDataClass() + { + $grandParent = $this->getBuilder(null, null, 'stdClass') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setInheritData(true) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $grandParent->add($parent); + $parent->add($form); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + } + + public function testGetPropertyPathDefaultsToIndexedNameIfDataClassOfFirstParentWithoutInheritDataIsNull() + { + $grandParent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $parent = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setInheritData(true) + ->getForm(); + $form = $this->getBuilder('name')->getForm(); + $grandParent->add($parent); + $parent->add($form); + + $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath()); + } + + public function testViewDataMayBeObjectIfDataClassIsNull() + { + $object = new \stdClass(); + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => $object, + ))); + $form = new Form($config); + + $form->setData('foo'); + + $this->assertSame($object, $form->getViewData()); + } + + public function testViewDataMayBeArrayAccessIfDataClassIsNull() + { + $arrayAccess = $this->getMock('\ArrayAccess'); + $config = new FormConfigBuilder('name', null, $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => $arrayAccess, + ))); + $form = new Form($config); + + $form->setData('foo'); + + $this->assertSame($arrayAccess, $form->getViewData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\LogicException + */ + public function testViewDataMustBeObjectIfDataClassIsSet() + { + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addViewTransformer(new FixedDataTransformer(array( + '' => '', + 'foo' => array('bar' => 'baz'), + ))); + $form = new Form($config); + + $form->setData('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testSetDataCannotInvokeItself() + { + // Cycle detection to prevent endless loops + $config = new FormConfigBuilder('name', 'stdClass', $this->dispatcher); + $config->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $event->getForm()->setData('bar'); + }); + $form = new Form($config); + + $form->setData('foo'); + } + + public function testSubmittingWrongDataIsIgnored() + { + $child = $this->getBuilder('child', $this->dispatcher); + $child->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { + // child form doesn't receive the wrong data that is submitted on parent + $this->assertNull($event->getData()); + }); + + $parent = $this->getBuilder('parent', new EventDispatcher()) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->add($child) + ->getForm(); + + $parent->submit('not-an-array'); + } + + public function testHandleRequestForwardsToRequestHandler() + { + $handler = $this->getMock('Symfony\Component\Form\RequestHandlerInterface'); + + $form = $this->getBuilder() + ->setRequestHandler($handler) + ->getForm(); + + $handler->expects($this->once()) + ->method('handleRequest') + ->with($this->identicalTo($form), 'REQUEST'); + + $this->assertSame($form, $form->handleRequest('REQUEST')); + } + + public function testFormInheritsParentData() + { + $child = $this->getBuilder('child') + ->setInheritData(true); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->setData('foo') + ->addModelTransformer(new FixedDataTransformer(array( + 'foo' => 'norm[foo]', + ))) + ->addViewTransformer(new FixedDataTransformer(array( + 'norm[foo]' => 'view[foo]', + ))) + ->add($child) + ->getForm(); + + $this->assertSame('foo', $parent->get('child')->getData()); + $this->assertSame('norm[foo]', $parent->get('child')->getNormData()); + $this->assertSame('view[foo]', $parent->get('child')->getViewData()); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testInheritDataDisallowsSetData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->setData('foo'); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testGetDataRequiresParentToBeSetIfInheritData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->getData(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testGetNormDataRequiresParentToBeSetIfInheritData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->getNormData(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testGetViewDataRequiresParentToBeSetIfInheritData() + { + $form = $this->getBuilder() + ->setInheritData(true) + ->getForm(); + + $form->getViewData(); + } + + public function testPostSubmitDataIsNullIfInheritData() + { + $form = $this->getBuilder() + ->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) { + $this->assertNull($event->getData()); + }) + ->setInheritData(true) + ->getForm(); + + $form->submit('foo'); + } + + public function testSubmitIsNeverFiredIfInheritData() + { + $form = $this->getBuilder() + ->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) { + $this->fail('The SUBMIT event should not be fired'); + }) + ->setInheritData(true) + ->getForm(); + + $form->submit('foo'); + } + + public function testInitializeSetsDefaultData() + { + $config = $this->getBuilder()->setData('DEFAULT')->getFormConfig(); + $form = $this->getMock('Symfony\Component\Form\Form', array('setData'), array($config)); + + $form->expects($this->once()) + ->method('setData') + ->with($this->identicalTo('DEFAULT')); + + /* @var Form $form */ + $form->initialize(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testInitializeFailsIfParent() + { + $parent = $this->getBuilder()->setRequired(false)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $child->initialize(); + } + + protected function createForm() + { + return $this->getBuilder()->getForm(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php new file mode 100644 index 0000000..7adff69 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Util/OrderedHashMapTest.php @@ -0,0 +1,487 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Util; + +use Symfony\Component\Form\Util\OrderedHashMap; + +/** + * @author Bernhard Schussek + */ +class OrderedHashMapTest extends \PHPUnit_Framework_TestCase +{ + public function testGet() + { + $map = new OrderedHashMap(); + $map['first'] = 1; + + $this->assertSame(1, $map['first']); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetNonExistingFails() + { + $map = new OrderedHashMap(); + + $map['first']; + } + + public function testInsertStringKeys() + { + $map = new OrderedHashMap(); + $map['first'] = 1; + $map['second'] = 2; + + $this->assertSame(array('first' => 1, 'second' => 2), iterator_to_array($map)); + } + + public function testInsertNullKeys() + { + $map = new OrderedHashMap(); + $map[] = 1; + $map['foo'] = 2; + $map[] = 3; + + $this->assertSame(array(0 => 1, 'foo' => 2, 1 => 3), iterator_to_array($map)); + } + + /** + * Updates should not change the position of an element, otherwise we could + * turn foreach loops into endless loops if they change the current + * element. + * + * foreach ($map as $index => $value) { + * $map[$index] = $value + 1; + * } + * + * And we don't want this, right? :) + */ + public function testUpdateDoesNotChangeElementPosition() + { + $map = new OrderedHashMap(); + $map['first'] = 1; + $map['second'] = 2; + $map['first'] = 1; + + $this->assertSame(array('first' => 1, 'second' => 2), iterator_to_array($map)); + } + + public function testIsset() + { + $map = new OrderedHashMap(); + $map['first'] = 1; + + $this->assertTrue(isset($map['first'])); + } + + public function testIssetReturnsFalseForNonExisting() + { + $map = new OrderedHashMap(); + + $this->assertFalse(isset($map['first'])); + } + + public function testIssetReturnsFalseForNull() + { + $map = new OrderedHashMap(); + $map['first'] = null; + + $this->assertFalse(isset($map['first'])); + } + + public function testUnset() + { + $map = new OrderedHashMap(); + $map['first'] = 1; + $map['second'] = 2; + + unset($map['first']); + + $this->assertSame(array('second' => 2), iterator_to_array($map)); + } + + public function testUnsetNonExistingSucceeds() + { + $map = new OrderedHashMap(); + + unset($map['first']); + } + + public function testEmptyIteration() + { + $map = new OrderedHashMap(); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationSupportsInsertion() + { + $map = new OrderedHashMap(array('first' => 1)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('first', $it->key()); + $this->assertSame(1, $it->current()); + + // dynamic modification + $map['added'] = 2; + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('first', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('added', $it->key()); + $this->assertSame(2, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationSupportsDeletionAndInsertion() + { + $map = new OrderedHashMap(array('first' => 1, 'removed' => 2)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('first', $it->key()); + $this->assertSame(1, $it->current()); + + // dynamic modification + unset($map['removed']); + $map['added'] = 3; + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('first', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('added', $it->key()); + $this->assertSame(3, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationSupportsDeletionOfCurrentElement() + { + $map = new OrderedHashMap(array('removed' => 1, 'next' => 2)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('removed', $it->key()); + $this->assertSame(1, $it->current()); + + unset($map['removed']); + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('removed', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(2, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationIgnoresReplacementOfCurrentElement() + { + $map = new OrderedHashMap(array('replaced' => 1, 'next' => 2)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('replaced', $it->key()); + $this->assertSame(1, $it->current()); + + $map['replaced'] = 3; + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('replaced', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(2, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationSupportsDeletionOfCurrentAndLastElement() + { + $map = new OrderedHashMap(array('removed' => 1)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('removed', $it->key()); + $this->assertSame(1, $it->current()); + + unset($map['removed']); + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('removed', $it->key()); + $this->assertSame(1, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationIgnoresReplacementOfCurrentAndLastElement() + { + $map = new OrderedHashMap(array('replaced' => 1)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('replaced', $it->key()); + $this->assertSame(1, $it->current()); + + $map['replaced'] = 2; + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('replaced', $it->key()); + $this->assertSame(1, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationSupportsDeletionOfPreviousElement() + { + $map = new OrderedHashMap(array('removed' => 1, 'next' => 2, 'onemore' => 3)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('removed', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(2, $it->current()); + + unset($map['removed']); + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(2, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('onemore', $it->key()); + $this->assertSame(3, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationIgnoresReplacementOfPreviousElement() + { + $map = new OrderedHashMap(array('replaced' => 1, 'next' => 2, 'onemore' => 3)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('replaced', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(2, $it->current()); + + $map['replaced'] = 4; + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(2, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('onemore', $it->key()); + $this->assertSame(3, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testIterationSupportsDeletionOfMultiplePreviousElements() + { + $map = new OrderedHashMap(array('removed' => 1, 'alsoremoved' => 2, 'next' => 3, 'onemore' => 4)); + $it = $map->getIterator(); + + $it->rewind(); + $this->assertTrue($it->valid()); + $this->assertSame('removed', $it->key()); + $this->assertSame(1, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('alsoremoved', $it->key()); + $this->assertSame(2, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(3, $it->current()); + + unset($map['removed'], $map['alsoremoved']); + + // iterator is unchanged + $this->assertTrue($it->valid()); + $this->assertSame('next', $it->key()); + $this->assertSame(3, $it->current()); + + // continue iteration + $it->next(); + $this->assertTrue($it->valid()); + $this->assertSame('onemore', $it->key()); + $this->assertSame(4, $it->current()); + + // end of map + $it->next(); + $this->assertFalse($it->valid()); + $this->assertNull($it->key()); + $this->assertNull($it->current()); + } + + public function testParallelIteration() + { + $map = new OrderedHashMap(array('first' => 1, 'second' => 2)); + $it1 = $map->getIterator(); + $it2 = $map->getIterator(); + + $it1->rewind(); + $this->assertTrue($it1->valid()); + $this->assertSame('first', $it1->key()); + $this->assertSame(1, $it1->current()); + + $it2->rewind(); + $this->assertTrue($it2->valid()); + $this->assertSame('first', $it2->key()); + $this->assertSame(1, $it2->current()); + + // 1: continue iteration + $it1->next(); + $this->assertTrue($it1->valid()); + $this->assertSame('second', $it1->key()); + $this->assertSame(2, $it1->current()); + + // 2: remains unchanged + $this->assertTrue($it2->valid()); + $this->assertSame('first', $it2->key()); + $this->assertSame(1, $it2->current()); + + // 1: advance to end of map + $it1->next(); + $this->assertFalse($it1->valid()); + $this->assertNull($it1->key()); + $this->assertNull($it1->current()); + + // 2: remains unchanged + $this->assertTrue($it2->valid()); + $this->assertSame('first', $it2->key()); + $this->assertSame(1, $it2->current()); + + // 2: continue iteration + $it2->next(); + $this->assertTrue($it2->valid()); + $this->assertSame('second', $it2->key()); + $this->assertSame(2, $it2->current()); + + // 1: remains unchanged + $this->assertFalse($it1->valid()); + $this->assertNull($it1->key()); + $this->assertNull($it1->current()); + + // 2: advance to end of map + $it2->next(); + $this->assertFalse($it2->valid()); + $this->assertNull($it2->key()); + $this->assertNull($it2->current()); + + // 1: remains unchanged + $this->assertFalse($it1->valid()); + $this->assertNull($it1->key()); + $this->assertNull($it1->current()); + } + + public function testCount() + { + $map = new OrderedHashMap(); + $map[] = 1; + $map['foo'] = 2; + unset($map[0]); + $map[] = 3; + + $this->assertSame(2, count($map)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Util/StringUtilTest.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Util/StringUtilTest.php new file mode 100644 index 0000000..fbdf84c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Tests/Util/StringUtilTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Util; + +use Symfony\Component\Form\Util\StringUtil; + +class StringUtilTest extends \PHPUnit_Framework_TestCase +{ + public function testTrim() + { + $data = ' Foo! '; + + $this->assertEquals('Foo!', StringUtil::trim($data)); + } + + /** + * @dataProvider spaceProvider + */ + public function testTrimUtf8Separators($hex) + { + // Convert hexadecimal representation into binary + // H: hex string, high nibble first (UCS-2BE) + // *: repeat until end of string + $binary = pack('H*', $hex); + + // Convert UCS-2BE to UTF-8 + $symbol = mb_convert_encoding($binary, 'UTF-8', 'UCS-2BE'); + $symbol = $symbol."ab\ncd".$symbol; + + $this->assertSame("ab\ncd", StringUtil::trim($symbol)); + } + + public function spaceProvider() + { + return array( + // separators + array('0020'), + array('00A0'), + array('1680'), +// array('180E'), + array('2000'), + array('2001'), + array('2002'), + array('2003'), + array('2004'), + array('2005'), + array('2006'), + array('2007'), + array('2008'), + array('2009'), + array('200A'), + array('2028'), + array('2029'), + array('202F'), + array('205F'), + array('3000'), + // controls + array('0009'), + array('000A'), + array('000B'), + array('000C'), + array('000D'), + array('0085'), + // zero width space +// array('200B'), + ); + } + + /** + * @dataProvider fqcnToBlockPrefixProvider + */ + public function testFqcnToBlockPrefix($fqcn, $expectedBlockPrefix) + { + $blockPrefix = StringUtil::fqcnToBlockPrefix($fqcn); + + $this->assertSame($expectedBlockPrefix, $blockPrefix); + } + + public function fqcnToBlockPrefixProvider() + { + return array( + array('TYPE', 'type'), + array('\Type', 'type'), + array('\UserType', 'user'), + array('UserType', 'user'), + array('Vendor\Name\Space\Type', 'type'), + array('Vendor\Name\Space\UserForm', 'user_form'), + array('Vendor\Name\Space\UserType', 'user'), + array('Vendor\Name\Space\usertype', 'user'), + array('Symfony\Component\Form\Form', 'form'), + array('Vendor\Name\Space\BarTypeBazType', 'bar_type_baz'), + array('FooBarBazType', 'foo_bar_baz'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/FormUtil.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/FormUtil.php new file mode 100644 index 0000000..0862179 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/FormUtil.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * @author Bernhard Schussek + */ +class FormUtil +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Returns whether the given data is empty. + * + * This logic is reused multiple times throughout the processing of + * a form and needs to be consistent. PHP's keyword `empty` cannot + * be used as it also considers 0 and "0" to be empty. + * + * @param mixed $data + * + * @return bool + */ + public static function isEmpty($data) + { + // Should not do a check for array() === $data!!! + // This method is used in occurrences where arrays are + // not considered to be empty, ever. + return null === $data || '' === $data; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php new file mode 100644 index 0000000..a320344 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * Iterator that traverses an array of forms. + * + * Contrary to \ArrayIterator, this iterator recognizes changes in the original + * array during iteration. + * + * You can wrap the iterator into a {@link \RecursiveIterator} in order to + * enter any child form that inherits its parent's data and iterate the children + * of that form as well. + * + * @author Bernhard Schussek + */ +class InheritDataAwareIterator extends \IteratorIterator implements \RecursiveIterator +{ + /** + * {@inheritdoc} + */ + public function getChildren() + { + return new static($this->current()); + } + + /** + * {@inheritdoc} + */ + public function hasChildren() + { + return (bool) $this->current()->getConfig()->getInheritData(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMap.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMap.php new file mode 100644 index 0000000..2ba53e2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMap.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * A hash map which keeps track of deletions and additions. + * + * Like in associative arrays, elements can be mapped to integer or string keys. + * Unlike associative arrays, the map keeps track of the order in which keys + * were added and removed. This order is reflected during iteration. + * + * The map supports concurrent modification during iteration. That means that + * you can insert and remove elements from within a foreach loop and the + * iterator will reflect those changes accordingly. + * + * While elements that are added during the loop are recognized by the iterator, + * changed elements are not. Otherwise the loop could be infinite if each loop + * changes the current element: + * + * $map = new OrderedHashMap(); + * $map[1] = 1; + * $map[2] = 2; + * $map[3] = 3; + * + * foreach ($map as $index => $value) { + * echo "$index: $value\n" + * if (1 === $index) { + * $map[1] = 4; + * $map[] = 5; + * } + * } + * + * print_r(iterator_to_array($map)); + * + * // => 1: 1 + * // 2: 2 + * // 3: 3 + * // 4: 5 + * // Array + * // ( + * // [1] => 4 + * // [2] => 2 + * // [3] => 3 + * // [4] => 5 + * // ) + * + * The map also supports multiple parallel iterators. That means that you can + * nest foreach loops without affecting each other's iteration: + * + * foreach ($map as $index => $value) { + * foreach ($map as $index2 => $value2) { + * // ... + * } + * } + * + * @author Bernhard Schussek + * + * @since 2.2.6 + */ +class OrderedHashMap implements \ArrayAccess, \IteratorAggregate, \Countable +{ + /** + * The elements of the map, indexed by their keys. + * + * @var array + */ + private $elements = array(); + + /** + * The keys of the map in the order in which they were inserted or changed. + * + * @var array + */ + private $orderedKeys = array(); + + /** + * References to the cursors of all open iterators. + * + * @var array + */ + private $managedCursors = array(); + + /** + * Creates a new map. + * + * @param array $elements The elements to insert initially. + * + * @since 2.2.6 + */ + public function __construct(array $elements = array()) + { + $this->elements = $elements; + $this->orderedKeys = array_keys($elements); + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function offsetExists($key) + { + return isset($this->elements[$key]); + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function offsetGet($key) + { + if (!isset($this->elements[$key])) { + throw new \OutOfBoundsException('The offset "'.$key.'" does not exist.'); + } + + return $this->elements[$key]; + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function offsetSet($key, $value) + { + if (null === $key || !isset($this->elements[$key])) { + if (null === $key) { + $key = array() === $this->orderedKeys + // If the array is empty, use 0 as key + ? 0 + // Imitate PHP's behavior of generating a key that equals + // the highest existing integer key + 1 + : max($this->orderedKeys) + 1; + } + + $this->orderedKeys[] = $key; + } + + $this->elements[$key] = $value; + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function offsetUnset($key) + { + if (false !== ($position = array_search($key, $this->orderedKeys))) { + array_splice($this->orderedKeys, $position, 1); + unset($this->elements[$key]); + + foreach ($this->managedCursors as $i => $cursor) { + if ($cursor >= $position) { + --$this->managedCursors[$i]; + } + } + } + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function getIterator() + { + return new OrderedHashMapIterator($this->elements, $this->orderedKeys, $this->managedCursors); + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function count() + { + return count($this->elements); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php new file mode 100644 index 0000000..f606626 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMapIterator.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * Iterator for {@link OrderedHashMap} objects. + * + * This class is internal and should not be used. + * + * @author Bernhard Schussek + * + * @since 2.2.6 + */ +class OrderedHashMapIterator implements \Iterator +{ + /** + * @var array + */ + private $elements; + + /** + * @var array + */ + private $orderedKeys; + + /** + * @var int + */ + private $cursor; + + /** + * @var int + */ + private $cursorId; + + /** + * @var array + */ + private $managedCursors; + + /** + * @var string|int|null + */ + private $key; + + /** + * @var mixed + */ + private $current; + + /** + * Creates a new iterator. + * + * @param array $elements The elements of the map, indexed by their + * keys. + * @param array $orderedKeys The keys of the map in the order in which + * they should be iterated. + * @param array $managedCursors An array from which to reference the + * iterator's cursor as long as it is alive. + * This array is managed by the corresponding + * {@link OrderedHashMap} instance to support + * recognizing the deletion of elements. + * + * @since 2.2.6 + */ + public function __construct(array &$elements, array &$orderedKeys, array &$managedCursors) + { + $this->elements = &$elements; + $this->orderedKeys = &$orderedKeys; + $this->managedCursors = &$managedCursors; + $this->cursorId = count($managedCursors); + + $this->managedCursors[$this->cursorId] = &$this->cursor; + } + + /** + * Removes the iterator's cursors from the managed cursors of the + * corresponding {@link OrderedHashMap} instance. + * + * @since 2.2.6 + */ + public function __destruct() + { + // Use array_splice() instead of isset() to prevent holes in the + // array indices, which would break the initialization of $cursorId + array_splice($this->managedCursors, $this->cursorId, 1); + } + + /** + *{@inheritdoc} + * + * @since 2.2.6 + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + * + * @since 2.2.6 + */ + public function next() + { + ++$this->cursor; + + if (isset($this->orderedKeys[$this->cursor])) { + $this->key = $this->orderedKeys[$this->cursor]; + $this->current = $this->elements[$this->key]; + } else { + $this->key = null; + $this->current = null; + } + } + + /** + *{@inheritdoc} + * + * @since 2.2.6 + */ + public function key() + { + return $this->key; + } + + /** + *{@inheritdoc} + * + * @since 2.2.6 + */ + public function valid() + { + return null !== $this->key; + } + + /** + *{@inheritdoc} + * + * @since 2.2.6 + */ + public function rewind() + { + $this->cursor = 0; + + if (isset($this->orderedKeys[0])) { + $this->key = $this->orderedKeys[0]; + $this->current = $this->elements[$this->key]; + } else { + $this->key = null; + $this->current = null; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/ServerParams.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/ServerParams.php new file mode 100644 index 0000000..d5367d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/ServerParams.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * @author Bernhard Schussek + */ +class ServerParams +{ + private $requestStack; + + public function __construct(RequestStack $requestStack = null) + { + $this->requestStack = $requestStack; + } + + /** + * Returns maximum post size in bytes. + * + * @return null|int The maximum post size in bytes + */ + public function getPostMaxSize() + { + $iniMax = strtolower($this->getNormalizedIniPostMaxSize()); + + if ('' === $iniMax) { + return; + } + + $max = ltrim($iniMax, '+'); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($iniMax, -1)) { + case 't': $max *= 1024; + case 'g': $max *= 1024; + case 'm': $max *= 1024; + case 'k': $max *= 1024; + } + + return $max; + } + + /** + * Returns the normalized "post_max_size" ini setting. + * + * @return string + */ + public function getNormalizedIniPostMaxSize() + { + return strtoupper(trim(ini_get('post_max_size'))); + } + + /** + * Returns the content length of the request. + * + * @return mixed The request content length. + */ + public function getContentLength() + { + if (null !== $this->requestStack && null !== $request = $this->requestStack->getCurrentRequest()) { + return $request->server->get('CONTENT_LENGTH'); + } + + return isset($_SERVER['CONTENT_LENGTH']) + ? (int) $_SERVER['CONTENT_LENGTH'] + : null; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/Util/StringUtil.php b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/StringUtil.php new file mode 100644 index 0000000..ee71ae7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/Util/StringUtil.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Util; + +/** + * @author Issei Murasawa + * @author Bernhard Schussek + */ +class StringUtil +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Returns the trimmed data. + * + * @param string $string + * + * @return string + */ + public static function trim($string) + { + if (null !== $result = @preg_replace('/^[\pZ\p{Cc}]+|[\pZ\p{Cc}]+$/u', '', $string)) { + return $result; + } + + return trim($string); + } + + /** + * Converts a fully-qualified class name to a block prefix. + * + * @param string $fqcn The fully-qualified class name + * + * @return string|null The block prefix or null if not a valid FQCN + */ + public static function fqcnToBlockPrefix($fqcn) + { + // Non-greedy ("+?") to match "type" suffix, if present + if (preg_match('~([^\\\\]+?)(type)?$~i', $fqcn, $matches)) { + return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $matches[1])); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/composer.json b/vendor/symfony/symfony/src/Symfony/Component/Form/composer.json new file mode 100644 index 0000000..bd5c024 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/composer.json @@ -0,0 +1,58 @@ +{ + "name": "symfony/form", + "type": "library", + "description": "Symfony Form Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/intl": "~2.8|~3.0", + "symfony/options-resolver": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "~2.8|~3.0" + }, + "require-dev": { + "doctrine/collections": "~1.0", + "symfony/validator": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/security-csrf": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0" + }, + "conflict": { + "symfony/doctrine-bridge": "<2.7", + "symfony/framework-bundle": "<2.7", + "symfony/twig-bridge": "<2.7" + }, + "suggest": { + "symfony/validator": "For form validation.", + "symfony/security-csrf": "For protecting forms against CSRF attacks.", + "symfony/twig-bridge": "For templating with Twig.", + "symfony/framework-bundle": "For templating with PHP." + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Form\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Form/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/Form/phpunit.xml.dist new file mode 100644 index 0000000..1c4acf4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Form/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/AcceptHeader.php new file mode 100644 index 0000000..2260787 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/AcceptHeader.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents an Accept-* header. + * + * An accept header is compound with a list of items, + * sorted by descending quality. + * + * @author Jean-François Simon + */ +class AcceptHeader +{ + /** + * @var AcceptHeaderItem[] + */ + private $items = array(); + + /** + * @var bool + */ + private $sorted = true; + + /** + * Constructor. + * + * @param AcceptHeaderItem[] $items + */ + public function __construct(array $items) + { + foreach ($items as $item) { + $this->add($item); + } + } + + /** + * Builds an AcceptHeader instance from a string. + * + * @param string $headerValue + * + * @return AcceptHeader + */ + public static function fromString($headerValue) + { + $index = 0; + + return new self(array_map(function ($itemValue) use (&$index) { + $item = AcceptHeaderItem::fromString($itemValue); + $item->setIndex($index++); + + return $item; + }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE))); + } + + /** + * Returns header value's string representation. + * + * @return string + */ + public function __toString() + { + return implode(',', $this->items); + } + + /** + * Tests if header has given value. + * + * @param string $value + * + * @return bool + */ + public function has($value) + { + return isset($this->items[$value]); + } + + /** + * Returns given value's item, if exists. + * + * @param string $value + * + * @return AcceptHeaderItem|null + */ + public function get($value) + { + return isset($this->items[$value]) ? $this->items[$value] : null; + } + + /** + * Adds an item. + * + * @param AcceptHeaderItem $item + * + * @return AcceptHeader + */ + public function add(AcceptHeaderItem $item) + { + $this->items[$item->getValue()] = $item; + $this->sorted = false; + + return $this; + } + + /** + * Returns all items. + * + * @return AcceptHeaderItem[] + */ + public function all() + { + $this->sort(); + + return $this->items; + } + + /** + * Filters items on their value using given regex. + * + * @param string $pattern + * + * @return AcceptHeader + */ + public function filter($pattern) + { + return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) { + return preg_match($pattern, $item->getValue()); + })); + } + + /** + * Returns first item. + * + * @return AcceptHeaderItem|null + */ + public function first() + { + $this->sort(); + + return !empty($this->items) ? reset($this->items) : null; + } + + /** + * Sorts items by descending quality. + */ + private function sort() + { + if (!$this->sorted) { + uasort($this->items, function ($a, $b) { + $qA = $a->getQuality(); + $qB = $b->getQuality(); + + if ($qA === $qB) { + return $a->getIndex() > $b->getIndex() ? 1 : -1; + } + + return $qA > $qB ? -1 : 1; + }); + + $this->sorted = true; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php new file mode 100644 index 0000000..21a5d15 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/AcceptHeaderItem.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents an Accept-* header item. + * + * @author Jean-François Simon + */ +class AcceptHeaderItem +{ + /** + * @var string + */ + private $value; + + /** + * @var float + */ + private $quality = 1.0; + + /** + * @var int + */ + private $index = 0; + + /** + * @var array + */ + private $attributes = array(); + + /** + * Constructor. + * + * @param string $value + * @param array $attributes + */ + public function __construct($value, array $attributes = array()) + { + $this->value = $value; + foreach ($attributes as $name => $value) { + $this->setAttribute($name, $value); + } + } + + /** + * Builds an AcceptHeaderInstance instance from a string. + * + * @param string $itemValue + * + * @return AcceptHeaderItem + */ + public static function fromString($itemValue) + { + $bits = preg_split('/\s*(?:;*("[^"]+");*|;*(\'[^\']+\');*|;+)\s*/', $itemValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $value = array_shift($bits); + $attributes = array(); + + $lastNullAttribute = null; + foreach ($bits as $bit) { + if (($start = substr($bit, 0, 1)) === ($end = substr($bit, -1)) && ($start === '"' || $start === '\'')) { + $attributes[$lastNullAttribute] = substr($bit, 1, -1); + } elseif ('=' === $end) { + $lastNullAttribute = $bit = substr($bit, 0, -1); + $attributes[$bit] = null; + } else { + $parts = explode('=', $bit); + $attributes[$parts[0]] = isset($parts[1]) && strlen($parts[1]) > 0 ? $parts[1] : ''; + } + } + + return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ($start === '"' || $start === '\'') ? substr($value, 1, -1) : $value, $attributes); + } + + /** + * Returns header value's string representation. + * + * @return string + */ + public function __toString() + { + $string = $this->value.($this->quality < 1 ? ';q='.$this->quality : ''); + if (count($this->attributes) > 0) { + $string .= ';'.implode(';', array_map(function ($name, $value) { + return sprintf(preg_match('/[,;=]/', $value) ? '%s="%s"' : '%s=%s', $name, $value); + }, array_keys($this->attributes), $this->attributes)); + } + + return $string; + } + + /** + * Set the item value. + * + * @param string $value + * + * @return AcceptHeaderItem + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Returns the item value. + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Set the item quality. + * + * @param float $quality + * + * @return AcceptHeaderItem + */ + public function setQuality($quality) + { + $this->quality = $quality; + + return $this; + } + + /** + * Returns the item quality. + * + * @return float + */ + public function getQuality() + { + return $this->quality; + } + + /** + * Set the item index. + * + * @param int $index + * + * @return AcceptHeaderItem + */ + public function setIndex($index) + { + $this->index = $index; + + return $this; + } + + /** + * Returns the item index. + * + * @return int + */ + public function getIndex() + { + return $this->index; + } + + /** + * Tests if an attribute exists. + * + * @param string $name + * + * @return bool + */ + public function hasAttribute($name) + { + return isset($this->attributes[$name]); + } + + /** + * Returns an attribute by its name. + * + * @param string $name + * @param mixed $default + * + * @return mixed + */ + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + + /** + * Returns all attributes. + * + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * Set an attribute. + * + * @param string $name + * @param string $value + * + * @return AcceptHeaderItem + */ + public function setAttribute($name, $value) + { + if ('q' === $name) { + $this->quality = (float) $value; + } else { + $this->attributes[$name] = (string) $value; + } + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ApacheRequest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ApacheRequest.php new file mode 100644 index 0000000..84803eb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ApacheRequest.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Request represents an HTTP request from an Apache server. + * + * @author Fabien Potencier + */ +class ApacheRequest extends Request +{ + /** + * {@inheritdoc} + */ + protected function prepareRequestUri() + { + return $this->server->get('REQUEST_URI'); + } + + /** + * {@inheritdoc} + */ + protected function prepareBaseUrl() + { + $baseUrl = $this->server->get('SCRIPT_NAME'); + + if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) { + // assume mod_rewrite + return rtrim(dirname($baseUrl), '/\\'); + } + + return $baseUrl; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php new file mode 100644 index 0000000..108f040 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php @@ -0,0 +1,326 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\File\Exception\FileException; + +/** + * BinaryFileResponse represents an HTTP response delivering a file. + * + * @author Niklas Fiekas + * @author stealth35 + * @author Igor Wiedler + * @author Jordan Alliot + * @author Sergey Linnik + */ +class BinaryFileResponse extends Response +{ + protected static $trustXSendfileTypeHeader = false; + + protected $file; + protected $offset; + protected $maxlen; + protected $deleteFileAfterSend = false; + + /** + * Constructor. + * + * @param \SplFileInfo|string $file The file to stream + * @param int $status The response status code + * @param array $headers An array of response headers + * @param bool $public Files are public by default + * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename + * @param bool $autoEtag Whether the ETag header should be automatically set + * @param bool $autoLastModified Whether the Last-Modified header should be automatically set + */ + public function __construct($file, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + { + parent::__construct(null, $status, $headers); + + $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified); + + if ($public) { + $this->setPublic(); + } + } + + /** + * @param \SplFileInfo|string $file The file to stream + * @param int $status The response status code + * @param array $headers An array of response headers + * @param bool $public Files are public by default + * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename + * @param bool $autoEtag Whether the ETag header should be automatically set + * @param bool $autoLastModified Whether the Last-Modified header should be automatically set + * + * @return BinaryFileResponse The created response + */ + public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + { + return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified); + } + + /** + * Sets the file to stream. + * + * @param \SplFileInfo|string $file The file to stream + * @param string $contentDisposition + * @param bool $autoEtag + * @param bool $autoLastModified + * + * @return BinaryFileResponse + * + * @throws FileException + */ + public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + { + if (!$file instanceof File) { + if ($file instanceof \SplFileInfo) { + $file = new File($file->getPathname()); + } else { + $file = new File((string) $file); + } + } + + if (!$file->isReadable()) { + throw new FileException('File must be readable.'); + } + + $this->file = $file; + + if ($autoEtag) { + $this->setAutoEtag(); + } + + if ($autoLastModified) { + $this->setAutoLastModified(); + } + + if ($contentDisposition) { + $this->setContentDisposition($contentDisposition); + } + + return $this; + } + + /** + * Gets the file. + * + * @return File The file to stream + */ + public function getFile() + { + return $this->file; + } + + /** + * Automatically sets the Last-Modified header according the file modification date. + */ + public function setAutoLastModified() + { + $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime())); + + return $this; + } + + /** + * Automatically sets the ETag header according to the checksum of the file. + */ + public function setAutoEtag() + { + $this->setEtag(sha1_file($this->file->getPathname())); + + return $this; + } + + /** + * Sets the Content-Disposition header with the given filename. + * + * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT + * @param string $filename Optionally use this filename instead of the real name of the file + * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename + * + * @return BinaryFileResponse + */ + public function setContentDisposition($disposition, $filename = '', $filenameFallback = '') + { + if ($filename === '') { + $filename = $this->file->getFilename(); + } + + $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback); + $this->headers->set('Content-Disposition', $dispositionHeader); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function prepare(Request $request) + { + $this->headers->set('Content-Length', $this->file->getSize()); + + if (!$this->headers->has('Accept-Ranges')) { + // Only accept ranges on safe HTTP methods + $this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none'); + } + + if (!$this->headers->has('Content-Type')) { + $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream'); + } + + if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + + $this->ensureIEOverSSLCompatibility($request); + + $this->offset = 0; + $this->maxlen = -1; + + if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) { + // Use X-Sendfile, do not send any content. + $type = $request->headers->get('X-Sendfile-Type'); + $path = $this->file->getRealPath(); + // Fall back to scheme://path for stream wrapped locations. + if (false === $path) { + $path = $this->file->getPathname(); + } + if (strtolower($type) == 'x-accel-redirect') { + // Do X-Accel-Mapping substitutions. + // @link http://wiki.nginx.org/X-accel#X-Accel-Redirect + foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) { + $mapping = explode('=', $mapping, 2); + + if (2 == count($mapping)) { + $pathPrefix = trim($mapping[0]); + $location = trim($mapping[1]); + + if (substr($path, 0, strlen($pathPrefix)) == $pathPrefix) { + $path = $location.substr($path, strlen($pathPrefix)); + break; + } + } + } + } + $this->headers->set($type, $path); + $this->maxlen = 0; + } elseif ($request->headers->has('Range')) { + // Process the range headers. + if (!$request->headers->has('If-Range') || $this->getEtag() == $request->headers->get('If-Range')) { + $range = $request->headers->get('Range'); + $fileSize = $this->file->getSize(); + + list($start, $end) = explode('-', substr($range, 6), 2) + array(0); + + $end = ('' === $end) ? $fileSize - 1 : (int) $end; + + if ('' === $start) { + $start = $fileSize - $end; + $end = $fileSize - 1; + } else { + $start = (int) $start; + } + + if ($start <= $end) { + if ($start < 0 || $end > $fileSize - 1) { + $this->setStatusCode(416); + } elseif ($start !== 0 || $end !== $fileSize - 1) { + $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1; + $this->offset = $start; + + $this->setStatusCode(206); + $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize)); + $this->headers->set('Content-Length', $end - $start + 1); + } + } + } + } + + return $this; + } + + /** + * Sends the file. + */ + public function sendContent() + { + if (!$this->isSuccessful()) { + parent::sendContent(); + + return; + } + + if (0 === $this->maxlen) { + return; + } + + $out = fopen('php://output', 'wb'); + $file = fopen($this->file->getPathname(), 'rb'); + + stream_copy_to_stream($file, $out, $this->maxlen, $this->offset); + + fclose($out); + fclose($file); + + if ($this->deleteFileAfterSend) { + unlink($this->file->getPathname()); + } + } + + /** + * {@inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.'); + } + } + + /** + * {@inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } + + /** + * Trust X-Sendfile-Type header. + */ + public static function trustXSendfileTypeHeader() + { + self::$trustXSendfileTypeHeader = true; + } + + /** + * If this is set to true, the file will be unlinked after the request is send + * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used. + * + * @param bool $shouldDelete + * + * @return BinaryFileResponse + */ + public function deleteFileAfterSend($shouldDelete) + { + $this->deleteFileAfterSend = $shouldDelete; + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/CHANGELOG.md new file mode 100644 index 0000000..ca98c04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -0,0 +1,133 @@ +CHANGELOG +========= + +3.0.0 +----- + + * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY" + +2.8.0 +----- + + * Finding deep items in `ParameterBag::get()` is deprecated since version 2.8 and + will be removed in 3.0. + +2.6.0 +----- + + * PdoSessionHandler changes + - implemented different session locking strategies to prevent loss of data by concurrent access to the same session + - [BC BREAK] save session data in a binary column without base64_encode + - [BC BREAK] added lifetime column to the session table which allows to have different lifetimes for each session + - implemented lazy connections that are only opened when a session is used by either passing a dsn string + explicitly or falling back to session.save_path ini setting + - added a createTable method that initializes a correctly defined table depending on the database vendor + +2.5.0 +----- + + * added `JsonResponse::setEncodingOptions()` & `JsonResponse::getEncodingOptions()` for easier manipulation + of the options used while encoding data to JSON format. + +2.4.0 +----- + + * added RequestStack + * added Request::getEncodings() + * added accessors methods to session handlers + +2.3.0 +----- + + * added support for ranges of IPs in trusted proxies + * `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode) + * Improved error-handling of `\Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler` + to ensure the supplied PDO handler throws Exceptions on error (as the class expects). Added related test cases + to verify that Exceptions are properly thrown when the PDO queries fail. + +2.2.0 +----- + + * fixed the Request::create() precedence (URI information always take precedence now) + * added Request::getTrustedProxies() + * deprecated Request::isProxyTrusted() + * [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects + * added a IpUtils class to check if an IP belongs to a CIDR + * added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method) + * disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to + enable it, and Request::getHttpMethodParameterOverride() to check if it is supported) + * Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3 + * Deprecated Flashbag::count() and \Countable interface, will be removed in 2.3 + +2.1.0 +----- + + * added Request::getSchemeAndHttpHost() and Request::getUserInfo() + * added a fluent interface to the Response class + * added Request::isProxyTrusted() + * added JsonResponse + * added a getTargetUrl method to RedirectResponse + * added support for streamed responses + * made Response::prepare() method the place to enforce HTTP specification + * [BC BREAK] moved management of the locale from the Session class to the Request class + * added a generic access to the PHP built-in filter mechanism: ParameterBag::filter() + * made FileBinaryMimeTypeGuesser command configurable + * added Request::getUser() and Request::getPassword() + * added support for the PATCH method in Request + * removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3 + * added ResponseHeaderBag::makeDisposition() (implements RFC 6266) + * made mimetype to extension conversion configurable + * [BC BREAK] Moved all session related classes and interfaces into own namespace, as + `Symfony\Component\HttpFoundation\Session` and renamed classes accordingly. + Session handlers are located in the subnamespace `Symfony\Component\HttpFoundation\Session\Handler`. + * SessionHandlers must implement `\SessionHandlerInterface` or extend from the + `Symfony\Component\HttpFoundation\Storage\Handler\NativeSessionHandler` base class. + * Added internal storage driver proxy mechanism for forward compatibility with + PHP 5.4 `\SessionHandler` class. + * Added session handlers for custom Memcache, Memcached and Null session save handlers. + * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionHandler`. + * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and + `remove()`. Added `getBag()`, `registerBag()`. The `NativeSessionStorage` class + is a mediator for the session storage internals including the session handlers + which do the real work of participating in the internal PHP session workflow. + * [BC BREAK] Introduced mock implementations of `SessionStorage` to enable unit + and functional testing without starting real PHP sessions. Removed + `ArraySessionStorage`, and replaced with `MockArraySessionStorage` for unit + tests; removed `FilesystemSessionStorage`, and replaced with`MockFileSessionStorage` + for functional tests. These do not interact with global session ini + configuration values, session functions or `$_SESSION` superglobal. This means + they can be configured directly allowing multiple instances to work without + conflicting in the same PHP process. + * [BC BREAK] Removed the `close()` method from the `Session` class, as this is + now redundant. + * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()` + `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag()` instead + which returns a `FlashBagInterface`. + * `Session->clear()` now only clears session attributes as before it cleared + flash messages and attributes. `Session->getFlashBag()->all()` clears flashes now. + * Session data is now managed by `SessionBagInterface` to better encapsulate + session data. + * Refactored session attribute and flash messages system to their own + `SessionBagInterface` implementations. + * Added `FlashBag`. Flashes expire when retrieved by `get()` or `all()`. This + implementation is ESI compatible. + * Added `AutoExpireFlashBag` (default) to replicate Symfony 2.0.x auto expire + behaviour of messages auto expiring after one page page load. Messages must + be retrieved by `get()` or `all()`. + * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate + attributes storage behaviour from 2.0.x (default). + * Added `Symfony\Component\HttpFoundation\Attribute\NamespacedAttributeBag` for + namespace session attributes. + * Flash API can stores messages in an array so there may be multiple messages + per flash type. The old `Session` class API remains without BC break as it + will allow single messages as before. + * Added basic session meta-data to the session to record session create time, + last updated time, and the lifetime of the session cookie that was provided + to the client. + * Request::getClientIp() method doesn't take a parameter anymore but bases + itself on the trustProxy parameter. + * Added isMethod() to Request object. + * [BC BREAK] The methods `getPathInfo()`, `getBaseUrl()` and `getBasePath()` of + a `Request` now all return a raw value (vs a urldecoded value before). Any call + to one of these methods must be checked and wrapped in a `rawurldecode()` if + needed. diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Cookie.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Cookie.php new file mode 100644 index 0000000..061703d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Cookie.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents a cookie. + * + * @author Johannes M. Schmitt + */ +class Cookie +{ + protected $name; + protected $value; + protected $domain; + protected $expire; + protected $path; + protected $secure; + protected $httpOnly; + + /** + * Constructor. + * + * @param string $name The name of the cookie + * @param string $value The value of the cookie + * @param int|string|\DateTime $expire The time the cookie expires + * @param string $path The path on the server in which the cookie will be available on + * @param string $domain The domain that the cookie is available to + * @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client + * @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol + * + * @throws \InvalidArgumentException + */ + public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true) + { + // from PHP source code + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); + } + + if (empty($name)) { + throw new \InvalidArgumentException('The cookie name cannot be empty.'); + } + + // convert expiration time to a Unix timestamp + if ($expire instanceof \DateTime) { + $expire = $expire->format('U'); + } elseif (!is_numeric($expire)) { + $expire = strtotime($expire); + + if (false === $expire || -1 === $expire) { + throw new \InvalidArgumentException('The cookie expiration time is not valid.'); + } + } + + $this->name = $name; + $this->value = $value; + $this->domain = $domain; + $this->expire = $expire; + $this->path = empty($path) ? '/' : $path; + $this->secure = (bool) $secure; + $this->httpOnly = (bool) $httpOnly; + } + + /** + * Returns the cookie as a string. + * + * @return string The cookie + */ + public function __toString() + { + $str = urlencode($this->getName()).'='; + + if ('' === (string) $this->getValue()) { + $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001); + } else { + $str .= urlencode($this->getValue()); + + if ($this->getExpiresTime() !== 0) { + $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()); + } + } + + if ($this->path) { + $str .= '; path='.$this->path; + } + + if ($this->getDomain()) { + $str .= '; domain='.$this->getDomain(); + } + + if (true === $this->isSecure()) { + $str .= '; secure'; + } + + if (true === $this->isHttpOnly()) { + $str .= '; httponly'; + } + + return $str; + } + + /** + * Gets the name of the cookie. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the domain that the cookie is available to. + * + * @return string + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Gets the time the cookie expires. + * + * @return int + */ + public function getExpiresTime() + { + return $this->expire; + } + + /** + * Gets the path on the server in which the cookie will be available on. + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client. + * + * @return bool + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Checks whether the cookie will be made accessible only through the HTTP protocol. + * + * @return bool + */ + public function isHttpOnly() + { + return $this->httpOnly; + } + + /** + * Whether this cookie is about to be cleared. + * + * @return bool + */ + public function isCleared() + { + return $this->expire < time(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.php new file mode 100644 index 0000000..e9c8441 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ExpressionRequestMatcher.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\HttpFoundation; + +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + +/** + * ExpressionRequestMatcher uses an expression to match a Request. + * + * @author Fabien Potencier + */ +class ExpressionRequestMatcher extends RequestMatcher +{ + private $language; + private $expression; + + public function setExpression(ExpressionLanguage $language, $expression) + { + $this->language = $language; + $this->expression = $expression; + } + + public function matches(Request $request) + { + if (!$this->language) { + throw new \LogicException('Unable to match the request as the expression language is not available.'); + } + + return $this->language->evaluate($this->expression, array( + 'request' => $request, + 'method' => $request->getMethod(), + 'path' => rawurldecode($request->getPathInfo()), + 'host' => $request->getHost(), + 'ip' => $request->getClientIp(), + 'attributes' => $request->attributes->all(), + )) && parent::matches($request); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php new file mode 100644 index 0000000..41f7a46 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when the access on a file was denied. + * + * @author Bernhard Schussek + */ +class AccessDeniedException extends FileException +{ + /** + * Constructor. + * + * @param string $path The path to the accessed file + */ + public function __construct($path) + { + parent::__construct(sprintf('The file %s could not be accessed', $path)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/FileException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/FileException.php new file mode 100644 index 0000000..fad5133 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/FileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an error occurred in the component File. + * + * @author Bernhard Schussek + */ +class FileException extends \RuntimeException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php new file mode 100644 index 0000000..ac90d40 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when a file was not found. + * + * @author Bernhard Schussek + */ +class FileNotFoundException extends FileException +{ + /** + * Constructor. + * + * @param string $path The path to the file that was not found + */ + public function __construct($path) + { + parent::__construct(sprintf('The file "%s" does not exist', $path)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php new file mode 100644 index 0000000..0444b87 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +class UnexpectedTypeException extends FileException +{ + public function __construct($value, $expectedType) + { + parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/UploadException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/UploadException.php new file mode 100644 index 0000000..7074e76 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/Exception/UploadException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an error occurred during file upload. + * + * @author Bernhard Schussek + */ +class UploadException extends FileException +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/File.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/File.php new file mode 100644 index 0000000..f1b28b4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/File.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; + +/** + * A file in the file system. + * + * @author Bernhard Schussek + */ +class File extends \SplFileInfo +{ + /** + * Constructs a new file from the given path. + * + * @param string $path The path to the file + * @param bool $checkPath Whether to check the path or not + * + * @throws FileNotFoundException If the given path is not a file + */ + public function __construct($path, $checkPath = true) + { + if ($checkPath && !is_file($path)) { + throw new FileNotFoundException($path); + } + + parent::__construct($path); + } + + /** + * Returns the extension based on the mime type. + * + * If the mime type is unknown, returns null. + * + * This method uses the mime type as guessed by getMimeType() + * to guess the file extension. + * + * @return string|null The guessed extension or null if it cannot be guessed + * + * @see ExtensionGuesser + * @see getMimeType() + */ + public function guessExtension() + { + $type = $this->getMimeType(); + $guesser = ExtensionGuesser::getInstance(); + + return $guesser->guess($type); + } + + /** + * Returns the mime type of the file. + * + * The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(), + * mime_content_type() and the system binary "file" (in this order), depending on + * which of those are available. + * + * @return string|null The guessed mime type (i.e. "application/pdf") + * + * @see MimeTypeGuesser + */ + public function getMimeType() + { + $guesser = MimeTypeGuesser::getInstance(); + + return $guesser->guess($this->getPathname()); + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if the target file could not be created + */ + public function move($directory, $name = null) + { + $target = $this->getTargetFile($directory, $name); + + if (!@rename($this->getPathname(), $target)) { + $error = error_get_last(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + } + + @chmod($target, 0666 & ~umask()); + + return $target; + } + + protected function getTargetFile($directory, $name = null) + { + if (!is_dir($directory)) { + if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new FileException(sprintf('Unable to create the "%s" directory', $directory)); + } + } elseif (!is_writable($directory)) { + throw new FileException(sprintf('Unable to write in the "%s" directory', $directory)); + } + + $target = rtrim($directory, '/\\').DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name)); + + return new self($target, false); + } + + /** + * Returns locale independent base name of the given path. + * + * @param string $name The new file name + * + * @return string containing + */ + protected function getName($name) + { + $originalName = str_replace('\\', '/', $name); + $pos = strrpos($originalName, '/'); + $originalName = false === $pos ? $originalName : substr($originalName, $pos + 1); + + return $originalName; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php new file mode 100644 index 0000000..ec9b78a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * A singleton mime type to file extension guesser. + * + * A default guesser is provided. + * You can register custom guessers by calling the register() + * method on the singleton instance: + * + * $guesser = ExtensionGuesser::getInstance(); + * $guesser->register(new MyCustomExtensionGuesser()); + * + * The last registered guesser is preferred over previously registered ones. + */ +class ExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * The singleton instance. + * + * @var ExtensionGuesser + */ + private static $instance = null; + + /** + * All registered ExtensionGuesserInterface instances. + * + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance. + * + * @return ExtensionGuesser + */ + public static function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Registers all natively provided extension guessers. + */ + private function __construct() + { + $this->register(new MimeTypeExtensionGuesser()); + } + + /** + * Registers a new extension guesser. + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param ExtensionGuesserInterface $guesser + */ + public function register(ExtensionGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the extension. + * + * The mime type is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $mimeType The mime type + * + * @return string The guessed extension or NULL, if none could be guessed + */ + public function guess($mimeType) + { + foreach ($this->guessers as $guesser) { + if (null !== $extension = $guesser->guess($mimeType)) { + return $extension; + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php new file mode 100644 index 0000000..d19a0e5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Guesses the file extension corresponding to a given mime type. + */ +interface ExtensionGuesserInterface +{ + /** + * Makes a best guess for a file extension, given a mime type. + * + * @param string $mimeType The mime type + * + * @return string The guessed extension or NULL, if none could be guessed + */ + public function guess($mimeType); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php new file mode 100644 index 0000000..f917a06 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type with the binary "file" (only available on *nix). + * + * @author Bernhard Schussek + */ +class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $cmd; + + /** + * Constructor. + * + * The $cmd pattern must contain a "%s" string that will be replaced + * with the file name to guess. + * + * The command output must start with the mime type of the file. + * + * @param string $cmd The command to run to get the mime type of a file + */ + public function __construct($cmd = 'file -b --mime %s 2>/dev/null') + { + $this->cmd = $cmd; + } + + /** + * Returns whether this guesser is supported on the current OS. + * + * @return bool + */ + public static function isSupported() + { + return '\\' !== DIRECTORY_SEPARATOR && function_exists('passthru') && function_exists('escapeshellarg'); + } + + /** + * {@inheritdoc} + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return; + } + + ob_start(); + + // need to use --mime instead of -i. see #6641 + passthru(sprintf($this->cmd, escapeshellarg($path)), $return); + if ($return > 0) { + ob_end_clean(); + + return; + } + + $type = trim(ob_get_clean()); + + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) { + // it's not a type, but an error message + return; + } + + return $match[1]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php new file mode 100644 index 0000000..a7e4ae2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type using the PECL extension FileInfo. + * + * @author Bernhard Schussek + */ +class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $magicFile; + + /** + * Constructor. + * + * @param string $magicFile A magic file to use with the finfo instance + * + * @link http://www.php.net/manual/en/function.finfo-open.php + */ + public function __construct($magicFile = null) + { + $this->magicFile = $magicFile; + } + + /** + * Returns whether this guesser is supported on the current OS/PHP setup. + * + * @return bool + */ + public static function isSupported() + { + return function_exists('finfo_open'); + } + + /** + * {@inheritdoc} + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return; + } + + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) { + return; + } + + return $finfo->file($path); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php new file mode 100644 index 0000000..75eeefb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -0,0 +1,807 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Provides a best-guess mapping of mime type to file extension. + */ +class MimeTypeExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * A map of mime types and their default extensions. + * + * This list has been placed under the public domain by the Apache HTTPD project. + * This list has been updated from upstream on 2013-04-23. + * + * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + * + * @var array + */ + protected $defaultExtensions = array( + 'application/andrew-inset' => 'ez', + 'application/applixware' => 'aw', + 'application/atom+xml' => 'atom', + 'application/atomcat+xml' => 'atomcat', + 'application/atomsvc+xml' => 'atomsvc', + 'application/ccxml+xml' => 'ccxml', + 'application/cdmi-capability' => 'cdmia', + 'application/cdmi-container' => 'cdmic', + 'application/cdmi-domain' => 'cdmid', + 'application/cdmi-object' => 'cdmio', + 'application/cdmi-queue' => 'cdmiq', + 'application/cu-seeme' => 'cu', + 'application/davmount+xml' => 'davmount', + 'application/docbook+xml' => 'dbk', + 'application/dssc+der' => 'dssc', + 'application/dssc+xml' => 'xdssc', + 'application/ecmascript' => 'ecma', + 'application/emma+xml' => 'emma', + 'application/epub+zip' => 'epub', + 'application/exi' => 'exi', + 'application/font-tdpfr' => 'pfr', + 'application/gml+xml' => 'gml', + 'application/gpx+xml' => 'gpx', + 'application/gxf' => 'gxf', + 'application/hyperstudio' => 'stk', + 'application/inkml+xml' => 'ink', + 'application/ipfix' => 'ipfix', + 'application/java-archive' => 'jar', + 'application/java-serialized-object' => 'ser', + 'application/java-vm' => 'class', + 'application/javascript' => 'js', + 'application/json' => 'json', + 'application/jsonml+json' => 'jsonml', + 'application/lost+xml' => 'lostxml', + 'application/mac-binhex40' => 'hqx', + 'application/mac-compactpro' => 'cpt', + 'application/mads+xml' => 'mads', + 'application/marc' => 'mrc', + 'application/marcxml+xml' => 'mrcx', + 'application/mathematica' => 'ma', + 'application/mathml+xml' => 'mathml', + 'application/mbox' => 'mbox', + 'application/mediaservercontrol+xml' => 'mscml', + 'application/metalink+xml' => 'metalink', + 'application/metalink4+xml' => 'meta4', + 'application/mets+xml' => 'mets', + 'application/mods+xml' => 'mods', + 'application/mp21' => 'm21', + 'application/mp4' => 'mp4s', + 'application/msword' => 'doc', + 'application/mxf' => 'mxf', + 'application/octet-stream' => 'bin', + 'application/oda' => 'oda', + 'application/oebps-package+xml' => 'opf', + 'application/ogg' => 'ogx', + 'application/omdoc+xml' => 'omdoc', + 'application/onenote' => 'onetoc', + 'application/oxps' => 'oxps', + 'application/patch-ops-error+xml' => 'xer', + 'application/pdf' => 'pdf', + 'application/pgp-encrypted' => 'pgp', + 'application/pgp-signature' => 'asc', + 'application/pics-rules' => 'prf', + 'application/pkcs10' => 'p10', + 'application/pkcs7-mime' => 'p7m', + 'application/pkcs7-signature' => 'p7s', + 'application/pkcs8' => 'p8', + 'application/pkix-attr-cert' => 'ac', + 'application/pkix-cert' => 'cer', + 'application/pkix-crl' => 'crl', + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/pls+xml' => 'pls', + 'application/postscript' => 'ai', + 'application/prs.cww' => 'cww', + 'application/pskc+xml' => 'pskcxml', + 'application/rdf+xml' => 'rdf', + 'application/reginfo+xml' => 'rif', + 'application/relax-ng-compact-syntax' => 'rnc', + 'application/resource-lists+xml' => 'rl', + 'application/resource-lists-diff+xml' => 'rld', + 'application/rls-services+xml' => 'rs', + 'application/rpki-ghostbusters' => 'gbr', + 'application/rpki-manifest' => 'mft', + 'application/rpki-roa' => 'roa', + 'application/rsd+xml' => 'rsd', + 'application/rss+xml' => 'rss', + 'application/rtf' => 'rtf', + 'application/sbml+xml' => 'sbml', + 'application/scvp-cv-request' => 'scq', + 'application/scvp-cv-response' => 'scs', + 'application/scvp-vp-request' => 'spq', + 'application/scvp-vp-response' => 'spp', + 'application/sdp' => 'sdp', + 'application/set-payment-initiation' => 'setpay', + 'application/set-registration-initiation' => 'setreg', + 'application/shf+xml' => 'shf', + 'application/smil+xml' => 'smi', + 'application/sparql-query' => 'rq', + 'application/sparql-results+xml' => 'srx', + 'application/srgs' => 'gram', + 'application/srgs+xml' => 'grxml', + 'application/sru+xml' => 'sru', + 'application/ssdl+xml' => 'ssdl', + 'application/ssml+xml' => 'ssml', + 'application/tei+xml' => 'tei', + 'application/thraud+xml' => 'tfi', + 'application/timestamped-data' => 'tsd', + 'application/vnd.3gpp.pic-bw-large' => 'plb', + 'application/vnd.3gpp.pic-bw-small' => 'psb', + 'application/vnd.3gpp.pic-bw-var' => 'pvb', + 'application/vnd.3gpp2.tcap' => 'tcap', + 'application/vnd.3m.post-it-notes' => 'pwn', + 'application/vnd.accpac.simply.aso' => 'aso', + 'application/vnd.accpac.simply.imp' => 'imp', + 'application/vnd.acucobol' => 'acu', + 'application/vnd.acucorp' => 'atc', + 'application/vnd.adobe.air-application-installer-package+zip' => 'air', + 'application/vnd.adobe.formscentral.fcdt' => 'fcdt', + 'application/vnd.adobe.fxp' => 'fxp', + 'application/vnd.adobe.xdp+xml' => 'xdp', + 'application/vnd.adobe.xfdf' => 'xfdf', + 'application/vnd.ahead.space' => 'ahead', + 'application/vnd.airzip.filesecure.azf' => 'azf', + 'application/vnd.airzip.filesecure.azs' => 'azs', + 'application/vnd.amazon.ebook' => 'azw', + 'application/vnd.americandynamics.acc' => 'acc', + 'application/vnd.amiga.ami' => 'ami', + 'application/vnd.android.package-archive' => 'apk', + 'application/vnd.anser-web-certificate-issue-initiation' => 'cii', + 'application/vnd.anser-web-funds-transfer-initiation' => 'fti', + 'application/vnd.antix.game-component' => 'atx', + 'application/vnd.apple.installer+xml' => 'mpkg', + 'application/vnd.apple.mpegurl' => 'm3u8', + 'application/vnd.aristanetworks.swi' => 'swi', + 'application/vnd.astraea-software.iota' => 'iota', + 'application/vnd.audiograph' => 'aep', + 'application/vnd.blueice.multipass' => 'mpm', + 'application/vnd.bmi' => 'bmi', + 'application/vnd.businessobjects' => 'rep', + 'application/vnd.chemdraw+xml' => 'cdxml', + 'application/vnd.chipnuts.karaoke-mmd' => 'mmd', + 'application/vnd.cinderella' => 'cdy', + 'application/vnd.claymore' => 'cla', + 'application/vnd.cloanto.rp9' => 'rp9', + 'application/vnd.clonk.c4group' => 'c4g', + 'application/vnd.cluetrust.cartomobile-config' => 'c11amc', + 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz', + 'application/vnd.commonspace' => 'csp', + 'application/vnd.contact.cmsg' => 'cdbcmsg', + 'application/vnd.cosmocaller' => 'cmc', + 'application/vnd.crick.clicker' => 'clkx', + 'application/vnd.crick.clicker.keyboard' => 'clkk', + 'application/vnd.crick.clicker.palette' => 'clkp', + 'application/vnd.crick.clicker.template' => 'clkt', + 'application/vnd.crick.clicker.wordbank' => 'clkw', + 'application/vnd.criticaltools.wbs+xml' => 'wbs', + 'application/vnd.ctc-posml' => 'pml', + 'application/vnd.cups-ppd' => 'ppd', + 'application/vnd.curl.car' => 'car', + 'application/vnd.curl.pcurl' => 'pcurl', + 'application/vnd.dart' => 'dart', + 'application/vnd.data-vision.rdz' => 'rdz', + 'application/vnd.dece.data' => 'uvf', + 'application/vnd.dece.ttml+xml' => 'uvt', + 'application/vnd.dece.unspecified' => 'uvx', + 'application/vnd.dece.zip' => 'uvz', + 'application/vnd.denovo.fcselayout-link' => 'fe_launch', + 'application/vnd.dna' => 'dna', + 'application/vnd.dolby.mlp' => 'mlp', + 'application/vnd.dpgraph' => 'dpg', + 'application/vnd.dreamfactory' => 'dfac', + 'application/vnd.ds-keypoint' => 'kpxx', + 'application/vnd.dvb.ait' => 'ait', + 'application/vnd.dvb.service' => 'svc', + 'application/vnd.dynageo' => 'geo', + 'application/vnd.ecowin.chart' => 'mag', + 'application/vnd.enliven' => 'nml', + 'application/vnd.epson.esf' => 'esf', + 'application/vnd.epson.msf' => 'msf', + 'application/vnd.epson.quickanime' => 'qam', + 'application/vnd.epson.salt' => 'slt', + 'application/vnd.epson.ssf' => 'ssf', + 'application/vnd.eszigno3+xml' => 'es3', + 'application/vnd.ezpix-album' => 'ez2', + 'application/vnd.ezpix-package' => 'ez3', + 'application/vnd.fdf' => 'fdf', + 'application/vnd.fdsn.mseed' => 'mseed', + 'application/vnd.fdsn.seed' => 'seed', + 'application/vnd.flographit' => 'gph', + 'application/vnd.fluxtime.clip' => 'ftc', + 'application/vnd.framemaker' => 'fm', + 'application/vnd.frogans.fnc' => 'fnc', + 'application/vnd.frogans.ltf' => 'ltf', + 'application/vnd.fsc.weblaunch' => 'fsc', + 'application/vnd.fujitsu.oasys' => 'oas', + 'application/vnd.fujitsu.oasys2' => 'oa2', + 'application/vnd.fujitsu.oasys3' => 'oa3', + 'application/vnd.fujitsu.oasysgp' => 'fg5', + 'application/vnd.fujitsu.oasysprs' => 'bh2', + 'application/vnd.fujixerox.ddd' => 'ddd', + 'application/vnd.fujixerox.docuworks' => 'xdw', + 'application/vnd.fujixerox.docuworks.binder' => 'xbd', + 'application/vnd.fuzzysheet' => 'fzs', + 'application/vnd.genomatix.tuxedo' => 'txd', + 'application/vnd.geogebra.file' => 'ggb', + 'application/vnd.geogebra.tool' => 'ggt', + 'application/vnd.geometry-explorer' => 'gex', + 'application/vnd.geonext' => 'gxt', + 'application/vnd.geoplan' => 'g2w', + 'application/vnd.geospace' => 'g3w', + 'application/vnd.gmx' => 'gmx', + 'application/vnd.google-earth.kml+xml' => 'kml', + 'application/vnd.google-earth.kmz' => 'kmz', + 'application/vnd.grafeq' => 'gqf', + 'application/vnd.groove-account' => 'gac', + 'application/vnd.groove-help' => 'ghf', + 'application/vnd.groove-identity-message' => 'gim', + 'application/vnd.groove-injector' => 'grv', + 'application/vnd.groove-tool-message' => 'gtm', + 'application/vnd.groove-tool-template' => 'tpl', + 'application/vnd.groove-vcard' => 'vcg', + 'application/vnd.hal+xml' => 'hal', + 'application/vnd.handheld-entertainment+xml' => 'zmm', + 'application/vnd.hbci' => 'hbci', + 'application/vnd.hhe.lesson-player' => 'les', + 'application/vnd.hp-hpgl' => 'hpgl', + 'application/vnd.hp-hpid' => 'hpid', + 'application/vnd.hp-hps' => 'hps', + 'application/vnd.hp-jlyt' => 'jlt', + 'application/vnd.hp-pcl' => 'pcl', + 'application/vnd.hp-pclxl' => 'pclxl', + 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx', + 'application/vnd.ibm.minipay' => 'mpy', + 'application/vnd.ibm.modcap' => 'afp', + 'application/vnd.ibm.rights-management' => 'irm', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.iccprofile' => 'icc', + 'application/vnd.igloader' => 'igl', + 'application/vnd.immervision-ivp' => 'ivp', + 'application/vnd.immervision-ivu' => 'ivu', + 'application/vnd.insors.igm' => 'igm', + 'application/vnd.intercon.formnet' => 'xpw', + 'application/vnd.intergeo' => 'i2g', + 'application/vnd.intu.qbo' => 'qbo', + 'application/vnd.intu.qfx' => 'qfx', + 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', + 'application/vnd.irepository.package+xml' => 'irp', + 'application/vnd.is-xpr' => 'xpr', + 'application/vnd.isac.fcs' => 'fcs', + 'application/vnd.jam' => 'jam', + 'application/vnd.jcp.javame.midlet-rms' => 'rms', + 'application/vnd.jisp' => 'jisp', + 'application/vnd.joost.joda-archive' => 'joda', + 'application/vnd.kahootz' => 'ktz', + 'application/vnd.kde.karbon' => 'karbon', + 'application/vnd.kde.kchart' => 'chrt', + 'application/vnd.kde.kformula' => 'kfo', + 'application/vnd.kde.kivio' => 'flw', + 'application/vnd.kde.kontour' => 'kon', + 'application/vnd.kde.kpresenter' => 'kpr', + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => 'kwd', + 'application/vnd.kenameaapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => 'kne', + 'application/vnd.koan' => 'skp', + 'application/vnd.kodak-descriptor' => 'sse', + 'application/vnd.las.las+xml' => 'lasxml', + 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', + 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', + 'application/vnd.lotus-1-2-3' => '123', + 'application/vnd.lotus-approach' => 'apr', + 'application/vnd.lotus-freelance' => 'pre', + 'application/vnd.lotus-notes' => 'nsf', + 'application/vnd.lotus-organizer' => 'org', + 'application/vnd.lotus-screencam' => 'scm', + 'application/vnd.lotus-wordpro' => 'lwp', + 'application/vnd.macports.portpkg' => 'portpkg', + 'application/vnd.mcd' => 'mcd', + 'application/vnd.medcalcdata' => 'mc1', + 'application/vnd.mediastation.cdkey' => 'cdkey', + 'application/vnd.mfer' => 'mwf', + 'application/vnd.mfmp' => 'mfm', + 'application/vnd.micrografx.flo' => 'flo', + 'application/vnd.micrografx.igx' => 'igx', + 'application/vnd.mif' => 'mif', + 'application/vnd.mobius.daf' => 'daf', + 'application/vnd.mobius.dis' => 'dis', + 'application/vnd.mobius.mbk' => 'mbk', + 'application/vnd.mobius.mqy' => 'mqy', + 'application/vnd.mobius.msl' => 'msl', + 'application/vnd.mobius.plc' => 'plc', + 'application/vnd.mobius.txf' => 'txf', + 'application/vnd.mophun.application' => 'mpn', + 'application/vnd.mophun.certificate' => 'mpc', + 'application/vnd.mozilla.xul+xml' => 'xul', + 'application/vnd.ms-artgalry' => 'cil', + 'application/vnd.ms-cab-compressed' => 'cab', + 'application/vnd.ms-excel' => 'xls', + 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam', + 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb', + 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', + 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm', + 'application/vnd.ms-fontobject' => 'eot', + 'application/vnd.ms-htmlhelp' => 'chm', + 'application/vnd.ms-ims' => 'ims', + 'application/vnd.ms-lrm' => 'lrm', + 'application/vnd.ms-officetheme' => 'thmx', + 'application/vnd.ms-pki.seccat' => 'cat', + 'application/vnd.ms-pki.stl' => 'stl', + 'application/vnd.ms-powerpoint' => 'ppt', + 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam', + 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm', + 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm', + 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm', + 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm', + 'application/vnd.ms-project' => 'mpp', + 'application/vnd.ms-word.document.macroenabled.12' => 'docm', + 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', + 'application/vnd.ms-works' => 'wps', + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.ms-xpsdocument' => 'xps', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.musician' => 'mus', + 'application/vnd.muvee.style' => 'msty', + 'application/vnd.mynfc' => 'taglet', + 'application/vnd.neurolanguage.nlu' => 'nlu', + 'application/vnd.nitf' => 'ntf', + 'application/vnd.noblenet-directory' => 'nnd', + 'application/vnd.noblenet-sealer' => 'nns', + 'application/vnd.noblenet-web' => 'nnw', + 'application/vnd.nokia.n-gage.data' => 'ngdat', + 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage', + 'application/vnd.nokia.radio-preset' => 'rpst', + 'application/vnd.nokia.radio-presets' => 'rpss', + 'application/vnd.novadigm.edm' => 'edm', + 'application/vnd.novadigm.edx' => 'edx', + 'application/vnd.novadigm.ext' => 'ext', + 'application/vnd.oasis.opendocument.chart' => 'odc', + 'application/vnd.oasis.opendocument.chart-template' => 'otc', + 'application/vnd.oasis.opendocument.database' => 'odb', + 'application/vnd.oasis.opendocument.formula' => 'odf', + 'application/vnd.oasis.opendocument.formula-template' => 'odft', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + 'application/vnd.oasis.opendocument.graphics-template' => 'otg', + 'application/vnd.oasis.opendocument.image' => 'odi', + 'application/vnd.oasis.opendocument.image-template' => 'oti', + 'application/vnd.oasis.opendocument.presentation' => 'odp', + 'application/vnd.oasis.opendocument.presentation-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.text' => 'odt', + 'application/vnd.oasis.opendocument.text-master' => 'odm', + 'application/vnd.oasis.opendocument.text-template' => 'ott', + 'application/vnd.oasis.opendocument.text-web' => 'oth', + 'application/vnd.olpc-sugar' => 'xo', + 'application/vnd.oma.dd2+xml' => 'dd2', + 'application/vnd.openofficeorg.extension' => 'oxt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', + 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx', + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', + 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', + 'application/vnd.osgeo.mapguide.package' => 'mgp', + 'application/vnd.osgi.dp' => 'dp', + 'application/vnd.osgi.subsystem' => 'esa', + 'application/vnd.palm' => 'pdb', + 'application/vnd.pawaafile' => 'paw', + 'application/vnd.pg.format' => 'str', + 'application/vnd.pg.osasli' => 'ei6', + 'application/vnd.picsel' => 'efif', + 'application/vnd.pmi.widget' => 'wg', + 'application/vnd.pocketlearn' => 'plf', + 'application/vnd.powerbuilder6' => 'pbd', + 'application/vnd.previewsystems.box' => 'box', + 'application/vnd.proteus.magazine' => 'mgz', + 'application/vnd.publishare-delta-tree' => 'qps', + 'application/vnd.pvi.ptid1' => 'ptid', + 'application/vnd.quark.quarkxpress' => 'qxd', + 'application/vnd.realvnc.bed' => 'bed', + 'application/vnd.recordare.musicxml' => 'mxl', + 'application/vnd.recordare.musicxml+xml' => 'musicxml', + 'application/vnd.rig.cryptonote' => 'cryptonote', + 'application/vnd.rim.cod' => 'cod', + 'application/vnd.rn-realmedia' => 'rm', + 'application/vnd.rn-realmedia-vbr' => 'rmvb', + 'application/vnd.route66.link66+xml' => 'link66', + 'application/vnd.sailingtracker.track' => 'st', + 'application/vnd.seemail' => 'see', + 'application/vnd.sema' => 'sema', + 'application/vnd.semd' => 'semd', + 'application/vnd.semf' => 'semf', + 'application/vnd.shana.informed.formdata' => 'ifm', + 'application/vnd.shana.informed.formtemplate' => 'itp', + 'application/vnd.shana.informed.interchange' => 'iif', + 'application/vnd.shana.informed.package' => 'ipk', + 'application/vnd.simtech-mindmapper' => 'twd', + 'application/vnd.smaf' => 'mmf', + 'application/vnd.smart.teacher' => 'teacher', + 'application/vnd.solent.sdkm+xml' => 'sdkm', + 'application/vnd.spotfire.dxp' => 'dxp', + 'application/vnd.spotfire.sfs' => 'sfs', + 'application/vnd.stardivision.calc' => 'sdc', + 'application/vnd.stardivision.draw' => 'sda', + 'application/vnd.stardivision.impress' => 'sdd', + 'application/vnd.stardivision.math' => 'smf', + 'application/vnd.stardivision.writer' => 'sdw', + 'application/vnd.stardivision.writer-global' => 'sgl', + 'application/vnd.stepmania.package' => 'smzip', + 'application/vnd.stepmania.stepchart' => 'sm', + 'application/vnd.sun.xml.calc' => 'sxc', + 'application/vnd.sun.xml.calc.template' => 'stc', + 'application/vnd.sun.xml.draw' => 'sxd', + 'application/vnd.sun.xml.draw.template' => 'std', + 'application/vnd.sun.xml.impress' => 'sxi', + 'application/vnd.sun.xml.impress.template' => 'sti', + 'application/vnd.sun.xml.math' => 'sxm', + 'application/vnd.sun.xml.writer' => 'sxw', + 'application/vnd.sun.xml.writer.global' => 'sxg', + 'application/vnd.sun.xml.writer.template' => 'stw', + 'application/vnd.sus-calendar' => 'sus', + 'application/vnd.svd' => 'svd', + 'application/vnd.symbian.install' => 'sis', + 'application/vnd.syncml+xml' => 'xsm', + 'application/vnd.syncml.dm+wbxml' => 'bdm', + 'application/vnd.syncml.dm+xml' => 'xdm', + 'application/vnd.tao.intent-module-archive' => 'tao', + 'application/vnd.tcpdump.pcap' => 'pcap', + 'application/vnd.tmobile-livetv' => 'tmo', + 'application/vnd.trid.tpt' => 'tpt', + 'application/vnd.triscape.mxs' => 'mxs', + 'application/vnd.trueapp' => 'tra', + 'application/vnd.ufdl' => 'ufd', + 'application/vnd.uiq.theme' => 'utz', + 'application/vnd.umajin' => 'umj', + 'application/vnd.unity' => 'unityweb', + 'application/vnd.uoml+xml' => 'uoml', + 'application/vnd.vcx' => 'vcx', + 'application/vnd.visio' => 'vsd', + 'application/vnd.visionary' => 'vis', + 'application/vnd.vsf' => 'vsf', + 'application/vnd.wap.wbxml' => 'wbxml', + 'application/vnd.wap.wmlc' => 'wmlc', + 'application/vnd.wap.wmlscriptc' => 'wmlsc', + 'application/vnd.webturbo' => 'wtb', + 'application/vnd.wolfram.player' => 'nbp', + 'application/vnd.wordperfect' => 'wpd', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wt.stf' => 'stf', + 'application/vnd.xara' => 'xar', + 'application/vnd.xfdl' => 'xfdl', + 'application/vnd.yamaha.hv-dic' => 'hvd', + 'application/vnd.yamaha.hv-script' => 'hvs', + 'application/vnd.yamaha.hv-voice' => 'hvp', + 'application/vnd.yamaha.openscoreformat' => 'osf', + 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg', + 'application/vnd.yamaha.smaf-audio' => 'saf', + 'application/vnd.yamaha.smaf-phrase' => 'spf', + 'application/vnd.yellowriver-custom-menu' => 'cmp', + 'application/vnd.zul' => 'zir', + 'application/vnd.zzazz.deck+xml' => 'zaz', + 'application/voicexml+xml' => 'vxml', + 'application/widget' => 'wgt', + 'application/winhlp' => 'hlp', + 'application/wsdl+xml' => 'wsdl', + 'application/wspolicy+xml' => 'wspolicy', + 'application/x-7z-compressed' => '7z', + 'application/x-abiword' => 'abw', + 'application/x-ace-compressed' => 'ace', + 'application/x-apple-diskimage' => 'dmg', + 'application/x-authorware-bin' => 'aab', + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bittorrent' => 'torrent', + 'application/x-blorb' => 'blb', + 'application/x-bzip' => 'bz', + 'application/x-bzip2' => 'bz2', + 'application/x-cbr' => 'cbr', + 'application/x-cdlink' => 'vcd', + 'application/x-cfs-compressed' => 'cfs', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-conference' => 'nsc', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => 'deb', + 'application/x-dgc-compressed' => 'dgc', + 'application/x-director' => 'dir', + 'application/x-doom' => 'wad', + 'application/x-dtbncx+xml' => 'ncx', + 'application/x-dtbook+xml' => 'dtb', + 'application/x-dtbresource+xml' => 'res', + 'application/x-dvi' => 'dvi', + 'application/x-envoy' => 'evy', + 'application/x-eva' => 'eva', + 'application/x-font-bdf' => 'bdf', + 'application/x-font-ghostscript' => 'gsf', + 'application/x-font-linux-psf' => 'psf', + 'application/x-font-otf' => 'otf', + 'application/x-font-pcf' => 'pcf', + 'application/x-font-snf' => 'snf', + 'application/x-font-ttf' => 'ttf', + 'application/x-font-type1' => 'pfa', + 'application/x-font-woff' => 'woff', + 'application/x-freearc' => 'arc', + 'application/x-futuresplash' => 'spl', + 'application/x-gca-compressed' => 'gca', + 'application/x-glulx' => 'ulx', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gramps-xml' => 'gramps', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-install-instructions' => 'install', + 'application/x-iso9660-image' => 'iso', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-lzh-compressed' => 'lzh', + 'application/x-mie' => 'mie', + 'application/x-mobipocket-ebook' => 'prc', + 'application/x-ms-application' => 'application', + 'application/x-ms-shortcut' => 'lnk', + 'application/x-ms-wmd' => 'wmd', + 'application/x-ms-wmz' => 'wmz', + 'application/x-ms-xbap' => 'xbap', + 'application/x-msaccess' => 'mdb', + 'application/x-msbinder' => 'obd', + 'application/x-mscardfile' => 'crd', + 'application/x-msclip' => 'clp', + 'application/x-msdownload' => 'exe', + 'application/x-msmediaview' => 'mvb', + 'application/x-msmetafile' => 'wmf', + 'application/x-msmoney' => 'mny', + 'application/x-mspublisher' => 'pub', + 'application/x-msschedule' => 'scd', + 'application/x-msterminal' => 'trm', + 'application/x-mswrite' => 'wri', + 'application/x-netcdf' => 'nc', + 'application/x-nzb' => 'nzb', + 'application/x-pkcs12' => 'p12', + 'application/x-pkcs7-certificates' => 'p7b', + 'application/x-pkcs7-certreqresp' => 'p7r', + 'application/x-rar-compressed' => 'rar', + 'application/x-rar' => 'rar', + 'application/x-research-info-systems' => 'ris', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-sql' => 'sql', + 'application/x-stuffit' => 'sit', + 'application/x-stuffitx' => 'sitx', + 'application/x-subrip' => 'srt', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-t3vm-image' => 't3', + 'application/x-tads' => 'gam', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => 'texinfo', + 'application/x-tgif' => 'obj', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => 'der', + 'application/x-xfig' => 'fig', + 'application/x-xliff+xml' => 'xlf', + 'application/x-xpinstall' => 'xpi', + 'application/x-xz' => 'xz', + 'application/x-zmachine' => 'z1', + 'application/xaml+xml' => 'xaml', + 'application/xcap-diff+xml' => 'xdf', + 'application/xenc+xml' => 'xenc', + 'application/xhtml+xml' => 'xhtml', + 'application/xml' => 'xml', + 'application/xml-dtd' => 'dtd', + 'application/xop+xml' => 'xop', + 'application/xproc+xml' => 'xpl', + 'application/xslt+xml' => 'xslt', + 'application/xspf+xml' => 'xspf', + 'application/xv+xml' => 'mxml', + 'application/yang' => 'yang', + 'application/yin+xml' => 'yin', + 'application/zip' => 'zip', + 'audio/adpcm' => 'adp', + 'audio/basic' => 'au', + 'audio/midi' => 'mid', + 'audio/mp4' => 'mp4a', + 'audio/mpeg' => 'mpga', + 'audio/ogg' => 'oga', + 'audio/s3m' => 's3m', + 'audio/silk' => 'sil', + 'audio/vnd.dece.audio' => 'uva', + 'audio/vnd.digital-winds' => 'eol', + 'audio/vnd.dra' => 'dra', + 'audio/vnd.dts' => 'dts', + 'audio/vnd.dts.hd' => 'dtshd', + 'audio/vnd.lucent.voice' => 'lvp', + 'audio/vnd.ms-playready.media.pya' => 'pya', + 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', + 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', + 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', + 'audio/vnd.rip' => 'rip', + 'audio/webm' => 'weba', + 'audio/x-aac' => 'aac', + 'audio/x-aiff' => 'aif', + 'audio/x-caf' => 'caf', + 'audio/x-flac' => 'flac', + 'audio/x-matroska' => 'mka', + 'audio/x-mpegurl' => 'm3u', + 'audio/x-ms-wax' => 'wax', + 'audio/x-ms-wma' => 'wma', + 'audio/x-pn-realaudio' => 'ram', + 'audio/x-pn-realaudio-plugin' => 'rmp', + 'audio/x-wav' => 'wav', + 'audio/xm' => 'xm', + 'chemical/x-cdx' => 'cdx', + 'chemical/x-cif' => 'cif', + 'chemical/x-cmdf' => 'cmdf', + 'chemical/x-cml' => 'cml', + 'chemical/x-csml' => 'csml', + 'chemical/x-xyz' => 'xyz', + 'image/bmp' => 'bmp', + 'image/x-ms-bmp' => 'bmp', + 'image/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => 'jpeg', + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + 'image/sgi' => 'sgi', + 'image/svg+xml' => 'svg', + 'image/tiff' => 'tiff', + 'image/vnd.adobe.photoshop' => 'psd', + 'image/vnd.dece.graphic' => 'uvi', + 'image/vnd.dvb.subtitle' => 'sub', + 'image/vnd.djvu' => 'djvu', + 'image/vnd.dwg' => 'dwg', + 'image/vnd.dxf' => 'dxf', + 'image/vnd.fastbidsheet' => 'fbs', + 'image/vnd.fpx' => 'fpx', + 'image/vnd.fst' => 'fst', + 'image/vnd.fujixerox.edmics-mmr' => 'mmr', + 'image/vnd.fujixerox.edmics-rlc' => 'rlc', + 'image/vnd.ms-modi' => 'mdi', + 'image/vnd.ms-photo' => 'wdp', + 'image/vnd.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-3ds' => '3ds', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => 'fh', + 'image/x-icon' => 'ico', + 'image/x-mrsid-image' => 'sid', + 'image/x-pcx' => 'pcx', + 'image/x-pict' => 'pic', + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-tga' => 'tga', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/rfc822' => 'eml', + 'model/iges' => 'igs', + 'model/mesh' => 'msh', + 'model/vnd.collada+xml' => 'dae', + 'model/vnd.dwf' => 'dwf', + 'model/vnd.gdl' => 'gdl', + 'model/vnd.gtw' => 'gtw', + 'model/vnd.mts' => 'mts', + 'model/vnd.vtu' => 'vtu', + 'model/vrml' => 'wrl', + 'model/x3d+binary' => 'x3db', + 'model/x3d+vrml' => 'x3dv', + 'model/x3d+xml' => 'x3d', + 'text/cache-manifest' => 'appcache', + 'text/calendar' => 'ics', + 'text/css' => 'css', + 'text/csv' => 'csv', + 'text/html' => 'html', + 'text/n3' => 'n3', + 'text/plain' => 'txt', + 'text/prs.lines.tag' => 'dsc', + 'text/richtext' => 'rtx', + 'text/rtf' => 'rtf', + 'text/sgml' => 'sgml', + 'text/tab-separated-values' => 'tsv', + 'text/troff' => 't', + 'text/turtle' => 'ttl', + 'text/uri-list' => 'uri', + 'text/vcard' => 'vcard', + 'text/vnd.curl' => 'curl', + 'text/vnd.curl.dcurl' => 'dcurl', + 'text/vnd.curl.scurl' => 'scurl', + 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.dvb.subtitle' => 'sub', + 'text/vnd.fly' => 'fly', + 'text/vnd.fmi.flexstor' => 'flx', + 'text/vnd.graphviz' => 'gv', + 'text/vnd.in3d.3dml' => '3dml', + 'text/vnd.in3d.spot' => 'spot', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/x-asm' => 's', + 'text/x-c' => 'c', + 'text/x-fortran' => 'f', + 'text/x-pascal' => 'p', + 'text/x-java-source' => 'java', + 'text/x-opml' => 'opml', + 'text/x-nfo' => 'nfo', + 'text/x-setext' => 'etx', + 'text/x-sfv' => 'sfv', + 'text/x-uuencode' => 'uu', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'video/3gpp' => '3gp', + 'video/3gpp2' => '3g2', + 'video/h261' => 'h261', + 'video/h263' => 'h263', + 'video/h264' => 'h264', + 'video/jpeg' => 'jpgv', + 'video/jpm' => 'jpm', + 'video/mj2' => 'mj2', + 'video/mp4' => 'mp4', + 'video/mpeg' => 'mpeg', + 'video/ogg' => 'ogv', + 'video/quicktime' => 'qt', + 'video/vnd.dece.hd' => 'uvh', + 'video/vnd.dece.mobile' => 'uvm', + 'video/vnd.dece.pd' => 'uvp', + 'video/vnd.dece.sd' => 'uvs', + 'video/vnd.dece.video' => 'uvv', + 'video/vnd.dvb.file' => 'dvb', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => 'mxu', + 'video/vnd.ms-playready.media.pyv' => 'pyv', + 'video/vnd.uvvu.mp4' => 'uvu', + 'video/vnd.vivo' => 'viv', + 'video/webm' => 'webm', + 'video/x-f4v' => 'f4v', + 'video/x-fli' => 'fli', + 'video/x-flv' => 'flv', + 'video/x-m4v' => 'm4v', + 'video/x-matroska' => 'mkv', + 'video/x-mng' => 'mng', + 'video/x-ms-asf' => 'asf', + 'video/x-ms-vob' => 'vob', + 'video/x-ms-wm' => 'wm', + 'video/x-ms-wmv' => 'wmv', + 'video/x-ms-wmx' => 'wmx', + 'video/x-ms-wvx' => 'wvx', + 'video/x-msvideo' => 'avi', + 'video/x-sgi-movie' => 'movie', + 'video/x-smv' => 'smv', + 'x-conference/x-cooltalk' => 'ice', + ); + + /** + * {@inheritdoc} + */ + public function guess($mimeType) + { + return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php new file mode 100644 index 0000000..ecc8a30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * A singleton mime type guesser. + * + * By default, all mime type guessers provided by the framework are installed + * (if available on the current OS/PHP setup). + * + * You can register custom guessers by calling the register() method on the + * singleton instance. Custom guessers are always called before any default ones. + * + * $guesser = MimeTypeGuesser::getInstance(); + * $guesser->register(new MyCustomMimeTypeGuesser()); + * + * If you want to change the order of the default guessers, just re-register your + * preferred one as a custom one. The last registered guesser is preferred over + * previously registered ones. + * + * Re-registering a built-in guesser also allows you to configure it: + * + * $guesser = MimeTypeGuesser::getInstance(); + * $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file')); + * + * @author Bernhard Schussek + */ +class MimeTypeGuesser implements MimeTypeGuesserInterface +{ + /** + * The singleton instance. + * + * @var MimeTypeGuesser + */ + private static $instance = null; + + /** + * All registered MimeTypeGuesserInterface instances. + * + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance. + * + * @return MimeTypeGuesser + */ + public static function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Resets the singleton instance. + */ + public static function reset() + { + self::$instance = null; + } + + /** + * Registers all natively provided mime type guessers. + */ + private function __construct() + { + if (FileBinaryMimeTypeGuesser::isSupported()) { + $this->register(new FileBinaryMimeTypeGuesser()); + } + + if (FileinfoMimeTypeGuesser::isSupported()) { + $this->register(new FileinfoMimeTypeGuesser()); + } + } + + /** + * Registers a new mime type guesser. + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param MimeTypeGuesserInterface $guesser + */ + public function register(MimeTypeGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the mime type of the given file. + * + * The file is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws \LogicException + * @throws FileNotFoundException + * @throws AccessDeniedException + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!$this->guessers) { + $msg = 'Unable to guess the mime type as no guessers are available'; + if (!FileinfoMimeTypeGuesser::isSupported()) { + $msg .= ' (Did you enable the php_fileinfo extension?)'; + } + throw new \LogicException($msg); + } + + foreach ($this->guessers as $guesser) { + if (null !== $mimeType = $guesser->guess($path)) { + return $mimeType; + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php new file mode 100644 index 0000000..f8c3ad2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type of a file. + * + * @author Bernhard Schussek + */ +interface MimeTypeGuesserInterface +{ + /** + * Guesses the mime type of the file with the given path. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws FileNotFoundException If the file does not exist + * @throws AccessDeniedException If the file could not be read + */ + public function guess($path); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/UploadedFile.php new file mode 100644 index 0000000..6b869e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -0,0 +1,293 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; + +/** + * A file uploaded through a form. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + * @author Fabien Potencier + */ +class UploadedFile extends File +{ + /** + * Whether the test mode is activated. + * + * Local files are used in test mode hence the code should not enforce HTTP uploads. + * + * @var bool + */ + private $test = false; + + /** + * The original name of the uploaded file. + * + * @var string + */ + private $originalName; + + /** + * The mime type provided by the uploader. + * + * @var string + */ + private $mimeType; + + /** + * The file size provided by the uploader. + * + * @var string + */ + private $size; + + /** + * The UPLOAD_ERR_XXX constant provided by the uploader. + * + * @var int + */ + private $error; + + /** + * Accepts the information of the uploaded file as provided by the PHP global $_FILES. + * + * The file object is only created when the uploaded file is valid (i.e. when the + * isValid() method returns true). Otherwise the only methods that could be called + * on an UploadedFile instance are: + * + * * getClientOriginalName, + * * getClientMimeType, + * * isValid, + * * getError. + * + * Calling any other method on an non-valid instance will cause an unpredictable result. + * + * @param string $path The full temporary path to the file + * @param string $originalName The original file name + * @param string $mimeType The type of the file as provided by PHP + * @param int $size The file size + * @param int $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants) + * @param bool $test Whether the test mode is active + * + * @throws FileException If file_uploads is disabled + * @throws FileNotFoundException If the file does not exist + */ + public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) + { + $this->originalName = $this->getName($originalName); + $this->mimeType = $mimeType ?: 'application/octet-stream'; + $this->size = $size; + $this->error = $error ?: UPLOAD_ERR_OK; + $this->test = (bool) $test; + + parent::__construct($path, UPLOAD_ERR_OK === $this->error); + } + + /** + * Returns the original file name. + * + * It is extracted from the request from which the file has been uploaded. + * Then it should not be considered as a safe value. + * + * @return string|null The original name + */ + public function getClientOriginalName() + { + return $this->originalName; + } + + /** + * Returns the original file extension. + * + * It is extracted from the original file name that was uploaded. + * Then it should not be considered as a safe value. + * + * @return string The extension + */ + public function getClientOriginalExtension() + { + return pathinfo($this->originalName, PATHINFO_EXTENSION); + } + + /** + * Returns the file mime type. + * + * The client mime type is extracted from the request from which the file + * was uploaded, so it should not be considered as a safe value. + * + * For a trusted mime type, use getMimeType() instead (which guesses the mime + * type based on the file content). + * + * @return string|null The mime type + * + * @see getMimeType() + */ + public function getClientMimeType() + { + return $this->mimeType; + } + + /** + * Returns the extension based on the client mime type. + * + * If the mime type is unknown, returns null. + * + * This method uses the mime type as guessed by getClientMimeType() + * to guess the file extension. As such, the extension returned + * by this method cannot be trusted. + * + * For a trusted extension, use guessExtension() instead (which guesses + * the extension based on the guessed mime type for the file). + * + * @return string|null The guessed extension or null if it cannot be guessed + * + * @see guessExtension() + * @see getClientMimeType() + */ + public function guessClientExtension() + { + $type = $this->getClientMimeType(); + $guesser = ExtensionGuesser::getInstance(); + + return $guesser->guess($type); + } + + /** + * Returns the file size. + * + * It is extracted from the request from which the file has been uploaded. + * Then it should not be considered as a safe value. + * + * @return int|null The file size + */ + public function getClientSize() + { + return $this->size; + } + + /** + * Returns the upload error. + * + * If the upload was successful, the constant UPLOAD_ERR_OK is returned. + * Otherwise one of the other UPLOAD_ERR_XXX constants is returned. + * + * @return int The upload error + */ + public function getError() + { + return $this->error; + } + + /** + * Returns whether the file was uploaded successfully. + * + * @return bool True if the file has been uploaded with HTTP and no error occurred. + */ + public function isValid() + { + $isOk = $this->error === UPLOAD_ERR_OK; + + return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname()); + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if, for any reason, the file could not have been moved + */ + public function move($directory, $name = null) + { + if ($this->isValid()) { + if ($this->test) { + return parent::move($directory, $name); + } + + $target = $this->getTargetFile($directory, $name); + + if (!@move_uploaded_file($this->getPathname(), $target)) { + $error = error_get_last(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + } + + @chmod($target, 0666 & ~umask()); + + return $target; + } + + throw new FileException($this->getErrorMessage()); + } + + /** + * Returns the maximum size of an uploaded file as configured in php.ini. + * + * @return int The maximum size of an uploaded file in bytes + */ + public static function getMaxFilesize() + { + $iniMax = strtolower(ini_get('upload_max_filesize')); + + if ('' === $iniMax) { + return PHP_INT_MAX; + } + + $max = ltrim($iniMax, '+'); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($iniMax, -1)) { + case 't': $max *= 1024; + case 'g': $max *= 1024; + case 'm': $max *= 1024; + case 'k': $max *= 1024; + } + + return $max; + } + + /** + * Returns an informative upload error message. + * + * @return string The error message regarding the specified error code + */ + public function getErrorMessage() + { + static $errors = array( + UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).', + UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.', + UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.', + UPLOAD_ERR_NO_FILE => 'No file was uploaded.', + UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.', + UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.', + UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.', + ); + + $errorCode = $this->error; + $maxFilesize = $errorCode === UPLOAD_ERR_INI_SIZE ? self::getMaxFilesize() / 1024 : 0; + $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.'; + + return sprintf($message, $this->getClientOriginalName(), $maxFilesize); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/FileBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/FileBag.php new file mode 100644 index 0000000..197eab4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/FileBag.php @@ -0,0 +1,145 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * FileBag is a container for uploaded files. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +class FileBag extends ParameterBag +{ + private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + + /** + * Constructor. + * + * @param array $parameters An array of HTTP files + */ + public function __construct(array $parameters = array()) + { + $this->replace($parameters); + } + + /** + * {@inheritdoc} + */ + public function replace(array $files = array()) + { + $this->parameters = array(); + $this->add($files); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + if (!is_array($value) && !$value instanceof UploadedFile) { + throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); + } + + parent::set($key, $this->convertFileInformation($value)); + } + + /** + * {@inheritdoc} + */ + public function add(array $files = array()) + { + foreach ($files as $key => $file) { + $this->set($key, $file); + } + } + + /** + * Converts uploaded files to UploadedFile instances. + * + * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information + * + * @return array A (multi-dimensional) array of UploadedFile instances + */ + protected function convertFileInformation($file) + { + if ($file instanceof UploadedFile) { + return $file; + } + + $file = $this->fixPhpFilesArray($file); + if (is_array($file)) { + $keys = array_keys($file); + sort($keys); + + if ($keys == self::$fileKeys) { + if (UPLOAD_ERR_NO_FILE == $file['error']) { + $file = null; + } else { + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + } + } else { + $file = array_map(array($this, 'convertFileInformation'), $file); + } + } + + return $file; + } + + /** + * Fixes a malformed PHP $_FILES array. + * + * PHP has a bug that the format of the $_FILES array differs, depending on + * whether the uploaded file fields had normal field names or array-like + * field names ("normal" vs. "parent[child]"). + * + * This method fixes the array to look like the "normal" $_FILES array. + * + * It's safe to pass an already converted array, in which case this method + * just returns the original array unmodified. + * + * @param array $data + * + * @return array + */ + protected function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + + foreach ($data['name'] as $key => $name) { + $files[$key] = $this->fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $name, + 'type' => $data['type'][$key], + 'tmp_name' => $data['tmp_name'][$key], + 'size' => $data['size'][$key], + )); + } + + return $files; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/HeaderBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/HeaderBag.php new file mode 100644 index 0000000..dc12000 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/HeaderBag.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * HeaderBag is a container for HTTP headers. + * + * @author Fabien Potencier + */ +class HeaderBag implements \IteratorAggregate, \Countable +{ + protected $headers = array(); + protected $cacheControl = array(); + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + */ + public function __construct(array $headers = array()) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns the headers as a string. + * + * @return string The headers + */ + public function __toString() + { + if (!$this->headers) { + return ''; + } + + $max = max(array_map('strlen', array_keys($this->headers))) + 1; + $content = ''; + ksort($this->headers); + foreach ($this->headers as $name => $values) { + $name = implode('-', array_map('ucfirst', explode('-', $name))); + foreach ($values as $value) { + $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value); + } + } + + return $content; + } + + /** + * Returns the headers. + * + * @return array An array of headers + */ + public function all() + { + return $this->headers; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + */ + public function keys() + { + return array_keys($this->headers); + } + + /** + * Replaces the current HTTP headers by a new set. + * + * @param array $headers An array of HTTP headers + */ + public function replace(array $headers = array()) + { + $this->headers = array(); + $this->add($headers); + } + + /** + * Adds new headers the current HTTP headers set. + * + * @param array $headers An array of HTTP headers + */ + public function add(array $headers) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns a header value by name. + * + * @param string $key The header name + * @param mixed $default The default value + * @param bool $first Whether to return the first value or all header values + * + * @return string|array The first header value if $first is true, an array of values otherwise + */ + public function get($key, $default = null, $first = true) + { + $key = strtr(strtolower($key), '_', '-'); + + if (!array_key_exists($key, $this->headers)) { + if (null === $default) { + return $first ? null : array(); + } + + return $first ? $default : array($default); + } + + if ($first) { + return count($this->headers[$key]) ? $this->headers[$key][0] : $default; + } + + return $this->headers[$key]; + } + + /** + * Sets a header by name. + * + * @param string $key The key + * @param string|array $values The value or an array of values + * @param bool $replace Whether to replace the actual value or not (true by default) + */ + public function set($key, $values, $replace = true) + { + $key = strtr(strtolower($key), '_', '-'); + + $values = array_values((array) $values); + + if (true === $replace || !isset($this->headers[$key])) { + $this->headers[$key] = $values; + } else { + $this->headers[$key] = array_merge($this->headers[$key], $values); + } + + if ('cache-control' === $key) { + $this->cacheControl = $this->parseCacheControl($values[0]); + } + } + + /** + * Returns true if the HTTP header is defined. + * + * @param string $key The HTTP header + * + * @return bool true if the parameter exists, false otherwise + */ + public function has($key) + { + return array_key_exists(strtr(strtolower($key), '_', '-'), $this->headers); + } + + /** + * Returns true if the given HTTP header contains the given value. + * + * @param string $key The HTTP header name + * @param string $value The HTTP value + * + * @return bool true if the value is contained in the header, false otherwise + */ + public function contains($key, $value) + { + return in_array($value, $this->get($key, null, false)); + } + + /** + * Removes a header. + * + * @param string $key The HTTP header name + */ + public function remove($key) + { + $key = strtr(strtolower($key), '_', '-'); + + unset($this->headers[$key]); + + if ('cache-control' === $key) { + $this->cacheControl = array(); + } + } + + /** + * Returns the HTTP header value converted to a date. + * + * @param string $key The parameter key + * @param \DateTime $default The default value + * + * @return null|\DateTime The parsed DateTime or the default value if the header does not exist + * + * @throws \RuntimeException When the HTTP header is not parseable + */ + public function getDate($key, \DateTime $default = null) + { + if (null === $value = $this->get($key)) { + return $default; + } + + if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) { + throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); + } + + return $date; + } + + /** + * Adds a custom Cache-Control directive. + * + * @param string $key The Cache-Control directive name + * @param mixed $value The Cache-Control directive value + */ + public function addCacheControlDirective($key, $value = true) + { + $this->cacheControl[$key] = $value; + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + /** + * Returns true if the Cache-Control directive is defined. + * + * @param string $key The Cache-Control directive + * + * @return bool true if the directive exists, false otherwise + */ + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl); + } + + /** + * Returns a Cache-Control directive value by name. + * + * @param string $key The directive name + * + * @return mixed|null The directive value if defined, null otherwise + */ + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; + } + + /** + * Removes a Cache-Control directive. + * + * @param string $key The Cache-Control directive + */ + public function removeCacheControlDirective($key) + { + unset($this->cacheControl[$key]); + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + /** + * Returns an iterator for headers. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + + /** + * Returns the number of headers. + * + * @return int The number of headers + */ + public function count() + { + return count($this->headers); + } + + protected function getCacheControlHeader() + { + $parts = array(); + ksort($this->cacheControl); + foreach ($this->cacheControl as $key => $value) { + if (true === $value) { + $parts[] = $key; + } else { + if (preg_match('#[^a-zA-Z0-9._-]#', $value)) { + $value = '"'.$value.'"'; + } + + $parts[] = "$key=$value"; + } + } + + return implode(', ', $parts); + } + + /** + * Parses a Cache-Control HTTP header. + * + * @param string $header The value of the Cache-Control HTTP header + * + * @return array An array representing the attribute values + */ + protected function parseCacheControl($header) + { + $cacheControl = array(); + preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $cacheControl[strtolower($match[1])] = isset($match[3]) ? $match[3] : (isset($match[2]) ? $match[2] : true); + } + + return $cacheControl; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/IpUtils.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/IpUtils.php new file mode 100644 index 0000000..e08301e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Http utility functions. + * + * @author Fabien Potencier + */ +class IpUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets. + * + * @param string $requestIp IP to check + * @param string|array $ips List of IPs or subnets (can be a string if only a single one) + * + * @return bool Whether the IP is valid + */ + public static function checkIp($requestIp, $ips) + { + if (!is_array($ips)) { + $ips = array($ips); + } + + $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4'; + + foreach ($ips as $ip) { + if (self::$method($requestIp, $ip)) { + return true; + } + } + + return false; + } + + /** + * Compares two IPv4 addresses. + * In case a subnet is given, it checks if it contains the request IP. + * + * @param string $requestIp IPv4 address to check + * @param string $ip IPv4 address or subnet in CIDR notation + * + * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet. + */ + public static function checkIp4($requestIp, $ip) + { + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ($netmask === '0') { + // Ensure IP is valid - using ip2long below implicitly validates, but we need to do it manually here + return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); + } + + if ($netmask < 0 || $netmask > 32) { + return false; + } + } else { + $address = $ip; + $netmask = 32; + } + + return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask); + } + + /** + * Compares two IPv6 addresses. + * In case a subnet is given, it checks if it contains the request IP. + * + * @author David Soria Parra + * + * @see https://github.com/dsp/v6tools + * + * @param string $requestIp IPv6 address to check + * @param string $ip IPv6 address or subnet in CIDR notation + * + * @return bool Whether the IP is valid + * + * @throws \RuntimeException When IPV6 support is not enabled + */ + public static function checkIp6($requestIp, $ip) + { + if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) { + throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); + } + + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ($netmask < 1 || $netmask > 128) { + return false; + } + } else { + $address = $ip; + $netmask = 128; + } + + $bytesAddr = unpack('n*', inet_pton($address)); + $bytesTest = unpack('n*', inet_pton($requestIp)); + + for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) { + $left = $netmask - 16 * ($i - 1); + $left = ($left <= 16) ? $left : 16; + $mask = ~(0xffff >> $left) & 0xffff; + if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/JsonResponse.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/JsonResponse.php new file mode 100644 index 0000000..075e500 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/JsonResponse.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response in JSON format. + * + * Note that this class does not force the returned JSON content to be an + * object. It is however recommended that you do return an object as it + * protects yourself against XSSI and JSON-JavaScript Hijacking. + * + * @see https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside + * + * @author Igor Wiedler + */ +class JsonResponse extends Response +{ + protected $data; + protected $callback; + + // Encode <, >, ', &, and " for RFC4627-compliant JSON, which may also be embedded into HTML. + // 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT + protected $encodingOptions = 15; + + /** + * Constructor. + * + * @param mixed $data The response data + * @param int $status The response status code + * @param array $headers An array of response headers + */ + public function __construct($data = null, $status = 200, $headers = array()) + { + parent::__construct('', $status, $headers); + + if (null === $data) { + $data = new \ArrayObject(); + } + + $this->setData($data); + } + + /** + * {@inheritdoc} + */ + public static function create($data = null, $status = 200, $headers = array()) + { + return new static($data, $status, $headers); + } + + /** + * Sets the JSONP callback. + * + * @param string|null $callback The JSONP callback or null to use none + * + * @return JsonResponse + * + * @throws \InvalidArgumentException When the callback name is not valid + */ + public function setCallback($callback = null) + { + if (null !== $callback) { + // taken from http://www.geekality.net/2011/08/03/valid-javascript-identifier/ + $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u'; + $parts = explode('.', $callback); + foreach ($parts as $part) { + if (!preg_match($pattern, $part)) { + throw new \InvalidArgumentException('The callback name is not valid.'); + } + } + } + + $this->callback = $callback; + + return $this->update(); + } + + /** + * Sets the data to be sent as JSON. + * + * @param mixed $data + * + * @return JsonResponse + * + * @throws \InvalidArgumentException + */ + public function setData($data = array()) + { + if (defined('HHVM_VERSION')) { + // HHVM does not trigger any warnings and let exceptions + // thrown from a JsonSerializable object pass through. + // If only PHP did the same... + $data = json_encode($data, $this->encodingOptions); + } else { + try { + // PHP 5.4 and up wrap exceptions thrown by JsonSerializable + // objects in a new exception that needs to be removed. + // Fortunately, PHP 5.5 and up do not trigger any warning anymore. + $data = json_encode($data, $this->encodingOptions); + } catch (\Exception $e) { + if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) { + throw $e->getPrevious() ?: $e; + } + throw $e; + } + } + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new \InvalidArgumentException(json_last_error_msg()); + } + + $this->data = $data; + + return $this->update(); + } + + /** + * Returns options used while encoding data to JSON. + * + * @return int + */ + public function getEncodingOptions() + { + return $this->encodingOptions; + } + + /** + * Sets options used while encoding data to JSON. + * + * @param int $encodingOptions + * + * @return JsonResponse + */ + public function setEncodingOptions($encodingOptions) + { + $this->encodingOptions = (int) $encodingOptions; + + return $this->setData(json_decode($this->data)); + } + + /** + * Updates the content and headers according to the JSON data and callback. + * + * @return JsonResponse + */ + protected function update() + { + if (null !== $this->callback) { + // Not using application/javascript for compatibility reasons with older browsers. + $this->headers->set('Content-Type', 'text/javascript'); + + return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data)); + } + + // Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback) + // in order to not overwrite a custom definition. + if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) { + $this->headers->set('Content-Type', 'application/json'); + } + + return $this->setContent($this->data); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ParameterBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ParameterBag.php new file mode 100644 index 0000000..6402602 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ParameterBag.php @@ -0,0 +1,240 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ParameterBag is a container for key/value pairs. + * + * @author Fabien Potencier + */ +class ParameterBag implements \IteratorAggregate, \Countable +{ + /** + * Parameter storage. + * + * @var array + */ + protected $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters An array of parameters + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $key The key + * @param mixed $default The default value if the parameter key does not exist + * + * @return mixed + * + * @throws \InvalidArgumentException + */ + public function get($key, $default = null) + { + return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; + } + + /** + * Sets a parameter by name. + * + * @param string $key The key + * @param mixed $value The value + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key The key + * + * @return bool true if the parameter exists, false otherwise + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key The key + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * Returns the alphabetic characters of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getAlpha($key, $default = '') + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default)); + } + + /** + * Returns the alphabetic characters and digits of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getAlnum($key, $default = '') + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default)); + } + + /** + * Returns the digits of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getDigits($key, $default = '') + { + // we need to remove - and + because they're allowed in the filter + return str_replace(array('-', '+'), '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT)); + } + + /** + * Returns the parameter value converted to integer. + * + * @param string $key The parameter key + * @param int $default The default value if the parameter key does not exist + * + * @return int The filtered value + */ + public function getInt($key, $default = 0) + { + return (int) $this->get($key, $default); + } + + /** + * Returns the parameter value converted to boolean. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * + * @return bool The filtered value + */ + public function getBoolean($key, $default = false) + { + return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN); + } + + /** + * Filter key. + * + * @param string $key Key. + * @param mixed $default Default = null. + * @param int $filter FILTER_* constant. + * @param mixed $options Filter options. + * + * @see http://php.net/manual/en/function.filter-var.php + * + * @return mixed + */ + public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array()) + { + $value = $this->get($key, $default); + + // Always turn $options into an array - this allows filter_var option shortcuts. + if (!is_array($options) && $options) { + $options = array('flags' => $options); + } + + // Add a convenience check for arrays. + if (is_array($value) && !isset($options['flags'])) { + $options['flags'] = FILTER_REQUIRE_ARRAY; + } + + return filter_var($value, $filter, $options); + } + + /** + * Returns an iterator for parameters. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->parameters); + } + + /** + * Returns the number of parameters. + * + * @return int The number of parameters + */ + public function count() + { + return count($this->parameters); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/README.md b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/README.md new file mode 100644 index 0000000..a7dba39 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/README.md @@ -0,0 +1,44 @@ +HttpFoundation Component +======================== + +HttpFoundation defines an object-oriented layer for the HTTP specification. + +It provides an abstraction for requests, responses, uploaded files, cookies, +sessions, ... + +In this example, we get a Request object from the current PHP global +variables: + +```php +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +$request = Request::createFromGlobals(); +echo $request->getPathInfo(); +``` + +You can also create a Request directly -- that's interesting for unit testing: + +```php +$request = Request::create('/?foo=bar', 'GET'); +echo $request->getPathInfo(); +``` + +And here is how to create and send a Response: + +```php +$response = new Response('Not Found', 404, array('Content-Type' => 'text/plain')); +$response->send(); +``` + +The Request and the Response classes have many other methods that implement +the HTTP specification. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/HttpFoundation/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RedirectResponse.php new file mode 100644 index 0000000..a21eb5c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RedirectResponse represents an HTTP response doing a redirect. + * + * @author Fabien Potencier + */ +class RedirectResponse extends Response +{ + protected $targetUrl; + + /** + * Creates a redirect response so that it conforms to the rules defined for a redirect status code. + * + * @param string $url The URL to redirect to + * @param int $status The status code (302 by default) + * @param array $headers The headers (Location is always set to the given URL) + * + * @throws \InvalidArgumentException + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3 + */ + public function __construct($url, $status = 302, $headers = array()) + { + if (empty($url)) { + throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + parent::__construct('', $status, $headers); + + $this->setTargetUrl($url); + + if (!$this->isRedirect()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status)); + } + } + + /** + * {@inheritdoc} + */ + public static function create($url = '', $status = 302, $headers = array()) + { + return new static($url, $status, $headers); + } + + /** + * Returns the target URL. + * + * @return string target URL + */ + public function getTargetUrl() + { + return $this->targetUrl; + } + + /** + * Sets the redirect target of this response. + * + * @param string $url The URL to redirect to + * + * @return RedirectResponse The current response. + * + * @throws \InvalidArgumentException + */ + public function setTargetUrl($url) + { + if (empty($url)) { + throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + $this->targetUrl = $url; + + $this->setContent( + sprintf(' + + + + + + Redirecting to %1$s + + + Redirecting to %1$s. + +', htmlspecialchars($url, ENT_QUOTES, 'UTF-8'))); + + $this->headers->set('Location', $url); + + return $this; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Request.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Request.php new file mode 100644 index 0000000..77f3d15 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Request.php @@ -0,0 +1,1913 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +/** + * Request represents an HTTP request. + * + * The methods dealing with URL accept / return a raw path (% encoded): + * * getBasePath + * * getBaseUrl + * * getPathInfo + * * getRequestUri + * * getUri + * * getUriForPath + * + * @author Fabien Potencier + */ +class Request +{ + const HEADER_FORWARDED = 'forwarded'; + const HEADER_CLIENT_IP = 'client_ip'; + const HEADER_CLIENT_HOST = 'client_host'; + const HEADER_CLIENT_PROTO = 'client_proto'; + const HEADER_CLIENT_PORT = 'client_port'; + + const METHOD_HEAD = 'HEAD'; + const METHOD_GET = 'GET'; + const METHOD_POST = 'POST'; + const METHOD_PUT = 'PUT'; + const METHOD_PATCH = 'PATCH'; + const METHOD_DELETE = 'DELETE'; + const METHOD_PURGE = 'PURGE'; + const METHOD_OPTIONS = 'OPTIONS'; + const METHOD_TRACE = 'TRACE'; + const METHOD_CONNECT = 'CONNECT'; + + /** + * @var string[] + */ + protected static $trustedProxies = array(); + + /** + * @var string[] + */ + protected static $trustedHostPatterns = array(); + + /** + * @var string[] + */ + protected static $trustedHosts = array(); + + /** + * Names for headers that can be trusted when + * using trusted proxies. + * + * The FORWARDED header is the standard as of rfc7239. + * + * The other headers are non-standard, but widely used + * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). + */ + protected static $trustedHeaders = array( + self::HEADER_FORWARDED => 'FORWARDED', + self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', + self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', + self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', + self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', + ); + + protected static $httpMethodParameterOverride = false; + + /** + * Custom parameters. + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $attributes; + + /** + * Request body parameters ($_POST). + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $request; + + /** + * Query string parameters ($_GET). + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $query; + + /** + * Server and execution environment parameters ($_SERVER). + * + * @var \Symfony\Component\HttpFoundation\ServerBag + */ + public $server; + + /** + * Uploaded files ($_FILES). + * + * @var \Symfony\Component\HttpFoundation\FileBag + */ + public $files; + + /** + * Cookies ($_COOKIE). + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $cookies; + + /** + * Headers (taken from the $_SERVER). + * + * @var \Symfony\Component\HttpFoundation\HeaderBag + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var array + */ + protected $languages; + + /** + * @var array + */ + protected $charsets; + + /** + * @var array + */ + protected $encodings; + + /** + * @var array + */ + protected $acceptableContentTypes; + + /** + * @var string + */ + protected $pathInfo; + + /** + * @var string + */ + protected $requestUri; + + /** + * @var string + */ + protected $baseUrl; + + /** + * @var string + */ + protected $basePath; + + /** + * @var string + */ + protected $method; + + /** + * @var string + */ + protected $format; + + /** + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + + /** + * @var string + */ + protected $locale; + + /** + * @var string + */ + protected $defaultLocale = 'en'; + + /** + * @var array + */ + protected static $formats; + + protected static $requestFactory; + + /** + * Constructor. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string|resource $content The raw body data + */ + public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); + } + + /** + * Sets the parameters for this request. + * + * This method also re-initializes all properties. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string|resource $content The raw body data + */ + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->request = new ParameterBag($request); + $this->query = new ParameterBag($query); + $this->attributes = new ParameterBag($attributes); + $this->cookies = new ParameterBag($cookies); + $this->files = new FileBag($files); + $this->server = new ServerBag($server); + $this->headers = new HeaderBag($this->server->getHeaders()); + + $this->content = $content; + $this->languages = null; + $this->charsets = null; + $this->encodings = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; + } + + /** + * Creates a new request with values from PHP's super globals. + * + * @return Request A new request + */ + public static function createFromGlobals() + { + // With the php's bug #66606, the php's built-in web server + // stores the Content-Type and Content-Length header values in + // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. + $server = $_SERVER; + if ('cli-server' === php_sapi_name()) { + if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) { + $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH']; + } + if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { + $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE']; + } + } + + $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); + + if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') + && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) + ) { + parse_str($request->getContent(), $data); + $request->request = new ParameterBag($data); + } + + return $request; + } + + /** + * Creates a Request based on a given URI and configuration. + * + * The information contained in the URI always take precedence + * over the other information (server and parameters). + * + * @param string $uri The URI + * @param string $method The HTTP method + * @param array $parameters The query (GET) or request (POST) parameters + * @param array $cookies The request cookies ($_COOKIE) + * @param array $files The request files ($_FILES) + * @param array $server The server parameters ($_SERVER) + * @param string $content The raw body data + * + * @return Request A Request instance + */ + public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) + { + $server = array_replace(array( + 'SERVER_NAME' => 'localhost', + 'SERVER_PORT' => 80, + 'HTTP_HOST' => 'localhost', + 'HTTP_USER_AGENT' => 'Symfony/2.X', + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', + 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', + 'REMOTE_ADDR' => '127.0.0.1', + 'SCRIPT_NAME' => '', + 'SCRIPT_FILENAME' => '', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REQUEST_TIME' => time(), + ), $server); + + $server['PATH_INFO'] = ''; + $server['REQUEST_METHOD'] = strtoupper($method); + + $components = parse_url($uri); + if (isset($components['host'])) { + $server['SERVER_NAME'] = $components['host']; + $server['HTTP_HOST'] = $components['host']; + } + + if (isset($components['scheme'])) { + if ('https' === $components['scheme']) { + $server['HTTPS'] = 'on'; + $server['SERVER_PORT'] = 443; + } else { + unset($server['HTTPS']); + $server['SERVER_PORT'] = 80; + } + } + + if (isset($components['port'])) { + $server['SERVER_PORT'] = $components['port']; + $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port']; + } + + if (isset($components['user'])) { + $server['PHP_AUTH_USER'] = $components['user']; + } + + if (isset($components['pass'])) { + $server['PHP_AUTH_PW'] = $components['pass']; + } + + if (!isset($components['path'])) { + $components['path'] = '/'; + } + + switch (strtoupper($method)) { + case 'POST': + case 'PUT': + case 'DELETE': + if (!isset($server['CONTENT_TYPE'])) { + $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + } + // no break + case 'PATCH': + $request = $parameters; + $query = array(); + break; + default: + $request = array(); + $query = $parameters; + break; + } + + $queryString = ''; + if (isset($components['query'])) { + parse_str(html_entity_decode($components['query']), $qs); + + if ($query) { + $query = array_replace($qs, $query); + $queryString = http_build_query($query, '', '&'); + } else { + $query = $qs; + $queryString = $components['query']; + } + } elseif ($query) { + $queryString = http_build_query($query, '', '&'); + } + + $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : ''); + $server['QUERY_STRING'] = $queryString; + + return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content); + } + + /** + * Sets a callable able to create a Request instance. + * + * This is mainly useful when you need to override the Request class + * to keep BC with an existing system. It should not be used for any + * other purpose. + * + * @param callable|null $callable A PHP callable + */ + public static function setFactory($callable) + { + self::$requestFactory = $callable; + } + + /** + * Clones a request and overrides some of its parameters. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * + * @return Request The duplicated request + */ + public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + { + $dup = clone $this; + if ($query !== null) { + $dup->query = new ParameterBag($query); + } + if ($request !== null) { + $dup->request = new ParameterBag($request); + } + if ($attributes !== null) { + $dup->attributes = new ParameterBag($attributes); + } + if ($cookies !== null) { + $dup->cookies = new ParameterBag($cookies); + } + if ($files !== null) { + $dup->files = new FileBag($files); + } + if ($server !== null) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $dup->languages = null; + $dup->charsets = null; + $dup->encodings = null; + $dup->acceptableContentTypes = null; + $dup->pathInfo = null; + $dup->requestUri = null; + $dup->baseUrl = null; + $dup->basePath = null; + $dup->method = null; + $dup->format = null; + + if (!$dup->get('_format') && $this->get('_format')) { + $dup->attributes->set('_format', $this->get('_format')); + } + + if (!$dup->getRequestFormat(null)) { + $dup->setRequestFormat($this->getRequestFormat(null)); + } + + return $dup; + } + + /** + * Clones the current request. + * + * Note that the session is not cloned as duplicated requests + * are most of the time sub-requests of the main one. + */ + public function __clone() + { + $this->query = clone $this->query; + $this->request = clone $this->request; + $this->attributes = clone $this->attributes; + $this->cookies = clone $this->cookies; + $this->files = clone $this->files; + $this->server = clone $this->server; + $this->headers = clone $this->headers; + } + + /** + * Returns the request as a string. + * + * @return string The request + */ + public function __toString() + { + try { + $content = $this->getContent(); + } catch (\LogicException $e) { + return trigger_error($e, E_USER_ERROR); + } + + return + sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". + $this->headers."\r\n". + $content; + } + + /** + * Overrides the PHP global variables according to this request instance. + * + * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. + * $_FILES is never overridden, see rfc1867 + */ + public function overrideGlobals() + { + $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), null, '&'))); + + $_GET = $this->query->all(); + $_POST = $this->request->all(); + $_SERVER = $this->server->all(); + $_COOKIE = $this->cookies->all(); + + foreach ($this->headers->all() as $key => $value) { + $key = strtoupper(str_replace('-', '_', $key)); + if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { + $_SERVER[$key] = implode(', ', $value); + } else { + $_SERVER['HTTP_'.$key] = implode(', ', $value); + } + } + + $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE); + + $requestOrder = ini_get('request_order') ?: ini_get('variables_order'); + $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp'; + + $_REQUEST = array(); + foreach (str_split($requestOrder) as $order) { + $_REQUEST = array_merge($_REQUEST, $request[$order]); + } + } + + /** + * Sets a list of trusted proxies. + * + * You should only list the reverse proxies that you manage directly. + * + * @param array $proxies A list of trusted proxies + */ + public static function setTrustedProxies(array $proxies) + { + self::$trustedProxies = $proxies; + } + + /** + * Gets the list of trusted proxies. + * + * @return array An array of trusted proxies. + */ + public static function getTrustedProxies() + { + return self::$trustedProxies; + } + + /** + * Sets a list of trusted host patterns. + * + * You should only list the hosts you manage using regexs. + * + * @param array $hostPatterns A list of trusted host patterns + */ + public static function setTrustedHosts(array $hostPatterns) + { + self::$trustedHostPatterns = array_map(function ($hostPattern) { + return sprintf('#%s#i', $hostPattern); + }, $hostPatterns); + // we need to reset trusted hosts on trusted host patterns change + self::$trustedHosts = array(); + } + + /** + * Gets the list of trusted host patterns. + * + * @return array An array of trusted host patterns. + */ + public static function getTrustedHosts() + { + return self::$trustedHostPatterns; + } + + /** + * Sets the name for trusted headers. + * + * The following header keys are supported: + * + * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp()) + * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getHost()) + * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getPort()) + * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure()) + * + * Setting an empty value allows to disable the trusted header for the given key. + * + * @param string $key The header key + * @param string $value The header name + * + * @throws \InvalidArgumentException + */ + public static function setTrustedHeaderName($key, $value) + { + if (!array_key_exists($key, self::$trustedHeaders)) { + throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); + } + + self::$trustedHeaders[$key] = $value; + } + + /** + * Gets the trusted proxy header name. + * + * @param string $key The header key + * + * @return string The header name + * + * @throws \InvalidArgumentException + */ + public static function getTrustedHeaderName($key) + { + if (!array_key_exists($key, self::$trustedHeaders)) { + throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key)); + } + + return self::$trustedHeaders[$key]; + } + + /** + * Normalizes a query string. + * + * It builds a normalized query string, where keys/value pairs are alphabetized, + * have consistent escaping and unneeded delimiters are removed. + * + * @param string $qs Query string + * + * @return string A normalized query string for the Request + */ + public static function normalizeQueryString($qs) + { + if ('' == $qs) { + return ''; + } + + $parts = array(); + $order = array(); + + foreach (explode('&', $qs) as $param) { + if ('' === $param || '=' === $param[0]) { + // Ignore useless delimiters, e.g. "x=y&". + // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway. + // PHP also does not include them when building _GET. + continue; + } + + $keyValuePair = explode('=', $param, 2); + + // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded). + // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. This is why we use urldecode and then normalize to + // RFC 3986 with rawurlencode. + $parts[] = isset($keyValuePair[1]) ? + rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) : + rawurlencode(urldecode($keyValuePair[0])); + $order[] = urldecode($keyValuePair[0]); + } + + array_multisort($order, SORT_ASC, $parts); + + return implode('&', $parts); + } + + /** + * Enables support for the _method request parameter to determine the intended HTTP method. + * + * Be warned that enabling this feature might lead to CSRF issues in your code. + * Check that you are using CSRF tokens when required. + * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered + * and used to send a "PUT" or "DELETE" request via the _method request parameter. + * If these methods are not protected against CSRF, this presents a possible vulnerability. + * + * The HTTP method can only be overridden when the real HTTP method is POST. + */ + public static function enableHttpMethodParameterOverride() + { + self::$httpMethodParameterOverride = true; + } + + /** + * Checks whether support for the _method request parameter is enabled. + * + * @return bool True when the _method request parameter is enabled, false otherwise + */ + public static function getHttpMethodParameterOverride() + { + return self::$httpMethodParameterOverride; + } + + /** + * Gets a "parameter" value from any bag. + * + * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the + * flexibility in controllers, it is better to explicitly get request parameters from the appropriate + * public property instead (attributes, query, request). + * + * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY + * + * @param string $key the key + * @param mixed $default the default value + * + * @return mixed + */ + public function get($key, $default = null) + { + if ($this !== $result = $this->attributes->get($key, $this)) { + return $result; + } + + if ($this !== $result = $this->query->get($key, $this)) { + return $result; + } + + if ($this !== $result = $this->request->get($key, $this)) { + return $result; + } + + return $default; + } + + /** + * Gets the Session. + * + * @return SessionInterface|null The session + */ + public function getSession() + { + return $this->session; + } + + /** + * Whether the request contains a Session which was started in one of the + * previous requests. + * + * @return bool + */ + public function hasPreviousSession() + { + // the check for $this->session avoids malicious users trying to fake a session cookie with proper name + return $this->hasSession() && $this->cookies->has($this->session->getName()); + } + + /** + * Whether the request contains a Session object. + * + * This method does not give any information about the state of the session object, + * like whether the session is started or not. It is just a way to check if this Request + * is associated with a Session instance. + * + * @return bool true when the Request contains a Session object, false otherwise + */ + public function hasSession() + { + return null !== $this->session; + } + + /** + * Sets the Session. + * + * @param SessionInterface $session The Session + */ + public function setSession(SessionInterface $session) + { + $this->session = $session; + } + + /** + * Returns the client IP addresses. + * + * In the returned array the most trusted IP address is first, and the + * least trusted one last. The "real" client IP address is the last one, + * but this is also the least trusted one. Trusted proxies are stripped. + * + * Use this method carefully; you should use getClientIp() instead. + * + * @return array The client IP addresses + * + * @see getClientIp() + */ + public function getClientIps() + { + $clientIps = array(); + $ip = $this->server->get('REMOTE_ADDR'); + + if (!$this->isFromTrustedProxy()) { + return array($ip); + } + + if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { + $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); + preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches); + $clientIps = $matches[3]; + } elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) { + $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP]))); + } + + $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from + $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies + + foreach ($clientIps as $key => $clientIp) { + // Remove port (unfortunately, it does happen) + if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { + $clientIps[$key] = $clientIp = $match[1]; + } + + if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { + unset($clientIps[$key]); + } + } + + // Now the IP chain contains only untrusted proxies and the client IP + return $clientIps ? array_reverse($clientIps) : array($ip); + } + + /** + * Returns the client IP address. + * + * This method can read the client IP address from the "X-Forwarded-For" header + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" + * header value is a comma+space separated list of IP addresses, the left-most + * being the original client, and each successive proxy that passed the request + * adding the IP address where it received the request from. + * + * If your reverse proxy uses a different header name than "X-Forwarded-For", + * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with + * the "client-ip" key. + * + * @return string The client IP address + * + * @see getClientIps() + * @see http://en.wikipedia.org/wiki/X-Forwarded-For + */ + public function getClientIp() + { + $ipAddresses = $this->getClientIps(); + + return $ipAddresses[0]; + } + + /** + * Returns current script name. + * + * @return string + */ + public function getScriptName() + { + return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', '')); + } + + /** + * Returns the path being requested relative to the executed script. + * + * The path info always starts with a /. + * + * Suppose this request is instantiated from /mysite on localhost: + * + * * http://localhost/mysite returns an empty string + * * http://localhost/mysite/about returns '/about' + * * http://localhost/mysite/enco%20ded returns '/enco%20ded' + * * http://localhost/mysite/about?var=1 returns '/about' + * + * @return string The raw path (i.e. not urldecoded) + */ + public function getPathInfo() + { + if (null === $this->pathInfo) { + $this->pathInfo = $this->preparePathInfo(); + } + + return $this->pathInfo; + } + + /** + * Returns the root path from which this request is executed. + * + * Suppose that an index.php file instantiates this request object: + * + * * http://localhost/index.php returns an empty string + * * http://localhost/index.php/page returns an empty string + * * http://localhost/web/index.php returns '/web' + * * http://localhost/we%20b/index.php returns '/we%20b' + * + * @return string The raw path (i.e. not urldecoded) + */ + public function getBasePath() + { + if (null === $this->basePath) { + $this->basePath = $this->prepareBasePath(); + } + + return $this->basePath; + } + + /** + * Returns the root URL from which this request is executed. + * + * The base URL never ends with a /. + * + * This is similar to getBasePath(), except that it also includes the + * script filename (e.g. index.php) if one exists. + * + * @return string The raw URL (i.e. not urldecoded) + */ + public function getBaseUrl() + { + if (null === $this->baseUrl) { + $this->baseUrl = $this->prepareBaseUrl(); + } + + return $this->baseUrl; + } + + /** + * Gets the request's scheme. + * + * @return string + */ + public function getScheme() + { + return $this->isSecure() ? 'https' : 'http'; + } + + /** + * Returns the port on which the request is made. + * + * This method can read the client port from the "X-Forwarded-Port" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Port" header must contain the client port. + * + * If your reverse proxy uses a different header name than "X-Forwarded-Port", + * configure it via "setTrustedHeaderName()" with the "client-port" key. + * + * @return string + */ + public function getPort() + { + if ($this->isFromTrustedProxy()) { + if (self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) { + return $port; + } + + if (self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && 'https' === $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO], 'http')) { + return 443; + } + } + + if ($host = $this->headers->get('HOST')) { + if ($host[0] === '[') { + $pos = strpos($host, ':', strrpos($host, ']')); + } else { + $pos = strrpos($host, ':'); + } + + if (false !== $pos) { + return (int) substr($host, $pos + 1); + } + + return 'https' === $this->getScheme() ? 443 : 80; + } + + return $this->server->get('SERVER_PORT'); + } + + /** + * Returns the user. + * + * @return string|null + */ + public function getUser() + { + return $this->headers->get('PHP_AUTH_USER'); + } + + /** + * Returns the password. + * + * @return string|null + */ + public function getPassword() + { + return $this->headers->get('PHP_AUTH_PW'); + } + + /** + * Gets the user info. + * + * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server + */ + public function getUserInfo() + { + $userinfo = $this->getUser(); + + $pass = $this->getPassword(); + if ('' != $pass) { + $userinfo .= ":$pass"; + } + + return $userinfo; + } + + /** + * Returns the HTTP host being requested. + * + * The port name will be appended to the host if it's non-standard. + * + * @return string + */ + public function getHttpHost() + { + $scheme = $this->getScheme(); + $port = $this->getPort(); + + if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) { + return $this->getHost(); + } + + return $this->getHost().':'.$port; + } + + /** + * Returns the requested URI (path and query string). + * + * @return string The raw URI (i.e. not URI decoded) + */ + public function getRequestUri() + { + if (null === $this->requestUri) { + $this->requestUri = $this->prepareRequestUri(); + } + + return $this->requestUri; + } + + /** + * Gets the scheme and HTTP host. + * + * If the URL was called with basic authentication, the user + * and the password are not added to the generated string. + * + * @return string The scheme and HTTP host + */ + public function getSchemeAndHttpHost() + { + return $this->getScheme().'://'.$this->getHttpHost(); + } + + /** + * Generates a normalized URI (URL) for the Request. + * + * @return string A normalized URI (URL) for the Request + * + * @see getQueryString() + */ + public function getUri() + { + if (null !== $qs = $this->getQueryString()) { + $qs = '?'.$qs; + } + + return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs; + } + + /** + * Generates a normalized URI for the given path. + * + * @param string $path A path to use instead of the current one + * + * @return string The normalized URI for the path + */ + public function getUriForPath($path) + { + return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path; + } + + /** + * Returns the path as relative reference from the current Request path. + * + * Only the URIs path component (no schema, host etc.) is relevant and must be given. + * Both paths must be absolute and not contain relative parts. + * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. + * Furthermore, they can be used to reduce the link size in documents. + * + * Example target paths, given a base path of "/a/b/c/d": + * - "/a/b/c/d" -> "" + * - "/a/b/c/" -> "./" + * - "/a/b/" -> "../" + * - "/a/b/c/other" -> "other" + * - "/a/x/y" -> "../../x/y" + * + * @param string $path The target path + * + * @return string The relative target path + */ + public function getRelativeUriForPath($path) + { + // be sure that we are dealing with an absolute path + if (!isset($path[0]) || '/' !== $path[0]) { + return $path; + } + + if ($path === $basePath = $this->getPathInfo()) { + return ''; + } + + $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); + $targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path); + array_pop($sourceDirs); + $targetFile = array_pop($targetDirs); + + foreach ($sourceDirs as $i => $dir) { + if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { + unset($sourceDirs[$i], $targetDirs[$i]); + } else { + break; + } + } + + $targetDirs[] = $targetFile; + $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); + + // A reference to the same base directory or an empty subdirectory must be prefixed with "./". + // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used + // as the first segment of a relative-path reference, as it would be mistaken for a scheme name + // (see http://tools.ietf.org/html/rfc3986#section-4.2). + return !isset($path[0]) || '/' === $path[0] + || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) + ? "./$path" : $path; + } + + /** + * Generates the normalized query string for the Request. + * + * It builds a normalized query string, where keys/value pairs are alphabetized + * and have consistent escaping. + * + * @return string|null A normalized query string for the Request + */ + public function getQueryString() + { + $qs = static::normalizeQueryString($this->server->get('QUERY_STRING')); + + return '' === $qs ? null : $qs; + } + + /** + * Checks whether the request is secure or not. + * + * This method can read the client protocol from the "X-Forwarded-Proto" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". + * + * If your reverse proxy uses a different header name than "X-Forwarded-Proto" + * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with + * the "client-proto" key. + * + * @return bool + */ + public function isSecure() + { + if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) { + return in_array(strtolower(current(explode(',', $proto))), array('https', 'on', 'ssl', '1')); + } + + $https = $this->server->get('HTTPS'); + + return !empty($https) && 'off' !== strtolower($https); + } + + /** + * Returns the host name. + * + * This method can read the client host name from the "X-Forwarded-Host" header + * when trusted proxies were set via "setTrustedProxies()". + * + * The "X-Forwarded-Host" header must contain the client host name. + * + * If your reverse proxy uses a different header name than "X-Forwarded-Host", + * configure it via "setTrustedHeaderName()" with the "client-host" key. + * + * @return string + * + * @throws \UnexpectedValueException when the host name is invalid + */ + public function getHost() + { + if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) { + $elements = explode(',', $host); + + $host = $elements[count($elements) - 1]; + } elseif (!$host = $this->headers->get('HOST')) { + if (!$host = $this->server->get('SERVER_NAME')) { + $host = $this->server->get('SERVER_ADDR', ''); + } + } + + // trim and remove port number from host + // host is lowercase as per RFC 952/2181 + $host = strtolower(preg_replace('/:\d+$/', '', trim($host))); + + // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user) + // check that it does not contain forbidden characters (see RFC 952 and RFC 2181) + // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names + if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) { + throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host)); + } + + if (count(self::$trustedHostPatterns) > 0) { + // to avoid host header injection attacks, you should provide a list of trusted host patterns + + if (in_array($host, self::$trustedHosts)) { + return $host; + } + + foreach (self::$trustedHostPatterns as $pattern) { + if (preg_match($pattern, $host)) { + self::$trustedHosts[] = $host; + + return $host; + } + } + + throw new \UnexpectedValueException(sprintf('Untrusted Host "%s"', $host)); + } + + return $host; + } + + /** + * Sets the request method. + * + * @param string $method + */ + public function setMethod($method) + { + $this->method = null; + $this->server->set('REQUEST_METHOD', $method); + } + + /** + * Gets the request "intended" method. + * + * If the X-HTTP-Method-Override header is set, and if the method is a POST, + * then it is used to determine the "real" intended HTTP method. + * + * The _method request parameter can also be used to determine the HTTP method, + * but only if enableHttpMethodParameterOverride() has been called. + * + * The method is always an uppercased string. + * + * @return string The request method + * + * @see getRealMethod() + */ + public function getMethod() + { + if (null === $this->method) { + $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + + if ('POST' === $this->method) { + if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) { + $this->method = strtoupper($method); + } elseif (self::$httpMethodParameterOverride) { + $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST'))); + } + } + } + + return $this->method; + } + + /** + * Gets the "real" request method. + * + * @return string The request method + * + * @see getMethod() + */ + public function getRealMethod() + { + return strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + } + + /** + * Gets the mime type associated with the format. + * + * @param string $format The format + * + * @return string The associated mime type (null if not found) + */ + public function getMimeType($format) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; + } + + /** + * Gets the format associated with the mime type. + * + * @param string $mimeType The associated mime type + * + * @return string|null The format (null if not found) + */ + public function getFormat($mimeType) + { + if (false !== $pos = strpos($mimeType, ';')) { + $mimeType = substr($mimeType, 0, $pos); + } + + if (null === static::$formats) { + static::initializeFormats(); + } + + foreach (static::$formats as $format => $mimeTypes) { + if (in_array($mimeType, (array) $mimeTypes)) { + return $format; + } + } + } + + /** + * Associates a format with mime types. + * + * @param string $format The format + * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type) + */ + public function setFormat($format, $mimeTypes) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); + } + + /** + * Gets the request format. + * + * Here is the process to determine the format: + * + * * format defined by the user (with setRequestFormat()) + * * _format request parameter + * * $default + * + * @param string $default The default format + * + * @return string The request format + */ + public function getRequestFormat($default = 'html') + { + if (null === $this->format) { + $this->format = $this->attributes->get('_format', $default); + } + + return $this->format; + } + + /** + * Sets the request format. + * + * @param string $format The request format. + */ + public function setRequestFormat($format) + { + $this->format = $format; + } + + /** + * Gets the format associated with the request. + * + * @return string|null The format (null if no content type is present) + */ + public function getContentType() + { + return $this->getFormat($this->headers->get('CONTENT_TYPE')); + } + + /** + * Sets the default locale. + * + * @param string $locale + */ + public function setDefaultLocale($locale) + { + $this->defaultLocale = $locale; + + if (null === $this->locale) { + $this->setPhpDefaultLocale($locale); + } + } + + /** + * Get the default locale. + * + * @return string + */ + public function getDefaultLocale() + { + return $this->defaultLocale; + } + + /** + * Sets the locale. + * + * @param string $locale + */ + public function setLocale($locale) + { + $this->setPhpDefaultLocale($this->locale = $locale); + } + + /** + * Get the locale. + * + * @return string + */ + public function getLocale() + { + return null === $this->locale ? $this->defaultLocale : $this->locale; + } + + /** + * Checks if the request method is of specified type. + * + * @param string $method Uppercase request method (GET, POST etc). + * + * @return bool + */ + public function isMethod($method) + { + return $this->getMethod() === strtoupper($method); + } + + /** + * Checks whether the method is safe or not. + * + * @return bool + */ + public function isMethodSafe() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + + /** + * Returns the request body content. + * + * @param bool $asResource If true, a resource will be returned + * + * @return string|resource The request body content or a resource to read the body stream. + * + * @throws \LogicException + */ + public function getContent($asResource = false) + { + $currentContentIsResource = is_resource($this->content); + if (PHP_VERSION_ID < 50600 && false === $this->content) { + throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.'); + } + + if (true === $asResource) { + if ($currentContentIsResource) { + rewind($this->content); + + return $this->content; + } + + // Content passed in parameter (test) + if (is_string($this->content)) { + $resource = fopen('php://temp', 'r+'); + fwrite($resource, $this->content); + rewind($resource); + + return $resource; + } + + $this->content = false; + + return fopen('php://input', 'rb'); + } + + if ($currentContentIsResource) { + rewind($this->content); + + return stream_get_contents($this->content); + } + + if (null === $this->content) { + $this->content = file_get_contents('php://input'); + } + + return $this->content; + } + + /** + * Gets the Etags. + * + * @return array The entity tags + */ + public function getETags() + { + return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); + } + + /** + * @return bool + */ + public function isNoCache() + { + return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); + } + + /** + * Returns the preferred language. + * + * @param array $locales An array of ordered available locales + * + * @return string|null The preferred locale + */ + public function getPreferredLanguage(array $locales = null) + { + $preferredLanguages = $this->getLanguages(); + + if (empty($locales)) { + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; + } + + if (!$preferredLanguages) { + return $locales[0]; + } + + $extendedPreferredLanguages = array(); + foreach ($preferredLanguages as $language) { + $extendedPreferredLanguages[] = $language; + if (false !== $position = strpos($language, '_')) { + $superLanguage = substr($language, 0, $position); + if (!in_array($superLanguage, $preferredLanguages)) { + $extendedPreferredLanguages[] = $superLanguage; + } + } + } + + $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales)); + + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; + } + + /** + * Gets a list of languages acceptable by the client browser. + * + * @return array Languages ordered in the user browser preferences + */ + public function getLanguages() + { + if (null !== $this->languages) { + return $this->languages; + } + + $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); + $this->languages = array(); + foreach ($languages as $lang => $acceptHeaderItem) { + if (false !== strpos($lang, '-')) { + $codes = explode('-', $lang); + if ('i' === $codes[0]) { + // Language not listed in ISO 639 that are not variants + // of any listed language, which can be registered with the + // i-prefix, such as i-cherokee + if (count($codes) > 1) { + $lang = $codes[1]; + } + } else { + for ($i = 0, $max = count($codes); $i < $max; ++$i) { + if ($i === 0) { + $lang = strtolower($codes[0]); + } else { + $lang .= '_'.strtoupper($codes[$i]); + } + } + } + } + + $this->languages[] = $lang; + } + + return $this->languages; + } + + /** + * Gets a list of charsets acceptable by the client browser. + * + * @return array List of charsets in preferable order + */ + public function getCharsets() + { + if (null !== $this->charsets) { + return $this->charsets; + } + + return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()); + } + + /** + * Gets a list of encodings acceptable by the client browser. + * + * @return array List of encodings in preferable order + */ + public function getEncodings() + { + if (null !== $this->encodings) { + return $this->encodings; + } + + return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()); + } + + /** + * Gets a list of content types acceptable by the client browser. + * + * @return array List of content types in preferable order + */ + public function getAcceptableContentTypes() + { + if (null !== $this->acceptableContentTypes) { + return $this->acceptableContentTypes; + } + + return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()); + } + + /** + * Returns true if the request is a XMLHttpRequest. + * + * It works if your JavaScript library sets an X-Requested-With HTTP header. + * It is known to work with common JavaScript frameworks: + * + * @link http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript + * + * @return bool true if the request is an XMLHttpRequest, false otherwise + */ + public function isXmlHttpRequest() + { + return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); + } + + /* + * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24) + * + * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd). + * + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + + protected function prepareRequestUri() + { + $requestUri = ''; + + if ($this->headers->has('X_ORIGINAL_URL')) { + // IIS with Microsoft Rewrite Module + $requestUri = $this->headers->get('X_ORIGINAL_URL'); + $this->headers->remove('X_ORIGINAL_URL'); + $this->server->remove('HTTP_X_ORIGINAL_URL'); + $this->server->remove('UNENCODED_URL'); + $this->server->remove('IIS_WasUrlRewritten'); + } elseif ($this->headers->has('X_REWRITE_URL')) { + // IIS with ISAPI_Rewrite + $requestUri = $this->headers->get('X_REWRITE_URL'); + $this->headers->remove('X_REWRITE_URL'); + } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') { + // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem) + $requestUri = $this->server->get('UNENCODED_URL'); + $this->server->remove('UNENCODED_URL'); + $this->server->remove('IIS_WasUrlRewritten'); + } elseif ($this->server->has('REQUEST_URI')) { + $requestUri = $this->server->get('REQUEST_URI'); + // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path + $schemeAndHttpHost = $this->getSchemeAndHttpHost(); + if (strpos($requestUri, $schemeAndHttpHost) === 0) { + $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); + } + } elseif ($this->server->has('ORIG_PATH_INFO')) { + // IIS 5.0, PHP as CGI + $requestUri = $this->server->get('ORIG_PATH_INFO'); + if ('' != $this->server->get('QUERY_STRING')) { + $requestUri .= '?'.$this->server->get('QUERY_STRING'); + } + $this->server->remove('ORIG_PATH_INFO'); + } + + // normalize the request URI to ease creating sub-requests from this request + $this->server->set('REQUEST_URI', $requestUri); + + return $requestUri; + } + + /** + * Prepares the base URL. + * + * @return string + */ + protected function prepareBaseUrl() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + + if (basename($this->server->get('SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('SCRIPT_NAME'); + } elseif (basename($this->server->get('PHP_SELF')) === $filename) { + $baseUrl = $this->server->get('PHP_SELF'); + } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility + } else { + // Backtrack up the script_filename to find the portion matching + // php_self + $path = $this->server->get('PHP_SELF', ''); + $file = $this->server->get('SCRIPT_FILENAME', ''); + $segs = explode('/', trim($file, '/')); + $segs = array_reverse($segs); + $index = 0; + $last = count($segs); + $baseUrl = ''; + do { + $seg = $segs[$index]; + $baseUrl = '/'.$seg.$baseUrl; + ++$index; + } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos); + } + + // Does the baseUrl have anything in common with the request_uri? + $requestUri = $this->getRequestUri(); + + if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { + // full $baseUrl matches + return $prefix; + } + + if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/'.DIRECTORY_SEPARATOR).'/')) { + // directory portion of $baseUrl matches + return rtrim($prefix, '/'.DIRECTORY_SEPARATOR); + } + + $truncatedRequestUri = $requestUri; + if (false !== $pos = strpos($requestUri, '?')) { + $truncatedRequestUri = substr($requestUri, 0, $pos); + } + + $basename = basename($baseUrl); + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) { + // no match whatsoever; set it blank + return ''; + } + + // If using mod_rewrite or ISAPI_Rewrite strip the script filename + // out of baseUrl. $pos !== 0 makes sure it is not matching a value + // from PATH_INFO or QUERY_STRING + if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && $pos !== 0) { + $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); + } + + return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR); + } + + /** + * Prepares the base path. + * + * @return string base path + */ + protected function prepareBasePath() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + $baseUrl = $this->getBaseUrl(); + if (empty($baseUrl)) { + return ''; + } + + if (basename($baseUrl) === $filename) { + $basePath = dirname($baseUrl); + } else { + $basePath = $baseUrl; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + $basePath = str_replace('\\', '/', $basePath); + } + + return rtrim($basePath, '/'); + } + + /** + * Prepares the path info. + * + * @return string path info + */ + protected function preparePathInfo() + { + $baseUrl = $this->getBaseUrl(); + + if (null === ($requestUri = $this->getRequestUri())) { + return '/'; + } + + $pathInfo = '/'; + + // Remove the query string from REQUEST_URI + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + + $pathInfo = substr($requestUri, strlen($baseUrl)); + if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) { + // If substr() returns false then PATH_INFO is set to an empty string + return '/'; + } elseif (null === $baseUrl) { + return $requestUri; + } + + return (string) $pathInfo; + } + + /** + * Initializes HTTP request formats. + */ + protected static function initializeFormats() + { + static::$formats = array( + 'html' => array('text/html', 'application/xhtml+xml'), + 'txt' => array('text/plain'), + 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), + 'css' => array('text/css'), + 'json' => array('application/json', 'application/x-json'), + 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), + 'rdf' => array('application/rdf+xml'), + 'atom' => array('application/atom+xml'), + 'rss' => array('application/rss+xml'), + 'form' => array('application/x-www-form-urlencoded'), + ); + } + + /** + * Sets the default PHP locale. + * + * @param string $locale + */ + private function setPhpDefaultLocale($locale) + { + // if either the class Locale doesn't exist, or an exception is thrown when + // setting the default locale, the intl module is not installed, and + // the call can be ignored: + try { + if (class_exists('Locale', false)) { + \Locale::setDefault($locale); + } + } catch (\Exception $e) { + } + } + + /* + * Returns the prefix as encoded in the string when the string starts with + * the given prefix, false otherwise. + * + * @param string $string The urlencoded string + * @param string $prefix The prefix not encoded + * + * @return string|false The prefix as it is encoded in $string, or false + */ + private function getUrlencodedPrefix($string, $prefix) + { + if (0 !== strpos(rawurldecode($string), $prefix)) { + return false; + } + + $len = strlen($prefix); + + if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) { + return $match[0]; + } + + return false; + } + + private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + if (self::$requestFactory) { + $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); + + if (!$request instanceof self) { + throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); + } + + return $request; + } + + return new static($query, $request, $attributes, $cookies, $files, $server, $content); + } + + private function isFromTrustedProxy() + { + return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcher.php new file mode 100644 index 0000000..ca094ca --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcher compares a pre-defined set of checks against a Request instance. + * + * @author Fabien Potencier + */ +class RequestMatcher implements RequestMatcherInterface +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $host; + + /** + * @var array + */ + private $methods = array(); + + /** + * @var string + */ + private $ips = array(); + + /** + * @var array + */ + private $attributes = array(); + + /** + * @var string[] + */ + private $schemes = array(); + + /** + * @param string|null $path + * @param string|null $host + * @param string|string[]|null $methods + * @param string|string[]|null $ips + * @param array $attributes + * @param string|string[]|null $schemes + */ + public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array(), $schemes = null) + { + $this->matchPath($path); + $this->matchHost($host); + $this->matchMethod($methods); + $this->matchIps($ips); + $this->matchScheme($schemes); + + foreach ($attributes as $k => $v) { + $this->matchAttribute($k, $v); + } + } + + /** + * Adds a check for the HTTP scheme. + * + * @param string|string[]|null $scheme An HTTP scheme or an array of HTTP schemes + */ + public function matchScheme($scheme) + { + $this->schemes = array_map('strtolower', (array) $scheme); + } + + /** + * Adds a check for the URL host name. + * + * @param string $regexp A Regexp + */ + public function matchHost($regexp) + { + $this->host = $regexp; + } + + /** + * Adds a check for the URL path info. + * + * @param string $regexp A Regexp + */ + public function matchPath($regexp) + { + $this->path = $regexp; + } + + /** + * Adds a check for the client IP. + * + * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIp($ip) + { + $this->matchIps($ip); + } + + /** + * Adds a check for the client IP. + * + * @param string|string[] $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIps($ips) + { + $this->ips = (array) $ips; + } + + /** + * Adds a check for the HTTP method. + * + * @param string|string[] $method An HTTP method or an array of HTTP methods + */ + public function matchMethod($method) + { + $this->methods = array_map('strtoupper', (array) $method); + } + + /** + * Adds a check for request attribute. + * + * @param string $key The request attribute name + * @param string $regexp A Regexp + */ + public function matchAttribute($key, $regexp) + { + $this->attributes[$key] = $regexp; + } + + /** + * {@inheritdoc} + */ + public function matches(Request $request) + { + if ($this->schemes && !in_array($request->getScheme(), $this->schemes)) { + return false; + } + + if ($this->methods && !in_array($request->getMethod(), $this->methods)) { + return false; + } + + foreach ($this->attributes as $key => $pattern) { + if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) { + return false; + } + } + + if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) { + return false; + } + + if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) { + return false; + } + + if (IpUtils::checkIp($request->getClientIp(), $this->ips)) { + return true; + } + + // Note to future implementors: add additional checks above the + // foreach above or else your check might not be run! + return count($this->ips) === 0; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php new file mode 100644 index 0000000..066e7e8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestMatcherInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcherInterface is an interface for strategies to match a Request. + * + * @author Fabien Potencier + */ +interface RequestMatcherInterface +{ + /** + * Decides whether the rule(s) implemented by the strategy matches the supplied request. + * + * @param Request $request The request to check for a match + * + * @return bool true if the request matches, false otherwise + */ + public function matches(Request $request); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestStack.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestStack.php new file mode 100644 index 0000000..3d9cfd0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/RequestStack.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Request stack that controls the lifecycle of requests. + * + * @author Benjamin Eberlei + */ +class RequestStack +{ + /** + * @var Request[] + */ + private $requests = array(); + + /** + * Pushes a Request on the stack. + * + * This method should generally not be called directly as the stack + * management should be taken care of by the application itself. + */ + public function push(Request $request) + { + $this->requests[] = $request; + } + + /** + * Pops the current request from the stack. + * + * This operation lets the current request go out of scope. + * + * This method should generally not be called directly as the stack + * management should be taken care of by the application itself. + * + * @return Request|null + */ + public function pop() + { + if (!$this->requests) { + return; + } + + return array_pop($this->requests); + } + + /** + * @return Request|null + */ + public function getCurrentRequest() + { + return end($this->requests) ?: null; + } + + /** + * Gets the master Request. + * + * Be warned that making your code aware of the master request + * might make it un-compatible with other features of your framework + * like ESI support. + * + * @return Request|null + */ + public function getMasterRequest() + { + if (!$this->requests) { + return; + } + + return $this->requests[0]; + } + + /** + * Returns the parent request of the current. + * + * Be warned that making your code aware of the parent request + * might make it un-compatible with other features of your framework + * like ESI support. + * + * If current Request is the master request, it returns null. + * + * @return Request|null + */ + public function getParentRequest() + { + $pos = count($this->requests) - 2; + + if (!isset($this->requests[$pos])) { + return; + } + + return $this->requests[$pos]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Response.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Response.php new file mode 100644 index 0000000..cf4d5db --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Response.php @@ -0,0 +1,1176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response. + * + * @author Fabien Potencier + */ +class Response +{ + const HTTP_CONTINUE = 100; + const HTTP_SWITCHING_PROTOCOLS = 101; + const HTTP_PROCESSING = 102; // RFC2518 + const HTTP_OK = 200; + const HTTP_CREATED = 201; + const HTTP_ACCEPTED = 202; + const HTTP_NON_AUTHORITATIVE_INFORMATION = 203; + const HTTP_NO_CONTENT = 204; + const HTTP_RESET_CONTENT = 205; + const HTTP_PARTIAL_CONTENT = 206; + const HTTP_MULTI_STATUS = 207; // RFC4918 + const HTTP_ALREADY_REPORTED = 208; // RFC5842 + const HTTP_IM_USED = 226; // RFC3229 + const HTTP_MULTIPLE_CHOICES = 300; + const HTTP_MOVED_PERMANENTLY = 301; + const HTTP_FOUND = 302; + const HTTP_SEE_OTHER = 303; + const HTTP_NOT_MODIFIED = 304; + const HTTP_USE_PROXY = 305; + const HTTP_RESERVED = 306; + const HTTP_TEMPORARY_REDIRECT = 307; + const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238 + const HTTP_BAD_REQUEST = 400; + const HTTP_UNAUTHORIZED = 401; + const HTTP_PAYMENT_REQUIRED = 402; + const HTTP_FORBIDDEN = 403; + const HTTP_NOT_FOUND = 404; + const HTTP_METHOD_NOT_ALLOWED = 405; + const HTTP_NOT_ACCEPTABLE = 406; + const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; + const HTTP_REQUEST_TIMEOUT = 408; + const HTTP_CONFLICT = 409; + const HTTP_GONE = 410; + const HTTP_LENGTH_REQUIRED = 411; + const HTTP_PRECONDITION_FAILED = 412; + const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; + const HTTP_REQUEST_URI_TOO_LONG = 414; + const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; + const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + const HTTP_EXPECTATION_FAILED = 417; + const HTTP_I_AM_A_TEAPOT = 418; // RFC2324 + const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918 + const HTTP_LOCKED = 423; // RFC4918 + const HTTP_FAILED_DEPENDENCY = 424; // RFC4918 + const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817 + const HTTP_UPGRADE_REQUIRED = 426; // RFC2817 + const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585 + const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585 + const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585 + const HTTP_INTERNAL_SERVER_ERROR = 500; + const HTTP_NOT_IMPLEMENTED = 501; + const HTTP_BAD_GATEWAY = 502; + const HTTP_SERVICE_UNAVAILABLE = 503; + const HTTP_GATEWAY_TIMEOUT = 504; + const HTTP_VERSION_NOT_SUPPORTED = 505; + const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295 + const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918 + const HTTP_LOOP_DETECTED = 508; // RFC5842 + const HTTP_NOT_EXTENDED = 510; // RFC2774 + const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585 + + /** + * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $version; + + /** + * @var int + */ + protected $statusCode; + + /** + * @var string + */ + protected $statusText; + + /** + * @var string + */ + protected $charset; + + /** + * Status codes translation table. + * + * The list of codes is complete according to the + * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry} + * (last updated 2015-05-19). + * + * Unless otherwise noted, the status code is defined in RFC2616. + * + * @var array + */ + public static $statusTexts = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', // RFC2518 + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC4918 + 208 => 'Already Reported', // RFC5842 + 226 => 'IM Used', // RFC3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', // RFC7238 + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Payload Too Large', + 414 => 'URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC2324 + 422 => 'Unprocessable Entity', // RFC4918 + 423 => 'Locked', // RFC4918 + 424 => 'Failed Dependency', // RFC4918 + 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817 + 426 => 'Upgrade Required', // RFC2817 + 428 => 'Precondition Required', // RFC6585 + 429 => 'Too Many Requests', // RFC6585 + 431 => 'Request Header Fields Too Large', // RFC6585 + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', // RFC2295 + 507 => 'Insufficient Storage', // RFC4918 + 508 => 'Loop Detected', // RFC5842 + 510 => 'Not Extended', // RFC2774 + 511 => 'Network Authentication Required', // RFC6585 + ); + + /** + * Constructor. + * + * @param mixed $content The response content, see setContent() + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @throws \InvalidArgumentException When the HTTP status code is not valid + */ + public function __construct($content = '', $status = 200, $headers = array()) + { + $this->headers = new ResponseHeaderBag($headers); + $this->setContent($content); + $this->setStatusCode($status); + $this->setProtocolVersion('1.0'); + } + + /** + * Factory method for chainability. + * + * Example: + * + * return Response::create($body, 200) + * ->setSharedMaxAge(300); + * + * @param mixed $content The response content, see setContent() + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @return Response + */ + public static function create($content = '', $status = 200, $headers = array()) + { + return new static($content, $status, $headers); + } + + /** + * Returns the Response as an HTTP string. + * + * The string representation of the Response is the same as the + * one that will be sent to the client only if the prepare() method + * has been called before. + * + * @return string The Response as an HTTP string + * + * @see prepare() + */ + public function __toString() + { + return + sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Clones the current Response instance. + */ + public function __clone() + { + $this->headers = clone $this->headers; + } + + /** + * Prepares the Response before it is sent to the client. + * + * This method tweaks the Response to ensure that it is + * compliant with RFC 2616. Most of the changes are based on + * the Request that is "associated" with this Response. + * + * @param Request $request A Request instance + * + * @return Response The current response. + */ + public function prepare(Request $request) + { + $headers = $this->headers; + + if ($this->isInformational() || $this->isEmpty()) { + $this->setContent(null); + $headers->remove('Content-Type'); + $headers->remove('Content-Length'); + } else { + // Content-type based on the Request + if (!$headers->has('Content-Type')) { + $format = $request->getRequestFormat(); + if (null !== $format && $mimeType = $request->getMimeType($format)) { + $headers->set('Content-Type', $mimeType); + } + } + + // Fix Content-Type + $charset = $this->charset ?: 'UTF-8'; + if (!$headers->has('Content-Type')) { + $headers->set('Content-Type', 'text/html; charset='.$charset); + } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) { + // add the charset + $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset); + } + + // Fix Content-Length + if ($headers->has('Transfer-Encoding')) { + $headers->remove('Content-Length'); + } + + if ($request->isMethod('HEAD')) { + // cf. RFC2616 14.13 + $length = $headers->get('Content-Length'); + $this->setContent(null); + if ($length) { + $headers->set('Content-Length', $length); + } + } + } + + // Fix protocol + if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + + // Check if we need to send extra expire info headers + if ('1.0' == $this->getProtocolVersion() && 'no-cache' == $this->headers->get('Cache-Control')) { + $this->headers->set('pragma', 'no-cache'); + $this->headers->set('expires', -1); + } + + $this->ensureIEOverSSLCompatibility($request); + + return $this; + } + + /** + * Sends HTTP headers. + * + * @return Response + */ + public function sendHeaders() + { + // headers have already been sent by the developer + if (headers_sent()) { + return $this; + } + + if (!$this->headers->has('Date')) { + $this->setDate(\DateTime::createFromFormat('U', time())); + } + + // headers + foreach ($this->headers->allPreserveCase() as $name => $values) { + foreach ($values as $value) { + header($name.': '.$value, false, $this->statusCode); + } + } + + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); + + // cookies + foreach ($this->headers->getCookies() as $cookie) { + setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + + return $this; + } + + /** + * Sends content for the current web response. + * + * @return Response + */ + public function sendContent() + { + echo $this->content; + + return $this; + } + + /** + * Sends HTTP headers and content. + * + * @return Response + */ + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } elseif ('cli' !== PHP_SAPI) { + static::closeOutputBuffers(0, true); + } + + return $this; + } + + /** + * Sets the response content. + * + * Valid types are strings, numbers, null, and objects that implement a __toString() method. + * + * @param mixed $content Content that can be cast to string + * + * @return Response + * + * @throws \UnexpectedValueException + */ + public function setContent($content) + { + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) { + throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content))); + } + + $this->content = (string) $content; + + return $this; + } + + /** + * Gets the current response content. + * + * @return string Content + */ + public function getContent() + { + return $this->content; + } + + /** + * Sets the HTTP protocol version (1.0 or 1.1). + * + * @param string $version The HTTP protocol version + * + * @return Response + */ + public function setProtocolVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * Gets the HTTP protocol version. + * + * @return string The HTTP protocol version + */ + public function getProtocolVersion() + { + return $this->version; + } + + /** + * Sets the response status code. + * + * @param int $code HTTP status code + * @param mixed $text HTTP status text + * + * If the status text is null it will be automatically populated for the known + * status codes and left empty otherwise. + * + * @return Response + * + * @throws \InvalidArgumentException When the HTTP status code is not valid + */ + public function setStatusCode($code, $text = null) + { + $this->statusCode = $code = (int) $code; + if ($this->isInvalid()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); + } + + if (null === $text) { + $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status'; + + return $this; + } + + if (false === $text) { + $this->statusText = ''; + + return $this; + } + + $this->statusText = $text; + + return $this; + } + + /** + * Retrieves the status code for the current web response. + * + * @return int Status code + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the response charset. + * + * @param string $charset Character set + * + * @return Response + */ + public function setCharset($charset) + { + $this->charset = $charset; + + return $this; + } + + /** + * Retrieves the response charset. + * + * @return string Character set + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Returns true if the response is worth caching under any circumstance. + * + * Responses marked "private" with an explicit Cache-Control directive are + * considered uncacheable. + * + * Responses with neither a freshness lifetime (Expires, max-age) nor cache + * validator (Last-Modified, ETag) are considered uncacheable. + * + * @return bool true if the response is worth caching, false otherwise + */ + public function isCacheable() + { + if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { + return false; + } + + if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) { + return false; + } + + return $this->isValidateable() || $this->isFresh(); + } + + /** + * Returns true if the response is "fresh". + * + * Fresh responses may be served from cache without any interaction with the + * origin. A response is considered fresh when it includes a Cache-Control/max-age + * indicator or Expires header and the calculated age is less than the freshness lifetime. + * + * @return bool true if the response is fresh, false otherwise + */ + public function isFresh() + { + return $this->getTtl() > 0; + } + + /** + * Returns true if the response includes headers that can be used to validate + * the response with the origin server using a conditional GET request. + * + * @return bool true if the response is validateable, false otherwise + */ + public function isValidateable() + { + return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); + } + + /** + * Marks the response as "private". + * + * It makes the response ineligible for serving other clients. + * + * @return Response + */ + public function setPrivate() + { + $this->headers->removeCacheControlDirective('public'); + $this->headers->addCacheControlDirective('private'); + + return $this; + } + + /** + * Marks the response as "public". + * + * It makes the response eligible for serving other clients. + * + * @return Response + */ + public function setPublic() + { + $this->headers->addCacheControlDirective('public'); + $this->headers->removeCacheControlDirective('private'); + + return $this; + } + + /** + * Returns true if the response must be revalidated by caches. + * + * This method indicates that the response must not be served stale by a + * cache in any circumstance without first revalidating with the origin. + * When present, the TTL of the response should not be overridden to be + * greater than the value provided by the origin. + * + * @return bool true if the response must be revalidated by a cache, false otherwise + */ + public function mustRevalidate() + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); + } + + /** + * Returns the Date header as a DateTime instance. + * + * @return \DateTime A \DateTime instance + * + * @throws \RuntimeException When the header is not parseable + */ + public function getDate() + { + if (!$this->headers->has('Date')) { + $this->setDate(\DateTime::createFromFormat('U', time())); + } + + return $this->headers->getDate('Date'); + } + + /** + * Sets the Date header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + */ + public function setDate(\DateTime $date) + { + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + /** + * Returns the age of the response. + * + * @return int The age of the response in seconds + */ + public function getAge() + { + if (null !== $age = $this->headers->get('Age')) { + return (int) $age; + } + + return max(time() - $this->getDate()->format('U'), 0); + } + + /** + * Marks the response stale by setting the Age header to be equal to the maximum age of the response. + * + * @return Response + */ + public function expire() + { + if ($this->isFresh()) { + $this->headers->set('Age', $this->getMaxAge()); + } + + return $this; + } + + /** + * Returns the value of the Expires header as a DateTime instance. + * + * @return \DateTime|null A DateTime instance or null if the header does not exist + */ + public function getExpires() + { + try { + return $this->headers->getDate('Expires'); + } catch (\RuntimeException $e) { + // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past + return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000'); + } + } + + /** + * Sets the Expires HTTP header with a DateTime instance. + * + * Passing null as value will remove the header. + * + * @param \DateTime|null $date A \DateTime instance or null to remove the header + * + * @return Response + */ + public function setExpires(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Expires'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + } + + return $this; + } + + /** + * Returns the number of seconds after the time specified in the response's Date + * header when the response should no longer be considered fresh. + * + * First, it checks for a s-maxage directive, then a max-age directive, and then it falls + * back on an expires header. It returns null when no maximum age can be established. + * + * @return int|null Number of seconds + */ + public function getMaxAge() + { + if ($this->headers->hasCacheControlDirective('s-maxage')) { + return (int) $this->headers->getCacheControlDirective('s-maxage'); + } + + if ($this->headers->hasCacheControlDirective('max-age')) { + return (int) $this->headers->getCacheControlDirective('max-age'); + } + + if (null !== $this->getExpires()) { + return $this->getExpires()->format('U') - $this->getDate()->format('U'); + } + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh. + * + * This methods sets the Cache-Control max-age directive. + * + * @param int $value Number of seconds + * + * @return Response + */ + public function setMaxAge($value) + { + $this->headers->addCacheControlDirective('max-age', $value); + + return $this; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh by shared caches. + * + * This methods sets the Cache-Control s-maxage directive. + * + * @param int $value Number of seconds + * + * @return Response + */ + public function setSharedMaxAge($value) + { + $this->setPublic(); + $this->headers->addCacheControlDirective('s-maxage', $value); + + return $this; + } + + /** + * Returns the response's time-to-live in seconds. + * + * It returns null when no freshness information is present in the response. + * + * When the responses TTL is <= 0, the response may not be served from cache without first + * revalidating with the origin. + * + * @return int|null The TTL in seconds + */ + public function getTtl() + { + if (null !== $maxAge = $this->getMaxAge()) { + return $maxAge - $this->getAge(); + } + } + + /** + * Sets the response's time-to-live for shared caches. + * + * This method adjusts the Cache-Control/s-maxage directive. + * + * @param int $seconds Number of seconds + * + * @return Response + */ + public function setTtl($seconds) + { + $this->setSharedMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Sets the response's time-to-live for private/client caches. + * + * This method adjusts the Cache-Control/max-age directive. + * + * @param int $seconds Number of seconds + * + * @return Response + */ + public function setClientTtl($seconds) + { + $this->setMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Returns the Last-Modified HTTP header as a DateTime instance. + * + * @return \DateTime|null A DateTime instance or null if the header does not exist + * + * @throws \RuntimeException When the HTTP header is not parseable + */ + public function getLastModified() + { + return $this->headers->getDate('Last-Modified'); + } + + /** + * Sets the Last-Modified HTTP header with a DateTime instance. + * + * Passing null as value will remove the header. + * + * @param \DateTime|null $date A \DateTime instance or null to remove the header + * + * @return Response + */ + public function setLastModified(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Last-Modified'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + } + + return $this; + } + + /** + * Returns the literal value of the ETag HTTP header. + * + * @return string|null The ETag HTTP header or null if it does not exist + */ + public function getEtag() + { + return $this->headers->get('ETag'); + } + + /** + * Sets the ETag value. + * + * @param string|null $etag The ETag unique identifier or null to remove the header + * @param bool $weak Whether you want a weak ETag or not + * + * @return Response + */ + public function setEtag($etag = null, $weak = false) + { + if (null === $etag) { + $this->headers->remove('Etag'); + } else { + if (0 !== strpos($etag, '"')) { + $etag = '"'.$etag.'"'; + } + + $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag); + } + + return $this; + } + + /** + * Sets the response's cache headers (validation and/or expiration). + * + * Available options are: etag, last_modified, max_age, s_maxage, private, and public. + * + * @param array $options An array of cache options + * + * @return Response + * + * @throws \InvalidArgumentException + */ + public function setCache(array $options) + { + if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) { + throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff)))); + } + + if (isset($options['etag'])) { + $this->setEtag($options['etag']); + } + + if (isset($options['last_modified'])) { + $this->setLastModified($options['last_modified']); + } + + if (isset($options['max_age'])) { + $this->setMaxAge($options['max_age']); + } + + if (isset($options['s_maxage'])) { + $this->setSharedMaxAge($options['s_maxage']); + } + + if (isset($options['public'])) { + if ($options['public']) { + $this->setPublic(); + } else { + $this->setPrivate(); + } + } + + if (isset($options['private'])) { + if ($options['private']) { + $this->setPrivate(); + } else { + $this->setPublic(); + } + } + + return $this; + } + + /** + * Modifies the response so that it conforms to the rules defined for a 304 status code. + * + * This sets the status, removes the body, and discards any headers + * that MUST NOT be included in 304 responses. + * + * @return Response + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3.5 + */ + public function setNotModified() + { + $this->setStatusCode(304); + $this->setContent(null); + + // remove headers that MUST NOT be included with 304 Not Modified responses + foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) { + $this->headers->remove($header); + } + + return $this; + } + + /** + * Returns true if the response includes a Vary header. + * + * @return bool true if the response includes a Vary header, false otherwise + */ + public function hasVary() + { + return null !== $this->headers->get('Vary'); + } + + /** + * Returns an array of header names given in the Vary header. + * + * @return array An array of Vary names + */ + public function getVary() + { + if (!$vary = $this->headers->get('Vary', null, false)) { + return array(); + } + + $ret = array(); + foreach ($vary as $item) { + $ret = array_merge($ret, preg_split('/[\s,]+/', $item)); + } + + return $ret; + } + + /** + * Sets the Vary header. + * + * @param string|array $headers + * @param bool $replace Whether to replace the actual value or not (true by default) + * + * @return Response + */ + public function setVary($headers, $replace = true) + { + $this->headers->set('Vary', $headers, $replace); + + return $this; + } + + /** + * Determines if the Response validators (ETag, Last-Modified) match + * a conditional value specified in the Request. + * + * If the Response is not modified, it sets the status code to 304 and + * removes the actual content by calling the setNotModified() method. + * + * @param Request $request A Request instance + * + * @return bool true if the Response validators match the Request, false otherwise + */ + public function isNotModified(Request $request) + { + if (!$request->isMethodSafe()) { + return false; + } + + $notModified = false; + $lastModified = $this->headers->get('Last-Modified'); + $modifiedSince = $request->headers->get('If-Modified-Since'); + + if ($etags = $request->getETags()) { + $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags); + } + + if ($modifiedSince && $lastModified) { + $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified); + } + + if ($notModified) { + $this->setNotModified(); + } + + return $notModified; + } + + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + /** + * Is response invalid? + * + * @return bool + */ + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * Is response informative? + * + * @return bool + */ + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * Is response successful? + * + * @return bool + */ + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * Is the response a redirect? + * + * @return bool + */ + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Is there a client error? + * + * @return bool + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Was there a server side error? + * + * @return bool + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Is the response OK? + * + * @return bool + */ + public function isOk() + { + return 200 === $this->statusCode; + } + + /** + * Is the response forbidden? + * + * @return bool + */ + public function isForbidden() + { + return 403 === $this->statusCode; + } + + /** + * Is the response a not found error? + * + * @return bool + */ + public function isNotFound() + { + return 404 === $this->statusCode; + } + + /** + * Is the response a redirect of some form? + * + * @param string $location + * + * @return bool + */ + public function isRedirect($location = null) + { + return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); + } + + /** + * Is the response empty? + * + * @return bool + */ + public function isEmpty() + { + return in_array($this->statusCode, array(204, 304)); + } + + /** + * Cleans or flushes output buffers up to target level. + * + * Resulting level can be greater than target level if a non-removable buffer has been encountered. + * + * @param int $targetLevel The target output buffering level + * @param bool $flush Whether to flush or clean the buffers + */ + public static function closeOutputBuffers($targetLevel, $flush) + { + $status = ob_get_status(true); + $level = count($status); + // PHP_OUTPUT_HANDLER_* are not defined on HHVM 3.3 + $flags = defined('PHP_OUTPUT_HANDLER_REMOVABLE') ? PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE) : -1; + + while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || $flags === ($s['flags'] & $flags) : $s['del'])) { + if ($flush) { + ob_end_flush(); + } else { + ob_end_clean(); + } + } + } + + /** + * Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9. + * + * @link http://support.microsoft.com/kb/323308 + */ + protected function ensureIEOverSSLCompatibility(Request $request) + { + if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) == 1 && true === $request->isSecure()) { + if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) { + $this->headers->remove('Cache-Control'); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php new file mode 100644 index 0000000..328bf49 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ResponseHeaderBag is a container for Response HTTP headers. + * + * @author Fabien Potencier + */ +class ResponseHeaderBag extends HeaderBag +{ + const COOKIES_FLAT = 'flat'; + const COOKIES_ARRAY = 'array'; + + const DISPOSITION_ATTACHMENT = 'attachment'; + const DISPOSITION_INLINE = 'inline'; + + /** + * @var array + */ + protected $computedCacheControl = array(); + + /** + * @var array + */ + protected $cookies = array(); + + /** + * @var array + */ + protected $headerNames = array(); + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + */ + public function __construct(array $headers = array()) + { + parent::__construct($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $cookies = ''; + foreach ($this->getCookies() as $cookie) { + $cookies .= 'Set-Cookie: '.$cookie."\r\n"; + } + + ksort($this->headerNames); + + return parent::__toString().$cookies; + } + + /** + * Returns the headers, with original capitalizations. + * + * @return array An array of headers + */ + public function allPreserveCase() + { + return array_combine($this->headerNames, $this->headers); + } + + /** + * {@inheritdoc} + */ + public function replace(array $headers = array()) + { + $this->headerNames = array(); + + parent::replace($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + } + + /** + * {@inheritdoc} + */ + public function set($key, $values, $replace = true) + { + parent::set($key, $values, $replace); + + $uniqueKey = strtr(strtolower($key), '_', '-'); + $this->headerNames[$uniqueKey] = $key; + + // ensure the cache-control header has sensible defaults + if (in_array($uniqueKey, array('cache-control', 'etag', 'last-modified', 'expires'))) { + $computed = $this->computeCacheControlValue(); + $this->headers['cache-control'] = array($computed); + $this->headerNames['cache-control'] = 'Cache-Control'; + $this->computedCacheControl = $this->parseCacheControl($computed); + } + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + parent::remove($key); + + $uniqueKey = strtr(strtolower($key), '_', '-'); + unset($this->headerNames[$uniqueKey]); + + if ('cache-control' === $uniqueKey) { + $this->computedCacheControl = array(); + } + } + + /** + * {@inheritdoc} + */ + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl); + } + + /** + * {@inheritdoc} + */ + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; + } + + /** + * Sets a cookie. + * + * @param Cookie $cookie + */ + public function setCookie(Cookie $cookie) + { + $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + } + + /** + * Removes a cookie from the array, but does not unset it in the browser. + * + * @param string $name + * @param string $path + * @param string $domain + */ + public function removeCookie($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + + unset($this->cookies[$domain][$path][$name]); + + if (empty($this->cookies[$domain][$path])) { + unset($this->cookies[$domain][$path]); + + if (empty($this->cookies[$domain])) { + unset($this->cookies[$domain]); + } + } + } + + /** + * Returns an array with all cookies. + * + * @param string $format + * + * @throws \InvalidArgumentException When the $format is invalid + * + * @return array + */ + public function getCookies($format = self::COOKIES_FLAT) + { + if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) { + throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY)))); + } + + if (self::COOKIES_ARRAY === $format) { + return $this->cookies; + } + + $flattenedCookies = array(); + foreach ($this->cookies as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; + } + + /** + * Clears a cookie in the browser. + * + * @param string $name + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httpOnly + */ + public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true) + { + $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly)); + } + + /** + * Generates a HTTP Content-Disposition field-value. + * + * @param string $disposition One of "inline" or "attachment" + * @param string $filename A unicode string + * @param string $filenameFallback A string containing only ASCII characters that + * is semantically equivalent to $filename. If the filename is already ASCII, + * it can be omitted, or just copied from $filename + * + * @return string A string suitable for use as a Content-Disposition field-value. + * + * @throws \InvalidArgumentException + * + * @see RFC 6266 + */ + public function makeDisposition($disposition, $filename, $filenameFallback = '') + { + if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) { + throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); + } + + if ('' == $filenameFallback) { + $filenameFallback = $filename; + } + + // filenameFallback is not ASCII. + if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) { + throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); + } + + // percent characters aren't safe in fallback. + if (false !== strpos($filenameFallback, '%')) { + throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); + } + + // path separators aren't allowed in either. + if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) { + throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); + } + + $output = sprintf('%s; filename="%s"', $disposition, str_replace('"', '\\"', $filenameFallback)); + + if ($filename !== $filenameFallback) { + $output .= sprintf("; filename*=utf-8''%s", rawurlencode($filename)); + } + + return $output; + } + + /** + * Returns the calculated value of the cache-control header. + * + * This considers several other headers and calculates or modifies the + * cache-control header to a sensible, conservative value. + * + * @return string + */ + protected function computeCacheControlValue() + { + if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { + return 'no-cache'; + } + + if (!$this->cacheControl) { + // conservative by default + return 'private, must-revalidate'; + } + + $header = $this->getCacheControlHeader(); + if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) { + return $header; + } + + // public if s-maxage is defined, private otherwise + if (!isset($this->cacheControl['s-maxage'])) { + return $header.', private'; + } + + return $header; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ServerBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ServerBag.php new file mode 100644 index 0000000..fa1cb2f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/ServerBag.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ServerBag is a container for HTTP headers from the $_SERVER variable. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * @author Robert Kiss + */ +class ServerBag extends ParameterBag +{ + /** + * Gets the HTTP headers. + * + * @return array + */ + public function getHeaders() + { + $headers = array(); + $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true); + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, 'HTTP_')) { + $headers[substr($key, 5)] = $value; + } + // CONTENT_* are not prefixed with HTTP_ + elseif (isset($contentHeaders[$key])) { + $headers[$key] = $value; + } + } + + if (isset($this->parameters['PHP_AUTH_USER'])) { + $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER']; + $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : ''; + } else { + /* + * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default + * For this workaround to work, add these lines to your .htaccess file: + * RewriteCond %{HTTP:Authorization} ^(.+)$ + * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * + * A sample .htaccess file: + * RewriteEngine On + * RewriteCond %{HTTP:Authorization} ^(.+)$ + * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{REQUEST_FILENAME} !-f + * RewriteRule ^(.*)$ app.php [QSA,L] + */ + + $authorizationHeader = null; + if (isset($this->parameters['HTTP_AUTHORIZATION'])) { + $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION']; + } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) { + $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION']; + } + + if (null !== $authorizationHeader) { + if (0 === stripos($authorizationHeader, 'basic ')) { + // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic + $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2); + if (count($exploded) == 2) { + list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + } + } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) { + // In some circumstances PHP_AUTH_DIGEST needs to be set + $headers['PHP_AUTH_DIGEST'] = $authorizationHeader; + $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader; + } elseif (0 === stripos($authorizationHeader, 'bearer ')) { + /* + * XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables, + * I'll just set $headers['AUTHORIZATION'] here. + * http://php.net/manual/en/reserved.variables.server.php + */ + $headers['AUTHORIZATION'] = $authorizationHeader; + } + } + } + + // PHP_AUTH_USER/PHP_AUTH_PW + if (isset($headers['PHP_AUTH_USER'])) { + $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']); + } elseif (isset($headers['PHP_AUTH_DIGEST'])) { + $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST']; + } + + return $headers; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php new file mode 100644 index 0000000..af292e3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class relates to session attribute storage. + */ +class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable +{ + private $name = 'attributes'; + + /** + * @var string + */ + private $storageKey; + + /** + * @var array + */ + protected $attributes = array(); + + /** + * Constructor. + * + * @param string $storageKey The key used to store attributes in the session + */ + public function __construct($storageKey = '_sf2_attributes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$attributes) + { + $this->attributes = &$attributes; + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->attributes = array(); + foreach ($attributes as $key => $value) { + $this->set($key, $value); + } + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + if (array_key_exists($name, $this->attributes)) { + $retval = $this->attributes[$name]; + unset($this->attributes[$name]); + } + + return $retval; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $return = $this->attributes; + $this->attributes = array(); + + return $return; + } + + /** + * Returns an iterator for attributes. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->attributes); + } + + /** + * Returns the number of attributes. + * + * @return int The number of attributes + */ + public function count() + { + return count($this->attributes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php new file mode 100644 index 0000000..0d8d179 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * Attributes store. + * + * @author Drak + */ +interface AttributeBagInterface extends SessionBagInterface +{ + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return bool true if the attribute is defined, false otherwise + */ + public function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found + * + * @return mixed + */ + public function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + */ + public function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + */ + public function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + public function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value or null when it does not exist + */ + public function remove($name); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php new file mode 100644 index 0000000..68cecf6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class provides structured storage of session attributes using + * a name spacing character in the key. + * + * @author Drak + */ +class NamespacedAttributeBag extends AttributeBag +{ + /** + * Namespace character. + * + * @var string + */ + private $namespaceCharacter; + + /** + * Constructor. + * + * @param string $storageKey Session storage key. + * @param string $namespaceCharacter Namespace character to use in keys. + */ + public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/') + { + $this->namespaceCharacter = $namespaceCharacter; + parent::__construct($storageKey); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + if (null === $attributes) { + return false; + } + + return array_key_exists($name, $attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + if (null === $attributes) { + return $default; + } + + return array_key_exists($name, $attributes) ? $attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $attributes = &$this->resolveAttributePath($name, true); + $name = $this->resolveKey($name); + $attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + $attributes = &$this->resolveAttributePath($name); + $name = $this->resolveKey($name); + if (null !== $attributes && array_key_exists($name, $attributes)) { + $retval = $attributes[$name]; + unset($attributes[$name]); + } + + return $retval; + } + + /** + * Resolves a path in attributes property and returns it as a reference. + * + * This method allows structured namespacing of session attributes. + * + * @param string $name Key name + * @param bool $writeContext Write context, default false + * + * @return array + */ + protected function &resolveAttributePath($name, $writeContext = false) + { + $array = &$this->attributes; + $name = (strpos($name, $this->namespaceCharacter) === 0) ? substr($name, 1) : $name; + + // Check if there is anything to do, else return + if (!$name) { + return $array; + } + + $parts = explode($this->namespaceCharacter, $name); + if (count($parts) < 2) { + if (!$writeContext) { + return $array; + } + + $array[$parts[0]] = array(); + + return $array; + } + + unset($parts[count($parts) - 1]); + + foreach ($parts as $part) { + if (null !== $array && !array_key_exists($part, $array)) { + $array[$part] = $writeContext ? array() : null; + } + + $array = &$array[$part]; + } + + return $array; + } + + /** + * Resolves the key from the name. + * + * This is the last part in a dot separated string. + * + * @param string $name + * + * @return string + */ + protected function resolveKey($name) + { + if (false !== $pos = strrpos($name, $this->namespaceCharacter)) { + $name = substr($name, $pos + 1); + } + + return $name; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php new file mode 100644 index 0000000..b9de5ce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * AutoExpireFlashBag flash message container. + * + * @author Drak + */ +class AutoExpireFlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array('display' => array(), 'new' => array()); + + /** + * The storage key for flashes in the session. + * + * @var string + */ + private $storageKey; + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + + // The logic: messages from the last request will be stored in new, so we move them to previous + // This request we will show what is in 'display'. What is placed into 'new' this time round will + // be moved to display next time round. + $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array(); + $this->flashes['new'] = array(); + } + + /** + * {@inheritdoc} + */ + public function add($type, $message) + { + $this->flashes['new'][$type][] = $message; + } + + /** + * {@inheritdoc} + */ + public function peek($type, array $default = array()) + { + return $this->has($type) ? $this->flashes['display'][$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array(); + } + + /** + * {@inheritdoc} + */ + public function get($type, array $default = array()) + { + $return = $default; + + if (!$this->has($type)) { + return $return; + } + + if (isset($this->flashes['display'][$type])) { + $return = $this->flashes['display'][$type]; + unset($this->flashes['display'][$type]); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->flashes['display']; + $this->flashes = array('new' => array(), 'display' => array()); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes['new'] = $messages; + } + + /** + * {@inheritdoc} + */ + public function set($type, $messages) + { + $this->flashes['new'][$type] = (array) $messages; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type]; + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes['display']); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->all(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php new file mode 100644 index 0000000..223d863 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * FlashBag flash message container. + * + * @author Drak + */ +class FlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array(); + + /** + * The storage key for flashes in the session. + * + * @var string + */ + private $storageKey; + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + } + + /** + * {@inheritdoc} + */ + public function add($type, $message) + { + $this->flashes[$type][] = $message; + } + + /** + * {@inheritdoc} + */ + public function peek($type, array $default = array()) + { + return $this->has($type) ? $this->flashes[$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return $this->flashes; + } + + /** + * {@inheritdoc} + */ + public function get($type, array $default = array()) + { + if (!$this->has($type)) { + return $default; + } + + $return = $this->flashes[$type]; + + unset($this->flashes[$type]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->peekAll(); + $this->flashes = array(); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function set($type, $messages) + { + $this->flashes[$type] = (array) $messages; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes = $messages; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes) && $this->flashes[$type]; + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->all(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php new file mode 100644 index 0000000..be79d9d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * FlashBagInterface. + * + * @author Drak + */ +interface FlashBagInterface extends SessionBagInterface +{ + /** + * Adds a flash message for type. + * + * @param string $type + * @param string $message + */ + public function add($type, $message); + + /** + * Registers a message for a given type. + * + * @param string $type + * @param string|array $message + */ + public function set($type, $message); + + /** + * Gets flash messages for a given type. + * + * @param string $type Message category type. + * @param array $default Default value if $type does not exist. + * + * @return array + */ + public function peek($type, array $default = array()); + + /** + * Gets all flash messages. + * + * @return array + */ + public function peekAll(); + + /** + * Gets and clears flash from the stack. + * + * @param string $type + * @param array $default Default value if $type does not exist. + * + * @return array + */ + public function get($type, array $default = array()); + + /** + * Gets and clears flashes from the stack. + * + * @return array + */ + public function all(); + + /** + * Sets all flash messages. + */ + public function setAll(array $messages); + + /** + * Has flash messages for a given type? + * + * @param string $type + * + * @return bool + */ + public function has($type); + + /** + * Returns a list of all defined types. + * + * @return array + */ + public function keys(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Session.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Session.php new file mode 100644 index 0000000..b743fe1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Session.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * Session. + * + * @author Fabien Potencier + * @author Drak + */ +class Session implements SessionInterface, \IteratorAggregate, \Countable +{ + /** + * Storage driver. + * + * @var SessionStorageInterface + */ + protected $storage; + + /** + * @var string + */ + private $flashName; + + /** + * @var string + */ + private $attributeName; + + /** + * Constructor. + * + * @param SessionStorageInterface $storage A SessionStorageInterface instance. + * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) + * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) + */ + public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + $this->storage = $storage ?: new NativeSessionStorage(); + + $attributes = $attributes ?: new AttributeBag(); + $this->attributeName = $attributes->getName(); + $this->registerBag($attributes); + + $flashes = $flashes ?: new FlashBag(); + $this->flashName = $flashes->getName(); + $this->registerBag($flashes); + } + + /** + * {@inheritdoc} + */ + public function start() + { + return $this->storage->start(); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return $this->storage->getBag($this->attributeName)->has($name); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return $this->storage->getBag($this->attributeName)->get($name, $default); + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->storage->getBag($this->attributeName)->set($name, $value); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->storage->getBag($this->attributeName)->all(); + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->storage->getBag($this->attributeName)->replace($attributes); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + return $this->storage->getBag($this->attributeName)->remove($name); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->storage->getBag($this->attributeName)->clear(); + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->storage->isStarted(); + } + + /** + * Returns an iterator for attributes. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->storage->getBag($this->attributeName)->all()); + } + + /** + * Returns the number of attributes. + * + * @return int The number of attributes + */ + public function count() + { + return count($this->storage->getBag($this->attributeName)->all()); + } + + /** + * {@inheritdoc} + */ + public function invalidate($lifetime = null) + { + $this->storage->clear(); + + return $this->migrate(true, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function migrate($destroy = false, $lifetime = null) + { + return $this->storage->regenerate($destroy, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->storage->save(); + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->storage->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + $this->storage->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->storage->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->storage->setName($name); + } + + /** + * {@inheritdoc} + */ + public function getMetadataBag() + { + return $this->storage->getMetadataBag(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->storage->registerBag($bag); + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + return $this->storage->getBag($name); + } + + /** + * Gets the flashbag interface. + * + * @return FlashBagInterface + */ + public function getFlashBag() + { + return $this->getBag($this->flashName); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php new file mode 100644 index 0000000..182a47d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * Session Bag store. + * + * @author Drak + */ +interface SessionBagInterface +{ + /** + * Gets this bag's name. + * + * @return string + */ + public function getName(); + + /** + * Initializes the Bag. + * + * @param array $array + */ + public function initialize(array &$array); + + /** + * Gets the storage key for this bag. + * + * @return string + */ + public function getStorageKey(); + + /** + * Clears out data from bag. + * + * @return mixed Whatever data was contained. + */ + public function clear(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php new file mode 100644 index 0000000..e2b6584 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/SessionInterface.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; + +/** + * Interface for the session. + * + * @author Drak + */ +interface SessionInterface +{ + /** + * Starts the session storage. + * + * @return bool True if session started. + * + * @throws \RuntimeException If session fails to start. + */ + public function start(); + + /** + * Returns the session ID. + * + * @return string The session ID. + */ + public function getId(); + + /** + * Sets the session ID. + * + * @param string $id + */ + public function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name. + */ + public function getName(); + + /** + * Sets the session name. + * + * @param string $name + */ + public function setName($name); + + /** + * Invalidates the current session. + * + * Clears all session attributes and flashes and regenerates the + * session and deletes the old session from persistence. + * + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + * + * @return bool True if session invalidated, false if error. + */ + public function invalidate($lifetime = null); + + /** + * Migrates the current session to a new session id while maintaining all + * session attributes. + * + * @param bool $destroy Whether to delete the old session or leave it to garbage collection. + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + * + * @return bool True if session migrated, false if error. + */ + public function migrate($destroy = false, $lifetime = null); + + /** + * Force the session to be saved and closed. + * + * This method is generally not required for real sessions as + * the session will be automatically saved at the end of + * code execution. + */ + public function save(); + + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return bool true if the attribute is defined, false otherwise + */ + public function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found. + * + * @return mixed + */ + public function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + */ + public function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + */ + public function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + public function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value or null when it does not exist + */ + public function remove($name); + + /** + * Clears all attributes. + */ + public function clear(); + + /** + * Checks if the session was started. + * + * @return bool + */ + public function isStarted(); + + /** + * Registers a SessionBagInterface with the session. + * + * @param SessionBagInterface $bag + */ + public function registerBag(SessionBagInterface $bag); + + /** + * Gets a bag instance by name. + * + * @param string $name + * + * @return SessionBagInterface + */ + public function getBag($name); + + /** + * Gets session meta. + * + * @return MetadataBag + */ + public function getMetadataBag(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php new file mode 100644 index 0000000..f6f99ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MemcacheSessionHandler. + * + * @author Drak + */ +class MemcacheSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \Memcache Memcache driver. + */ + private $memcache; + + /** + * @var int Time to live in seconds + */ + private $ttl; + + /** + * @var string Key prefix for shared environments. + */ + private $prefix; + + /** + * Constructor. + * + * List of available options: + * * prefix: The prefix to use for the memcache keys in order to avoid collision + * * expiretime: The time to live in seconds + * + * @param \Memcache $memcache A \Memcache instance + * @param array $options An associative array of Memcache options + * + * @throws \InvalidArgumentException When unsupported options are passed + */ + public function __construct(\Memcache $memcache, array $options = array()) + { + if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { + throw new \InvalidArgumentException(sprintf( + 'The following options are not supported "%s"', implode(', ', $diff) + )); + } + + $this->memcache = $memcache; + $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; + $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->memcache->close(); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->memcache->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->memcache->delete($this->prefix.$sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // not required here because memcache will auto expire the records anyhow. + return true; + } + + /** + * Return a Memcache instance. + * + * @return \Memcache + */ + protected function getMemcache() + { + return $this->memcache; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php new file mode 100644 index 0000000..d87bef8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MemcachedSessionHandler. + * + * Memcached based session storage handler based on the Memcached class + * provided by the PHP memcached extension. + * + * @see http://php.net/memcached + * + * @author Drak + */ +class MemcachedSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \Memcached Memcached driver. + */ + private $memcached; + + /** + * @var int Time to live in seconds + */ + private $ttl; + + /** + * @var string Key prefix for shared environments. + */ + private $prefix; + + /** + * Constructor. + * + * List of available options: + * * prefix: The prefix to use for the memcached keys in order to avoid collision + * * expiretime: The time to live in seconds + * + * @param \Memcached $memcached A \Memcached instance + * @param array $options An associative array of Memcached options + * + * @throws \InvalidArgumentException When unsupported options are passed + */ + public function __construct(\Memcached $memcached, array $options = array()) + { + $this->memcached = $memcached; + + if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { + throw new \InvalidArgumentException(sprintf( + 'The following options are not supported "%s"', implode(', ', $diff) + )); + } + + $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; + $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->memcached->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->memcached->delete($this->prefix.$sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // not required here because memcached will auto expire the records anyhow. + return true; + } + + /** + * Return a Memcached instance. + * + * @return \Memcached + */ + protected function getMemcached() + { + return $this->memcached; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php new file mode 100644 index 0000000..f1df25d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -0,0 +1,188 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MongoDB session handler. + * + * @author Markus Bachmann + */ +class MongoDbSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \Mongo + */ + private $mongo; + + /** + * @var \MongoCollection + */ + private $collection; + + /** + * @var array + */ + private $options; + + /** + * Constructor. + * + * List of available options: + * * database: The name of the database [required] + * * collection: The name of the collection [required] + * * id_field: The field name for storing the session id [default: _id] + * * data_field: The field name for storing the session data [default: data] + * * time_field: The field name for storing the timestamp [default: time] + * * expiry_field: The field name for storing the expiry-timestamp [default: expires_at] + * + * It is strongly recommended to put an index on the `expiry_field` for + * garbage-collection. Alternatively it's possible to automatically expire + * the sessions in the database as described below: + * + * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions + * automatically. Such an index can for example look like this: + * + * db..ensureIndex( + * { "": 1 }, + * { "expireAfterSeconds": 0 } + * ) + * + * More details on: http://docs.mongodb.org/manual/tutorial/expire-data/ + * + * If you use such an index, you can drop `gc_probability` to 0 since + * no garbage-collection is required. + * + * @param \Mongo|\MongoClient $mongo A MongoClient or Mongo instance + * @param array $options An associative array of field options + * + * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided + * @throws \InvalidArgumentException When "database" or "collection" not provided + */ + public function __construct($mongo, array $options) + { + if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo)) { + throw new \InvalidArgumentException('MongoClient or Mongo instance required'); + } + + if (!isset($options['database']) || !isset($options['collection'])) { + throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler'); + } + + $this->mongo = $mongo; + + $this->options = array_merge(array( + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'expiry_field' => 'expires_at', + ), $options); + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + $this->getCollection()->remove(array( + $this->options['id_field'] => $sessionId, + )); + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + $this->getCollection()->remove(array( + $this->options['expiry_field'] => array('$lt' => new \MongoDate()), + )); + + return true; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + $expiry = new \MongoDate(time() + (int) ini_get('session.gc_maxlifetime')); + + $fields = array( + $this->options['data_field'] => new \MongoBinData($data, \MongoBinData::BYTE_ARRAY), + $this->options['time_field'] => new \MongoDate(), + $this->options['expiry_field'] => $expiry, + ); + + $this->getCollection()->update( + array($this->options['id_field'] => $sessionId), + array('$set' => $fields), + array('upsert' => true, 'multiple' => false) + ); + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + $dbData = $this->getCollection()->findOne(array( + $this->options['id_field'] => $sessionId, + $this->options['expiry_field'] => array('$gte' => new \MongoDate()), + )); + + return null === $dbData ? '' : $dbData[$this->options['data_field']]->bin; + } + + /** + * Return a "MongoCollection" instance. + * + * @return \MongoCollection + */ + private function getCollection() + { + if (null === $this->collection) { + $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']); + } + + return $this->collection; + } + + /** + * Return a Mongo instance. + * + * @return \Mongo + */ + protected function getMongo() + { + return $this->mongo; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php new file mode 100644 index 0000000..f39235c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeFileSessionHandler. + * + * Native session handler using PHP's built in file storage. + * + * @author Drak + */ +class NativeFileSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. + * Default null will leave setting as defined by PHP. + * '/path', 'N;/path', or 'N;octal-mode;/path + * + * @see http://php.net/session.configuration.php#ini.session.save-path for further details. + * + * @throws \InvalidArgumentException On invalid $savePath + */ + public function __construct($savePath = null) + { + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + $baseDir = $savePath; + + if ($count = substr_count($savePath, ';')) { + if ($count > 2) { + throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath)); + } + + // characters after last ';' are the path + $baseDir = ltrim(strrchr($savePath, ';'), ';'); + } + + if ($baseDir && !is_dir($baseDir)) { + mkdir($baseDir, 0777, true); + } + + ini_set('session.save_path', $savePath); + ini_set('session.save_handler', 'files'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php new file mode 100644 index 0000000..4ae410f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Adds SessionHandler functionality if available. + * + * @see http://php.net/sessionhandler + */ +class NativeSessionHandler extends \SessionHandler +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php new file mode 100644 index 0000000..1516d43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NullSessionHandler. + * + * Can be used in unit testing or in a situations where persisted sessions are not desired. + * + * @author Drak + */ +class NullSessionHandler implements \SessionHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return true; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php new file mode 100644 index 0000000..48e81ee --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -0,0 +1,695 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Session handler using a PDO connection to read and write data. + * + * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements + * different locking strategies to handle concurrent access to the same session. + * Locking is necessary to prevent loss of data due to race conditions and to keep + * the session data consistent between read() and write(). With locking, requests + * for the same session will wait until the other one finished writing. For this + * reason it's best practice to close a session as early as possible to improve + * concurrency. PHPs internal files session handler also implements locking. + * + * Attention: Since SQLite does not support row level locks but locks the whole database, + * it means only one session can be accessed at a time. Even different sessions would wait + * for another to finish. So saving session in SQLite should only be considered for + * development or prototypes. + * + * Session data is a binary string that can contain non-printable characters like the null byte. + * For this reason it must be saved in a binary column in the database like BLOB in MySQL. + * Saving it in a character column could corrupt the data. You can use createTable() + * to initialize a correctly defined table. + * + * @see http://php.net/sessionhandlerinterface + * + * @author Fabien Potencier + * @author Michael Williams + * @author Tobias Schultze + */ +class PdoSessionHandler implements \SessionHandlerInterface +{ + /** + * No locking is done. This means sessions are prone to loss of data due to + * race conditions of concurrent requests to the same session. The last session + * write will win in this case. It might be useful when you implement your own + * logic to deal with this like an optimistic approach. + */ + const LOCK_NONE = 0; + + /** + * Creates an application-level lock on a session. The disadvantage is that the + * lock is not enforced by the database and thus other, unaware parts of the + * application could still concurrently modify the session. The advantage is it + * does not require a transaction. + * This mode is not available for SQLite and not yet implemented for oci and sqlsrv. + */ + const LOCK_ADVISORY = 1; + + /** + * Issues a real row lock. Since it uses a transaction between opening and + * closing a session, you have to be careful when you use same database connection + * that you also use for your application logic. This mode is the default because + * it's the only reliable solution across DBMSs. + */ + const LOCK_TRANSACTIONAL = 2; + + /** + * @var \PDO|null PDO instance or null when not connected yet + */ + private $pdo; + + /** + * @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled + */ + private $dsn = false; + + /** + * @var string Database driver + */ + private $driver; + + /** + * @var string Table name + */ + private $table = 'sessions'; + + /** + * @var string Column for session id + */ + private $idCol = 'sess_id'; + + /** + * @var string Column for session data + */ + private $dataCol = 'sess_data'; + + /** + * @var string Column for lifetime + */ + private $lifetimeCol = 'sess_lifetime'; + + /** + * @var string Column for timestamp + */ + private $timeCol = 'sess_time'; + + /** + * @var string Username when lazy-connect + */ + private $username = ''; + + /** + * @var string Password when lazy-connect + */ + private $password = ''; + + /** + * @var array Connection options when lazy-connect + */ + private $connectionOptions = array(); + + /** + * @var int The strategy for locking, see constants + */ + private $lockMode = self::LOCK_TRANSACTIONAL; + + /** + * It's an array to support multiple reads before closing which is manual, non-standard usage. + * + * @var \PDOStatement[] An array of statements to release advisory locks + */ + private $unlockStatements = array(); + + /** + * @var bool True when the current session exists but expired according to session.gc_maxlifetime + */ + private $sessionExpired = false; + + /** + * @var bool Whether a transaction is active + */ + private $inTransaction = false; + + /** + * @var bool Whether gc() has been called + */ + private $gcCalled = false; + + /** + * Constructor. + * + * You can either pass an existing database connection as PDO instance or + * pass a DSN string that will be used to lazy-connect to the database + * when the session is actually used. Furthermore it's possible to pass null + * which will then use the session.save_path ini setting as PDO DSN parameter. + * + * List of available options: + * * db_table: The name of the table [default: sessions] + * * db_id_col: The column where to store the session id [default: sess_id] + * * db_data_col: The column where to store the session data [default: sess_data] + * * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime] + * * db_time_col: The column where to store the timestamp [default: sess_time] + * * db_username: The username when lazy-connect [default: ''] + * * db_password: The password when lazy-connect [default: ''] + * * db_connection_options: An array of driver-specific connection options [default: array()] + * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL] + * + * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or null + * @param array $options An associative array of options + * + * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + */ + public function __construct($pdoOrDsn = null, array $options = array()) + { + if ($pdoOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { + throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); + } + + $this->pdo = $pdoOrDsn; + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + $this->dsn = $pdoOrDsn; + } + + $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table; + $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol; + $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol; + $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol; + $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol; + $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username; + $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password; + $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions; + $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode; + } + + /** + * Creates the table to store sessions which can be called once for setup. + * + * Session ID is saved in a column of maximum length 128 because that is enough even + * for a 512 bit configured session.hash_function like Whirlpool. Session data is + * saved in a BLOB. One could also use a shorter inlined varbinary column + * if one was sure the data fits into it. + * + * @throws \PDOException When the table already exists + * @throws \DomainException When an unsupported PDO driver is used + */ + public function createTable() + { + // connect if we are not yet + $this->getConnection(); + + switch ($this->driver) { + case 'mysql': + // We use varbinary for the ID column because it prevents unwanted conversions: + // - character set conversions between server and client + // - trailing space removal + // - case-insensitivity + // - language processing like é == e + $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol MEDIUMINT NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB"; + break; + case 'sqlite': + $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'pgsql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + try { + $this->pdo->exec($sql); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + + /** + * Returns true when the current session exists but expired according to session.gc_maxlifetime. + * + * Can be used to distinguish between a new session and one that expired due to inactivity. + * + * @return bool Whether current session expired + */ + public function isSessionExpired() + { + return $this->sessionExpired; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + if (null === $this->pdo) { + $this->connect($this->dsn ?: $savePath); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + try { + return $this->doRead($sessionId); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process. + // This way, pruning expired sessions does not block them from being started while the current session is used. + $this->gcCalled = true; + + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + // delete the record associated with this id + $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $stmt->execute(); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + $maxlifetime = (int) ini_get('session.gc_maxlifetime'); + + try { + // We use a single MERGE SQL query when supported by the database. + $mergeSql = $this->getMergeSql(); + + if (null !== $mergeSql) { + $mergeStmt = $this->pdo->prepare($mergeSql); + $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $mergeStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $mergeStmt->execute(); + + return true; + } + + $updateStmt = $this->pdo->prepare( + "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id" + ); + $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $updateStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $updateStmt->execute(); + + // When MERGE is not supported, like in Postgres, we have to use this approach that can result in + // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior). + // We can just catch such an error and re-execute the update. This is similar to a serializable + // transaction with retry logic on serialization failures but without the overhead and without possible + // false positives due to longer gap locking. + if (!$updateStmt->rowCount()) { + try { + $insertStmt = $this->pdo->prepare( + "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)" + ); + $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $insertStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $insertStmt->execute(); + } catch (\PDOException $e) { + // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys + if (0 === strpos($e->getCode(), '23')) { + $updateStmt->execute(); + } else { + throw $e; + } + } + } + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->commit(); + + while ($unlockStmt = array_shift($this->unlockStatements)) { + $unlockStmt->execute(); + } + + if ($this->gcCalled) { + $this->gcCalled = false; + + // delete the session records that have expired + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time"; + + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + } + + if (false !== $this->dsn) { + $this->pdo = null; // only close lazy-connection + } + + return true; + } + + /** + * Lazy-connects to the database. + * + * @param string $dsn DSN string + */ + private function connect($dsn) + { + $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + } + + /** + * Helper method to begin a transaction. + * + * Since SQLite does not support row level locks, we have to acquire a reserved lock + * on the database immediately. Because of https://bugs.php.net/42766 we have to create + * such a transaction manually which also means we cannot use PDO::commit or + * PDO::rollback or PDO::inTransaction for SQLite. + * + * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions + * due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ . + * So we change it to READ COMMITTED. + */ + private function beginTransaction() + { + if (!$this->inTransaction) { + if ('sqlite' === $this->driver) { + $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); + } else { + if ('mysql' === $this->driver) { + $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); + } + $this->pdo->beginTransaction(); + } + $this->inTransaction = true; + } + } + + /** + * Helper method to commit a transaction. + */ + private function commit() + { + if ($this->inTransaction) { + try { + // commit read-write transaction which also releases the lock + if ('sqlite' === $this->driver) { + $this->pdo->exec('COMMIT'); + } else { + $this->pdo->commit(); + } + $this->inTransaction = false; + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + } + + /** + * Helper method to rollback a transaction. + */ + private function rollback() + { + // We only need to rollback if we are in a transaction. Otherwise the resulting + // error would hide the real problem why rollback was called. We might not be + // in a transaction when not using the transactional locking behavior or when + // two callbacks (e.g. destroy and write) are invoked that both fail. + if ($this->inTransaction) { + if ('sqlite' === $this->driver) { + $this->pdo->exec('ROLLBACK'); + } else { + $this->pdo->rollBack(); + } + $this->inTransaction = false; + } + } + + /** + * Reads the session data in respect to the different locking strategies. + * + * We need to make sure we do not return session data that is already considered garbage according + * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. + * + * @param string $sessionId Session ID + * + * @return string The session data + */ + private function doRead($sessionId) + { + $this->sessionExpired = false; + + if (self::LOCK_ADVISORY === $this->lockMode) { + $this->unlockStatements[] = $this->doAdvisoryLock($sessionId); + } + + $selectSql = $this->getSelectSql(); + $selectStmt = $this->pdo->prepare($selectSql); + $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $selectStmt->execute(); + + $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); + + if ($sessionRows) { + if ($sessionRows[0][1] + $sessionRows[0][2] < time()) { + $this->sessionExpired = true; + + return ''; + } + + return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0]; + } + + if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) { + // Exclusive-reading of non-existent rows does not block, so we need to do an insert to block + // until other connections to the session are committed. + try { + $insertStmt = $this->pdo->prepare( + "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)" + ); + $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $insertStmt->bindValue(':data', '', \PDO::PARAM_LOB); + $insertStmt->bindValue(':lifetime', 0, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $insertStmt->execute(); + } catch (\PDOException $e) { + // Catch duplicate key error because other connection created the session already. + // It would only not be the case when the other connection destroyed the session. + if (0 === strpos($e->getCode(), '23')) { + // Retrieve finished session data written by concurrent connection. SELECT + // FOR UPDATE is necessary to avoid deadlock of connection that starts reading + // before we write (transform intention to real lock). + $selectStmt->execute(); + $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); + + if ($sessionRows) { + return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0]; + } + + return ''; + } + + throw $e; + } + } + + return ''; + } + + /** + * Executes an application-level lock on the database. + * + * @param string $sessionId Session ID + * + * @return \PDOStatement The statement that needs to be executed later to release the lock + * + * @throws \DomainException When an unsupported PDO driver is used + * + * @todo implement missing advisory locks + * - for oci using DBMS_LOCK.REQUEST + * - for sqlsrv using sp_getapplock with LockOwner = Session + */ + private function doAdvisoryLock($sessionId) + { + switch ($this->driver) { + case 'mysql': + // should we handle the return value? 0 on timeout, null on error + // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout + $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)'); + $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); + $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + + return $releaseStmt; + case 'pgsql': + // Obtaining an exclusive session level advisory lock requires an integer key. + // So we convert the HEX representation of the session id to an integer. + // Since integers are signed, we have to skip one hex char to fit in the range. + if (4 === PHP_INT_SIZE) { + $sessionInt1 = hexdec(substr($sessionId, 0, 7)); + $sessionInt2 = hexdec(substr($sessionId, 7, 7)); + + $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)'); + $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); + $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)'); + $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); + $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); + } else { + $sessionBigInt = hexdec(substr($sessionId, 0, 15)); + + $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)'); + $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)'); + $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); + } + + return $releaseStmt; + case 'sqlite': + throw new \DomainException('SQLite does not support advisory locks.'); + default: + throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver)); + } + } + + /** + * Return a locking or nonlocking SQL query to read session information. + * + * @return string The SQL string + * + * @throws \DomainException When an unsupported PDO driver is used + */ + private function getSelectSql() + { + if (self::LOCK_TRANSACTIONAL === $this->lockMode) { + $this->beginTransaction(); + + switch ($this->driver) { + case 'mysql': + case 'oci': + case 'pgsql': + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE"; + case 'sqlsrv': + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id"; + case 'sqlite': + // we already locked when starting transaction + break; + default: + throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver)); + } + } + + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id"; + } + + /** + * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database for writing session data. + * + * @return string|null The SQL string or null when not supported + */ + private function getMergeSql() + { + switch ($this->driver) { + case 'mysql': + return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; + case 'oci': + // DUAL is Oracle specific dummy table + return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time"; + case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): + // MERGE is only available since SQL Server 2008 and must be terminated by semicolon + // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx + return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time;"; + case 'sqlite': + return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; + } + } + + /** + * Return a PDO instance. + * + * @return \PDO + */ + protected function getConnection() + { + if (null === $this->pdo) { + $this->connect($this->dsn ?: ini_get('session.save_path')); + } + + return $this->pdo; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.php new file mode 100644 index 0000000..d49c36c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Wraps another SessionHandlerInterface to only write the session when it has been modified. + * + * @author Adrien Brault + */ +class WriteCheckSessionHandler implements \SessionHandlerInterface +{ + /** + * @var \SessionHandlerInterface + */ + private $wrappedSessionHandler; + + /** + * @var array sessionId => session + */ + private $readSessions; + + public function __construct(\SessionHandlerInterface $wrappedSessionHandler) + { + $this->wrappedSessionHandler = $wrappedSessionHandler; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->wrappedSessionHandler->close(); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->wrappedSessionHandler->destroy($sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return $this->wrappedSessionHandler->gc($maxlifetime); + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return $this->wrappedSessionHandler->open($savePath, $sessionName); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + $session = $this->wrappedSessionHandler->read($sessionId); + + $this->readSessions[$sessionId] = $session; + + return $session; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + if (isset($this->readSessions[$sessionId]) && $data === $this->readSessions[$sessionId]) { + return true; + } + + return $this->wrappedSessionHandler->write($sessionId, $data); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php new file mode 100644 index 0000000..ec7b267 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MetadataBag.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * Metadata container. + * + * Adds metadata to the session. + * + * @author Drak + */ +class MetadataBag implements SessionBagInterface +{ + const CREATED = 'c'; + const UPDATED = 'u'; + const LIFETIME = 'l'; + + /** + * @var string + */ + private $name = '__metadata'; + + /** + * @var string + */ + private $storageKey; + + /** + * @var array + */ + protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0); + + /** + * Unix timestamp. + * + * @var int + */ + private $lastUsed; + + /** + * @var int + */ + private $updateThreshold; + + /** + * Constructor. + * + * @param string $storageKey The key used to store bag in the session. + * @param int $updateThreshold The time to wait between two UPDATED updates + */ + public function __construct($storageKey = '_sf2_meta', $updateThreshold = 0) + { + $this->storageKey = $storageKey; + $this->updateThreshold = $updateThreshold; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$array) + { + $this->meta = &$array; + + if (isset($array[self::CREATED])) { + $this->lastUsed = $this->meta[self::UPDATED]; + + $timeStamp = time(); + if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) { + $this->meta[self::UPDATED] = $timeStamp; + } + } else { + $this->stampCreated(); + } + } + + /** + * Gets the lifetime that the session cookie was set with. + * + * @return int + */ + public function getLifetime() + { + return $this->meta[self::LIFETIME]; + } + + /** + * Stamps a new session's metadata. + * + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + */ + public function stampNew($lifetime = null) + { + $this->stampCreated($lifetime); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * Gets the created timestamp metadata. + * + * @return int Unix timestamp + */ + public function getCreated() + { + return $this->meta[self::CREATED]; + } + + /** + * Gets the last used metadata. + * + * @return int Unix timestamp + */ + public function getLastUsed() + { + return $this->lastUsed; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // nothing to do + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Sets name. + * + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + private function stampCreated($lifetime = null) + { + $timeStamp = time(); + $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp; + $this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php new file mode 100644 index 0000000..bc13737 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php @@ -0,0 +1,268 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * MockArraySessionStorage mocks the session for unit tests. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle. + * + * When doing functional testing, you should use MockFileSessionStorage instead. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * @author Drak + */ +class MockArraySessionStorage implements SessionStorageInterface +{ + /** + * @var string + */ + protected $id = ''; + + /** + * @var string + */ + protected $name; + + /** + * @var bool + */ + protected $started = false; + + /** + * @var bool + */ + protected $closed = false; + + /** + * @var array + */ + protected $data = array(); + + /** + * @var MetadataBag + */ + protected $metadataBag; + + /** + * @var array + */ + protected $bags; + + /** + * Constructor. + * + * @param string $name Session name + * @param MetadataBag $metaBag MetadataBag instance. + */ + public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null) + { + $this->name = $name; + $this->setMetadataBag($metaBag); + } + + /** + * Sets the session data. + * + * @param array $array + */ + public function setSessionData(array $array) + { + $this->data = $array; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (empty($this->id)) { + $this->id = $this->generateId(); + } + + $this->loadSession(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false, $lifetime = null) + { + if (!$this->started) { + $this->start(); + } + + $this->metadataBag->stampNew($lifetime); + $this->id = $this->generateId(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + if ($this->started) { + throw new \LogicException('Cannot set session ID after the session has started.'); + } + + $this->id = $id; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function save() + { + if (!$this->started || $this->closed) { + throw new \RuntimeException('Trying to save a session that was not started yet or was already closed'); + } + // nothing to do since we don't persist the session data + $this->closed = false; + $this->started = false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $this->data = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (!$this->started) { + $this->start(); + } + + return $this->bags[$name]; + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->started; + } + + /** + * Sets the MetadataBag. + * + * @param MetadataBag $bag + */ + public function setMetadataBag(MetadataBag $bag = null) + { + if (null === $bag) { + $bag = new MetadataBag(); + } + + $this->metadataBag = $bag; + } + + /** + * Gets the MetadataBag. + * + * @return MetadataBag + */ + public function getMetadataBag() + { + return $this->metadataBag; + } + + /** + * Generates a session ID. + * + * This doesn't need to be particularly cryptographically secure since this is just + * a mock. + * + * @return string + */ + protected function generateId() + { + return hash('sha256', uniqid('ss_mock_', true)); + } + + protected function loadSession() + { + $bags = array_merge($this->bags, array($this->metadataBag)); + + foreach ($bags as $bag) { + $key = $bag->getStorageKey(); + $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array(); + $bag->initialize($this->data[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php new file mode 100644 index 0000000..1f4117b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +/** + * MockFileSessionStorage is used to mock sessions for + * functional testing when done in a single PHP process. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle and this class does + * not pollute any session related globals, including session_*() functions + * or session.* PHP ini directives. + * + * @author Drak + */ +class MockFileSessionStorage extends MockArraySessionStorage +{ + /** + * @var string + */ + private $savePath; + + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. + * @param string $name Session name. + * @param MetadataBag $metaBag MetadataBag instance. + */ + public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null) + { + if (null === $savePath) { + $savePath = sys_get_temp_dir(); + } + + if (!is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + $this->savePath = $savePath; + + parent::__construct($name, $metaBag); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (!$this->id) { + $this->id = $this->generateId(); + } + + $this->read(); + + $this->started = true; + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false, $lifetime = null) + { + if (!$this->started) { + $this->start(); + } + + if ($destroy) { + $this->destroy(); + } + + return parent::regenerate($destroy, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function save() + { + if (!$this->started) { + throw new \RuntimeException('Trying to save a session that was not started yet or was already closed'); + } + + file_put_contents($this->getFilePath(), serialize($this->data)); + + // this is needed for Silex, where the session object is re-used across requests + // in functional tests. In Symfony, the container is rebooted, so we don't have + // this issue + $this->started = false; + } + + /** + * Deletes a session from persistent storage. + * Deliberately leaves session data in memory intact. + */ + private function destroy() + { + if (is_file($this->getFilePath())) { + unlink($this->getFilePath()); + } + } + + /** + * Calculate path to file. + * + * @return string File path + */ + private function getFilePath() + { + return $this->savePath.'/'.$this->id.'.mocksess'; + } + + /** + * Reads session from storage and loads session. + */ + private function read() + { + $filePath = $this->getFilePath(); + $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array(); + + $this->loadSession(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php new file mode 100644 index 0000000..dc672c5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -0,0 +1,399 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * This provides a base class for session attribute storage. + * + * @author Drak + */ +class NativeSessionStorage implements SessionStorageInterface +{ + /** + * Array of SessionBagInterface. + * + * @var SessionBagInterface[] + */ + protected $bags; + + /** + * @var bool + */ + protected $started = false; + + /** + * @var bool + */ + protected $closed = false; + + /** + * @var AbstractProxy + */ + protected $saveHandler; + + /** + * @var MetadataBag + */ + protected $metadataBag; + + /** + * Constructor. + * + * Depending on how you want the storage driver to behave you probably + * want to override this constructor entirely. + * + * List of options for $options array with their defaults. + * + * @see http://php.net/session.configuration for options + * but we omit 'session.' from the beginning of the keys for convenience. + * + * ("auto_start", is not supported as it tells PHP to start a session before + * PHP starts to execute user-land code. Setting during runtime has no effect). + * + * cache_limiter, "" (use "0" to prevent headers from being sent entirely). + * cookie_domain, "" + * cookie_httponly, "" + * cookie_lifetime, "0" + * cookie_path, "/" + * cookie_secure, "" + * entropy_file, "" + * entropy_length, "0" + * gc_divisor, "100" + * gc_maxlifetime, "1440" + * gc_probability, "1" + * hash_bits_per_character, "4" + * hash_function, "0" + * name, "PHPSESSID" + * referer_check, "" + * serialize_handler, "php" + * use_cookies, "1" + * use_only_cookies, "1" + * use_trans_sid, "0" + * upload_progress.enabled, "1" + * upload_progress.cleanup, "1" + * upload_progress.prefix, "upload_progress_" + * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS" + * upload_progress.freq, "1%" + * upload_progress.min-freq, "1" + * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset=" + * + * @param array $options Session configuration options. + * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler + * @param MetadataBag $metaBag MetadataBag. + */ + public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null) + { + session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used) + ini_set('session.use_cookies', 1); + + session_register_shutdown(); + + $this->setMetadataBag($metaBag); + $this->setOptions($options); + $this->setSaveHandler($handler); + } + + /** + * Gets the save handler instance. + * + * @return AbstractProxy + */ + public function getSaveHandler() + { + return $this->saveHandler; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (\PHP_SESSION_ACTIVE === session_status()) { + throw new \RuntimeException('Failed to start the session: already started by PHP.'); + } + + if (ini_get('session.use_cookies') && headers_sent($file, $line)) { + throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line)); + } + + // ok to try and start the session + if (!session_start()) { + throw new \RuntimeException('Failed to start the session'); + } + + $this->loadSession(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->saveHandler->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + $this->saveHandler->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->saveHandler->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->saveHandler->setName($name); + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false, $lifetime = null) + { + // Cannot regenerate the session ID for non-active sessions. + if (PHP_VERSION_ID >= 50400 && \PHP_SESSION_ACTIVE !== session_status()) { + return false; + } + + // Check if session ID exists in PHP 5.3 + if (PHP_VERSION_ID < 50400 && '' === session_id()) { + return false; + } + + if (null !== $lifetime) { + ini_set('session.cookie_lifetime', $lifetime); + } + + if ($destroy) { + $this->metadataBag->stampNew(); + } + + $isRegenerated = session_regenerate_id($destroy); + + // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it. + // @see https://bugs.php.net/bug.php?id=70013 + $this->loadSession(); + + return $isRegenerated; + } + + /** + * {@inheritdoc} + */ + public function save() + { + session_write_close(); + + $this->closed = true; + $this->started = false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $_SESSION = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if ($this->saveHandler->isActive() && !$this->started) { + $this->loadSession(); + } elseif (!$this->started) { + $this->start(); + } + + return $this->bags[$name]; + } + + /** + * Sets the MetadataBag. + * + * @param MetadataBag $metaBag + */ + public function setMetadataBag(MetadataBag $metaBag = null) + { + if (null === $metaBag) { + $metaBag = new MetadataBag(); + } + + $this->metadataBag = $metaBag; + } + + /** + * Gets the MetadataBag. + * + * @return MetadataBag + */ + public function getMetadataBag() + { + return $this->metadataBag; + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->started; + } + + /** + * Sets session.* ini variables. + * + * For convenience we omit 'session.' from the beginning of the keys. + * Explicitly ignores other ini keys. + * + * @param array $options Session ini directives array(key => value). + * + * @see http://php.net/session.configuration + */ + public function setOptions(array $options) + { + $validOptions = array_flip(array( + 'cache_limiter', 'cookie_domain', 'cookie_httponly', + 'cookie_lifetime', 'cookie_path', 'cookie_secure', + 'entropy_file', 'entropy_length', 'gc_divisor', + 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', + 'hash_function', 'name', 'referer_check', + 'serialize_handler', 'use_cookies', + 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', + 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', + 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags', + )); + + foreach ($options as $key => $value) { + if (isset($validOptions[$key])) { + ini_set('session.'.$key, $value); + } + } + } + + /** + * Registers session save handler as a PHP session handler. + * + * To use internal PHP session save handlers, override this method using ini_set with + * session.save_handler and session.save_path e.g. + * + * ini_set('session.save_handler', 'files'); + * ini_set('session.save_path', /tmp'); + * + * or pass in a NativeSessionHandler instance which configures session.save_handler in the + * constructor, for a template see NativeFileSessionHandler or use handlers in + * composer package drak/native-session + * + * @see http://php.net/session-set-save-handler + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/sessionhandler + * @see http://github.com/drak/NativeSession + * + * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $saveHandler + * + * @throws \InvalidArgumentException + */ + public function setSaveHandler($saveHandler = null) + { + if (!$saveHandler instanceof AbstractProxy && + !$saveHandler instanceof NativeSessionHandler && + !$saveHandler instanceof \SessionHandlerInterface && + null !== $saveHandler) { + throw new \InvalidArgumentException('Must be instance of AbstractProxy or NativeSessionHandler; implement \SessionHandlerInterface; or be null.'); + } + + // Wrap $saveHandler in proxy and prevent double wrapping of proxy + if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { + $saveHandler = new SessionHandlerProxy($saveHandler); + } elseif (!$saveHandler instanceof AbstractProxy) { + $saveHandler = new SessionHandlerProxy(new \SessionHandler()); + } + $this->saveHandler = $saveHandler; + + if ($this->saveHandler instanceof \SessionHandlerInterface) { + session_set_save_handler($this->saveHandler, false); + } + } + + /** + * Load the session with attributes. + * + * After starting the session, PHP retrieves the session from whatever handlers + * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()). + * PHP takes the return value from the read() handler, unserializes it + * and populates $_SESSION with the result automatically. + * + * @param array|null $session + */ + protected function loadSession(array &$session = null) + { + if (null === $session) { + $session = &$_SESSION; + } + + $bags = array_merge($this->bags, array($this->metadataBag)); + + foreach ($bags as $bag) { + $key = $bag->getStorageKey(); + $session[$key] = isset($session[$key]) ? $session[$key] : array(); + $bag->initialize($session[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/PhpBridgeSessionStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/PhpBridgeSessionStorage.php new file mode 100644 index 0000000..6f02a7f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/PhpBridgeSessionStorage.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; + +/** + * Allows session to be started by PHP and managed by Symfony. + * + * @author Drak + */ +class PhpBridgeSessionStorage extends NativeSessionStorage +{ + /** + * Constructor. + * + * @param AbstractProxy|NativeSessionHandler|\SessionHandlerInterface|null $handler + * @param MetadataBag $metaBag MetadataBag + */ + public function __construct($handler = null, MetadataBag $metaBag = null) + { + $this->setMetadataBag($metaBag); + $this->setSaveHandler($handler); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + $this->loadSession(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags and nothing else that may be set + // since the purpose of this driver is to share a handler + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // reconnect the bags to the session + $this->loadSession(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php new file mode 100644 index 0000000..a747865 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * AbstractProxy. + * + * @author Drak + */ +abstract class AbstractProxy +{ + /** + * Flag if handler wraps an internal PHP session handler (using \SessionHandler). + * + * @var bool + */ + protected $wrapper = false; + + /** + * @var string + */ + protected $saveHandlerName; + + /** + * Gets the session.save_handler name. + * + * @return string + */ + public function getSaveHandlerName() + { + return $this->saveHandlerName; + } + + /** + * Is this proxy handler and instance of \SessionHandlerInterface. + * + * @return bool + */ + public function isSessionHandlerInterface() + { + return $this instanceof \SessionHandlerInterface; + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool + */ + public function isWrapper() + { + return $this->wrapper; + } + + /** + * Has a session started? + * + * @return bool + */ + public function isActive() + { + return \PHP_SESSION_ACTIVE === session_status(); + } + + /** + * Gets the session ID. + * + * @return string + */ + public function getId() + { + return session_id(); + } + + /** + * Sets the session ID. + * + * @param string $id + * + * @throws \LogicException + */ + public function setId($id) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the ID of an active session'); + } + + session_id($id); + } + + /** + * Gets the session name. + * + * @return string + */ + public function getName() + { + return session_name(); + } + + /** + * Sets the session name. + * + * @param string $name + * + * @throws \LogicException + */ + public function setName($name) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the name of an active session'); + } + + session_name($name); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php new file mode 100644 index 0000000..5bb2c71 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * NativeProxy. + * + * This proxy is built-in session handlers in PHP 5.3.x + * + * @author Drak + */ +class NativeProxy extends AbstractProxy +{ + /** + * Constructor. + */ + public function __construct() + { + // this makes an educated guess as to what the handler is since it should already be set. + $this->saveHandlerName = ini_get('session.save_handler'); + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool False. + */ + public function isWrapper() + { + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php new file mode 100644 index 0000000..c5e97d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * SessionHandler proxy. + * + * @author Drak + */ +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface +{ + /** + * @var \SessionHandlerInterface + */ + protected $handler; + + /** + * Constructor. + * + * @param \SessionHandlerInterface $handler + */ + public function __construct(\SessionHandlerInterface $handler) + { + $this->handler = $handler; + $this->wrapper = ($handler instanceof \SessionHandler); + $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; + } + + // \SessionHandlerInterface + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return (bool) $this->handler->open($savePath, $sessionName); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return (bool) $this->handler->close(); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return (string) $this->handler->read($sessionId); + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return (bool) $this->handler->write($sessionId, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return (bool) $this->handler->destroy($sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return (bool) $this->handler->gc($maxlifetime); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php new file mode 100644 index 0000000..9f81847 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * StorageInterface. + * + * @author Fabien Potencier + * @author Drak + */ +interface SessionStorageInterface +{ + /** + * Starts the session. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return bool True if started. + */ + public function start(); + + /** + * Checks if the session is started. + * + * @return bool True if started, false otherwise. + */ + public function isStarted(); + + /** + * Returns the session ID. + * + * @return string The session ID or empty. + */ + public function getId(); + + /** + * Sets the session ID. + * + * @param string $id + */ + public function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name. + */ + public function getName(); + + /** + * Sets the session name. + * + * @param string $name + */ + public function setName($name); + + /** + * Regenerates id that represents this storage. + * + * This method must invoke session_regenerate_id($destroy) unless + * this interface is used for a storage object designed for unit + * or functional testing where a real PHP session would interfere + * with testing. + * + * Note regenerate+destroy should not clear the session data in memory + * only delete the session data from persistent storage. + * + * Care: When regenerating the session ID no locking is involved in PHPs + * session design. See https://bugs.php.net/bug.php?id=61470 for a discussion. + * So you must make sure the regenerated session is saved BEFORE sending the + * headers with the new ID. Symfonys HttpKernel offers a listener for this. + * See Symfony\Component\HttpKernel\EventListener\SaveSessionListener. + * Otherwise session data could get lost again for concurrent requests with the + * new ID. One result could be that you get logged out after just logging in. + * + * @param bool $destroy Destroy session when regenerating? + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + * + * @return bool True if session regenerated, false if error + * + * @throws \RuntimeException If an error occurs while regenerating this storage + */ + public function regenerate($destroy = false, $lifetime = null); + + /** + * Force the session to be saved and closed. + * + * This method must invoke session_write_close() unless this interface is + * used for a storage object design for unit or functional testing where + * a real PHP session would interfere with testing, in which case it + * it should actually persist the session data if required. + * + * @throws \RuntimeException If the session is saved without being started, or if the session + * is already closed. + */ + public function save(); + + /** + * Clear all session data in memory. + */ + public function clear(); + + /** + * Gets a SessionBagInterface by name. + * + * @param string $name + * + * @return SessionBagInterface + * + * @throws \InvalidArgumentException If the bag does not exist + */ + public function getBag($name); + + /** + * Registers a SessionBagInterface for use. + * + * @param SessionBagInterface $bag + */ + public function registerBag(SessionBagInterface $bag); + + /** + * @return MetadataBag + */ + public function getMetadataBag(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/StreamedResponse.php new file mode 100644 index 0000000..b021d3c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * StreamedResponse represents a streamed HTTP response. + * + * A StreamedResponse uses a callback for its content. + * + * The callback should use the standard PHP functions like echo + * to stream the response back to the client. The flush() method + * can also be used if needed. + * + * @see flush() + * + * @author Fabien Potencier + */ +class StreamedResponse extends Response +{ + protected $callback; + protected $streamed; + + /** + * Constructor. + * + * @param callable|null $callback A valid PHP callback or null to set it later + * @param int $status The response status code + * @param array $headers An array of response headers + */ + public function __construct(callable $callback = null, $status = 200, $headers = array()) + { + parent::__construct(null, $status, $headers); + + if (null !== $callback) { + $this->setCallback($callback); + } + $this->streamed = false; + } + + /** + * Factory method for chainability. + * + * @param callable|null $callback A valid PHP callback or null to set it later + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @return StreamedResponse + */ + public static function create($callback = null, $status = 200, $headers = array()) + { + return new static($callback, $status, $headers); + } + + /** + * Sets the PHP callback associated with this Response. + * + * @param callable $callback A valid PHP callback + */ + public function setCallback(callable $callback) + { + $this->callback = $callback; + } + + /** + * {@inheritdoc} + * + * This method only sends the content once. + */ + public function sendContent() + { + if ($this->streamed) { + return; + } + + $this->streamed = true; + + if (null === $this->callback) { + throw new \LogicException('The Response callback must not be null.'); + } + + call_user_func($this->callback); + } + + /** + * {@inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a StreamedResponse instance.'); + } + } + + /** + * {@inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderItemTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderItemTest.php new file mode 100644 index 0000000..e4f354f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderItemTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\AcceptHeaderItem; + +class AcceptHeaderItemTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideFromStringData + */ + public function testFromString($string, $value, array $attributes) + { + $item = AcceptHeaderItem::fromString($string); + $this->assertEquals($value, $item->getValue()); + $this->assertEquals($attributes, $item->getAttributes()); + } + + public function provideFromStringData() + { + return array( + array( + 'text/html', + 'text/html', array(), + ), + array( + '"this;should,not=matter"', + 'this;should,not=matter', array(), + ), + array( + "text/plain; charset=utf-8;param=\"this;should,not=matter\";\tfootnotes=true", + 'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'), + ), + array( + '"this;should,not=matter";charset=utf-8', + 'this;should,not=matter', array('charset' => 'utf-8'), + ), + ); + } + + /** + * @dataProvider provideToStringData + */ + public function testToString($value, array $attributes, $string) + { + $item = new AcceptHeaderItem($value, $attributes); + $this->assertEquals($string, (string) $item); + } + + public function provideToStringData() + { + return array( + array( + 'text/html', array(), + 'text/html', + ), + array( + 'text/plain', array('charset' => 'utf-8', 'param' => 'this;should,not=matter', 'footnotes' => 'true'), + 'text/plain;charset=utf-8;param="this;should,not=matter";footnotes=true', + ), + ); + } + + public function testValue() + { + $item = new AcceptHeaderItem('value', array()); + $this->assertEquals('value', $item->getValue()); + + $item->setValue('new value'); + $this->assertEquals('new value', $item->getValue()); + + $item->setValue(1); + $this->assertEquals('1', $item->getValue()); + } + + public function testQuality() + { + $item = new AcceptHeaderItem('value', array()); + $this->assertEquals(1.0, $item->getQuality()); + + $item->setQuality(0.5); + $this->assertEquals(0.5, $item->getQuality()); + + $item->setAttribute('q', 0.75); + $this->assertEquals(0.75, $item->getQuality()); + $this->assertFalse($item->hasAttribute('q')); + } + + public function testAttribute() + { + $item = new AcceptHeaderItem('value', array()); + $this->assertEquals(array(), $item->getAttributes()); + $this->assertFalse($item->hasAttribute('test')); + $this->assertNull($item->getAttribute('test')); + $this->assertEquals('default', $item->getAttribute('test', 'default')); + + $item->setAttribute('test', 'value'); + $this->assertEquals(array('test' => 'value'), $item->getAttributes()); + $this->assertTrue($item->hasAttribute('test')); + $this->assertEquals('value', $item->getAttribute('test')); + $this->assertEquals('value', $item->getAttribute('test', 'default')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php new file mode 100644 index 0000000..9b3b58e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\AcceptHeader; +use Symfony\Component\HttpFoundation\AcceptHeaderItem; + +class AcceptHeaderTest extends \PHPUnit_Framework_TestCase +{ + public function testFirst() + { + $header = AcceptHeader::fromString('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c'); + $this->assertSame('text/html', $header->first()->getValue()); + } + + /** + * @dataProvider provideFromStringData + */ + public function testFromString($string, array $items) + { + $header = AcceptHeader::fromString($string); + $parsed = array_values($header->all()); + // reset index since the fixtures don't have them set + foreach ($parsed as $item) { + $item->setIndex(0); + } + $this->assertEquals($items, $parsed); + } + + public function provideFromStringData() + { + return array( + array('', array()), + array('gzip', array(new AcceptHeaderItem('gzip'))), + array('gzip,deflate,sdch', array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))), + array("gzip, deflate\t,sdch", array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch'))), + array('"this;should,not=matter"', array(new AcceptHeaderItem('this;should,not=matter'))), + ); + } + + /** + * @dataProvider provideToStringData + */ + public function testToString(array $items, $string) + { + $header = new AcceptHeader($items); + $this->assertEquals($string, (string) $header); + } + + public function provideToStringData() + { + return array( + array(array(), ''), + array(array(new AcceptHeaderItem('gzip')), 'gzip'), + array(array(new AcceptHeaderItem('gzip'), new AcceptHeaderItem('deflate'), new AcceptHeaderItem('sdch')), 'gzip,deflate,sdch'), + array(array(new AcceptHeaderItem('this;should,not=matter')), 'this;should,not=matter'), + ); + } + + /** + * @dataProvider provideFilterData + */ + public function testFilter($string, $filter, array $values) + { + $header = AcceptHeader::fromString($string)->filter($filter); + $this->assertEquals($values, array_keys($header->all())); + } + + public function provideFilterData() + { + return array( + array('fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4', '/fr.*/', array('fr-FR', 'fr')), + ); + } + + /** + * @dataProvider provideSortingData + */ + public function testSorting($string, array $values) + { + $header = AcceptHeader::fromString($string); + $this->assertEquals($values, array_keys($header->all())); + } + + public function provideSortingData() + { + return array( + 'quality has priority' => array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')), + 'order matters when q is equal' => array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('ISO-8859-1', 'utf-8', '*')), + 'order matters when q is equal2' => array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('utf-8', 'ISO-8859-1', '*')), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ApacheRequestTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ApacheRequestTest.php new file mode 100644 index 0000000..6845118 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ApacheRequestTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\ApacheRequest; + +class ApacheRequestTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideServerVars + */ + public function testUriMethods($server, $expectedRequestUri, $expectedBaseUrl, $expectedPathInfo) + { + $request = new ApacheRequest(); + $request->server->replace($server); + + $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct'); + $this->assertEquals($expectedBaseUrl, $request->getBaseUrl(), '->getBaseUrl() is correct'); + $this->assertEquals($expectedPathInfo, $request->getPathInfo(), '->getPathInfo() is correct'); + } + + public function provideServerVars() + { + return array( + array( + array( + 'REQUEST_URI' => '/foo/app_dev.php/bar', + 'SCRIPT_NAME' => '/foo/app_dev.php', + 'PATH_INFO' => '/bar', + ), + '/foo/app_dev.php/bar', + '/foo/app_dev.php', + '/bar', + ), + array( + array( + 'REQUEST_URI' => '/foo/bar', + 'SCRIPT_NAME' => '/foo/app_dev.php', + ), + '/foo/bar', + '/foo', + '/bar', + ), + array( + array( + 'REQUEST_URI' => '/app_dev.php/foo/bar', + 'SCRIPT_NAME' => '/app_dev.php', + 'PATH_INFO' => '/foo/bar', + ), + '/app_dev.php/foo/bar', + '/app_dev.php', + '/foo/bar', + ), + array( + array( + 'REQUEST_URI' => '/foo/bar', + 'SCRIPT_NAME' => '/app_dev.php', + ), + '/foo/bar', + '', + '/foo/bar', + ), + array( + array( + 'REQUEST_URI' => '/app_dev.php', + 'SCRIPT_NAME' => '/app_dev.php', + ), + '/app_dev.php', + '/app_dev.php', + '/', + ), + array( + array( + 'REQUEST_URI' => '/', + 'SCRIPT_NAME' => '/app_dev.php', + ), + '/', + '', + '/', + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php new file mode 100644 index 0000000..d7744ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Tests\File\FakeFile; + +class BinaryFileResponseTest extends ResponseTestCase +{ + public function testConstruction() + { + $file = __DIR__.'/../README.md'; + $response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true); + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEquals('Foo', $response->headers->get('X-Header')); + $this->assertTrue($response->headers->has('ETag')); + $this->assertTrue($response->headers->has('Last-Modified')); + $this->assertFalse($response->headers->has('Content-Disposition')); + + $response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE); + $this->assertEquals(404, $response->getStatusCode()); + $this->assertFalse($response->headers->has('ETag')); + $this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition')); + } + + /** + * @expectedException \LogicException + */ + public function testSetContent() + { + $response = new BinaryFileResponse(__FILE__); + $response->setContent('foo'); + } + + public function testGetContent() + { + $response = new BinaryFileResponse(__FILE__); + $this->assertFalse($response->getContent()); + } + + /** + * @dataProvider provideRanges + */ + public function testRequests($requestRange, $offset, $length, $responseRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); + + // do a request to get the ETag + $request = Request::create('/'); + $response->prepare($request); + $etag = $response->headers->get('ETag'); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('If-Range', $etag); + $request->headers->set('Range', $requestRange); + + $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); + fseek($file, $offset); + $data = fread($file, $length); + fclose($file); + + $this->expectOutputString($data); + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(206, $response->getStatusCode()); + $this->assertEquals($responseRange, $response->headers->get('Content-Range')); + } + + public function provideRanges() + { + return array( + array('bytes=1-4', 1, 4, 'bytes 1-4/35'), + array('bytes=-5', 30, 5, 'bytes 30-34/35'), + array('bytes=30-', 30, 5, 'bytes 30-34/35'), + array('bytes=30-30', 30, 1, 'bytes 30-30/35'), + array('bytes=30-34', 30, 5, 'bytes 30-34/35'), + ); + } + + /** + * @dataProvider provideFullFileRanges + */ + public function testFullFileRequests($requestRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('Range', $requestRange); + + $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); + $data = fread($file, 35); + fclose($file); + + $this->expectOutputString($data); + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function provideFullFileRanges() + { + return array( + array('bytes=0-'), + array('bytes=0-34'), + array('bytes=-35'), + // Syntactical invalid range-request should also return the full resource + array('bytes=20-10'), + array('bytes=50-40'), + ); + } + + /** + * @dataProvider provideInvalidRanges + */ + public function testInvalidRequests($requestRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('Range', $requestRange); + + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(416, $response->getStatusCode()); + #$this->assertEquals('', $response->headers->get('Content-Range')); + } + + public function provideInvalidRanges() + { + return array( + array('bytes=-40'), + array('bytes=30-40'), + ); + } + + /** + * @dataProvider provideXSendfileFiles + */ + public function testXSendfile($file) + { + $request = Request::create('/'); + $request->headers->set('X-Sendfile-Type', 'X-Sendfile'); + + BinaryFileResponse::trustXSendfileTypeHeader(); + $response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream')); + $response->prepare($request); + + $this->expectOutputString(''); + $response->sendContent(); + + $this->assertContains('README.md', $response->headers->get('X-Sendfile')); + } + + public function provideXSendfileFiles() + { + return array( + array(__DIR__.'/../README.md'), + array('file://'.__DIR__.'/../README.md'), + ); + } + + /** + * @dataProvider getSampleXAccelMappings + */ + public function testXAccelMapping($realpath, $mapping, $virtual) + { + $request = Request::create('/'); + $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect'); + $request->headers->set('X-Accel-Mapping', $mapping); + + $file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test'); + + BinaryFileResponse::trustXSendfileTypeHeader(); + $response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream')); + $reflection = new \ReflectionObject($response); + $property = $reflection->getProperty('file'); + $property->setAccessible(true); + $property->setValue($response, $file); + + $response->prepare($request); + $this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect')); + } + + public function testDeleteFileAfterSend() + { + $request = Request::create('/'); + + $path = __DIR__.'/File/Fixtures/to_delete'; + touch($path); + $realPath = realpath($path); + $this->assertFileExists($realPath); + + $response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream')); + $response->deleteFileAfterSend(true); + + $response->prepare($request); + $response->sendContent(); + + $this->assertFileNotExists($path); + } + + public function testAcceptRangeOnUnsafeMethods() + { + $request = Request::create('/', 'POST'); + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); + $response->prepare($request); + + $this->assertEquals('none', $response->headers->get('Accept-Ranges')); + } + + public function testAcceptRangeNotOverriden() + { + $request = Request::create('/', 'POST'); + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); + $response->headers->set('Accept-Ranges', 'foo'); + $response->prepare($request); + + $this->assertEquals('foo', $response->headers->get('Accept-Ranges')); + } + + public function getSampleXAccelMappings() + { + return array( + array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'), + array('/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'), + ); + } + + protected function provideResponse() + { + return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream')); + } + + public static function tearDownAfterClass() + { + $path = __DIR__.'/../Fixtures/to_delete'; + if (file_exists($path)) { + @unlink($path); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php new file mode 100644 index 0000000..fd90103 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Cookie; + +/** + * CookieTest. + * + * @author John Kary + * @author Hugo Hamon + * + * @group time-sensitive + */ +class CookieTest extends \PHPUnit_Framework_TestCase +{ + public function invalidNames() + { + return array( + array(''), + array(',MyName'), + array(';MyName'), + array(' MyName'), + array("\tMyName"), + array("\rMyName"), + array("\nMyName"), + array("\013MyName"), + array("\014MyName"), + ); + } + + /** + * @dataProvider invalidNames + * @expectedException \InvalidArgumentException + */ + public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name) + { + new Cookie($name); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidExpiration() + { + $cookie = new Cookie('MyCookie', 'foo', 'bar'); + } + + public function testGetValue() + { + $value = 'MyValue'; + $cookie = new Cookie('MyCookie', $value); + + $this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value'); + } + + public function testGetPath() + { + $cookie = new Cookie('foo', 'bar'); + + $this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path'); + } + + public function testGetExpiresTime() + { + $cookie = new Cookie('foo', 'bar', 3600); + + $this->assertEquals(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); + } + + public function testConstructorWithDateTime() + { + $expire = new \DateTime(); + $cookie = new Cookie('foo', 'bar', $expire); + + $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); + } + + public function testGetExpiresTimeWithStringValue() + { + $value = '+1 day'; + $cookie = new Cookie('foo', 'bar', $value); + $expire = strtotime($value); + + $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1); + } + + public function testGetDomain() + { + $cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com'); + + $this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid'); + } + + public function testIsSecure() + { + $cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com', true); + + $this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS'); + } + + public function testIsHttpOnly() + { + $cookie = new Cookie('foo', 'bar', 3600, '/', '.myfoodomain.com', false, true); + + $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP'); + } + + public function testCookieIsNotCleared() + { + $cookie = new Cookie('foo', 'bar', time() + 3600 * 24); + + $this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet'); + } + + public function testCookieIsCleared() + { + $cookie = new Cookie('foo', 'bar', time() - 20); + + $this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired'); + } + + public function testToString() + { + $cookie = new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); + $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly', $cookie->__toString(), '->__toString() returns string representation of the cookie'); + + $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com'); + $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/admin/; domain=.myfoodomain.com; httponly', $cookie->__toString(), '->__toString() returns string representation of a cleared cookie if value is NULL'); + + $cookie = new Cookie('foo', 'bar', 0, '/', ''); + $this->assertEquals('foo=bar; path=/; httponly', $cookie->__toString()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ExpressionRequestMatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ExpressionRequestMatcherTest.php new file mode 100644 index 0000000..fda372f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ExpressionRequestMatcherTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\HttpFoundation\ExpressionRequestMatcher; +use Symfony\Component\HttpFoundation\Request; + +class ExpressionRequestMatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \LogicException + */ + public function testWhenNoExpressionIsSet() + { + $expressionRequestMatcher = new ExpressionRequestMatcher(); + $expressionRequestMatcher->matches(new Request()); + } + + /** + * @dataProvider provideExpressions + */ + public function testMatchesWhenParentMatchesIsTrue($expression, $expected) + { + $request = Request::create('/foo'); + $expressionRequestMatcher = new ExpressionRequestMatcher(); + + $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression); + $this->assertSame($expected, $expressionRequestMatcher->matches($request)); + } + + /** + * @dataProvider provideExpressions + */ + public function testMatchesWhenParentMatchesIsFalse($expression) + { + $request = Request::create('/foo'); + $request->attributes->set('foo', 'foo'); + $expressionRequestMatcher = new ExpressionRequestMatcher(); + $expressionRequestMatcher->matchAttribute('foo', 'bar'); + + $expressionRequestMatcher->setExpression(new ExpressionLanguage(), $expression); + $this->assertFalse($expressionRequestMatcher->matches($request)); + } + + public function provideExpressions() + { + return array( + array('request.getMethod() == method', true), + array('request.getPathInfo() == path', true), + array('request.getHost() == host', true), + array('request.getClientIp() == ip', true), + array('request.attributes.all() == attributes', true), + array('request.getMethod() == method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', true), + array('request.getMethod() != method', false), + array('request.getMethod() != method && request.getPathInfo() == path && request.getHost() == host && request.getClientIp() == ip && request.attributes.all() == attributes', false), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FakeFile.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FakeFile.php new file mode 100644 index 0000000..c415989 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FakeFile.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\File; + +use Symfony\Component\HttpFoundation\File\File as OrigFile; + +class FakeFile extends OrigFile +{ + private $realpath; + + public function __construct($realpath, $path) + { + $this->realpath = $realpath; + parent::__construct($path, false); + } + + public function isReadable() + { + return true; + } + + public function getRealpath() + { + return $this->realpath; + } + + public function getSize() + { + return 42; + } + + public function getMTime() + { + return time(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FileTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FileTest.php new file mode 100644 index 0000000..d216eab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/FileTest.php @@ -0,0 +1,179 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\File; + +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; + +class FileTest extends \PHPUnit_Framework_TestCase +{ + protected $file; + + public function testGetMimeTypeUsesMimeTypeGuessers() + { + $file = new File(__DIR__.'/Fixtures/test.gif'); + $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif'); + + MimeTypeGuesser::getInstance()->register($guesser); + + $this->assertEquals('image/gif', $file->getMimeType()); + } + + public function testGuessExtensionWithoutGuesser() + { + $file = new File(__DIR__.'/Fixtures/directory/.empty'); + + $this->assertNull($file->guessExtension()); + } + + public function testGuessExtensionIsBasedOnMimeType() + { + $file = new File(__DIR__.'/Fixtures/test'); + $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif'); + + MimeTypeGuesser::getInstance()->register($guesser); + + $this->assertEquals('gif', $file->guessExtension()); + } + + /** + * @requires extension fileinfo + */ + public function testGuessExtensionWithReset() + { + $file = new File(__DIR__.'/Fixtures/other-file.example'); + $guesser = $this->createMockGuesser($file->getPathname(), 'image/gif'); + MimeTypeGuesser::getInstance()->register($guesser); + + $this->assertEquals('gif', $file->guessExtension()); + + MimeTypeGuesser::reset(); + + $this->assertNull($file->guessExtension()); + } + + public function testConstructWhenFileNotExists() + { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + + new File(__DIR__.'/Fixtures/not_here'); + } + + public function testMove() + { + $path = __DIR__.'/Fixtures/test.copy.gif'; + $targetDir = __DIR__.'/Fixtures/directory'; + $targetPath = $targetDir.'/test.copy.gif'; + @unlink($path); + @unlink($targetPath); + copy(__DIR__.'/Fixtures/test.gif', $path); + + $file = new File($path); + $movedFile = $file->move($targetDir); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile); + + $this->assertTrue(file_exists($targetPath)); + $this->assertFalse(file_exists($path)); + $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); + + @unlink($targetPath); + } + + public function testMoveWithNewName() + { + $path = __DIR__.'/Fixtures/test.copy.gif'; + $targetDir = __DIR__.'/Fixtures/directory'; + $targetPath = $targetDir.'/test.newname.gif'; + @unlink($path); + @unlink($targetPath); + copy(__DIR__.'/Fixtures/test.gif', $path); + + $file = new File($path); + $movedFile = $file->move($targetDir, 'test.newname.gif'); + + $this->assertTrue(file_exists($targetPath)); + $this->assertFalse(file_exists($path)); + $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); + + @unlink($targetPath); + } + + public function getFilenameFixtures() + { + return array( + array('original.gif', 'original.gif'), + array('..\\..\\original.gif', 'original.gif'), + array('../../original.gif', 'original.gif'), + array('файлfile.gif', 'файлfile.gif'), + array('..\\..\\файлfile.gif', 'файлfile.gif'), + array('../../файлfile.gif', 'файлfile.gif'), + ); + } + + /** + * @dataProvider getFilenameFixtures + */ + public function testMoveWithNonLatinName($filename, $sanitizedFilename) + { + $path = __DIR__.'/Fixtures/'.$sanitizedFilename; + $targetDir = __DIR__.'/Fixtures/directory/'; + $targetPath = $targetDir.$sanitizedFilename; + @unlink($path); + @unlink($targetPath); + copy(__DIR__.'/Fixtures/test.gif', $path); + + $file = new File($path); + $movedFile = $file->move($targetDir, $filename); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\File\File', $movedFile); + + $this->assertTrue(file_exists($targetPath)); + $this->assertFalse(file_exists($path)); + $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); + + @unlink($targetPath); + } + + public function testMoveToAnUnexistentDirectory() + { + $sourcePath = __DIR__.'/Fixtures/test.copy.gif'; + $targetDir = __DIR__.'/Fixtures/directory/sub'; + $targetPath = $targetDir.'/test.copy.gif'; + @unlink($sourcePath); + @unlink($targetPath); + @rmdir($targetDir); + copy(__DIR__.'/Fixtures/test.gif', $sourcePath); + + $file = new File($sourcePath); + $movedFile = $file->move($targetDir); + + $this->assertFileExists($targetPath); + $this->assertFileNotExists($sourcePath); + $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); + + @unlink($sourcePath); + @unlink($targetPath); + @rmdir($targetDir); + } + + protected function createMockGuesser($path, $mimeType) + { + $guesser = $this->getMock('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface'); + $guesser + ->expects($this->once()) + ->method('guess') + ->with($this->equalTo($path)) + ->will($this->returnValue($mimeType)) + ; + + return $guesser; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/.unknownextension b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/.unknownextension new file mode 100644 index 0000000..4d1ae35 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/.unknownextension @@ -0,0 +1 @@ +f \ No newline at end of file diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/directory/.empty b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/directory/.empty new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/other-file.example b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/other-file.example new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/test b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/test new file mode 100644 index 0000000..b636f4b Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/test differ diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/test.gif b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/test.gif new file mode 100644 index 0000000..b636f4b Binary files /dev/null and b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/Fixtures/test.gif differ diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.php new file mode 100644 index 0000000..1d5648e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/MimeType/MimeTypeTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\File\MimeType; + +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser; + +/** + * @requires extension fileinfo + */ +class MimeTypeTest extends \PHPUnit_Framework_TestCase +{ + protected $path; + + public function testGuessImageWithoutExtension() + { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithDirectory() + { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + + MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory'); + } + + public function testGuessImageWithFileBinaryMimeTypeGuesser() + { + $guesser = MimeTypeGuesser::getInstance(); + $guesser->register(new FileBinaryMimeTypeGuesser()); + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithKnownExtension() + { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif')); + } + + public function testGuessFileWithUnknownExtension() + { + $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension')); + } + + public function testGuessWithIncorrectPath() + { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here'); + } + + public function testGuessWithNonReadablePath() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Can not verify chmod operations on Windows'); + } + + if (!getenv('USER') || 'root' === getenv('USER')) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + $path = __DIR__.'/../Fixtures/to_delete'; + touch($path); + @chmod($path, 0333); + + if (substr(sprintf('%o', fileperms($path)), -4) == '0333') { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException'); + MimeTypeGuesser::getInstance()->guess($path); + } else { + $this->markTestSkipped('Can not verify chmod operations, change of file permissions failed'); + } + } + + public static function tearDownAfterClass() + { + $path = __DIR__.'/../Fixtures/to_delete'; + if (file_exists($path)) { + @chmod($path, 0666); + @unlink($path); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php new file mode 100644 index 0000000..5b48970 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/File/UploadedFileTest.php @@ -0,0 +1,272 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\File; + +use Symfony\Component\HttpFoundation\File\UploadedFile; + +class UploadedFileTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + if (!ini_get('file_uploads')) { + $this->markTestSkipped('file_uploads is disabled in php.ini'); + } + } + + public function testConstructWhenFileNotExists() + { + $this->setExpectedException('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + + new UploadedFile( + __DIR__.'/Fixtures/not_here', + 'original.gif', + null + ); + } + + public function testFileUploadsWithNoMimeType() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK + ); + + $this->assertEquals('application/octet-stream', $file->getClientMimeType()); + + if (extension_loaded('fileinfo')) { + $this->assertEquals('image/gif', $file->getMimeType()); + } + } + + public function testFileUploadsWithUnknownMimeType() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/.unknownextension', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/.unknownextension'), + UPLOAD_ERR_OK + ); + + $this->assertEquals('application/octet-stream', $file->getClientMimeType()); + } + + public function testGuessClientExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals('gif', $file->guessClientExtension()); + } + + public function testGuessClientExtensionWithIncorrectMimeType() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/jpeg', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals('jpeg', $file->guessClientExtension()); + } + + public function testErrorIsOkByDefault() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals(UPLOAD_ERR_OK, $file->getError()); + } + + public function testGetClientOriginalName() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals('original.gif', $file->getClientOriginalName()); + } + + public function testGetClientOriginalExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals('gif', $file->getClientOriginalExtension()); + } + + /** + * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException + */ + public function testMoveLocalFileIsNotAllowed() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK + ); + + $movedFile = $file->move(__DIR__.'/Fixtures/directory'); + } + + public function testMoveLocalFileIsAllowedInTestMode() + { + $path = __DIR__.'/Fixtures/test.copy.gif'; + $targetDir = __DIR__.'/Fixtures/directory'; + $targetPath = $targetDir.'/test.copy.gif'; + @unlink($path); + @unlink($targetPath); + copy(__DIR__.'/Fixtures/test.gif', $path); + + $file = new UploadedFile( + $path, + 'original.gif', + 'image/gif', + filesize($path), + UPLOAD_ERR_OK, + true + ); + + $movedFile = $file->move(__DIR__.'/Fixtures/directory'); + + $this->assertTrue(file_exists($targetPath)); + $this->assertFalse(file_exists($path)); + $this->assertEquals(realpath($targetPath), $movedFile->getRealPath()); + + @unlink($targetPath); + } + + public function testGetClientOriginalNameSanitizeFilename() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + '../../original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals('original.gif', $file->getClientOriginalName()); + } + + public function testGetSize() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + null + ); + + $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); + + $file = new UploadedFile( + __DIR__.'/Fixtures/test', + 'original.gif', + 'image/gif' + ); + + $this->assertEquals(filesize(__DIR__.'/Fixtures/test'), $file->getSize()); + } + + public function testGetExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null + ); + + $this->assertEquals('gif', $file->getExtension()); + } + + public function testIsValid() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK, + true + ); + + $this->assertTrue($file->isValid()); + } + + /** + * @dataProvider uploadedFileErrorProvider + */ + public function testIsInvalidOnUploadError($error) + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/test.gif'), + $error + ); + + $this->assertFalse($file->isValid()); + } + + public function uploadedFileErrorProvider() + { + return array( + array(UPLOAD_ERR_INI_SIZE), + array(UPLOAD_ERR_FORM_SIZE), + array(UPLOAD_ERR_PARTIAL), + array(UPLOAD_ERR_NO_TMP_DIR), + array(UPLOAD_ERR_EXTENSION), + ); + } + + public function testIsInvalidIfNotHttpUpload() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK + ); + + $this->assertFalse($file->isValid()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php new file mode 100644 index 0000000..738604b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/FileBagTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\FileBag; + +/** + * FileBagTest. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +class FileBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testFileMustBeAnArrayOrUploadedFile() + { + new FileBag(array('file' => 'foo')); + } + + public function testShouldConvertsUploadedFiles() + { + $tmpFile = $this->createTempFile(); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + + $bag = new FileBag(array('file' => array( + 'name' => basename($tmpFile), + 'type' => 'text/plain', + 'tmp_name' => $tmpFile, + 'error' => 0, + 'size' => 100, + ))); + + $this->assertEquals($file, $bag->get('file')); + } + + public function testShouldSetEmptyUploadedFilesToNull() + { + $bag = new FileBag(array('file' => array( + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => UPLOAD_ERR_NO_FILE, + 'size' => 0, + ))); + + $this->assertNull($bag->get('file')); + } + + public function testShouldConvertUploadedFilesWithPhpBug() + { + $tmpFile = $this->createTempFile(); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + + $bag = new FileBag(array( + 'child' => array( + 'name' => array( + 'file' => basename($tmpFile), + ), + 'type' => array( + 'file' => 'text/plain', + ), + 'tmp_name' => array( + 'file' => $tmpFile, + ), + 'error' => array( + 'file' => 0, + ), + 'size' => array( + 'file' => 100, + ), + ), + )); + + $files = $bag->all(); + $this->assertEquals($file, $files['child']['file']); + } + + public function testShouldConvertNestedUploadedFilesWithPhpBug() + { + $tmpFile = $this->createTempFile(); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + + $bag = new FileBag(array( + 'child' => array( + 'name' => array( + 'sub' => array('file' => basename($tmpFile)), + ), + 'type' => array( + 'sub' => array('file' => 'text/plain'), + ), + 'tmp_name' => array( + 'sub' => array('file' => $tmpFile), + ), + 'error' => array( + 'sub' => array('file' => 0), + ), + 'size' => array( + 'sub' => array('file' => 100), + ), + ), + )); + + $files = $bag->all(); + $this->assertEquals($file, $files['child']['sub']['file']); + } + + public function testShouldNotConvertNestedUploadedFiles() + { + $tmpFile = $this->createTempFile(); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain', 100, 0); + $bag = new FileBag(array('image' => array('file' => $file))); + + $files = $bag->all(); + $this->assertEquals($file, $files['image']['file']); + } + + protected function createTempFile() + { + return tempnam(sys_get_temp_dir().'/form_test', 'FormTest'); + } + + protected function setUp() + { + mkdir(sys_get_temp_dir().'/form_test', 0777, true); + } + + protected function tearDown() + { + foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) { + unlink($file); + } + + rmdir(sys_get_temp_dir().'/form_test'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php new file mode 100644 index 0000000..d4d02d9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/HeaderBagTest.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\HeaderBag; + +class HeaderBagTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $bag = new HeaderBag(array('foo' => 'bar')); + $this->assertTrue($bag->has('foo')); + } + + public function testToStringNull() + { + $bag = new HeaderBag(); + $this->assertEquals('', $bag->__toString()); + } + + public function testToStringNotNull() + { + $bag = new HeaderBag(array('foo' => 'bar')); + $this->assertEquals("Foo: bar\r\n", $bag->__toString()); + } + + public function testKeys() + { + $bag = new HeaderBag(array('foo' => 'bar')); + $keys = $bag->keys(); + $this->assertEquals('foo', $keys[0]); + } + + public function testGetDate() + { + $bag = new HeaderBag(array('foo' => 'Tue, 4 Sep 2012 20:00:00 +0200')); + $headerDate = $bag->getDate('foo'); + $this->assertInstanceOf('DateTime', $headerDate); + } + + /** + * @expectedException \RuntimeException + */ + public function testGetDateException() + { + $bag = new HeaderBag(array('foo' => 'Tue')); + $headerDate = $bag->getDate('foo'); + } + + public function testGetCacheControlHeader() + { + $bag = new HeaderBag(); + $bag->addCacheControlDirective('public', '#a'); + $this->assertTrue($bag->hasCacheControlDirective('public')); + $this->assertEquals('#a', $bag->getCacheControlDirective('public')); + } + + public function testAll() + { + $bag = new HeaderBag(array('foo' => 'bar')); + $this->assertEquals(array('foo' => array('bar')), $bag->all(), '->all() gets all the input'); + + $bag = new HeaderBag(array('FOO' => 'BAR')); + $this->assertEquals(array('foo' => array('BAR')), $bag->all(), '->all() gets all the input key are lower case'); + } + + public function testReplace() + { + $bag = new HeaderBag(array('foo' => 'bar')); + + $bag->replace(array('NOPE' => 'BAR')); + $this->assertEquals(array('nope' => array('BAR')), $bag->all(), '->replace() replaces the input with the argument'); + $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input'); + } + + public function testGet() + { + $bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz')); + $this->assertEquals('bar', $bag->get('foo'), '->get return current value'); + $this->assertEquals('bar', $bag->get('FoO'), '->get key in case insensitive'); + $this->assertEquals(array('bar'), $bag->get('foo', 'nope', false), '->get return the value as array'); + + // defaults + $this->assertNull($bag->get('none'), '->get unknown values returns null'); + $this->assertEquals('default', $bag->get('none', 'default'), '->get unknown values returns default'); + $this->assertEquals(array('default'), $bag->get('none', 'default', false), '->get unknown values returns default as array'); + + $bag->set('foo', 'bor', false); + $this->assertEquals('bar', $bag->get('foo'), '->get return first value'); + $this->assertEquals(array('bar', 'bor'), $bag->get('foo', 'nope', false), '->get return all values as array'); + } + + public function testSetAssociativeArray() + { + $bag = new HeaderBag(); + $bag->set('foo', array('bad-assoc-index' => 'value')); + $this->assertSame('value', $bag->get('foo')); + $this->assertEquals(array('value'), $bag->get('foo', 'nope', false), 'assoc indices of multi-valued headers are ignored'); + } + + public function testContains() + { + $bag = new HeaderBag(array('foo' => 'bar', 'fuzz' => 'bizz')); + $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value'); + $this->assertTrue($bag->contains('fuzz', 'bizz'), '->contains second value'); + $this->assertFalse($bag->contains('nope', 'nope'), '->contains unknown value'); + $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value'); + + // Multiple values + $bag->set('foo', 'bor', false); + $this->assertTrue($bag->contains('foo', 'bar'), '->contains first value'); + $this->assertTrue($bag->contains('foo', 'bor'), '->contains second value'); + $this->assertFalse($bag->contains('foo', 'nope'), '->contains unknown value'); + } + + public function testCacheControlDirectiveAccessors() + { + $bag = new HeaderBag(); + $bag->addCacheControlDirective('public'); + + $this->assertTrue($bag->hasCacheControlDirective('public')); + $this->assertTrue($bag->getCacheControlDirective('public')); + $this->assertEquals('public', $bag->get('cache-control')); + + $bag->addCacheControlDirective('max-age', 10); + $this->assertTrue($bag->hasCacheControlDirective('max-age')); + $this->assertEquals(10, $bag->getCacheControlDirective('max-age')); + $this->assertEquals('max-age=10, public', $bag->get('cache-control')); + + $bag->removeCacheControlDirective('max-age'); + $this->assertFalse($bag->hasCacheControlDirective('max-age')); + } + + public function testCacheControlDirectiveParsing() + { + $bag = new HeaderBag(array('cache-control' => 'public, max-age=10')); + $this->assertTrue($bag->hasCacheControlDirective('public')); + $this->assertTrue($bag->getCacheControlDirective('public')); + + $this->assertTrue($bag->hasCacheControlDirective('max-age')); + $this->assertEquals(10, $bag->getCacheControlDirective('max-age')); + + $bag->addCacheControlDirective('s-maxage', 100); + $this->assertEquals('max-age=10, public, s-maxage=100', $bag->get('cache-control')); + } + + public function testCacheControlDirectiveParsingQuotedZero() + { + $bag = new HeaderBag(array('cache-control' => 'max-age="0"')); + $this->assertTrue($bag->hasCacheControlDirective('max-age')); + $this->assertEquals(0, $bag->getCacheControlDirective('max-age')); + } + + public function testCacheControlDirectiveOverrideWithReplace() + { + $bag = new HeaderBag(array('cache-control' => 'private, max-age=100')); + $bag->replace(array('cache-control' => 'public, max-age=10')); + $this->assertTrue($bag->hasCacheControlDirective('public')); + $this->assertTrue($bag->getCacheControlDirective('public')); + + $this->assertTrue($bag->hasCacheControlDirective('max-age')); + $this->assertEquals(10, $bag->getCacheControlDirective('max-age')); + } + + public function testGetIterator() + { + $headers = array('foo' => 'bar', 'hello' => 'world', 'third' => 'charm'); + $headerBag = new HeaderBag($headers); + + $i = 0; + foreach ($headerBag as $key => $val) { + ++$i; + $this->assertEquals(array($headers[$key]), $val); + } + + $this->assertEquals(count($headers), $i); + } + + public function testCount() + { + $headers = array('foo' => 'bar', 'HELLO' => 'WORLD'); + $headerBag = new HeaderBag($headers); + + $this->assertEquals(count($headers), count($headerBag)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php new file mode 100644 index 0000000..a7bb62a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\IpUtils; + +class IpUtilsTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider testIpv4Provider + */ + public function testIpv4($matches, $remoteAddr, $cidr) + { + $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr)); + } + + public function testIpv4Provider() + { + return array( + array(true, '192.168.1.1', '192.168.1.1'), + array(true, '192.168.1.1', '192.168.1.1/1'), + array(true, '192.168.1.1', '192.168.1.0/24'), + array(false, '192.168.1.1', '1.2.3.4/1'), + array(false, '192.168.1.1', '192.168.1.1/33'), // invalid subnet + array(true, '192.168.1.1', array('1.2.3.4/1', '192.168.1.0/24')), + array(true, '192.168.1.1', array('192.168.1.0/24', '1.2.3.4/1')), + array(false, '192.168.1.1', array('1.2.3.4/1', '4.3.2.1/1')), + array(true, '1.2.3.4', '0.0.0.0/0'), + array(true, '1.2.3.4', '192.168.1.0/0'), + array(false, '1.2.3.4', '256.256.256/0'), // invalid CIDR notation + ); + } + + /** + * @dataProvider testIpv6Provider + */ + public function testIpv6($matches, $remoteAddr, $cidr) + { + if (!defined('AF_INET6')) { + $this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".'); + } + + $this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr)); + } + + public function testIpv6Provider() + { + return array( + array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), + array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), + array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'), + array(true, '0:0:0:0:0:0:0:1', '::1'), + array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'), + array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')), + array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')), + array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')), + ); + } + + /** + * @expectedException \RuntimeException + * @requires extension sockets + */ + public function testAnIpv6WithOptionDisabledIpv6() + { + if (defined('AF_INET6')) { + $this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".'); + } + + IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php new file mode 100644 index 0000000..44f2647 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\JsonResponse; + +class JsonResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructorEmptyCreatesJsonObject() + { + $response = new JsonResponse(); + $this->assertSame('{}', $response->getContent()); + } + + public function testConstructorWithArrayCreatesJsonArray() + { + $response = new JsonResponse(array(0, 1, 2, 3)); + $this->assertSame('[0,1,2,3]', $response->getContent()); + } + + public function testConstructorWithAssocArrayCreatesJsonObject() + { + $response = new JsonResponse(array('foo' => 'bar')); + $this->assertSame('{"foo":"bar"}', $response->getContent()); + } + + public function testConstructorWithSimpleTypes() + { + $response = new JsonResponse('foo'); + $this->assertSame('"foo"', $response->getContent()); + + $response = new JsonResponse(0); + $this->assertSame('0', $response->getContent()); + + $response = new JsonResponse(0.1); + $this->assertSame('0.1', $response->getContent()); + + $response = new JsonResponse(true); + $this->assertSame('true', $response->getContent()); + } + + public function testConstructorWithCustomStatus() + { + $response = new JsonResponse(array(), 202); + $this->assertSame(202, $response->getStatusCode()); + } + + public function testConstructorAddsContentTypeHeader() + { + $response = new JsonResponse(); + $this->assertSame('application/json', $response->headers->get('Content-Type')); + } + + public function testConstructorWithCustomHeaders() + { + $response = new JsonResponse(array(), 200, array('ETag' => 'foo')); + $this->assertSame('application/json', $response->headers->get('Content-Type')); + $this->assertSame('foo', $response->headers->get('ETag')); + } + + public function testConstructorWithCustomContentType() + { + $headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json'); + + $response = new JsonResponse(array(), 200, $headers); + $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type')); + } + + public function testCreate() + { + $response = JsonResponse::create(array('foo' => 'bar'), 204); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertEquals('{"foo":"bar"}', $response->getContent()); + $this->assertEquals(204, $response->getStatusCode()); + } + + public function testStaticCreateEmptyJsonObject() + { + $response = JsonResponse::create(); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('{}', $response->getContent()); + } + + public function testStaticCreateJsonArray() + { + $response = JsonResponse::create(array(0, 1, 2, 3)); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('[0,1,2,3]', $response->getContent()); + } + + public function testStaticCreateJsonObject() + { + $response = JsonResponse::create(array('foo' => 'bar')); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('{"foo":"bar"}', $response->getContent()); + } + + public function testStaticCreateWithSimpleTypes() + { + $response = JsonResponse::create('foo'); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('"foo"', $response->getContent()); + + $response = JsonResponse::create(0); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('0', $response->getContent()); + + $response = JsonResponse::create(0.1); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('0.1', $response->getContent()); + + $response = JsonResponse::create(true); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response); + $this->assertSame('true', $response->getContent()); + } + + public function testStaticCreateWithCustomStatus() + { + $response = JsonResponse::create(array(), 202); + $this->assertSame(202, $response->getStatusCode()); + } + + public function testStaticCreateAddsContentTypeHeader() + { + $response = JsonResponse::create(); + $this->assertSame('application/json', $response->headers->get('Content-Type')); + } + + public function testStaticCreateWithCustomHeaders() + { + $response = JsonResponse::create(array(), 200, array('ETag' => 'foo')); + $this->assertSame('application/json', $response->headers->get('Content-Type')); + $this->assertSame('foo', $response->headers->get('ETag')); + } + + public function testStaticCreateWithCustomContentType() + { + $headers = array('Content-Type' => 'application/vnd.acme.blog-v1+json'); + + $response = JsonResponse::create(array(), 200, $headers); + $this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type')); + } + + public function testSetCallback() + { + $response = JsonResponse::create(array('foo' => 'bar'))->setCallback('callback'); + + $this->assertEquals('/**/callback({"foo":"bar"});', $response->getContent()); + $this->assertEquals('text/javascript', $response->headers->get('Content-Type')); + } + + public function testJsonEncodeFlags() + { + $response = new JsonResponse('<>\'&"'); + + $this->assertEquals('"\u003C\u003E\u0027\u0026\u0022"', $response->getContent()); + } + + public function testGetEncodingOptions() + { + $response = new JsonResponse(); + + $this->assertEquals(JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT, $response->getEncodingOptions()); + } + + public function testSetEncodingOptions() + { + $response = new JsonResponse(); + $response->setData(array(array(1, 2, 3))); + + $this->assertEquals('[[1,2,3]]', $response->getContent()); + + $response->setEncodingOptions(JSON_FORCE_OBJECT); + + $this->assertEquals('{"0":{"0":1,"1":2,"2":3}}', $response->getContent()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetCallbackInvalidIdentifier() + { + $response = new JsonResponse('foo'); + $response->setCallback('+invalid'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetContent() + { + JsonResponse::create("\xB1\x31"); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage This error is expected + */ + public function testSetContentJsonSerializeError() + { + $serializable = new JsonSerializableObject(); + + JsonResponse::create($serializable); + } +} + +if (interface_exists('JsonSerializable')) { + class JsonSerializableObject implements \JsonSerializable + { + public function jsonSerialize() + { + throw new \Exception('This error is expected'); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php new file mode 100644 index 0000000..1f724d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php @@ -0,0 +1,193 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\ParameterBag; + +class ParameterBagTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $this->testAll(); + } + + public function testAll() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $bag->all(), '->all() gets all the input'); + } + + public function testKeys() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $this->assertEquals(array('foo'), $bag->keys()); + } + + public function testAdd() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $bag->add(array('bar' => 'bas')); + $this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all()); + } + + public function testRemove() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $bag->add(array('bar' => 'bas')); + $this->assertEquals(array('foo' => 'bar', 'bar' => 'bas'), $bag->all()); + $bag->remove('bar'); + $this->assertEquals(array('foo' => 'bar'), $bag->all()); + } + + public function testReplace() + { + $bag = new ParameterBag(array('foo' => 'bar')); + + $bag->replace(array('FOO' => 'BAR')); + $this->assertEquals(array('FOO' => 'BAR'), $bag->all(), '->replace() replaces the input with the argument'); + $this->assertFalse($bag->has('foo'), '->replace() overrides previously set the input'); + } + + public function testGet() + { + $bag = new ParameterBag(array('foo' => 'bar', 'null' => null)); + + $this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter'); + $this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined'); + $this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set'); + } + + public function testGetDoesNotUseDeepByDefault() + { + $bag = new ParameterBag(array('foo' => array('bar' => 'moo'))); + + $this->assertNull($bag->get('foo[bar]')); + } + + public function testSet() + { + $bag = new ParameterBag(array()); + + $bag->set('foo', 'bar'); + $this->assertEquals('bar', $bag->get('foo'), '->set() sets the value of parameter'); + + $bag->set('foo', 'baz'); + $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter'); + } + + public function testHas() + { + $bag = new ParameterBag(array('foo' => 'bar')); + + $this->assertTrue($bag->has('foo'), '->has() returns true if a parameter is defined'); + $this->assertFalse($bag->has('unknown'), '->has() return false if a parameter is not defined'); + } + + public function testGetAlpha() + { + $bag = new ParameterBag(array('word' => 'foo_BAR_012')); + + $this->assertEquals('fooBAR', $bag->getAlpha('word'), '->getAlpha() gets only alphabetic characters'); + $this->assertEquals('', $bag->getAlpha('unknown'), '->getAlpha() returns empty string if a parameter is not defined'); + } + + public function testGetAlnum() + { + $bag = new ParameterBag(array('word' => 'foo_BAR_012')); + + $this->assertEquals('fooBAR012', $bag->getAlnum('word'), '->getAlnum() gets only alphanumeric characters'); + $this->assertEquals('', $bag->getAlnum('unknown'), '->getAlnum() returns empty string if a parameter is not defined'); + } + + public function testGetDigits() + { + $bag = new ParameterBag(array('word' => 'foo_BAR_012')); + + $this->assertEquals('012', $bag->getDigits('word'), '->getDigits() gets only digits as string'); + $this->assertEquals('', $bag->getDigits('unknown'), '->getDigits() returns empty string if a parameter is not defined'); + } + + public function testGetInt() + { + $bag = new ParameterBag(array('digits' => '0123')); + + $this->assertEquals(123, $bag->getInt('digits'), '->getInt() gets a value of parameter as integer'); + $this->assertEquals(0, $bag->getInt('unknown'), '->getInt() returns zero if a parameter is not defined'); + } + + public function testFilter() + { + $bag = new ParameterBag(array( + 'digits' => '0123ab', + 'email' => 'example@example.com', + 'url' => 'http://example.com/foo', + 'dec' => '256', + 'hex' => '0x100', + 'array' => array('bang'), + )); + + $this->assertEmpty($bag->filter('nokey'), '->filter() should return empty by default if no key is found'); + + $this->assertEquals('0123', $bag->filter('digits', '', FILTER_SANITIZE_NUMBER_INT), '->filter() gets a value of parameter as integer filtering out invalid characters'); + + $this->assertEquals('example@example.com', $bag->filter('email', '', FILTER_VALIDATE_EMAIL), '->filter() gets a value of parameter as email'); + + $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, array('flags' => FILTER_FLAG_PATH_REQUIRED)), '->filter() gets a value of parameter as URL with a path'); + + // This test is repeated for code-coverage + $this->assertEquals('http://example.com/foo', $bag->filter('url', '', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED), '->filter() gets a value of parameter as URL with a path'); + + $this->assertFalse($bag->filter('dec', '', FILTER_VALIDATE_INT, array( + 'flags' => FILTER_FLAG_ALLOW_HEX, + 'options' => array('min_range' => 1, 'max_range' => 0xff), + )), '->filter() gets a value of parameter as integer between boundaries'); + + $this->assertFalse($bag->filter('hex', '', FILTER_VALIDATE_INT, array( + 'flags' => FILTER_FLAG_ALLOW_HEX, + 'options' => array('min_range' => 1, 'max_range' => 0xff), + )), '->filter() gets a value of parameter as integer between boundaries'); + + $this->assertEquals(array('bang'), $bag->filter('array', ''), '->filter() gets a value of parameter as an array'); + } + + public function testGetIterator() + { + $parameters = array('foo' => 'bar', 'hello' => 'world'); + $bag = new ParameterBag($parameters); + + $i = 0; + foreach ($bag as $key => $val) { + ++$i; + $this->assertEquals($parameters[$key], $val); + } + + $this->assertEquals(count($parameters), $i); + } + + public function testCount() + { + $parameters = array('foo' => 'bar', 'hello' => 'world'); + $bag = new ParameterBag($parameters); + + $this->assertEquals(count($parameters), count($bag)); + } + + public function testGetBoolean() + { + $parameters = array('string_true' => 'true', 'string_false' => 'false'); + $bag = new ParameterBag($parameters); + + $this->assertTrue($bag->getBoolean('string_true'), '->getBoolean() gets the string true as boolean true'); + $this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false'); + $this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php new file mode 100644 index 0000000..2a097d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RedirectResponseTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\RedirectResponse; + +class RedirectResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testGenerateMetaRedirect() + { + $response = new RedirectResponse('foo.bar'); + + $this->assertEquals(1, preg_match( + '##', + preg_replace(array('/\s+/', '/\'/'), array(' ', '"'), $response->getContent()) + )); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRedirectResponseConstructorNullUrl() + { + $response = new RedirectResponse(null); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRedirectResponseConstructorWrongStatusCode() + { + $response = new RedirectResponse('foo.bar', 404); + } + + public function testGenerateLocationHeader() + { + $response = new RedirectResponse('foo.bar'); + + $this->assertTrue($response->headers->has('Location')); + $this->assertEquals('foo.bar', $response->headers->get('Location')); + } + + public function testGetTargetUrl() + { + $response = new RedirectResponse('foo.bar'); + + $this->assertEquals('foo.bar', $response->getTargetUrl()); + } + + public function testSetTargetUrl() + { + $response = new RedirectResponse('foo.bar'); + $response->setTargetUrl('baz.beep'); + + $this->assertEquals('baz.beep', $response->getTargetUrl()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetTargetUrlNull() + { + $response = new RedirectResponse('foo.bar'); + $response->setTargetUrl(null); + } + + public function testCreate() + { + $response = RedirectResponse::create('foo', 301); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertEquals(301, $response->getStatusCode()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestMatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestMatcherTest.php new file mode 100644 index 0000000..cee9bcc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestMatcherTest.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\RequestMatcher; +use Symfony\Component\HttpFoundation\Request; + +class RequestMatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider testMethodFixtures + */ + public function testMethod($requestMethod, $matcherMethod, $isMatch) + { + $matcher = new RequestMatcher(); + $matcher->matchMethod($matcherMethod); + $request = Request::create('', $requestMethod); + $this->assertSame($isMatch, $matcher->matches($request)); + + $matcher = new RequestMatcher(null, null, $matcherMethod); + $request = Request::create('', $requestMethod); + $this->assertSame($isMatch, $matcher->matches($request)); + } + + public function testMethodFixtures() + { + return array( + array('get', 'get', true), + array('get', array('get', 'post'), true), + array('get', 'post', false), + array('get', 'GET', true), + array('get', array('GET', 'POST'), true), + array('get', 'POST', false), + ); + } + + public function testScheme() + { + $httpRequest = $request = $request = Request::create(''); + $httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on')); + + $matcher = new RequestMatcher(); + $matcher->matchScheme('https'); + $this->assertFalse($matcher->matches($httpRequest)); + $this->assertTrue($matcher->matches($httpsRequest)); + + $matcher->matchScheme('http'); + $this->assertFalse($matcher->matches($httpsRequest)); + $this->assertTrue($matcher->matches($httpRequest)); + + $matcher = new RequestMatcher(); + $this->assertTrue($matcher->matches($httpsRequest)); + $this->assertTrue($matcher->matches($httpRequest)); + } + + /** + * @dataProvider testHostFixture + */ + public function testHost($pattern, $isMatch) + { + $matcher = new RequestMatcher(); + $request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com')); + + $matcher->matchHost($pattern); + $this->assertSame($isMatch, $matcher->matches($request)); + + $matcher = new RequestMatcher(null, $pattern); + $this->assertSame($isMatch, $matcher->matches($request)); + } + + public function testHostFixture() + { + return array( + array('.*\.example\.com', true), + array('\.example\.com$', true), + array('^.*\.example\.com$', true), + array('.*\.sensio\.com', false), + array('.*\.example\.COM', true), + array('\.example\.COM$', true), + array('^.*\.example\.COM$', true), + array('.*\.sensio\.COM', false), + ); + } + + public function testPath() + { + $matcher = new RequestMatcher(); + + $request = Request::create('/admin/foo'); + + $matcher->matchPath('/admin/.*'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchPath('/admin'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchPath('^/admin/.*$'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchMethod('/blog/.*'); + $this->assertFalse($matcher->matches($request)); + } + + public function testPathWithLocaleIsNotSupported() + { + $matcher = new RequestMatcher(); + $request = Request::create('/en/login'); + $request->setLocale('en'); + + $matcher->matchPath('^/{_locale}/login$'); + $this->assertFalse($matcher->matches($request)); + } + + public function testPathWithEncodedCharacters() + { + $matcher = new RequestMatcher(); + $request = Request::create('/admin/fo%20o'); + $matcher->matchPath('^/admin/fo o*$'); + $this->assertTrue($matcher->matches($request)); + } + + public function testAttributes() + { + $matcher = new RequestMatcher(); + + $request = Request::create('/admin/foo'); + $request->attributes->set('foo', 'foo_bar'); + + $matcher->matchAttribute('foo', 'foo_.*'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchAttribute('foo', 'foo'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchAttribute('foo', '^foo_bar$'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchAttribute('foo', 'babar'); + $this->assertFalse($matcher->matches($request)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestStackTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestStackTest.php new file mode 100644 index 0000000..e26b806 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestStackTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +class RequestStackTest extends \PHPUnit_Framework_TestCase +{ + public function testGetCurrentRequest() + { + $requestStack = new RequestStack(); + $this->assertNull($requestStack->getCurrentRequest()); + + $request = Request::create('/foo'); + + $requestStack->push($request); + $this->assertSame($request, $requestStack->getCurrentRequest()); + + $this->assertSame($request, $requestStack->pop()); + $this->assertNull($requestStack->getCurrentRequest()); + + $this->assertNull($requestStack->pop()); + } + + public function testGetMasterRequest() + { + $requestStack = new RequestStack(); + $this->assertNull($requestStack->getMasterRequest()); + + $masterRequest = Request::create('/foo'); + $subRequest = Request::create('/bar'); + + $requestStack->push($masterRequest); + $requestStack->push($subRequest); + + $this->assertSame($masterRequest, $requestStack->getMasterRequest()); + } + + public function testGetParentRequest() + { + $requestStack = new RequestStack(); + $this->assertNull($requestStack->getParentRequest()); + + $masterRequest = Request::create('/foo'); + + $requestStack->push($masterRequest); + $this->assertNull($requestStack->getParentRequest()); + + $firstSubRequest = Request::create('/bar'); + + $requestStack->push($firstSubRequest); + $this->assertSame($masterRequest, $requestStack->getParentRequest()); + + $secondSubRequest = Request::create('/baz'); + + $requestStack->push($secondSubRequest); + $this->assertSame($firstSubRequest, $requestStack->getParentRequest()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php new file mode 100644 index 0000000..830bec2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -0,0 +1,1872 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Request; + +class RequestTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $this->testInitialize(); + } + + public function testInitialize() + { + $request = new Request(); + + $request->initialize(array('foo' => 'bar')); + $this->assertEquals('bar', $request->query->get('foo'), '->initialize() takes an array of query parameters as its first argument'); + + $request->initialize(array(), array('foo' => 'bar')); + $this->assertEquals('bar', $request->request->get('foo'), '->initialize() takes an array of request parameters as its second argument'); + + $request->initialize(array(), array(), array('foo' => 'bar')); + $this->assertEquals('bar', $request->attributes->get('foo'), '->initialize() takes an array of attributes as its third argument'); + + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_FOO' => 'bar')); + $this->assertEquals('bar', $request->headers->get('FOO'), '->initialize() takes an array of HTTP headers as its sixth argument'); + } + + public function testGetLocale() + { + $request = new Request(); + $request->setLocale('pl'); + $locale = $request->getLocale(); + $this->assertEquals('pl', $locale); + } + + public function testGetUser() + { + $request = Request::create('http://user_test:password_test@test.com/'); + $user = $request->getUser(); + + $this->assertEquals('user_test', $user); + } + + public function testGetPassword() + { + $request = Request::create('http://user_test:password_test@test.com/'); + $password = $request->getPassword(); + + $this->assertEquals('password_test', $password); + } + + public function testIsNoCache() + { + $request = new Request(); + $isNoCache = $request->isNoCache(); + + $this->assertFalse($isNoCache); + } + + public function testGetContentType() + { + $request = new Request(); + $contentType = $request->getContentType(); + + $this->assertNull($contentType); + } + + public function testSetDefaultLocale() + { + $request = new Request(); + $request->setDefaultLocale('pl'); + $locale = $request->getLocale(); + + $this->assertEquals('pl', $locale); + } + + public function testCreate() + { + $request = Request::create('http://test.com/foo?bar=baz'); + $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com/foo', 'GET', array('bar' => 'baz')); + $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz')); + $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('https://test.com/foo?bar=baz'); + $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(443, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('test.com:90/foo'); + $this->assertEquals('http://test.com:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('test.com', $request->getHost()); + $this->assertEquals('test.com:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('https://test.com:90/foo'); + $this->assertEquals('https://test.com:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('test.com', $request->getHost()); + $this->assertEquals('test.com:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://127.0.0.1:90/foo'); + $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('127.0.0.1', $request->getHost()); + $this->assertEquals('127.0.0.1:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://[::1]:90/foo'); + $this->assertEquals('https://[::1]:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('[::1]', $request->getHost()); + $this->assertEquals('[::1]:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://[::1]/foo'); + $this->assertEquals('https://[::1]/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('[::1]', $request->getHost()); + $this->assertEquals('[::1]', $request->getHttpHost()); + $this->assertEquals(443, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; + $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json); + $this->assertEquals($json, $request->getContent()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com'); + $this->assertEquals('http://test.com/', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com?test=1'); + $this->assertEquals('http://test.com/?test=1', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('test=1', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com:90/?test=1'); + $this->assertEquals('http://test.com:90/?test=1', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('test=1', $request->getQueryString()); + $this->assertEquals(90, $request->getPort()); + $this->assertEquals('test.com:90', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://username:password@test.com'); + $this->assertEquals('http://test.com/', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertEquals('username', $request->getUser()); + $this->assertEquals('password', $request->getPassword()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://username@test.com'); + $this->assertEquals('http://test.com/', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertEquals('username', $request->getUser()); + $this->assertSame('', $request->getPassword()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com/?foo'); + $this->assertEquals('/?foo', $request->getRequestUri()); + $this->assertEquals(array('foo' => ''), $request->query->all()); + + ## assume rewrite rule: (.*) --> app/app.php ; app/ is a symlink to a symfony web/ directory + $request = Request::create('http://test.com/apparthotel-1234', 'GET', array(), array(), array(), + array( + 'DOCUMENT_ROOT' => '/var/www/www.test.com', + 'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php', + 'SCRIPT_NAME' => '/app/app.php', + 'PHP_SELF' => '/app/app.php/apparthotel-1234', + )); + $this->assertEquals('http://test.com/apparthotel-1234', $request->getUri()); + $this->assertEquals('/apparthotel-1234', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + } + + public function testCreateCheckPrecedence() + { + // server is used by default + $request = Request::create('/', 'DELETE', array(), array(), array(), array( + 'HTTP_HOST' => 'example.com', + 'HTTPS' => 'on', + 'SERVER_PORT' => 443, + 'PHP_AUTH_USER' => 'fabien', + 'PHP_AUTH_PW' => 'pa$$', + 'QUERY_STRING' => 'foo=bar', + 'CONTENT_TYPE' => 'application/json', + )); + $this->assertEquals('example.com', $request->getHost()); + $this->assertEquals(443, $request->getPort()); + $this->assertTrue($request->isSecure()); + $this->assertEquals('fabien', $request->getUser()); + $this->assertEquals('pa$$', $request->getPassword()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals('application/json', $request->headers->get('CONTENT_TYPE')); + + // URI has precedence over server + $request = Request::create('http://thomas:pokemon@example.net:8080/?foo=bar', 'GET', array(), array(), array(), array( + 'HTTP_HOST' => 'example.com', + 'HTTPS' => 'on', + 'SERVER_PORT' => 443, + )); + $this->assertEquals('example.net', $request->getHost()); + $this->assertEquals(8080, $request->getPort()); + $this->assertFalse($request->isSecure()); + $this->assertEquals('thomas', $request->getUser()); + $this->assertEquals('pokemon', $request->getPassword()); + $this->assertEquals('foo=bar', $request->getQueryString()); + } + + public function testDuplicate() + { + $request = new Request(array('foo' => 'bar'), array('foo' => 'bar'), array('foo' => 'bar'), array(), array(), array('HTTP_FOO' => 'bar')); + $dup = $request->duplicate(); + + $this->assertEquals($request->query->all(), $dup->query->all(), '->duplicate() duplicates a request an copy the current query parameters'); + $this->assertEquals($request->request->all(), $dup->request->all(), '->duplicate() duplicates a request an copy the current request parameters'); + $this->assertEquals($request->attributes->all(), $dup->attributes->all(), '->duplicate() duplicates a request an copy the current attributes'); + $this->assertEquals($request->headers->all(), $dup->headers->all(), '->duplicate() duplicates a request an copy the current HTTP headers'); + + $dup = $request->duplicate(array('foo' => 'foobar'), array('foo' => 'foobar'), array('foo' => 'foobar'), array(), array(), array('HTTP_FOO' => 'foobar')); + + $this->assertEquals(array('foo' => 'foobar'), $dup->query->all(), '->duplicate() overrides the query parameters if provided'); + $this->assertEquals(array('foo' => 'foobar'), $dup->request->all(), '->duplicate() overrides the request parameters if provided'); + $this->assertEquals(array('foo' => 'foobar'), $dup->attributes->all(), '->duplicate() overrides the attributes if provided'); + $this->assertEquals(array('foo' => array('foobar')), $dup->headers->all(), '->duplicate() overrides the HTTP header if provided'); + } + + public function testDuplicateWithFormat() + { + $request = new Request(array(), array(), array('_format' => 'json')); + $dup = $request->duplicate(); + + $this->assertEquals('json', $dup->getRequestFormat()); + $this->assertEquals('json', $dup->attributes->get('_format')); + + $request = new Request(); + $request->setRequestFormat('xml'); + $dup = $request->duplicate(); + + $this->assertEquals('xml', $dup->getRequestFormat()); + } + + /** + * @dataProvider getFormatToMimeTypeMapProvider + */ + public function testGetFormatFromMimeType($format, $mimeTypes) + { + $request = new Request(); + foreach ($mimeTypes as $mime) { + $this->assertEquals($format, $request->getFormat($mime)); + } + $request->setFormat($format, $mimeTypes); + foreach ($mimeTypes as $mime) { + $this->assertEquals($format, $request->getFormat($mime)); + } + } + + public function testGetFormatFromMimeTypeWithParameters() + { + $request = new Request(); + $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8')); + } + + /** + * @dataProvider getFormatToMimeTypeMapProvider + */ + public function testGetMimeTypeFromFormat($format, $mimeTypes) + { + if (null !== $format) { + $request = new Request(); + $this->assertEquals($mimeTypes[0], $request->getMimeType($format)); + } + } + + public function getFormatToMimeTypeMapProvider() + { + return array( + array(null, array(null, 'unexistent-mime-type')), + array('txt', array('text/plain')), + array('js', array('application/javascript', 'application/x-javascript', 'text/javascript')), + array('css', array('text/css')), + array('json', array('application/json', 'application/x-json')), + array('xml', array('text/xml', 'application/xml', 'application/x-xml')), + array('rdf', array('application/rdf+xml')), + array('atom', array('application/atom+xml')), + ); + } + + public function testGetUri() + { + $server = array(); + + // Standard Request on non default PORT + // http://host:8080/index.php/path/info?query=string + + $server['HTTP_HOST'] = 'host:8080'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '8080'; + + $server['QUERY_STRING'] = 'query=string'; + $server['REQUEST_URI'] = '/index.php/path/info?query=string'; + $server['SCRIPT_NAME'] = '/index.php'; + $server['PATH_INFO'] = '/path/info'; + $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info'; + $server['PHP_SELF'] = '/index_dev.php/path/info'; + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + + $request = new Request(); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://host:8080/index.php/path/info?query=string', $request->getUri(), '->getUri() with non default port'); + + // Use std port number + $server['HTTP_HOST'] = 'host'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://host/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port'); + + // Without HOST HEADER + unset($server['HTTP_HOST']); + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://servername/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port without HOST_HEADER'); + + // Request with URL REWRITING (hide index.php) + // RewriteCond %{REQUEST_FILENAME} !-f + // RewriteRule ^(.*)$ index.php [QSA,L] + // http://host:8080/path/info?query=string + $server = array(); + $server['HTTP_HOST'] = 'host:8080'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '8080'; + + $server['REDIRECT_QUERY_STRING'] = 'query=string'; + $server['REDIRECT_URL'] = '/path/info'; + $server['SCRIPT_NAME'] = '/index.php'; + $server['QUERY_STRING'] = 'query=string'; + $server['REQUEST_URI'] = '/path/info?toto=test&1=1'; + $server['SCRIPT_NAME'] = '/index.php'; + $server['PHP_SELF'] = '/index.php'; + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://host:8080/path/info?query=string', $request->getUri(), '->getUri() with rewrite'); + + // Use std port number + // http://host/path/info?query=string + $server['HTTP_HOST'] = 'host'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://host/path/info?query=string', $request->getUri(), '->getUri() with rewrite and default port'); + + // Without HOST HEADER + unset($server['HTTP_HOST']); + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://servername/path/info?query=string', $request->getUri(), '->getUri() with rewrite, default port without HOST_HEADER'); + + // With encoded characters + + $server = array( + 'HTTP_HOST' => 'host:8080', + 'SERVER_NAME' => 'servername', + 'SERVER_PORT' => '8080', + 'QUERY_STRING' => 'query=string', + 'REQUEST_URI' => '/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', + 'SCRIPT_NAME' => '/ba se/index_dev.php', + 'PATH_TRANSLATED' => 'redirect:/index.php/foo bar/in+fo', + 'PHP_SELF' => '/ba se/index_dev.php/path/info', + 'SCRIPT_FILENAME' => '/some/where/ba se/index_dev.php', + ); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals( + 'http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', + $request->getUri() + ); + + // with user info + + $server['PHP_AUTH_USER'] = 'fabien'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri()); + + $server['PHP_AUTH_PW'] = 'symfony'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri()); + } + + public function testGetUriForPath() + { + $request = Request::create('http://test.com/foo?bar=baz'); + $this->assertEquals('http://test.com/some/path', $request->getUriForPath('/some/path')); + + $request = Request::create('http://test.com:90/foo?bar=baz'); + $this->assertEquals('http://test.com:90/some/path', $request->getUriForPath('/some/path')); + + $request = Request::create('https://test.com/foo?bar=baz'); + $this->assertEquals('https://test.com/some/path', $request->getUriForPath('/some/path')); + + $request = Request::create('https://test.com:90/foo?bar=baz'); + $this->assertEquals('https://test.com:90/some/path', $request->getUriForPath('/some/path')); + + $server = array(); + + // Standard Request on non default PORT + // http://host:8080/index.php/path/info?query=string + + $server['HTTP_HOST'] = 'host:8080'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '8080'; + + $server['QUERY_STRING'] = 'query=string'; + $server['REQUEST_URI'] = '/index.php/path/info?query=string'; + $server['SCRIPT_NAME'] = '/index.php'; + $server['PATH_INFO'] = '/path/info'; + $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info'; + $server['PHP_SELF'] = '/index_dev.php/path/info'; + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + + $request = new Request(); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://host:8080/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with non default port'); + + // Use std port number + $server['HTTP_HOST'] = 'host'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://host/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port'); + + // Without HOST HEADER + unset($server['HTTP_HOST']); + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://servername/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port without HOST_HEADER'); + + // Request with URL REWRITING (hide index.php) + // RewriteCond %{REQUEST_FILENAME} !-f + // RewriteRule ^(.*)$ index.php [QSA,L] + // http://host:8080/path/info?query=string + $server = array(); + $server['HTTP_HOST'] = 'host:8080'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '8080'; + + $server['REDIRECT_QUERY_STRING'] = 'query=string'; + $server['REDIRECT_URL'] = '/path/info'; + $server['SCRIPT_NAME'] = '/index.php'; + $server['QUERY_STRING'] = 'query=string'; + $server['REQUEST_URI'] = '/path/info?toto=test&1=1'; + $server['SCRIPT_NAME'] = '/index.php'; + $server['PHP_SELF'] = '/index.php'; + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://host:8080/some/path', $request->getUriForPath('/some/path'), '->getUri() with rewrite'); + + // Use std port number + // http://host/path/info?query=string + $server['HTTP_HOST'] = 'host'; + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://host/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite and default port'); + + // Without HOST HEADER + unset($server['HTTP_HOST']); + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '80'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite, default port without HOST_HEADER'); + $this->assertEquals('servername', $request->getHttpHost()); + + // with user info + + $server['PHP_AUTH_USER'] = 'fabien'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path')); + + $server['PHP_AUTH_PW'] = 'symfony'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path')); + } + + /** + * @dataProvider getRelativeUriForPathData() + */ + public function testGetRelativeUriForPath($expected, $pathinfo, $path) + { + $this->assertEquals($expected, Request::create($pathinfo)->getRelativeUriForPath($path)); + } + + public function getRelativeUriForPathData() + { + return array( + array('me.png', '/foo', '/me.png'), + array('../me.png', '/foo/bar', '/me.png'), + array('me.png', '/foo/bar', '/foo/me.png'), + array('../baz/me.png', '/foo/bar/b', '/foo/baz/me.png'), + array('../../fooz/baz/me.png', '/foo/bar/b', '/fooz/baz/me.png'), + array('baz/me.png', '/foo/bar/b', 'baz/me.png'), + ); + } + + public function testGetUserInfo() + { + $request = new Request(); + + $server = array('PHP_AUTH_USER' => 'fabien'); + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('fabien', $request->getUserInfo()); + + $server['PHP_AUTH_USER'] = '0'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('0', $request->getUserInfo()); + + $server['PHP_AUTH_PW'] = '0'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('0:0', $request->getUserInfo()); + } + + public function testGetSchemeAndHttpHost() + { + $request = new Request(); + + $server = array(); + $server['SERVER_NAME'] = 'servername'; + $server['SERVER_PORT'] = '90'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); + + $server['PHP_AUTH_USER'] = 'fabien'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); + + $server['PHP_AUTH_USER'] = '0'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); + + $server['PHP_AUTH_PW'] = '0'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost()); + } + + /** + * @dataProvider getQueryStringNormalizationData + */ + public function testGetQueryString($query, $expectedQuery, $msg) + { + $request = new Request(); + + $request->server->set('QUERY_STRING', $query); + $this->assertSame($expectedQuery, $request->getQueryString(), $msg); + } + + public function getQueryStringNormalizationData() + { + return array( + array('foo', 'foo', 'works with valueless parameters'), + array('foo=', 'foo=', 'includes a dangling equal sign'), + array('bar=&foo=bar', 'bar=&foo=bar', '->works with empty parameters'), + array('foo=bar&bar=', 'bar=&foo=bar', 'sorts keys alphabetically'), + + // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded). + // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. + array('him=John%20Doe&her=Jane+Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes spaces in both encodings "%20" and "+"'), + + array('foo[]=1&foo[]=2', 'foo%5B%5D=1&foo%5B%5D=2', 'allows array notation'), + array('foo=1&foo=2', 'foo=1&foo=2', 'allows repeated parameters'), + array('pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'), + array('0', '0', 'allows "0"'), + array('Jane Doe&John%20Doe', 'Jane%20Doe&John%20Doe', 'normalizes encoding in keys'), + array('her=Jane Doe&him=John%20Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes encoding in values'), + array('foo=bar&&&test&&', 'foo=bar&test', 'removes unneeded delimiters'), + array('formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'), + + // Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway. + // PHP also does not include them when building _GET. + array('foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'), + ); + } + + public function testGetQueryStringReturnsNull() + { + $request = new Request(); + + $this->assertNull($request->getQueryString(), '->getQueryString() returns null for non-existent query string'); + + $request->server->set('QUERY_STRING', ''); + $this->assertNull($request->getQueryString(), '->getQueryString() returns null for empty query string'); + } + + public function testGetHost() + { + $request = new Request(); + + $request->initialize(array('foo' => 'bar')); + $this->assertEquals('', $request->getHost(), '->getHost() return empty string if not initialized'); + + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com')); + $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header'); + + // Host header with port number + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com:8080')); + $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header with port number'); + + // Server values + $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com')); + $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from server name'); + + $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com', 'HTTP_HOST' => 'www.host.com')); + $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME '); + } + + public function testGetPort() + { + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https', + 'HTTP_X_FORWARDED_PORT' => '443', + )); + $port = $request->getPort(); + + $this->assertEquals(80, $port, 'Without trusted proxies FORWARDED_PROTO and FORWARDED_PORT are ignored.'); + + Request::setTrustedProxies(array('1.1.1.1')); + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https', + 'HTTP_X_FORWARDED_PORT' => '8443', + )); + $this->assertEquals(80, $request->getPort(), 'With PROTO and PORT on untrusted connection server value takes precedence.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(8443, $request->getPort(), 'With PROTO and PORT set PORT takes precedence.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https', + )); + $this->assertEquals(80, $request->getPort(), 'With only PROTO set getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(443, $request->getPort(), 'With only PROTO set getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'http', + )); + $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() returns port of the original request.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'On', + )); + $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is On, getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is On, getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => '1', + )); + $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is 1, getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is 1, getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'something-else', + )); + $port = $request->getPort(); + $this->assertEquals(80, $port, 'With only PROTO set and value is not recognized, getPort() defaults to 80.'); + + Request::setTrustedProxies(array()); + } + + /** + * @expectedException \RuntimeException + */ + public function testGetHostWithFakeHttpHostValue() + { + $request = new Request(); + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.host.com?query=string')); + $request->getHost(); + } + + public function testGetSetMethod() + { + $request = new Request(); + + $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns GET if no method is defined'); + + $request->setMethod('get'); + $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns an uppercased string'); + + $request->setMethod('PURGE'); + $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method even if it is not a standard one'); + + $request->setMethod('POST'); + $this->assertEquals('POST', $request->getMethod(), '->getMethod() returns the method POST if no _method is defined'); + + $request->setMethod('POST'); + $request->request->set('_method', 'purge'); + $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled'); + + $request = new Request(); + $request->setMethod('POST'); + $request->request->set('_method', 'purge'); + + $this->assertFalse(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be disabled by default'); + + Request::enableHttpMethodParameterOverride(); + + $this->assertTrue(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be enabled now but it is not'); + + $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); + $this->disableHttpMethodParameterOverride(); + + $request = new Request(); + $request->setMethod('POST'); + $request->query->set('_method', 'purge'); + $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled'); + + $request = new Request(); + $request->setMethod('POST'); + $request->query->set('_method', 'purge'); + Request::enableHttpMethodParameterOverride(); + $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); + $this->disableHttpMethodParameterOverride(); + + $request = new Request(); + $request->setMethod('POST'); + $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete'); + $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override even though _method is set if defined and POST'); + + $request = new Request(); + $request->setMethod('POST'); + $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete'); + $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override if defined and POST'); + } + + /** + * @dataProvider testGetClientIpsProvider + */ + public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies); + + $this->assertEquals($expected[0], $request->getClientIp()); + + Request::setTrustedProxies(array()); + } + + /** + * @dataProvider testGetClientIpsProvider + */ + public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies); + + $this->assertEquals($expected, $request->getClientIps()); + + Request::setTrustedProxies(array()); + } + + /** + * @dataProvider testGetClientIpsForwardedProvider + */ + public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies); + + $this->assertEquals($expected, $request->getClientIps()); + + Request::setTrustedProxies(array()); + } + + public function testGetClientIpsForwardedProvider() + { + // $expected $remoteAddr $httpForwarded $trustedProxies + return array( + array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null), + array(array('_gazonk'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')), + array(array('88.88.88.88'), '127.0.0.1', 'for="88.88.88.88:80"', array('127.0.0.1')), + array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')), + array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3', array('::1')), + array(array('2001:db8:cafe::17'), '::1', 'for="[2001:db8:cafe::17]:4711', array('::1')), + ); + } + + public function testGetClientIpsProvider() + { + // $expected $remoteAddr $httpForwardedFor $trustedProxies + return array( + // simple IPv4 + array(array('88.88.88.88'), '88.88.88.88', null, null), + // trust the IPv4 remote addr + array(array('88.88.88.88'), '88.88.88.88', null, array('88.88.88.88')), + + // simple IPv6 + array(array('::1'), '::1', null, null), + // trust the IPv6 remote addr + array(array('::1'), '::1', null, array('::1')), + + // forwarded for with remote IPv4 addr not trusted + array(array('127.0.0.1'), '127.0.0.1', '88.88.88.88', null), + // forwarded for with remote IPv4 addr trusted + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1')), + // forwarded for with remote IPv4 and all FF addrs trusted + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')), + // forwarded for with remote IPv4 range trusted + array(array('88.88.88.88'), '123.45.67.89', '88.88.88.88', array('123.45.67.0/24')), + + // forwarded for with remote IPv6 addr not trusted + array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', null), + // forwarded for with remote IPv6 addr trusted + array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')), + // forwarded for with remote IPv6 range trusted + array(array('88.88.88.88'), '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88', array('2a01:198:603:0::/65')), + + // multiple forwarded for with remote IPv4 addr trusted + array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')), + // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted + array(array('87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')), + // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle + array(array('88.88.88.88', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21')), + // multiple forwarded for with remote IPv4 addr and all reverse proxies trusted + array(array('127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21', '88.88.88.88', '127.0.0.1')), + + // multiple forwarded for with remote IPv6 addr trusted + array(array('2620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')), + // multiple forwarded for with remote IPv6 addr and some reverse proxies trusted + array(array('3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3')), + // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle + array(array('2620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3,3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3')), + + // client IP with port + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88:12345, 127.0.0.1', array('127.0.0.1')), + ); + } + + public function testGetContentWorksTwiceInDefaultMode() + { + $req = new Request(); + $this->assertEquals('', $req->getContent()); + $this->assertEquals('', $req->getContent()); + } + + public function testGetContentReturnsResource() + { + $req = new Request(); + $retval = $req->getContent(true); + $this->assertInternalType('resource', $retval); + $this->assertEquals('', fread($retval, 1)); + $this->assertTrue(feof($retval)); + } + + public function testGetContentReturnsResourceWhenContentSetInConstructor() + { + $req = new Request(array(), array(), array(), array(), array(), array(), 'MyContent'); + $resource = $req->getContent(true); + + $this->assertTrue(is_resource($resource)); + $this->assertEquals('MyContent', stream_get_contents($resource)); + } + + public function testContentAsResource() + { + $resource = fopen('php://memory', 'r+'); + fwrite($resource, 'My other content'); + rewind($resource); + + $req = new Request(array(), array(), array(), array(), array(), array(), $resource); + $this->assertEquals('My other content', stream_get_contents($req->getContent(true))); + $this->assertEquals('My other content', $req->getContent()); + } + + /** + * @expectedException \LogicException + * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider + */ + public function testGetContentCantBeCalledTwiceWithResources($first, $second) + { + if (PHP_VERSION_ID >= 50600) { + $this->markTestSkipped('PHP >= 5.6 allows to open php://input several times.'); + } + + $req = new Request(); + $req->getContent($first); + $req->getContent($second); + } + + /** + * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider + * @requires PHP 5.6 + */ + public function testGetContentCanBeCalledTwiceWithResources($first, $second) + { + $req = new Request(); + $a = $req->getContent($first); + $b = $req->getContent($second); + + if ($first) { + $a = stream_get_contents($a); + } + + if ($second) { + $b = stream_get_contents($b); + } + + $this->assertEquals($a, $b); + } + + public function getContentCantBeCalledTwiceWithResourcesProvider() + { + return array( + 'Resource then fetch' => array(true, false), + 'Resource then resource' => array(true, true), + ); + } + + public function provideOverloadedMethods() + { + return array( + array('PUT'), + array('DELETE'), + array('PATCH'), + array('put'), + array('delete'), + array('patch'), + + ); + } + + /** + * @dataProvider provideOverloadedMethods + */ + public function testCreateFromGlobals($method) + { + $normalizedMethod = strtoupper($method); + + $_GET['foo1'] = 'bar1'; + $_POST['foo2'] = 'bar2'; + $_COOKIE['foo3'] = 'bar3'; + $_FILES['foo4'] = array('bar4'); + $_SERVER['foo5'] = 'bar5'; + + $request = Request::createFromGlobals(); + $this->assertEquals('bar1', $request->query->get('foo1'), '::fromGlobals() uses values from $_GET'); + $this->assertEquals('bar2', $request->request->get('foo2'), '::fromGlobals() uses values from $_POST'); + $this->assertEquals('bar3', $request->cookies->get('foo3'), '::fromGlobals() uses values from $_COOKIE'); + $this->assertEquals(array('bar4'), $request->files->get('foo4'), '::fromGlobals() uses values from $_FILES'); + $this->assertEquals('bar5', $request->server->get('foo5'), '::fromGlobals() uses values from $_SERVER'); + + unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']); + + $_SERVER['REQUEST_METHOD'] = $method; + $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $request = RequestContentProxy::createFromGlobals(); + $this->assertEquals($normalizedMethod, $request->getMethod()); + $this->assertEquals('mycontent', $request->request->get('content')); + + unset($_SERVER['REQUEST_METHOD'], $_SERVER['CONTENT_TYPE']); + + Request::createFromGlobals(); + Request::enableHttpMethodParameterOverride(); + $_POST['_method'] = $method; + $_POST['foo6'] = 'bar6'; + $_SERVER['REQUEST_METHOD'] = 'PoSt'; + $request = Request::createFromGlobals(); + $this->assertEquals($normalizedMethod, $request->getMethod()); + $this->assertEquals('POST', $request->getRealMethod()); + $this->assertEquals('bar6', $request->request->get('foo6')); + + unset($_POST['_method'], $_POST['foo6'], $_SERVER['REQUEST_METHOD']); + $this->disableHttpMethodParameterOverride(); + } + + public function testOverrideGlobals() + { + $request = new Request(); + $request->initialize(array('foo' => 'bar')); + + // as the Request::overrideGlobals really work, it erase $_SERVER, so we must backup it + $server = $_SERVER; + + $request->overrideGlobals(); + + $this->assertEquals(array('foo' => 'bar'), $_GET); + + $request->initialize(array(), array('foo' => 'bar')); + $request->overrideGlobals(); + + $this->assertEquals(array('foo' => 'bar'), $_POST); + + $this->assertArrayNotHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); + + $request->headers->set('X_FORWARDED_PROTO', 'https'); + + Request::setTrustedProxies(array('1.1.1.1')); + $this->assertFalse($request->isSecure()); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertTrue($request->isSecure()); + Request::setTrustedProxies(array()); + + $request->overrideGlobals(); + + $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER); + + $request->headers->set('CONTENT_TYPE', 'multipart/form-data'); + $request->headers->set('CONTENT_LENGTH', 12345); + + $request->overrideGlobals(); + + $this->assertArrayHasKey('CONTENT_TYPE', $_SERVER); + $this->assertArrayHasKey('CONTENT_LENGTH', $_SERVER); + + $request->initialize(array('foo' => 'bar', 'baz' => 'foo')); + $request->query->remove('baz'); + + $request->overrideGlobals(); + + $this->assertEquals(array('foo' => 'bar'), $_GET); + $this->assertEquals('foo=bar', $_SERVER['QUERY_STRING']); + $this->assertEquals('foo=bar', $request->server->get('QUERY_STRING')); + + // restore initial $_SERVER array + $_SERVER = $server; + } + + public function testGetScriptName() + { + $request = new Request(); + $this->assertEquals('', $request->getScriptName()); + + $server = array(); + $server['SCRIPT_NAME'] = '/index.php'; + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/index.php', $request->getScriptName()); + + $server = array(); + $server['ORIG_SCRIPT_NAME'] = '/frontend.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/frontend.php', $request->getScriptName()); + + $server = array(); + $server['SCRIPT_NAME'] = '/index.php'; + $server['ORIG_SCRIPT_NAME'] = '/frontend.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/index.php', $request->getScriptName()); + } + + public function testGetBasePath() + { + $request = new Request(); + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $server['SCRIPT_NAME'] = '/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $server['PHP_SELF'] = '/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('', $request->getBasePath()); + + $server = array(); + $server['SCRIPT_FILENAME'] = '/some/where/index.php'; + $server['ORIG_SCRIPT_NAME'] = '/index.php'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('', $request->getBasePath()); + } + + public function testGetPathInfo() + { + $request = new Request(); + $this->assertEquals('/', $request->getPathInfo()); + + $server = array(); + $server['REQUEST_URI'] = '/path/info'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/path/info', $request->getPathInfo()); + + $server = array(); + $server['REQUEST_URI'] = '/path%20test/info'; + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertEquals('/path%20test/info', $request->getPathInfo()); + } + + public function testGetParameterPrecedence() + { + $request = new Request(); + $request->attributes->set('foo', 'attr'); + $request->query->set('foo', 'query'); + $request->request->set('foo', 'body'); + + $this->assertSame('attr', $request->get('foo')); + + $request->attributes->remove('foo'); + $this->assertSame('query', $request->get('foo')); + + $request->query->remove('foo'); + $this->assertSame('body', $request->get('foo')); + + $request->request->remove('foo'); + $this->assertNull($request->get('foo')); + } + + public function testGetPreferredLanguage() + { + $request = new Request(); + $this->assertNull($request->getPreferredLanguage()); + $this->assertNull($request->getPreferredLanguage(array())); + $this->assertEquals('fr', $request->getPreferredLanguage(array('fr'))); + $this->assertEquals('fr', $request->getPreferredLanguage(array('fr', 'en'))); + $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'fr'))); + $this->assertEquals('fr-ch', $request->getPreferredLanguage(array('fr-ch', 'fr-fr'))); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'en-us'))); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8'); + $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5'); + $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en'))); + } + + public function testIsXmlHttpRequest() + { + $request = new Request(); + $this->assertFalse($request->isXmlHttpRequest()); + + $request->headers->set('X-Requested-With', 'XMLHttpRequest'); + $this->assertTrue($request->isXmlHttpRequest()); + + $request->headers->remove('X-Requested-With'); + $this->assertFalse($request->isXmlHttpRequest()); + } + + /** + * @requires extension intl + */ + public function testIntlLocale() + { + $request = new Request(); + + $request->setDefaultLocale('fr'); + $this->assertEquals('fr', $request->getLocale()); + $this->assertEquals('fr', \Locale::getDefault()); + + $request->setLocale('en'); + $this->assertEquals('en', $request->getLocale()); + $this->assertEquals('en', \Locale::getDefault()); + + $request->setDefaultLocale('de'); + $this->assertEquals('en', $request->getLocale()); + $this->assertEquals('en', \Locale::getDefault()); + } + + public function testGetCharsets() + { + $request = new Request(); + $this->assertEquals(array(), $request->getCharsets()); + $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6'); + $this->assertEquals(array(), $request->getCharsets()); // testing caching + + $request = new Request(); + $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6'); + $this->assertEquals(array('ISO-8859-1', 'US-ASCII', 'UTF-8', 'ISO-10646-UCS-2'), $request->getCharsets()); + + $request = new Request(); + $request->headers->set('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'); + $this->assertEquals(array('ISO-8859-1', 'utf-8', '*'), $request->getCharsets()); + } + + public function testGetEncodings() + { + $request = new Request(); + $this->assertEquals(array(), $request->getEncodings()); + $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch'); + $this->assertEquals(array(), $request->getEncodings()); // testing caching + + $request = new Request(); + $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch'); + $this->assertEquals(array('gzip', 'deflate', 'sdch'), $request->getEncodings()); + + $request = new Request(); + $request->headers->set('Accept-Encoding', 'gzip;q=0.4,deflate;q=0.9,compress;q=0.7'); + $this->assertEquals(array('deflate', 'compress', 'gzip'), $request->getEncodings()); + } + + public function testGetAcceptableContentTypes() + { + $request = new Request(); + $this->assertEquals(array(), $request->getAcceptableContentTypes()); + $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*'); + $this->assertEquals(array(), $request->getAcceptableContentTypes()); // testing caching + + $request = new Request(); + $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*'); + $this->assertEquals(array('application/vnd.wap.wmlscriptc', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml', 'application/xhtml+xml', 'text/html', 'multipart/mixed', '*/*'), $request->getAcceptableContentTypes()); + } + + public function testGetLanguages() + { + $request = new Request(); + $this->assertEquals(array(), $request->getLanguages()); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages()); + $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages()); + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en-us; q=0.6, en; q=0.8'); + $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test out of order qvalues + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, en, en-us'); + $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test equal weighting without qvalues + + $request = new Request(); + $request->headers->set('Accept-language', 'zh; q=0.6, en, en-us; q=0.6'); + $this->assertEquals(array('en', 'zh', 'en_US'), $request->getLanguages()); // Test equal weighting with qvalues + + $request = new Request(); + $request->headers->set('Accept-language', 'zh, i-cherokee; q=0.6'); + $this->assertEquals(array('zh', 'cherokee'), $request->getLanguages()); + } + + public function testGetRequestFormat() + { + $request = new Request(); + $this->assertEquals('html', $request->getRequestFormat()); + + $request = new Request(); + $this->assertNull($request->getRequestFormat(null)); + + $request = new Request(); + $this->assertNull($request->setRequestFormat('foo')); + $this->assertEquals('foo', $request->getRequestFormat(null)); + + $request = new Request(array('_format' => 'foo')); + $this->assertEquals('html', $request->getRequestFormat()); + } + + public function testHasSession() + { + $request = new Request(); + + $this->assertFalse($request->hasSession()); + $request->setSession(new Session(new MockArraySessionStorage())); + $this->assertTrue($request->hasSession()); + } + + public function testGetSession() + { + $request = new Request(); + + $request->setSession(new Session(new MockArraySessionStorage())); + $this->assertTrue($request->hasSession()); + + $session = $request->getSession(); + $this->assertObjectHasAttribute('storage', $session); + $this->assertObjectHasAttribute('flashName', $session); + $this->assertObjectHasAttribute('attributeName', $session); + } + + public function testHasPreviousSession() + { + $request = new Request(); + + $this->assertFalse($request->hasPreviousSession()); + $request->cookies->set('MOCKSESSID', 'foo'); + $this->assertFalse($request->hasPreviousSession()); + $request->setSession(new Session(new MockArraySessionStorage())); + $this->assertTrue($request->hasPreviousSession()); + } + + public function testToString() + { + $request = new Request(); + + $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6'); + + $this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $request->__toString()); + } + + public function testIsMethod() + { + $request = new Request(); + $request->setMethod('POST'); + $this->assertTrue($request->isMethod('POST')); + $this->assertTrue($request->isMethod('post')); + $this->assertFalse($request->isMethod('GET')); + $this->assertFalse($request->isMethod('get')); + + $request->setMethod('GET'); + $this->assertTrue($request->isMethod('GET')); + $this->assertTrue($request->isMethod('get')); + $this->assertFalse($request->isMethod('POST')); + $this->assertFalse($request->isMethod('post')); + } + + /** + * @dataProvider getBaseUrlData + */ + public function testGetBaseUrl($uri, $server, $expectedBaseUrl, $expectedPathInfo) + { + $request = Request::create($uri, 'GET', array(), array(), array(), $server); + + $this->assertSame($expectedBaseUrl, $request->getBaseUrl(), 'baseUrl'); + $this->assertSame($expectedPathInfo, $request->getPathInfo(), 'pathInfo'); + } + + public function getBaseUrlData() + { + return array( + array( + '/fruit/strawberry/1234index.php/blah', + array( + 'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/fruit/index.php', + 'SCRIPT_NAME' => '/fruit/index.php', + 'PHP_SELF' => '/fruit/index.php', + ), + '/fruit', + '/strawberry/1234index.php/blah', + ), + array( + '/fruit/strawberry/1234index.php/blah', + array( + 'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/index.php', + 'SCRIPT_NAME' => '/index.php', + 'PHP_SELF' => '/index.php', + ), + '', + '/fruit/strawberry/1234index.php/blah', + ), + array( + '/foo%20bar/', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar', + '/', + ), + array( + '/foo%20bar/home', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar', + '/home', + ), + array( + '/foo%20bar/app.php/home', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar/app.php', + '/home', + ), + array( + '/foo%20bar/app.php/home%3Dbaz', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar/app.php', + '/home%3Dbaz', + ), + array( + '/foo/bar+baz', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', + 'SCRIPT_NAME' => '/foo/app.php', + 'PHP_SELF' => '/foo/app.php', + ), + '/foo', + '/bar+baz', + ), + ); + } + + /** + * @dataProvider urlencodedStringPrefixData + */ + public function testUrlencodedStringPrefix($string, $prefix, $expect) + { + $request = new Request(); + + $me = new \ReflectionMethod($request, 'getUrlencodedPrefix'); + $me->setAccessible(true); + + $this->assertSame($expect, $me->invoke($request, $string, $prefix)); + } + + public function urlencodedStringPrefixData() + { + return array( + array('foo', 'foo', 'foo'), + array('fo%6f', 'foo', 'fo%6f'), + array('foo/bar', 'foo', 'foo'), + array('fo%6f/bar', 'foo', 'fo%6f'), + array('f%6f%6f/bar', 'foo', 'f%6f%6f'), + array('%66%6F%6F/bar', 'foo', '%66%6F%6F'), + array('fo+o/bar', 'fo+o', 'fo+o'), + array('fo%2Bo/bar', 'fo+o', 'fo%2Bo'), + ); + } + + private function disableHttpMethodParameterOverride() + { + $class = new \ReflectionClass('Symfony\\Component\\HttpFoundation\\Request'); + $property = $class->getProperty('httpMethodParameterOverride'); + $property->setAccessible(true); + $property->setValue(false); + } + + private function getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies) + { + $request = new Request(); + + $server = array('REMOTE_ADDR' => $remoteAddr); + if (null !== $httpForwardedFor) { + $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor; + } + + if ($trustedProxies) { + Request::setTrustedProxies($trustedProxies); + } + + $request->initialize(array(), array(), array(), array(), array(), $server); + + return $request; + } + + private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies) + { + $request = new Request(); + + $server = array('REMOTE_ADDR' => $remoteAddr); + + if (null !== $httpForwarded) { + $server['HTTP_FORWARDED'] = $httpForwarded; + } + + if ($trustedProxies) { + Request::setTrustedProxies($trustedProxies); + } + + $request->initialize(array(), array(), array(), array(), array(), $server); + + return $request; + } + + public function testTrustedProxies() + { + $request = Request::create('http://example.com/'); + $request->server->set('REMOTE_ADDR', '3.3.3.3'); + $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2'); + $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080'); + $request->headers->set('X_FORWARDED_PROTO', 'https'); + $request->headers->set('X_FORWARDED_PORT', 443); + $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4'); + $request->headers->set('X_MY_HOST', 'my.example.com'); + $request->headers->set('X_MY_PROTO', 'http'); + $request->headers->set('X_MY_PORT', 81); + + // no trusted proxies + $this->assertEquals('3.3.3.3', $request->getClientIp()); + $this->assertEquals('example.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + $this->assertFalse($request->isSecure()); + + // disabling proxy trusting + Request::setTrustedProxies(array()); + $this->assertEquals('3.3.3.3', $request->getClientIp()); + $this->assertEquals('example.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + $this->assertFalse($request->isSecure()); + + // trusted proxy via setTrustedProxies() + Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2')); + $this->assertEquals('1.1.1.1', $request->getClientIp()); + $this->assertEquals('real.example.com', $request->getHost()); + $this->assertEquals(443, $request->getPort()); + $this->assertTrue($request->isSecure()); + + // trusted proxy via setTrustedProxies() + Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2')); + $this->assertEquals('3.3.3.3', $request->getClientIp()); + $this->assertEquals('example.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + $this->assertFalse($request->isSecure()); + + // check various X_FORWARDED_PROTO header values + Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2')); + $request->headers->set('X_FORWARDED_PROTO', 'ssl'); + $this->assertTrue($request->isSecure()); + + $request->headers->set('X_FORWARDED_PROTO', 'https, http'); + $this->assertTrue($request->isSecure()); + + // custom header names + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_MY_FOR'); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_MY_HOST'); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_MY_PORT'); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_MY_PROTO'); + $this->assertEquals('4.4.4.4', $request->getClientIp()); + $this->assertEquals('my.example.com', $request->getHost()); + $this->assertEquals(81, $request->getPort()); + $this->assertFalse($request->isSecure()); + + // disabling via empty header names + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, null); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, null); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, null); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, null); + $this->assertEquals('3.3.3.3', $request->getClientIp()); + $this->assertEquals('example.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + $this->assertFalse($request->isSecure()); + + // reset + Request::setTrustedProxies(array()); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR'); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST'); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT'); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetTrustedProxiesInvalidHeaderName() + { + Request::create('http://example.com/'); + Request::setTrustedHeaderName('bogus name', 'X_MY_FOR'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetTrustedProxiesInvalidHeaderName() + { + Request::create('http://example.com/'); + Request::getTrustedHeaderName('bogus name'); + } + + /** + * @dataProvider iisRequestUriProvider + */ + public function testIISRequestUri($headers, $server, $expectedRequestUri) + { + $request = new Request(); + $request->headers->replace($headers); + $request->server->replace($server); + + $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct'); + + $subRequestUri = '/bar/foo'; + $subRequest = Request::create($subRequestUri, 'get', array(), array(), array(), $request->server->all()); + $this->assertEquals($subRequestUri, $subRequest->getRequestUri(), '->getRequestUri() is correct in sub request'); + } + + public function iisRequestUriProvider() + { + return array( + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array(), + '/foo/bar', + ), + array( + array( + 'X_REWRITE_URL' => '/foo/bar', + ), + array(), + '/foo/bar', + ), + array( + array(), + array( + 'IIS_WasUrlRewritten' => '1', + 'UNENCODED_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array( + 'HTTP_X_ORIGINAL_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array( + 'IIS_WasUrlRewritten' => '1', + 'UNENCODED_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array( + 'HTTP_X_ORIGINAL_URL' => '/foo/bar', + 'IIS_WasUrlRewritten' => '1', + 'UNENCODED_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array(), + array( + 'ORIG_PATH_INFO' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array(), + array( + 'ORIG_PATH_INFO' => '/foo/bar', + 'QUERY_STRING' => 'foo=bar', + ), + '/foo/bar?foo=bar', + ), + ); + } + + public function testTrustedHosts() + { + // create a request + $request = Request::create('/'); + + // no trusted host set -> no host check + $request->headers->set('host', 'evil.com'); + $this->assertEquals('evil.com', $request->getHost()); + + // add a trusted domain and all its subdomains + Request::setTrustedHosts(array('^([a-z]{9}\.)?trusted\.com$')); + + // untrusted host + $request->headers->set('host', 'evil.com'); + try { + $request->getHost(); + $this->fail('Request::getHost() should throw an exception when host is not trusted.'); + } catch (\UnexpectedValueException $e) { + $this->assertEquals('Untrusted Host "evil.com"', $e->getMessage()); + } + + // trusted hosts + $request->headers->set('host', 'trusted.com'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + + $request->server->set('HTTPS', true); + $request->headers->set('host', 'trusted.com'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(443, $request->getPort()); + $request->server->set('HTTPS', false); + + $request->headers->set('host', 'trusted.com:8000'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(8000, $request->getPort()); + + $request->headers->set('host', 'subdomain.trusted.com'); + $this->assertEquals('subdomain.trusted.com', $request->getHost()); + + // reset request for following tests + Request::setTrustedHosts(array()); + } + + public function testFactory() + { + Request::setFactory(function (array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { + return new NewRequest(); + }); + + $this->assertEquals('foo', Request::create('/')->getFoo()); + + Request::setFactory(null); + } + + /** + * @dataProvider getLongHostNames + */ + public function testVeryLongHosts($host) + { + $start = microtime(true); + + $request = Request::create('/'); + $request->headers->set('host', $host); + $this->assertEquals($host, $request->getHost()); + $this->assertLessThan(1, microtime(true) - $start); + } + + /** + * @dataProvider getHostValidities + */ + public function testHostValidity($host, $isValid, $expectedHost = null, $expectedPort = null) + { + $request = Request::create('/'); + $request->headers->set('host', $host); + + if ($isValid) { + $this->assertSame($expectedHost ?: $host, $request->getHost()); + if ($expectedPort) { + $this->assertSame($expectedPort, $request->getPort()); + } + } else { + $this->setExpectedException('UnexpectedValueException', 'Invalid Host'); + $request->getHost(); + } + } + + public function getHostValidities() + { + return array( + array('.a', false), + array('a..', false), + array('a.', true), + array("\xE9", false), + array('[::1]', true), + array('[::1]:80', true, '[::1]', 80), + array(str_repeat('.', 101), false), + ); + } + + public function getLongHostNames() + { + return array( + array('a'.str_repeat('.a', 40000)), + array(str_repeat(':', 101)), + ); + } +} + +class RequestContentProxy extends Request +{ + public function getContent($asResource = false) + { + return http_build_query(array('_method' => 'PUT', 'content' => 'mycontent')); + } +} + +class NewRequest extends Request +{ + public function getFoo() + { + return 'foo'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php new file mode 100644 index 0000000..8e487d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php @@ -0,0 +1,297 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Cookie; + +/** + * @group time-sensitive + */ +class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider provideAllPreserveCase + */ + public function testAllPreserveCase($headers, $expected) + { + $bag = new ResponseHeaderBag($headers); + + $this->assertEquals($expected, $bag->allPreserveCase(), '->allPreserveCase() gets all input keys in original case'); + } + + public function provideAllPreserveCase() + { + return array( + array( + array('fOo' => 'BAR'), + array('fOo' => array('BAR'), 'Cache-Control' => array('no-cache')), + ), + array( + array('ETag' => 'xyzzy'), + array('ETag' => array('xyzzy'), 'Cache-Control' => array('private, must-revalidate')), + ), + array( + array('Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ=='), + array('Content-MD5' => array('Q2hlY2sgSW50ZWdyaXR5IQ=='), 'Cache-Control' => array('no-cache')), + ), + array( + array('P3P' => 'CP="CAO PSA OUR"'), + array('P3P' => array('CP="CAO PSA OUR"'), 'Cache-Control' => array('no-cache')), + ), + array( + array('WWW-Authenticate' => 'Basic realm="WallyWorld"'), + array('WWW-Authenticate' => array('Basic realm="WallyWorld"'), 'Cache-Control' => array('no-cache')), + ), + array( + array('X-UA-Compatible' => 'IE=edge,chrome=1'), + array('X-UA-Compatible' => array('IE=edge,chrome=1'), 'Cache-Control' => array('no-cache')), + ), + array( + array('X-XSS-Protection' => '1; mode=block'), + array('X-XSS-Protection' => array('1; mode=block'), 'Cache-Control' => array('no-cache')), + ), + ); + } + + public function testCacheControlHeader() + { + $bag = new ResponseHeaderBag(array()); + $this->assertEquals('no-cache', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + + $bag = new ResponseHeaderBag(array('Cache-Control' => 'public')); + $this->assertEquals('public', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('public')); + + $bag = new ResponseHeaderBag(array('ETag' => 'abcde')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('private')); + $this->assertTrue($bag->hasCacheControlDirective('must-revalidate')); + $this->assertFalse($bag->hasCacheControlDirective('max-age')); + + $bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array( + 'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT', + 'Cache-Control' => 'max-age=3600', + )); + $this->assertEquals('max-age=3600, private', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100')); + $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100')); + $this->assertEquals('s-maxage=100', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100')); + $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100')); + $this->assertEquals('max-age=100, public', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(); + $bag->set('Last-Modified', 'abcde'); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + } + + public function testToStringIncludesCookieHeaders() + { + $bag = new ResponseHeaderBag(array()); + $bag->setCookie(new Cookie('foo', 'bar')); + + $this->assertContains('Set-Cookie: foo=bar; path=/; httponly', explode("\r\n", $bag->__toString())); + + $bag->clearCookie('foo'); + + $this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; httponly#m', $bag->__toString()); + } + + public function testClearCookieSecureNotHttpOnly() + { + $bag = new ResponseHeaderBag(array()); + + $bag->clearCookie('foo', '/', null, true, false); + + $this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; secure#m', $bag->__toString()); + } + + public function testReplace() + { + $bag = new ResponseHeaderBag(array()); + $this->assertEquals('no-cache', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + + $bag->replace(array('Cache-Control' => 'public')); + $this->assertEquals('public', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('public')); + } + + public function testReplaceWithRemove() + { + $bag = new ResponseHeaderBag(array()); + $this->assertEquals('no-cache', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + + $bag->remove('Cache-Control'); + $bag->replace(array()); + $this->assertEquals('no-cache', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + } + + public function testCookiesWithSameNames() + { + $bag = new ResponseHeaderBag(); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar')); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo')); + $bag->setCookie(new Cookie('foo', 'bar')); + + $this->assertCount(4, $bag->getCookies()); + + $headers = explode("\r\n", $bag->__toString()); + $this->assertContains('Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly', $headers); + $this->assertContains('Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly', $headers); + $this->assertContains('Set-Cookie: foo=bar; path=/path/bar; domain=bar.foo; httponly', $headers); + $this->assertContains('Set-Cookie: foo=bar; path=/; httponly', $headers); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertTrue(isset($cookies['foo.bar']['/path/foo']['foo'])); + $this->assertTrue(isset($cookies['foo.bar']['/path/bar']['foo'])); + $this->assertTrue(isset($cookies['bar.foo']['/path/bar']['foo'])); + $this->assertTrue(isset($cookies['']['/']['foo'])); + } + + public function testRemoveCookie() + { + $bag = new ResponseHeaderBag(); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); + $bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar')); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertTrue(isset($cookies['foo.bar']['/path/foo'])); + + $bag->removeCookie('foo', '/path/foo', 'foo.bar'); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertFalse(isset($cookies['foo.bar']['/path/foo'])); + + $bag->removeCookie('bar', '/path/bar', 'foo.bar'); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertFalse(isset($cookies['foo.bar'])); + } + + public function testRemoveCookieWithNullRemove() + { + $bag = new ResponseHeaderBag(); + $bag->setCookie(new Cookie('foo', 'bar', 0)); + $bag->setCookie(new Cookie('bar', 'foo', 0)); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertTrue(isset($cookies['']['/'])); + + $bag->removeCookie('foo', null); + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertFalse(isset($cookies['']['/']['foo'])); + + $bag->removeCookie('bar', null); + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertFalse(isset($cookies['']['/']['bar'])); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetCookiesWithInvalidArgument() + { + $bag = new ResponseHeaderBag(); + + $cookies = $bag->getCookies('invalid_argument'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testMakeDispositionInvalidDisposition() + { + $headers = new ResponseHeaderBag(); + + $headers->makeDisposition('invalid', 'foo.html'); + } + + /** + * @dataProvider provideMakeDisposition + */ + public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected) + { + $headers = new ResponseHeaderBag(); + + $this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback)); + } + + public function testToStringDoesntMessUpHeaders() + { + $headers = new ResponseHeaderBag(); + + $headers->set('Location', 'http://www.symfony.com'); + $headers->set('Content-type', 'text/html'); + + (string) $headers; + + $allHeaders = $headers->allPreserveCase(); + $this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']); + $this->assertEquals(array('text/html'), $allHeaders['Content-type']); + } + + public function provideMakeDisposition() + { + return array( + array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'), + array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'), + array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'), + array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'), + array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'), + array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'), + ); + } + + /** + * @dataProvider provideMakeDispositionFail + * @expectedException \InvalidArgumentException + */ + public function testMakeDispositionFail($disposition, $filename) + { + $headers = new ResponseHeaderBag(); + + $headers->makeDisposition($disposition, $filename); + } + + public function provideMakeDispositionFail() + { + return array( + array('attachment', 'foo%20bar.html'), + array('attachment', 'foo/bar.html'), + array('attachment', '/foo.html'), + array('attachment', 'foo\bar.html'), + array('attachment', '\foo.html'), + array('attachment', 'föö.html'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php new file mode 100644 index 0000000..97674d4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -0,0 +1,893 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class ResponseTest extends ResponseTestCase +{ + public function testCreate() + { + $response = Response::create('foo', 301, array('Foo' => 'bar')); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); + $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals('bar', $response->headers->get('foo')); + } + + public function testToString() + { + $response = new Response(); + $response = explode("\r\n", $response); + $this->assertEquals('HTTP/1.0 200 OK', $response[0]); + $this->assertEquals('Cache-Control: no-cache', $response[1]); + } + + public function testClone() + { + $response = new Response(); + $responseClone = clone $response; + $this->assertEquals($response, $responseClone); + } + + public function testSendHeaders() + { + $response = new Response(); + $headers = $response->sendHeaders(); + $this->assertObjectHasAttribute('headers', $headers); + $this->assertObjectHasAttribute('content', $headers); + $this->assertObjectHasAttribute('version', $headers); + $this->assertObjectHasAttribute('statusCode', $headers); + $this->assertObjectHasAttribute('statusText', $headers); + $this->assertObjectHasAttribute('charset', $headers); + } + + public function testSend() + { + $response = new Response(); + $responseSend = $response->send(); + $this->assertObjectHasAttribute('headers', $responseSend); + $this->assertObjectHasAttribute('content', $responseSend); + $this->assertObjectHasAttribute('version', $responseSend); + $this->assertObjectHasAttribute('statusCode', $responseSend); + $this->assertObjectHasAttribute('statusText', $responseSend); + $this->assertObjectHasAttribute('charset', $responseSend); + } + + public function testGetCharset() + { + $response = new Response(); + $charsetOrigin = 'UTF-8'; + $response->setCharset($charsetOrigin); + $charset = $response->getCharset(); + $this->assertEquals($charsetOrigin, $charset); + } + + public function testIsCacheable() + { + $response = new Response(); + $this->assertFalse($response->isCacheable()); + } + + public function testIsCacheableWithErrorCode() + { + $response = new Response('', 500); + $this->assertFalse($response->isCacheable()); + } + + public function testIsCacheableWithNoStoreDirective() + { + $response = new Response(); + $response->headers->set('cache-control', 'private'); + $this->assertFalse($response->isCacheable()); + } + + public function testIsCacheableWithSetTtl() + { + $response = new Response(); + $response->setTtl(10); + $this->assertTrue($response->isCacheable()); + } + + public function testMustRevalidate() + { + $response = new Response(); + $this->assertFalse($response->mustRevalidate()); + } + + public function testMustRevalidateWithMustRevalidateCacheControlHeader() + { + $response = new Response(); + $response->headers->set('cache-control', 'must-revalidate'); + + $this->assertTrue($response->mustRevalidate()); + } + + public function testMustRevalidateWithProxyRevalidateCacheControlHeader() + { + $response = new Response(); + $response->headers->set('cache-control', 'proxy-revalidate'); + + $this->assertTrue($response->mustRevalidate()); + } + + public function testSetNotModified() + { + $response = new Response(); + $modified = $response->setNotModified(); + $this->assertObjectHasAttribute('headers', $modified); + $this->assertObjectHasAttribute('content', $modified); + $this->assertObjectHasAttribute('version', $modified); + $this->assertObjectHasAttribute('statusCode', $modified); + $this->assertObjectHasAttribute('statusText', $modified); + $this->assertObjectHasAttribute('charset', $modified); + $this->assertEquals(304, $modified->getStatusCode()); + } + + public function testIsSuccessful() + { + $response = new Response(); + $this->assertTrue($response->isSuccessful()); + } + + public function testIsNotModified() + { + $response = new Response(); + $modified = $response->isNotModified(new Request()); + $this->assertFalse($modified); + } + + public function testIsNotModifiedNotSafe() + { + $request = Request::create('/homepage', 'POST'); + + $response = new Response(); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsNotModifiedLastModified() + { + $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; + $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; + $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; + + $request = new Request(); + $request->headers->set('If-Modified-Since', $modified); + + $response = new Response(); + + $response->headers->set('Last-Modified', $modified); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('Last-Modified', $before); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('Last-Modified', $after); + $this->assertFalse($response->isNotModified($request)); + + $response->headers->set('Last-Modified', ''); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsNotModifiedEtag() + { + $etagOne = 'randomly_generated_etag'; + $etagTwo = 'randomly_generated_etag_2'; + + $request = new Request(); + $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); + + $response = new Response(); + + $response->headers->set('ETag', $etagOne); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', $etagTwo); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', ''); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsNotModifiedLastModifiedAndEtag() + { + $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; + $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; + $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; + $etag = 'randomly_generated_etag'; + + $request = new Request(); + $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); + $request->headers->set('If-Modified-Since', $modified); + + $response = new Response(); + + $response->headers->set('ETag', $etag); + $response->headers->set('Last-Modified', $after); + $this->assertFalse($response->isNotModified($request)); + + $response->headers->set('ETag', 'non-existent-etag'); + $response->headers->set('Last-Modified', $before); + $this->assertFalse($response->isNotModified($request)); + + $response->headers->set('ETag', $etag); + $response->headers->set('Last-Modified', $modified); + $this->assertTrue($response->isNotModified($request)); + } + + public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified() + { + $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; + $etag = 'randomly_generated_etag'; + + $request = new Request(); + $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); + $request->headers->set('If-Modified-Since', $modified); + + $response = new Response(); + + $response->headers->set('ETag', $etag); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', 'non-existent-etag'); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsValidateable() + { + $response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); + $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present'); + + $response = new Response('', 200, array('ETag' => '"12345"')); + $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present'); + + $response = new Response(); + $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present'); + } + + public function testGetDate() + { + $oneHourAgo = $this->createDateTimeOneHourAgo(); + $response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822))); + $date = $response->getDate(); + $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present'); + + $response = new Response(); + $date = $response->getDate(); + $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present'); + + $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); + $now = $this->createDateTimeNow(); + $response->headers->set('Date', $now->format(DATE_RFC2822)); + $date = $response->getDate(); + $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified'); + + $response = new Response('', 200); + $response->headers->remove('Date'); + $this->assertInstanceOf('\DateTime', $response->getDate()); + } + + public function testGetMaxAge() + { + $response = new Response(); + $response->headers->set('Cache-Control', 's-maxage=600, max-age=0'); + $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=600'); + $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'must-revalidate'); + $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); + $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'must-revalidate'); + $response->headers->set('Expires', -1); + $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822)); + + $response = new Response(); + $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available'); + } + + public function testSetSharedMaxAge() + { + $response = new Response(); + $response->setSharedMaxAge(20); + + $cacheControl = $response->headers->get('Cache-Control'); + $this->assertEquals('public, s-maxage=20', $cacheControl); + } + + public function testIsPrivate() + { + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=100'); + $response->setPrivate(); + $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); + $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'public, max-age=100'); + $response->setPrivate(); + $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); + $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); + $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive'); + } + + public function testExpire() + { + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=100'); + $response->expire(); + $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500'); + $response->expire(); + $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500'); + $response->headers->set('Age', '1000'); + $response->expire(); + $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired'); + + $response = new Response(); + $response->expire(); + $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information'); + + $response = new Response(); + $response->headers->set('Expires', -1); + $response->expire(); + $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired'); + } + + public function testGetTtl() + { + $response = new Response(); + $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present'); + + $response = new Response(); + $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); + $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present'); + + $response = new Response(); + $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)); + $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past'); + + $response = new Response(); + $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822)); + $response->headers->set('Age', 0); + $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=60'); + $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present'); + } + + public function testSetClientTtl() + { + $response = new Response(); + $response->setClientTtl(10); + + $this->assertEquals($response->getMaxAge(), $response->getAge() + 10); + } + + public function testGetSetProtocolVersion() + { + $response = new Response(); + + $this->assertEquals('1.0', $response->getProtocolVersion()); + + $response->setProtocolVersion('1.1'); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + } + + public function testGetVary() + { + $response = new Response(); + $this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language'); + $this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language User-Agent X-Foo'); + $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo'); + $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas'); + + $vary = array('Accept-Language', 'User-Agent', 'X-foo'); + + $response = new Response(); + $response->headers->set('Vary', $vary); + $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo'); + $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); + } + + public function testSetVary() + { + $response = new Response(); + $response->setVary('Accept-Language'); + $this->assertEquals(array('Accept-Language'), $response->getVary()); + + $response->setVary('Accept-Language, User-Agent'); + $this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default'); + + $response->setVary('X-Foo', false); + $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false'); + } + + public function testDefaultContentType() + { + $headerMock = $this->getMock('Symfony\Component\HttpFoundation\ResponseHeaderBag', array('set')); + $headerMock->expects($this->at(0)) + ->method('set') + ->with('Content-Type', 'text/html'); + $headerMock->expects($this->at(1)) + ->method('set') + ->with('Content-Type', 'text/html; charset=UTF-8'); + + $response = new Response('foo'); + $response->headers = $headerMock; + + $response->prepare(new Request()); + } + + public function testContentTypeCharset() + { + $response = new Response(); + $response->headers->set('Content-Type', 'text/css'); + + // force fixContentType() to be called + $response->prepare(new Request()); + + $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type')); + } + + public function testPrepareDoesNothingIfContentTypeIsSet() + { + $response = new Response('foo'); + $response->headers->set('Content-Type', 'text/plain'); + + $response->prepare(new Request()); + + $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type')); + } + + public function testPrepareDoesNothingIfRequestFormatIsNotDefined() + { + $response = new Response('foo'); + + $response->prepare(new Request()); + + $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type')); + } + + public function testPrepareSetContentType() + { + $response = new Response('foo'); + $request = Request::create('/'); + $request->setRequestFormat('json'); + + $response->prepare($request); + + $this->assertEquals('application/json', $response->headers->get('content-type')); + } + + public function testPrepareRemovesContentForHeadRequests() + { + $response = new Response('foo'); + $request = Request::create('/', 'HEAD'); + + $length = 12345; + $response->headers->set('Content-Length', $length); + $response->prepare($request); + + $this->assertEquals('', $response->getContent()); + $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13'); + } + + public function testPrepareRemovesContentForInformationalResponse() + { + $response = new Response('foo'); + $request = Request::create('/'); + + $response->setContent('content'); + $response->setStatusCode(101); + $response->prepare($request); + $this->assertEquals('', $response->getContent()); + $this->assertFalse($response->headers->has('Content-Type')); + $this->assertFalse($response->headers->has('Content-Type')); + + $response->setContent('content'); + $response->setStatusCode(304); + $response->prepare($request); + $this->assertEquals('', $response->getContent()); + $this->assertFalse($response->headers->has('Content-Type')); + $this->assertFalse($response->headers->has('Content-Length')); + } + + public function testPrepareRemovesContentLength() + { + $response = new Response('foo'); + $request = Request::create('/'); + + $response->headers->set('Content-Length', 12345); + $response->prepare($request); + $this->assertEquals(12345, $response->headers->get('Content-Length')); + + $response->headers->set('Transfer-Encoding', 'chunked'); + $response->prepare($request); + $this->assertFalse($response->headers->has('Content-Length')); + } + + public function testPrepareSetsPragmaOnHttp10Only() + { + $request = Request::create('/', 'GET'); + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); + + $response = new Response('foo'); + $response->prepare($request); + $this->assertEquals('no-cache', $response->headers->get('pragma')); + $this->assertEquals('-1', $response->headers->get('expires')); + + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); + $response = new Response('foo'); + $response->prepare($request); + $this->assertFalse($response->headers->has('pragma')); + $this->assertFalse($response->headers->has('expires')); + } + + public function testSetCache() + { + $response = new Response(); + //array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public') + try { + $response->setCache(array('wrong option' => 'value')); + $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported'); + $this->assertContains('"wrong option"', $e->getMessage()); + } + + $options = array('etag' => '"whatever"'); + $response->setCache($options); + $this->assertEquals($response->getEtag(), '"whatever"'); + + $now = $this->createDateTimeNow(); + $options = array('last_modified' => $now); + $response->setCache($options); + $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp()); + + $options = array('max_age' => 100); + $response->setCache($options); + $this->assertEquals($response->getMaxAge(), 100); + + $options = array('s_maxage' => 200); + $response->setCache($options); + $this->assertEquals($response->getMaxAge(), 200); + + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('public' => true)); + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('public' => false)); + $this->assertFalse($response->headers->hasCacheControlDirective('public')); + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('private' => true)); + $this->assertFalse($response->headers->hasCacheControlDirective('public')); + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('private' => false)); + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + } + + public function testSendContent() + { + $response = new Response('test response rendering', 200); + + ob_start(); + $response->sendContent(); + $string = ob_get_clean(); + $this->assertContains('test response rendering', $string); + } + + public function testSetPublic() + { + $response = new Response(); + $response->setPublic(); + + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + } + + public function testSetExpires() + { + $response = new Response(); + $response->setExpires(null); + + $this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null'); + + $now = $this->createDateTimeNow(); + $response->setExpires($now); + + $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp()); + } + + public function testSetLastModified() + { + $response = new Response(); + $response->setLastModified($this->createDateTimeNow()); + $this->assertNotNull($response->getLastModified()); + + $response->setLastModified(null); + $this->assertNull($response->getLastModified()); + } + + public function testIsInvalid() + { + $response = new Response(); + + try { + $response->setStatusCode(99); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertTrue($response->isInvalid()); + } + + try { + $response->setStatusCode(650); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertTrue($response->isInvalid()); + } + + $response = new Response('', 200); + $this->assertFalse($response->isInvalid()); + } + + /** + * @dataProvider getStatusCodeFixtures + */ + public function testSetStatusCode($code, $text, $expectedText) + { + $response = new Response(); + + $response->setStatusCode($code, $text); + + $statusText = new \ReflectionProperty($response, 'statusText'); + $statusText->setAccessible(true); + + $this->assertEquals($expectedText, $statusText->getValue($response)); + } + + public function getStatusCodeFixtures() + { + return array( + array('200', null, 'OK'), + array('200', false, ''), + array('200', 'foo', 'foo'), + array('199', null, 'unknown status'), + array('199', false, ''), + array('199', 'foo', 'foo'), + ); + } + + public function testIsInformational() + { + $response = new Response('', 100); + $this->assertTrue($response->isInformational()); + + $response = new Response('', 200); + $this->assertFalse($response->isInformational()); + } + + public function testIsRedirectRedirection() + { + foreach (array(301, 302, 303, 307) as $code) { + $response = new Response('', $code); + $this->assertTrue($response->isRedirection()); + $this->assertTrue($response->isRedirect()); + } + + $response = new Response('', 304); + $this->assertTrue($response->isRedirection()); + $this->assertFalse($response->isRedirect()); + + $response = new Response('', 200); + $this->assertFalse($response->isRedirection()); + $this->assertFalse($response->isRedirect()); + + $response = new Response('', 404); + $this->assertFalse($response->isRedirection()); + $this->assertFalse($response->isRedirect()); + + $response = new Response('', 301, array('Location' => '/good-uri')); + $this->assertFalse($response->isRedirect('/bad-uri')); + $this->assertTrue($response->isRedirect('/good-uri')); + } + + public function testIsNotFound() + { + $response = new Response('', 404); + $this->assertTrue($response->isNotFound()); + + $response = new Response('', 200); + $this->assertFalse($response->isNotFound()); + } + + public function testIsEmpty() + { + foreach (array(204, 304) as $code) { + $response = new Response('', $code); + $this->assertTrue($response->isEmpty()); + } + + $response = new Response('', 200); + $this->assertFalse($response->isEmpty()); + } + + public function testIsForbidden() + { + $response = new Response('', 403); + $this->assertTrue($response->isForbidden()); + + $response = new Response('', 200); + $this->assertFalse($response->isForbidden()); + } + + public function testIsOk() + { + $response = new Response('', 200); + $this->assertTrue($response->isOk()); + + $response = new Response('', 404); + $this->assertFalse($response->isOk()); + } + + public function testIsServerOrClientError() + { + $response = new Response('', 404); + $this->assertTrue($response->isClientError()); + $this->assertFalse($response->isServerError()); + + $response = new Response('', 500); + $this->assertFalse($response->isClientError()); + $this->assertTrue($response->isServerError()); + } + + public function testHasVary() + { + $response = new Response(); + $this->assertFalse($response->hasVary()); + + $response->setVary('User-Agent'); + $this->assertTrue($response->hasVary()); + } + + public function testSetEtag() + { + $response = new Response('', 200, array('ETag' => '"12345"')); + $response->setEtag(); + + $this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null'); + } + + /** + * @dataProvider validContentProvider + */ + public function testSetContent($content) + { + $response = new Response(); + $response->setContent($content); + $this->assertEquals((string) $content, $response->getContent()); + } + + /** + * @expectedException \UnexpectedValueException + * @dataProvider invalidContentProvider + */ + public function testSetContentInvalid($content) + { + $response = new Response(); + $response->setContent($content); + } + + public function testSettersAreChainable() + { + $response = new Response(); + + $setters = array( + 'setProtocolVersion' => '1.0', + 'setCharset' => 'UTF-8', + 'setPublic' => null, + 'setPrivate' => null, + 'setDate' => $this->createDateTimeNow(), + 'expire' => null, + 'setMaxAge' => 1, + 'setSharedMaxAge' => 1, + 'setTtl' => 1, + 'setClientTtl' => 1, + ); + + foreach ($setters as $setter => $arg) { + $this->assertEquals($response, $response->{$setter}($arg)); + } + } + + public function validContentProvider() + { + return array( + 'obj' => array(new StringableObject()), + 'string' => array('Foo'), + 'int' => array(2), + ); + } + + public function invalidContentProvider() + { + return array( + 'obj' => array(new \stdClass()), + 'array' => array(array()), + 'bool' => array(true, '1'), + ); + } + + protected function createDateTimeOneHourAgo() + { + return $this->createDateTimeNow()->sub(new \DateInterval('PT1H')); + } + + protected function createDateTimeOneHourLater() + { + return $this->createDateTimeNow()->add(new \DateInterval('PT1H')); + } + + protected function createDateTimeNow() + { + $date = new \DateTime(); + + return $date->setTimestamp(time()); + } + + protected function provideResponse() + { + return new Response(); + } +} + +class StringableObject +{ + public function __toString() + { + return 'Foo'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTestCase.php new file mode 100644 index 0000000..94c770a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ResponseTestCase.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Request; + +abstract class ResponseTestCase extends \PHPUnit_Framework_TestCase +{ + public function testNoCacheControlHeaderOnAttachmentUsingHTTPSAndMSIE() + { + // Check for HTTPS and IE 8 + $request = new Request(); + $request->server->set('HTTPS', true); + $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)'); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertFalse($response->headers->has('Cache-Control')); + + // Check for IE 10 and HTTPS + $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertTrue($response->headers->has('Cache-Control')); + + // Check for IE 9 and HTTPS + $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)'); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertTrue($response->headers->has('Cache-Control')); + + // Check for IE 9 and HTTP + $request->server->set('HTTPS', false); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertTrue($response->headers->has('Cache-Control')); + + // Check for IE 8 and HTTP + $request->server->set('HTTP_USER_AGENT', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)'); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertTrue($response->headers->has('Cache-Control')); + + // Check for non-IE and HTTPS + $request->server->set('HTTPS', true); + $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17'); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertTrue($response->headers->has('Cache-Control')); + + // Check for non-IE and HTTP + $request->server->set('HTTPS', false); + + $response = $this->provideResponse(); + $response->headers->set('Content-Disposition', 'attachment; filename="fname.ext"'); + $response->prepare($request); + + $this->assertTrue($response->headers->has('Cache-Control')); + } + + abstract protected function provideResponse(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php new file mode 100644 index 0000000..20773c4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/ServerBagTest.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\ServerBag; + +/** + * ServerBagTest. + * + * @author Bulat Shakirzyanov + */ +class ServerBagTest extends \PHPUnit_Framework_TestCase +{ + public function testShouldExtractHeadersFromServerArray() + { + $server = array( + 'SOME_SERVER_VARIABLE' => 'value', + 'SOME_SERVER_VARIABLE2' => 'value', + 'ROOT' => 'value', + 'HTTP_CONTENT_TYPE' => 'text/html', + 'HTTP_CONTENT_LENGTH' => '0', + 'HTTP_ETAG' => 'asdf', + 'PHP_AUTH_USER' => 'foo', + 'PHP_AUTH_PW' => 'bar', + ); + + $bag = new ServerBag($server); + + $this->assertEquals(array( + 'CONTENT_TYPE' => 'text/html', + 'CONTENT_LENGTH' => '0', + 'ETAG' => 'asdf', + 'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'), + 'PHP_AUTH_USER' => 'foo', + 'PHP_AUTH_PW' => 'bar', + ), $bag->getHeaders()); + } + + public function testHttpPasswordIsOptional() + { + $bag = new ServerBag(array('PHP_AUTH_USER' => 'foo')); + + $this->assertEquals(array( + 'AUTHORIZATION' => 'Basic '.base64_encode('foo:'), + 'PHP_AUTH_USER' => 'foo', + 'PHP_AUTH_PW' => '', + ), $bag->getHeaders()); + } + + public function testHttpBasicAuthWithPhpCgi() + { + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'))); + + $this->assertEquals(array( + 'AUTHORIZATION' => 'Basic '.base64_encode('foo:bar'), + 'PHP_AUTH_USER' => 'foo', + 'PHP_AUTH_PW' => 'bar', + ), $bag->getHeaders()); + } + + public function testHttpBasicAuthWithPhpCgiBogus() + { + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic_'.base64_encode('foo:bar'))); + + // Username and passwords should not be set as the header is bogus + $headers = $bag->getHeaders(); + $this->assertFalse(isset($headers['PHP_AUTH_USER'])); + $this->assertFalse(isset($headers['PHP_AUTH_PW'])); + } + + public function testHttpBasicAuthWithPhpCgiRedirect() + { + $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'))); + + $this->assertEquals(array( + 'AUTHORIZATION' => 'Basic '.base64_encode('username:pass:word'), + 'PHP_AUTH_USER' => 'username', + 'PHP_AUTH_PW' => 'pass:word', + ), $bag->getHeaders()); + } + + public function testHttpBasicAuthWithPhpCgiEmptyPassword() + { + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('foo:'))); + + $this->assertEquals(array( + 'AUTHORIZATION' => 'Basic '.base64_encode('foo:'), + 'PHP_AUTH_USER' => 'foo', + 'PHP_AUTH_PW' => '', + ), $bag->getHeaders()); + } + + public function testHttpDigestAuthWithPhpCgi() + { + $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest)); + + $this->assertEquals(array( + 'AUTHORIZATION' => $digest, + 'PHP_AUTH_DIGEST' => $digest, + ), $bag->getHeaders()); + } + + public function testHttpDigestAuthWithPhpCgiBogus() + { + $digest = 'Digest_username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $digest)); + + // Username and passwords should not be set as the header is bogus + $headers = $bag->getHeaders(); + $this->assertFalse(isset($headers['PHP_AUTH_USER'])); + $this->assertFalse(isset($headers['PHP_AUTH_PW'])); + } + + public function testHttpDigestAuthWithPhpCgiRedirect() + { + $digest = 'Digest username="foo", realm="acme", nonce="'.md5('secret').'", uri="/protected, qop="auth"'; + $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $digest)); + + $this->assertEquals(array( + 'AUTHORIZATION' => $digest, + 'PHP_AUTH_DIGEST' => $digest, + ), $bag->getHeaders()); + } + + public function testOAuthBearerAuth() + { + $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; + $bag = new ServerBag(array('HTTP_AUTHORIZATION' => $headerContent)); + + $this->assertEquals(array( + 'AUTHORIZATION' => $headerContent, + ), $bag->getHeaders()); + } + + public function testOAuthBearerAuthWithRedirect() + { + $headerContent = 'Bearer L-yLEOr9zhmUYRkzN1jwwxwQ-PBNiKDc8dgfB4hTfvo'; + $bag = new ServerBag(array('REDIRECT_HTTP_AUTHORIZATION' => $headerContent)); + + $this->assertEquals(array( + 'AUTHORIZATION' => $headerContent, + ), $bag->getHeaders()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/AttributeBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/AttributeBagTest.php new file mode 100644 index 0000000..8d0e7a5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/AttributeBagTest.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + +/** + * Tests AttributeBag. + * + * @author Drak + */ +class AttributeBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var array + */ + private $array; + + /** + * @var AttributeBag + */ + private $bag; + + protected function setUp() + { + $this->array = array( + 'hello' => 'world', + 'always' => 'be happy', + 'user.login' => 'drak', + 'csrf.token' => array( + 'a' => '1234', + 'b' => '4321', + ), + 'category' => array( + 'fishing' => array( + 'first' => 'cod', + 'second' => 'sole',), + ), + ); + $this->bag = new AttributeBag('_sf2'); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->bag = null; + $this->array = array(); + } + + public function testInitialize() + { + $bag = new AttributeBag(); + $bag->initialize($this->array); + $this->assertEquals($this->array, $bag->all()); + $array = array('should' => 'change'); + $bag->initialize($array); + $this->assertEquals($array, $bag->all()); + } + + public function testGetStorageKey() + { + $this->assertEquals('_sf2', $this->bag->getStorageKey()); + $attributeBag = new AttributeBag('test'); + $this->assertEquals('test', $attributeBag->getStorageKey()); + } + + public function testGetSetName() + { + $this->assertEquals('attributes', $this->bag->getName()); + $this->bag->setName('foo'); + $this->assertEquals('foo', $this->bag->getName()); + } + + /** + * @dataProvider attributesProvider + */ + public function testHas($key, $value, $exists) + { + $this->assertEquals($exists, $this->bag->has($key)); + } + + /** + * @dataProvider attributesProvider + */ + public function testGet($key, $value, $expected) + { + $this->assertEquals($value, $this->bag->get($key)); + } + + public function testGetDefaults() + { + $this->assertNull($this->bag->get('user2.login')); + $this->assertEquals('default', $this->bag->get('user2.login', 'default')); + } + + /** + * @dataProvider attributesProvider + */ + public function testSet($key, $value, $expected) + { + $this->bag->set($key, $value); + $this->assertEquals($value, $this->bag->get($key)); + } + + public function testAll() + { + $this->assertEquals($this->array, $this->bag->all()); + + $this->bag->set('hello', 'fabien'); + $array = $this->array; + $array['hello'] = 'fabien'; + $this->assertEquals($array, $this->bag->all()); + } + + public function testReplace() + { + $array = array(); + $array['name'] = 'jack'; + $array['foo.bar'] = 'beep'; + $this->bag->replace($array); + $this->assertEquals($array, $this->bag->all()); + $this->assertNull($this->bag->get('hello')); + $this->assertNull($this->bag->get('always')); + $this->assertNull($this->bag->get('user.login')); + } + + public function testRemove() + { + $this->assertEquals('world', $this->bag->get('hello')); + $this->bag->remove('hello'); + $this->assertNull($this->bag->get('hello')); + + $this->assertEquals('be happy', $this->bag->get('always')); + $this->bag->remove('always'); + $this->assertNull($this->bag->get('always')); + + $this->assertEquals('drak', $this->bag->get('user.login')); + $this->bag->remove('user.login'); + $this->assertNull($this->bag->get('user.login')); + } + + public function testClear() + { + $this->bag->clear(); + $this->assertEquals(array(), $this->bag->all()); + } + + public function attributesProvider() + { + return array( + array('hello', 'world', true), + array('always', 'be happy', true), + array('user.login', 'drak', true), + array('csrf.token', array('a' => '1234', 'b' => '4321'), true), + array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true), + array('user2.login', null, false), + array('never', null, false), + array('bye', null, false), + array('bye/for/now', null, false), + ); + } + + public function testGetIterator() + { + $i = 0; + foreach ($this->bag as $key => $val) { + $this->assertEquals($this->array[$key], $val); + ++$i; + } + + $this->assertEquals(count($this->array), $i); + } + + public function testCount() + { + $this->assertEquals(count($this->array), count($this->bag)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php new file mode 100644 index 0000000..b8261da --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; + +/** + * Tests NamespacedAttributeBag. + * + * @author Drak + */ +class NamespacedAttributeBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var array + */ + private $array; + + /** + * @var NamespacedAttributeBag + */ + private $bag; + + protected function setUp() + { + $this->array = array( + 'hello' => 'world', + 'always' => 'be happy', + 'user.login' => 'drak', + 'csrf.token' => array( + 'a' => '1234', + 'b' => '4321', + ), + 'category' => array( + 'fishing' => array( + 'first' => 'cod', + 'second' => 'sole',), + ), + ); + $this->bag = new NamespacedAttributeBag('_sf2', '/'); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->bag = null; + $this->array = array(); + } + + public function testInitialize() + { + $bag = new NamespacedAttributeBag(); + $bag->initialize($this->array); + $this->assertEquals($this->array, $this->bag->all()); + $array = array('should' => 'not stick'); + $bag->initialize($array); + + // should have remained the same + $this->assertEquals($this->array, $this->bag->all()); + } + + public function testGetStorageKey() + { + $this->assertEquals('_sf2', $this->bag->getStorageKey()); + $attributeBag = new NamespacedAttributeBag('test'); + $this->assertEquals('test', $attributeBag->getStorageKey()); + } + + /** + * @dataProvider attributesProvider + */ + public function testHas($key, $value, $exists) + { + $this->assertEquals($exists, $this->bag->has($key)); + } + + /** + * @dataProvider attributesProvider + */ + public function testGet($key, $value, $expected) + { + $this->assertEquals($value, $this->bag->get($key)); + } + + public function testGetDefaults() + { + $this->assertNull($this->bag->get('user2.login')); + $this->assertEquals('default', $this->bag->get('user2.login', 'default')); + } + + /** + * @dataProvider attributesProvider + */ + public function testSet($key, $value, $expected) + { + $this->bag->set($key, $value); + $this->assertEquals($value, $this->bag->get($key)); + } + + public function testAll() + { + $this->assertEquals($this->array, $this->bag->all()); + + $this->bag->set('hello', 'fabien'); + $array = $this->array; + $array['hello'] = 'fabien'; + $this->assertEquals($array, $this->bag->all()); + } + + public function testReplace() + { + $array = array(); + $array['name'] = 'jack'; + $array['foo.bar'] = 'beep'; + $this->bag->replace($array); + $this->assertEquals($array, $this->bag->all()); + $this->assertNull($this->bag->get('hello')); + $this->assertNull($this->bag->get('always')); + $this->assertNull($this->bag->get('user.login')); + } + + public function testRemove() + { + $this->assertEquals('world', $this->bag->get('hello')); + $this->bag->remove('hello'); + $this->assertNull($this->bag->get('hello')); + + $this->assertEquals('be happy', $this->bag->get('always')); + $this->bag->remove('always'); + $this->assertNull($this->bag->get('always')); + + $this->assertEquals('drak', $this->bag->get('user.login')); + $this->bag->remove('user.login'); + $this->assertNull($this->bag->get('user.login')); + } + + public function testRemoveExistingNamespacedAttribute() + { + $this->assertSame('cod', $this->bag->remove('category/fishing/first')); + } + + public function testRemoveNonexistingNamespacedAttribute() + { + $this->assertNull($this->bag->remove('foo/bar/baz')); + } + + public function testClear() + { + $this->bag->clear(); + $this->assertEquals(array(), $this->bag->all()); + } + + public function attributesProvider() + { + return array( + array('hello', 'world', true), + array('always', 'be happy', true), + array('user.login', 'drak', true), + array('csrf.token', array('a' => '1234', 'b' => '4321'), true), + array('csrf.token/a', '1234', true), + array('csrf.token/b', '4321', true), + array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true), + array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true), + array('category/fishing/missing/first', null, false), + array('category/fishing/first', 'cod', true), + array('category/fishing/second', 'sole', true), + array('category/fishing/missing/second', null, false), + array('user2.login', null, false), + array('never', null, false), + array('bye', null, false), + array('bye/for/now', null, false), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php new file mode 100644 index 0000000..852158f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/AutoExpireFlashBagTest.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag as FlashBag; + +/** + * AutoExpireFlashBagTest. + * + * @author Drak + */ +class AutoExpireFlashBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag + */ + private $bag; + + /** + * @var array + */ + protected $array = array(); + + protected function setUp() + { + parent::setUp(); + $this->bag = new FlashBag(); + $this->array = array('new' => array('notice' => array('A previous flash message'))); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->bag = null; + parent::tearDown(); + } + + public function testInitialize() + { + $bag = new FlashBag(); + $array = array('new' => array('notice' => array('A previous flash message'))); + $bag->initialize($array); + $this->assertEquals(array('A previous flash message'), $bag->peek('notice')); + $array = array('new' => array( + 'notice' => array('Something else'), + 'error' => array('a'), + )); + $bag->initialize($array); + $this->assertEquals(array('Something else'), $bag->peek('notice')); + $this->assertEquals(array('a'), $bag->peek('error')); + } + + public function testGetStorageKey() + { + $this->assertEquals('_sf2_flashes', $this->bag->getStorageKey()); + $attributeBag = new FlashBag('test'); + $this->assertEquals('test', $attributeBag->getStorageKey()); + } + + public function testGetSetName() + { + $this->assertEquals('flashes', $this->bag->getName()); + $this->bag->setName('foo'); + $this->assertEquals('foo', $this->bag->getName()); + } + + public function testPeek() + { + $this->assertEquals(array(), $this->bag->peek('non_existing')); + $this->assertEquals(array('default'), $this->bag->peek('non_existing', array('default'))); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + } + + public function testSet() + { + $this->bag->set('notice', 'Foo'); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + } + + public function testHas() + { + $this->assertFalse($this->bag->has('nothing')); + $this->assertTrue($this->bag->has('notice')); + } + + public function testKeys() + { + $this->assertEquals(array('notice'), $this->bag->keys()); + } + + public function testPeekAll() + { + $array = array( + 'new' => array( + 'notice' => 'Foo', + 'error' => 'Bar', + ), + ); + + $this->bag->initialize($array); + $this->assertEquals(array( + 'notice' => 'Foo', + 'error' => 'Bar', + ), $this->bag->peekAll() + ); + + $this->assertEquals(array( + 'notice' => 'Foo', + 'error' => 'Bar', + ), $this->bag->peekAll() + ); + } + + public function testGet() + { + $this->assertEquals(array(), $this->bag->get('non_existing')); + $this->assertEquals(array('default'), $this->bag->get('non_existing', array('default'))); + $this->assertEquals(array('A previous flash message'), $this->bag->get('notice')); + $this->assertEquals(array(), $this->bag->get('notice')); + } + + public function testSetAll() + { + $this->bag->setAll(array('a' => 'first', 'b' => 'second')); + $this->assertFalse($this->bag->has('a')); + $this->assertFalse($this->bag->has('b')); + } + + public function testAll() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('error', 'Bar'); + $this->assertEquals(array( + 'notice' => array('A previous flash message'), + ), $this->bag->all() + ); + + $this->assertEquals(array(), $this->bag->all()); + } + + public function testClear() + { + $this->assertEquals(array('notice' => array('A previous flash message')), $this->bag->clear()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php new file mode 100644 index 0000000..fc3658d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Flash/FlashBagTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; + +/** + * FlashBagTest. + * + * @author Drak + */ +class FlashBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface + */ + private $bag; + + /** + * @var array + */ + protected $array = array(); + + protected function setUp() + { + parent::setUp(); + $this->bag = new FlashBag(); + $this->array = array('notice' => array('A previous flash message')); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->bag = null; + parent::tearDown(); + } + + public function testInitialize() + { + $bag = new FlashBag(); + $bag->initialize($this->array); + $this->assertEquals($this->array, $bag->peekAll()); + $array = array('should' => array('change')); + $bag->initialize($array); + $this->assertEquals($array, $bag->peekAll()); + } + + public function testGetStorageKey() + { + $this->assertEquals('_sf2_flashes', $this->bag->getStorageKey()); + $attributeBag = new FlashBag('test'); + $this->assertEquals('test', $attributeBag->getStorageKey()); + } + + public function testGetSetName() + { + $this->assertEquals('flashes', $this->bag->getName()); + $this->bag->setName('foo'); + $this->assertEquals('foo', $this->bag->getName()); + } + + public function testPeek() + { + $this->assertEquals(array(), $this->bag->peek('non_existing')); + $this->assertEquals(array('default'), $this->bag->peek('not_existing', array('default'))); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + } + + public function testGet() + { + $this->assertEquals(array(), $this->bag->get('non_existing')); + $this->assertEquals(array('default'), $this->bag->get('not_existing', array('default'))); + $this->assertEquals(array('A previous flash message'), $this->bag->get('notice')); + $this->assertEquals(array(), $this->bag->get('notice')); + } + + public function testAll() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('error', 'Bar'); + $this->assertEquals(array( + 'notice' => array('Foo'), + 'error' => array('Bar'), ), $this->bag->all() + ); + + $this->assertEquals(array(), $this->bag->all()); + } + + public function testSet() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('notice', 'Bar'); + $this->assertEquals(array('Bar'), $this->bag->peek('notice')); + } + + public function testHas() + { + $this->assertFalse($this->bag->has('nothing')); + $this->assertTrue($this->bag->has('notice')); + } + + public function testKeys() + { + $this->assertEquals(array('notice'), $this->bag->keys()); + } + + public function testPeekAll() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('error', 'Bar'); + $this->assertEquals(array( + 'notice' => array('Foo'), + 'error' => array('Bar'), + ), $this->bag->peekAll() + ); + $this->assertTrue($this->bag->has('notice')); + $this->assertTrue($this->bag->has('error')); + $this->assertEquals(array( + 'notice' => array('Foo'), + 'error' => array('Bar'), + ), $this->bag->peekAll() + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php new file mode 100644 index 0000000..57460c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/SessionTest.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session; + +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; + +/** + * SessionTest. + * + * @author Fabien Potencier + * @author Robert Schönthal + * @author Drak + */ +class SessionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface + */ + protected $storage; + + /** + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + + protected function setUp() + { + $this->storage = new MockArraySessionStorage(); + $this->session = new Session($this->storage, new AttributeBag(), new FlashBag()); + } + + protected function tearDown() + { + $this->storage = null; + $this->session = null; + } + + public function testStart() + { + $this->assertEquals('', $this->session->getId()); + $this->assertTrue($this->session->start()); + $this->assertNotEquals('', $this->session->getId()); + } + + public function testIsStarted() + { + $this->assertFalse($this->session->isStarted()); + $this->session->start(); + $this->assertTrue($this->session->isStarted()); + } + + public function testSetId() + { + $this->assertEquals('', $this->session->getId()); + $this->session->setId('0123456789abcdef'); + $this->session->start(); + $this->assertEquals('0123456789abcdef', $this->session->getId()); + } + + public function testSetName() + { + $this->assertEquals('MOCKSESSID', $this->session->getName()); + $this->session->setName('session.test.com'); + $this->session->start(); + $this->assertEquals('session.test.com', $this->session->getName()); + } + + public function testGet() + { + // tests defaults + $this->assertNull($this->session->get('foo')); + $this->assertEquals(1, $this->session->get('foo', 1)); + } + + /** + * @dataProvider setProvider + */ + public function testSet($key, $value) + { + $this->session->set($key, $value); + $this->assertEquals($value, $this->session->get($key)); + } + + /** + * @dataProvider setProvider + */ + public function testHas($key, $value) + { + $this->session->set($key, $value); + $this->assertTrue($this->session->has($key)); + $this->assertFalse($this->session->has($key.'non_value')); + } + + public function testReplace() + { + $this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome')); + $this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all()); + $this->session->replace(array()); + $this->assertEquals(array(), $this->session->all()); + } + + /** + * @dataProvider setProvider + */ + public function testAll($key, $value, $result) + { + $this->session->set($key, $value); + $this->assertEquals($result, $this->session->all()); + } + + /** + * @dataProvider setProvider + */ + public function testClear($key, $value) + { + $this->session->set('hi', 'fabien'); + $this->session->set($key, $value); + $this->session->clear(); + $this->assertEquals(array(), $this->session->all()); + } + + public function setProvider() + { + return array( + array('foo', 'bar', array('foo' => 'bar')), + array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')), + array('great', 'symfony is great', array('great' => 'symfony is great')), + ); + } + + /** + * @dataProvider setProvider + */ + public function testRemove($key, $value) + { + $this->session->set('hi.world', 'have a nice day'); + $this->session->set($key, $value); + $this->session->remove($key); + $this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all()); + } + + public function testInvalidate() + { + $this->session->set('invalidate', 123); + $this->session->invalidate(); + $this->assertEquals(array(), $this->session->all()); + } + + public function testMigrate() + { + $this->session->set('migrate', 321); + $this->session->migrate(); + $this->assertEquals(321, $this->session->get('migrate')); + } + + public function testMigrateDestroy() + { + $this->session->set('migrate', 333); + $this->session->migrate(true); + $this->assertEquals(333, $this->session->get('migrate')); + } + + public function testSave() + { + $this->session->start(); + $this->session->save(); + } + + public function testGetId() + { + $this->assertEquals('', $this->session->getId()); + $this->session->start(); + $this->assertNotEquals('', $this->session->getId()); + } + + public function testGetFlashBag() + { + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag()); + } + + public function testGetIterator() + { + $attributes = array('hello' => 'world', 'symfony' => 'rocks'); + foreach ($attributes as $key => $val) { + $this->session->set($key, $val); + } + + $i = 0; + foreach ($this->session as $key => $val) { + $this->assertEquals($attributes[$key], $val); + ++$i; + } + + $this->assertEquals(count($attributes), $i); + } + + public function testGetCount() + { + $this->session->set('hello', 'world'); + $this->session->set('symfony', 'rocks'); + + $this->assertCount(2, $this->session); + } + + public function testGetMeta() + { + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php new file mode 100644 index 0000000..e879524 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler; + +/** + * @requires extension memcache + * @group time-sensitive + */ +class MemcacheSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + const PREFIX = 'prefix_'; + const TTL = 1000; + /** + * @var MemcacheSessionHandler + */ + protected $storage; + + protected $memcache; + + protected function setUp() + { + parent::setUp(); + $this->memcache = $this->getMock('Memcache'); + $this->storage = new MemcacheSessionHandler( + $this->memcache, + array('prefix' => self::PREFIX, 'expiretime' => self::TTL) + ); + } + + protected function tearDown() + { + $this->memcache = null; + $this->storage = null; + parent::tearDown(); + } + + public function testOpenSession() + { + $this->assertTrue($this->storage->open('', '')); + } + + public function testCloseSession() + { + $this->memcache + ->expects($this->once()) + ->method('close') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->close()); + } + + public function testReadSession() + { + $this->memcache + ->expects($this->once()) + ->method('get') + ->with(self::PREFIX.'id') + ; + + $this->assertEquals('', $this->storage->read('id')); + } + + public function testWriteSession() + { + $this->memcache + ->expects($this->once()) + ->method('set') + ->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2)) + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->write('id', 'data')); + } + + public function testDestroySession() + { + $this->memcache + ->expects($this->once()) + ->method('delete') + ->with(self::PREFIX.'id') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->destroy('id')); + } + + public function testGcSession() + { + $this->assertTrue($this->storage->gc(123)); + } + + /** + * @dataProvider getOptionFixtures + */ + public function testSupportedOptions($options, $supported) + { + try { + new MemcacheSessionHandler($this->memcache, $options); + $this->assertTrue($supported); + } catch (\InvalidArgumentException $e) { + $this->assertFalse($supported); + } + } + + public function getOptionFixtures() + { + return array( + array(array('prefix' => 'session'), true), + array(array('expiretime' => 100), true), + array(array('prefix' => 'session', 'expiretime' => 200), true), + array(array('expiretime' => 100, 'foo' => 'bar'), false), + ); + } + + public function testGetConnection() + { + $method = new \ReflectionMethod($this->storage, 'getMemcache'); + $method->setAccessible(true); + + $this->assertInstanceOf('\Memcache', $method->invoke($this->storage)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php new file mode 100644 index 0000000..f51ec8e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler; + +/** + * @requires extension memcached + * @group time-sensitive + */ +class MemcachedSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + const PREFIX = 'prefix_'; + const TTL = 1000; + + /** + * @var MemcachedSessionHandler + */ + protected $storage; + + protected $memcached; + + protected function setUp() + { + parent::setUp(); + + if (version_compare(phpversion('memcached'), '2.2.0', '>=')) { + $this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower'); + } + + $this->memcached = $this->getMock('Memcached'); + $this->storage = new MemcachedSessionHandler( + $this->memcached, + array('prefix' => self::PREFIX, 'expiretime' => self::TTL) + ); + } + + protected function tearDown() + { + $this->memcached = null; + $this->storage = null; + parent::tearDown(); + } + + public function testOpenSession() + { + $this->assertTrue($this->storage->open('', '')); + } + + public function testCloseSession() + { + $this->assertTrue($this->storage->close()); + } + + public function testReadSession() + { + $this->memcached + ->expects($this->once()) + ->method('get') + ->with(self::PREFIX.'id') + ; + + $this->assertEquals('', $this->storage->read('id')); + } + + public function testWriteSession() + { + $this->memcached + ->expects($this->once()) + ->method('set') + ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2)) + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->write('id', 'data')); + } + + public function testDestroySession() + { + $this->memcached + ->expects($this->once()) + ->method('delete') + ->with(self::PREFIX.'id') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->destroy('id')); + } + + public function testGcSession() + { + $this->assertTrue($this->storage->gc(123)); + } + + /** + * @dataProvider getOptionFixtures + */ + public function testSupportedOptions($options, $supported) + { + try { + new MemcachedSessionHandler($this->memcached, $options); + $this->assertTrue($supported); + } catch (\InvalidArgumentException $e) { + $this->assertFalse($supported); + } + } + + public function getOptionFixtures() + { + return array( + array(array('prefix' => 'session'), true), + array(array('expiretime' => 100), true), + array(array('prefix' => 'session', 'expiretime' => 200), true), + array(array('expiretime' => 100, 'foo' => 'bar'), false), + ); + } + + public function testGetConnection() + { + $method = new \ReflectionMethod($this->storage, 'getMemcached'); + $method->setAccessible(true); + + $this->assertInstanceOf('\Memcached', $method->invoke($this->storage)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php new file mode 100644 index 0000000..3fef78b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; + +/** + * @author Markus Bachmann + * @requires extension mongo + * @group time-sensitive + */ +class MongoDbSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $mongo; + private $storage; + public $options; + + protected function setUp() + { + parent::setUp(); + + $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? 'Mongo' : 'MongoClient'; + + $this->mongo = $this->getMockBuilder($mongoClass) + ->disableOriginalConstructor() + ->getMock(); + + $this->options = array( + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'expiry_field' => 'expires_at', + 'database' => 'sf2-test', + 'collection' => 'session-test', + ); + + $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorShouldThrowExceptionForInvalidMongo() + { + new MongoDbSessionHandler(new \stdClass(), $this->options); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorShouldThrowExceptionForMissingOptions() + { + new MongoDbSessionHandler($this->mongo, array()); + } + + public function testOpenMethodAlwaysReturnTrue() + { + $this->assertTrue($this->storage->open('test', 'test'), 'The "open" method should always return true'); + } + + public function testCloseMethodAlwaysReturnTrue() + { + $this->assertTrue($this->storage->close(), 'The "close" method should always return true'); + } + + public function testRead() + { + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + // defining the timeout before the actual method call + // allows to test for "greater than" values in the $criteria + $testTimeout = time() + 1; + + $collection->expects($this->once()) + ->method('findOne') + ->will($this->returnCallback(function ($criteria) use ($testTimeout) { + $this->assertArrayHasKey($this->options['id_field'], $criteria); + $this->assertEquals($criteria[$this->options['id_field']], 'foo'); + + $this->assertArrayHasKey($this->options['expiry_field'], $criteria); + $this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]); + $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout); + + return array( + $this->options['id_field'] => 'foo', + $this->options['data_field'] => new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY), + $this->options['id_field'] => new \MongoDate(), + ); + })); + + $this->assertEquals('bar', $this->storage->read('foo')); + } + + public function testWrite() + { + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $data = array(); + + $collection->expects($this->once()) + ->method('update') + ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { + $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); + $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + $data = $updateData['$set']; + })); + + $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); + $this->assertTrue($this->storage->write('foo', 'bar')); + + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); + } + + public function testWriteWhenUsingExpiresField() + { + $this->options = array( + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'database' => 'sf2-test', + 'collection' => 'session-test', + 'expiry_field' => 'expiresAt', + ); + + $this->storage = new MongoDbSessionHandler($this->mongo, $this->options); + + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $data = array(); + + $collection->expects($this->once()) + ->method('update') + ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { + $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); + $this->assertEquals(array('upsert' => true, 'multiple' => false), $options); + + $data = $updateData['$set']; + })); + + $this->assertTrue($this->storage->write('foo', 'bar')); + + $this->assertEquals('bar', $data[$this->options['data_field']]->bin); + $this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); + $this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); + } + + public function testReplaceSessionData() + { + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $data = array(); + + $collection->expects($this->exactly(2)) + ->method('update') + ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { + $data = $updateData; + })); + + $this->storage->write('foo', 'bar'); + $this->storage->write('foo', 'foobar'); + + $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin); + } + + public function testDestroy() + { + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $collection->expects($this->once()) + ->method('remove') + ->with(array($this->options['id_field'] => 'foo')); + + $this->assertTrue($this->storage->destroy('foo')); + } + + public function testGc() + { + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $collection->expects($this->once()) + ->method('remove') + ->will($this->returnCallback(function ($criteria) { + $this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec); + })); + + $this->assertTrue($this->storage->gc(1)); + } + + public function testGetConnection() + { + $method = new \ReflectionMethod($this->storage, 'getMongo'); + $method->setAccessible(true); + + $mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? '\Mongo' : '\MongoClient'; + + $this->assertInstanceOf($mongoClass, $method->invoke($this->storage)); + } + + private function createMongoCollectionMock() + { + $collection = $this->getMockBuilder('MongoCollection') + ->disableOriginalConstructor() + ->getMock(); + + return $collection; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php new file mode 100644 index 0000000..c3eeacf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * Test class for NativeFileSessionHandler. + * + * @author Drak + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class NativeFileSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler(sys_get_temp_dir())); + + $this->assertEquals('files', $storage->getSaveHandler()->getSaveHandlerName()); + $this->assertEquals('user', ini_get('session.save_handler')); + + $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path')); + $this->assertEquals('TESTING', ini_get('session.name')); + } + + /** + * @dataProvider savePathDataProvider + */ + public function testConstructSavePath($savePath, $expectedSavePath, $path) + { + $handler = new NativeFileSessionHandler($savePath); + $this->assertEquals($expectedSavePath, ini_get('session.save_path')); + $this->assertTrue(is_dir(realpath($path))); + + rmdir($path); + } + + public function savePathDataProvider() + { + $base = sys_get_temp_dir(); + + return array( + array("$base/foo", "$base/foo", "$base/foo"), + array("5;$base/foo", "5;$base/foo", "$base/foo"), + array("5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructException() + { + $handler = new NativeFileSessionHandler('something;invalid;with;too-many-args'); + } + + public function testConstructDefault() + { + $path = ini_get('session.save_path'); + $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler()); + + $this->assertEquals($path, ini_get('session.save_path')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php new file mode 100644 index 0000000..b2304cb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; + +/** + * Test class for NativeSessionHandler. + * + * @author Drak + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class NativeSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $handler = new NativeSessionHandler(); + + $this->assertTrue($handler instanceof \SessionHandler); + $this->assertTrue($handler instanceof NativeSessionHandler); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php new file mode 100644 index 0000000..35823d6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Session; + +/** + * Test class for NullSessionHandler. + * + * @author Drak + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class NullSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testSaveHandlers() + { + $storage = $this->getStorage(); + $this->assertEquals('user', ini_get('session.save_handler')); + } + + public function testSession() + { + session_id('nullsessionstorage'); + $storage = $this->getStorage(); + $session = new Session($storage); + $this->assertNull($session->get('something')); + $session->set('something', 'unique'); + $this->assertEquals('unique', $session->get('something')); + } + + public function testNothingIsPersisted() + { + session_id('nullsessionstorage'); + $storage = $this->getStorage(); + $session = new Session($storage); + $session->start(); + $this->assertEquals('nullsessionstorage', $session->getId()); + $this->assertNull($session->get('something')); + } + + public function getStorage() + { + return new NativeSessionStorage(array(), new NullSessionHandler()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php new file mode 100644 index 0000000..1af3210 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -0,0 +1,357 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; + +/** + * @requires extension pdo_sqlite + * @group time-sensitive + */ +class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + private $dbFile; + + protected function tearDown() + { + // make sure the temporary database file is deleted when it has been created (even when a test fails) + if ($this->dbFile) { + @unlink($this->dbFile); + } + parent::tearDown(); + } + + protected function getPersistentSqliteDsn() + { + $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions'); + + return 'sqlite:'.$this->dbFile; + } + + protected function getMemorySqlitePdo() + { + $pdo = new \PDO('sqlite::memory:'); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $storage = new PdoSessionHandler($pdo); + $storage->createTable(); + + return $pdo; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testWrongPdoErrMode() + { + $pdo = $this->getMemorySqlitePdo(); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); + + $storage = new PdoSessionHandler($pdo); + } + + /** + * @expectedException \RuntimeException + */ + public function testInexistentTable() + { + $storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table')); + $storage->open('', 'sid'); + $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + } + + /** + * @expectedException \RuntimeException + */ + public function testCreateTableTwice() + { + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->createTable(); + } + + public function testWithLazyDsnConnection() + { + $dsn = $this->getPersistentSqliteDsn(); + + $storage = new PdoSessionHandler($dsn); + $storage->createTable(); + $storage->open('', 'sid'); + $data = $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertSame('', $data, 'New session returns empty string data'); + + $storage->open('', 'sid'); + $data = $storage->read('id'); + $storage->close(); + $this->assertSame('data', $data, 'Written value can be read back correctly'); + } + + public function testWithLazySavePathConnection() + { + $dsn = $this->getPersistentSqliteDsn(); + + // Open is called with what ini_set('session.save_path', $dsn) would mean + $storage = new PdoSessionHandler(null); + $storage->open($dsn, 'sid'); + $storage->createTable(); + $data = $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertSame('', $data, 'New session returns empty string data'); + + $storage->open($dsn, 'sid'); + $data = $storage->read('id'); + $storage->close(); + $this->assertSame('data', $data, 'Written value can be read back correctly'); + } + + public function testReadWriteReadWithNullByte() + { + $sessionData = 'da'."\0".'ta'; + + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->open('', 'sid'); + $readData = $storage->read('id'); + $storage->write('id', $sessionData); + $storage->close(); + $this->assertSame('', $readData, 'New session returns empty string data'); + + $storage->open('', 'sid'); + $readData = $storage->read('id'); + $storage->close(); + $this->assertSame($sessionData, $readData, 'Written value can be read back correctly'); + } + + public function testReadConvertsStreamToString() + { + $pdo = new MockPdo('pgsql'); + $pdo->prepareResult = $this->getMock('PDOStatement'); + + $content = 'foobar'; + $stream = $this->createStream($content); + + $pdo->prepareResult->expects($this->once())->method('fetchAll') + ->will($this->returnValue(array(array($stream, 42, time())))); + + $storage = new PdoSessionHandler($pdo); + $result = $storage->read('foo'); + + $this->assertSame($content, $result); + } + + public function testReadLockedConvertsStreamToString() + { + $pdo = new MockPdo('pgsql'); + $selectStmt = $this->getMock('PDOStatement'); + $insertStmt = $this->getMock('PDOStatement'); + + $pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) { + return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt; + }; + + $content = 'foobar'; + $stream = $this->createStream($content); + $exception = null; + + $selectStmt->expects($this->atLeast(2))->method('fetchAll') + ->will($this->returnCallback(function () use (&$exception, $stream) { + return $exception ? array(array($stream, 42, time())) : array(); + })); + + $insertStmt->expects($this->once())->method('execute') + ->will($this->returnCallback(function () use (&$exception) { + throw $exception = new \PDOException('', '23'); + })); + + $storage = new PdoSessionHandler($pdo); + $result = $storage->read('foo'); + + $this->assertSame($content, $result); + } + + public function testReadingRequiresExactlySameId() + { + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->open('', 'sid'); + $storage->write('id', 'data'); + $storage->write('test', 'data'); + $storage->write('space ', 'data'); + $storage->close(); + + $storage->open('', 'sid'); + $readDataCaseSensitive = $storage->read('ID'); + $readDataNoCharFolding = $storage->read('tést'); + $readDataKeepSpace = $storage->read('space '); + $readDataExtraSpace = $storage->read('space '); + $storage->close(); + + $this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)'); + $this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)'); + $this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is'); + $this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is'); + } + + /** + * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace). + */ + public function testWriteDifferentSessionIdThanRead() + { + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->open('', 'sid'); + $storage->read('id'); + $storage->destroy('id'); + $storage->write('new_id', 'data_of_new_session_id'); + $storage->close(); + + $storage->open('', 'sid'); + $data = $storage->read('new_id'); + $storage->close(); + + $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available'); + } + + public function testWrongUsageStillWorks() + { + // wrong method sequence that should no happen, but still works + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + $storage->write('id', 'data'); + $storage->write('other_id', 'other_data'); + $storage->destroy('inexistent'); + $storage->open('', 'sid'); + $data = $storage->read('id'); + $otherData = $storage->read('other_id'); + $storage->close(); + + $this->assertSame('data', $data); + $this->assertSame('other_data', $otherData); + } + + public function testSessionDestroy() + { + $pdo = $this->getMemorySqlitePdo(); + $storage = new PdoSessionHandler($pdo); + + $storage->open('', 'sid'); + $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + + $storage->open('', 'sid'); + $storage->read('id'); + $storage->destroy('id'); + $storage->close(); + $this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn()); + + $storage->open('', 'sid'); + $data = $storage->read('id'); + $storage->close(); + $this->assertSame('', $data, 'Destroyed session returns empty string'); + } + + public function testSessionGC() + { + $previousLifeTime = ini_set('session.gc_maxlifetime', 1000); + $pdo = $this->getMemorySqlitePdo(); + $storage = new PdoSessionHandler($pdo); + + $storage->open('', 'sid'); + $storage->read('id'); + $storage->write('id', 'data'); + $storage->close(); + + $storage->open('', 'sid'); + $storage->read('gc_id'); + ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read + $storage->write('gc_id', 'data'); + $storage->close(); + $this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called'); + + $storage->open('', 'sid'); + $data = $storage->read('gc_id'); + $storage->gc(-1); + $storage->close(); + + ini_set('session.gc_maxlifetime', $previousLifeTime); + + $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet'); + $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned'); + } + + public function testGetConnection() + { + $storage = new PdoSessionHandler($this->getMemorySqlitePdo()); + + $method = new \ReflectionMethod($storage, 'getConnection'); + $method->setAccessible(true); + + $this->assertInstanceOf('\PDO', $method->invoke($storage)); + } + + public function testGetConnectionConnectsIfNeeded() + { + $storage = new PdoSessionHandler('sqlite::memory:'); + + $method = new \ReflectionMethod($storage, 'getConnection'); + $method->setAccessible(true); + + $this->assertInstanceOf('\PDO', $method->invoke($storage)); + } + + private function createStream($content) + { + $stream = tmpfile(); + fwrite($stream, $content); + fseek($stream, 0); + + return $stream; + } +} + +class MockPdo extends \PDO +{ + public $prepareResult; + private $driverName; + private $errorMode; + + public function __construct($driverName = null, $errorMode = null) + { + $this->driverName = $driverName; + $this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION; + } + + public function getAttribute($attribute) + { + if (\PDO::ATTR_ERRMODE === $attribute) { + return $this->errorMode; + } + + if (\PDO::ATTR_DRIVER_NAME === $attribute) { + return $this->driverName; + } + + return parent::getAttribute($attribute); + } + + public function prepare($statement, $driverOptions = array()) + { + return is_callable($this->prepareResult) + ? call_user_func($this->prepareResult, $statement, $driverOptions) + : $this->prepareResult; + } + + public function beginTransaction() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php new file mode 100644 index 0000000..069c887 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler; + +use Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler; + +/** + * @author Adrien Brault + */ +class WriteCheckSessionHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function test() + { + $wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface'); + $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); + + $wrappedSessionHandlerMock + ->expects($this->once()) + ->method('close') + ->with() + ->will($this->returnValue(true)) + ; + + $this->assertTrue($writeCheckSessionHandler->close()); + } + + public function testWrite() + { + $wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface'); + $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); + + $wrappedSessionHandlerMock + ->expects($this->once()) + ->method('write') + ->with('foo', 'bar') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar')); + } + + public function testSkippedWrite() + { + $wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface'); + $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); + + $wrappedSessionHandlerMock + ->expects($this->once()) + ->method('read') + ->with('foo') + ->will($this->returnValue('bar')) + ; + + $wrappedSessionHandlerMock + ->expects($this->never()) + ->method('write') + ; + + $this->assertEquals('bar', $writeCheckSessionHandler->read('foo')); + $this->assertTrue($writeCheckSessionHandler->write('foo', 'bar')); + } + + public function testNonSkippedWrite() + { + $wrappedSessionHandlerMock = $this->getMock('SessionHandlerInterface'); + $writeCheckSessionHandler = new WriteCheckSessionHandler($wrappedSessionHandlerMock); + + $wrappedSessionHandlerMock + ->expects($this->once()) + ->method('read') + ->with('foo') + ->will($this->returnValue('bar')) + ; + + $wrappedSessionHandlerMock + ->expects($this->once()) + ->method('write') + ->with('foo', 'baZZZ') + ->will($this->returnValue(true)) + ; + + $this->assertEquals('bar', $writeCheckSessionHandler->read('foo')); + $this->assertTrue($writeCheckSessionHandler->write('foo', 'baZZZ')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MetadataBagTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MetadataBagTest.php new file mode 100644 index 0000000..2fa473f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MetadataBagTest.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; + +/** + * Test class for MetadataBag. + * + * @group time-sensitive + */ +class MetadataBagTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var MetadataBag + */ + protected $bag; + + /** + * @var array + */ + protected $array = array(); + + protected function setUp() + { + parent::setUp(); + $this->bag = new MetadataBag(); + $this->array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 0); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->array = array(); + $this->bag = null; + parent::tearDown(); + } + + public function testInitialize() + { + $sessionMetadata = array(); + + $bag1 = new MetadataBag(); + $bag1->initialize($sessionMetadata); + $this->assertGreaterThanOrEqual(time(), $bag1->getCreated()); + $this->assertEquals($bag1->getCreated(), $bag1->getLastUsed()); + + sleep(1); + $bag2 = new MetadataBag(); + $bag2->initialize($sessionMetadata); + $this->assertEquals($bag1->getCreated(), $bag2->getCreated()); + $this->assertEquals($bag1->getLastUsed(), $bag2->getLastUsed()); + $this->assertEquals($bag2->getCreated(), $bag2->getLastUsed()); + + sleep(1); + $bag3 = new MetadataBag(); + $bag3->initialize($sessionMetadata); + $this->assertEquals($bag1->getCreated(), $bag3->getCreated()); + $this->assertGreaterThan($bag2->getLastUsed(), $bag3->getLastUsed()); + $this->assertNotEquals($bag3->getCreated(), $bag3->getLastUsed()); + } + + public function testGetSetName() + { + $this->assertEquals('__metadata', $this->bag->getName()); + $this->bag->setName('foo'); + $this->assertEquals('foo', $this->bag->getName()); + } + + public function testGetStorageKey() + { + $this->assertEquals('_sf2_meta', $this->bag->getStorageKey()); + } + + public function testGetLifetime() + { + $bag = new MetadataBag(); + $array = array(MetadataBag::CREATED => 1234567, MetadataBag::UPDATED => 12345678, MetadataBag::LIFETIME => 1000); + $bag->initialize($array); + $this->assertEquals(1000, $bag->getLifetime()); + } + + public function testGetCreated() + { + $this->assertEquals(1234567, $this->bag->getCreated()); + } + + public function testGetLastUsed() + { + $this->assertLessThanOrEqual(time(), $this->bag->getLastUsed()); + } + + public function testClear() + { + $this->bag->clear(); + } + + public function testSkipLastUsedUpdate() + { + $bag = new MetadataBag('', 30); + $timeStamp = time(); + + $created = $timeStamp - 15; + $sessionMetadata = array( + MetadataBag::CREATED => $created, + MetadataBag::UPDATED => $created, + MetadataBag::LIFETIME => 1000, + ); + $bag->initialize($sessionMetadata); + + $this->assertEquals($created, $sessionMetadata[MetadataBag::UPDATED]); + } + + public function testDoesNotSkipLastUsedUpdate() + { + $bag = new MetadataBag('', 30); + $timeStamp = time(); + + $created = $timeStamp - 45; + $sessionMetadata = array( + MetadataBag::CREATED => $created, + MetadataBag::UPDATED => $created, + MetadataBag::LIFETIME => 1000, + ); + $bag->initialize($sessionMetadata); + + $this->assertEquals($timeStamp, $sessionMetadata[MetadataBag::UPDATED]); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php new file mode 100644 index 0000000..c56ea4a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; + +/** + * Test class for MockArraySessionStorage. + * + * @author Drak + */ +class MockArraySessionStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var MockArraySessionStorage + */ + private $storage; + + /** + * @var AttributeBag + */ + private $attributes; + + /** + * @var FlashBag + */ + private $flashes; + + private $data; + + protected function setUp() + { + $this->attributes = new AttributeBag(); + $this->flashes = new FlashBag(); + + $this->data = array( + $this->attributes->getStorageKey() => array('foo' => 'bar'), + $this->flashes->getStorageKey() => array('notice' => 'hello'), + ); + + $this->storage = new MockArraySessionStorage(); + $this->storage->registerBag($this->flashes); + $this->storage->registerBag($this->attributes); + $this->storage->setSessionData($this->data); + } + + protected function tearDown() + { + $this->data = null; + $this->flashes = null; + $this->attributes = null; + $this->storage = null; + } + + public function testStart() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $id = $this->storage->getId(); + $this->assertNotEquals('', $id); + $this->storage->start(); + $this->assertEquals($id, $this->storage->getId()); + } + + public function testRegenerate() + { + $this->storage->start(); + $id = $this->storage->getId(); + $this->storage->regenerate(); + $this->assertNotEquals($id, $this->storage->getId()); + $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all()); + $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll()); + + $id = $this->storage->getId(); + $this->storage->regenerate(true); + $this->assertNotEquals($id, $this->storage->getId()); + $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all()); + $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll()); + } + + public function testGetId() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $this->assertNotEquals('', $this->storage->getId()); + } + + /** + * @expectedException \RuntimeException + */ + public function testUnstartedSave() + { + $this->storage->save(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockFileSessionStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockFileSessionStorageTest.php new file mode 100644 index 0000000..54321ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockFileSessionStorageTest.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + +/** + * Test class for MockFileSessionStorage. + * + * @author Drak + */ +class MockFileSessionStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + private $sessionDir; + + /** + * @var MockFileSessionStorage + */ + protected $storage; + + protected function setUp() + { + $this->sessionDir = sys_get_temp_dir().'/sf2test'; + $this->storage = $this->getStorage(); + } + + protected function tearDown() + { + $this->sessionDir = null; + $this->storage = null; + array_map('unlink', glob($this->sessionDir.'/*.session')); + if (is_dir($this->sessionDir)) { + rmdir($this->sessionDir); + } + } + + public function testStart() + { + $this->assertEquals('', $this->storage->getId()); + $this->assertTrue($this->storage->start()); + $id = $this->storage->getId(); + $this->assertNotEquals('', $this->storage->getId()); + $this->assertTrue($this->storage->start()); + $this->assertEquals($id, $this->storage->getId()); + } + + public function testRegenerate() + { + $this->storage->start(); + $this->storage->getBag('attributes')->set('regenerate', 1234); + $this->storage->regenerate(); + $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate')); + $this->storage->regenerate(true); + $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate')); + } + + public function testGetId() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $this->assertNotEquals('', $this->storage->getId()); + } + + public function testSave() + { + $this->storage->start(); + $id = $this->storage->getId(); + $this->assertNotEquals('108', $this->storage->getBag('attributes')->get('new')); + $this->assertFalse($this->storage->getBag('flashes')->has('newkey')); + $this->storage->getBag('attributes')->set('new', '108'); + $this->storage->getBag('flashes')->set('newkey', 'test'); + $this->storage->save(); + + $storage = $this->getStorage(); + $storage->setId($id); + $storage->start(); + $this->assertEquals('108', $storage->getBag('attributes')->get('new')); + $this->assertTrue($storage->getBag('flashes')->has('newkey')); + $this->assertEquals(array('test'), $storage->getBag('flashes')->peek('newkey')); + } + + public function testMultipleInstances() + { + $storage1 = $this->getStorage(); + $storage1->start(); + $storage1->getBag('attributes')->set('foo', 'bar'); + $storage1->save(); + + $storage2 = $this->getStorage(); + $storage2->setId($storage1->getId()); + $storage2->start(); + $this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances'); + } + + /** + * @expectedException \RuntimeException + */ + public function testSaveWithoutStart() + { + $storage1 = $this->getStorage(); + $storage1->save(); + } + + private function getStorage() + { + $storage = new MockFileSessionStorage($this->sessionDir); + $storage->registerBag(new FlashBag()); + $storage->registerBag(new AttributeBag()); + + return $storage; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php new file mode 100644 index 0000000..778a3f6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * Test class for NativeSessionStorage. + * + * @author Drak + * + * These tests require separate processes. + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class NativeSessionStorageTest extends \PHPUnit_Framework_TestCase +{ + private $savePath; + + protected function setUp() + { + $this->iniSet('session.save_handler', 'files'); + $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test'); + if (!is_dir($this->savePath)) { + mkdir($this->savePath); + } + } + + protected function tearDown() + { + session_write_close(); + array_map('unlink', glob($this->savePath.'/*')); + if (is_dir($this->savePath)) { + rmdir($this->savePath); + } + + $this->savePath = null; + } + + /** + * @param array $options + * + * @return NativeSessionStorage + */ + protected function getStorage(array $options = array()) + { + $storage = new NativeSessionStorage($options); + $storage->registerBag(new AttributeBag()); + + return $storage; + } + + public function testBag() + { + $storage = $this->getStorage(); + $bag = new FlashBag(); + $storage->registerBag($bag); + $this->assertSame($bag, $storage->getBag($bag->getName())); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRegisterBagException() + { + $storage = $this->getStorage(); + $storage->getBag('non_existing'); + } + + public function testGetId() + { + $storage = $this->getStorage(); + $this->assertSame('', $storage->getId(), 'Empty ID before starting session'); + + $storage->start(); + $id = $storage->getId(); + $this->assertInternalType('string', $id); + $this->assertNotSame('', $id); + + $storage->save(); + $this->assertSame($id, $storage->getId(), 'ID stays after saving session'); + } + + public function testRegenerate() + { + $storage = $this->getStorage(); + $storage->start(); + $id = $storage->getId(); + $storage->getBag('attributes')->set('lucky', 7); + $storage->regenerate(); + $this->assertNotEquals($id, $storage->getId()); + $this->assertEquals(7, $storage->getBag('attributes')->get('lucky')); + } + + public function testRegenerateDestroy() + { + $storage = $this->getStorage(); + $storage->start(); + $id = $storage->getId(); + $storage->getBag('attributes')->set('legs', 11); + $storage->regenerate(true); + $this->assertNotEquals($id, $storage->getId()); + $this->assertEquals(11, $storage->getBag('attributes')->get('legs')); + } + + public function testSessionGlobalIsUpToDateAfterIdRegeneration() + { + $storage = $this->getStorage(); + $storage->start(); + $storage->getBag('attributes')->set('lucky', 7); + $storage->regenerate(); + $storage->getBag('attributes')->set('lucky', 42); + + $this->assertEquals(42, $_SESSION['_sf2_attributes']['lucky']); + } + + public function testRegenerationFailureDoesNotFlagStorageAsStarted() + { + $storage = $this->getStorage(); + $this->assertFalse($storage->regenerate()); + $this->assertFalse($storage->isStarted()); + } + + public function testDefaultSessionCacheLimiter() + { + $this->iniSet('session.cache_limiter', 'nocache'); + + $storage = new NativeSessionStorage(); + $this->assertEquals('', ini_get('session.cache_limiter')); + } + + public function testExplicitSessionCacheLimiter() + { + $this->iniSet('session.cache_limiter', 'nocache'); + + $storage = new NativeSessionStorage(array('cache_limiter' => 'public')); + $this->assertEquals('public', ini_get('session.cache_limiter')); + } + + public function testCookieOptions() + { + $options = array( + 'cookie_lifetime' => 123456, + 'cookie_path' => '/my/cookie/path', + 'cookie_domain' => 'symfony.example.com', + 'cookie_secure' => true, + 'cookie_httponly' => false, + ); + + $this->getStorage($options); + $temp = session_get_cookie_params(); + $gco = array(); + + foreach ($temp as $key => $value) { + $gco['cookie_'.$key] = $value; + } + + $this->assertEquals($options, $gco); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetSaveHandlerException() + { + $storage = $this->getStorage(); + $storage->setSaveHandler(new \stdClass()); + } + + public function testSetSaveHandler() + { + $this->iniSet('session.save_handler', 'files'); + $storage = $this->getStorage(); + $storage->setSaveHandler(); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(null); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandlerProxy(new NativeSessionHandler())); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new NativeSessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler())); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new NullSessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + } + + /** + * @expectedException \RuntimeException + */ + public function testStarted() + { + $storage = $this->getStorage(); + + $this->assertFalse($storage->getSaveHandler()->isActive()); + $this->assertFalse($storage->isStarted()); + + session_start(); + $this->assertTrue(isset($_SESSION)); + $this->assertTrue($storage->getSaveHandler()->isActive()); + + // PHP session might have started, but the storage driver has not, so false is correct here + $this->assertFalse($storage->isStarted()); + + $key = $storage->getMetadataBag()->getStorageKey(); + $this->assertFalse(isset($_SESSION[$key])); + $storage->start(); + } + + public function testRestart() + { + $storage = $this->getStorage(); + $storage->start(); + $id = $storage->getId(); + $storage->getBag('attributes')->set('lucky', 7); + $storage->save(); + $storage->start(); + $this->assertSame($id, $storage->getId(), 'Same session ID after restarting'); + $this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php new file mode 100644 index 0000000..b349277 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + +/** + * Test class for PhpSessionStorage. + * + * @author Drak + * + * These tests require separate processes. + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class PhpBridgeSessionStorageTest extends \PHPUnit_Framework_TestCase +{ + private $savePath; + + protected function setUp() + { + $this->iniSet('session.save_handler', 'files'); + $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test'); + if (!is_dir($this->savePath)) { + mkdir($this->savePath); + } + } + + protected function tearDown() + { + session_write_close(); + array_map('unlink', glob($this->savePath.'/*')); + if (is_dir($this->savePath)) { + rmdir($this->savePath); + } + + $this->savePath = null; + } + + /** + * @return PhpBridgeSessionStorage + */ + protected function getStorage() + { + $storage = new PhpBridgeSessionStorage(); + $storage->registerBag(new AttributeBag()); + + return $storage; + } + + public function testPhpSession() + { + $storage = $this->getStorage(); + + $this->assertFalse($storage->getSaveHandler()->isActive()); + $this->assertFalse($storage->isStarted()); + + session_start(); + $this->assertTrue(isset($_SESSION)); + // in PHP 5.4 we can reliably detect a session started + $this->assertTrue($storage->getSaveHandler()->isActive()); + // PHP session might have started, but the storage driver has not, so false is correct here + $this->assertFalse($storage->isStarted()); + + $key = $storage->getMetadataBag()->getStorageKey(); + $this->assertFalse(isset($_SESSION[$key])); + $storage->start(); + $this->assertTrue(isset($_SESSION[$key])); + } + + public function testClear() + { + $storage = $this->getStorage(); + session_start(); + $_SESSION['drak'] = 'loves symfony'; + $storage->getBag('attributes')->set('symfony', 'greatness'); + $key = $storage->getBag('attributes')->getStorageKey(); + $this->assertEquals($_SESSION[$key], array('symfony' => 'greatness')); + $this->assertEquals($_SESSION['drak'], 'loves symfony'); + $storage->clear(); + $this->assertEquals($_SESSION[$key], array()); + $this->assertEquals($_SESSION['drak'], 'loves symfony'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php new file mode 100644 index 0000000..b29c433 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; + +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; + +// Note until PHPUnit_Mock_Objects 1.2 is released you cannot mock abstracts due to +// https://github.com/sebastianbergmann/phpunit-mock-objects/issues/73 +class ConcreteProxy extends AbstractProxy +{ +} + +class ConcreteSessionHandlerInterfaceProxy extends AbstractProxy implements \SessionHandlerInterface +{ + public function open($savePath, $sessionName) + { + } + + public function close() + { + } + + public function read($id) + { + } + + public function write($id, $data) + { + } + + public function destroy($id) + { + } + + public function gc($maxlifetime) + { + } +} + +/** + * Test class for AbstractProxy. + * + * @author Drak + */ +class AbstractProxyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AbstractProxy + */ + protected $proxy; + + protected function setUp() + { + $this->proxy = new ConcreteProxy(); + } + + protected function tearDown() + { + $this->proxy = null; + } + + public function testGetSaveHandlerName() + { + $this->assertNull($this->proxy->getSaveHandlerName()); + } + + public function testIsSessionHandlerInterface() + { + $this->assertFalse($this->proxy->isSessionHandlerInterface()); + $sh = new ConcreteSessionHandlerInterfaceProxy(); + $this->assertTrue($sh->isSessionHandlerInterface()); + } + + public function testIsWrapper() + { + $this->assertFalse($this->proxy->isWrapper()); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testIsActive() + { + $this->assertFalse($this->proxy->isActive()); + session_start(); + $this->assertTrue($this->proxy->isActive()); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testName() + { + $this->assertEquals(session_name(), $this->proxy->getName()); + $this->proxy->setName('foo'); + $this->assertEquals('foo', $this->proxy->getName()); + $this->assertEquals(session_name(), $this->proxy->getName()); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + * @expectedException \LogicException + */ + public function testNameException() + { + session_start(); + $this->proxy->setName('foo'); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testId() + { + $this->assertEquals(session_id(), $this->proxy->getId()); + $this->proxy->setId('foo'); + $this->assertEquals('foo', $this->proxy->getId()); + $this->assertEquals(session_id(), $this->proxy->getId()); + } + + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + * @expectedException \LogicException + */ + public function testIdException() + { + session_start(); + $this->proxy->setId('foo'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/NativeProxyTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/NativeProxyTest.php new file mode 100644 index 0000000..e9184ce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/NativeProxyTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; + +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; + +/** + * Test class for NativeProxy. + * + * @author Drak + */ +class NativeProxyTest extends \PHPUnit_Framework_TestCase +{ + public function testIsWrapper() + { + $proxy = new NativeProxy(); + $this->assertFalse($proxy->isWrapper()); + } + + public function testGetSaveHandlerName() + { + $name = ini_get('session.save_handler'); + $proxy = new NativeProxy(); + $this->assertEquals($name, $proxy->getSaveHandlerName()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php new file mode 100644 index 0000000..bc810a6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; + +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * Tests for SessionHandlerProxy class. + * + * @author Drak + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class SessionHandlerProxyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_Matcher + */ + private $mock; + + /** + * @var SessionHandlerProxy + */ + private $proxy; + + protected function setUp() + { + $this->mock = $this->getMock('SessionHandlerInterface'); + $this->proxy = new SessionHandlerProxy($this->mock); + } + + protected function tearDown() + { + $this->mock = null; + $this->proxy = null; + } + + public function testOpenTrue() + { + $this->mock->expects($this->once()) + ->method('open') + ->will($this->returnValue(true)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->open('name', 'id'); + $this->assertFalse($this->proxy->isActive()); + } + + public function testOpenFalse() + { + $this->mock->expects($this->once()) + ->method('open') + ->will($this->returnValue(false)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->open('name', 'id'); + $this->assertFalse($this->proxy->isActive()); + } + + public function testClose() + { + $this->mock->expects($this->once()) + ->method('close') + ->will($this->returnValue(true)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->close(); + $this->assertFalse($this->proxy->isActive()); + } + + public function testCloseFalse() + { + $this->mock->expects($this->once()) + ->method('close') + ->will($this->returnValue(false)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->close(); + $this->assertFalse($this->proxy->isActive()); + } + + public function testRead() + { + $this->mock->expects($this->once()) + ->method('read'); + + $this->proxy->read('id'); + } + + public function testWrite() + { + $this->mock->expects($this->once()) + ->method('write'); + + $this->proxy->write('id', 'data'); + } + + public function testDestroy() + { + $this->mock->expects($this->once()) + ->method('destroy'); + + $this->proxy->destroy('id'); + } + + public function testGc() + { + $this->mock->expects($this->once()) + ->method('gc'); + + $this->proxy->gc(86400); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php new file mode 100644 index 0000000..121459c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Tests/StreamedResponseTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\StreamedResponse; + +class StreamedResponseTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $response = new StreamedResponse(function () { echo 'foo'; }, 404, array('Content-Type' => 'text/plain')); + + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEquals('text/plain', $response->headers->get('Content-Type')); + } + + public function testPrepareWith11Protocol() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $request = Request::create('/'); + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); + + $response->prepare($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertNotEquals('chunked', $response->headers->get('Transfer-Encoding'), 'Apache assumes responses with a Transfer-Encoding header set to chunked to already be encoded.'); + } + + public function testPrepareWith10Protocol() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $request = Request::create('/'); + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); + + $response->prepare($request); + + $this->assertEquals('1.0', $response->getProtocolVersion()); + $this->assertNull($response->headers->get('Transfer-Encoding')); + } + + public function testPrepareWithHeadRequest() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $request = Request::create('/', 'HEAD'); + + $response->prepare($request); + } + + public function testPrepareWithCacheHeaders() + { + $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Cache-Control' => 'max-age=600, public')); + $request = Request::create('/', 'GET'); + + $response->prepare($request); + $this->assertEquals('max-age=600, public', $response->headers->get('Cache-Control')); + } + + public function testSendContent() + { + $called = 0; + + $response = new StreamedResponse(function () use (&$called) { ++$called; }); + + $response->sendContent(); + $this->assertEquals(1, $called); + + $response->sendContent(); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \LogicException + */ + public function testSendContentWithNonCallable() + { + $response = new StreamedResponse(null); + $response->sendContent(); + } + + /** + * @expectedException \LogicException + */ + public function testSetContent() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $response->setContent('foo'); + } + + public function testGetContent() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $this->assertFalse($response->getContent()); + } + + public function testCreate() + { + $response = StreamedResponse::create(function () {}, 204); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response); + $this->assertEquals(204, $response->getStatusCode()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/composer.json b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/composer.json new file mode 100644 index 0000000..91ccc39 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/composer.json @@ -0,0 +1,36 @@ +{ + "name": "symfony/http-foundation", + "type": "library", + "description": "Symfony HttpFoundation Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/phpunit.xml.dist new file mode 100644 index 0000000..9ffdb43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/Bundle.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/Bundle.php new file mode 100644 index 0000000..647b363 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/Bundle.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Bundle; + +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Console\Application; +use Symfony\Component\Finder\Finder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * An implementation of BundleInterface that adds a few conventions + * for DependencyInjection extensions and Console commands. + * + * @author Fabien Potencier + */ +abstract class Bundle implements BundleInterface +{ + use ContainerAwareTrait; + + protected $name; + protected $extension; + protected $path; + + /** + * Boots the Bundle. + */ + public function boot() + { + } + + /** + * Shutdowns the Bundle. + */ + public function shutdown() + { + } + + /** + * Builds the bundle. + * + * It is only ever called once when the cache is empty. + * + * This method can be overridden to register compilation passes, + * other extensions, ... + * + * @param ContainerBuilder $container A ContainerBuilder instance + */ + public function build(ContainerBuilder $container) + { + } + + /** + * Returns the bundle's container extension. + * + * @return ExtensionInterface|null The container extension + * + * @throws \LogicException + */ + public function getContainerExtension() + { + if (null === $this->extension) { + $extension = $this->createContainerExtension(); + + if (null !== $extension) { + if (!$extension instanceof ExtensionInterface) { + throw new \LogicException(sprintf('Extension %s must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface.', get_class($extension))); + } + + // check naming convention + $basename = preg_replace('/Bundle$/', '', $this->getName()); + $expectedAlias = Container::underscore($basename); + + if ($expectedAlias != $extension->getAlias()) { + throw new \LogicException(sprintf( + 'Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.', + $expectedAlias, $extension->getAlias() + )); + } + + $this->extension = $extension; + } else { + $this->extension = false; + } + } + + if ($this->extension) { + return $this->extension; + } + } + + /** + * Gets the Bundle namespace. + * + * @return string The Bundle namespace + */ + public function getNamespace() + { + $class = get_class($this); + + return substr($class, 0, strrpos($class, '\\')); + } + + /** + * Gets the Bundle directory path. + * + * @return string The Bundle absolute path + */ + public function getPath() + { + if (null === $this->path) { + $reflected = new \ReflectionObject($this); + $this->path = dirname($reflected->getFileName()); + } + + return $this->path; + } + + /** + * Returns the bundle parent name. + * + * @return string The Bundle parent name it overrides or null if no parent + */ + public function getParent() + { + } + + /** + * Returns the bundle name (the class short name). + * + * @return string The Bundle name + */ + final public function getName() + { + if (null !== $this->name) { + return $this->name; + } + + $name = get_class($this); + $pos = strrpos($name, '\\'); + + return $this->name = false === $pos ? $name : substr($name, $pos + 1); + } + + /** + * Finds and registers Commands. + * + * Override this method if your bundle commands do not follow the conventions: + * + * * Commands are in the 'Command' sub-directory + * * Commands extend Symfony\Component\Console\Command\Command + * + * @param Application $application An Application instance + */ + public function registerCommands(Application $application) + { + if (!is_dir($dir = $this->getPath().'/Command')) { + return; + } + + if (!class_exists('Symfony\Component\Finder\Finder')) { + throw new \RuntimeException('You need the symfony/finder component to register bundle commands.'); + } + + $finder = new Finder(); + $finder->files()->name('*Command.php')->in($dir); + + $prefix = $this->getNamespace().'\\Command'; + foreach ($finder as $file) { + $ns = $prefix; + if ($relativePath = $file->getRelativePath()) { + $ns .= '\\'.strtr($relativePath, '/', '\\'); + } + $class = $ns.'\\'.$file->getBasename('.php'); + if ($this->container) { + $alias = 'console.command.'.strtolower(str_replace('\\', '_', $class)); + if ($this->container->has($alias)) { + continue; + } + } + $r = new \ReflectionClass($class); + if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { + $application->add($r->newInstance()); + } + } + } + + /** + * Returns the bundle's container extension class. + * + * @return string + */ + protected function getContainerExtensionClass() + { + $basename = preg_replace('/Bundle$/', '', $this->getName()); + + return $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; + } + + /** + * Creates the bundle's container extension. + * + * @return ExtensionInterface|null + */ + protected function createContainerExtension() + { + if (class_exists($class = $this->getContainerExtensionClass())) { + return new $class(); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php new file mode 100644 index 0000000..25eea1d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Bundle/BundleInterface.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Bundle; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * BundleInterface. + * + * @author Fabien Potencier + */ +interface BundleInterface extends ContainerAwareInterface +{ + /** + * Boots the Bundle. + */ + public function boot(); + + /** + * Shutdowns the Bundle. + */ + public function shutdown(); + + /** + * Builds the bundle. + * + * It is only ever called once when the cache is empty. + * + * @param ContainerBuilder $container A ContainerBuilder instance + */ + public function build(ContainerBuilder $container); + + /** + * Returns the container extension that should be implicitly loaded. + * + * @return ExtensionInterface|null The default extension or null if there is none + */ + public function getContainerExtension(); + + /** + * Returns the bundle name that this bundle overrides. + * + * Despite its name, this method does not imply any parent/child relationship + * between the bundles, just a way to extend and override an existing + * bundle. + * + * @return string The Bundle name it overrides or null if no parent + */ + public function getParent(); + + /** + * Returns the bundle name (the class short name). + * + * @return string The Bundle name + */ + public function getName(); + + /** + * Gets the Bundle namespace. + * + * @return string The Bundle namespace + */ + public function getNamespace(); + + /** + * Gets the Bundle directory path. + * + * The path should always be returned as a Unix path (with /). + * + * @return string The Bundle absolute path + */ + public function getPath(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CHANGELOG.md b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CHANGELOG.md new file mode 100644 index 0000000..5ca85c6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -0,0 +1,104 @@ +CHANGELOG +========= + +3.0.0 +----- + + * removed `Symfony\Component\HttpKernel\Kernel::init()` + * removed `Symfony\Component\HttpKernel\Kernel::isClassInActiveBundle()` and `Symfony\Component\HttpKernel\KernelInterface::isClassInActiveBundle()` + * removed `Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher::setProfiler()` + * removed `Symfony\Component\HttpKernel\EventListener\FragmentListener::getLocalIpAddresses()` + * removed `Symfony\Component\HttpKernel\EventListener\LocaleListener::setRequest()` + * removed `Symfony\Component\HttpKernel\EventListener\RouterListener::setRequest()` + * removed `Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelRequest()` + * removed `Symfony\Component\HttpKernel\Fragment\FragmentHandler::setRequest()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::hasSurrogateEsiCapability()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::addSurrogateEsiCapability()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::needsEsiParsing()` + * removed `Symfony\Component\HttpKernel\HttpCache\HttpCache::getEsi()` + * removed `Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel` + * removed `Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass` + * removed `Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener` + * removed `Symfony\Component\HttpKernel\EventListener\EsiListener` + * removed `Symfony\Component\HttpKernel\HttpCache\EsiResponseCacheStrategy` + * removed `Symfony\Component\HttpKernel\HttpCache\EsiResponseCacheStrategyInterface` + * removed `Symfony\Component\HttpKernel\Log\LoggerInterface` + * removed `Symfony\Component\HttpKernel\Log\NullLogger` + * removed `Symfony\Component\HttpKernel\Profiler::import()` + * removed `Symfony\Component\HttpKernel\Profiler::export()` + +2.8.0 +----- + + * deprecated `Profiler::import` and `Profiler::export` + +2.7.0 +----- + + * added the HTTP status code to profiles + +2.6.0 +----- + + * deprecated `Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener`, use `Symfony\Component\HttpKernel\EventListener\DebugHandlersListener` instead + * deprecated unused method `Symfony\Component\HttpKernel\Kernel::isClassInActiveBundle` and `Symfony\Component\HttpKernel\KernelInterface::isClassInActiveBundle` + +2.5.0 +----- + + * deprecated `Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass`, use `Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass` instead + +2.4.0 +----- + + * added event listeners for the session + * added the KernelEvents::FINISH_REQUEST event + +2.3.0 +----- + + * [BC BREAK] renamed `Symfony\Component\HttpKernel\EventListener\DeprecationLoggerListener` to `Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener` and changed its constructor + * deprecated `Symfony\Component\HttpKernel\Debug\ErrorHandler`, `Symfony\Component\HttpKernel\Debug\ExceptionHandler`, + `Symfony\Component\HttpKernel\Exception\FatalErrorException` and `Symfony\Component\HttpKernel\Exception\FlattenException` + * deprecated `Symfony\Component\HttpKernel\Kernel::init()` + * added the possibility to specify an id an extra attributes to hinclude tags + * added the collect of data if a controller is a Closure in the Request collector + * pass exceptions from the ExceptionListener to the logger using the logging context to allow for more + detailed messages + +2.2.0 +----- + + * [BC BREAK] the path info for sub-request is now always _fragment (or whatever you configured instead of the default) + * added Symfony\Component\HttpKernel\EventListener\FragmentListener + * added Symfony\Component\HttpKernel\UriSigner + * added Symfony\Component\HttpKernel\FragmentRenderer and rendering strategies (in Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface) + * added Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel + * added ControllerReference to create reference of Controllers (used in the FragmentRenderer class) + * [BC BREAK] renamed TimeDataCollector::getTotalTime() to + TimeDataCollector::getDuration() + * updated the MemoryDataCollector to include the memory used in the + kernel.terminate event listeners + * moved the Stopwatch classes to a new component + * added TraceableControllerResolver + * added TraceableEventDispatcher (removed ContainerAwareTraceableEventDispatcher) + * added support for WinCache opcode cache in ConfigDataCollector + +2.1.0 +----- + + * [BC BREAK] the charset is now configured via the Kernel::getCharset() method + * [BC BREAK] the current locale for the user is not stored anymore in the session + * added the HTTP method to the profiler storage + * updated all listeners to implement EventSubscriberInterface + * added TimeDataCollector + * added ContainerAwareTraceableEventDispatcher + * moved TraceableEventDispatcherInterface to the EventDispatcher component + * added RouterListener, LocaleListener, and StreamedResponseListener + * added CacheClearerInterface (and ChainCacheClearer) + * added a kernel.terminate event (via TerminableInterface and PostResponseEvent) + * added a Stopwatch class + * added WarmableInterface + * improved extensibility between bundles + * added profiler storages for Memcache(d), File-based, MongoDB, Redis + * moved Filesystem class to its own component diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php new file mode 100644 index 0000000..d4a2db3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheClearer/CacheClearerInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheClearer; + +/** + * CacheClearerInterface. + * + * @author Dustin Dobervich + */ +interface CacheClearerInterface +{ + /** + * Clears any caches necessary. + * + * @param string $cacheDir The cache directory. + */ + public function clear($cacheDir); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php new file mode 100644 index 0000000..81c43b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheClearer/ChainCacheClearer.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheClearer; + +/** + * ChainCacheClearer. + * + * @author Dustin Dobervich + */ +class ChainCacheClearer implements CacheClearerInterface +{ + /** + * @var array + */ + protected $clearers; + + /** + * Constructs a new instance of ChainCacheClearer. + * + * @param array $clearers The initial clearers. + */ + public function __construct(array $clearers = array()) + { + $this->clearers = $clearers; + } + + /** + * {@inheritdoc} + */ + public function clear($cacheDir) + { + foreach ($this->clearers as $clearer) { + $clearer->clear($cacheDir); + } + } + + /** + * Adds a cache clearer to the aggregate. + * + * @param CacheClearerInterface $clearer + */ + public function add(CacheClearerInterface $clearer) + { + $this->clearers[] = $clearer; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php new file mode 100644 index 0000000..948b3ff --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheWarmer; + +/** + * Abstract cache warmer that knows how to write a file to the cache. + * + * @author Fabien Potencier + */ +abstract class CacheWarmer implements CacheWarmerInterface +{ + protected function writeCacheFile($file, $content) + { + $tmpFile = tempnam(dirname($file), basename($file)); + if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { + @chmod($file, 0666 & ~umask()); + + return; + } + + throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php new file mode 100644 index 0000000..e5f4e4f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheWarmer; + +/** + * Aggregates several cache warmers into a single one. + * + * @author Fabien Potencier + */ +class CacheWarmerAggregate implements CacheWarmerInterface +{ + protected $warmers = array(); + protected $optionalsEnabled = false; + + public function __construct(array $warmers = array()) + { + foreach ($warmers as $warmer) { + $this->add($warmer); + } + } + + public function enableOptionalWarmers() + { + $this->optionalsEnabled = true; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + foreach ($this->warmers as $warmer) { + if (!$this->optionalsEnabled && $warmer->isOptional()) { + continue; + } + + $warmer->warmUp($cacheDir); + } + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always false + */ + public function isOptional() + { + return false; + } + + public function setWarmers(array $warmers) + { + $this->warmers = array(); + foreach ($warmers as $warmer) { + $this->add($warmer); + } + } + + public function add(CacheWarmerInterface $warmer) + { + $this->warmers[] = $warmer; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php new file mode 100644 index 0000000..8fece5e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheWarmer; + +/** + * Interface for classes able to warm up the cache. + * + * @author Fabien Potencier + */ +interface CacheWarmerInterface extends WarmableInterface +{ + /** + * Checks whether this warmer is optional or not. + * + * Optional warmers can be ignored on certain conditions. + * + * A warmer should return true if the cache can be + * generated incrementally and on-demand. + * + * @return bool true if the warmer is optional, false otherwise + */ + public function isOptional(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php new file mode 100644 index 0000000..25d8ee8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheWarmer; + +/** + * Interface for classes that support warming their cache. + * + * @author Fabien Potencier + */ +interface WarmableInterface +{ + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.php new file mode 100644 index 0000000..80b1bd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\BrowserKit\Client as BaseClient; +use Symfony\Component\BrowserKit\Request as DomRequest; +use Symfony\Component\BrowserKit\Response as DomResponse; +use Symfony\Component\BrowserKit\Cookie as DomCookie; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Client simulates a browser and makes requests to a Kernel object. + * + * @author Fabien Potencier + */ +class Client extends BaseClient +{ + protected $kernel; + + /** + * Constructor. + * + * @param HttpKernelInterface $kernel An HttpKernel instance + * @param array $server The server parameters (equivalent of $_SERVER) + * @param History $history A History instance to store the browser history + * @param CookieJar $cookieJar A CookieJar instance to store the cookies + */ + public function __construct(HttpKernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + // These class properties must be set before calling the parent constructor, as it may depend on it. + $this->kernel = $kernel; + $this->followRedirects = false; + + parent::__construct($server, $history, $cookieJar); + } + + /** + * {@inheritdoc} + * + * @return Request|null A Request instance + */ + public function getRequest() + { + return parent::getRequest(); + } + + /** + * {@inheritdoc} + * + * @return Response|null A Response instance + */ + public function getResponse() + { + return parent::getResponse(); + } + + /** + * Makes a request. + * + * @param Request $request A Request instance + * + * @return Response A Response instance + */ + protected function doRequest($request) + { + $response = $this->kernel->handle($request); + + if ($this->kernel instanceof TerminableInterface) { + $this->kernel->terminate($request, $response); + } + + return $response; + } + + /** + * Returns the script to execute when the request must be insulated. + * + * @param Request $request A Request instance + * + * @return string + */ + protected function getScript($request) + { + $kernel = str_replace("'", "\\'", serialize($this->kernel)); + $request = str_replace("'", "\\'", serialize($request)); + + $r = new \ReflectionClass('\\Symfony\\Component\\ClassLoader\\ClassLoader'); + $requirePath = str_replace("'", "\\'", $r->getFileName()); + $symfonyPath = str_replace("'", "\\'", dirname(dirname(dirname(__DIR__)))); + $errorReporting = error_reporting(); + + $code = <<addPrefix('Symfony', '$symfonyPath'); +\$loader->register(); + +\$kernel = unserialize('$kernel'); +\$request = unserialize('$request'); +EOF; + + return $code.$this->getHandleScript(); + } + + protected function getHandleScript() + { + return <<<'EOF' +$response = $kernel->handle($request); + +if ($kernel instanceof Symfony\Component\HttpKernel\TerminableInterface) { + $kernel->terminate($request, $response); +} + +echo serialize($response); +EOF; + } + + /** + * Converts the BrowserKit request to a HttpKernel request. + * + * @param DomRequest $request A DomRequest instance + * + * @return Request A Request instance + */ + protected function filterRequest(DomRequest $request) + { + $httpRequest = Request::create($request->getUri(), $request->getMethod(), $request->getParameters(), $request->getCookies(), $request->getFiles(), $request->getServer(), $request->getContent()); + + foreach ($this->filterFiles($httpRequest->files->all()) as $key => $value) { + $httpRequest->files->set($key, $value); + } + + return $httpRequest; + } + + /** + * Filters an array of files. + * + * This method created test instances of UploadedFile so that the move() + * method can be called on those instances. + * + * If the size of a file is greater than the allowed size (from php.ini) then + * an invalid UploadedFile is returned with an error set to UPLOAD_ERR_INI_SIZE. + * + * @see UploadedFile + * + * @param array $files An array of files + * + * @return array An array with all uploaded files marked as already moved + */ + protected function filterFiles(array $files) + { + $filtered = array(); + foreach ($files as $key => $value) { + if (is_array($value)) { + $filtered[$key] = $this->filterFiles($value); + } elseif ($value instanceof UploadedFile) { + if ($value->isValid() && $value->getSize() > UploadedFile::getMaxFilesize()) { + $filtered[$key] = new UploadedFile( + '', + $value->getClientOriginalName(), + $value->getClientMimeType(), + 0, + UPLOAD_ERR_INI_SIZE, + true + ); + } else { + $filtered[$key] = new UploadedFile( + $value->getPathname(), + $value->getClientOriginalName(), + $value->getClientMimeType(), + $value->getClientSize(), + $value->getError(), + true + ); + } + } + } + + return $filtered; + } + + /** + * Converts the HttpKernel response to a BrowserKit response. + * + * @param Response $response A Response instance + * + * @return DomResponse A DomResponse instance + */ + protected function filterResponse($response) + { + $headers = $response->headers->all(); + if ($response->headers->getCookies()) { + $cookies = array(); + foreach ($response->headers->getCookies() as $cookie) { + $cookies[] = new DomCookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + $headers['Set-Cookie'] = $cookies; + } + + // this is needed to support StreamedResponse + ob_start(); + $response->sendContent(); + $content = ob_get_clean(); + + return new DomResponse($content, $response->getStatusCode(), $headers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/EnvParametersResource.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/EnvParametersResource.php new file mode 100644 index 0000000..bad199b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/EnvParametersResource.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; + +/** + * EnvParametersResource represents resources stored in prefixed environment variables. + * + * @author Chris Wilkinson + */ +class EnvParametersResource implements SelfCheckingResourceInterface, \Serializable +{ + /** + * @var string + */ + private $prefix; + + /** + * @var string + */ + private $variables; + + /** + * Constructor. + * + * @param string $prefix + */ + public function __construct($prefix) + { + $this->prefix = $prefix; + $this->variables = $this->findVariables(); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return serialize($this->getResource()); + } + + /** + * @return array An array with two keys: 'prefix' for the prefix used and 'variables' containing all the variables watched by this resource + */ + public function getResource() + { + return array('prefix' => $this->prefix, 'variables' => $this->variables); + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return $this->findVariables() === $this->variables; + } + + public function serialize() + { + return serialize(array('prefix' => $this->prefix, 'variables' => $this->variables)); + } + + public function unserialize($serialized) + { + $unserialized = unserialize($serialized); + + $this->prefix = $unserialized['prefix']; + $this->variables = $unserialized['variables']; + } + + private function findVariables() + { + $variables = array(); + + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, $this->prefix)) { + $variables[$key] = $value; + } + } + + ksort($variables); + + return $variables; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/FileLocator.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/FileLocator.php new file mode 100644 index 0000000..169c9ad --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Config/FileLocator.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Config; + +use Symfony\Component\Config\FileLocator as BaseFileLocator; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * FileLocator uses the KernelInterface to locate resources in bundles. + * + * @author Fabien Potencier + */ +class FileLocator extends BaseFileLocator +{ + private $kernel; + private $path; + + /** + * Constructor. + * + * @param KernelInterface $kernel A KernelInterface instance + * @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; + if (null !== $path) { + $this->path = $path; + $paths[] = $path; + } + + parent::__construct($paths); + } + + /** + * {@inheritdoc} + */ + public function locate($file, $currentPath = null, $first = true) + { + if (isset($file[0]) && '@' === $file[0]) { + return $this->kernel->locateResource($file, $this->path, $first); + } + + return parent::locate($file, $currentPath, $first); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php new file mode 100644 index 0000000..3d1592e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerReference.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; + +/** + * Acts as a marker and a data holder for a Controller. + * + * Some methods in Symfony accept both a URI (as a string) or a controller as + * an argument. In the latter case, instead of passing an array representing + * the controller, you can use an instance of this class. + * + * @author Fabien Potencier + * + * @see FragmentRendererInterface + */ +class ControllerReference +{ + public $controller; + public $attributes = array(); + public $query = array(); + + /** + * Constructor. + * + * @param string $controller The controller name + * @param array $attributes An array of parameters to add to the Request attributes + * @param array $query An array of parameters to add to the Request query string + */ + public function __construct($controller, array $attributes = array(), array $query = array()) + { + $this->controller = $controller; + $this->attributes = $attributes; + $this->query = $query; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php new file mode 100644 index 0000000..a4a5fdb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * ControllerResolver. + * + * This implementation uses the '_controller' request attribute to determine + * the controller to execute and uses the request attributes to determine + * the controller method arguments. + * + * @author Fabien Potencier + */ +class ControllerResolver implements ControllerResolverInterface +{ + private $logger; + + /** + * Constructor. + * + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * {@inheritdoc} + * + * This method looks for a '_controller' request attribute that represents + * the controller name (a string like ClassName::MethodName). + */ + public function getController(Request $request) + { + if (!$controller = $request->attributes->get('_controller')) { + if (null !== $this->logger) { + $this->logger->warning('Unable to look for the controller as the "_controller" parameter is missing.'); + } + + return false; + } + + if (is_array($controller)) { + return $controller; + } + + if (is_object($controller)) { + if (method_exists($controller, '__invoke')) { + return $controller; + } + + throw new \InvalidArgumentException(sprintf('Controller "%s" for URI "%s" is not callable.', get_class($controller), $request->getPathInfo())); + } + + if (false === strpos($controller, ':')) { + if (method_exists($controller, '__invoke')) { + return $this->instantiateController($controller); + } elseif (function_exists($controller)) { + return $controller; + } + } + + $callable = $this->createController($controller); + + if (!is_callable($callable)) { + throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s', $request->getPathInfo(), $this->getControllerError($callable))); + } + + return $callable; + } + + /** + * {@inheritdoc} + */ + public function getArguments(Request $request, $controller) + { + if (is_array($controller)) { + $r = new \ReflectionMethod($controller[0], $controller[1]); + } elseif (is_object($controller) && !$controller instanceof \Closure) { + $r = new \ReflectionObject($controller); + $r = $r->getMethod('__invoke'); + } else { + $r = new \ReflectionFunction($controller); + } + + return $this->doGetArguments($request, $controller, $r->getParameters()); + } + + protected function doGetArguments(Request $request, $controller, array $parameters) + { + $attributes = $request->attributes->all(); + $arguments = array(); + foreach ($parameters as $param) { + if (array_key_exists($param->name, $attributes)) { + $arguments[] = $attributes[$param->name]; + } elseif ($param->getClass() && $param->getClass()->isInstance($request)) { + $arguments[] = $request; + } elseif ($param->isDefaultValueAvailable()) { + $arguments[] = $param->getDefaultValue(); + } else { + if (is_array($controller)) { + $repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]); + } elseif (is_object($controller)) { + $repr = get_class($controller); + } else { + $repr = $controller; + } + + throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name)); + } + } + + return $arguments; + } + + /** + * Returns a callable for the given controller. + * + * @param string $controller A Controller string + * + * @return callable A PHP callable + * + * @throws \InvalidArgumentException + */ + protected function createController($controller) + { + if (false === strpos($controller, '::')) { + throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".', $controller)); + } + + list($class, $method) = explode('::', $controller, 2); + + if (!class_exists($class)) { + throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + return array($this->instantiateController($class), $method); + } + + /** + * Returns an instantiated controller. + * + * @param string $class A class name + * + * @return object + */ + protected function instantiateController($class) + { + return new $class(); + } + + private function getControllerError($callable) + { + if (is_string($callable)) { + if (false !== strpos($callable, '::')) { + $callable = explode('::', $callable); + } + + if (class_exists($callable) && !method_exists($callable, '__invoke')) { + return sprintf('Class "%s" does not have a method "__invoke".', $callable); + } + + if (!function_exists($callable)) { + return sprintf('Function "%s" does not exist.', $callable); + } + } + + if (!is_array($callable)) { + return sprintf('Invalid type for controller given, expected string or array, got "%s".', gettype($callable)); + } + + if (2 !== count($callable)) { + return sprintf('Invalid format for controller, expected array(controller, method) or controller::method.'); + } + + list($controller, $method) = $callable; + + if (is_string($controller) && !class_exists($controller)) { + return sprintf('Class "%s" does not exist.', $controller); + } + + $className = is_object($controller) ? get_class($controller) : $controller; + + if (method_exists($controller, $method)) { + return sprintf('Method "%s" on class "%s" should be public and non-abstract.', $method, $className); + } + + $collection = get_class_methods($controller); + + $alternatives = array(); + + foreach ($collection as $item) { + $lev = levenshtein($method, $item); + + if ($lev <= strlen($method) / 3 || false !== strpos($item, $method)) { + $alternatives[] = $item; + } + } + + asort($alternatives); + + $message = sprintf('Expected method "%s" on class "%s"', $method, $className); + + if (count($alternatives) > 0) { + $message .= sprintf(', did you mean "%s"?', implode('", "', $alternatives)); + } else { + $message .= sprintf('. Available methods: "%s".', implode('", "', $collection)); + } + + return $message; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php new file mode 100644 index 0000000..f7b19ed --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\HttpFoundation\Request; + +/** + * A ControllerResolverInterface implementation knows how to determine the + * controller to execute based on a Request object. + * + * It can also determine the arguments to pass to the Controller. + * + * A Controller can be any valid PHP callable. + * + * @author Fabien Potencier + */ +interface ControllerResolverInterface +{ + /** + * Returns the Controller instance associated with a Request. + * + * As several resolvers can exist for a single application, a resolver must + * return false when it is not able to determine the controller. + * + * The resolver must only throw an exception when it should be able to load + * controller but cannot because of some errors made by the developer. + * + * @param Request $request A Request instance + * + * @return callable|false A PHP callable representing the Controller, + * or false if this resolver is not able to determine the controller + * + * @throws \LogicException If the controller can't be found + */ + public function getController(Request $request); + + /** + * Returns the arguments to pass to the controller. + * + * @param Request $request A Request instance + * @param callable $controller A PHP callable + * + * @return array An array of arguments to pass to the controller + * + * @throws \RuntimeException When value for argument given is not provided + */ + public function getArguments(Request $request, $controller); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php new file mode 100644 index 0000000..f8de31c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/TraceableControllerResolver.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\HttpFoundation\Request; + +/** + * TraceableControllerResolver. + * + * @author Fabien Potencier + */ +class TraceableControllerResolver implements ControllerResolverInterface +{ + private $resolver; + private $stopwatch; + + /** + * Constructor. + * + * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance + * @param Stopwatch $stopwatch A Stopwatch instance + */ + public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch) + { + $this->resolver = $resolver; + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function getController(Request $request) + { + $e = $this->stopwatch->start('controller.get_callable'); + + $ret = $this->resolver->getController($request); + + $e->stop(); + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function getArguments(Request $request, $controller) + { + $e = $this->stopwatch->start('controller.get_arguments'); + + $ret = $this->resolver->getArguments($request, $controller); + + $e->stop(); + + return $ret; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php new file mode 100644 index 0000000..b8405d5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/AjaxDataCollector.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * AjaxDataCollector. + * + * @author Bart van den Burg + */ +class AjaxDataCollector extends DataCollector +{ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // all collecting is done client side + } + + public function getName() + { + return 'ajax'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php new file mode 100644 index 0000000..853adcb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -0,0 +1,291 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * ConfigDataCollector. + * + * @author Fabien Potencier + */ +class ConfigDataCollector extends DataCollector +{ + /** + * @var KernelInterface + */ + private $kernel; + private $name; + private $version; + private $cacheVersionInfo = true; + + /** + * Constructor. + * + * @param string $name The name of the application using the web profiler + * @param string $version The version of the application using the web profiler + */ + public function __construct($name = null, $version = null) + { + $this->name = $name; + $this->version = $version; + } + + /** + * Sets the Kernel associated with this Request. + * + * @param KernelInterface $kernel A KernelInterface instance + */ + public function setKernel(KernelInterface $kernel = null) + { + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'app_name' => $this->name, + 'app_version' => $this->version, + 'token' => $response->headers->get('X-Debug-Token'), + 'symfony_version' => Kernel::VERSION, + 'symfony_state' => 'unknown', + 'name' => isset($this->kernel) ? $this->kernel->getName() : 'n/a', + 'env' => isset($this->kernel) ? $this->kernel->getEnvironment() : 'n/a', + 'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a', + 'php_version' => PHP_VERSION, + 'xdebug_enabled' => extension_loaded('xdebug'), + 'eaccel_enabled' => extension_loaded('eaccelerator') && ini_get('eaccelerator.enable'), + 'apc_enabled' => extension_loaded('apc') && ini_get('apc.enabled'), + 'xcache_enabled' => extension_loaded('xcache') && ini_get('xcache.cacher'), + 'wincache_enabled' => extension_loaded('wincache') && ini_get('wincache.ocenabled'), + 'zend_opcache_enabled' => extension_loaded('Zend OPcache') && ini_get('opcache.enable'), + 'bundles' => array(), + 'sapi_name' => php_sapi_name(), + ); + + if (isset($this->kernel)) { + foreach ($this->kernel->getBundles() as $name => $bundle) { + $this->data['bundles'][$name] = $bundle->getPath(); + } + + $this->data['symfony_state'] = $this->determineSymfonyState(); + } + } + + public function getApplicationName() + { + return $this->data['app_name']; + } + + public function getApplicationVersion() + { + return $this->data['app_version']; + } + + /** + * Gets the token. + * + * @return string The token + */ + public function getToken() + { + return $this->data['token']; + } + + /** + * Gets the Symfony version. + * + * @return string The Symfony version + */ + public function getSymfonyVersion() + { + return $this->data['symfony_version']; + } + + /** + * Returns the state of the current Symfony release. + * + * @return string One of: unknown, dev, stable, eom, eol + */ + public function getSymfonyState() + { + return $this->data['symfony_state']; + } + + public function setCacheVersionInfo($cacheVersionInfo) + { + $this->cacheVersionInfo = $cacheVersionInfo; + } + + /** + * Gets the PHP version. + * + * @return string The PHP version + */ + public function getPhpVersion() + { + return $this->data['php_version']; + } + + /** + * Gets the application name. + * + * @return string The application name + */ + public function getAppName() + { + return $this->data['name']; + } + + /** + * Gets the environment. + * + * @return string The environment + */ + public function getEnv() + { + return $this->data['env']; + } + + /** + * Returns true if the debug is enabled. + * + * @return bool true if debug is enabled, false otherwise + */ + public function isDebug() + { + return $this->data['debug']; + } + + /** + * Returns true if the XDebug is enabled. + * + * @return bool true if XDebug is enabled, false otherwise + */ + public function hasXDebug() + { + return $this->data['xdebug_enabled']; + } + + /** + * Returns true if EAccelerator is enabled. + * + * @return bool true if EAccelerator is enabled, false otherwise + */ + public function hasEAccelerator() + { + return $this->data['eaccel_enabled']; + } + + /** + * Returns true if APC is enabled. + * + * @return bool true if APC is enabled, false otherwise + */ + public function hasApc() + { + return $this->data['apc_enabled']; + } + + /** + * Returns true if Zend OPcache is enabled. + * + * @return bool true if Zend OPcache is enabled, false otherwise + */ + public function hasZendOpcache() + { + return $this->data['zend_opcache_enabled']; + } + + /** + * Returns true if XCache is enabled. + * + * @return bool true if XCache is enabled, false otherwise + */ + public function hasXCache() + { + return $this->data['xcache_enabled']; + } + + /** + * Returns true if WinCache is enabled. + * + * @return bool true if WinCache is enabled, false otherwise + */ + public function hasWinCache() + { + return $this->data['wincache_enabled']; + } + + /** + * Returns true if any accelerator is enabled. + * + * @return bool true if any accelerator is enabled, false otherwise + */ + public function hasAccelerator() + { + return $this->hasApc() || $this->hasZendOpcache() || $this->hasEAccelerator() || $this->hasXCache() || $this->hasWinCache(); + } + + public function getBundles() + { + return $this->data['bundles']; + } + + /** + * Gets the PHP SAPI name. + * + * @return string The environment + */ + public function getSapiName() + { + return $this->data['sapi_name']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'config'; + } + + /** + * Tries to retrieve information about the current Symfony version. + * + * @return string One of: dev, stable, eom, eol + */ + private function determineSymfonyState() + { + $now = new \DateTime(); + $eom = \DateTime::createFromFormat('m/Y', Kernel::END_OF_MAINTENANCE)->modify('last day of this month'); + $eol = \DateTime::createFromFormat('m/Y', Kernel::END_OF_LIFE)->modify('last day of this month'); + + if ($now > $eol) { + $versionState = 'eol'; + } elseif ($now > $eom) { + $versionState = 'eom'; + } elseif ('' !== Kernel::EXTRA_VERSION) { + $versionState = 'dev'; + } else { + $versionState = 'stable'; + } + + return $versionState; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php new file mode 100644 index 0000000..5dca652 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; + +/** + * DataCollector. + * + * Children of this class must store the collected data in the data property. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +abstract class DataCollector implements DataCollectorInterface, \Serializable +{ + protected $data = array(); + + /** + * @var ValueExporter + */ + private $valueExporter; + + public function serialize() + { + return serialize($this->data); + } + + public function unserialize($data) + { + $this->data = unserialize($data); + } + + /** + * Converts a PHP variable to a string. + * + * @param mixed $var A PHP variable + * + * @return string The string representation of the variable + */ + protected function varToString($var) + { + if (null === $this->valueExporter) { + $this->valueExporter = new ValueExporter(); + } + + return $this->valueExporter->exportValue($var); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php new file mode 100644 index 0000000..2820ad5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * DataCollectorInterface. + * + * @author Fabien Potencier + */ +interface DataCollectorInterface +{ + /** + * Collects data for the given Request and Response. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * @param \Exception $exception An Exception instance + */ + public function collect(Request $request, Response $response, \Exception $exception = null); + + /** + * Returns the name of the collector. + * + * @return string The collector name + */ + public function getName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php new file mode 100644 index 0000000..ab52678 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -0,0 +1,295 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; + +/** + * @author Nicolas Grekas + */ +class DumpDataCollector extends DataCollector implements DataDumperInterface +{ + private $stopwatch; + private $fileLinkFormat; + private $dataCount = 0; + private $isCollected = true; + private $clonesCount = 0; + private $clonesIndex = 0; + private $rootRefs; + private $charset; + private $requestStack; + private $dumper; + private $dumperIsInjected; + + public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null) + { + $this->stopwatch = $stopwatch; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'; + $this->requestStack = $requestStack; + $this->dumper = $dumper; + $this->dumperIsInjected = null !== $dumper; + + // All clones share these properties by reference: + $this->rootRefs = array( + &$this->data, + &$this->dataCount, + &$this->isCollected, + &$this->clonesCount, + ); + } + + public function __clone() + { + $this->clonesIndex = ++$this->clonesCount; + } + + public function dump(Data $data) + { + if ($this->stopwatch) { + $this->stopwatch->start('dump'); + } + if ($this->isCollected) { + $this->isCollected = false; + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 7); + + $file = $trace[0]['file']; + $line = $trace[0]['line']; + $name = false; + $fileExcerpt = false; + + for ($i = 1; $i < 7; ++$i) { + if (isset($trace[$i]['class'], $trace[$i]['function']) + && 'dump' === $trace[$i]['function'] + && 'Symfony\Component\VarDumper\VarDumper' === $trace[$i]['class'] + ) { + $file = $trace[$i]['file']; + $line = $trace[$i]['line']; + + while (++$i < 7) { + if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) { + $file = $trace[$i]['file']; + $line = $trace[$i]['line']; + + break; + } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof \Twig_Template) { + $info = $trace[$i]['object']; + $name = $info->getTemplateName(); + $src = method_exists($info, 'getSource') ? $info->getSource() : $info->getEnvironment()->getLoader()->getSource($name); + $info = $info->getDebugInfo(); + if (null !== $src && isset($info[$trace[$i - 1]['line']])) { + $file = false; + $line = $info[$trace[$i - 1]['line']]; + $src = explode("\n", $src); + $fileExcerpt = array(); + + for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) { + $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; + } + + $fileExcerpt = '
      '.implode("\n", $fileExcerpt).'
    '; + } + break; + } + } + break; + } + } + + if (false === $name) { + $name = str_replace('\\', '/', $file); + $name = substr($name, strrpos($name, '/') + 1); + } + + if ($this->dumper) { + $this->doDump($data, $name, $file, $line); + } + + $this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt'); + ++$this->dataCount; + + if ($this->stopwatch) { + $this->stopwatch->stop('dump'); + } + } + + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // Sub-requests and programmatic calls stay in the collected profile. + if ($this->dumper || ($this->requestStack && $this->requestStack->getMasterRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) { + return; + } + + // In all other conditions that remove the web debug toolbar, dumps are written on the output. + if (!$this->requestStack + || !$response->headers->has('X-Debug-Token') + || $response->isRedirection() + || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html')) + || 'html' !== $request->getRequestFormat() + || false === strripos($response->getContent(), '') + ) { + if ($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) { + $this->dumper = new HtmlDumper('php://output', $this->charset); + } else { + $this->dumper = new CliDumper('php://output', $this->charset); + } + + foreach ($this->data as $dump) { + $this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']); + } + } + } + + public function serialize() + { + if ($this->clonesCount !== $this->clonesIndex) { + return 'a:0:{}'; + } + + $ser = serialize($this->data); + $this->data = array(); + $this->dataCount = 0; + $this->isCollected = true; + if (!$this->dumperIsInjected) { + $this->dumper = null; + } + + return $ser; + } + + public function unserialize($data) + { + parent::unserialize($data); + $this->dataCount = count($this->data); + self::__construct($this->stopwatch); + } + + public function getDumpsCount() + { + return $this->dataCount; + } + + public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1) + { + $data = fopen('php://memory', 'r+b'); + + if ('html' === $format) { + $dumper = new HtmlDumper($data, $this->charset); + } else { + throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format)); + } + $dumps = array(); + + foreach ($this->data as $dump) { + $dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth)); + + rewind($data); + $dump['data'] = stream_get_contents($data); + ftruncate($data, 0); + rewind($data); + $dumps[] = $dump; + } + + return $dumps; + } + + public function getName() + { + return 'dump'; + } + + public function __destruct() + { + if (0 === $this->clonesCount-- && !$this->isCollected && $this->data) { + $this->clonesCount = 0; + $this->isCollected = true; + + $h = headers_list(); + $i = count($h); + array_unshift($h, 'Content-Type: '.ini_get('default_mimetype')); + while (0 !== stripos($h[$i], 'Content-Type:')) { + --$i; + } + + if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) { + $this->dumper = new HtmlDumper('php://output', $this->charset); + } else { + $this->dumper = new CliDumper('php://output', $this->charset); + } + + foreach ($this->data as $i => $dump) { + $this->data[$i] = null; + $this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']); + } + + $this->data = array(); + $this->dataCount = 0; + } + } + + private function doDump($data, $name, $file, $line) + { + if ($this->dumper instanceof CliDumper) { + $contextDumper = function ($name, $file, $line, $fileLinkFormat) { + if ($this instanceof HtmlDumper) { + if ('' !== $file) { + $s = $this->style('meta', '%s'); + $name = strip_tags($this->style('', $name)); + $file = strip_tags($this->style('', $file)); + if ($fileLinkFormat) { + $link = strtr(strip_tags($this->style('', $fileLinkFormat)), array('%f' => $file, '%l' => (int) $line)); + $name = sprintf(''.$s.'', $link, $file, $name); + } else { + $name = sprintf(''.$s.'', $file, $name); + } + } else { + $name = $this->style('meta', $name); + } + $this->line = $name.' on line '.$this->style('meta', $line).':'; + } else { + $this->line = $this->style('meta', $name).' on line '.$this->style('meta', $line).':'; + } + $this->dumpLine(0); + }; + $contextDumper = $contextDumper->bindTo($this->dumper, $this->dumper); + $contextDumper($name, $file, $line, $this->fileLinkFormat); + } else { + $cloner = new VarCloner(); + $this->dumper->dump($cloner->cloneVar($name.' on line '.$line.':')); + } + $this->dumper->dump($data); + } + + private function htmlEncode($s) + { + $html = ''; + + $dumper = new HtmlDumper(function ($line) use (&$html) {$html .= $line;}, $this->charset); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); + + $cloner = new VarCloner(); + $dumper->dump($cloner->cloneVar($s)); + + return substr(strip_tags($html), 1, -1); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php new file mode 100644 index 0000000..0a87bc3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/EventDataCollector.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; + +/** + * EventDataCollector. + * + * @author Fabien Potencier + */ +class EventDataCollector extends DataCollector implements LateDataCollectorInterface +{ + protected $dispatcher; + + public function __construct(EventDispatcherInterface $dispatcher = null) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'called_listeners' => array(), + 'not_called_listeners' => array(), + ); + } + + public function lateCollect() + { + if ($this->dispatcher instanceof TraceableEventDispatcherInterface) { + $this->setCalledListeners($this->dispatcher->getCalledListeners()); + $this->setNotCalledListeners($this->dispatcher->getNotCalledListeners()); + } + } + + /** + * Sets the called listeners. + * + * @param array $listeners An array of called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function setCalledListeners(array $listeners) + { + $this->data['called_listeners'] = $listeners; + } + + /** + * Gets the called listeners. + * + * @return array An array of called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function getCalledListeners() + { + return $this->data['called_listeners']; + } + + /** + * Sets the not called listeners. + * + * @param array $listeners An array of not called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function setNotCalledListeners(array $listeners) + { + $this->data['not_called_listeners'] = $listeners; + } + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function getNotCalledListeners() + { + return $this->data['not_called_listeners']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'events'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php new file mode 100644 index 0000000..9fe8264 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/ExceptionDataCollector.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * ExceptionDataCollector. + * + * @author Fabien Potencier + */ +class ExceptionDataCollector extends DataCollector +{ + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (null !== $exception) { + $this->data = array( + 'exception' => FlattenException::create($exception), + ); + } + } + + /** + * Checks if the exception is not null. + * + * @return bool true if the exception is not null, false otherwise + */ + public function hasException() + { + return isset($this->data['exception']); + } + + /** + * Gets the exception. + * + * @return \Exception The exception + */ + public function getException() + { + return $this->data['exception']; + } + + /** + * Gets the exception message. + * + * @return string The exception message + */ + public function getMessage() + { + return $this->data['exception']->getMessage(); + } + + /** + * Gets the exception code. + * + * @return int The exception code + */ + public function getCode() + { + return $this->data['exception']->getCode(); + } + + /** + * Gets the status code. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->data['exception']->getStatusCode(); + } + + /** + * Gets the exception trace. + * + * @return array The exception trace + */ + public function getTrace() + { + return $this->data['exception']->getTrace(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'exception'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LateDataCollectorInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LateDataCollectorInterface.php new file mode 100644 index 0000000..012332d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LateDataCollectorInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +/** + * LateDataCollectorInterface. + * + * @author Fabien Potencier + */ +interface LateDataCollectorInterface +{ + /** + * Collects data as late as possible. + */ + public function lateCollect(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php new file mode 100644 index 0000000..11a4cc8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -0,0 +1,216 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * LogDataCollector. + * + * @author Fabien Potencier + */ +class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface +{ + private $errorNames = array( + E_DEPRECATED => 'E_DEPRECATED', + E_USER_DEPRECATED => 'E_USER_DEPRECATED', + E_NOTICE => 'E_NOTICE', + E_USER_NOTICE => 'E_USER_NOTICE', + E_STRICT => 'E_STRICT', + E_WARNING => 'E_WARNING', + E_USER_WARNING => 'E_USER_WARNING', + E_COMPILE_WARNING => 'E_COMPILE_WARNING', + E_CORE_WARNING => 'E_CORE_WARNING', + E_USER_ERROR => 'E_USER_ERROR', + E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', + E_COMPILE_ERROR => 'E_COMPILE_ERROR', + E_PARSE => 'E_PARSE', + E_ERROR => 'E_ERROR', + E_CORE_ERROR => 'E_CORE_ERROR', + ); + + private $logger; + + public function __construct($logger = null) + { + if (null !== $logger && $logger instanceof DebugLoggerInterface) { + $this->logger = $logger; + } + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // everything is done as late as possible + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + if (null !== $this->logger) { + $this->data = $this->computeErrorsCount(); + $this->data['logs'] = $this->sanitizeLogs($this->logger->getLogs()); + } + } + + /** + * Gets the called events. + * + * @return array An array of called events + * + * @see TraceableEventDispatcherInterface + */ + public function countErrors() + { + return isset($this->data['error_count']) ? $this->data['error_count'] : 0; + } + + /** + * Gets the logs. + * + * @return array An array of logs + */ + public function getLogs() + { + return isset($this->data['logs']) ? $this->data['logs'] : array(); + } + + public function getPriorities() + { + return isset($this->data['priorities']) ? $this->data['priorities'] : array(); + } + + public function countDeprecations() + { + return isset($this->data['deprecation_count']) ? $this->data['deprecation_count'] : 0; + } + + public function countScreams() + { + return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'logger'; + } + + private function sanitizeLogs($logs) + { + $errorContextById = array(); + $sanitizedLogs = array(); + + foreach ($logs as $log) { + $context = $this->sanitizeContext($log['context']); + + if (isset($context['type'], $context['file'], $context['line'], $context['level'])) { + $errorId = md5("{$context['type']}/{$context['line']}/{$context['file']}\x00{$log['message']}", true); + $silenced = !($context['type'] & $context['level']); + if (isset($this->errorNames[$context['type']])) { + $context = array_merge(array('name' => $this->errorNames[$context['type']]), $context); + } + + if (isset($errorContextById[$errorId])) { + if (isset($errorContextById[$errorId]['errorCount'])) { + ++$errorContextById[$errorId]['errorCount']; + } else { + $errorContextById[$errorId]['errorCount'] = 2; + } + + if (!$silenced && isset($errorContextById[$errorId]['scream'])) { + unset($errorContextById[$errorId]['scream']); + $errorContextById[$errorId]['level'] = $context['level']; + } + + continue; + } + + $errorContextById[$errorId] = &$context; + if ($silenced) { + $context['scream'] = true; + } + + $log['context'] = &$context; + unset($context); + } else { + $log['context'] = $context; + } + + $sanitizedLogs[] = $log; + } + + return $sanitizedLogs; + } + + private function sanitizeContext($context) + { + if (is_array($context)) { + foreach ($context as $key => $value) { + $context[$key] = $this->sanitizeContext($value); + } + + return $context; + } + + if (is_resource($context)) { + return sprintf('Resource(%s)', get_resource_type($context)); + } + + if (is_object($context)) { + return sprintf('Object(%s)', get_class($context)); + } + + return $context; + } + + private function computeErrorsCount() + { + $count = array( + 'error_count' => $this->logger->countErrors(), + 'deprecation_count' => 0, + 'scream_count' => 0, + 'priorities' => array(), + ); + + foreach ($this->logger->getLogs() as $log) { + if (isset($count['priorities'][$log['priority']])) { + ++$count['priorities'][$log['priority']]['count']; + } else { + $count['priorities'][$log['priority']] = array( + 'count' => 1, + 'name' => $log['priorityName'], + ); + } + + if (isset($log['context']['type'], $log['context']['level'])) { + if (E_DEPRECATED === $log['context']['type'] || E_USER_DEPRECATED === $log['context']['type']) { + ++$count['deprecation_count']; + } elseif (!($log['context']['type'] & $log['context']['level'])) { + ++$count['scream_count']; + } + } + } + + ksort($count['priorities']); + + return $count; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php new file mode 100644 index 0000000..9385010 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/MemoryDataCollector.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * MemoryDataCollector. + * + * @author Fabien Potencier + */ +class MemoryDataCollector extends DataCollector implements LateDataCollectorInterface +{ + public function __construct() + { + $this->data = array( + 'memory' => 0, + 'memory_limit' => $this->convertToBytes(ini_get('memory_limit')), + ); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->updateMemoryUsage(); + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + $this->updateMemoryUsage(); + } + + /** + * Gets the memory. + * + * @return int The memory + */ + public function getMemory() + { + return $this->data['memory']; + } + + /** + * Gets the PHP memory limit. + * + * @return int The memory limit + */ + public function getMemoryLimit() + { + return $this->data['memory_limit']; + } + + /** + * Updates the memory usage data. + */ + public function updateMemoryUsage() + { + $this->data['memory'] = memory_get_peak_usage(true); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'memory'; + } + + private function convertToBytes($memoryLimit) + { + if ('-1' === $memoryLimit) { + return -1; + } + + $memoryLimit = strtolower($memoryLimit); + $max = strtolower(ltrim($memoryLimit, '+')); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($memoryLimit, -1)) { + case 't': $max *= 1024; + case 'g': $max *= 1024; + case 'm': $max *= 1024; + case 'k': $max *= 1024; + } + + return $max; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php new file mode 100644 index 0000000..9a499a7 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -0,0 +1,342 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\HeaderBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * RequestDataCollector. + * + * @author Fabien Potencier + */ +class RequestDataCollector extends DataCollector implements EventSubscriberInterface +{ + protected $controllers; + + public function __construct() + { + $this->controllers = new \SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $responseHeaders = $response->headers->all(); + $cookies = array(); + foreach ($response->headers->getCookies() as $cookie) { + $cookies[] = $this->getCookieHeader($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + if (count($cookies) > 0) { + $responseHeaders['Set-Cookie'] = $cookies; + } + + // attributes are serialized and as they can be anything, they need to be converted to strings. + $attributes = array(); + foreach ($request->attributes->all() as $key => $value) { + if ('_route' === $key && is_object($value)) { + $attributes[$key] = $this->varToString($value->getPath()); + } elseif ('_route_params' === $key) { + // we need to keep route params as an array (see getRouteParams()) + foreach ($value as $k => $v) { + $value[$k] = $this->varToString($v); + } + $attributes[$key] = $value; + } else { + $attributes[$key] = $this->varToString($value); + } + } + + $content = null; + try { + $content = $request->getContent(); + } catch (\LogicException $e) { + // the user already got the request content as a resource + $content = false; + } + + $sessionMetadata = array(); + $sessionAttributes = array(); + $flashes = array(); + if ($request->hasSession()) { + $session = $request->getSession(); + if ($session->isStarted()) { + $sessionMetadata['Created'] = date(DATE_RFC822, $session->getMetadataBag()->getCreated()); + $sessionMetadata['Last used'] = date(DATE_RFC822, $session->getMetadataBag()->getLastUsed()); + $sessionMetadata['Lifetime'] = $session->getMetadataBag()->getLifetime(); + $sessionAttributes = $session->all(); + $flashes = $session->getFlashBag()->peekAll(); + } + } + + $statusCode = $response->getStatusCode(); + + $this->data = array( + 'format' => $request->getRequestFormat(), + 'content' => $content, + 'content_type' => $response->headers->get('Content-Type', 'text/html'), + 'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '', + 'status_code' => $statusCode, + 'request_query' => $request->query->all(), + 'request_request' => $request->request->all(), + 'request_headers' => $request->headers->all(), + 'request_server' => $request->server->all(), + 'request_cookies' => $request->cookies->all(), + 'request_attributes' => $attributes, + 'response_headers' => $responseHeaders, + 'session_metadata' => $sessionMetadata, + 'session_attributes' => $sessionAttributes, + 'flashes' => $flashes, + 'path_info' => $request->getPathInfo(), + 'controller' => 'n/a', + 'locale' => $request->getLocale(), + ); + + if (isset($this->data['request_headers']['php-auth-pw'])) { + $this->data['request_headers']['php-auth-pw'] = '******'; + } + + if (isset($this->data['request_server']['PHP_AUTH_PW'])) { + $this->data['request_server']['PHP_AUTH_PW'] = '******'; + } + + if (isset($this->data['request_request']['_password'])) { + $this->data['request_request']['_password'] = '******'; + } + + if (isset($this->controllers[$request])) { + $controller = $this->controllers[$request]; + if (is_array($controller)) { + try { + $r = new \ReflectionMethod($controller[0], $controller[1]); + $this->data['controller'] = array( + 'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0], + 'method' => $controller[1], + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ); + } catch (\ReflectionException $e) { + if (is_callable($controller)) { + // using __call or __callStatic + $this->data['controller'] = array( + 'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0], + 'method' => $controller[1], + 'file' => 'n/a', + 'line' => 'n/a', + ); + } + } + } elseif ($controller instanceof \Closure) { + $r = new \ReflectionFunction($controller); + $this->data['controller'] = array( + 'class' => $r->getName(), + 'method' => null, + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ); + } elseif (is_object($controller)) { + $r = new \ReflectionClass($controller); + $this->data['controller'] = array( + 'class' => $r->getName(), + 'method' => null, + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ); + } else { + $this->data['controller'] = (string) $controller ?: 'n/a'; + } + unset($this->controllers[$request]); + } + } + + public function getPathInfo() + { + return $this->data['path_info']; + } + + public function getRequestRequest() + { + return new ParameterBag($this->data['request_request']); + } + + public function getRequestQuery() + { + return new ParameterBag($this->data['request_query']); + } + + public function getRequestHeaders() + { + return new HeaderBag($this->data['request_headers']); + } + + public function getRequestServer() + { + return new ParameterBag($this->data['request_server']); + } + + public function getRequestCookies() + { + return new ParameterBag($this->data['request_cookies']); + } + + public function getRequestAttributes() + { + return new ParameterBag($this->data['request_attributes']); + } + + public function getResponseHeaders() + { + return new ResponseHeaderBag($this->data['response_headers']); + } + + public function getSessionMetadata() + { + return $this->data['session_metadata']; + } + + public function getSessionAttributes() + { + return $this->data['session_attributes']; + } + + public function getFlashes() + { + return $this->data['flashes']; + } + + public function getContent() + { + return $this->data['content']; + } + + public function getContentType() + { + return $this->data['content_type']; + } + + public function getStatusText() + { + return $this->data['status_text']; + } + + public function getStatusCode() + { + return $this->data['status_code']; + } + + public function getFormat() + { + return $this->data['format']; + } + + public function getLocale() + { + return $this->data['locale']; + } + + /** + * Gets the route name. + * + * The _route request attributes is automatically set by the Router Matcher. + * + * @return string The route + */ + public function getRoute() + { + return isset($this->data['request_attributes']['_route']) ? $this->data['request_attributes']['_route'] : ''; + } + + /** + * Gets the route parameters. + * + * The _route_params request attributes is automatically set by the RouterListener. + * + * @return array The parameters + */ + public function getRouteParams() + { + return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params'] : array(); + } + + /** + * Gets the controller. + * + * @return string The controller as a string + */ + public function getController() + { + return $this->data['controller']; + } + + public function onKernelController(FilterControllerEvent $event) + { + $this->controllers[$event->getRequest()] = $event->getController(); + } + + public static function getSubscribedEvents() + { + return array(KernelEvents::CONTROLLER => 'onKernelController'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'request'; + } + + private function getCookieHeader($name, $value, $expires, $path, $domain, $secure, $httponly) + { + $cookie = sprintf('%s=%s', $name, urlencode($value)); + + if (0 !== $expires) { + if (is_numeric($expires)) { + $expires = (int) $expires; + } elseif ($expires instanceof \DateTime) { + $expires = $expires->getTimestamp(); + } else { + $tmp = strtotime($expires); + if (false === $tmp || -1 == $tmp) { + throw new \InvalidArgumentException(sprintf('The "expires" cookie parameter is not valid (%s).', $expires)); + } + $expires = $tmp; + } + + $cookie .= '; expires='.str_replace('+0000', '', \DateTime::createFromFormat('U', $expires, new \DateTimeZone('GMT'))->format('D, d-M-Y H:i:s T')); + } + + if ($domain) { + $cookie .= '; domain='.$domain; + } + + $cookie .= '; path='.$path; + + if ($secure) { + $cookie .= '; secure'; + } + + if ($httponly) { + $cookie .= '; httponly'; + } + + return $cookie; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php new file mode 100644 index 0000000..76d9623 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/RouterDataCollector.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; + +/** + * RouterDataCollector. + * + * @author Fabien Potencier + */ +class RouterDataCollector extends DataCollector +{ + protected $controllers; + + public function __construct() + { + $this->controllers = new \SplObjectStorage(); + + $this->data = array( + 'redirect' => false, + 'url' => null, + 'route' => null, + ); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if ($response instanceof RedirectResponse) { + $this->data['redirect'] = true; + $this->data['url'] = $response->getTargetUrl(); + + if ($this->controllers->contains($request)) { + $this->data['route'] = $this->guessRoute($request, $this->controllers[$request]); + } + } + + unset($this->controllers[$request]); + } + + protected function guessRoute(Request $request, $controller) + { + return 'n/a'; + } + + /** + * Remembers the controller associated to each request. + * + * @param FilterControllerEvent $event The filter controller event + */ + public function onKernelController(FilterControllerEvent $event) + { + $this->controllers[$event->getRequest()] = $event->getController(); + } + + /** + * @return bool Whether this request will result in a redirect + */ + public function getRedirect() + { + return $this->data['redirect']; + } + + /** + * @return string|null The target URL + */ + public function getTargetUrl() + { + return $this->data['url']; + } + + /** + * @return string|null The target route + */ + public function getTargetRoute() + { + return $this->data['route']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'router'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php new file mode 100644 index 0000000..4ccaafa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/TimeDataCollector.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * TimeDataCollector. + * + * @author Fabien Potencier + */ +class TimeDataCollector extends DataCollector implements LateDataCollectorInterface +{ + protected $kernel; + protected $stopwatch; + + public function __construct(KernelInterface $kernel = null, $stopwatch = null) + { + $this->kernel = $kernel; + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (null !== $this->kernel) { + $startTime = $this->kernel->getStartTime(); + } else { + $startTime = $request->server->get('REQUEST_TIME_FLOAT', $request->server->get('REQUEST_TIME')); + } + + $this->data = array( + 'token' => $response->headers->get('X-Debug-Token'), + 'start_time' => $startTime * 1000, + 'events' => array(), + ); + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + if (null !== $this->stopwatch && isset($this->data['token'])) { + $this->setEvents($this->stopwatch->getSectionEvents($this->data['token'])); + } + unset($this->data['token']); + } + + /** + * Sets the request events. + * + * @param array $events The request events + */ + public function setEvents(array $events) + { + foreach ($events as $event) { + $event->ensureStopped(); + } + + $this->data['events'] = $events; + } + + /** + * Gets the request events. + * + * @return array The request events + */ + public function getEvents() + { + return $this->data['events']; + } + + /** + * Gets the request elapsed time. + * + * @return float The elapsed time + */ + public function getDuration() + { + if (!isset($this->data['events']['__section__'])) { + return 0; + } + + $lastEvent = $this->data['events']['__section__']; + + return $lastEvent->getOrigin() + $lastEvent->getDuration() - $this->getStartTime(); + } + + /** + * Gets the initialization time. + * + * This is the time spent until the beginning of the request handling. + * + * @return float The elapsed time + */ + public function getInitTime() + { + if (!isset($this->data['events']['__section__'])) { + return 0; + } + + return $this->data['events']['__section__']->getOrigin() - $this->getStartTime(); + } + + /** + * Gets the request time. + * + * @return int The time + */ + public function getStartTime() + { + return $this->data['start_time']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'time'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php new file mode 100644 index 0000000..d2f0898 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector\Util; + +/** + * @author Bernhard Schussek + */ +class ValueExporter +{ + /** + * Converts a PHP value to a string. + * + * @param mixed $value The PHP value + * @param int $depth only for internal usage + * @param bool $deep only for internal usage + * + * @return string The string representation of the given value + */ + public function exportValue($value, $depth = 1, $deep = false) + { + if (is_object($value)) { + if ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ISO8601)); + } + + return sprintf('Object(%s)', get_class($value)); + } + + if (is_array($value)) { + if (empty($value)) { + return '[]'; + } + + $indent = str_repeat(' ', $depth); + + $a = array(); + foreach ($value as $k => $v) { + if (is_array($v)) { + $deep = true; + } + $a[] = sprintf('%s => %s', $k, $this->exportValue($v, $depth + 1, $deep)); + } + + if ($deep) { + return sprintf("[\n%s%s\n%s]", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); + } + + return sprintf('[%s]', implode(', ', $a)); + } + + if (is_resource($value)) { + return sprintf('Resource(%s#%d)', get_resource_type($value), $value); + } + + if (null === $value) { + return 'null'; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + return (string) $value; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php new file mode 100644 index 0000000..9644b04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Debug; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher as BaseTraceableEventDispatcher; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\Event; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher extends BaseTraceableEventDispatcher +{ + /** + * {@inheritdoc} + */ + protected function preDispatch($eventName, Event $event) + { + switch ($eventName) { + case KernelEvents::REQUEST: + $this->stopwatch->openSection(); + break; + case KernelEvents::VIEW: + case KernelEvents::RESPONSE: + // stop only if a controller has been executed + if ($this->stopwatch->isStarted('controller')) { + $this->stopwatch->stop('controller'); + } + break; + case KernelEvents::TERMINATE: + $token = $event->getResponse()->headers->get('X-Debug-Token'); + // There is a very special case when using built-in AppCache class as kernel wrapper, in the case + // of an ESI request leading to a `stale` response [B] inside a `fresh` cached response [A]. + // In this case, `$token` contains the [B] debug token, but the open `stopwatch` section ID + // is equal to the [A] debug token. Trying to reopen section with the [B] token throws an exception + // which must be caught. + try { + $this->stopwatch->openSection($token); + } catch (\LogicException $e) { + } + break; + } + } + + /** + * {@inheritdoc} + */ + protected function postDispatch($eventName, Event $event) + { + switch ($eventName) { + case KernelEvents::CONTROLLER: + $this->stopwatch->start('controller', 'section'); + break; + case KernelEvents::RESPONSE: + $token = $event->getResponse()->headers->get('X-Debug-Token'); + $this->stopwatch->stopSection($token); + break; + case KernelEvents::TERMINATE: + // In the special case described in the `preDispatch` method above, the `$token` section + // does not exist, then closing it throws an exception which must be caught. + $token = $event->getResponse()->headers->get('X-Debug-Token'); + try { + $this->stopwatch->stopSection($token); + } catch (\LogicException $e) { + } + break; + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php new file mode 100644 index 0000000..09af6bd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/AddClassesToCachePass.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\HttpKernel\Kernel; + +/** + * Sets the classes to compile in the cache for the container. + * + * @author Fabien Potencier + */ +class AddClassesToCachePass implements CompilerPassInterface +{ + private $kernel; + + public function __construct(Kernel $kernel) + { + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $classes = array(); + foreach ($container->getExtensions() as $extension) { + if ($extension instanceof Extension) { + $classes = array_merge($classes, $extension->getClassesToCompile()); + } + } + + $this->kernel->setClassCache(array_unique($container->getParameterBag()->resolveValue($classes))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php new file mode 100644 index 0000000..c7eca30 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This extension sub-class provides first-class integration with the + * Config/Definition Component. + * + * You can use this as base class if + * + * a) you use the Config/Definition component for configuration, + * b) your configuration class is named "Configuration", and + * c) the configuration class resides in the DependencyInjection sub-folder. + * + * @author Johannes M. Schmitt + */ +abstract class ConfigurableExtension extends Extension +{ + /** + * {@inheritdoc} + */ + final public function load(array $configs, ContainerBuilder $container) + { + $this->loadInternal($this->processConfiguration($this->getConfiguration($configs, $container), $configs), $container); + } + + /** + * Configures the passed container according to the merged configuration. + * + * @param array $mergedConfig + * @param ContainerBuilder $container + */ + abstract protected function loadInternal(array $mergedConfig, ContainerBuilder $container); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php new file mode 100644 index 0000000..2ca0f13 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Extension\Extension as BaseExtension; + +/** + * Allow adding classes to the class cache. + * + * @author Fabien Potencier + */ +abstract class Extension extends BaseExtension +{ + private $classes = array(); + + /** + * Gets the classes to cache. + * + * @return array An array of classes + */ + public function getClassesToCompile() + { + return $this->classes; + } + + /** + * Adds classes to the class cache. + * + * @param array $classes An array of classes + */ + public function addClassesToCompile(array $classes) + { + $this->classes = array_merge($this->classes, $classes); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php new file mode 100644 index 0000000..bed193c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + +/** + * Adds services tagged kernel.fragment_renderer as HTTP content rendering strategies. + * + * @author Fabien Potencier + */ +class FragmentRendererPass implements CompilerPassInterface +{ + private $handlerService; + private $rendererTag; + + /** + * @param string $handlerService Service name of the fragment handler in the container + * @param string $rendererTag Tag name used for fragments + */ + public function __construct($handlerService = 'fragment.handler', $rendererTag = 'kernel.fragment_renderer') + { + $this->handlerService = $handlerService; + $this->rendererTag = $rendererTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->handlerService)) { + return; + } + + $definition = $container->getDefinition($this->handlerService); + foreach ($container->findTaggedServiceIds($this->rendererTag) as $id => $tags) { + $def = $container->getDefinition($id); + if (!$def->isPublic()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must be public as fragment renderer are lazy-loaded.', $id)); + } + + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as fragment renderer are lazy-loaded.', $id)); + } + + $refClass = new \ReflectionClass($container->getParameterBag()->resolveValue($def->getClass())); + $interface = 'Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface'; + if (!$refClass->implementsInterface($interface)) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + foreach ($tags as $tag) { + $definition->addMethodCall('addRendererService', array($tag['alias'], $id)); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php new file mode 100644 index 0000000..7899562 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; + +/** + * Lazily loads fragment renderers from the dependency injection container. + * + * @author Fabien Potencier + */ +class LazyLoadingFragmentHandler extends FragmentHandler +{ + private $container; + private $rendererIds = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A container + * @param RequestStack $requestStack The Request stack that controls the lifecycle of requests + * @param bool $debug Whether the debug mode is enabled or not + */ + public function __construct(ContainerInterface $container, RequestStack $requestStack, $debug = false) + { + $this->container = $container; + + parent::__construct($requestStack, array(), $debug); + } + + /** + * Adds a service as a fragment renderer. + * + * @param string $renderer The render service id + */ + public function addRendererService($name, $renderer) + { + $this->rendererIds[$name] = $renderer; + } + + /** + * {@inheritdoc} + */ + public function render($uri, $renderer = 'inline', array $options = array()) + { + if (isset($this->rendererIds[$renderer])) { + $this->addRenderer($this->container->get($this->rendererIds[$renderer])); + + unset($this->rendererIds[$renderer]); + } + + return parent::render($uri, $renderer, $options); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php new file mode 100644 index 0000000..dcd7382 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass as BaseMergeExtensionConfigurationPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Ensures certain extensions are always loaded. + * + * @author Kris Wallsmith + */ +class MergeExtensionConfigurationPass extends BaseMergeExtensionConfigurationPass +{ + private $extensions; + + public function __construct(array $extensions) + { + $this->extensions = $extensions; + } + + public function process(ContainerBuilder $container) + { + foreach ($this->extensions as $extension) { + if (!count($container->getExtensionConfig($extension))) { + $container->loadFromExtension($extension, array()); + } + } + + parent::process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php new file mode 100644 index 0000000..e0af131 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows filtering of a controller callable. + * + * You can call getController() to retrieve the current controller. With + * setController() you can set a new controller that is used in the processing + * of the request. + * + * Controllers should be callables. + * + * @author Bernhard Schussek + */ +class FilterControllerEvent extends KernelEvent +{ + /** + * The current controller. + */ + private $controller; + + public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, $requestType) + { + parent::__construct($kernel, $request, $requestType); + + $this->setController($controller); + } + + /** + * Returns the current controller. + * + * @return callable + */ + public function getController() + { + return $this->controller; + } + + /** + * Sets a new controller. + * + * @param callable $controller + * + * @throws \LogicException + */ + public function setController(callable $controller) + { + $this->controller = $controller; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php new file mode 100644 index 0000000..ed816a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to filter a Response object. + * + * You can call getResponse() to retrieve the current response. With + * setResponse() you can set a new response that will be returned to the + * browser. + * + * @author Bernhard Schussek + */ +class FilterResponseEvent extends KernelEvent +{ + /** + * The current response object. + * + * @var Response + */ + private $response; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response) + { + parent::__construct($kernel, $request, $requestType); + + $this->setResponse($response); + } + + /** + * Returns the current response object. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a new response object. + * + * @param Response $response + */ + public function setResponse(Response $response) + { + $this->response = $response; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php new file mode 100644 index 0000000..ee72484 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +/** + * Triggered whenever a request is fully processed. + * + * @author Benjamin Eberlei + */ +class FinishRequestEvent extends KernelEvent +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php new file mode 100644 index 0000000..4c96a4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseEvent.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to create a response for a request. + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * @author Bernhard Schussek + */ +class GetResponseEvent extends KernelEvent +{ + /** + * The response object. + * + * @var Response + */ + private $response; + + /** + * Returns the response object. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a response and stops event propagation. + * + * @param Response $response + */ + public function setResponse(Response $response) + { + $this->response = $response; + + $this->stopPropagation(); + } + + /** + * Returns whether a response was set. + * + * @return bool Whether a response was set + */ + public function hasResponse() + { + return null !== $this->response; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php new file mode 100644 index 0000000..f70ce09 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to create a response for the return value of a controller. + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * @author Bernhard Schussek + */ +class GetResponseForControllerResultEvent extends GetResponseEvent +{ + /** + * The return value of the controller. + * + * @var mixed + */ + private $controllerResult; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, $controllerResult) + { + parent::__construct($kernel, $request, $requestType); + + $this->controllerResult = $controllerResult; + } + + /** + * Returns the return value of the controller. + * + * @return mixed The controller return value + */ + public function getControllerResult() + { + return $this->controllerResult; + } + + /** + * Assigns the return value of the controller. + * + * @param mixed $controllerResult The controller return value + */ + public function setControllerResult($controllerResult) + { + $this->controllerResult = $controllerResult; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php new file mode 100644 index 0000000..003953f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to create a response for a thrown exception. + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * You can also call setException() to replace the thrown exception. This + * exception will be thrown if no response is set during processing of this + * event. + * + * @author Bernhard Schussek + */ +class GetResponseForExceptionEvent extends GetResponseEvent +{ + /** + * The exception object. + * + * @var \Exception + */ + private $exception; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e) + { + parent::__construct($kernel, $request, $requestType); + + $this->setException($e); + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/KernelEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/KernelEvent.php new file mode 100644 index 0000000..2043a01 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/KernelEvent.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\Event; + +/** + * Base class for events thrown in the HttpKernel component. + * + * @author Bernhard Schussek + */ +class KernelEvent extends Event +{ + /** + * The kernel in which this event was thrown. + * + * @var HttpKernelInterface + */ + private $kernel; + + /** + * The request the kernel is currently processing. + * + * @var Request + */ + private $request; + + /** + * The request type the kernel is currently processing. One of + * HttpKernelInterface::MASTER_REQUEST and HttpKernelInterface::SUB_REQUEST. + * + * @var int + */ + private $requestType; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType) + { + $this->kernel = $kernel; + $this->request = $request; + $this->requestType = $requestType; + } + + /** + * Returns the kernel in which this event was thrown. + * + * @return HttpKernelInterface + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Returns the request the kernel is currently processing. + * + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Returns the request type the kernel is currently processing. + * + * @return int One of HttpKernelInterface::MASTER_REQUEST and + * HttpKernelInterface::SUB_REQUEST + */ + public function getRequestType() + { + return $this->requestType; + } + + /** + * Checks if this is a master request. + * + * @return bool True if the request is a master request + */ + public function isMasterRequest() + { + return HttpKernelInterface::MASTER_REQUEST === $this->requestType; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php new file mode 100644 index 0000000..2406fdd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Event/PostResponseEvent.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to execute logic after a response was sent. + * + * Since it's only triggered on master requests, the `getRequestType()` method + * will always return the value of `HttpKernelInterface::MASTER_REQUEST`. + * + * @author Jordi Boggiano + */ +class PostResponseEvent extends KernelEvent +{ + private $response; + + public function __construct(HttpKernelInterface $kernel, Request $request, Response $response) + { + parent::__construct($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $this->response = $response; + } + + /** + * Returns the response for which this event was thrown. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php new file mode 100644 index 0000000..14a5d43 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/AddRequestFormatsListener.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +/** + * Adds configured formats to each request. + * + * @author Gildas Quemener + */ +class AddRequestFormatsListener implements EventSubscriberInterface +{ + /** + * @var array + */ + protected $formats; + + /** + * @param array $formats + */ + public function __construct(array $formats) + { + $this->formats = $formats; + } + + /** + * Adds request formats. + * + * @param GetResponseEvent $event + */ + public function onKernelRequest(GetResponseEvent $event) + { + foreach ($this->formats as $format => $mimeTypes) { + $event->getRequest()->setFormat($format, $mimeTypes); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(KernelEvents::REQUEST => 'onKernelRequest'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php new file mode 100644 index 0000000..878e349 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\KernelEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * Configures errors and exceptions handlers. + * + * @author Nicolas Grekas + */ +class DebugHandlersListener implements EventSubscriberInterface +{ + private $exceptionHandler; + private $logger; + private $levels; + private $throwAt; + private $scream; + private $fileLinkFormat; + private $firstCall = true; + + /** + * @param callable|null $exceptionHandler A handler that will be called on Exception + * @param LoggerInterface|null $logger A PSR-3 logger + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value + * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged + * @param string $fileLinkFormat The format for links to source files + */ + public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, $throwAt = E_ALL, $scream = true, $fileLinkFormat = null) + { + $this->exceptionHandler = $exceptionHandler; + $this->logger = $logger; + $this->levels = null === $levels ? E_ALL : $levels; + $this->throwAt = is_numeric($throwAt) ? (int) $throwAt : (null === $throwAt ? null : ($throwAt ? E_ALL : null)); + $this->scream = (bool) $scream; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + } + + /** + * Configures the error handler. + * + * @param Event|null $event The triggering event + */ + public function configure(Event $event = null) + { + if (!$this->firstCall) { + return; + } + $this->firstCall = false; + if ($this->logger || null !== $this->throwAt) { + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler instanceof ErrorHandler) { + if ($this->logger) { + $handler->setDefaultLogger($this->logger, $this->levels); + if (is_array($this->levels)) { + $scream = 0; + foreach ($this->levels as $type => $log) { + $scream |= $type; + } + } else { + $scream = $this->levels; + } + if ($this->scream) { + $handler->screamAt($scream); + } + $this->logger = $this->levels = null; + } + if (null !== $this->throwAt) { + $handler->throwAt($this->throwAt, true); + } + } + } + if (!$this->exceptionHandler) { + if ($event instanceof KernelEvent) { + $this->exceptionHandler = array($event->getKernel(), 'terminateWithException'); + } elseif ($event instanceof ConsoleEvent && $app = $event->getCommand()->getApplication()) { + $output = $event->getOutput(); + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + $this->exceptionHandler = function ($e) use ($app, $output) { + $app->renderException($e, $output); + }; + } + } + if ($this->exceptionHandler) { + $handler = set_exception_handler('var_dump'); + $handler = is_array($handler) ? $handler[0] : null; + restore_exception_handler(); + if ($handler instanceof ErrorHandler) { + $h = $handler->setExceptionHandler('var_dump') ?: $this->exceptionHandler; + $handler->setExceptionHandler($h); + $handler = is_array($h) ? $h[0] : null; + } + if ($handler instanceof ExceptionHandler) { + $handler->setHandler($this->exceptionHandler); + if (null !== $this->fileLinkFormat) { + $handler->setFileLinkFormat($this->fileLinkFormat); + } + } + $this->exceptionHandler = null; + } + } + + public static function getSubscribedEvents() + { + $events = array(KernelEvents::REQUEST => array('configure', 2048)); + + if (defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) { + $events[ConsoleEvents::COMMAND] = array('configure', 2048); + } + + return $events; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php new file mode 100644 index 0000000..bccde8e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Symfony\Component\VarDumper\VarDumper; + +/** + * Configures dump() handler. + * + * @author Nicolas Grekas + */ +class DumpListener implements EventSubscriberInterface +{ + private $cloner; + private $dumper; + + /** + * @param ClonerInterface $cloner Cloner service. + * @param DataDumperInterface $dumper Dumper service. + */ + public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper) + { + $this->cloner = $cloner; + $this->dumper = $dumper; + } + + public function configure() + { + $cloner = $this->cloner; + $dumper = $this->dumper; + + VarDumper::setHandler(function ($var) use ($cloner, $dumper) { + $dumper->dump($cloner->cloneVar($var)); + }); + } + + public static function getSubscribedEvents() + { + // Register early to have a working dump() as early as possible + return array(KernelEvents::REQUEST => array('configure', 1024)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php new file mode 100644 index 0000000..4d92a24 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ExceptionListener. + * + * @author Fabien Potencier + */ +class ExceptionListener implements EventSubscriberInterface +{ + protected $controller; + protected $logger; + + public function __construct($controller, LoggerInterface $logger = null) + { + $this->controller = $controller; + $this->logger = $logger; + } + + public function onKernelException(GetResponseForExceptionEvent $event) + { + $exception = $event->getException(); + $request = $event->getRequest(); + + $this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine())); + + $request = $this->duplicateRequest($exception, $request); + + try { + $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false); + } catch (\Exception $e) { + $this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), false); + + $wrapper = $e; + + while ($prev = $wrapper->getPrevious()) { + if ($exception === $wrapper = $prev) { + throw $e; + } + } + + $prev = new \ReflectionProperty('Exception', 'previous'); + $prev->setAccessible(true); + $prev->setValue($wrapper, $exception); + + throw $e; + } + + $event->setResponse($response); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::EXCEPTION => array('onKernelException', -128), + ); + } + + /** + * Logs an exception. + * + * @param \Exception $exception The \Exception instance + * @param string $message The error message to log + */ + protected function logException(\Exception $exception, $message) + { + if (null !== $this->logger) { + if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) { + $this->logger->critical($message, array('exception' => $exception)); + } else { + $this->logger->error($message, array('exception' => $exception)); + } + } + } + + /** + * Clones the request for the exception. + * + * @param \Exception $exception The thrown exception. + * @param Request $request The original request. + * + * @return Request $request The cloned request. + */ + protected function duplicateRequest(\Exception $exception, Request $request) + { + $attributes = array( + '_controller' => $this->controller, + 'exception' => FlattenException::create($exception), + 'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null, + ); + $request = $request->duplicate(null, null, $attributes); + $request->setMethod('GET'); + + return $request; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php new file mode 100644 index 0000000..87ee080 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Handles content fragments represented by special URIs. + * + * All URL paths starting with /_fragment are handled as + * content fragments by this listener. + * + * If throws an AccessDeniedHttpException exception if the request + * is not signed or if it is not an internal sub-request. + * + * @author Fabien Potencier + */ +class FragmentListener implements EventSubscriberInterface +{ + private $signer; + private $fragmentPath; + + /** + * Constructor. + * + * @param UriSigner $signer A UriSigner instance + * @param string $fragmentPath The path that triggers this listener + */ + public function __construct(UriSigner $signer, $fragmentPath = '/_fragment') + { + $this->signer = $signer; + $this->fragmentPath = $fragmentPath; + } + + /** + * Fixes request attributes when the path is '/_fragment'. + * + * @param GetResponseEvent $event A GetResponseEvent instance + * + * @throws AccessDeniedHttpException if the request does not come from a trusted IP. + */ + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + if ($request->attributes->has('_controller') || $this->fragmentPath !== rawurldecode($request->getPathInfo())) { + return; + } + + if ($event->isMasterRequest()) { + $this->validateRequest($request); + } + + parse_str($request->query->get('_path', ''), $attributes); + $request->attributes->add($attributes); + $request->attributes->set('_route_params', array_replace($request->attributes->get('_route_params', array()), $attributes)); + $request->query->remove('_path'); + } + + protected function validateRequest(Request $request) + { + // is the Request safe? + if (!$request->isMethodSafe()) { + throw new AccessDeniedHttpException(); + } + + // is the Request signed? + // we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering) + if ($this->signer->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().(null !== ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''))) { + return; + } + + throw new AccessDeniedHttpException(); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array(array('onKernelRequest', 48)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php new file mode 100644 index 0000000..99fc786 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\RequestContextAwareInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Initializes the locale based on the current request. + * + * @author Fabien Potencier + */ +class LocaleListener implements EventSubscriberInterface +{ + private $router; + private $defaultLocale; + private $requestStack; + + /** + * Constructor. + * + * @param RequestStack $requestStack A RequestStack instance + * @param string $defaultLocale The default locale + * @param RequestContextAwareInterface|null $router The router + */ + public function __construct(RequestStack $requestStack, $defaultLocale = 'en', RequestContextAwareInterface $router = null) + { + $this->defaultLocale = $defaultLocale; + $this->requestStack = $requestStack; + $this->router = $router; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + $request->setDefaultLocale($this->defaultLocale); + + $this->setLocale($request); + $this->setRouterContext($request); + } + + public function onKernelFinishRequest(FinishRequestEvent $event) + { + if (null !== $parentRequest = $this->requestStack->getParentRequest()) { + $this->setRouterContext($parentRequest); + } + } + + private function setLocale(Request $request) + { + if ($locale = $request->attributes->get('_locale')) { + $request->setLocale($locale); + } + } + + private function setRouterContext(Request $request) + { + if (null !== $this->router) { + $this->router->getContext()->setParameter('_locale', $request->getLocale()); + } + } + + public static function getSubscribedEvents() + { + return array( + // must be registered after the Router to have access to the _locale + KernelEvents::REQUEST => array(array('onKernelRequest', 16)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php new file mode 100644 index 0000000..c3772b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\RequestMatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ProfilerListener collects data for the current request by listening to the kernel events. + * + * @author Fabien Potencier + */ +class ProfilerListener implements EventSubscriberInterface +{ + protected $profiler; + protected $matcher; + protected $onlyException; + protected $onlyMasterRequests; + protected $exception; + protected $profiles; + protected $requestStack; + protected $parents; + + /** + * Constructor. + * + * @param Profiler $profiler A Profiler instance + * @param RequestStack $requestStack A RequestStack instance + * @param RequestMatcherInterface|null $matcher A RequestMatcher instance + * @param bool $onlyException true if the profiler only collects data when an exception occurs, false otherwise + * @param bool $onlyMasterRequests true if the profiler only collects data when the request is a master request, false otherwise + */ + public function __construct(Profiler $profiler, RequestStack $requestStack, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false) + { + $this->profiler = $profiler; + $this->matcher = $matcher; + $this->onlyException = (bool) $onlyException; + $this->onlyMasterRequests = (bool) $onlyMasterRequests; + $this->profiles = new \SplObjectStorage(); + $this->parents = new \SplObjectStorage(); + $this->requestStack = $requestStack; + } + + /** + * Handles the onKernelException event. + * + * @param GetResponseForExceptionEvent $event A GetResponseForExceptionEvent instance + */ + public function onKernelException(GetResponseForExceptionEvent $event) + { + if ($this->onlyMasterRequests && !$event->isMasterRequest()) { + return; + } + + $this->exception = $event->getException(); + } + + /** + * Handles the onKernelResponse event. + * + * @param FilterResponseEvent $event A FilterResponseEvent instance + */ + public function onKernelResponse(FilterResponseEvent $event) + { + $master = $event->isMasterRequest(); + if ($this->onlyMasterRequests && !$master) { + return; + } + + if ($this->onlyException && null === $this->exception) { + return; + } + + $request = $event->getRequest(); + $exception = $this->exception; + $this->exception = null; + + if (null !== $this->matcher && !$this->matcher->matches($request)) { + return; + } + + if (!$profile = $this->profiler->collect($request, $event->getResponse(), $exception)) { + return; + } + + $this->profiles[$request] = $profile; + + $this->parents[$request] = $this->requestStack->getParentRequest(); + } + + public function onKernelTerminate(PostResponseEvent $event) + { + // attach children to parents + foreach ($this->profiles as $request) { + // isset call should be removed when requestStack is required + if (isset($this->parents[$request]) && null !== $parentRequest = $this->parents[$request]) { + if (isset($this->profiles[$parentRequest])) { + $this->profiles[$parentRequest]->addChild($this->profiles[$request]); + } + } + } + + // save profiles + foreach ($this->profiles as $request) { + $this->profiler->saveProfile($this->profiles[$request]); + } + + $this->profiles = new \SplObjectStorage(); + $this->parents = new \SplObjectStorage(); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => array('onKernelResponse', -100), + KernelEvents::EXCEPTION => 'onKernelException', + KernelEvents::TERMINATE => array('onKernelTerminate', -1024), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php new file mode 100644 index 0000000..eeb2b0f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/ResponseListener.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ResponseListener fixes the Response headers based on the Request. + * + * @author Fabien Potencier + */ +class ResponseListener implements EventSubscriberInterface +{ + private $charset; + + public function __construct($charset) + { + $this->charset = $charset; + } + + /** + * Filters the Response. + * + * @param FilterResponseEvent $event A FilterResponseEvent instance + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $response = $event->getResponse(); + + if (null === $response->getCharset()) { + $response->setCharset($this->charset); + } + + $response->prepare($event->getRequest()); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php new file mode 100644 index 0000000..01a51b1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RequestContextAwareInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Initializes the context from the request and sets request attributes based on a matching route. + * + * @author Fabien Potencier + */ +class RouterListener implements EventSubscriberInterface +{ + private $matcher; + private $context; + private $logger; + private $requestStack; + + /** + * Constructor. + * + * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher + * @param RequestStack $requestStack A RequestStack instance + * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) + * @param LoggerInterface|null $logger The logger + * + * @throws \InvalidArgumentException + */ + public function __construct($matcher, RequestStack $requestStack, RequestContext $context = null, LoggerInterface $logger = null) + { + if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { + throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); + } + + if (null === $context && !$matcher instanceof RequestContextAwareInterface) { + throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.'); + } + + $this->matcher = $matcher; + $this->context = $context ?: $matcher->getContext(); + $this->requestStack = $requestStack; + $this->logger = $logger; + } + + private function setCurrentRequest(Request $request = null) + { + if (null !== $request) { + $this->context->fromRequest($request); + } + } + + /** + * After a sub-request is done, we need to reset the routing context to the parent request so that the URL generator + * operates on the correct context again. + * + * @param FinishRequestEvent $event + */ + public function onKernelFinishRequest(FinishRequestEvent $event) + { + $this->setCurrentRequest($this->requestStack->getParentRequest()); + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + $this->setCurrentRequest($request); + + if ($request->attributes->has('_controller')) { + // routing is already done + return; + } + + // add attributes based on the request (routing) + try { + // matching a request is more powerful than matching a URL path + context, so try that first + if ($this->matcher instanceof RequestMatcherInterface) { + $parameters = $this->matcher->matchRequest($request); + } else { + $parameters = $this->matcher->match($request->getPathInfo()); + } + + if (null !== $this->logger) { + $this->logger->info(sprintf('Matched route "%s".', isset($parameters['_route']) ? $parameters['_route'] : 'n/a'), array( + 'route_parameters' => $parameters, + 'request_uri' => $request->getUri(), + )); + } + + $request->attributes->add($parameters); + unset($parameters['_route'], $parameters['_controller']); + $request->attributes->set('_route_params', $parameters); + } catch (ResourceNotFoundException $e) { + $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); + + if ($referer = $request->headers->get('referer')) { + $message .= sprintf(' (from "%s")', $referer); + } + + throw new NotFoundHttpException($message, $e); + } catch (MethodNotAllowedException $e) { + $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), implode(', ', $e->getAllowedMethods())); + + throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array(array('onKernelRequest', 32)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php new file mode 100644 index 0000000..36809b5 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Saves the session, in case it is still open, before sending the response/headers. + * + * This ensures several things in case the developer did not save the session explicitly: + * + * * If a session save handler without locking is used, it ensures the data is available + * on the next request, e.g. after a redirect. PHPs auto-save at script end via + * session_register_shutdown is executed after fastcgi_finish_request. So in this case + * the data could be missing the next request because it might not be saved the moment + * the new request is processed. + * * A locking save handler (e.g. the native 'files') circumvents concurrency problems like + * the one above. But by saving the session before long-running things in the terminate event, + * we ensure the session is not blocked longer than needed. + * * When regenerating the session ID no locking is involved in PHPs session design. See + * https://bugs.php.net/bug.php?id=61470 for a discussion. So in this case, the session must + * be saved anyway before sending the headers with the new session ID. Otherwise session + * data could get lost again for concurrent requests with the new ID. One result could be + * that you get logged out after just logging in. + * + * This listener should be executed as one of the last listeners, so that previous listeners + * can still operate on the open session. This prevents the overhead of restarting it. + * Listeners after closing the session can still work with the session as usual because + * Symfonys session implementation starts the session on demand. So writing to it after + * it is saved will just restart it. + * + * @author Tobias Schultze + */ +class SaveSessionListener implements EventSubscriberInterface +{ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $session = $event->getRequest()->getSession(); + if ($session && $session->isStarted()) { + $session->save(); + } + } + + public static function getSubscribedEvents() + { + return array( + // low priority but higher than StreamedResponseListener + KernelEvents::RESPONSE => array(array('onKernelResponse', -1000)), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php new file mode 100644 index 0000000..ecf065f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SessionListener.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Sets the session in the request. + * + * @author Johannes M. Schmitt + */ +abstract class SessionListener implements EventSubscriberInterface +{ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $request = $event->getRequest(); + $session = $this->getSession(); + if (null === $session || $request->hasSession()) { + return; + } + + $request->setSession($session); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 128), + ); + } + + /** + * Gets the session object. + * + * @return SessionInterface|null A SessionInterface instance or null if no session is available + */ + abstract protected function getSession(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php new file mode 100644 index 0000000..571cd74 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/StreamedResponseListener.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * StreamedResponseListener is responsible for sending the Response + * to the client. + * + * @author Fabien Potencier + */ +class StreamedResponseListener implements EventSubscriberInterface +{ + /** + * Filters the Response. + * + * @param FilterResponseEvent $event A FilterResponseEvent instance + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $response = $event->getResponse(); + + if ($response instanceof StreamedResponse) { + $response->send(); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => array('onKernelResponse', -1024), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SurrogateListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SurrogateListener.php new file mode 100644 index 0000000..dc815a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/SurrogateListener.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * SurrogateListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for Surrogates. + * + * @author Fabien Potencier + */ +class SurrogateListener implements EventSubscriberInterface +{ + private $surrogate; + + /** + * Constructor. + * + * @param SurrogateInterface $surrogate An SurrogateInterface instance + */ + public function __construct(SurrogateInterface $surrogate = null) + { + $this->surrogate = $surrogate; + } + + /** + * Filters the Response. + * + * @param FilterResponseEvent $event A FilterResponseEvent instance + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest() || null === $this->surrogate) { + return; + } + + $this->surrogate->addSurrogateControl($event->getResponse()); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/TestSessionListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/TestSessionListener.php new file mode 100644 index 0000000..8fc8e57 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/TestSessionListener.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * TestSessionListener. + * + * Saves session in test environment. + * + * @author Bulat Shakirzyanov + * @author Fabien Potencier + */ +abstract class TestSessionListener implements EventSubscriberInterface +{ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + // bootstrap the session + $session = $this->getSession(); + if (!$session) { + return; + } + + $cookies = $event->getRequest()->cookies; + + if ($cookies->has($session->getName())) { + $session->setId($cookies->get($session->getName())); + } + } + + /** + * Checks if session was initialized and saves if current request is master + * Runs on 'kernel.response' in test environment. + * + * @param FilterResponseEvent $event + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $session = $event->getRequest()->getSession(); + if ($session && $session->isStarted()) { + $session->save(); + $params = session_get_cookie_params(); + $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 192), + KernelEvents::RESPONSE => array('onKernelResponse', -128), + ); + } + + /** + * Gets the session object. + * + * @return SessionInterface|null A SessionInterface instance or null if no session is available + */ + abstract protected function getSession(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php new file mode 100644 index 0000000..6967ad0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/EventListener/TranslatorListener.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * Synchronizes the locale between the request and the translator. + * + * @author Fabien Potencier + */ +class TranslatorListener implements EventSubscriberInterface +{ + private $translator; + private $requestStack; + + public function __construct(TranslatorInterface $translator, RequestStack $requestStack) + { + $this->translator = $translator; + $this->requestStack = $requestStack; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $this->setLocale($event->getRequest()); + } + + public function onKernelFinishRequest(FinishRequestEvent $event) + { + if (null === $parentRequest = $this->requestStack->getParentRequest()) { + return; + } + + $this->setLocale($parentRequest); + } + + public static function getSubscribedEvents() + { + return array( + // must be registered after the Locale listener + KernelEvents::REQUEST => array(array('onKernelRequest', 10)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } + + private function setLocale(Request $request) + { + try { + $this->translator->setLocale($request->getLocale()); + } catch (\InvalidArgumentException $e) { + $this->translator->setLocale($request->getDefaultLocale()); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php new file mode 100644 index 0000000..79d8639 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/AccessDeniedHttpException.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * AccessDeniedHttpException. + * + * @author Fabien Potencier + * @author Christophe Coevoet + */ +class AccessDeniedHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(403, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php new file mode 100644 index 0000000..5f68172 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/BadRequestHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * BadRequestHttpException. + * + * @author Ben Ramsey + */ +class BadRequestHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(400, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php new file mode 100644 index 0000000..34d738e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/ConflictHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * ConflictHttpException. + * + * @author Ben Ramsey + */ +class ConflictHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(409, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php new file mode 100644 index 0000000..16ea223 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/GoneHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * GoneHttpException. + * + * @author Ben Ramsey + */ +class GoneHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(410, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/HttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/HttpException.php new file mode 100644 index 0000000..4e1b526 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/HttpException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * HttpException. + * + * @author Kris Wallsmith + */ +class HttpException extends \RuntimeException implements HttpExceptionInterface +{ + private $statusCode; + private $headers; + + public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0) + { + $this->statusCode = $statusCode; + $this->headers = $headers; + + parent::__construct($message, $code, $previous); + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getHeaders() + { + return $this->headers; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/HttpExceptionInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/HttpExceptionInterface.php new file mode 100644 index 0000000..8aa50a9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/HttpExceptionInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * Interface for HTTP error exceptions. + * + * @author Kris Wallsmith + */ +interface HttpExceptionInterface +{ + /** + * Returns the status code. + * + * @return int An HTTP response status code + */ + public function getStatusCode(); + + /** + * Returns response headers. + * + * @return array Response headers + */ + public function getHeaders(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php new file mode 100644 index 0000000..0c4b943 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/LengthRequiredHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * LengthRequiredHttpException. + * + * @author Ben Ramsey + */ +class LengthRequiredHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(411, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php new file mode 100644 index 0000000..78dd26b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/MethodNotAllowedHttpException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * MethodNotAllowedHttpException. + * + * @author Kris Wallsmith + */ +class MethodNotAllowedHttpException extends HttpException +{ + /** + * Constructor. + * + * @param array $allow An array of allowed methods + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct(array $allow, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array('Allow' => strtoupper(implode(', ', $allow))); + + parent::__construct(405, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php new file mode 100644 index 0000000..cc6be4b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/NotAcceptableHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * NotAcceptableHttpException. + * + * @author Ben Ramsey + */ +class NotAcceptableHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(406, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php new file mode 100644 index 0000000..4639e37 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/NotFoundHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * NotFoundHttpException. + * + * @author Fabien Potencier + */ +class NotFoundHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(404, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php new file mode 100644 index 0000000..9df0e7b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionFailedHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * PreconditionFailedHttpException. + * + * @author Ben Ramsey + */ +class PreconditionFailedHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(412, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php new file mode 100644 index 0000000..08ebca2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/PreconditionRequiredHttpException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * PreconditionRequiredHttpException. + * + * @author Ben Ramsey + * + * @see http://tools.ietf.org/html/rfc6585 + */ +class PreconditionRequiredHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(428, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php new file mode 100644 index 0000000..32b9e2d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/ServiceUnavailableHttpException.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * ServiceUnavailableHttpException. + * + * @author Ben Ramsey + */ +class ServiceUnavailableHttpException extends HttpException +{ + /** + * Constructor. + * + * @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array(); + if ($retryAfter) { + $headers = array('Retry-After' => $retryAfter); + } + + parent::__construct(503, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php new file mode 100644 index 0000000..ab86e09 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/TooManyRequestsHttpException.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * TooManyRequestsHttpException. + * + * @author Ben Ramsey + * + * @see http://tools.ietf.org/html/rfc6585 + */ +class TooManyRequestsHttpException extends HttpException +{ + /** + * Constructor. + * + * @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array(); + if ($retryAfter) { + $headers = array('Retry-After' => $retryAfter); + } + + parent::__construct(429, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php new file mode 100644 index 0000000..0dfe42d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnauthorizedHttpException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * UnauthorizedHttpException. + * + * @author Ben Ramsey + */ +class UnauthorizedHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $challenge WWW-Authenticate challenge string + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($challenge, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array('WWW-Authenticate' => $challenge); + + parent::__construct(401, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php new file mode 100644 index 0000000..eb13f56 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnprocessableEntityHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * UnprocessableEntityHttpException. + * + * @author Steve Hutchins + */ +class UnprocessableEntityHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(422, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php new file mode 100644 index 0000000..a9d8fa0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Exception/UnsupportedMediaTypeHttpException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * UnsupportedMediaTypeHttpException. + * + * @author Ben Ramsey + */ +class UnsupportedMediaTypeHttpException extends HttpException +{ + /** + * Constructor. + * + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(415, $message, $previous, array(), $code); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php new file mode 100644 index 0000000..1968001 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; +use Symfony\Component\HttpKernel\UriSigner; + +/** + * Implements Surrogate rendering strategy. + * + * @author Fabien Potencier + */ +abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRenderer +{ + private $surrogate; + private $inlineStrategy; + private $signer; + + /** + * Constructor. + * + * The "fallback" strategy when surrogate is not available should always be an + * instance of InlineFragmentRenderer. + * + * @param SurrogateInterface $surrogate An Surrogate instance + * @param FragmentRendererInterface $inlineStrategy The inline strategy to use when the surrogate is not supported + * @param UriSigner $signer + */ + public function __construct(SurrogateInterface $surrogate = null, FragmentRendererInterface $inlineStrategy, UriSigner $signer = null) + { + $this->surrogate = $surrogate; + $this->inlineStrategy = $inlineStrategy; + $this->signer = $signer; + } + + /** + * {@inheritdoc} + * + * Note that if the current Request has no surrogate capability, this method + * falls back to use the inline rendering strategy. + * + * Additional available options: + * + * * alt: an alternative URI to render in case of an error + * * comment: a comment to add when returning the surrogate tag + * + * Note, that not all surrogate strategies support all options. For now + * 'alt' and 'comment' are only supported by ESI. + * + * @see Symfony\Component\HttpKernel\HttpCache\SurrogateInterface + */ + public function render($uri, Request $request, array $options = array()) + { + if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) { + return $this->inlineStrategy->render($uri, $request, $options); + } + + if ($uri instanceof ControllerReference) { + $uri = $this->generateSignedFragmentUri($uri, $request); + } + + $alt = isset($options['alt']) ? $options['alt'] : null; + if ($alt instanceof ControllerReference) { + $alt = $this->generateSignedFragmentUri($alt, $request); + } + + $tag = $this->surrogate->renderIncludeTag($uri, $alt, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : ''); + + return new Response($tag); + } + + private function generateSignedFragmentUri($uri, Request $request) + { + if (null === $this->signer) { + throw new \LogicException('You must use a URI when using the ESI rendering strategy or set a URL signer.'); + } + + // we need to sign the absolute URI, but want to return the path only. + $fragmentUri = $this->signer->sign($this->generateFragmentUri($uri, $request, true)); + + return substr($fragmentUri, strlen($request->getSchemeAndHttpHost())); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/EsiFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/EsiFragmentRenderer.php new file mode 100644 index 0000000..a4570e3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/EsiFragmentRenderer.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +/** + * Implements the ESI rendering strategy. + * + * @author Fabien Potencier + */ +class EsiFragmentRenderer extends AbstractSurrogateFragmentRenderer +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'esi'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php new file mode 100644 index 0000000..8bc74a2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +/** + * Renders a URI that represents a resource fragment. + * + * This class handles the rendering of resource fragments that are included into + * a main resource. The handling of the rendering is managed by specialized renderers. + * + * @author Fabien Potencier + * + * @see FragmentRendererInterface + */ +class FragmentHandler +{ + private $debug; + private $renderers = array(); + private $requestStack; + + /** + * Constructor. + * + * @param RequestStack $requestStack The Request stack that controls the lifecycle of requests + * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances + * @param bool $debug Whether the debug mode is enabled or not + */ + public function __construct(RequestStack $requestStack, array $renderers = array(), $debug = false) + { + $this->requestStack = $requestStack; + foreach ($renderers as $renderer) { + $this->addRenderer($renderer); + } + $this->debug = $debug; + } + + /** + * Adds a renderer. + * + * @param FragmentRendererInterface $renderer A FragmentRendererInterface instance + */ + public function addRenderer(FragmentRendererInterface $renderer) + { + $this->renderers[$renderer->getName()] = $renderer; + } + + /** + * Renders a URI and returns the Response content. + * + * Available options: + * + * * ignore_errors: true to return an empty string in case of an error + * + * @param string|ControllerReference $uri A URI as a string or a ControllerReference instance + * @param string $renderer The renderer name + * @param array $options An array of options + * + * @return string|null The Response content or null when the Response is streamed + * + * @throws \InvalidArgumentException when the renderer does not exist + * @throws \LogicException when no master request is being handled + */ + public function render($uri, $renderer = 'inline', array $options = array()) + { + if (!isset($options['ignore_errors'])) { + $options['ignore_errors'] = !$this->debug; + } + + if (!isset($this->renderers[$renderer])) { + throw new \InvalidArgumentException(sprintf('The "%s" renderer does not exist.', $renderer)); + } + + if (!$request = $this->requestStack->getCurrentRequest()) { + throw new \LogicException('Rendering a fragment can only be done when handling a Request.'); + } + + return $this->deliver($this->renderers[$renderer]->render($uri, $request, $options)); + } + + /** + * Delivers the Response as a string. + * + * When the Response is a StreamedResponse, the content is streamed immediately + * instead of being returned. + * + * @param Response $response A Response instance + * + * @return string|null The Response content or null when the Response is streamed + * + * @throws \RuntimeException when the Response is not successful + */ + protected function deliver(Response $response) + { + if (!$response->isSuccessful()) { + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->requestStack->getCurrentRequest()->getUri(), $response->getStatusCode())); + } + + if (!$response instanceof StreamedResponse) { + return $response->getContent(); + } + + $response->sendContent(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentRendererInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentRendererInterface.php new file mode 100644 index 0000000..b177c3a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/FragmentRendererInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpFoundation\Response; + +/** + * Interface implemented by all rendering strategies. + * + * @author Fabien Potencier + */ +interface FragmentRendererInterface +{ + /** + * Renders a URI and returns the Response content. + * + * @param string|ControllerReference $uri A URI as a string or a ControllerReference instance + * @param Request $request A Request instance + * @param array $options An array of options + * + * @return Response A Response instance + */ + public function render($uri, Request $request, array $options = array()); + + /** + * Gets the name of the strategy. + * + * @return string The strategy name + */ + public function getName(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php new file mode 100644 index 0000000..304b527 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\EngineInterface; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\UriSigner; + +/** + * Implements the Hinclude rendering strategy. + * + * @author Fabien Potencier + */ +class HIncludeFragmentRenderer extends RoutableFragmentRenderer +{ + private $globalDefaultTemplate; + private $signer; + private $templating; + private $charset; + + /** + * Constructor. + * + * @param EngineInterface|\Twig_Environment $templating An EngineInterface or a \Twig_Environment instance + * @param UriSigner $signer A UriSigner instance + * @param string $globalDefaultTemplate The global default content (it can be a template name or the content) + * @param string $charset + */ + public function __construct($templating = null, UriSigner $signer = null, $globalDefaultTemplate = null, $charset = 'utf-8') + { + $this->setTemplating($templating); + $this->globalDefaultTemplate = $globalDefaultTemplate; + $this->signer = $signer; + $this->charset = $charset; + } + + /** + * Sets the templating engine to use to render the default content. + * + * @param EngineInterface|\Twig_Environment|null $templating An EngineInterface or a \Twig_Environment instance + * + * @throws \InvalidArgumentException + */ + public function setTemplating($templating) + { + if (null !== $templating && !$templating instanceof EngineInterface && !$templating instanceof \Twig_Environment) { + throw new \InvalidArgumentException('The hinclude rendering strategy needs an instance of \Twig_Environment or Symfony\Component\Templating\EngineInterface'); + } + + $this->templating = $templating; + } + + /** + * Checks if a templating engine has been set. + * + * @return bool true if the templating engine has been set, false otherwise + */ + public function hasTemplating() + { + return null !== $this->templating; + } + + /** + * {@inheritdoc} + * + * Additional available options: + * + * * default: The default content (it can be a template name or the content) + * * id: An optional hx:include tag id attribute + * * attributes: An optional array of hx:include tag attributes + */ + public function render($uri, Request $request, array $options = array()) + { + if ($uri instanceof ControllerReference) { + if (null === $this->signer) { + throw new \LogicException('You must use a proper URI when using the Hinclude rendering strategy or set a URL signer.'); + } + + // we need to sign the absolute URI, but want to return the path only. + $uri = substr($this->signer->sign($this->generateFragmentUri($uri, $request, true)), strlen($request->getSchemeAndHttpHost())); + } + + // We need to replace ampersands in the URI with the encoded form in order to return valid html/xml content. + $uri = str_replace('&', '&', $uri); + + $template = isset($options['default']) ? $options['default'] : $this->globalDefaultTemplate; + if (null !== $this->templating && $template && $this->templateExists($template)) { + $content = $this->templating->render($template); + } else { + $content = $template; + } + + $attributes = isset($options['attributes']) && is_array($options['attributes']) ? $options['attributes'] : array(); + if (isset($options['id']) && $options['id']) { + $attributes['id'] = $options['id']; + } + $renderedAttributes = ''; + if (count($attributes) > 0) { + $flags = ENT_QUOTES | ENT_SUBSTITUTE; + foreach ($attributes as $attribute => $value) { + $renderedAttributes .= sprintf( + ' %s="%s"', + htmlspecialchars($attribute, $flags, $this->charset, false), + htmlspecialchars($value, $flags, $this->charset, false) + ); + } + } + + return new Response(sprintf('%s', $uri, $renderedAttributes, $content)); + } + + /** + * @param string $template + * + * @return bool + */ + private function templateExists($template) + { + if ($this->templating instanceof EngineInterface) { + try { + return $this->templating->exists($template); + } catch (\InvalidArgumentException $e) { + return false; + } + } + + $loader = $this->templating->getLoader(); + if ($loader instanceof \Twig_ExistsLoaderInterface) { + return $loader->exists($template); + } + + try { + $loader->getSource($template); + + return true; + } catch (\Twig_Error_Loader $e) { + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'hinclude'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php new file mode 100644 index 0000000..a6ab82e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * Implements the inline rendering strategy where the Request is rendered by the current HTTP kernel. + * + * @author Fabien Potencier + */ +class InlineFragmentRenderer extends RoutableFragmentRenderer +{ + private $kernel; + private $dispatcher; + + /** + * Constructor. + * + * @param HttpKernelInterface $kernel A HttpKernelInterface instance + * @param EventDispatcherInterface $dispatcher A EventDispatcherInterface instance + */ + public function __construct(HttpKernelInterface $kernel, EventDispatcherInterface $dispatcher = null) + { + $this->kernel = $kernel; + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + * + * Additional available options: + * + * * alt: an alternative URI to render in case of an error + */ + public function render($uri, Request $request, array $options = array()) + { + $reference = null; + if ($uri instanceof ControllerReference) { + $reference = $uri; + + // Remove attributes from the generated URI because if not, the Symfony + // routing system will use them to populate the Request attributes. We don't + // want that as we want to preserve objects (so we manually set Request attributes + // below instead) + $attributes = $reference->attributes; + $reference->attributes = array(); + + // The request format and locale might have been overridden by the user + foreach (array('_format', '_locale') as $key) { + if (isset($attributes[$key])) { + $reference->attributes[$key] = $attributes[$key]; + } + } + + $uri = $this->generateFragmentUri($uri, $request, false, false); + + $reference->attributes = array_merge($attributes, $reference->attributes); + } + + $subRequest = $this->createSubRequest($uri, $request); + + // override Request attributes as they can be objects (which are not supported by the generated URI) + if (null !== $reference) { + $subRequest->attributes->add($reference->attributes); + } + + $level = ob_get_level(); + try { + return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); + } catch (\Exception $e) { + // we dispatch the exception event to trigger the logging + // the response that comes back is simply ignored + if (isset($options['ignore_errors']) && $options['ignore_errors'] && $this->dispatcher) { + $event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e); + + $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); + } + + // let's clean up the output buffers that were created by the sub-request + Response::closeOutputBuffers($level, false); + + if (isset($options['alt'])) { + $alt = $options['alt']; + unset($options['alt']); + + return $this->render($alt, $request, $options); + } + + if (!isset($options['ignore_errors']) || !$options['ignore_errors']) { + throw $e; + } + + return new Response(); + } + } + + protected function createSubRequest($uri, Request $request) + { + $cookies = $request->cookies->all(); + $server = $request->server->all(); + + // Override the arguments to emulate a sub-request. + // Sub-request object will point to localhost as client ip and real client ip + // will be included into trusted header for client ip + try { + if ($trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) { + $currentXForwardedFor = $request->headers->get($trustedHeaderName, ''); + + $server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); + } + } catch (\InvalidArgumentException $e) { + // Do nothing + } + + $server['REMOTE_ADDR'] = '127.0.0.1'; + + $subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server); + if ($request->headers->has('Surrogate-Capability')) { + $subRequest->headers->set('Surrogate-Capability', $request->headers->get('Surrogate-Capability')); + } + + if ($session = $request->getSession()) { + $subRequest->setSession($session); + } + + return $subRequest; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'inline'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/RoutableFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/RoutableFragmentRenderer.php new file mode 100644 index 0000000..d7eeb89 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/RoutableFragmentRenderer.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\FragmentListener; + +/** + * Adds the possibility to generate a fragment URI for a given Controller. + * + * @author Fabien Potencier + */ +abstract class RoutableFragmentRenderer implements FragmentRendererInterface +{ + private $fragmentPath = '/_fragment'; + + /** + * Sets the fragment path that triggers the fragment listener. + * + * @param string $path The path + * + * @see FragmentListener + */ + public function setFragmentPath($path) + { + $this->fragmentPath = $path; + } + + /** + * Generates a fragment URI for a given controller. + * + * @param ControllerReference $reference A ControllerReference instance + * @param Request $request A Request instance + * @param bool $absolute Whether to generate an absolute URL or not + * @param bool $strict Whether to allow non-scalar attributes or not + * + * @return string A fragment URI + */ + protected function generateFragmentUri(ControllerReference $reference, Request $request, $absolute = false, $strict = true) + { + if ($strict) { + $this->checkNonScalar($reference->attributes); + } + + // We need to forward the current _format and _locale values as we don't have + // a proper routing pattern to do the job for us. + // This makes things inconsistent if you switch from rendering a controller + // to rendering a route if the route pattern does not contain the special + // _format and _locale placeholders. + if (!isset($reference->attributes['_format'])) { + $reference->attributes['_format'] = $request->getRequestFormat(); + } + if (!isset($reference->attributes['_locale'])) { + $reference->attributes['_locale'] = $request->getLocale(); + } + + $reference->attributes['_controller'] = $reference->controller; + + $reference->query['_path'] = http_build_query($reference->attributes, '', '&'); + + $path = $this->fragmentPath.'?'.http_build_query($reference->query, '', '&'); + + if ($absolute) { + return $request->getUriForPath($path); + } + + return $request->getBaseUrl().$path; + } + + private function checkNonScalar($values) + { + foreach ($values as $key => $value) { + if (is_array($value)) { + $this->checkNonScalar($value); + } elseif (!is_scalar($value) && null !== $value) { + throw new \LogicException(sprintf('Controller attributes cannot contain non-scalar/non-null values (value for key "%s" is not a scalar or null).', $key)); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/SsiFragmentRenderer.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/SsiFragmentRenderer.php new file mode 100644 index 0000000..45e7122 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Fragment/SsiFragmentRenderer.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +/** + * Implements the SSI rendering strategy. + * + * @author Sebastian Krebs + */ +class SsiFragmentRenderer extends AbstractSurrogateFragmentRenderer +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ssi'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Esi.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Esi.php new file mode 100644 index 0000000..c12d241 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Esi.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * Esi implements the ESI capabilities to Request and Response instances. + * + * For more information, read the following W3C notes: + * + * * ESI Language Specification 1.0 (http://www.w3.org/TR/esi-lang) + * + * * Edge Architecture Specification (http://www.w3.org/TR/edge-arch) + * + * @author Fabien Potencier + */ +class Esi implements SurrogateInterface +{ + private $contentTypes; + private $phpEscapeMap = array( + array('', '', '', ''), + ); + + /** + * Constructor. + * + * @param array $contentTypes An array of content-type that should be parsed for ESI information. + * (default: text/html, text/xml, application/xhtml+xml, and application/xml) + */ + public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml')) + { + $this->contentTypes = $contentTypes; + } + + public function getName() + { + return 'esi'; + } + + /** + * Returns a new cache strategy instance. + * + * @return ResponseCacheStrategyInterface A ResponseCacheStrategyInterface instance + */ + public function createCacheStrategy() + { + return new ResponseCacheStrategy(); + } + + /** + * Checks that at least one surrogate has ESI/1.0 capability. + * + * @param Request $request A Request instance + * + * @return bool true if one surrogate has ESI/1.0 capability, false otherwise + */ + public function hasSurrogateCapability(Request $request) + { + if (null === $value = $request->headers->get('Surrogate-Capability')) { + return false; + } + + return false !== strpos($value, 'ESI/1.0'); + } + + /** + * Adds ESI/1.0 capability to the given Request. + * + * @param Request $request A Request instance + */ + public function addSurrogateCapability(Request $request) + { + $current = $request->headers->get('Surrogate-Capability'); + $new = 'symfony2="ESI/1.0"'; + + $request->headers->set('Surrogate-Capability', $current ? $current.', '.$new : $new); + } + + /** + * Adds HTTP headers to specify that the Response needs to be parsed for ESI. + * + * This method only adds an ESI HTTP header if the Response has some ESI tags. + * + * @param Response $response A Response instance + */ + public function addSurrogateControl(Response $response) + { + if (false !== strpos($response->getContent(), 'headers->set('Surrogate-Control', 'content="ESI/1.0"'); + } + } + + /** + * Checks that the Response needs to be parsed for ESI tags. + * + * @param Response $response A Response instance + * + * @return bool true if the Response needs to be parsed, false otherwise + */ + public function needsParsing(Response $response) + { + if (!$control = $response->headers->get('Surrogate-Control')) { + return false; + } + + return (bool) preg_match('#content="[^"]*ESI/1.0[^"]*"#', $control); + } + + /** + * Renders an ESI tag. + * + * @param string $uri A URI + * @param string $alt An alternate URI + * @param bool $ignoreErrors Whether to ignore errors or not + * @param string $comment A comment to add as an esi:include tag + * + * @return string + */ + public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = '') + { + $html = sprintf('', + $uri, + $ignoreErrors ? ' onerror="continue"' : '', + $alt ? sprintf(' alt="%s"', $alt) : '' + ); + + if (!empty($comment)) { + return sprintf("\n%s", $comment, $html); + } + + return $html; + } + + /** + * Replaces a Response ESI tags with the included resource content. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * + * @return Response + */ + public function process(Request $request, Response $response) + { + $type = $response->headers->get('Content-Type'); + if (empty($type)) { + $type = 'text/html'; + } + + $parts = explode(';', $type); + if (!in_array($parts[0], $this->contentTypes)) { + return $response; + } + + // we don't use a proper XML parser here as we can have ESI tags in a plain text response + $content = $response->getContent(); + $content = preg_replace('#.*?#s', '', $content); + $content = preg_replace('#]+>#s', '', $content); + + $chunks = preg_split('##', $content, -1, PREG_SPLIT_DELIM_CAPTURE); + $chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]); + + $i = 1; + while (isset($chunks[$i])) { + $options = array(); + preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER); + foreach ($matches as $set) { + $options[$set[1]] = $set[2]; + } + + if (!isset($options['src'])) { + throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.'); + } + + $chunks[$i] = sprintf('surrogate->handle($this, %s, %s, %s) ?>'."\n", + var_export($options['src'], true), + var_export(isset($options['alt']) ? $options['alt'] : '', true), + isset($options['onerror']) && 'continue' === $options['onerror'] ? 'true' : 'false' + ); + ++$i; + $chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]); + ++$i; + } + $content = implode('', $chunks); + + $response->setContent($content); + $response->headers->set('X-Body-Eval', 'ESI'); + + // remove ESI/1.0 from the Surrogate-Control header + if ($response->headers->has('Surrogate-Control')) { + $value = $response->headers->get('Surrogate-Control'); + if ('content="ESI/1.0"' == $value) { + $response->headers->remove('Surrogate-Control'); + } elseif (preg_match('#,\s*content="ESI/1.0"#', $value)) { + $response->headers->set('Surrogate-Control', preg_replace('#,\s*content="ESI/1.0"#', '', $value)); + } elseif (preg_match('#content="ESI/1.0",\s*#', $value)) { + $response->headers->set('Surrogate-Control', preg_replace('#content="ESI/1.0",\s*#', '', $value)); + } + } + } + + /** + * Handles an ESI from the cache. + * + * @param HttpCache $cache An HttpCache instance + * @param string $uri The main URI + * @param string $alt An alternative URI + * @param bool $ignoreErrors Whether to ignore errors or not + * + * @return string + * + * @throws \RuntimeException + * @throws \Exception + */ + public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors) + { + $subRequest = Request::create($uri, 'get', array(), $cache->getRequest()->cookies->all(), array(), $cache->getRequest()->server->all()); + + try { + $response = $cache->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true); + + if (!$response->isSuccessful()) { + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $subRequest->getUri(), $response->getStatusCode())); + } + + return $response->getContent(); + } catch (\Exception $e) { + if ($alt) { + return $this->handle($cache, $alt, '', $ignoreErrors); + } + + if (!$ignoreErrors) { + throw $e; + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php new file mode 100644 index 0000000..a66c9d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -0,0 +1,684 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\TerminableInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Cache provides HTTP caching. + * + * @author Fabien Potencier + */ +class HttpCache implements HttpKernelInterface, TerminableInterface +{ + private $kernel; + private $store; + private $request; + private $surrogate; + private $surrogateCacheStrategy; + private $options = array(); + private $traces = array(); + + /** + * Constructor. + * + * The available options are: + * + * * debug: If true, the traces are added as a HTTP header to ease debugging + * + * * default_ttl The number of seconds that a cache entry should be considered + * fresh when no explicit freshness information is provided in + * a response. Explicit Cache-Control or Expires headers + * override this value. (default: 0) + * + * * private_headers Set of request headers that trigger "private" cache-control behavior + * on responses that don't explicitly state whether the response is + * public or private via a Cache-Control directive. (default: Authorization and Cookie) + * + * * allow_reload Specifies whether the client can force a cache reload by including a + * Cache-Control "no-cache" directive in the request. Set it to ``true`` + * for compliance with RFC 2616. (default: false) + * + * * allow_revalidate Specifies whether the client can force a cache revalidate by including + * a Cache-Control "max-age=0" directive in the request. Set it to ``true`` + * for compliance with RFC 2616. (default: false) + * + * * stale_while_revalidate Specifies the default number of seconds (the granularity is the second as the + * Response TTL precision is a second) during which the cache can immediately return + * a stale response while it revalidates it in the background (default: 2). + * This setting is overridden by the stale-while-revalidate HTTP Cache-Control + * extension (see RFC 5861). + * + * * stale_if_error Specifies the default number of seconds (the granularity is the second) during which + * the cache can serve a stale response when an error is encountered (default: 60). + * This setting is overridden by the stale-if-error HTTP Cache-Control extension + * (see RFC 5861). + * + * @param HttpKernelInterface $kernel An HttpKernelInterface instance + * @param StoreInterface $store A Store instance + * @param SurrogateInterface $surrogate A SurrogateInterface instance + * @param array $options An array of options + */ + public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = array()) + { + $this->store = $store; + $this->kernel = $kernel; + $this->surrogate = $surrogate; + + // needed in case there is a fatal error because the backend is too slow to respond + register_shutdown_function(array($this->store, 'cleanup')); + + $this->options = array_merge(array( + 'debug' => false, + 'default_ttl' => 0, + 'private_headers' => array('Authorization', 'Cookie'), + 'allow_reload' => false, + 'allow_revalidate' => false, + 'stale_while_revalidate' => 2, + 'stale_if_error' => 60, + ), $options); + } + + /** + * Gets the current store. + * + * @return StoreInterface $store A StoreInterface instance + */ + public function getStore() + { + return $this->store; + } + + /** + * Returns an array of events that took place during processing of the last request. + * + * @return array An array of events + */ + public function getTraces() + { + return $this->traces; + } + + /** + * Returns a log message for the events of the last request processing. + * + * @return string A log message + */ + public function getLog() + { + $log = array(); + foreach ($this->traces as $request => $traces) { + $log[] = sprintf('%s: %s', $request, implode(', ', $traces)); + } + + return implode('; ', $log); + } + + /** + * Gets the Request instance associated with the master request. + * + * @return Request A Request instance + */ + public function getRequest() + { + return $this->request; + } + + /** + * Gets the Kernel instance. + * + * @return HttpKernelInterface An HttpKernelInterface instance + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Gets the Surrogate instance. + * + * @return SurrogateInterface A Surrogate instance + */ + public function getSurrogate() + { + return $this->surrogate; + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + // FIXME: catch exceptions and implement a 500 error page here? -> in Varnish, there is a built-in error page mechanism + if (HttpKernelInterface::MASTER_REQUEST === $type) { + $this->traces = array(); + $this->request = $request; + if (null !== $this->surrogate) { + $this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy(); + } + } + + $path = $request->getPathInfo(); + if ($qs = $request->getQueryString()) { + $path .= '?'.$qs; + } + $this->traces[$request->getMethod().' '.$path] = array(); + + if (!$request->isMethodSafe()) { + $response = $this->invalidate($request, $catch); + } elseif ($request->headers->has('expect')) { + $response = $this->pass($request, $catch); + } else { + $response = $this->lookup($request, $catch); + } + + $this->restoreResponseBody($request, $response); + + $response->setDate(\DateTime::createFromFormat('U', time(), new \DateTimeZone('UTC'))); + + if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) { + $response->headers->set('X-Symfony-Cache', $this->getLog()); + } + + if (null !== $this->surrogate) { + if (HttpKernelInterface::MASTER_REQUEST === $type) { + $this->surrogateCacheStrategy->update($response); + } else { + $this->surrogateCacheStrategy->add($response); + } + } + + $response->prepare($request); + + $response->isNotModified($request); + + return $response; + } + + /** + * {@inheritdoc} + */ + public function terminate(Request $request, Response $response) + { + if ($this->getKernel() instanceof TerminableInterface) { + $this->getKernel()->terminate($request, $response); + } + } + + /** + * Forwards the Request to the backend without storing the Response in the cache. + * + * @param Request $request A Request instance + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + */ + protected function pass(Request $request, $catch = false) + { + $this->record($request, 'pass'); + + return $this->forward($request, $catch); + } + + /** + * Invalidates non-safe methods (like POST, PUT, and DELETE). + * + * @param Request $request A Request instance + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + * + * @throws \Exception + * + * @see RFC2616 13.10 + */ + protected function invalidate(Request $request, $catch = false) + { + $response = $this->pass($request, $catch); + + // invalidate only when the response is successful + if ($response->isSuccessful() || $response->isRedirect()) { + try { + $this->store->invalidate($request, $catch); + + // As per the RFC, invalidate Location and Content-Location URLs if present + foreach (array('Location', 'Content-Location') as $header) { + if ($uri = $response->headers->get($header)) { + $subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all()); + + $this->store->invalidate($subRequest); + } + } + + $this->record($request, 'invalidate'); + } catch (\Exception $e) { + $this->record($request, 'invalidate-failed'); + + if ($this->options['debug']) { + throw $e; + } + } + } + + return $response; + } + + /** + * Lookups a Response from the cache for the given Request. + * + * When a matching cache entry is found and is fresh, it uses it as the + * response without forwarding any request to the backend. When a matching + * cache entry is found but is stale, it attempts to "validate" the entry with + * the backend using conditional GET. When no matching cache entry is found, + * it triggers "miss" processing. + * + * @param Request $request A Request instance + * @param bool $catch whether to process exceptions + * + * @return Response A Response instance + * + * @throws \Exception + */ + protected function lookup(Request $request, $catch = false) + { + // if allow_reload and no-cache Cache-Control, allow a cache reload + if ($this->options['allow_reload'] && $request->isNoCache()) { + $this->record($request, 'reload'); + + return $this->fetch($request, $catch); + } + + try { + $entry = $this->store->lookup($request); + } catch (\Exception $e) { + $this->record($request, 'lookup-failed'); + + if ($this->options['debug']) { + throw $e; + } + + return $this->pass($request, $catch); + } + + if (null === $entry) { + $this->record($request, 'miss'); + + return $this->fetch($request, $catch); + } + + if (!$this->isFreshEnough($request, $entry)) { + $this->record($request, 'stale'); + + return $this->validate($request, $entry, $catch); + } + + $this->record($request, 'fresh'); + + $entry->headers->set('Age', $entry->getAge()); + + return $entry; + } + + /** + * Validates that a cache entry is fresh. + * + * The original request is used as a template for a conditional + * GET request with the backend. + * + * @param Request $request A Request instance + * @param Response $entry A Response instance to validate + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + */ + protected function validate(Request $request, Response $entry, $catch = false) + { + $subRequest = clone $request; + + // send no head requests because we want content + $subRequest->setMethod('GET'); + + // add our cached last-modified validator + $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + + // Add our cached etag validator to the environment. + // We keep the etags from the client to handle the case when the client + // has a different private valid entry which is not cached here. + $cachedEtags = $entry->getEtag() ? array($entry->getEtag()) : array(); + $requestEtags = $request->getETags(); + if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) { + $subRequest->headers->set('if_none_match', implode(', ', $etags)); + } + + $response = $this->forward($subRequest, $catch, $entry); + + if (304 == $response->getStatusCode()) { + $this->record($request, 'valid'); + + // return the response and not the cache entry if the response is valid but not cached + $etag = $response->getEtag(); + if ($etag && in_array($etag, $requestEtags) && !in_array($etag, $cachedEtags)) { + return $response; + } + + $entry = clone $entry; + $entry->headers->remove('Date'); + + foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) { + if ($response->headers->has($name)) { + $entry->headers->set($name, $response->headers->get($name)); + } + } + + $response = $entry; + } else { + $this->record($request, 'invalid'); + } + + if ($response->isCacheable()) { + $this->store($request, $response); + } + + return $response; + } + + /** + * Forwards the Request to the backend and determines whether the response should be stored. + * + * This methods is triggered when the cache missed or a reload is required. + * + * @param Request $request A Request instance + * @param bool $catch whether to process exceptions + * + * @return Response A Response instance + */ + protected function fetch(Request $request, $catch = false) + { + $subRequest = clone $request; + + // send no head requests because we want content + $subRequest->setMethod('GET'); + + // avoid that the backend sends no content + $subRequest->headers->remove('if_modified_since'); + $subRequest->headers->remove('if_none_match'); + + $response = $this->forward($subRequest, $catch); + + if ($response->isCacheable()) { + $this->store($request, $response); + } + + return $response; + } + + /** + * Forwards the Request to the backend and returns the Response. + * + * @param Request $request A Request instance + * @param bool $catch Whether to catch exceptions or not + * @param Response $entry A Response instance (the stale entry if present, null otherwise) + * + * @return Response A Response instance + */ + protected function forward(Request $request, $catch = false, Response $entry = null) + { + if ($this->surrogate) { + $this->surrogate->addSurrogateCapability($request); + } + + // modify the X-Forwarded-For header if needed + $forwardedFor = $request->headers->get('X-Forwarded-For'); + if ($forwardedFor) { + $request->headers->set('X-Forwarded-For', $forwardedFor.', '.$request->server->get('REMOTE_ADDR')); + } else { + $request->headers->set('X-Forwarded-For', $request->server->get('REMOTE_ADDR')); + } + + // fix the client IP address by setting it to 127.0.0.1 as HttpCache + // is always called from the same process as the backend. + $request->server->set('REMOTE_ADDR', '127.0.0.1'); + + // make sure HttpCache is a trusted proxy + if (!in_array('127.0.0.1', $trustedProxies = Request::getTrustedProxies())) { + $trustedProxies[] = '127.0.0.1'; + Request::setTrustedProxies($trustedProxies); + } + + // always a "master" request (as the real master request can be in cache) + $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $catch); + // FIXME: we probably need to also catch exceptions if raw === true + + // we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC + if (null !== $entry && in_array($response->getStatusCode(), array(500, 502, 503, 504))) { + if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) { + $age = $this->options['stale_if_error']; + } + + if (abs($entry->getTtl()) < $age) { + $this->record($request, 'stale-if-error'); + + return $entry; + } + } + + $this->processResponseBody($request, $response); + + if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) { + $response->setPrivate(true); + } elseif ($this->options['default_ttl'] > 0 && null === $response->getTtl() && !$response->headers->getCacheControlDirective('must-revalidate')) { + $response->setTtl($this->options['default_ttl']); + } + + return $response; + } + + /** + * Checks whether the cache entry is "fresh enough" to satisfy the Request. + * + * @param Request $request A Request instance + * @param Response $entry A Response instance + * + * @return bool true if the cache entry if fresh enough, false otherwise + */ + protected function isFreshEnough(Request $request, Response $entry) + { + if (!$entry->isFresh()) { + return $this->lock($request, $entry); + } + + if ($this->options['allow_revalidate'] && null !== $maxAge = $request->headers->getCacheControlDirective('max-age')) { + return $maxAge > 0 && $maxAge >= $entry->getAge(); + } + + return true; + } + + /** + * Locks a Request during the call to the backend. + * + * @param Request $request A Request instance + * @param Response $entry A Response instance + * + * @return bool true if the cache entry can be returned even if it is staled, false otherwise + */ + protected function lock(Request $request, Response $entry) + { + // try to acquire a lock to call the backend + $lock = $this->store->lock($request); + + // there is already another process calling the backend + if (true !== $lock) { + // check if we can serve the stale entry + if (null === $age = $entry->headers->getCacheControlDirective('stale-while-revalidate')) { + $age = $this->options['stale_while_revalidate']; + } + + if (abs($entry->getTtl()) < $age) { + $this->record($request, 'stale-while-revalidate'); + + // server the stale response while there is a revalidation + return true; + } + + // wait for the lock to be released + $wait = 0; + while ($this->store->isLocked($request) && $wait < 5000000) { + usleep(50000); + $wait += 50000; + } + + if ($wait < 2000000) { + // replace the current entry with the fresh one + $new = $this->lookup($request); + $entry->headers = $new->headers; + $entry->setContent($new->getContent()); + $entry->setStatusCode($new->getStatusCode()); + $entry->setProtocolVersion($new->getProtocolVersion()); + foreach ($new->headers->getCookies() as $cookie) { + $entry->headers->setCookie($cookie); + } + } else { + // backend is slow as hell, send a 503 response (to avoid the dog pile effect) + $entry->setStatusCode(503); + $entry->setContent('503 Service Unavailable'); + $entry->headers->set('Retry-After', 10); + } + + return true; + } + + // we have the lock, call the backend + return false; + } + + /** + * Writes the Response to the cache. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * + * @throws \Exception + */ + protected function store(Request $request, Response $response) + { + try { + $this->store->write($request, $response); + + $this->record($request, 'store'); + + $response->headers->set('Age', $response->getAge()); + } catch (\Exception $e) { + $this->record($request, 'store-failed'); + + if ($this->options['debug']) { + throw $e; + } + } + + // now that the response is cached, release the lock + $this->store->unlock($request); + } + + /** + * Restores the Response body. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + */ + private function restoreResponseBody(Request $request, Response $response) + { + if ($request->isMethod('HEAD') || 304 === $response->getStatusCode()) { + $response->setContent(null); + $response->headers->remove('X-Body-Eval'); + $response->headers->remove('X-Body-File'); + + return; + } + + if ($response->headers->has('X-Body-Eval')) { + ob_start(); + + if ($response->headers->has('X-Body-File')) { + include $response->headers->get('X-Body-File'); + } else { + eval('; ?>'.$response->getContent().'setContent(ob_get_clean()); + $response->headers->remove('X-Body-Eval'); + if (!$response->headers->has('Transfer-Encoding')) { + $response->headers->set('Content-Length', strlen($response->getContent())); + } + } elseif ($response->headers->has('X-Body-File')) { + $response->setContent(file_get_contents($response->headers->get('X-Body-File'))); + } else { + return; + } + + $response->headers->remove('X-Body-File'); + } + + protected function processResponseBody(Request $request, Response $response) + { + if (null !== $this->surrogate && $this->surrogate->needsParsing($response)) { + $this->surrogate->process($request, $response); + } + } + + /** + * Checks if the Request includes authorization or other sensitive information + * that should cause the Response to be considered private by default. + * + * @param Request $request A Request instance + * + * @return bool true if the Request is private, false otherwise + */ + private function isPrivateRequest(Request $request) + { + foreach ($this->options['private_headers'] as $key) { + $key = strtolower(str_replace('HTTP_', '', $key)); + + if ('cookie' === $key) { + if (count($request->cookies->all())) { + return true; + } + } elseif ($request->headers->has($key)) { + return true; + } + } + + return false; + } + + /** + * Records that an event took place. + * + * @param Request $request A Request instance + * @param string $event The event name + */ + private function record(Request $request, $event) + { + $path = $request->getPathInfo(); + if ($qs = $request->getQueryString()) { + $path .= '?'.$qs; + } + $this->traces[$request->getMethod().' '.$path][] = $event; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php new file mode 100644 index 0000000..67ffd97 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php @@ -0,0 +1,85 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Response; + +/** + * ResponseCacheStrategy knows how to compute the Response cache HTTP header + * based on the different response cache headers. + * + * This implementation changes the master response TTL to the smallest TTL received + * or force validation if one of the surrogates has validation cache strategy. + * + * @author Fabien Potencier + */ +class ResponseCacheStrategy implements ResponseCacheStrategyInterface +{ + private $cacheable = true; + private $embeddedResponses = 0; + private $ttls = array(); + private $maxAges = array(); + + /** + * {@inheritdoc} + */ + public function add(Response $response) + { + if ($response->isValidateable()) { + $this->cacheable = false; + } else { + $this->ttls[] = $response->getTtl(); + $this->maxAges[] = $response->getMaxAge(); + } + + ++$this->embeddedResponses; + } + + /** + * {@inheritdoc} + */ + public function update(Response $response) + { + // if we have no embedded Response, do nothing + if (0 === $this->embeddedResponses) { + return; + } + + // Remove validation related headers in order to avoid browsers using + // their own cache, because some of the response content comes from + // at least one embedded response (which likely has a different caching strategy). + if ($response->isValidateable()) { + $response->setEtag(null); + $response->setLastModified(null); + $this->cacheable = false; + } + + if (!$this->cacheable) { + $response->headers->set('Cache-Control', 'no-cache, must-revalidate'); + + return; + } + + $this->ttls[] = $response->getTtl(); + $this->maxAges[] = $response->getMaxAge(); + + if (null !== $maxAge = min($this->maxAges)) { + $response->setSharedMaxAge($maxAge); + $response->headers->set('Age', $maxAge - min($this->ttls)); + } + $response->setMaxAge(0); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategyInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategyInterface.php new file mode 100644 index 0000000..d70c2e0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategyInterface.php @@ -0,0 +1,41 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Response; + +/** + * ResponseCacheStrategyInterface implementations know how to compute the + * Response cache HTTP header based on the different response cache headers. + * + * @author Fabien Potencier + */ +interface ResponseCacheStrategyInterface +{ + /** + * Adds a Response. + * + * @param Response $response + */ + public function add(Response $response); + + /** + * Updates the Response HTTP headers based on the embedded Responses. + * + * @param Response $response + */ + public function update(Response $response); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Ssi.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Ssi.php new file mode 100644 index 0000000..43311b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Ssi.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * Ssi implements the SSI capabilities to Request and Response instances. + * + * @author Sebastian Krebs + */ +class Ssi implements SurrogateInterface +{ + private $contentTypes; + private $phpEscapeMap = array( + array('', '', '', ''), + ); + + /** + * Constructor. + * + * @param array $contentTypes An array of content-type that should be parsed for SSI information. + * (default: text/html, text/xml, application/xhtml+xml, and application/xml) + */ + public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml')) + { + $this->contentTypes = $contentTypes; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ssi'; + } + + /** + * {@inheritdoc} + */ + public function createCacheStrategy() + { + return new ResponseCacheStrategy(); + } + + /** + * {@inheritdoc} + */ + public function hasSurrogateCapability(Request $request) + { + if (null === $value = $request->headers->get('Surrogate-Capability')) { + return false; + } + + return false !== strpos($value, 'SSI/1.0'); + } + + /** + * {@inheritdoc} + */ + public function addSurrogateCapability(Request $request) + { + $current = $request->headers->get('Surrogate-Capability'); + $new = 'symfony2="SSI/1.0"'; + + $request->headers->set('Surrogate-Capability', $current ? $current.', '.$new : $new); + } + + /** + * {@inheritdoc} + */ + public function addSurrogateControl(Response $response) + { + if (false !== strpos($response->getContent(), '', $uri); + } + + /** + * {@inheritdoc} + */ + public function process(Request $request, Response $response) + { + $type = $response->headers->get('Content-Type'); + if (empty($type)) { + $type = 'text/html'; + } + + $parts = explode(';', $type); + if (!in_array($parts[0], $this->contentTypes)) { + return $response; + } + + // we don't use a proper XML parser here as we can have SSI tags in a plain text response + $content = $response->getContent(); + + $chunks = preg_split('##', $content, -1, PREG_SPLIT_DELIM_CAPTURE); + $chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]); + + $i = 1; + while (isset($chunks[$i])) { + $options = array(); + preg_match_all('/(virtual)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER); + foreach ($matches as $set) { + $options[$set[1]] = $set[2]; + } + + if (!isset($options['virtual'])) { + throw new \RuntimeException('Unable to process an SSI tag without a "virtual" attribute.'); + } + + $chunks[$i] = sprintf('surrogate->handle($this, %s, \'\', false) ?>'."\n", + var_export($options['virtual'], true) + ); + ++$i; + $chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]); + ++$i; + } + $content = implode('', $chunks); + + $response->setContent($content); + $response->headers->set('X-Body-Eval', 'SSI'); + + // remove SSI/1.0 from the Surrogate-Control header + if ($response->headers->has('Surrogate-Control')) { + $value = $response->headers->get('Surrogate-Control'); + if ('content="SSI/1.0"' == $value) { + $response->headers->remove('Surrogate-Control'); + } elseif (preg_match('#,\s*content="SSI/1.0"#', $value)) { + $response->headers->set('Surrogate-Control', preg_replace('#,\s*content="SSI/1.0"#', '', $value)); + } elseif (preg_match('#content="SSI/1.0",\s*#', $value)) { + $response->headers->set('Surrogate-Control', preg_replace('#content="SSI/1.0",\s*#', '', $value)); + } + } + } + + /** + * {@inheritdoc} + */ + public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors) + { + $subRequest = Request::create($uri, 'get', array(), $cache->getRequest()->cookies->all(), array(), $cache->getRequest()->server->all()); + + try { + $response = $cache->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true); + + if (!$response->isSuccessful()) { + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $subRequest->getUri(), $response->getStatusCode())); + } + + return $response->getContent(); + } catch (\Exception $e) { + if ($alt) { + return $this->handle($cache, $alt, '', $ignoreErrors); + } + + if (!$ignoreErrors) { + throw $e; + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Store.php new file mode 100644 index 0000000..463caa0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -0,0 +1,456 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Store implements all the logic for storing cache metadata (Request and Response headers). + * + * @author Fabien Potencier + */ +class Store implements StoreInterface +{ + protected $root; + private $keyCache; + private $locks; + + /** + * Constructor. + * + * @param string $root The path to the cache directory + * + * @throws \RuntimeException + */ + public function __construct($root) + { + $this->root = $root; + if (!is_dir($this->root)) { + if (false === @mkdir($this->root, 0777, true) && !is_dir($this->root)) { + throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root)); + } + } + $this->keyCache = new \SplObjectStorage(); + $this->locks = array(); + } + + /** + * Cleanups storage. + */ + public function cleanup() + { + // unlock everything + foreach ($this->locks as $lock) { + if (file_exists($lock)) { + @unlink($lock); + } + } + + $error = error_get_last(); + if (1 === $error['type'] && false === headers_sent()) { + // send a 503 + header('HTTP/1.0 503 Service Unavailable'); + header('Retry-After: 10'); + echo '503 Service Unavailable'; + } + } + + /** + * Locks the cache for a given Request. + * + * @param Request $request A Request instance + * + * @return bool|string true if the lock is acquired, the path to the current lock otherwise + */ + public function lock(Request $request) + { + $path = $this->getPath($this->getCacheKey($request).'.lck'); + if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { + return false; + } + + $lock = @fopen($path, 'x'); + if (false !== $lock) { + fclose($lock); + + $this->locks[] = $path; + + return true; + } + + return !file_exists($path) ?: $path; + } + + /** + * Releases the lock for the given Request. + * + * @param Request $request A Request instance + * + * @return bool False if the lock file does not exist or cannot be unlocked, true otherwise + */ + public function unlock(Request $request) + { + $file = $this->getPath($this->getCacheKey($request).'.lck'); + + return is_file($file) ? @unlink($file) : false; + } + + public function isLocked(Request $request) + { + $path = $this->getPath($this->getCacheKey($request).'.lck'); + clearstatcache(true, $path); + + return is_file($path); + } + + /** + * Locates a cached Response for the Request provided. + * + * @param Request $request A Request instance + * + * @return Response|null A Response instance, or null if no cache entry was found + */ + public function lookup(Request $request) + { + $key = $this->getCacheKey($request); + + if (!$entries = $this->getMetadata($key)) { + return; + } + + // find a cached entry that matches the request. + $match = null; + foreach ($entries as $entry) { + if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? implode(', ', $entry[1]['vary']) : '', $request->headers->all(), $entry[0])) { + $match = $entry; + + break; + } + } + + if (null === $match) { + return; + } + + list($req, $headers) = $match; + if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) { + return $this->restoreResponse($headers, $body); + } + + // TODO the metaStore referenced an entity that doesn't exist in + // the entityStore. We definitely want to return nil but we should + // also purge the entry from the meta-store when this is detected. + } + + /** + * Writes a cache entry to the store for the given Request and Response. + * + * Existing entries are read and any that match the response are removed. This + * method calls write with the new list of cache entries. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * + * @return string The key under which the response is stored + * + * @throws \RuntimeException + */ + public function write(Request $request, Response $response) + { + $key = $this->getCacheKey($request); + $storedEnv = $this->persistRequest($request); + + // write the response body to the entity store if this is the original response + if (!$response->headers->has('X-Content-Digest')) { + $digest = $this->generateContentDigest($response); + + if (false === $this->save($digest, $response->getContent())) { + throw new \RuntimeException('Unable to store the entity.'); + } + + $response->headers->set('X-Content-Digest', $digest); + + if (!$response->headers->has('Transfer-Encoding')) { + $response->headers->set('Content-Length', strlen($response->getContent())); + } + } + + // read existing cache entries, remove non-varying, and add this one to the list + $entries = array(); + $vary = $response->headers->get('vary'); + foreach ($this->getMetadata($key) as $entry) { + if (!isset($entry[1]['vary'][0])) { + $entry[1]['vary'] = array(''); + } + + if ($vary != $entry[1]['vary'][0] || !$this->requestsMatch($vary, $entry[0], $storedEnv)) { + $entries[] = $entry; + } + } + + $headers = $this->persistResponse($response); + unset($headers['age']); + + array_unshift($entries, array($storedEnv, $headers)); + + if (false === $this->save($key, serialize($entries))) { + throw new \RuntimeException('Unable to store the metadata.'); + } + + return $key; + } + + /** + * Returns content digest for $response. + * + * @param Response $response + * + * @return string + */ + protected function generateContentDigest(Response $response) + { + return 'en'.hash('sha256', $response->getContent()); + } + + /** + * Invalidates all cache entries that match the request. + * + * @param Request $request A Request instance + * + * @throws \RuntimeException + */ + public function invalidate(Request $request) + { + $modified = false; + $key = $this->getCacheKey($request); + + $entries = array(); + foreach ($this->getMetadata($key) as $entry) { + $response = $this->restoreResponse($entry[1]); + if ($response->isFresh()) { + $response->expire(); + $modified = true; + $entries[] = array($entry[0], $this->persistResponse($response)); + } else { + $entries[] = $entry; + } + } + + if ($modified) { + if (false === $this->save($key, serialize($entries))) { + throw new \RuntimeException('Unable to store the metadata.'); + } + } + } + + /** + * Determines whether two Request HTTP header sets are non-varying based on + * the vary response header value provided. + * + * @param string $vary A Response vary header + * @param array $env1 A Request HTTP header array + * @param array $env2 A Request HTTP header array + * + * @return bool true if the two environments match, false otherwise + */ + private function requestsMatch($vary, $env1, $env2) + { + if (empty($vary)) { + return true; + } + + foreach (preg_split('/[\s,]+/', $vary) as $header) { + $key = strtr(strtolower($header), '_', '-'); + $v1 = isset($env1[$key]) ? $env1[$key] : null; + $v2 = isset($env2[$key]) ? $env2[$key] : null; + if ($v1 !== $v2) { + return false; + } + } + + return true; + } + + /** + * Gets all data associated with the given key. + * + * Use this method only if you know what you are doing. + * + * @param string $key The store key + * + * @return array An array of data associated with the key + */ + private function getMetadata($key) + { + if (false === $entries = $this->load($key)) { + return array(); + } + + return unserialize($entries); + } + + /** + * Purges data for the given URL. + * + * @param string $url A URL + * + * @return bool true if the URL exists and has been purged, false otherwise + */ + public function purge($url) + { + if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) { + unlink($path); + + return true; + } + + return false; + } + + /** + * Loads data for the given key. + * + * @param string $key The store key + * + * @return string The data associated with the key + */ + private function load($key) + { + $path = $this->getPath($key); + + return is_file($path) ? file_get_contents($path) : false; + } + + /** + * Save data for the given key. + * + * @param string $key The store key + * @param string $data The data to store + * + * @return bool + */ + private function save($key, $data) + { + $path = $this->getPath($key); + if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { + return false; + } + + $tmpFile = tempnam(dirname($path), basename($path)); + if (false === $fp = @fopen($tmpFile, 'wb')) { + return false; + } + @fwrite($fp, $data); + @fclose($fp); + + if ($data != file_get_contents($tmpFile)) { + return false; + } + + if (false === @rename($tmpFile, $path)) { + return false; + } + + @chmod($path, 0666 & ~umask()); + } + + public function getPath($key) + { + return $this->root.DIRECTORY_SEPARATOR.substr($key, 0, 2).DIRECTORY_SEPARATOR.substr($key, 2, 2).DIRECTORY_SEPARATOR.substr($key, 4, 2).DIRECTORY_SEPARATOR.substr($key, 6); + } + + /** + * Generates a cache key for the given Request. + * + * This method should return a key that must only depend on a + * normalized version of the request URI. + * + * If the same URI can have more than one representation, based on some + * headers, use a Vary header to indicate them, and each representation will + * be stored independently under the same cache key. + * + * @param Request $request A Request instance + * + * @return string A key for the given Request + */ + protected function generateCacheKey(Request $request) + { + return 'md'.hash('sha256', $request->getUri()); + } + + /** + * Returns a cache key for the given Request. + * + * @param Request $request A Request instance + * + * @return string A key for the given Request + */ + private function getCacheKey(Request $request) + { + if (isset($this->keyCache[$request])) { + return $this->keyCache[$request]; + } + + return $this->keyCache[$request] = $this->generateCacheKey($request); + } + + /** + * Persists the Request HTTP headers. + * + * @param Request $request A Request instance + * + * @return array An array of HTTP headers + */ + private function persistRequest(Request $request) + { + return $request->headers->all(); + } + + /** + * Persists the Response HTTP headers. + * + * @param Response $response A Response instance + * + * @return array An array of HTTP headers + */ + private function persistResponse(Response $response) + { + $headers = $response->headers->all(); + $headers['X-Status'] = array($response->getStatusCode()); + + return $headers; + } + + /** + * Restores a Response from the HTTP headers and body. + * + * @param array $headers An array of HTTP headers for the Response + * @param string $body The Response body + * + * @return Response + */ + private function restoreResponse($headers, $body = null) + { + $status = $headers['X-Status'][0]; + unset($headers['X-Status']); + + if (null !== $body) { + $headers['X-Body-File'] = array($body); + } + + return new Response($body, $status, $headers); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/StoreInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/StoreInterface.php new file mode 100644 index 0000000..ddc0c04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/StoreInterface.php @@ -0,0 +1,96 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Interface implemented by HTTP cache stores. + * + * @author Fabien Potencier + */ +interface StoreInterface +{ + /** + * Locates a cached Response for the Request provided. + * + * @param Request $request A Request instance + * + * @return Response|null A Response instance, or null if no cache entry was found + */ + public function lookup(Request $request); + + /** + * Writes a cache entry to the store for the given Request and Response. + * + * Existing entries are read and any that match the response are removed. This + * method calls write with the new list of cache entries. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * + * @return string The key under which the response is stored + */ + public function write(Request $request, Response $response); + + /** + * Invalidates all cache entries that match the request. + * + * @param Request $request A Request instance + */ + public function invalidate(Request $request); + + /** + * Locks the cache for a given Request. + * + * @param Request $request A Request instance + * + * @return bool|string true if the lock is acquired, the path to the current lock otherwise + */ + public function lock(Request $request); + + /** + * Releases the lock for the given Request. + * + * @param Request $request A Request instance + * + * @return bool False if the lock file does not exist or cannot be unlocked, true otherwise + */ + public function unlock(Request $request); + + /** + * Returns whether or not a lock exists. + * + * @param Request $request A Request instance + * + * @return bool true if lock exists, false otherwise + */ + public function isLocked(Request $request); + + /** + * Purges data for the given URL. + * + * @param string $url A URL + * + * @return bool true if the URL exists and has been purged, false otherwise + */ + public function purge($url); + + /** + * Cleanups storage. + */ + public function cleanup(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php new file mode 100644 index 0000000..5d65fd6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpCache/SurrogateInterface.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +interface SurrogateInterface +{ + /** + * Returns surrogate name. + * + * @return string + */ + public function getName(); + + /** + * Returns a new cache strategy instance. + * + * @return ResponseCacheStrategyInterface A ResponseCacheStrategyInterface instance + */ + public function createCacheStrategy(); + + /** + * Checks that at least one surrogate has Surrogate capability. + * + * @param Request $request A Request instance + * + * @return bool true if one surrogate has Surrogate capability, false otherwise + */ + public function hasSurrogateCapability(Request $request); + + /** + * Adds Surrogate-capability to the given Request. + * + * @param Request $request A Request instance + */ + public function addSurrogateCapability(Request $request); + + /** + * Adds HTTP headers to specify that the Response needs to be parsed for Surrogate. + * + * This method only adds an Surrogate HTTP header if the Response has some Surrogate tags. + * + * @param Response $response A Response instance + */ + public function addSurrogateControl(Response $response); + + /** + * Checks that the Response needs to be parsed for Surrogate tags. + * + * @param Response $response A Response instance + * + * @return bool true if the Response needs to be parsed, false otherwise + */ + public function needsParsing(Response $response); + + /** + * Renders a Surrogate tag. + * + * @param string $uri A URI + * @param string $alt An alternate URI + * @param bool $ignoreErrors Whether to ignore errors or not + * @param string $comment A comment to add as an esi:include tag + * + * @return string + */ + public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = ''); + + /** + * Replaces a Response Surrogate tags with the included resource content. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * + * @return Response + */ + public function process(Request $request, Response $response); + + /** + * Handles a Surrogate from the cache. + * + * @param HttpCache $cache An HttpCache instance + * @param string $uri The main URI + * @param string $alt An alternative URI + * @param bool $ignoreErrors Whether to ignore errors or not + * + * @return string + * + * @throws \RuntimeException + * @throws \Exception + */ + public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php new file mode 100644 index 0000000..1600b2c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -0,0 +1,285 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * HttpKernel notifies events to convert a Request object to a Response one. + * + * @author Fabien Potencier + */ +class HttpKernel implements HttpKernelInterface, TerminableInterface +{ + protected $dispatcher; + protected $resolver; + protected $requestStack; + + /** + * Constructor. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance + * @param RequestStack $requestStack A stack for master/sub requests + */ + public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null) + { + $this->dispatcher = $dispatcher; + $this->resolver = $resolver; + $this->requestStack = $requestStack ?: new RequestStack(); + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $request->headers->set('X-Php-Ob-Level', ob_get_level()); + + try { + return $this->handleRaw($request, $type); + } catch (\Exception $e) { + if (false === $catch) { + $this->finishRequest($request, $type); + + throw $e; + } + + return $this->handleException($e, $request, $type); + } + } + + /** + * {@inheritdoc} + */ + public function terminate(Request $request, Response $response) + { + $this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response)); + } + + /** + * @throws \LogicException If the request stack is empty + * + * @internal + */ + public function terminateWithException(\Exception $exception) + { + if (!$request = $this->requestStack->getMasterRequest()) { + throw new \LogicException('Request stack is empty', 0, $exception); + } + + $response = $this->handleException($exception, $request, self::MASTER_REQUEST); + + $response->sendHeaders(); + $response->sendContent(); + + $this->terminate($request, $response); + } + + /** + * Handles a request to convert it to a response. + * + * Exceptions are not caught. + * + * @param Request $request A Request instance + * @param int $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * + * @return Response A Response instance + * + * @throws \LogicException If one of the listener does not behave as expected + * @throws NotFoundHttpException When controller cannot be found + */ + private function handleRaw(Request $request, $type = self::MASTER_REQUEST) + { + $this->requestStack->push($request); + + // request + $event = new GetResponseEvent($this, $request, $type); + $this->dispatcher->dispatch(KernelEvents::REQUEST, $event); + + if ($event->hasResponse()) { + return $this->filterResponse($event->getResponse(), $request, $type); + } + + // load controller + if (false === $controller = $this->resolver->getController($request)) { + throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo())); + } + + $event = new FilterControllerEvent($this, $controller, $request, $type); + $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event); + $controller = $event->getController(); + + // controller arguments + $arguments = $this->resolver->getArguments($request, $controller); + + // call controller + $response = call_user_func_array($controller, $arguments); + + // view + if (!$response instanceof Response) { + $event = new GetResponseForControllerResultEvent($this, $request, $type, $response); + $this->dispatcher->dispatch(KernelEvents::VIEW, $event); + + if ($event->hasResponse()) { + $response = $event->getResponse(); + } + + if (!$response instanceof Response) { + $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response)); + + // the user may have forgotten to return something + if (null === $response) { + $msg .= ' Did you forget to add a return statement somewhere in your controller?'; + } + throw new \LogicException($msg); + } + } + + return $this->filterResponse($response, $request, $type); + } + + /** + * Filters a response object. + * + * @param Response $response A Response instance + * @param Request $request An error message in case the response is not a Response object + * @param int $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * + * @return Response The filtered Response instance + * + * @throws \RuntimeException if the passed object is not a Response instance + */ + private function filterResponse(Response $response, Request $request, $type) + { + $event = new FilterResponseEvent($this, $request, $type, $response); + + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->finishRequest($request, $type); + + return $event->getResponse(); + } + + /** + * Publishes the finish request event, then pop the request from the stack. + * + * Note that the order of the operations is important here, otherwise + * operations such as {@link RequestStack::getParentRequest()} can lead to + * weird results. + * + * @param Request $request + * @param int $type + */ + private function finishRequest(Request $request, $type) + { + $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type)); + $this->requestStack->pop(); + } + + /** + * Handles an exception by trying to convert it to a Response. + * + * @param \Exception $e An \Exception instance + * @param Request $request A Request instance + * @param int $type The type of the request + * + * @return Response A Response instance + * + * @throws \Exception + */ + private function handleException(\Exception $e, $request, $type) + { + $event = new GetResponseForExceptionEvent($this, $request, $type, $e); + $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); + + // a listener might have replaced the exception + $e = $event->getException(); + + if (!$event->hasResponse()) { + $this->finishRequest($request, $type); + + throw $e; + } + + $response = $event->getResponse(); + + // the developer asked for a specific status code + if ($response->headers->has('X-Status-Code')) { + $response->setStatusCode($response->headers->get('X-Status-Code')); + + $response->headers->remove('X-Status-Code'); + } elseif (!$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) { + // ensure that we actually have an error response + if ($e instanceof HttpExceptionInterface) { + // keep the HTTP status code and headers + $response->setStatusCode($e->getStatusCode()); + $response->headers->add($e->getHeaders()); + } else { + $response->setStatusCode(500); + } + } + + try { + return $this->filterResponse($response, $request, $type); + } catch (\Exception $e) { + return $response; + } + } + + private function varToString($var) + { + if (is_object($var)) { + return sprintf('Object(%s)', get_class($var)); + } + + if (is_array($var)) { + $a = array(); + foreach ($var as $k => $v) { + $a[] = sprintf('%s => %s', $k, $this->varToString($v)); + } + + return sprintf('Array(%s)', implode(', ', $a)); + } + + if (is_resource($var)) { + return sprintf('Resource(%s)', get_resource_type($var)); + } + + if (null === $var) { + return 'null'; + } + + if (false === $var) { + return 'false'; + } + + if (true === $var) { + return 'true'; + } + + return (string) $var; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernelInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernelInterface.php new file mode 100644 index 0000000..5050bfc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernelInterface.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * HttpKernelInterface handles a Request to convert it to a Response. + * + * @author Fabien Potencier + */ +interface HttpKernelInterface +{ + const MASTER_REQUEST = 1; + const SUB_REQUEST = 2; + + /** + * Handles a Request to convert it to a Response. + * + * When $catch is true, the implementation must catch all exceptions + * and do its best to convert them to a Response instance. + * + * @param Request $request A Request instance + * @param int $type The type of the request + * (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * @param bool $catch Whether to catch exceptions or not + * + * @return Response A Response instance + * + * @throws \Exception When an Exception occurs during processing + */ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php new file mode 100644 index 0000000..62c53ea --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php @@ -0,0 +1,729 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Config\EnvParametersResource; +use Symfony\Component\HttpKernel\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; +use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\ClassLoader\ClassCollectionLoader; + +/** + * The Kernel is the heart of the Symfony system. + * + * It manages an environment made of bundles. + * + * @author Fabien Potencier + */ +abstract class Kernel implements KernelInterface, TerminableInterface +{ + /** + * @var BundleInterface[] + */ + protected $bundles = array(); + + protected $bundleMap; + protected $container; + protected $rootDir; + protected $environment; + protected $debug; + protected $booted = false; + protected $name; + protected $startTime; + protected $loadClassCache; + + const VERSION = '3.0.1'; + const VERSION_ID = 30001; + const MAJOR_VERSION = 3; + const MINOR_VERSION = 0; + const RELEASE_VERSION = 1; + const EXTRA_VERSION = ''; + + const END_OF_MAINTENANCE = '07/2016'; + const END_OF_LIFE = '01/2017'; + + /** + * Constructor. + * + * @param string $environment The environment + * @param bool $debug Whether to enable debugging or not + */ + public function __construct($environment, $debug) + { + $this->environment = $environment; + $this->debug = (bool) $debug; + $this->rootDir = $this->getRootDir(); + $this->name = $this->getName(); + + if ($this->debug) { + $this->startTime = microtime(true); + } + } + + public function __clone() + { + if ($this->debug) { + $this->startTime = microtime(true); + } + + $this->booted = false; + $this->container = null; + } + + /** + * Boots the current kernel. + */ + public function boot() + { + if (true === $this->booted) { + return; + } + + if ($this->loadClassCache) { + $this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]); + } + + // init bundles + $this->initializeBundles(); + + // init container + $this->initializeContainer(); + + foreach ($this->getBundles() as $bundle) { + $bundle->setContainer($this->container); + $bundle->boot(); + } + + $this->booted = true; + } + + /** + * {@inheritdoc} + */ + public function terminate(Request $request, Response $response) + { + if (false === $this->booted) { + return; + } + + if ($this->getHttpKernel() instanceof TerminableInterface) { + $this->getHttpKernel()->terminate($request, $response); + } + } + + /** + * {@inheritdoc} + */ + public function shutdown() + { + if (false === $this->booted) { + return; + } + + $this->booted = false; + + foreach ($this->getBundles() as $bundle) { + $bundle->shutdown(); + $bundle->setContainer(null); + } + + $this->container = null; + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + if (false === $this->booted) { + $this->boot(); + } + + return $this->getHttpKernel()->handle($request, $type, $catch); + } + + /** + * Gets a HTTP kernel from the container. + * + * @return HttpKernel + */ + protected function getHttpKernel() + { + return $this->container->get('http_kernel'); + } + + /** + * {@inheritdoc} + */ + public function getBundles() + { + return $this->bundles; + } + + /** + * {@inheritdoc} + */ + public function getBundle($name, $first = true) + { + if (!isset($this->bundleMap[$name])) { + throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, get_class($this))); + } + + if (true === $first) { + return $this->bundleMap[$name][0]; + } + + return $this->bundleMap[$name]; + } + + /** + * {@inheritdoc} + * + * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle + */ + public function locateResource($name, $dir = null, $first = true) + { + if ('@' !== $name[0]) { + throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); + } + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); + } + + $bundleName = substr($name, 1); + $path = ''; + if (false !== strpos($bundleName, '/')) { + list($bundleName, $path) = explode('/', $bundleName, 2); + } + + $isResource = 0 === strpos($path, 'Resources') && null !== $dir; + $overridePath = substr($path, 9); + $resourceBundle = null; + $bundles = $this->getBundle($bundleName, false); + $files = array(); + + foreach ($bundles as $bundle) { + if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { + if (null !== $resourceBundle) { + throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', + $file, + $resourceBundle, + $dir.'/'.$bundles[0]->getName().$overridePath + )); + } + + if ($first) { + return $file; + } + $files[] = $file; + } + + if (file_exists($file = $bundle->getPath().'/'.$path)) { + if ($first && !$isResource) { + return $file; + } + $files[] = $file; + $resourceBundle = $bundle->getName(); + } + } + + if (count($files) > 0) { + return $first && $isResource ? $files[0] : $files; + } + + throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + if (null === $this->name) { + $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); + } + + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getEnvironment() + { + return $this->environment; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->debug; + } + + /** + * {@inheritdoc} + */ + public function getRootDir() + { + if (null === $this->rootDir) { + $r = new \ReflectionObject($this); + $this->rootDir = dirname($r->getFileName()); + } + + return $this->rootDir; + } + + /** + * {@inheritdoc} + */ + public function getContainer() + { + return $this->container; + } + + /** + * Loads the PHP class cache. + * + * This methods only registers the fact that you want to load the cache classes. + * The cache will actually only be loaded when the Kernel is booted. + * + * That optimization is mainly useful when using the HttpCache class in which + * case the class cache is not loaded if the Response is in the cache. + * + * @param string $name The cache name prefix + * @param string $extension File extension of the resulting file + */ + public function loadClassCache($name = 'classes', $extension = '.php') + { + $this->loadClassCache = array($name, $extension); + } + + /** + * Used internally. + */ + public function setClassCache(array $classes) + { + file_put_contents($this->getCacheDir().'/classes.map', sprintf('debug ? $this->startTime : -INF; + } + + /** + * {@inheritdoc} + */ + public function getCacheDir() + { + return $this->rootDir.'/cache/'.$this->environment; + } + + /** + * {@inheritdoc} + */ + public function getLogDir() + { + return $this->rootDir.'/logs'; + } + + /** + * {@inheritdoc} + */ + public function getCharset() + { + return 'UTF-8'; + } + + protected function doLoadClassCache($name, $extension) + { + if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) { + ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension); + } + } + + /** + * Initializes the data structures related to the bundle management. + * + * - the bundles property maps a bundle name to the bundle instance, + * - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). + * + * @throws \LogicException if two bundles share a common name + * @throws \LogicException if a bundle tries to extend a non-registered bundle + * @throws \LogicException if a bundle tries to extend itself + * @throws \LogicException if two bundles extend the same ancestor + */ + protected function initializeBundles() + { + // init bundles + $this->bundles = array(); + $topMostBundles = array(); + $directChildren = array(); + + foreach ($this->registerBundles() as $bundle) { + $name = $bundle->getName(); + if (isset($this->bundles[$name])) { + throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); + } + $this->bundles[$name] = $bundle; + + if ($parentName = $bundle->getParent()) { + if (isset($directChildren[$parentName])) { + throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); + } + if ($parentName == $name) { + throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name)); + } + $directChildren[$parentName] = $name; + } else { + $topMostBundles[$name] = $bundle; + } + } + + // look for orphans + if (!empty($directChildren) && count($diff = array_diff_key($directChildren, $this->bundles))) { + $diff = array_keys($diff); + + throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); + } + + // inheritance + $this->bundleMap = array(); + foreach ($topMostBundles as $name => $bundle) { + $bundleMap = array($bundle); + $hierarchy = array($name); + + while (isset($directChildren[$name])) { + $name = $directChildren[$name]; + array_unshift($bundleMap, $this->bundles[$name]); + $hierarchy[] = $name; + } + + foreach ($hierarchy as $bundle) { + $this->bundleMap[$bundle] = $bundleMap; + array_pop($bundleMap); + } + } + } + + /** + * Gets the container class. + * + * @return string The container class + */ + protected function getContainerClass() + { + return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer'; + } + + /** + * Gets the container's base class. + * + * All names except Container must be fully qualified. + * + * @return string + */ + protected function getContainerBaseClass() + { + return 'Container'; + } + + /** + * Initializes the service container. + * + * The cached version of the service container is used when fresh, otherwise the + * container is built. + */ + protected function initializeContainer() + { + $class = $this->getContainerClass(); + $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); + $fresh = true; + if (!$cache->isFresh()) { + $container = $this->buildContainer(); + $container->compile(); + $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); + + $fresh = false; + } + + require_once $cache->getPath(); + + $this->container = new $class(); + $this->container->set('kernel', $this); + + if (!$fresh && $this->container->has('cache_warmer')) { + $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); + } + } + + /** + * Returns the kernel parameters. + * + * @return array An array of kernel parameters + */ + protected function getKernelParameters() + { + $bundles = array(); + foreach ($this->bundles as $name => $bundle) { + $bundles[$name] = get_class($bundle); + } + + return array_merge( + array( + 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, + 'kernel.environment' => $this->environment, + 'kernel.debug' => $this->debug, + 'kernel.name' => $this->name, + 'kernel.cache_dir' => realpath($this->getCacheDir()) ?: $this->getCacheDir(), + 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), + 'kernel.bundles' => $bundles, + 'kernel.charset' => $this->getCharset(), + 'kernel.container_class' => $this->getContainerClass(), + ), + $this->getEnvParameters() + ); + } + + /** + * Gets the environment parameters. + * + * Only the parameters starting with "SYMFONY__" are considered. + * + * @return array An array of parameters + */ + protected function getEnvParameters() + { + $parameters = array(); + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, 'SYMFONY__')) { + $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; + } + } + + return $parameters; + } + + /** + * Builds the service container. + * + * @return ContainerBuilder The compiled service container + * + * @throws \RuntimeException + */ + protected function buildContainer() + { + foreach (array('cache' => $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); + } + } elseif (!is_writable($dir)) { + throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); + } + } + + $container = $this->getContainerBuilder(); + $container->addObjectResource($this); + $this->prepareContainer($container); + + if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { + $container->merge($cont); + } + + $container->addCompilerPass(new AddClassesToCachePass($this)); + $container->addResource(new EnvParametersResource('SYMFONY__')); + + return $container; + } + + /** + * Prepares the ContainerBuilder before it is compiled. + * + * @param ContainerBuilder $container A ContainerBuilder instance + */ + protected function prepareContainer(ContainerBuilder $container) + { + $extensions = array(); + foreach ($this->bundles as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $container->registerExtension($extension); + $extensions[] = $extension->getAlias(); + } + + if ($this->debug) { + $container->addObjectResource($bundle); + } + } + foreach ($this->bundles as $bundle) { + $bundle->build($container); + } + + // ensure these extensions are implicitly loaded + $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); + } + + /** + * Gets a new ContainerBuilder instance used to build the service container. + * + * @return ContainerBuilder + */ + protected function getContainerBuilder() + { + $container = new ContainerBuilder(new ParameterBag($this->getKernelParameters())); + + if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { + $container->setProxyInstantiator(new RuntimeInstantiator()); + } + + return $container; + } + + /** + * Dumps the service container to PHP code in the cache. + * + * @param ConfigCache $cache The config cache + * @param ContainerBuilder $container The service container + * @param string $class The name of the class to generate + * @param string $baseClass The name of the container's base class + */ + protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) + { + // cache the container + $dumper = new PhpDumper($container); + + if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { + $dumper->setProxyDumper(new ProxyDumper(md5($cache->getPath()))); + } + + $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass, 'file' => $cache->getPath())); + if (!$this->debug) { + $content = static::stripComments($content); + } + + $cache->write($content, $container->getResources()); + } + + /** + * Returns a loader for the container. + * + * @param ContainerInterface $container The service container + * + * @return DelegatingLoader The loader + */ + protected function getContainerLoader(ContainerInterface $container) + { + $locator = new FileLocator($this); + $resolver = new LoaderResolver(array( + new XmlFileLoader($container, $locator), + new YamlFileLoader($container, $locator), + new IniFileLoader($container, $locator), + new PhpFileLoader($container, $locator), + new DirectoryLoader($container, $locator), + new ClosureLoader($container), + )); + + return new DelegatingLoader($resolver); + } + + /** + * Removes comments from a PHP source string. + * + * We don't use the PHP php_strip_whitespace() function + * as we want the content to be readable and well-formatted. + * + * @param string $source A PHP string + * + * @return string The PHP string with the comments removed + */ + public static function stripComments($source) + { + if (!function_exists('token_get_all')) { + return $source; + } + + $rawChunk = ''; + $output = ''; + $tokens = token_get_all($source); + $ignoreSpace = false; + for (reset($tokens); false !== $token = current($tokens); next($tokens)) { + if (is_string($token)) { + $rawChunk .= $token; + } elseif (T_START_HEREDOC === $token[0]) { + $output .= $rawChunk.$token[1]; + do { + $token = next($tokens); + $output .= $token[1]; + } while ($token[0] !== T_END_HEREDOC); + $rawChunk = ''; + } elseif (T_WHITESPACE === $token[0]) { + if ($ignoreSpace) { + $ignoreSpace = false; + + continue; + } + + // replace multiple new lines with a single newline + $rawChunk .= preg_replace(array('/\n{2,}/S'), "\n", $token[1]); + } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { + $ignoreSpace = true; + } else { + $rawChunk .= $token[1]; + + // The PHP-open tag already has a new-line + if (T_OPEN_TAG === $token[0]) { + $ignoreSpace = true; + } + } + } + + $output .= $rawChunk; + + return $output; + } + + public function serialize() + { + return serialize(array($this->environment, $this->debug)); + } + + public function unserialize($data) + { + list($environment, $debug) = unserialize($data); + + $this->__construct($environment, $debug); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelEvents.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelEvents.php new file mode 100644 index 0000000..8768979 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelEvents.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +/** + * Contains all events thrown in the HttpKernel component. + * + * @author Bernhard Schussek + */ +final class KernelEvents +{ + /** + * The REQUEST event occurs at the very beginning of request + * dispatching. + * + * This event allows you to create a response for a request before any + * other code in the framework is executed. The event listener method + * receives a Symfony\Component\HttpKernel\Event\GetResponseEvent + * instance. + * + * @Event + * + * @var string + */ + const REQUEST = 'kernel.request'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to create a response for a thrown exception or + * to modify the thrown exception. The event listener method receives + * a Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent + * instance. + * + * @Event + * + * @var string + */ + const EXCEPTION = 'kernel.exception'; + + /** + * The VIEW event occurs when the return value of a controller + * is not a Response instance. + * + * This event allows you to create a response for the return value of the + * controller. The event listener method receives a + * Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent + * instance. + * + * @Event + * + * @var string + */ + const VIEW = 'kernel.view'; + + /** + * The CONTROLLER event occurs once a controller was found for + * handling a request. + * + * This event allows you to change the controller that will handle the + * request. The event listener method receives a + * Symfony\Component\HttpKernel\Event\FilterControllerEvent instance. + * + * @Event + * + * @var string + */ + const CONTROLLER = 'kernel.controller'; + + /** + * The RESPONSE event occurs once a response was created for + * replying to a request. + * + * This event allows you to modify or replace the response that will be + * replied. The event listener method receives a + * Symfony\Component\HttpKernel\Event\FilterResponseEvent instance. + * + * @Event + * + * @var string + */ + const RESPONSE = 'kernel.response'; + + /** + * The TERMINATE event occurs once a response was sent. + * + * This event allows you to run expensive post-response jobs. + * The event listener method receives a + * Symfony\Component\HttpKernel\Event\PostResponseEvent instance. + * + * @Event + * + * @var string + */ + const TERMINATE = 'kernel.terminate'; + + /** + * The FINISH_REQUEST event occurs when a response was generated for a request. + * + * This event allows you to reset the global and environmental state of + * the application, when it was changed during the request. + * + * @var string + */ + const FINISH_REQUEST = 'kernel.finish_request'; +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelInterface.php new file mode 100644 index 0000000..de0f5b3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/KernelInterface.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\Config\Loader\LoaderInterface; + +/** + * The Kernel is the heart of the Symfony system. + * + * It manages an environment made of bundles. + * + * @author Fabien Potencier + */ +interface KernelInterface extends HttpKernelInterface, \Serializable +{ + /** + * Returns an array of bundles to register. + * + * @return BundleInterface[] An array of bundle instances. + */ + public function registerBundles(); + + /** + * Loads the container configuration. + * + * @param LoaderInterface $loader A LoaderInterface instance + */ + public function registerContainerConfiguration(LoaderInterface $loader); + + /** + * Boots the current kernel. + */ + public function boot(); + + /** + * Shutdowns the kernel. + * + * This method is mainly useful when doing functional testing. + */ + public function shutdown(); + + /** + * Gets the registered bundle instances. + * + * @return BundleInterface[] An array of registered bundle instances + */ + public function getBundles(); + + /** + * Returns a bundle and optionally its descendants by its name. + * + * @param string $name Bundle name + * @param bool $first Whether to return the first bundle only or together with its descendants + * + * @return BundleInterface|BundleInterface[] A BundleInterface instance or an array of BundleInterface instances if $first is false + * + * @throws \InvalidArgumentException when the bundle is not enabled + */ + public function getBundle($name, $first = true); + + /** + * Returns the file path for a given resource. + * + * A Resource can be a file or a directory. + * + * The resource name must follow the following pattern: + * + * "@BundleName/path/to/a/file.something" + * + * where BundleName is the name of the bundle + * and the remaining part is the relative path in the bundle. + * + * If $dir is passed, and the first segment of the path is "Resources", + * this method will look for a file named: + * + * $dir//path/without/Resources + * + * before looking in the bundle resource folder. + * + * @param string $name A resource name to locate + * @param string $dir A directory where to look for the resource first + * @param bool $first Whether to return the first path or paths for all matching bundles + * + * @return string|array The absolute path of the resource or an array if $first is false + * + * @throws \InvalidArgumentException if the file cannot be found or the name is not valid + * @throws \RuntimeException if the name contains invalid/unsafe characters + */ + public function locateResource($name, $dir = null, $first = true); + + /** + * Gets the name of the kernel. + * + * @return string The kernel name + */ + public function getName(); + + /** + * Gets the environment. + * + * @return string The current environment + */ + public function getEnvironment(); + + /** + * Checks if debug mode is enabled. + * + * @return bool true if debug mode is enabled, false otherwise + */ + public function isDebug(); + + /** + * Gets the application root dir. + * + * @return string The application root dir + */ + public function getRootDir(); + + /** + * Gets the current container. + * + * @return ContainerInterface A ContainerInterface instance + */ + public function getContainer(); + + /** + * Gets the request start time (not available if debug is disabled). + * + * @return int The request start timestamp + */ + public function getStartTime(); + + /** + * Gets the cache directory. + * + * @return string The cache directory + */ + public function getCacheDir(); + + /** + * Gets the log directory. + * + * @return string The log directory + */ + public function getLogDir(); + + /** + * Gets the charset of the application. + * + * @return string The charset + */ + public function getCharset(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/LICENSE b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/LICENSE new file mode 100644 index 0000000..43028bc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2015 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php new file mode 100644 index 0000000..5635a21 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Log; + +/** + * DebugLoggerInterface. + * + * @author Fabien Potencier + */ +interface DebugLoggerInterface +{ + /** + * Returns an array of logs. + * + * A log is an array with the following mandatory keys: + * timestamp, message, priority, and priorityName. + * It can also have an optional context key containing an array. + * + * @return array An array of logs + */ + public function getLogs(); + + /** + * Returns the number of errors. + * + * @return int The number of errors + */ + public function countErrors(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php new file mode 100644 index 0000000..29da4ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/FileProfilerStorage.php @@ -0,0 +1,284 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +/** + * Storage for profiler using files. + * + * @author Alexandre Salomé + */ +class FileProfilerStorage implements ProfilerStorageInterface +{ + /** + * Folder where profiler data are stored. + * + * @var string + */ + private $folder; + + /** + * Constructs the file storage using a "dsn-like" path. + * + * Example : "file:/path/to/the/storage/folder" + * + * @param string $dsn The DSN + * + * @throws \RuntimeException + */ + public function __construct($dsn) + { + if (0 !== strpos($dsn, 'file:')) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use FileStorage with an invalid dsn "%s". The expected format is "file:/path/to/the/storage/folder".', $dsn)); + } + $this->folder = substr($dsn, 5); + + if (!is_dir($this->folder) && false === @mkdir($this->folder, 0777, true) && !is_dir($this->folder)) { + throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder)); + } + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null) + { + $file = $this->getIndexFilename(); + + if (!file_exists($file)) { + return array(); + } + + $file = fopen($file, 'r'); + fseek($file, 0, SEEK_END); + + $result = array(); + while (count($result) < $limit && $line = $this->readLineFromFile($file)) { + $values = str_getcsv($line); + list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent) = $values; + $csvStatusCode = isset($values[6]) ? $values[6] : null; + + $csvTime = (int) $csvTime; + + if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method)) { + continue; + } + + if (!empty($start) && $csvTime < $start) { + continue; + } + + if (!empty($end) && $csvTime > $end) { + continue; + } + + $result[$csvToken] = array( + 'token' => $csvToken, + 'ip' => $csvIp, + 'method' => $csvMethod, + 'url' => $csvUrl, + 'time' => $csvTime, + 'parent' => $csvParent, + 'status_code' => $csvStatusCode, + ); + } + + fclose($file); + + return array_values($result); + } + + /** + * {@inheritdoc} + */ + public function purge() + { + $flags = \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveDirectoryIterator($this->folder, $flags); + $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST); + + foreach ($iterator as $file) { + if (is_file($file)) { + unlink($file); + } else { + rmdir($file); + } + } + } + + /** + * {@inheritdoc} + */ + public function read($token) + { + if (!$token || !file_exists($file = $this->getFilename($token))) { + return; + } + + return $this->createProfileFromData($token, unserialize(file_get_contents($file))); + } + + /** + * {@inheritdoc} + * + * @throws \RuntimeException + */ + public function write(Profile $profile) + { + $file = $this->getFilename($profile->getToken()); + + $profileIndexed = is_file($file); + if (!$profileIndexed) { + // Create directory + $dir = dirname($file); + if (!is_dir($dir) && false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir)); + } + } + + // Store profile + $data = array( + 'token' => $profile->getToken(), + 'parent' => $profile->getParentToken(), + 'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()), + 'data' => $profile->getCollectors(), + 'ip' => $profile->getIp(), + 'method' => $profile->getMethod(), + 'url' => $profile->getUrl(), + 'time' => $profile->getTime(), + ); + + if (false === file_put_contents($file, serialize($data))) { + return false; + } + + if (!$profileIndexed) { + // Add to index + if (false === $file = fopen($this->getIndexFilename(), 'a')) { + return false; + } + + fputcsv($file, array( + $profile->getToken(), + $profile->getIp(), + $profile->getMethod(), + $profile->getUrl(), + $profile->getTime(), + $profile->getParentToken(), + $profile->getStatusCode(), + )); + fclose($file); + } + + return true; + } + + /** + * Gets filename to store data, associated to the token. + * + * @param string $token + * + * @return string The profile filename + */ + protected function getFilename($token) + { + // Uses 4 last characters, because first are mostly the same. + $folderA = substr($token, -2, 2); + $folderB = substr($token, -4, 2); + + return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token; + } + + /** + * Gets the index filename. + * + * @return string The index filename + */ + protected function getIndexFilename() + { + return $this->folder.'/index.csv'; + } + + /** + * Reads a line in the file, backward. + * + * This function automatically skips the empty lines and do not include the line return in result value. + * + * @param resource $file The file resource, with the pointer placed at the end of the line to read + * + * @return mixed A string representing the line or null if beginning of file is reached + */ + protected function readLineFromFile($file) + { + $line = ''; + $position = ftell($file); + + if (0 === $position) { + return; + } + + while (true) { + $chunkSize = min($position, 1024); + $position -= $chunkSize; + fseek($file, $position); + + if (0 === $chunkSize) { + // bof reached + break; + } + + $buffer = fread($file, $chunkSize); + + if (false === ($upTo = strrpos($buffer, "\n"))) { + $line = $buffer.$line; + continue; + } + + $position += $upTo; + $line = substr($buffer, $upTo + 1).$line; + fseek($file, max(0, $position), SEEK_SET); + + if ('' !== $line) { + break; + } + } + + return '' === $line ? null : $line; + } + + protected function createProfileFromData($token, $data, $parent = null) + { + $profile = new Profile($token); + $profile->setIp($data['ip']); + $profile->setMethod($data['method']); + $profile->setUrl($data['url']); + $profile->setTime($data['time']); + $profile->setCollectors($data['data']); + + if (!$parent && $data['parent']) { + $parent = $this->read($data['parent']); + } + + if ($parent) { + $profile->setParent($parent); + } + + foreach ($data['children'] as $token) { + if (!$token || !file_exists($file = $this->getFilename($token))) { + continue; + } + + $profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile)); + } + + return $profile; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profile.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profile.php new file mode 100644 index 0000000..a4e4ba6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profile.php @@ -0,0 +1,292 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; + +/** + * Profile. + * + * @author Fabien Potencier + */ +class Profile +{ + private $token; + + /** + * @var DataCollectorInterface[] + */ + private $collectors = array(); + + private $ip; + private $method; + private $url; + private $time; + private $statusCode; + + /** + * @var Profile + */ + private $parent; + + /** + * @var Profile[] + */ + private $children = array(); + + /** + * Constructor. + * + * @param string $token The token + */ + public function __construct($token) + { + $this->token = $token; + } + + /** + * Sets the token. + * + * @param string $token The token + */ + public function setToken($token) + { + $this->token = $token; + } + + /** + * Gets the token. + * + * @return string The token + */ + public function getToken() + { + return $this->token; + } + + /** + * Sets the parent token. + * + * @param Profile $parent The parent Profile + */ + public function setParent(Profile $parent) + { + $this->parent = $parent; + } + + /** + * Returns the parent profile. + * + * @return Profile The parent profile + */ + public function getParent() + { + return $this->parent; + } + + /** + * Returns the parent token. + * + * @return null|string The parent token + */ + public function getParentToken() + { + return $this->parent ? $this->parent->getToken() : null; + } + + /** + * Returns the IP. + * + * @return string The IP + */ + public function getIp() + { + return $this->ip; + } + + /** + * Sets the IP. + * + * @param string $ip + */ + public function setIp($ip) + { + $this->ip = $ip; + } + + /** + * Returns the request method. + * + * @return string The request method + */ + public function getMethod() + { + return $this->method; + } + + public function setMethod($method) + { + $this->method = $method; + } + + /** + * Returns the URL. + * + * @return string The URL + */ + public function getUrl() + { + return $this->url; + } + + public function setUrl($url) + { + $this->url = $url; + } + + /** + * Returns the time. + * + * @return string The time + */ + public function getTime() + { + if (null === $this->time) { + return 0; + } + + return $this->time; + } + + public function setTime($time) + { + $this->time = $time; + } + + /** + * @param int $statusCode + */ + public function setStatusCode($statusCode) + { + $this->statusCode = $statusCode; + } + + /** + * @return int + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Finds children profilers. + * + * @return Profile[] An array of Profile + */ + public function getChildren() + { + return $this->children; + } + + /** + * Sets children profiler. + * + * @param Profile[] $children An array of Profile + */ + public function setChildren(array $children) + { + $this->children = array(); + foreach ($children as $child) { + $this->addChild($child); + } + } + + /** + * Adds the child token. + * + * @param Profile $child The child Profile + */ + public function addChild(Profile $child) + { + $this->children[] = $child; + $child->setParent($this); + } + + /** + * Gets a Collector by name. + * + * @param string $name A collector name + * + * @return DataCollectorInterface A DataCollectorInterface instance + * + * @throws \InvalidArgumentException if the collector does not exist + */ + public function getCollector($name) + { + if (!isset($this->collectors[$name])) { + throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name)); + } + + return $this->collectors[$name]; + } + + /** + * Gets the Collectors associated with this profile. + * + * @return DataCollectorInterface[] + */ + public function getCollectors() + { + return $this->collectors; + } + + /** + * Sets the Collectors associated with this profile. + * + * @param DataCollectorInterface[] $collectors + */ + public function setCollectors(array $collectors) + { + $this->collectors = array(); + foreach ($collectors as $collector) { + $this->addCollector($collector); + } + } + + /** + * Adds a Collector. + * + * @param DataCollectorInterface $collector A DataCollectorInterface instance + */ + public function addCollector(DataCollectorInterface $collector) + { + $this->collectors[$collector->getName()] = $collector; + } + + /** + * Returns true if a Collector for the given name exists. + * + * @param string $name A collector name + * + * @return bool + */ + public function hasCollector($name) + { + return isset($this->collectors[$name]); + } + + public function __sleep() + { + return array('token', 'parent', 'children', 'collectors', 'ip', 'method', 'url', 'time'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profiler.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profiler.php new file mode 100644 index 0000000..8373a15 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profiler.php @@ -0,0 +1,264 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; +use Psr\Log\LoggerInterface; + +/** + * Profiler. + * + * @author Fabien Potencier + */ +class Profiler +{ + /** + * @var ProfilerStorageInterface + */ + private $storage; + + /** + * @var DataCollectorInterface[] + */ + private $collectors = array(); + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var bool + */ + private $enabled = true; + + /** + * Constructor. + * + * @param ProfilerStorageInterface $storage A ProfilerStorageInterface instance + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null) + { + $this->storage = $storage; + $this->logger = $logger; + } + + /** + * Disables the profiler. + */ + public function disable() + { + $this->enabled = false; + } + + /** + * Enables the profiler. + */ + public function enable() + { + $this->enabled = true; + } + + /** + * Loads the Profile for the given Response. + * + * @param Response $response A Response instance + * + * @return Profile A Profile instance + */ + public function loadProfileFromResponse(Response $response) + { + if (!$token = $response->headers->get('X-Debug-Token')) { + return false; + } + + return $this->loadProfile($token); + } + + /** + * Loads the Profile for the given token. + * + * @param string $token A token + * + * @return Profile A Profile instance + */ + public function loadProfile($token) + { + return $this->storage->read($token); + } + + /** + * Saves a Profile. + * + * @param Profile $profile A Profile instance + * + * @return bool + */ + public function saveProfile(Profile $profile) + { + // late collect + foreach ($profile->getCollectors() as $collector) { + if ($collector instanceof LateDataCollectorInterface) { + $collector->lateCollect(); + } + } + + if (!($ret = $this->storage->write($profile)) && null !== $this->logger) { + $this->logger->warning('Unable to store the profiler information.', array('configured_storage' => get_class($this->storage))); + } + + return $ret; + } + + /** + * Purges all data from the storage. + */ + public function purge() + { + $this->storage->purge(); + } + + /** + * Finds profiler tokens for the given criteria. + * + * @param string $ip The IP + * @param string $url The URL + * @param string $limit The maximum number of tokens to return + * @param string $method The request method + * @param string $start The start date to search from + * @param string $end The end date to search to + * + * @return array An array of tokens + * + * @see http://php.net/manual/en/datetime.formats.php for the supported date/time formats + */ + public function find($ip, $url, $limit, $method, $start, $end) + { + return $this->storage->find($ip, $url, $limit, $method, $this->getTimestamp($start), $this->getTimestamp($end)); + } + + /** + * Collects data for the given Response. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * @param \Exception $exception An exception instance if the request threw one + * + * @return Profile|null A Profile instance or null if the profiler is disabled + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (false === $this->enabled) { + return; + } + + $profile = new Profile(substr(hash('sha256', uniqid(mt_rand(), true)), 0, 6)); + $profile->setTime(time()); + $profile->setUrl($request->getUri()); + $profile->setIp($request->getClientIp()); + $profile->setMethod($request->getMethod()); + $profile->setStatusCode($response->getStatusCode()); + + $response->headers->set('X-Debug-Token', $profile->getToken()); + + foreach ($this->collectors as $collector) { + $collector->collect($request, $response, $exception); + + // we need to clone for sub-requests + $profile->addCollector(clone $collector); + } + + return $profile; + } + + /** + * Gets the Collectors associated with this profiler. + * + * @return array An array of collectors + */ + public function all() + { + return $this->collectors; + } + + /** + * Sets the Collectors associated with this profiler. + * + * @param DataCollectorInterface[] $collectors An array of collectors + */ + public function set(array $collectors = array()) + { + $this->collectors = array(); + foreach ($collectors as $collector) { + $this->add($collector); + } + } + + /** + * Adds a Collector. + * + * @param DataCollectorInterface $collector A DataCollectorInterface instance + */ + public function add(DataCollectorInterface $collector) + { + $this->collectors[$collector->getName()] = $collector; + } + + /** + * Returns true if a Collector for the given name exists. + * + * @param string $name A collector name + * + * @return bool + */ + public function has($name) + { + return isset($this->collectors[$name]); + } + + /** + * Gets a Collector by name. + * + * @param string $name A collector name + * + * @return DataCollectorInterface A DataCollectorInterface instance + * + * @throws \InvalidArgumentException if the collector does not exist + */ + public function get($name) + { + if (!isset($this->collectors[$name])) { + throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name)); + } + + return $this->collectors[$name]; + } + + private function getTimestamp($value) + { + if (null === $value || '' == $value) { + return; + } + + try { + $value = new \DateTime(is_numeric($value) ? '@'.$value : $value); + } catch (\Exception $e) { + return; + } + + return $value->getTimestamp(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorageInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorageInterface.php new file mode 100644 index 0000000..ea72af2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/ProfilerStorageInterface.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +/** + * ProfilerStorageInterface. + * + * @author Fabien Potencier + */ +interface ProfilerStorageInterface +{ + /** + * Finds profiler tokens for the given criteria. + * + * @param string $ip The IP + * @param string $url The URL + * @param string $limit The maximum number of tokens to return + * @param string $method The request method + * @param int|null $start The start date to search from + * @param int|null $end The end date to search to + * + * @return array An array of tokens + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null); + + /** + * Reads data associated with the given token. + * + * The method returns false if the token does not exist in the storage. + * + * @param string $token A token + * + * @return Profile The profile associated with token + */ + public function read($token); + + /** + * Saves a Profile. + * + * @param Profile $profile A Profile instance + * + * @return bool Write operation successful + */ + public function write(Profile $profile); + + /** + * Purges all data from the database. + */ + public function purge(); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/README.md b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/README.md new file mode 100644 index 0000000..9ec8c04 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/README.md @@ -0,0 +1,99 @@ +HttpKernel Component +==================== + +HttpKernel provides the building blocks to create flexible and fast HTTP-based +frameworks. + +``HttpKernelInterface`` is the core interface of the Symfony full-stack +framework: + +```php +interface HttpKernelInterface +{ + /** + * Handles a Request to convert it to a Response. + * + * @param Request $request A Request instance + * + * @return Response A Response instance + */ + function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); +} +``` + +It takes a ``Request`` as an input and should return a ``Response`` as an +output. Using this interface makes your code compatible with all frameworks +using the Symfony components. And this will give you many cool features for +free. + +Creating a framework based on the Symfony components is really easy. Here is +a very simple, but fully-featured framework based on the Symfony components: + +```php +$routes = new RouteCollection(); +$routes->add('hello', new Route('/hello', array('_controller' => + function (Request $request) { + return new Response(sprintf("Hello %s", $request->get('name'))); + } +))); + +$request = Request::createFromGlobals(); + +$context = new RequestContext(); +$context->fromRequest($request); + +$matcher = new UrlMatcher($routes, $context); + +$dispatcher = new EventDispatcher(); +$dispatcher->addSubscriber(new RouterListener($matcher)); + +$resolver = new ControllerResolver(); + +$kernel = new HttpKernel($dispatcher, $resolver); + +$kernel->handle($request)->send(); +``` + +This is all you need to create a flexible framework with the Symfony +components. + +Want to add an HTTP reverse proxy and benefit from HTTP caching and Edge Side +Includes? + +```php +$kernel = new HttpKernel($dispatcher, $resolver); + +$kernel = new HttpCache($kernel, new Store(__DIR__.'/cache')); +``` + +Want to functional test this small framework? + +```php +$client = new Client($kernel); +$crawler = $client->request('GET', '/hello/Fabien'); + +$this->assertEquals('Fabien', $crawler->filter('p > span')->text()); +``` + +Want nice error pages instead of ugly PHP exceptions? + +```php +$dispatcher->addSubscriber(new ExceptionListener(function (Request $request) { + $msg = 'Something went wrong! ('.$request->get('exception')->getMessage().')'; + + return new Response($msg, 500); +})); +``` + +And that's why the simple looking ``HttpKernelInterface`` is so powerful. It +gives you access to a lot of cool features, ready to be used out of the box, +with no efforts. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/HttpKernel/ + $ composer install + $ phpunit diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/TerminableInterface.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/TerminableInterface.php new file mode 100644 index 0000000..d55a15b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/TerminableInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Terminable extends the Kernel request/response cycle with dispatching a post + * response event after sending the response and before shutting down the kernel. + * + * @author Jordi Boggiano + * @author Pierre Minnieur + */ +interface TerminableInterface +{ + /** + * Terminates a request/response cycle. + * + * Should be called after sending the response and before shutting down the kernel. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + */ + public function terminate(Request $request, Response $response); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php new file mode 100644 index 0000000..57568d3 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Bundle/BundleTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Bundle; + +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\ExtensionNotValidBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\ExtensionPresentBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionAbsentBundle\ExtensionAbsentBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand; + +class BundleTest extends \PHPUnit_Framework_TestCase +{ + public function testGetContainerExtension() + { + $bundle = new ExtensionPresentBundle(); + + $this->assertInstanceOf( + 'Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection\ExtensionPresentExtension', + $bundle->getContainerExtension() + ); + } + + public function testRegisterCommands() + { + $cmd = new FooCommand(); + $app = $this->getMock('Symfony\Component\Console\Application'); + $app->expects($this->once())->method('add')->with($this->equalTo($cmd)); + + $bundle = new ExtensionPresentBundle(); + $bundle->registerCommands($app); + + $bundle2 = new ExtensionAbsentBundle(); + + $this->assertNull($bundle2->registerCommands($app)); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface + */ + public function testGetContainerExtensionWithInvalidClass() + { + $bundle = new ExtensionNotValidBundle(); + $bundle->getContainerExtension(); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php new file mode 100644 index 0000000..b54d169 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheClearer/ChainCacheClearerTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\CacheClearer; + +use Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer; + +class ChainCacheClearerTest extends \PHPUnit_Framework_TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_clearer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheDir); + } + + public function testInjectClearersInConstructor() + { + $clearer = $this->getMockClearer(); + $clearer + ->expects($this->once()) + ->method('clear'); + + $chainClearer = new ChainCacheClearer(array($clearer)); + $chainClearer->clear(self::$cacheDir); + } + + public function testInjectClearerUsingAdd() + { + $clearer = $this->getMockClearer(); + $clearer + ->expects($this->once()) + ->method('clear'); + + $chainClearer = new ChainCacheClearer(); + $chainClearer->add($clearer); + $chainClearer->clear(self::$cacheDir); + } + + protected function getMockClearer() + { + return $this->getMock('Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php new file mode 100644 index 0000000..e78c8d1 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; + +class CacheWarmerAggregateTest extends \PHPUnit_Framework_TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheDir); + } + + public function testInjectWarmersUsingConstructor() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + public function testInjectWarmersUsingAdd() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(); + $aggregate->add($warmer); + $aggregate->warmUp(self::$cacheDir); + } + + public function testInjectWarmersUsingSetWarmers() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(); + $aggregate->setWarmers(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->never()) + ->method('isOptional'); + $warmer + ->expects($this->once()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->enableOptionalWarmers(); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesNotCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsNotEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('isOptional') + ->will($this->returnValue(true)); + $warmer + ->expects($this->never()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + protected function getCacheWarmerMock() + { + $warmer = $this->getMockBuilder('Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface') + ->disableOriginalConstructor() + ->getMock(); + + return $warmer; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerTest.php new file mode 100644 index 0000000..f0ab05d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/CacheWarmer/CacheWarmerTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; + +class CacheWarmerTest extends \PHPUnit_Framework_TestCase +{ + protected static $cacheFile; + + public static function setUpBeforeClass() + { + self::$cacheFile = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheFile); + } + + public function testWriteCacheFileCreatesTheFile() + { + $warmer = new TestCacheWarmer(self::$cacheFile); + $warmer->warmUp(dirname(self::$cacheFile)); + + $this->assertTrue(file_exists(self::$cacheFile)); + } + + /** + * @expectedException \RuntimeException + */ + public function testWriteNonWritableCacheFileThrowsARuntimeException() + { + $nonWritableFile = '/this/file/is/very/probably/not/writable'; + $warmer = new TestCacheWarmer($nonWritableFile); + $warmer->warmUp(dirname($nonWritableFile)); + } +} + +class TestCacheWarmer extends CacheWarmer +{ + protected $file; + + public function __construct($file) + { + $this->file = $file; + } + + public function warmUp($cacheDir) + { + $this->writeCacheFile($this->file, 'content'); + } + + public function isOptional() + { + return false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/ClientTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/ClientTest.php new file mode 100644 index 0000000..b5d2c9c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/ClientTest.php @@ -0,0 +1,179 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\Client; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpKernel\Tests\Fixtures\TestClient; + +class ClientTest extends \PHPUnit_Framework_TestCase +{ + public function testDoRequest() + { + $client = new Client(new TestHttpKernel()); + + $client->request('GET', '/'); + $this->assertEquals('Request: /', $client->getResponse()->getContent(), '->doRequest() uses the request handler to make the request'); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Request', $client->getInternalRequest()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Request', $client->getRequest()); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getInternalResponse()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $client->getResponse()); + + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('Request: /', $client->getResponse()->getContent(), '->doRequest() uses the request handler to make the request'); + $this->assertEquals('www.example.com', $client->getRequest()->getHost(), '->doRequest() uses the request handler to make the request'); + + $client->request('GET', 'http://www.example.com/?parameter=http://google.com'); + $this->assertEquals('http://www.example.com/?parameter='.urlencode('http://google.com'), $client->getRequest()->getUri(), '->doRequest() uses the request handler to make the request'); + } + + public function testGetScript() + { + $client = new TestClient(new TestHttpKernel()); + $client->insulate(); + $client->request('GET', '/'); + + $this->assertEquals('Request: /', $client->getResponse()->getContent(), '->getScript() returns a script that uses the request handler to make the request'); + } + + public function testFilterResponseConvertsCookies() + { + $client = new Client(new TestHttpKernel()); + + $r = new \ReflectionObject($client); + $m = $r->getMethod('filterResponse'); + $m->setAccessible(true); + + $expected = array( + 'foo=bar; expires=Sun, 15 Feb 2009 20:00:00 GMT; domain=http://example.com; path=/foo; secure; httponly', + 'foo1=bar1; expires=Sun, 15 Feb 2009 20:00:00 GMT; domain=http://example.com; path=/foo; secure; httponly', + ); + + $response = new Response(); + $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $domResponse = $m->invoke($client, $response); + $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie')); + + $response = new Response(); + $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $response->headers->setCookie(new Cookie('foo1', 'bar1', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $domResponse = $m->invoke($client, $response); + $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie')); + $this->assertEquals($expected, $domResponse->getHeader('Set-Cookie', false)); + } + + public function testFilterResponseSupportsStreamedResponses() + { + $client = new Client(new TestHttpKernel()); + + $r = new \ReflectionObject($client); + $m = $r->getMethod('filterResponse'); + $m->setAccessible(true); + + $response = new StreamedResponse(function () { + echo 'foo'; + }); + + $domResponse = $m->invoke($client, $response); + $this->assertEquals('foo', $domResponse->getContent()); + } + + public function testUploadedFile() + { + $source = tempnam(sys_get_temp_dir(), 'source'); + $target = sys_get_temp_dir().'/sf.moved.file'; + @unlink($target); + + $kernel = new TestHttpKernel(); + $client = new Client($kernel); + + $files = array( + array('tmp_name' => $source, 'name' => 'original', 'type' => 'mime/original', 'size' => 123, 'error' => UPLOAD_ERR_OK), + new UploadedFile($source, 'original', 'mime/original', 123, UPLOAD_ERR_OK, true), + ); + + $file = null; + foreach ($files as $file) { + $client->request('POST', '/', array(), array('foo' => $file)); + + $files = $client->getRequest()->files->all(); + + $this->assertCount(1, $files); + + $file = $files['foo']; + + $this->assertEquals('original', $file->getClientOriginalName()); + $this->assertEquals('mime/original', $file->getClientMimeType()); + $this->assertEquals('123', $file->getClientSize()); + $this->assertTrue($file->isValid()); + } + + $file->move(dirname($target), basename($target)); + + $this->assertFileExists($target); + unlink($target); + } + + public function testUploadedFileWhenNoFileSelected() + { + $kernel = new TestHttpKernel(); + $client = new Client($kernel); + + $file = array('tmp_name' => '', 'name' => '', 'type' => '', 'size' => 0, 'error' => UPLOAD_ERR_NO_FILE); + + $client->request('POST', '/', array(), array('foo' => $file)); + + $files = $client->getRequest()->files->all(); + + $this->assertCount(1, $files); + $this->assertNull($files['foo']); + } + + public function testUploadedFileWhenSizeExceedsUploadMaxFileSize() + { + $source = tempnam(sys_get_temp_dir(), 'source'); + + $kernel = new TestHttpKernel(); + $client = new Client($kernel); + + $file = $this + ->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->setConstructorArgs(array($source, 'original', 'mime/original', 123, UPLOAD_ERR_OK, true)) + ->setMethods(array('getSize')) + ->getMock() + ; + + $file->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(INF)) + ; + + $client->request('POST', '/', array(), array($file)); + + $files = $client->getRequest()->files->all(); + + $this->assertCount(1, $files); + + $file = $files[0]; + + $this->assertFalse($file->isValid()); + $this->assertEquals(UPLOAD_ERR_INI_SIZE, $file->getError()); + $this->assertEquals('mime/original', $file->getClientMimeType()); + $this->assertEquals('original', $file->getClientOriginalName()); + $this->assertEquals(0, $file->getClientSize()); + + unlink($source); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Config/EnvParametersResourceTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Config/EnvParametersResourceTest.php new file mode 100644 index 0000000..ee5ecce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Config/EnvParametersResourceTest.php @@ -0,0 +1,106 @@ + + * + * 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\EnvParametersResource; + +class EnvParametersResourceTest extends \PHPUnit_Framework_TestCase +{ + protected $prefix = '__DUMMY_'; + protected $initialEnv; + protected $resource; + + protected function setUp() + { + $this->initialEnv = array( + $this->prefix.'1' => 'foo', + $this->prefix.'2' => 'bar', + ); + + foreach ($this->initialEnv as $key => $value) { + $_SERVER[$key] = $value; + } + + $this->resource = new EnvParametersResource($this->prefix); + } + + protected function tearDown() + { + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, $this->prefix)) { + unset($_SERVER[$key]); + } + } + } + + public function testGetResource() + { + $this->assertSame( + array('prefix' => $this->prefix, 'variables' => $this->initialEnv), + $this->resource->getResource(), + '->getResource() returns the resource' + ); + } + + public function testToString() + { + $this->assertSame( + serialize(array('prefix' => $this->prefix, 'variables' => $this->initialEnv)), + (string) $this->resource + ); + } + + public function testIsFreshNotChanged() + { + $this->assertTrue( + $this->resource->isFresh(time()), + '->isFresh() returns true if the variables have not changed' + ); + } + + public function testIsFreshValueChanged() + { + reset($this->initialEnv); + $_SERVER[key($this->initialEnv)] = 'baz'; + + $this->assertFalse( + $this->resource->isFresh(time()), + '->isFresh() returns false if a variable has been changed' + ); + } + + public function testIsFreshValueRemoved() + { + reset($this->initialEnv); + unset($_SERVER[key($this->initialEnv)]); + + $this->assertFalse( + $this->resource->isFresh(time()), + '->isFresh() returns false if a variable has been removed' + ); + } + + public function testIsFreshValueAdded() + { + $_SERVER[$this->prefix.'3'] = 'foo'; + + $this->assertFalse( + $this->resource->isFresh(time()), + '->isFresh() returns false if a variable has been added' + ); + } + + public function testSerializeUnserialize() + { + $this->assertEquals($this->resource, unserialize(serialize($this->resource))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Config/FileLocatorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Config/FileLocatorTest.php new file mode 100644 index 0000000..be59486 --- /dev/null +++ b/vendor/symfony/symfony/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/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php new file mode 100644 index 0000000..0460898 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolver; +use Symfony\Component\HttpFoundation\Request; + +class ControllerResolverTest extends \PHPUnit_Framework_TestCase +{ + public function testGetControllerWithoutControllerParameter() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->once())->method('warning')->with('Unable to look for the controller as the "_controller" parameter is missing.'); + $resolver = $this->createControllerResolver($logger); + + $request = Request::create('/'); + $this->assertFalse($resolver->getController($request), '->getController() returns false when the request has no _controller attribute'); + } + + public function testGetControllerWithLambda() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', $lambda = function () {}); + $controller = $resolver->getController($request); + $this->assertSame($lambda, $controller); + } + + public function testGetControllerWithObjectAndInvokeMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', $this); + $controller = $resolver->getController($request); + $this->assertSame($this, $controller); + } + + public function testGetControllerWithObjectAndMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', array($this, 'controllerMethod1')); + $controller = $resolver->getController($request); + $this->assertSame(array($this, 'controllerMethod1'), $controller); + } + + public function testGetControllerWithClassAndMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', array('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', 'controllerMethod4')); + $controller = $resolver->getController($request); + $this->assertSame(array('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', 'controllerMethod4'), $controller); + } + + public function testGetControllerWithObjectAndMethodAsString() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::controllerMethod1'); + $controller = $resolver->getController($request); + $this->assertInstanceOf('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', $controller[0], '->getController() returns a PHP callable'); + } + + public function testGetControllerWithClassAndInvokeMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest'); + $controller = $resolver->getController($request); + $this->assertInstanceOf('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', $controller); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetControllerOnObjectWithoutInvokeMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', new \stdClass()); + $resolver->getController($request); + } + + public function testGetControllerWithFunction() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\Controller\some_controller_function'); + $controller = $resolver->getController($request); + $this->assertSame('Symfony\Component\HttpKernel\Tests\Controller\some_controller_function', $controller); + } + + /** + * @dataProvider getUndefinedControllers + */ + public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null) + { + $resolver = $this->createControllerResolver(); + $this->setExpectedException($exceptionName, $exceptionMessage); + + $request = Request::create('/'); + $request->attributes->set('_controller', $controller); + $resolver->getController($request); + } + + public function getUndefinedControllers() + { + return array( + array(1, 'InvalidArgumentException', 'Unable to find controller "1".'), + array('foo', 'InvalidArgumentException', 'Unable to find controller "foo".'), + array('foo::bar', 'InvalidArgumentException', 'Class "foo" does not exist.'), + array('stdClass', 'InvalidArgumentException', 'Unable to find controller "stdClass".'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'), + ); + } + + public function testGetArguments() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $controller = array(new self(), 'testGetArguments'); + $this->assertEquals(array(), $resolver->getArguments($request, $controller), '->getArguments() returns an empty array if the method takes no arguments'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = array(new self(), 'controllerMethod1'); + $this->assertEquals(array('foo'), $resolver->getArguments($request, $controller), '->getArguments() returns an array of arguments for the controller method'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = array(new self(), 'controllerMethod2'); + $this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller), '->getArguments() uses default values if present'); + + $request->attributes->set('bar', 'bar'); + $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller), '->getArguments() overrides default values if provided in the request attributes'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = function ($foo) {}; + $this->assertEquals(array('foo'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = function ($foo, $bar = 'bar') {}; + $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = new self(); + $this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller)); + $request->attributes->set('bar', 'bar'); + $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('foobar', 'foobar'); + $controller = 'Symfony\Component\HttpKernel\Tests\Controller\some_controller_function'; + $this->assertEquals(array('foo', 'foobar'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('foobar', 'foobar'); + $controller = array(new self(), 'controllerMethod3'); + + try { + $resolver->getArguments($request, $controller); + $this->fail('->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); + } + + $request = Request::create('/'); + $controller = array(new self(), 'controllerMethod5'); + $this->assertEquals(array($request), $resolver->getArguments($request, $controller), '->getArguments() injects the request'); + } + + public function testCreateControllerCanReturnAnyCallable() + { + $mock = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolver', array('createController')); + $mock->expects($this->once())->method('createController')->will($this->returnValue('Symfony\Component\HttpKernel\Tests\Controller\some_controller_function')); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'foobar'); + $mock->getController($request); + } + + protected function createControllerResolver(LoggerInterface $logger = null) + { + return new ControllerResolver($logger); + } + + public function __invoke($foo, $bar = null) + { + } + + public function controllerMethod1($foo) + { + } + + protected function controllerMethod2($foo, $bar = null) + { + } + + protected function controllerMethod3($foo, $bar, $foobar) + { + } + + protected static function controllerMethod4() + { + } + + protected function controllerMethod5(Request $request) + { + } +} + +function some_controller_function($foo, $foobar) +{ +} + +class ControllerTest +{ + public function publicAction() + { + } + + private function privateAction() + { + } + + protected function protectedAction() + { + } + + public static function staticAction() + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php new file mode 100644 index 0000000..4a0dc26 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ConfigDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollect() + { + $kernel = new KernelForTest('test', true); + $c = new ConfigDataCollector(); + $c->setCacheVersionInfo(false); + $c->setKernel($kernel); + $c->collect(new Request(), new Response()); + + $this->assertSame('test', $c->getEnv()); + $this->assertTrue($c->isDebug()); + $this->assertSame('config', $c->getName()); + $this->assertSame('testkernel', $c->getAppName()); + $this->assertSame(PHP_VERSION, $c->getPhpVersion()); + $this->assertSame(Kernel::VERSION, $c->getSymfonyVersion()); + $this->assertNull($c->getToken()); + + // if else clause because we don't know it + if (extension_loaded('xdebug')) { + $this->assertTrue($c->hasXDebug()); + } else { + $this->assertFalse($c->hasXDebug()); + } + + // if else clause because we don't know it + if (((extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) + || + (extension_loaded('apc') && ini_get('apc.enabled')) + || + (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) + || + (extension_loaded('xcache') && ini_get('xcache.cacher')) + || + (extension_loaded('wincache') && ini_get('wincache.ocenabled')))) { + $this->assertTrue($c->hasAccelerator()); + } else { + $this->assertFalse($c->hasAccelerator()); + } + } +} + +class KernelForTest extends Kernel +{ + public function getName() + { + return 'testkernel'; + } + + public function registerBundles() + { + } + + public function getBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php new file mode 100644 index 0000000..a607d27 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * @author Nicolas Grekas + */ +class DumpDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testDump() + { + $data = new Data(array(array(123))); + + $collector = new DumpDataCollector(); + + $this->assertSame('dump', $collector->getName()); + + $collector->dump($data); + $line = __LINE__ - 1; + $this->assertSame(1, $collector->getDumpsCount()); + + $dump = $collector->getDumps('html'); + $this->assertTrue(isset($dump[0]['data'])); + $dump[0]['data'] = preg_replace('/^.*?
     "
    123\n
    \n", + 'name' => 'DumpDataCollectorTest.php', + 'file' => __FILE__, + 'line' => $line, + 'fileExcerpt' => false, + ), + ); + $this->assertEquals($xDump, $dump); + + $this->assertStringMatchesFormat( + 'a:1:{i:0;a:5:{s:4:"data";O:39:"Symfony\Component\VarDumper\Cloner\Data":4:{s:45:"Symfony\Component\VarDumper\Cloner\Datadata";a:1:{i:0;a:1:{i:0;i:123;}}s:49:"Symfony\Component\VarDumper\Cloner\DatamaxDepth";i:%i;s:57:"Symfony\Component\VarDumper\Cloner\DatamaxItemsPerDepth";i:%i;s:54:"Symfony\Component\VarDumper\Cloner\DatauseRefHandles";i:%i;}s:4:"name";s:25:"DumpDataCollectorTest.php";s:4:"file";s:%a', + str_replace("\0", '', $collector->serialize()) + ); + + $this->assertSame(0, $collector->getDumpsCount()); + $this->assertSame('a:0:{}', $collector->serialize()); + } + + public function testCollectDefault() + { + $data = new Data(array(array(123))); + + $collector = new DumpDataCollector(); + + $collector->dump($data); + $line = __LINE__ - 1; + + ob_start(); + $collector->collect(new Request(), new Response()); + $output = ob_get_clean(); + + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output); + $this->assertSame(1, $collector->getDumpsCount()); + $collector->serialize(); + } + + public function testCollectHtml() + { + $data = new Data(array(array(123))); + + $collector = new DumpDataCollector(null, 'test://%f:%l'); + + $collector->dump($data); + $line = __LINE__ - 1; + $file = __FILE__; + $xOutput = <<DumpDataCollectorTest.php on line {$line}: +123 +
    + +EOTXT; + + ob_start(); + $response = new Response(); + $response->headers->set('Content-Type', 'text/html'); + $collector->collect(new Request(), $response); + $output = ob_get_clean(); + $output = preg_replace('#<(script|style).*?#s', '', $output); + $output = preg_replace('/sf-dump-\d+/', 'sf-dump', $output); + + $this->assertSame($xOutput, $output); + $this->assertSame(1, $collector->getDumpsCount()); + $collector->serialize(); + } + + public function testFlush() + { + $data = new Data(array(array(456))); + $collector = new DumpDataCollector(); + $collector->dump($data); + $line = __LINE__ - 1; + + ob_start(); + $collector = null; + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php new file mode 100644 index 0000000..6c71f4c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/ExceptionDataCollectorTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ExceptionDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollect() + { + $e = new \Exception('foo', 500); + $c = new ExceptionDataCollector(); + $flattened = FlattenException::create($e); + $trace = $flattened->getTrace(); + + $this->assertFalse($c->hasException()); + + $c->collect(new Request(), new Response(), $e); + + $this->assertTrue($c->hasException()); + $this->assertEquals($flattened, $c->getException()); + $this->assertSame('foo', $c->getMessage()); + $this->assertSame(500, $c->getCode()); + $this->assertSame('exception', $c->getName()); + $this->assertSame($trace, $c->getTrace()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php new file mode 100644 index 0000000..4c1f380 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector; + +class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getCollectTestData + */ + public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount, $expectedScreamCount, $expectedPriorities = null) + { + $logger = $this->getMock('Symfony\Component\HttpKernel\Log\DebugLoggerInterface'); + $logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb)); + $logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue($logs)); + + $c = new LoggerDataCollector($logger); + $c->lateCollect(); + + $this->assertSame('logger', $c->getName()); + $this->assertSame($nb, $c->countErrors()); + $this->assertSame($expectedLogs ?: $logs, $c->getLogs()); + $this->assertSame($expectedDeprecationCount, $c->countDeprecations()); + $this->assertSame($expectedScreamCount, $c->countScreams()); + + if (isset($expectedPriorities)) { + $this->assertSame($expectedPriorities, $c->getPriorities()); + } + } + + public function getCollectTestData() + { + return array( + array( + 1, + array(array('message' => 'foo', 'context' => array(), 'priority' => 100, 'priorityName' => 'DEBUG')), + null, + 0, + 0, + ), + array( + 1, + array(array('message' => 'foo', 'context' => array('foo' => fopen(__FILE__, 'r')), 'priority' => 100, 'priorityName' => 'DEBUG')), + array(array('message' => 'foo', 'context' => array('foo' => 'Resource(stream)'), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, + 0, + ), + array( + 1, + array(array('message' => 'foo', 'context' => array('foo' => new \stdClass()), 'priority' => 100, 'priorityName' => 'DEBUG')), + array(array('message' => 'foo', 'context' => array('foo' => 'Object(stdClass)'), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, + 0, + ), + array( + 1, + array( + array('message' => 'foo', 'context' => array('type' => E_DEPRECATED, 'level' => E_ALL), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo2', 'context' => array('type' => E_USER_DEPRECATED, 'level' => E_ALL), 'priority' => 100, 'priorityName' => 'DEBUG'), + ), + null, + 2, + 0, + array(100 => array('count' => 2, 'name' => 'DEBUG')), + ), + array( + 1, + array(array('message' => 'foo3', 'context' => array('name' => 'E_USER_WARNING', 'type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG')), + array(array('message' => 'foo3', 'context' => array('name' => 'E_USER_WARNING', 'type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123, 'scream' => true), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, + 1, + ), + array( + 1, + array( + array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => -1, 'file' => __FILE__, 'line' => 123), 'priority' => 100, 'priorityName' => 'DEBUG'), + ), + array(array('message' => 'foo3', 'context' => array('name' => 'E_USER_WARNING', 'type' => E_USER_WARNING, 'level' => -1, 'file' => __FILE__, 'line' => 123, 'errorCount' => 2), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, + 1, + ), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/MemoryDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/MemoryDataCollectorTest.php new file mode 100644 index 0000000..340b428 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/MemoryDataCollectorTest.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class MemoryDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollect() + { + $collector = new MemoryDataCollector(); + $collector->collect(new Request(), new Response()); + + $this->assertInternalType('integer', $collector->getMemory()); + $this->assertInternalType('integer', $collector->getMemoryLimit()); + $this->assertSame('memory', $collector->getName()); + } + + /** @dataProvider getBytesConversionTestData */ + public function testBytesConversion($limit, $bytes) + { + $collector = new MemoryDataCollector(); + $method = new \ReflectionMethod($collector, 'convertToBytes'); + $method->setAccessible(true); + $this->assertEquals($bytes, $method->invoke($collector, $limit)); + } + + public function getBytesConversionTestData() + { + return array( + array('2k', 2048), + array('2 k', 2048), + array('8m', 8 * 1024 * 1024), + array('+2 k', 2048), + array('+2???k', 2048), + array('0x10', 16), + array('0xf', 15), + array('010', 8), + array('+0x10 k', 16 * 1024), + array('1g', 1024 * 1024 * 1024), + array('1G', 1024 * 1024 * 1024), + array('-1', -1), + array('0', 0), + array('2mk', 2048), // the unit must be the last char, so in this case 'k', not 'm' + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php new file mode 100644 index 0000000..2eb1c41 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -0,0 +1,222 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollect() + { + $c = new RequestDataCollector(); + + $c->collect($this->createRequest(), $this->createResponse()); + + $attributes = $c->getRequestAttributes(); + + $this->assertSame('request', $c->getName()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\HeaderBag', $c->getRequestHeaders()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestServer()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestCookies()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $attributes); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestRequest()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestQuery()); + $this->assertSame('html', $c->getFormat()); + $this->assertSame('foobar', $c->getRoute()); + $this->assertSame(array('name' => 'foo'), $c->getRouteParams()); + $this->assertSame(array(), $c->getSessionAttributes()); + $this->assertSame('en', $c->getLocale()); + $this->assertRegExp('/Resource\(stream#\d+\)/', $attributes->get('resource')); + $this->assertSame('Object(stdClass)', $attributes->get('object')); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\HeaderBag', $c->getResponseHeaders()); + $this->assertSame('OK', $c->getStatusText()); + $this->assertSame(200, $c->getStatusCode()); + $this->assertSame('application/json', $c->getContentType()); + } + + /** + * Test various types of controller callables. + */ + public function testControllerInspection() + { + // make sure we always match the line number + $r1 = new \ReflectionMethod($this, 'testControllerInspection'); + $r2 = new \ReflectionMethod($this, 'staticControllerMethod'); + $r3 = new \ReflectionClass($this); + // test name, callable, expected + $controllerTests = array( + array( + '"Regular" callable', + array($this, 'testControllerInspection'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'testControllerInspection', + 'file' => __FILE__, + 'line' => $r1->getStartLine(), + ), + ), + + array( + 'Closure', + function () { return 'foo'; }, + array( + 'class' => __NAMESPACE__.'\{closure}', + 'method' => null, + 'file' => __FILE__, + 'line' => __LINE__ - 5, + ), + ), + + array( + 'Static callback as string', + 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest::staticControllerMethod', + 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest::staticControllerMethod', + ), + + array( + 'Static callable with instance', + array($this, 'staticControllerMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine(), + ), + ), + + array( + 'Static callable with class name', + array('Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', 'staticControllerMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine(), + ), + ), + + array( + 'Callable with instance depending on __call()', + array($this, 'magicMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'magicMethod', + 'file' => 'n/a', + 'line' => 'n/a', + ), + ), + + array( + 'Callable with class name depending on __callStatic()', + array('Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', 'magicMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'magicMethod', + 'file' => 'n/a', + 'line' => 'n/a', + ), + ), + + array( + 'Invokable controller', + $this, + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => null, + 'file' => __FILE__, + 'line' => $r3->getStartLine(), + ), + ), + ); + + $c = new RequestDataCollector(); + $request = $this->createRequest(); + $response = $this->createResponse(); + foreach ($controllerTests as $controllerTest) { + $this->injectController($c, $controllerTest[1], $request); + $c->collect($request, $response); + $this->assertSame($controllerTest[2], $c->getController(), sprintf('Testing: %s', $controllerTest[0])); + } + } + + protected function createRequest() + { + $request = Request::create('http://test.com/foo?bar=baz'); + $request->attributes->set('foo', 'bar'); + $request->attributes->set('_route', 'foobar'); + $request->attributes->set('_route_params', array('name' => 'foo')); + $request->attributes->set('resource', fopen(__FILE__, 'r')); + $request->attributes->set('object', new \stdClass()); + + return $request; + } + + protected function createResponse() + { + $response = new Response(); + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'application/json'); + $response->headers->setCookie(new Cookie('foo', 'bar', 1, '/foo', 'localhost', true, true)); + $response->headers->setCookie(new Cookie('bar', 'foo', new \DateTime('@946684800'))); + $response->headers->setCookie(new Cookie('bazz', 'foo', '2000-12-12')); + + return $response; + } + + /** + * Inject the given controller callable into the data collector. + */ + protected function injectController($collector, $controller, $request) + { + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $httpKernel = new HttpKernel(new EventDispatcher(), $resolver); + $event = new FilterControllerEvent($httpKernel, $controller, $request, HttpKernelInterface::MASTER_REQUEST); + $collector->onKernelController($event); + } + + /** + * Dummy method used as controller callable. + */ + public static function staticControllerMethod() + { + throw new \LogicException('Unexpected method call'); + } + + /** + * Magic method to allow non existing methods to be called and delegated. + */ + public function __call($method, $args) + { + throw new \LogicException('Unexpected method call'); + } + + /** + * Magic method to allow non existing methods to be called and delegated. + */ + public static function __callStatic($method, $args) + { + throw new \LogicException('Unexpected method call'); + } + + public function __invoke() + { + throw new \LogicException('Unexpected method call'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.php new file mode 100644 index 0000000..0bdcccd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/TimeDataCollectorTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class TimeDataCollectorTest extends \PHPUnit_Framework_TestCase +{ + public function testCollect() + { + $c = new TimeDataCollector(); + + $request = new Request(); + $request->server->set('REQUEST_TIME', 1); + + $c->collect($request, new Response()); + + $this->assertEquals(1000, $c->getStartTime()); + + $request->server->set('REQUEST_TIME_FLOAT', 2); + + $c->collect($request, new Response()); + + $this->assertEquals(2000, $c->getStartTime()); + + $request = new Request(); + $c->collect($request, new Response()); + $this->assertEquals(0, $c->getStartTime()); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface'); + $kernel->expects($this->once())->method('getStartTime')->will($this->returnValue(123456)); + + $c = new TimeDataCollector($kernel); + $request = new Request(); + $request->server->set('REQUEST_TIME', 1); + + $c->collect($request, new Response()); + $this->assertEquals(123456000, $c->getStartTime()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php new file mode 100644 index 0000000..d5bccfd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DataCollector/Util/ValueExporterTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector\Util; + +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; + +class ValueExporterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ValueExporter + */ + private $valueExporter; + + protected function setUp() + { + $this->valueExporter = new ValueExporter(); + } + + public function testDateTime() + { + $dateTime = new \DateTime('2014-06-10 07:35:40', new \DateTimeZone('UTC')); + $this->assertSame('Object(DateTime) - 2014-06-10T07:35:40+0000', $this->valueExporter->exportValue($dateTime)); + } + + public function testDateTimeImmutable() + { + $dateTime = new \DateTimeImmutable('2014-06-10 07:35:40', new \DateTimeZone('UTC')); + $this->assertSame('Object(DateTimeImmutable) - 2014-06-10T07:35:40+0000', $this->valueExporter->exportValue($dateTime)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 0000000..f64d724 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testStopwatchSections() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch = new Stopwatch()); + $kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); }); + $request = Request::create('/'); + $response = $kernel->handle($request); + $kernel->terminate($request, $response); + + $events = $stopwatch->getSectionEvents($response->headers->get('X-Debug-Token')); + $this->assertEquals(array( + '__section__', + 'kernel.request', + 'kernel.controller', + 'controller', + 'kernel.response', + 'kernel.terminate', + ), array_keys($events)); + } + + public function testStopwatchCheckControllerOnRequestEvent() + { + $stopwatch = $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch') + ->setMethods(array('isStarted')) + ->getMock(); + $stopwatch->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch); + + $kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); }); + $request = Request::create('/'); + $kernel->handle($request); + } + + public function testStopwatchStopControllerOnRequestEvent() + { + $stopwatch = $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch') + ->setMethods(array('isStarted', 'stop', 'stopSection')) + ->getMock(); + $stopwatch->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + $stopwatch->expects($this->once()) + ->method('stop'); + $stopwatch->expects($this->once()) + ->method('stopSection'); + + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch); + + $kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); }); + $request = Request::create('/'); + $kernel->handle($request); + } + + public function testAddListenerNested() + { + $called1 = false; + $called2 = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('my-event', function () use ($dispatcher, &$called1, &$called2) { + $called1 = true; + $dispatcher->addListener('my-event', function () use (&$called2) { + $called2 = true; + }); + }); + $dispatcher->dispatch('my-event'); + $this->assertTrue($called1); + $this->assertFalse($called2); + $dispatcher->dispatch('my-event'); + $this->assertTrue($called2); + } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function () use ($eventDispatcher, &$listener1) { + $eventDispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } + + protected function getHttpKernel($dispatcher, $controller) + { + $resolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface'); + $resolver->expects($this->once())->method('getController')->will($this->returnValue($controller)); + $resolver->expects($this->once())->method('getArguments')->will($this->returnValue(array())); + + return new HttpKernel($dispatcher, $resolver); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php new file mode 100644 index 0000000..98266e9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass; +use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; + +class FragmentRendererPassTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests that content rendering not implementing FragmentRendererInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testContentRendererWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_content_renderer' => array(array('alias' => 'foo')), + ); + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.fragment_renderer here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $pass = new FragmentRendererPass(); + $pass->process($builder); + } + + public function testValidContentRenderer() + { + $services = array( + 'my_content_renderer' => array(array('alias' => 'foo')), + ); + + $renderer = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $renderer + ->expects($this->once()) + ->method('addMethodCall') + ->with('addRendererService', array('foo', 'my_content_renderer')) + ; + + $definition = $this->getMock('Symfony\Component\DependencyInjection\Definition'); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\HttpKernel\Tests\DependencyInjection\RendererService')); + $definition + ->expects($this->once()) + ->method('isPublic') + ->will($this->returnValue(true)) + ; + + $builder = $this->getMock( + 'Symfony\Component\DependencyInjection\ContainerBuilder', + array('hasDefinition', 'findTaggedServiceIds', 'getDefinition') + ); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.fragment_renderer here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->onConsecutiveCalls($renderer, $definition)); + + $pass = new FragmentRendererPass(); + $pass->process($builder); + } +} + +class RendererService implements FragmentRendererInterface +{ + public function render($uri, Request $request = null, array $options = array()) + { + } + + public function getName() + { + return 'test'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php new file mode 100644 index 0000000..34bd87a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class LazyLoadingFragmentHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function test() + { + $renderer = $this->getMock('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface'); + $renderer->expects($this->once())->method('getName')->will($this->returnValue('foo')); + $renderer->expects($this->any())->method('render')->will($this->returnValue(new Response())); + + $requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $requestStack->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $container->expects($this->once())->method('get')->will($this->returnValue($renderer)); + + $handler = new LazyLoadingFragmentHandler($container, $requestStack, false); + $handler->addRendererService('foo', 'foo'); + + $handler->render('/foo', 'foo'); + + // second call should not lazy-load anymore (see once() above on the get() method) + $handler->render('/foo', 'foo'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php new file mode 100644 index 0000000..3426f6f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; + +class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase +{ + public function testAutoloadMainExtension() + { + $container = $this->getMock( + 'Symfony\\Component\\DependencyInjection\\ContainerBuilder', + array('getExtensionConfig', 'loadFromExtension', 'getParameterBag') + ); + $params = $this->getMock('Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag'); + + $container->expects($this->at(0)) + ->method('getExtensionConfig') + ->with('loaded') + ->will($this->returnValue(array(array()))); + $container->expects($this->at(1)) + ->method('getExtensionConfig') + ->with('notloaded') + ->will($this->returnValue(array())); + $container->expects($this->once()) + ->method('loadFromExtension') + ->with('notloaded', array()); + + $container->expects($this->any()) + ->method('getParameterBag') + ->will($this->returnValue($params)); + $params->expects($this->any()) + ->method('all') + ->will($this->returnValue(array())); + $container->expects($this->any()) + ->method('getDefinitions') + ->will($this->returnValue(array())); + $container->expects($this->any()) + ->method('getAliases') + ->will($this->returnValue(array())); + $container->expects($this->any()) + ->method('getExtensions') + ->will($this->returnValue(array())); + + $configPass = new MergeExtensionConfigurationPass(array('loaded', 'notloaded')); + $configPass->process($container); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/AddRequestFormatsListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/AddRequestFormatsListenerTest.php new file mode 100644 index 0000000..3a82ce0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/AddRequestFormatsListenerTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\EventListener\AddRequestFormatsListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Test AddRequestFormatsListener class. + * + * @author Gildas Quemener + */ +class AddRequestFormatsListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AddRequestFormatsListener + */ + private $listener; + + protected function setUp() + { + $this->listener = new AddRequestFormatsListener(array('csv' => array('text/csv', 'text/plain'))); + } + + protected function tearDown() + { + $this->listener = null; + } + + public function testIsAnEventSubscriber() + { + $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventSubscriberInterface', $this->listener); + } + + public function testRegisteredEvent() + { + $this->assertEquals( + array(KernelEvents::REQUEST => 'onKernelRequest'), + AddRequestFormatsListener::getSubscribedEvents() + ); + } + + public function testSetAdditionalFormats() + { + $request = $this->getRequestMock(); + $event = $this->getGetResponseEventMock($request); + + $request->expects($this->once()) + ->method('setFormat') + ->with('csv', array('text/csv', 'text/plain')); + + $this->listener->onKernelRequest($event); + } + + protected function getRequestMock() + { + return $this->getMock('Symfony\Component\HttpFoundation\Request'); + } + + protected function getGetResponseEventMock(Request $request) + { + $event = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + + $event->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)); + + return $event; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php new file mode 100644 index 0000000..4584a48 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/DebugHandlersListenerTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Psr\Log\LogLevel; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpKernel\EventListener\DebugHandlersListener; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * DebugHandlersListenerTest. + * + * @author Nicolas Grekas + */ +class DebugHandlersListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testConfigure() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $userHandler = function () {}; + $listener = new DebugHandlersListener($userHandler, $logger); + $xHandler = new ExceptionHandler(); + $eHandler = new ErrorHandler(); + $eHandler->setExceptionHandler(array($xHandler, 'handle')); + + $exception = null; + set_error_handler(array($eHandler, 'handleError')); + set_exception_handler(array($eHandler, 'handleException')); + try { + $listener->configure(); + } catch (\Exception $exception) { + } + restore_exception_handler(); + restore_error_handler(); + + if (null !== $exception) { + throw $exception; + } + + $this->assertSame($userHandler, $xHandler->setHandler('var_dump')); + + $loggers = $eHandler->setLoggers(array()); + + $this->assertArrayHasKey(E_DEPRECATED, $loggers); + $this->assertSame(array($logger, LogLevel::INFO), $loggers[E_DEPRECATED]); + } + + public function testConsoleEvent() + { + $dispatcher = new EventDispatcher(); + $listener = new DebugHandlersListener(null); + $app = $this->getMock('Symfony\Component\Console\Application'); + $app->expects($this->once())->method('getHelperSet')->will($this->returnValue(new HelperSet())); + $command = new Command(__FUNCTION__); + $command->setApplication($app); + $event = new ConsoleEvent($command, new ArgvInput(), new ConsoleOutput()); + + $dispatcher->addSubscriber($listener); + + $xListeners = array( + KernelEvents::REQUEST => array(array($listener, 'configure')), + ConsoleEvents::COMMAND => array(array($listener, 'configure')), + ); + $this->assertSame($xListeners, $dispatcher->getListeners()); + + $exception = null; + $eHandler = new ErrorHandler(); + set_error_handler(array($eHandler, 'handleError')); + set_exception_handler(array($eHandler, 'handleException')); + try { + $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + } catch (\Exception $exception) { + } + restore_exception_handler(); + restore_error_handler(); + + if (null !== $exception) { + throw $exception; + } + + $xHandler = $eHandler->setExceptionHandler('var_dump'); + $this->assertInstanceOf('Closure', $xHandler); + + $app->expects($this->once()) + ->method('renderException'); + + $xHandler(new \Exception()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php new file mode 100644 index 0000000..9bbdade --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/DumpListenerTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\EventListener\DumpListener; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Symfony\Component\VarDumper\VarDumper; + +/** + * DumpListenerTest. + * + * @author Nicolas Grekas + */ +class DumpListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testSubscribedEvents() + { + $this->assertSame( + array(KernelEvents::REQUEST => array('configure', 1024)), + DumpListener::getSubscribedEvents() + ); + } + + public function testConfigure() + { + $prevDumper = VarDumper::setHandler('var_dump'); + VarDumper::setHandler($prevDumper); + + $cloner = new MockCloner(); + $dumper = new MockDumper(); + + ob_start(); + $exception = null; + $listener = new DumpListener($cloner, $dumper); + + try { + $listener->configure(); + + VarDumper::dump('foo'); + VarDumper::dump('bar'); + + $this->assertSame('+foo-+bar-', ob_get_clean()); + } catch (\Exception $exception) { + } + + VarDumper::setHandler($prevDumper); + + if (null !== $exception) { + throw $exception; + } + } +} + +class MockCloner implements ClonerInterface +{ + public function cloneVar($var) + { + return new Data(array($var.'-')); + } +} + +class MockDumper implements DataDumperInterface +{ + public function dump(Data $data) + { + $rawData = $data->getRawData(); + + echo '+'.$rawData[0]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php new file mode 100644 index 0000000..947af11 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Tests\Logger; + +/** + * ExceptionListenerTest. + * + * @author Robert Schönthal + * + * @group time-sensitive + */ +class ExceptionListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testConstruct() + { + $logger = new TestLogger(); + $l = new ExceptionListener('foo', $logger); + + $_logger = new \ReflectionProperty(get_class($l), 'logger'); + $_logger->setAccessible(true); + $_controller = new \ReflectionProperty(get_class($l), 'controller'); + $_controller->setAccessible(true); + + $this->assertSame($logger, $_logger->getValue($l)); + $this->assertSame('foo', $_controller->getValue($l)); + } + + /** + * @dataProvider provider + */ + public function testHandleWithoutLogger($event, $event2) + { + $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); + + $l = new ExceptionListener('foo'); + $l->onKernelException($event); + + $this->assertEquals(new Response('foo'), $event->getResponse()); + + try { + $l->onKernelException($event2); + $this->fail('RuntimeException expected'); + } catch (\RuntimeException $e) { + $this->assertSame('bar', $e->getMessage()); + $this->assertSame('foo', $e->getPrevious()->getMessage()); + } + } + + /** + * @dataProvider provider + */ + public function testHandleWithLogger($event, $event2) + { + $logger = new TestLogger(); + + $l = new ExceptionListener('foo', $logger); + $l->onKernelException($event); + + $this->assertEquals(new Response('foo'), $event->getResponse()); + + try { + $l->onKernelException($event2); + $this->fail('RuntimeException expected'); + } catch (\RuntimeException $e) { + $this->assertSame('bar', $e->getMessage()); + $this->assertSame('foo', $e->getPrevious()->getMessage()); + } + + $this->assertEquals(3, $logger->countErrors()); + $this->assertCount(3, $logger->getLogs('critical')); + } + + public function provider() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + return array(array(null, null)); + } + + $request = new Request(); + $exception = new \Exception('foo'); + $event = new GetResponseForExceptionEvent(new TestKernel(), $request, 'foo', $exception); + $event2 = new GetResponseForExceptionEvent(new TestKernelThatThrowsException(), $request, 'foo', $exception); + + return array( + array($event, $event2), + ); + } + + public function testSubRequestFormat() + { + $listener = new ExceptionListener('foo', $this->getMock('Psr\Log\LoggerInterface')); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + return new Response($request->getRequestFormat()); + })); + + $request = Request::create('/'); + $request->setRequestFormat('xml'); + + $event = new GetResponseForExceptionEvent($kernel, $request, 'foo', new \Exception('foo')); + $listener->onKernelException($event); + + $response = $event->getResponse(); + $this->assertEquals('xml', $response->getContent()); + } +} + +class TestLogger extends Logger implements DebugLoggerInterface +{ + public function countErrors() + { + return count($this->logs['critical']); + } +} + +class TestKernel implements HttpKernelInterface +{ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + return new Response('foo'); + } +} + +class TestKernelThatThrowsException implements HttpKernelInterface +{ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + throw new \RuntimeException('bar'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php new file mode 100644 index 0000000..fd5d63b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\EventListener\FragmentListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\UriSigner; + +class FragmentListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testOnlyTriggeredOnFragmentRoute() + { + $request = Request::create('http://example.com/foo?_path=foo%3Dbar%26_controller%3Dfoo'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request); + + $expected = $request->attributes->all(); + + $listener->onKernelRequest($event); + + $this->assertEquals($expected, $request->attributes->all()); + $this->assertTrue($request->query->has('_path')); + } + + public function testOnlyTriggeredIfControllerWasNotDefinedYet() + { + $request = Request::create('http://example.com/_fragment?_path=foo%3Dbar%26_controller%3Dfoo'); + $request->attributes->set('_controller', 'bar'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request, HttpKernelInterface::SUB_REQUEST); + + $expected = $request->attributes->all(); + + $listener->onKernelRequest($event); + + $this->assertEquals($expected, $request->attributes->all()); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + public function testAccessDeniedWithNonSafeMethods() + { + $request = Request::create('http://example.com/_fragment', 'POST'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + public function testAccessDeniedWithWrongSignature() + { + $request = Request::create('http://example.com/_fragment', 'GET', array(), array(), array(), array('REMOTE_ADDR' => '10.0.0.1')); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + } + + public function testWithSignature() + { + $signer = new UriSigner('foo'); + $request = Request::create($signer->sign('http://example.com/_fragment?_path=foo%3Dbar%26_controller%3Dfoo'), 'GET', array(), array(), array(), array('REMOTE_ADDR' => '10.0.0.1')); + + $listener = new FragmentListener($signer); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + + $this->assertEquals(array('foo' => 'bar', '_controller' => 'foo'), $request->attributes->get('_route_params')); + $this->assertFalse($request->query->has('_path')); + } + + private function createGetResponseEvent(Request $request, $requestType = HttpKernelInterface::MASTER_REQUEST) + { + return new GetResponseEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, $requestType); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php new file mode 100644 index 0000000..fe6eb2b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\LocaleListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +class LocaleListenerTest extends \PHPUnit_Framework_TestCase +{ + private $requestStack; + + protected function setUp() + { + $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array(), array(), '', false); + } + + public function testDefaultLocaleWithoutSession() + { + $listener = new LocaleListener($this->requestStack, 'fr'); + $event = $this->getEvent($request = Request::create('/')); + + $listener->onKernelRequest($event); + $this->assertEquals('fr', $request->getLocale()); + } + + public function testLocaleFromRequestAttribute() + { + $request = Request::create('/'); + session_name('foo'); + $request->cookies->set('foo', 'value'); + + $request->attributes->set('_locale', 'es'); + $listener = new LocaleListener($this->requestStack, 'fr'); + $event = $this->getEvent($request); + + $listener->onKernelRequest($event); + $this->assertEquals('es', $request->getLocale()); + } + + public function testLocaleSetForRoutingContext() + { + // the request context is updated + $context = $this->getMock('Symfony\Component\Routing\RequestContext'); + $context->expects($this->once())->method('setParameter')->with('_locale', 'es'); + + $router = $this->getMock('Symfony\Component\Routing\Router', array('getContext'), array(), '', false); + $router->expects($this->once())->method('getContext')->will($this->returnValue($context)); + + $request = Request::create('/'); + + $request->attributes->set('_locale', 'es'); + $listener = new LocaleListener($this->requestStack, 'fr', $router); + $listener->onKernelRequest($this->getEvent($request)); + } + + public function testRouterResetWithParentRequestOnKernelFinishRequest() + { + // the request context is updated + $context = $this->getMock('Symfony\Component\Routing\RequestContext'); + $context->expects($this->once())->method('setParameter')->with('_locale', 'es'); + + $router = $this->getMock('Symfony\Component\Routing\Router', array('getContext'), array(), '', false); + $router->expects($this->once())->method('getContext')->will($this->returnValue($context)); + + $parentRequest = Request::create('/'); + $parentRequest->setLocale('es'); + + $this->requestStack->expects($this->once())->method('getParentRequest')->will($this->returnValue($parentRequest)); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\FinishRequestEvent', array(), array(), '', false); + + $listener = new LocaleListener($this->requestStack, 'fr', $router); + $listener->onKernelFinishRequest($event); + } + + public function testRequestLocaleIsNotOverridden() + { + $request = Request::create('/'); + $request->setLocale('de'); + $listener = new LocaleListener($this->requestStack, '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/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php new file mode 100644 index 0000000..d452ceb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\EventListener\ProfilerListener; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Kernel; + +class ProfilerListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test a master and sub request with an exception and `onlyException` profiler option enabled. + */ + public function testKernelTerminate() + { + $profile = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profile') + ->disableOriginalConstructor() + ->getMock(); + + $profiler = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $profiler->expects($this->once()) + ->method('collect') + ->will($this->returnValue($profile)); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + + $masterRequest = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $subRequest = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $response = $this->getMockBuilder('Symfony\Component\HttpFoundation\Response') + ->disableOriginalConstructor() + ->getMock(); + + $requestStack = new RequestStack(); + $requestStack->push($masterRequest); + + $onlyException = true; + $listener = new ProfilerListener($profiler, $requestStack, null, $onlyException); + + // master request + $listener->onKernelResponse(new FilterResponseEvent($kernel, $masterRequest, Kernel::MASTER_REQUEST, $response)); + + // sub request + $listener->onKernelException(new GetResponseForExceptionEvent($kernel, $subRequest, Kernel::SUB_REQUEST, new HttpException(404))); + $listener->onKernelResponse(new FilterResponseEvent($kernel, $subRequest, Kernel::SUB_REQUEST, $response)); + + $listener->onKernelTerminate(new PostResponseEvent($kernel, $masterRequest, $response)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ResponseListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ResponseListenerTest.php new file mode 100644 index 0000000..821688e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/ResponseListenerTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\EventListener\ResponseListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ResponseListenerTest extends \PHPUnit_Framework_TestCase +{ + private $dispatcher; + + private $kernel; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $listener = new ResponseListener('UTF-8'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + + $this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->kernel = null; + } + + public function testFilterDoesNothingForSubRequests() + { + $response = new Response('foo'); + + $event = new FilterResponseEvent($this->kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('content-type')); + } + + public function testFilterSetsNonDefaultCharsetIfNotOverridden() + { + $listener = new ResponseListener('ISO-8859-15'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'), 1); + + $response = new Response('foo'); + + $event = new FilterResponseEvent($this->kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('ISO-8859-15', $response->getCharset()); + } + + public function testFilterDoesNothingIfCharsetIsOverridden() + { + $listener = new ResponseListener('ISO-8859-15'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'), 1); + + $response = new Response('foo'); + $response->setCharset('ISO-8859-1'); + + $event = new FilterResponseEvent($this->kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('ISO-8859-1', $response->getCharset()); + } + + public function testFiltersSetsNonDefaultCharsetIfNotOverriddenOnNonTextContentType() + { + $listener = new ResponseListener('ISO-8859-15'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'), 1); + + $response = new Response('foo'); + $request = Request::create('/'); + $request->setRequestFormat('application/json'); + + $event = new FilterResponseEvent($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('ISO-8859-15', $response->getCharset()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php new file mode 100644 index 0000000..d0bc61a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -0,0 +1,161 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\Routing\RequestContext; + +class RouterListenerTest extends \PHPUnit_Framework_TestCase +{ + private $requestStack; + + protected function setUp() + { + $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array(), array(), '', false); + } + + /** + * @dataProvider getPortData + */ + public function testPort($defaultHttpPort, $defaultHttpsPort, $uri, $expectedHttpPort, $expectedHttpsPort) + { + $urlMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\UrlMatcherInterface') + ->disableOriginalConstructor() + ->getMock(); + $context = new RequestContext(); + $context->setHttpPort($defaultHttpPort); + $context->setHttpsPort($defaultHttpsPort); + $urlMatcher->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($context)); + + $listener = new RouterListener($urlMatcher, $this->requestStack); + $event = $this->createGetResponseEventForUri($uri); + $listener->onKernelRequest($event); + + $this->assertEquals($expectedHttpPort, $context->getHttpPort()); + $this->assertEquals($expectedHttpsPort, $context->getHttpsPort()); + $this->assertEquals(0 === strpos($uri, 'https') ? 'https' : 'http', $context->getScheme()); + } + + public function getPortData() + { + return array( + array(80, 443, 'http://localhost/', 80, 443), + array(80, 443, 'http://localhost:90/', 90, 443), + array(80, 443, 'https://localhost/', 80, 443), + array(80, 443, 'https://localhost:90/', 80, 90), + ); + } + + /** + * @param string $uri + * + * @return GetResponseEvent + */ + private function createGetResponseEventForUri($uri) + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $request = Request::create($uri); + $request->attributes->set('_controller', null); // Prevents going in to routing process + + return new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidMatcher() + { + new RouterListener(new \stdClass(), $this->requestStack); + } + + public function testRequestMatcher() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $request = Request::create('http://localhost/'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $requestMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface'); + $requestMatcher->expects($this->once()) + ->method('matchRequest') + ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) + ->will($this->returnValue(array())); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext()); + $listener->onKernelRequest($event); + } + + public function testSubRequestWithDifferentMethod() + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $request = Request::create('http://localhost/', 'post'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $requestMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface'); + $requestMatcher->expects($this->any()) + ->method('matchRequest') + ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) + ->will($this->returnValue(array())); + + $context = new RequestContext(); + $requestMatcher->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($context)); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext()); + $listener->onKernelRequest($event); + + // sub-request with another HTTP method + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $request = Request::create('http://localhost/', 'get'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST); + + $listener->onKernelRequest($event); + + $this->assertEquals('GET', $context->getMethod()); + } + + /** + * @dataProvider getLoggingParameterData + */ + public function testLoggingParameter($parameter, $log) + { + $requestMatcher = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface'); + $requestMatcher->expects($this->once()) + ->method('matchRequest') + ->will($this->returnValue($parameter)); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->once()) + ->method('info') + ->with($this->equalTo($log)); + + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $request = Request::create('http://localhost/'); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext(), $logger); + $listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); + } + + public function getLoggingParameterData() + { + return array( + array(array('_route' => 'foo'), 'Matched route "foo".'), + array(array(), 'Matched route "n/a".'), + ); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/SurrogateListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/SurrogateListenerTest.php new file mode 100644 index 0000000..1a0acf9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/SurrogateListenerTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\EventListener\SurrogateListener; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class SurrogateListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testFilterDoesNothingForSubRequests() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo '); + $listener = new SurrogateListener(new Esi()); + + $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('Surrogate-Control')); + } + + public function testFilterWhenThereIsSomeEsiIncludes() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo '); + $listener = new SurrogateListener(new Esi()); + + $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('content="ESI/1.0"', $event->getResponse()->headers->get('Surrogate-Control')); + } + + public function testFilterWhenThereIsNoEsiIncludes() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $response = new Response('foo'); + $listener = new SurrogateListener(new Esi()); + + $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('Surrogate-Control')); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php new file mode 100644 index 0000000..cbaaf5f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/TestSessionListenerTest.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +/** + * SessionListenerTest. + * + * Tests SessionListener. + * + * @author Bulat Shakirzyanov + */ +class TestSessionListenerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var TestSessionListener + */ + private $listener; + + /** + * @var SessionInterface + */ + private $session; + + protected function setUp() + { + $this->listener = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\EventListener\TestSessionListener'); + $this->session = $this->getSession(); + } + + public function testShouldSaveMasterRequestSession() + { + $this->sessionHasBeenStarted(); + $this->sessionMustBeSaved(); + + $this->filterResponse(new Request()); + } + + public function testShouldNotSaveSubRequestSession() + { + $this->sessionMustNotBeSaved(); + + $this->filterResponse(new Request(), HttpKernelInterface::SUB_REQUEST); + } + + public function testDoesNotDeleteCookieIfUsingSessionLifetime() + { + $this->sessionHasBeenStarted(); + + $params = session_get_cookie_params(); + session_set_cookie_params(0, $params['path'], $params['domain'], $params['secure'], $params['httponly']); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST); + $cookies = $response->headers->getCookies(); + + $this->assertEquals(0, reset($cookies)->getExpiresTime()); + } + + public function testUnstartedSessionIsNotSave() + { + $this->sessionHasNotBeenStarted(); + $this->sessionMustNotBeSaved(); + + $this->filterResponse(new Request()); + } + + private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST) + { + $request->setSession($this->session); + $response = new Response(); + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $event = new FilterResponseEvent($kernel, $request, $type, $response); + + $this->listener->onKernelResponse($event); + + $this->assertSame($response, $event->getResponse()); + + return $response; + } + + private function sessionMustNotBeSaved() + { + $this->session->expects($this->never()) + ->method('save'); + } + + private function sessionMustBeSaved() + { + $this->session->expects($this->once()) + ->method('save'); + } + + private function sessionHasBeenStarted() + { + $this->session->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + } + + private function sessionHasNotBeenStarted() + { + $this->session->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + } + + private function getSession() + { + $mock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session') + ->disableOriginalConstructor() + ->getMock(); + + // set return value for getName() + $mock->expects($this->any())->method('getName')->will($this->returnValue('MOCKSESSID')); + + return $mock; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php new file mode 100644 index 0000000..c37d647 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/EventListener/TranslatorListenerTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\TranslatorListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class TranslatorListenerTest extends \PHPUnit_Framework_TestCase +{ + private $listener; + private $translator; + private $requestStack; + + protected function setUp() + { + $this->translator = $this->getMock('Symfony\Component\Translation\TranslatorInterface'); + $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack'); + $this->listener = new TranslatorListener($this->translator, $this->requestStack); + } + + public function testLocaleIsSetInOnKernelRequest() + { + $this->translator + ->expects($this->once()) + ->method('setLocale') + ->with($this->equalTo('fr')); + + $event = new GetResponseEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + } + + public function testDefaultLocaleIsUsedOnExceptionsInOnKernelRequest() + { + $this->translator + ->expects($this->at(0)) + ->method('setLocale') + ->will($this->throwException(new \InvalidArgumentException())); + $this->translator + ->expects($this->at(1)) + ->method('setLocale') + ->with($this->equalTo('en')); + + $event = new GetResponseEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + } + + public function testLocaleIsSetInOnKernelFinishRequestWhenParentRequestExists() + { + $this->translator + ->expects($this->once()) + ->method('setLocale') + ->with($this->equalTo('fr')); + + $this->setMasterRequest($this->createRequest('fr')); + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + public function testLocaleIsNotSetInOnKernelFinishRequestWhenParentRequestDoesNotExist() + { + $this->translator + ->expects($this->never()) + ->method('setLocale'); + + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + public function testDefaultLocaleIsUsedOnExceptionsInOnKernelFinishRequest() + { + $this->translator + ->expects($this->at(0)) + ->method('setLocale') + ->will($this->throwException(new \InvalidArgumentException())); + $this->translator + ->expects($this->at(1)) + ->method('setLocale') + ->with($this->equalTo('en')); + + $this->setMasterRequest($this->createRequest('fr')); + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + private function createHttpKernel() + { + return $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + } + + private function createRequest($locale) + { + $request = new Request(); + $request->setLocale($locale); + + return $request; + } + + private function setMasterRequest($request) + { + $this->requestStack + ->expects($this->any()) + ->method('getParentRequest') + ->will($this->returnValue($request)); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/BaseBundle/Resources/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/BaseBundle/Resources/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/BaseBundle/Resources/hide.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/BaseBundle/Resources/hide.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle1Bundle/bar.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle1Bundle/bar.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle1Bundle/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle1Bundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle2Bundle/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Bundle2Bundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ChildBundle/Resources/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ChildBundle/Resources/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ChildBundle/Resources/hide.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ChildBundle/Resources/hide.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php new file mode 100644 index 0000000..c8bfd36 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionAbsentBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionAbsentBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php new file mode 100644 index 0000000..3b31781 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionLoadedBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class ExtensionLoadedExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php new file mode 100644 index 0000000..3af81cb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionLoadedBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionLoadedBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php new file mode 100644 index 0000000..0fd6431 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\DependencyInjection; + +class ExtensionNotValidExtension +{ + public function getAlias() + { + return 'extension_not_valid'; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php new file mode 100644 index 0000000..34e2920 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionNotValidBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php new file mode 100644 index 0000000..f3fd14b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command; + +use Symfony\Component\Console\Command\Command; + +class FooCommand extends Command +{ + protected function configure() + { + $this->setName('foo'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php new file mode 100644 index 0000000..e42f816 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class ExtensionPresentExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php new file mode 100644 index 0000000..36a7ad4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionPresentBundle extends Bundle +{ +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForOverrideName.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForOverrideName.php new file mode 100644 index 0000000..a1102ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForOverrideName.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; + +class KernelForOverrideName extends Kernel +{ + protected $name = 'overridden'; + + public function registerBundles() + { + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php new file mode 100644 index 0000000..5fd61bb --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; + +class KernelForTest extends Kernel +{ + public function getBundleMap() + { + return $this->bundleMap; + } + + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function isBooted() + { + return $this->booted; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/BaseBundle/hide.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/BaseBundle/hide.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/ChildBundle/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/ChildBundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/FooBundle/foo.txt b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/Resources/FooBundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestClient.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestClient.php new file mode 100644 index 0000000..e7d60cf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestClient.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Client; + +class TestClient extends Client +{ + protected function getScript($request) + { + $script = parent::getScript($request); + + $autoload = file_exists(__DIR__.'/../../vendor/autoload.php') + ? __DIR__.'/../../vendor/autoload.php' + : __DIR__.'/../../../../../../vendor/autoload.php' + ; + + $script = preg_replace('/(\->register\(\);)/', "$0\nrequire_once '$autoload';\n", $script); + + return $script; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php new file mode 100644 index 0000000..da7ef5b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fixtures/TestEventDispatcher.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestEventDispatcher extends EventDispatcher implements TraceableEventDispatcherInterface +{ + public function getCalledListeners() + { + return array('foo'); + } + + public function getNotCalledListeners() + { + return array('bar'); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php new file mode 100644 index 0000000..88c92b6 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\UriSigner; + +class EsiFragmentRendererTest extends \PHPUnit_Framework_TestCase +{ + public function testRenderFallbackToInlineStrategyIfEsiNotSupported() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true)); + $strategy->render('/', Request::create('/')); + } + + public function testRender() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $this->assertEquals('', $strategy->render('/', $request)->getContent()); + $this->assertEquals("\n", $strategy->render('/', $request, array('comment' => 'This is a comment'))->getContent()); + $this->assertEquals('', $strategy->render('/', $request, array('alt' => 'foo'))->getContent()); + } + + public function testRenderControllerReference() + { + $signer = new UriSigner('foo'); + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(), $signer); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $reference = new ControllerReference('main_controller', array(), array()); + $altReference = new ControllerReference('alt_controller', array(), array()); + + $this->assertEquals( + '', + $strategy->render($reference, $request, array('alt' => $altReference))->getContent() + ); + } + + /** + * @expectedException \LogicException + */ + public function testRenderControllerReferenceWithoutSignerThrowsException() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $strategy->render(new ControllerReference('main_controller'), $request); + } + + /** + * @expectedException \LogicException + */ + public function testRenderAltControllerReferenceWithoutSignerThrowsException() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $strategy->render('/', $request, array('alt' => new ControllerReference('alt_controller'))); + } + + private function getInlineStrategy($called = false) + { + $inline = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer')->disableOriginalConstructor()->getMock(); + + if ($called) { + $inline->expects($this->once())->method('render'); + } + + return $inline; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php new file mode 100644 index 0000000..79fee3f --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class FragmentHandlerTest extends \PHPUnit_Framework_TestCase +{ + private $requestStack; + + protected function setUp() + { + $this->requestStack = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') + ->disableOriginalConstructor() + ->getMock() + ; + $this->requestStack + ->expects($this->any()) + ->method('getCurrentRequest') + ->will($this->returnValue(Request::create('/'))) + ; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRenderWhenRendererDoesNotExist() + { + $handler = new FragmentHandler($this->requestStack); + $handler->render('/', 'foo'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRenderWithUnknownRenderer() + { + $handler = $this->getHandler($this->returnValue(new Response('foo'))); + + $handler->render('/', 'bar'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Error when rendering "http://localhost/" (Status code is 404). + */ + public function testDeliverWithUnsuccessfulResponse() + { + $handler = $this->getHandler($this->returnValue(new Response('foo', 404))); + + $handler->render('/', 'foo'); + } + + public function testRender() + { + $handler = $this->getHandler($this->returnValue(new Response('foo')), array('/', Request::create('/'), array('foo' => 'foo', 'ignore_errors' => true))); + + $this->assertEquals('foo', $handler->render('/', 'foo', array('foo' => 'foo'))); + } + + protected function getHandler($returnValue, $arguments = array()) + { + $renderer = $this->getMock('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface'); + $renderer + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')) + ; + $e = $renderer + ->expects($this->any()) + ->method('render') + ->will($returnValue) + ; + + if ($arguments) { + call_user_func_array(array($e, 'with'), $arguments); + } + + $handler = new FragmentHandler($this->requestStack); + $handler->addRenderer($renderer); + + return $handler; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php new file mode 100644 index 0000000..2f266db --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer; +use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\HttpFoundation\Request; + +class HIncludeFragmentRendererTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \LogicException + */ + public function testRenderExceptionWhenControllerAndNoSigner() + { + $strategy = new HIncludeFragmentRenderer(); + $strategy->render(new ControllerReference('main_controller', array(), array()), Request::create('/')); + } + + public function testRenderWithControllerAndSigner() + { + $strategy = new HIncludeFragmentRenderer(null, new UriSigner('foo')); + + $this->assertEquals('', $strategy->render(new ControllerReference('main_controller', array(), array()), Request::create('/'))->getContent()); + } + + public function testRenderWithUri() + { + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('', $strategy->render('/foo', Request::create('/'))->getContent()); + + $strategy = new HIncludeFragmentRenderer(null, new UriSigner('foo')); + $this->assertEquals('', $strategy->render('/foo', Request::create('/'))->getContent()); + } + + public function testRenderWithDefault() + { + // only default + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); + + // only global default + $strategy = new HIncludeFragmentRenderer(null, null, 'global_default'); + $this->assertEquals('global_default', $strategy->render('/foo', Request::create('/'), array())->getContent()); + + // global default and default + $strategy = new HIncludeFragmentRenderer(null, null, 'global_default'); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); + } + + public function testRenderWithAttributesOptions() + { + // with id + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'id' => 'bar'))->getContent()); + + // with attributes + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'attributes' => array('p1' => 'v1', 'p2' => 'v2')))->getContent()); + + // with id & attributes + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'id' => 'bar', 'attributes' => array('p1' => 'v1', 'p2' => 'v2')))->getContent()); + } + + public function testRenderWithDefaultText() + { + $engine = $this->getMock('Symfony\\Component\\Templating\\EngineInterface'); + $engine->expects($this->once()) + ->method('exists') + ->with('default') + ->will($this->throwException(new \InvalidArgumentException())); + + // only default + $strategy = new HIncludeFragmentRenderer($engine); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php new file mode 100644 index 0000000..4e487a4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase +{ + public function testRender() + { + $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo')))); + + $this->assertEquals('foo', $strategy->render('/', Request::create('/'))->getContent()); + } + + public function testRenderWithControllerReference() + { + $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo')))); + + $this->assertEquals('foo', $strategy->render(new ControllerReference('main_controller', array(), array()), Request::create('/'))->getContent()); + } + + public function testRenderWithObjectsAsAttributes() + { + $object = new \stdClass(); + + $subRequest = Request::create('/_fragment?_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dmain_controller'); + $subRequest->attributes->replace(array('object' => $object, '_format' => 'html', '_controller' => 'main_controller', '_locale' => 'en')); + $subRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest)); + + $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/')); + } + + public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController() + { + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', array('getController')); + $resolver + ->expects($this->once()) + ->method('getController') + ->will($this->returnValue(function (\stdClass $object, Bar $object1) { + return new Response($object1->getBar()); + })) + ; + + $kernel = new HttpKernel(new EventDispatcher(), $resolver); + $renderer = new InlineFragmentRenderer($kernel); + + $response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/')); + $this->assertEquals('bar', $response->getContent()); + } + + public function testRenderWithTrustedHeaderDisabled() + { + $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP); + + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, ''); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest(Request::create('/'))); + $strategy->render('/', Request::create('/')); + + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName); + } + + /** + * @expectedException \RuntimeException + */ + public function testRenderExceptionNoIgnoreErrors() + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $dispatcher->expects($this->never())->method('dispatch'); + + $strategy = new InlineFragmentRenderer($this->getKernel($this->throwException(new \RuntimeException('foo'))), $dispatcher); + + $this->assertEquals('foo', $strategy->render('/', Request::create('/'))->getContent()); + } + + public function testRenderExceptionIgnoreErrors() + { + $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $dispatcher->expects($this->once())->method('dispatch')->with(KernelEvents::EXCEPTION); + + $strategy = new InlineFragmentRenderer($this->getKernel($this->throwException(new \RuntimeException('foo'))), $dispatcher); + + $this->assertEmpty($strategy->render('/', Request::create('/'), array('ignore_errors' => true))->getContent()); + } + + public function testRenderExceptionIgnoreErrorsWithAlt() + { + $strategy = new InlineFragmentRenderer($this->getKernel($this->onConsecutiveCalls( + $this->throwException(new \RuntimeException('foo')), + $this->returnValue(new Response('bar')) + ))); + + $this->assertEquals('bar', $strategy->render('/', Request::create('/'), array('ignore_errors' => true, 'alt' => '/foo'))->getContent()); + } + + private function getKernel($returnValue) + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel + ->expects($this->any()) + ->method('handle') + ->will($returnValue) + ; + + return $kernel; + } + + /** + * Creates a Kernel expecting a request equals to $request + * Allows delta in comparison in case REQUEST_TIME changed by 1 second. + */ + private function getKernelExpectingRequest(Request $request) + { + $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); + $kernel + ->expects($this->any()) + ->method('handle') + ->with($this->equalTo($request, 1)) + ; + + return $kernel; + } + + public function testExceptionInSubRequestsDoesNotMangleOutputBuffers() + { + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $resolver + ->expects($this->once()) + ->method('getController') + ->will($this->returnValue(function () { + ob_start(); + echo 'bar'; + throw new \RuntimeException(); + })) + ; + $resolver + ->expects($this->once()) + ->method('getArguments') + ->will($this->returnValue(array())) + ; + + $kernel = new HttpKernel(new EventDispatcher(), $resolver); + $renderer = new InlineFragmentRenderer($kernel); + + // simulate a main request with output buffering + ob_start(); + echo 'Foo'; + + // simulate a sub-request with output buffering and an exception + $renderer->render('/', Request::create('/'), array('ignore_errors' => true)); + + $this->assertEquals('Foo', ob_get_clean()); + } + + public function testESIHeaderIsKeptInSubrequest() + { + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + + if (Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) { + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + } + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $strategy->render('/', $request); + } + + public function testESIHeaderIsKeptInSubrequestWithTrustedHeaderDisabled() + { + $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP); + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, ''); + + $this->testESIHeaderIsKeptInSubrequest(); + + Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, $trustedHeaderName); + } +} + +class Bar +{ + public $bar = 'bar'; + + public function getBar() + { + return $this->bar; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/RoutableFragmentRendererTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/RoutableFragmentRendererTest.php new file mode 100644 index 0000000..1b5f471 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Fragment/RoutableFragmentRendererTest.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +class RoutableFragmentRendererTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getGenerateFragmentUriData + */ + public function testGenerateFragmentUri($uri, $controller) + { + $this->assertEquals($uri, $this->callGenerateFragmentUriMethod($controller, Request::create('/'))); + } + + /** + * @dataProvider getGenerateFragmentUriData + */ + public function testGenerateAbsoluteFragmentUri($uri, $controller) + { + $this->assertEquals('http://localhost'.$uri, $this->callGenerateFragmentUriMethod($controller, Request::create('/'), true)); + } + + public function getGenerateFragmentUriData() + { + return array( + array('/_fragment?_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array(), array())), + array('/_fragment?_path=_format%3Dxml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('_format' => 'xml'), array())), + array('/_fragment?_path=foo%3Dfoo%26_format%3Djson%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => 'foo', '_format' => 'json'), array())), + array('/_fragment?bar=bar&_path=foo%3Dfoo%26_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => 'foo'), array('bar' => 'bar'))), + array('/_fragment?foo=foo&_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array(), array('foo' => 'foo'))), + array('/_fragment?_path=foo%255B0%255D%3Dfoo%26foo%255B1%255D%3Dbar%26_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => array('foo', 'bar')), array())), + ); + } + + public function testGenerateFragmentUriWithARequest() + { + $request = Request::create('/'); + $request->attributes->set('_format', 'json'); + $request->setLocale('fr'); + $controller = new ControllerReference('controller', array(), array()); + + $this->assertEquals('/_fragment?_path=_format%3Djson%26_locale%3Dfr%26_controller%3Dcontroller', $this->callGenerateFragmentUriMethod($controller, $request)); + } + + /** + * @expectedException \LogicException + * @dataProvider getGenerateFragmentUriDataWithNonScalar + */ + public function testGenerateFragmentUriWithNonScalar($controller) + { + $this->callGenerateFragmentUriMethod($controller, Request::create('/')); + } + + public function getGenerateFragmentUriDataWithNonScalar() + { + return array( + array(new ControllerReference('controller', array('foo' => new Foo(), 'bar' => 'bar'), array())), + array(new ControllerReference('controller', array('foo' => array('foo' => 'foo'), 'bar' => array('bar' => new Foo())), array())), + ); + } + + private function callGenerateFragmentUriMethod(ControllerReference $reference, Request $request, $absolute = false) + { + $renderer = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer'); + $r = new \ReflectionObject($renderer); + $m = $r->getMethod('generateFragmentUri'); + $m->setAccessible(true); + + return $m->invoke($renderer, $reference, $request, $absolute); + } +} + +class Foo +{ + public $foo; + + public function getFoo() + { + return $this->foo; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php new file mode 100644 index 0000000..0d52ce8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/EsiTest.php @@ -0,0 +1,247 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class EsiTest extends \PHPUnit_Framework_TestCase +{ + public function testHasSurrogateEsiCapability() + { + $esi = new Esi(); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $this->assertTrue($esi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'foobar'); + $this->assertFalse($esi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $this->assertFalse($esi->hasSurrogateCapability($request)); + } + + public function testAddSurrogateEsiCapability() + { + $esi = new Esi(); + + $request = Request::create('/'); + $esi->addSurrogateCapability($request); + $this->assertEquals('symfony2="ESI/1.0"', $request->headers->get('Surrogate-Capability')); + + $esi->addSurrogateCapability($request); + $this->assertEquals('symfony2="ESI/1.0", symfony2="ESI/1.0"', $request->headers->get('Surrogate-Capability')); + } + + public function testAddSurrogateControl() + { + $esi = new Esi(); + + $response = new Response('foo '); + $esi->addSurrogateControl($response); + $this->assertEquals('content="ESI/1.0"', $response->headers->get('Surrogate-Control')); + + $response = new Response('foo'); + $esi->addSurrogateControl($response); + $this->assertEquals('', $response->headers->get('Surrogate-Control')); + } + + public function testNeedsEsiParsing() + { + $esi = new Esi(); + + $response = new Response(); + $response->headers->set('Surrogate-Control', 'content="ESI/1.0"'); + $this->assertTrue($esi->needsParsing($response)); + + $response = new Response(); + $this->assertFalse($esi->needsParsing($response)); + } + + public function testRenderIncludeTag() + { + $esi = new Esi(); + + $this->assertEquals('', $esi->renderIncludeTag('/', '/alt', true)); + $this->assertEquals('', $esi->renderIncludeTag('/', '/alt', false)); + $this->assertEquals('', $esi->renderIncludeTag('/')); + $this->assertEquals(''."\n".'', $esi->renderIncludeTag('/', '/alt', true, 'some comment')); + } + + public function testProcessDoesNothingIfContentTypeIsNotHtml() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(); + $response->headers->set('Content-Type', 'text/plain'); + $esi->process($request, $response); + + $this->assertFalse($response->headers->has('x-body-eval')); + } + + public function testMultilineEsiRemoveTagsAreRemoved() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(' Keep this'." And this"); + $esi->process($request, $response); + + $this->assertEquals(' Keep this And this', $response->getContent()); + } + + public function testCommentTagsAreRemoved() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(' Keep this'); + $esi->process($request, $response); + + $this->assertEquals(' Keep this', $response->getContent()); + } + + public function testProcess() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent()); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'foo\\\'\', \'bar\\\'\', true) ?>'."\n", $response->getContent()); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + } + + public function testProcessEscapesPhpTags() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(''); + $esi->process($request, $response); + + $this->assertEquals('php cript language=php>', $response->getContent()); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessWhenNoSrcInAnEsi() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $esi->process($request, $response); + } + + public function testProcessRemoveSurrogateControlHeader() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $response->headers->set('Surrogate-Control', 'content="ESI/1.0"'); + $esi->process($request, $response); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + + $response->headers->set('Surrogate-Control', 'no-store, content="ESI/1.0"'); + $esi->process($request, $response); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + + $response->headers->set('Surrogate-Control', 'content="ESI/1.0", no-store'); + $esi->process($request, $response); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + } + + public function testHandle() + { + $esi = new Esi(); + $cache = $this->getCache(Request::create('/'), new Response('foo')); + $this->assertEquals('foo', $esi->handle($cache, '/', '/alt', true)); + } + + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenResponseIsNot200() + { + $esi = new Esi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $esi->handle($cache, '/', '/alt', false); + } + + public function testHandleWhenResponseIsNot200AndErrorsAreIgnored() + { + $esi = new Esi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $this->assertEquals('', $esi->handle($cache, '/', '/alt', true)); + } + + public function testHandleWhenResponseIsNot200AndAltIsPresent() + { + $esi = new Esi(); + $response1 = new Response('foo'); + $response1->setStatusCode(404); + $response2 = new Response('bar'); + $cache = $this->getCache(Request::create('/'), array($response1, $response2)); + $this->assertEquals('bar', $esi->handle($cache, '/', '/alt', false)); + } + + protected function getCache($request, $response) + { + $cache = $this->getMock('Symfony\Component\HttpKernel\HttpCache\HttpCache', array('getRequest', 'handle'), array(), '', false); + $cache->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + if (is_array($response)) { + $cache->expects($this->any()) + ->method('handle') + ->will(call_user_func_array(array($this, 'onConsecutiveCalls'), $response)) + ; + } else { + $cache->expects($this->any()) + ->method('handle') + ->will($this->returnValue($response)) + ; + } + + return $cache; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php new file mode 100644 index 0000000..616bc17 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -0,0 +1,1227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class HttpCacheTest extends HttpCacheTestCase +{ + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + $storeMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface') + ->disableOriginalConstructor() + ->getMock(); + + // does not implement TerminableInterface + $kernelMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpKernelInterface') + ->disableOriginalConstructor() + ->getMock(); + + $kernelMock->expects($this->never()) + ->method('terminate'); + + $kernel = new HttpCache($kernelMock, $storeMock); + $kernel->terminate(Request::create('/'), new Response()); + + // implements TerminableInterface + $kernelMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Kernel') + ->disableOriginalConstructor() + ->setMethods(array('terminate', 'registerBundles', 'registerContainerConfiguration')) + ->getMock(); + + $kernelMock->expects($this->once()) + ->method('terminate'); + + $kernel = new HttpCache($kernelMock, $storeMock); + $kernel->terminate(Request::create('/'), new Response()); + } + + public function testPassesOnNonGetHeadRequests() + { + $this->setNextResponse(200); + $this->request('POST', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertTraceContains('pass'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testInvalidatesOnPostPutDeleteRequests() + { + foreach (array('post', 'put', 'delete') as $method) { + $this->setNextResponse(200); + $this->request($method, '/'); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertTraceContains('invalidate'); + $this->assertTraceContains('pass'); + } + } + + public function testDoesNotCacheWithAuthorizationRequestHeaderAndNonPublicResponse() + { + $this->setNextResponse(200, array('ETag' => '"Foo"')); + $this->request('GET', '/', array('HTTP_AUTHORIZATION' => 'basic foobarbaz')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertEquals('private', $this->response->headers->get('Cache-Control')); + + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testDoesCacheWithAuthorizationRequestHeaderAndPublicResponse() + { + $this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '"Foo"')); + $this->request('GET', '/', array('HTTP_AUTHORIZATION' => 'basic foobarbaz')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertTrue($this->response->headers->has('Age')); + $this->assertEquals('public', $this->response->headers->get('Cache-Control')); + } + + public function testDoesNotCacheWithCookieHeaderAndNonPublicResponse() + { + $this->setNextResponse(200, array('ETag' => '"Foo"')); + $this->request('GET', '/', array(), array('foo' => 'bar')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertEquals('private', $this->response->headers->get('Cache-Control')); + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testDoesNotCacheRequestsWithACookieHeader() + { + $this->setNextResponse(200); + $this->request('GET', '/', array(), array('foo' => 'bar')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertEquals('private', $this->response->headers->get('Cache-Control')); + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testRespondsWith304WhenIfModifiedSinceMatchesLastModified() + { + $time = \DateTime::createFromFormat('U', time()); + + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Last-Modified' => $time->format(DATE_RFC2822), 'Content-Type' => 'text/plain'), 'Hello World'); + $this->request('GET', '/', array('HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + + $this->assertHttpKernelIsCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->headers->get('Content-Type')); + $this->assertEmpty($this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testRespondsWith304WhenIfNoneMatchMatchesETag() + { + $this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '12345', 'Content-Type' => 'text/plain'), 'Hello World'); + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345')); + + $this->assertHttpKernelIsCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->headers->get('Content-Type')); + $this->assertTrue($this->response->headers->has('ETag')); + $this->assertEmpty($this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testRespondsWith304OnlyIfIfNoneMatchAndIfModifiedSinceBothMatch() + { + $time = \DateTime::createFromFormat('U', time()); + + $this->setNextResponse(200, array(), '', function ($request, $response) use ($time) { + $response->setStatusCode(200); + $response->headers->set('ETag', '12345'); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + $response->headers->set('Content-Type', 'text/plain'); + $response->setContent('Hello World'); + }); + + // only ETag matches + $t = \DateTime::createFromFormat('U', time() - 3600); + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $t->format(DATE_RFC2822))); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + + // only Last-Modified matches + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '1234', 'HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + + // Both matches + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + } + + public function testValidatesPrivateResponsesCachedOnTheClient() + { + $this->setNextResponse(200, array(), '', function ($request, $response) { + $etags = preg_split('/\s*,\s*/', $request->headers->get('IF_NONE_MATCH')); + if ($request->cookies->has('authenticated')) { + $response->headers->set('Cache-Control', 'private, no-store'); + $response->setETag('"private tag"'); + if (in_array('"private tag"', $etags)) { + $response->setStatusCode(304); + } else { + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'text/plain'); + $response->setContent('private data'); + } + } else { + $response->headers->set('Cache-Control', 'public'); + $response->setETag('"public tag"'); + if (in_array('"public tag"', $etags)) { + $response->setStatusCode(304); + } else { + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'text/plain'); + $response->setContent('public data'); + } + } + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('"public tag"', $this->response->headers->get('ETag')); + $this->assertEquals('public data', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $this->request('GET', '/', array(), array('authenticated' => '')); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('"private tag"', $this->response->headers->get('ETag')); + $this->assertEquals('private data', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceNotContains('store'); + } + + public function testStoresResponsesWhenNoCacheRequestDirectivePresent() + { + $time = \DateTime::createFromFormat('U', time() + 5); + + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('store'); + $this->assertTrue($this->response->headers->has('Age')); + } + + public function testReloadsResponsesWhenCacheHitsButNoCacheRequestDirectivePresentWhenAllowReloadIsSetTrue() + { + $count = 0; + + $this->setNextResponse(200, array('Cache-Control' => 'public, max-age=10000'), '', function ($request, $response) use (&$count) { + ++$count; + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_reload'] = true; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Goodbye World', $this->response->getContent()); + $this->assertTraceContains('reload'); + $this->assertTraceContains('store'); + } + + public function testDoesNotReloadResponsesWhenAllowReloadIsSetFalseDefault() + { + $count = 0; + + $this->setNextResponse(200, array('Cache-Control' => 'public, max-age=10000'), '', function ($request, $response) use (&$count) { + ++$count; + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_reload'] = false; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('reload'); + + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('reload'); + } + + public function testRevalidatesFreshCacheEntryWhenMaxAgeRequestDirectiveIsExceededWhenAllowRevalidateOptionIsSetTrue() + { + $count = 0; + + $this->setNextResponse(200, array(), '', function ($request, $response) use (&$count) { + ++$count; + $response->headers->set('Cache-Control', 'public, max-age=10000'); + $response->setETag($count); + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_revalidate'] = true; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Goodbye World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceContains('store'); + } + + public function testDoesNotRevalidateFreshCacheEntryWhenEnableRevalidateOptionIsSetFalseDefault() + { + $count = 0; + + $this->setNextResponse(200, array(), '', function ($request, $response) use (&$count) { + ++$count; + $response->headers->set('Cache-Control', 'public, max-age=10000'); + $response->setETag($count); + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_revalidate'] = false; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('stale'); + $this->assertTraceNotContains('invalid'); + $this->assertTraceContains('fresh'); + + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('stale'); + $this->assertTraceNotContains('invalid'); + $this->assertTraceContains('fresh'); + } + + public function testFetchesResponseFromBackendWhenCacheMisses() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTrue($this->response->headers->has('Age')); + } + + public function testDoesNotCacheSomeStatusCodeResponses() + { + foreach (array_merge(range(201, 202), range(204, 206), range(303, 305), range(400, 403), range(405, 409), range(411, 417), range(500, 505)) as $code) { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse($code, array('Expires' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals($code, $this->response->getStatusCode()); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + } + + public function testDoesNotCacheResponsesWithExplicitNoStoreDirective() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Expires' => $time->format(DATE_RFC2822), 'Cache-Control' => 'no-store')); + + $this->request('GET', '/'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testDoesNotCacheResponsesWithoutFreshnessInformationOrAValidator() + { + $this->setNextResponse(); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceNotContains('store'); + } + + public function testCachesResponsesWithExplicitNoCacheDirective() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Expires' => $time->format(DATE_RFC2822), 'Cache-Control' => 'public, no-cache')); + + $this->request('GET', '/'); + $this->assertTraceContains('store'); + $this->assertTrue($this->response->headers->has('Age')); + } + + public function testCachesResponsesWithAnExpirationHeader() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + } + + public function testCachesResponsesWithAMaxAgeDirective() + { + $this->setNextResponse(200, array('Cache-Control' => 'public, max-age=5')); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + } + + public function testCachesResponsesWithASMaxAgeDirective() + { + $this->setNextResponse(200, array('Cache-Control' => 's-maxage=5')); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + } + + public function testCachesResponsesWithALastModifiedValidatorButNoFreshnessInformation() + { + $time = \DateTime::createFromFormat('U', time()); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Last-Modified' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testCachesResponsesWithAnETagValidatorButNoFreshnessInformation() + { + $this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '"123456"')); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testHitsCachedResponsesWithExpiresHeader() + { + $time1 = \DateTime::createFromFormat('U', time() - 5); + $time2 = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Date' => $time1->format(DATE_RFC2822), 'Expires' => $time2->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTrue(strtotime($this->responses[0]->headers->get('Date')) - strtotime($this->response->headers->get('Date')) < 2); + $this->assertTrue($this->response->headers->get('Age') > 0); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testHitsCachedResponseWithMaxAgeDirective() + { + $time = \DateTime::createFromFormat('U', time() - 5); + $this->setNextResponse(200, array('Date' => $time->format(DATE_RFC2822), 'Cache-Control' => 'public, max-age=10')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTrue(strtotime($this->responses[0]->headers->get('Date')) - strtotime($this->response->headers->get('Date')) < 2); + $this->assertTrue($this->response->headers->get('Age') > 0); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testHitsCachedResponseWithSMaxAgeDirective() + { + $time = \DateTime::createFromFormat('U', time() - 5); + $this->setNextResponse(200, array('Date' => $time->format(DATE_RFC2822), 'Cache-Control' => 's-maxage=10, max-age=0')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTrue(strtotime($this->responses[0]->headers->get('Date')) - strtotime($this->response->headers->get('Date')) < 2); + $this->assertTrue($this->response->headers->get('Age') > 0); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformation() + { + $this->setNextResponse(); + + $this->cacheConfig['default_ttl'] = 10; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=10/', $this->response->headers->get('Cache-Control')); + + $this->cacheConfig['default_ttl'] = 10; + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=10/', $this->response->headers->get('Cache-Control')); + } + + public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformationAndAfterTtlWasExpired() + { + $this->setNextResponse(); + + $this->cacheConfig['default_ttl'] = 2; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + // expires the cache + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + $tmp = unserialize($values[0]); + $time = \DateTime::createFromFormat('U', time() - 5); + $tmp[0][1]['date'] = $time->format(DATE_RFC2822); + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('save'); + $m->setAccessible(true); + $m->invoke($this->store, 'md'.hash('sha256', 'http://localhost/'), serialize($tmp)); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->setNextResponse(); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + } + + public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformationAndAfterTtlWasExpiredWithStatus304() + { + $this->setNextResponse(); + + $this->cacheConfig['default_ttl'] = 2; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + // expires the cache + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + $tmp = unserialize($values[0]); + $time = \DateTime::createFromFormat('U', time() - 5); + $tmp[0][1]['date'] = $time->format(DATE_RFC2822); + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('save'); + $m->setAccessible(true); + $m->invoke($this->store, 'md'.hash('sha256', 'http://localhost/'), serialize($tmp)); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('miss'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + } + + public function testDoesNotAssignDefaultTtlWhenResponseHasMustRevalidateDirective() + { + $this->setNextResponse(200, array('Cache-Control' => 'must-revalidate')); + + $this->cacheConfig['default_ttl'] = 10; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertNotRegExp('/s-maxage/', $this->response->headers->get('Cache-Control')); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testFetchesFullResponseWhenCacheStaleAndNoValidatorsPresent() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + + // build initial request + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertNotNull($this->response->headers->get('Age')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + # go in and play around with the cached metadata directly ... + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + $tmp = unserialize($values[0]); + $time = \DateTime::createFromFormat('U', time()); + $tmp[0][1]['expires'] = $time->format(DATE_RFC2822); + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('save'); + $m->setAccessible(true); + $m->invoke($this->store, 'md'.hash('sha256', 'http://localhost/'), serialize($tmp)); + + // build subsequent request; should be found but miss due to freshness + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTrue($this->response->headers->get('Age') <= 1); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('stale'); + $this->assertTraceNotContains('fresh'); + $this->assertTraceNotContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testValidatesCachedResponsesWithLastModifiedAndNoFreshnessInformation() + { + $time = \DateTime::createFromFormat('U', time()); + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time) { + $response->headers->set('Cache-Control', 'public'); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + if ($time->format(DATE_RFC2822) == $request->headers->get('IF_MODIFIED_SINCE')) { + $response->setStatusCode(304); + $response->setContent(''); + } + }); + + // build initial request + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Last-Modified')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('stale'); + + // build subsequent request; should be found but miss due to freshness + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Last-Modified')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTrue($this->response->headers->get('Age') <= 1); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('miss'); + } + + public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + $response->headers->set('Cache-Control', 'public'); + $response->headers->set('ETag', '"12345"'); + if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) { + $response->setStatusCode(304); + $response->setContent(''); + } + }); + + // build initial request + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('ETag')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + // build subsequent request; should be found but miss due to freshness + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('ETag')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTrue($this->response->headers->get('Age') <= 1); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('miss'); + } + + public function testReplacesCachedResponsesWhenValidationResultsInNon304Response() + { + $time = \DateTime::createFromFormat('U', time()); + $count = 0; + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time, &$count) { + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + $response->headers->set('Cache-Control', 'public'); + switch (++$count) { + case 1: + $response->setContent('first response'); + break; + case 2: + $response->setContent('second response'); + break; + case 3: + $response->setContent(''); + $response->setStatusCode(304); + break; + } + }); + + // first request should fetch from backend and store in cache + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('first response', $this->response->getContent()); + + // second request is validated, is invalid, and replaces cached entry + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('second response', $this->response->getContent()); + + // third response is validated, valid, and returns cached entry + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('second response', $this->response->getContent()); + + $this->assertEquals(3, $count); + } + + public function testPassesHeadRequestsThroughDirectlyOnPass() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + $response->setContent(''); + $response->setStatusCode(200); + $this->assertEquals('HEAD', $request->getMethod()); + }); + + $this->request('HEAD', '/', array('HTTP_EXPECT' => 'something ...')); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('', $this->response->getContent()); + } + + public function testUsesCacheToRespondToHeadRequestsWhenFresh() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->setContent('Hello World'); + $response->setStatusCode(200); + $this->assertNotEquals('HEAD', $request->getMethod()); + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('HEAD', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->getContent()); + $this->assertEquals(strlen('Hello World'), $this->response->headers->get('Content-Length')); + } + + public function testSendsNoContentWhenFresh() + { + $time = \DateTime::createFromFormat('U', time()); + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time) { + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/', array('HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->getContent()); + } + + public function testInvalidatesCachedResponsesOnPost() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + if ('GET' == $request->getMethod()) { + $response->setStatusCode(200); + $response->headers->set('Cache-Control', 'public, max-age=500'); + $response->setContent('Hello World'); + } elseif ('POST' == $request->getMethod()) { + $response->setStatusCode(303); + $response->headers->set('Location', '/'); + $response->headers->remove('Cache-Control'); + $response->setContent(''); + } + }); + + // build initial request to enter into the cache + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + // make sure it is valid + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + // now POST to same URL + $this->request('POST', '/helloworld'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('/', $this->response->headers->get('Location')); + $this->assertTraceContains('invalidate'); + $this->assertTraceContains('pass'); + $this->assertEquals('', $this->response->getContent()); + + // now make sure it was actually invalidated + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceContains('store'); + } + + public function testServesFromCacheWhenHeadersMatch() + { + $count = 0; + $this->setNextResponse(200, array('Cache-Control' => 'max-age=10000'), '', function ($request, $response) use (&$count) { + $response->headers->set('Vary', 'Accept User-Agent Foo'); + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->headers->set('X-Response-Count', ++$count); + $response->setContent($request->headers->get('USER_AGENT')); + }); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + } + + public function testStoresMultipleResponsesWhenHeadersDiffer() + { + $count = 0; + $this->setNextResponse(200, array('Cache-Control' => 'max-age=10000'), '', function ($request, $response) use (&$count) { + $response->headers->set('Vary', 'Accept User-Agent Foo'); + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->headers->set('X-Response-Count', ++$count); + $response->setContent($request->headers->get('USER_AGENT')); + }); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertEquals(1, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/2.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Bob/2.0', $this->response->getContent()); + $this->assertEquals(2, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertTraceContains('fresh'); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertEquals(1, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/2.0')); + $this->assertTraceContains('fresh'); + $this->assertEquals('Bob/2.0', $this->response->getContent()); + $this->assertEquals(2, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_USER_AGENT' => 'Bob/2.0')); + $this->assertTraceContains('miss'); + $this->assertEquals('Bob/2.0', $this->response->getContent()); + $this->assertEquals(3, $this->response->headers->get('X-Response-Count')); + } + + public function testShouldCatchExceptions() + { + $this->catchExceptions(); + + $this->setNextResponse(); + $this->request('GET', '/'); + + $this->assertExceptionsAreCaught(); + } + + public function testShouldCatchExceptionsWhenReloadingAndNoCacheRequest() + { + $this->catchExceptions(); + + $this->setNextResponse(); + $this->cacheConfig['allow_reload'] = true; + $this->request('GET', '/', array(), array(), false, array('Pragma' => 'no-cache')); + + $this->assertExceptionsAreCaught(); + } + + public function testShouldNotCatchExceptions() + { + $this->catchExceptions(false); + + $this->setNextResponse(); + $this->request('GET', '/'); + + $this->assertExceptionsAreNotCaught(); + } + + public function testEsiCacheSendsTheLowestTtl() + { + $responses = array( + array( + 'status' => 200, + 'body' => ' ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array('Cache-Control' => 's-maxage=300'), + ), + array( + 'status' => 200, + 'body' => 'My name is Bobby.', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent()); + + // check for 100 or 99 as the test can be executed after a second change + $this->assertTrue(in_array($this->response->getTtl(), array(99, 100))); + } + + public function testEsiCacheForceValidation() + { + $responses = array( + array( + 'status' => 200, + 'body' => ' ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array('ETag' => 'foobar'), + ), + array( + 'status' => 200, + 'body' => 'My name is Bobby.', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent()); + $this->assertNull($this->response->getTtl()); + $this->assertTrue($this->response->mustRevalidate()); + $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache')); + } + + public function testEsiRecalculateContentLengthHeader() + { + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Content-Length' => 26, + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World!', $this->response->getContent()); + $this->assertEquals(12, $this->response->headers->get('Content-Length')); + } + + public function testClientIpIsAlwaysLocalhostForForwardedRequests() + { + $this->setNextResponse(); + $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); + + $this->assertEquals('127.0.0.1', $this->kernel->getBackendRequest()->server->get('REMOTE_ADDR')); + } + + /** + * @dataProvider getTrustedProxyData + */ + public function testHttpCacheIsSetAsATrustedProxy(array $existing, array $expected) + { + Request::setTrustedProxies($existing); + + $this->setNextResponse(); + $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); + + $this->assertEquals($expected, Request::getTrustedProxies()); + } + + public function getTrustedProxyData() + { + return array( + array(array(), array('127.0.0.1')), + array(array('10.0.0.2'), array('10.0.0.2', '127.0.0.1')), + array(array('10.0.0.2', '127.0.0.1'), array('10.0.0.2', '127.0.0.1')), + ); + } + + /** + * @dataProvider getXForwardedForData + */ + public function testXForwarderForHeaderForForwardedRequests($xForwardedFor, $expected) + { + $this->setNextResponse(); + $server = array('REMOTE_ADDR' => '10.0.0.1'); + if (false !== $xForwardedFor) { + $server['HTTP_X_FORWARDED_FOR'] = $xForwardedFor; + } + $this->request('GET', '/', $server); + + $this->assertEquals($expected, $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); + } + + public function getXForwardedForData() + { + return array( + array(false, '10.0.0.1'), + array('10.0.0.2', '10.0.0.2, 10.0.0.1'), + array('10.0.0.2, 10.0.0.3', '10.0.0.2, 10.0.0.3, 10.0.0.1'), + ); + } + + public function testXForwarderForHeaderForPassRequests() + { + $this->setNextResponse(); + $server = array('REMOTE_ADDR' => '10.0.0.1'); + $this->request('POST', '/', $server); + + $this->assertEquals('10.0.0.1', $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); + } + + public function testEsiCacheRemoveValidationHeadersIfEmbeddedResponses() + { + $time = \DateTime::createFromFormat('U', time()); + + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Surrogate-Control' => 'content="ESI/1.0"', + 'ETag' => 'hey', + 'Last-Modified' => $time->format(DATE_RFC2822), + ), + ), + array( + 'status' => 200, + 'body' => 'Hey!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertNull($this->response->getETag()); + $this->assertNull($this->response->getLastModified()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php new file mode 100644 index 0000000..1fcd210 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\Store; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class HttpCacheTestCase extends \PHPUnit_Framework_TestCase +{ + protected $kernel; + protected $cache; + protected $caches; + protected $cacheConfig; + protected $request; + protected $response; + protected $responses; + protected $catch; + protected $esi; + protected $store; + + protected function setUp() + { + $this->kernel = null; + + $this->cache = null; + $this->esi = null; + $this->caches = array(); + $this->cacheConfig = array(); + + $this->request = null; + $this->response = null; + $this->responses = array(); + + $this->catch = false; + + $this->clearDirectory(sys_get_temp_dir().'/http_cache'); + } + + protected function tearDown() + { + $this->kernel = null; + $this->cache = null; + $this->caches = null; + $this->request = null; + $this->response = null; + $this->responses = null; + $this->cacheConfig = null; + $this->catch = null; + $this->esi = null; + + $this->clearDirectory(sys_get_temp_dir().'/http_cache'); + } + + public function assertHttpKernelIsCalled() + { + $this->assertTrue($this->kernel->hasBeenCalled()); + } + + public function assertHttpKernelIsNotCalled() + { + $this->assertFalse($this->kernel->hasBeenCalled()); + } + + public function assertResponseOk() + { + $this->assertEquals(200, $this->response->getStatusCode()); + } + + public function assertTraceContains($trace) + { + $traces = $this->cache->getTraces(); + $traces = current($traces); + + $this->assertRegExp('/'.$trace.'/', implode(', ', $traces)); + } + + public function assertTraceNotContains($trace) + { + $traces = $this->cache->getTraces(); + $traces = current($traces); + + $this->assertNotRegExp('/'.$trace.'/', implode(', ', $traces)); + } + + public function assertExceptionsAreCaught() + { + $this->assertTrue($this->kernel->isCatchingExceptions()); + } + + public function assertExceptionsAreNotCaught() + { + $this->assertFalse($this->kernel->isCatchingExceptions()); + } + + public function request($method, $uri = '/', $server = array(), $cookies = array(), $esi = false, $headers = array()) + { + if (null === $this->kernel) { + throw new \LogicException('You must call setNextResponse() before calling request().'); + } + + $this->kernel->reset(); + + $this->store = new Store(sys_get_temp_dir().'/http_cache'); + + $this->cacheConfig['debug'] = true; + + $this->esi = $esi ? new Esi() : null; + $this->cache = new HttpCache($this->kernel, $this->store, $this->esi, $this->cacheConfig); + $this->request = Request::create($uri, $method, array(), $cookies, array(), $server); + $this->request->headers->add($headers); + + $this->response = $this->cache->handle($this->request, HttpKernelInterface::MASTER_REQUEST, $this->catch); + + $this->responses[] = $this->response; + } + + public function getMetaStorageValues() + { + $values = array(); + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(sys_get_temp_dir().'/http_cache/md', \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + $values[] = file_get_contents($file); + } + + return $values; + } + + // A basic response with 200 status code and a tiny body. + public function setNextResponse($statusCode = 200, array $headers = array(), $body = 'Hello World', \Closure $customizer = null) + { + $this->kernel = new TestHttpKernel($body, $statusCode, $headers, $customizer); + } + + public function setNextResponses($responses) + { + $this->kernel = new TestMultipleHttpKernel($responses); + } + + public function catchExceptions($catch = true) + { + $this->catch = $catch; + } + + public static function clearDirectory($directory) + { + if (!is_dir($directory)) { + return; + } + + $fp = opendir($directory); + while (false !== $file = readdir($fp)) { + if (!in_array($file, array('.', '..'))) { + if (is_link($directory.'/'.$file)) { + unlink($directory.'/'.$file); + } elseif (is_dir($directory.'/'.$file)) { + self::clearDirectory($directory.'/'.$file); + rmdir($directory.'/'.$file); + } else { + unlink($directory.'/'.$file); + } + } + } + + closedir($fp); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/SsiTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/SsiTest.php new file mode 100644 index 0000000..07b70dc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/SsiTest.php @@ -0,0 +1,214 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Ssi; + +class SsiTest extends \PHPUnit_Framework_TestCase +{ + public function testHasSurrogateSsiCapability() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="SSI/1.0"'); + $this->assertTrue($ssi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'foobar'); + $this->assertFalse($ssi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $this->assertFalse($ssi->hasSurrogateCapability($request)); + } + + public function testAddSurrogateSsiCapability() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $ssi->addSurrogateCapability($request); + $this->assertEquals('symfony2="SSI/1.0"', $request->headers->get('Surrogate-Capability')); + + $ssi->addSurrogateCapability($request); + $this->assertEquals('symfony2="SSI/1.0", symfony2="SSI/1.0"', $request->headers->get('Surrogate-Capability')); + } + + public function testAddSurrogateControl() + { + $ssi = new Ssi(); + + $response = new Response('foo '); + $ssi->addSurrogateControl($response); + $this->assertEquals('content="SSI/1.0"', $response->headers->get('Surrogate-Control')); + + $response = new Response('foo'); + $ssi->addSurrogateControl($response); + $this->assertEquals('', $response->headers->get('Surrogate-Control')); + } + + public function testNeedsSsiParsing() + { + $ssi = new Ssi(); + + $response = new Response(); + $response->headers->set('Surrogate-Control', 'content="SSI/1.0"'); + $this->assertTrue($ssi->needsParsing($response)); + + $response = new Response(); + $this->assertFalse($ssi->needsParsing($response)); + } + + public function testRenderIncludeTag() + { + $ssi = new Ssi(); + + $this->assertEquals('', $ssi->renderIncludeTag('/', '/alt', true)); + $this->assertEquals('', $ssi->renderIncludeTag('/', '/alt', false)); + $this->assertEquals('', $ssi->renderIncludeTag('/')); + } + + public function testProcessDoesNothingIfContentTypeIsNotHtml() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response(); + $response->headers->set('Content-Type', 'text/plain'); + $ssi->process($request, $response); + + $this->assertFalse($response->headers->has('x-body-eval')); + } + + public function testProcess() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $ssi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + + $response = new Response('foo '); + $ssi->process($request, $response); + + $this->assertEquals("foo surrogate->handle(\$this, 'foo\\'', '', false) ?>"."\n", $response->getContent()); + } + + public function testProcessEscapesPhpTags() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response(''); + $ssi->process($request, $response); + + $this->assertEquals('php cript language=php>', $response->getContent()); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessWhenNoSrcInAnSsi() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $ssi->process($request, $response); + } + + public function testProcessRemoveSurrogateControlHeader() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $response->headers->set('Surrogate-Control', 'content="SSI/1.0"'); + $ssi->process($request, $response); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + + $response->headers->set('Surrogate-Control', 'no-store, content="SSI/1.0"'); + $ssi->process($request, $response); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + + $response->headers->set('Surrogate-Control', 'content="SSI/1.0", no-store'); + $ssi->process($request, $response); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + } + + public function testHandle() + { + $ssi = new Ssi(); + $cache = $this->getCache(Request::create('/'), new Response('foo')); + $this->assertEquals('foo', $ssi->handle($cache, '/', '/alt', true)); + } + + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenResponseIsNot200() + { + $ssi = new Ssi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $ssi->handle($cache, '/', '/alt', false); + } + + public function testHandleWhenResponseIsNot200AndErrorsAreIgnored() + { + $ssi = new Ssi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $this->assertEquals('', $ssi->handle($cache, '/', '/alt', true)); + } + + public function testHandleWhenResponseIsNot200AndAltIsPresent() + { + $ssi = new Ssi(); + $response1 = new Response('foo'); + $response1->setStatusCode(404); + $response2 = new Response('bar'); + $cache = $this->getCache(Request::create('/'), array($response1, $response2)); + $this->assertEquals('bar', $ssi->handle($cache, '/', '/alt', false)); + } + + protected function getCache($request, $response) + { + $cache = $this->getMock('Symfony\Component\HttpKernel\HttpCache\HttpCache', array('getRequest', 'handle'), array(), '', false); + $cache->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + if (is_array($response)) { + $cache->expects($this->any()) + ->method('handle') + ->will(call_user_func_array(array($this, 'onConsecutiveCalls'), $response)) + ; + } else { + $cache->expects($this->any()) + ->method('handle') + ->will($this->returnValue($response)) + ; + } + + return $cache; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php new file mode 100644 index 0000000..4198ce4 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -0,0 +1,269 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Store; + +class StoreTest extends \PHPUnit_Framework_TestCase +{ + protected $request; + protected $response; + protected $store; + + protected function setUp() + { + $this->request = Request::create('/'); + $this->response = new Response('hello world', 200, array()); + + HttpCacheTestCase::clearDirectory(sys_get_temp_dir().'/http_cache'); + + $this->store = new Store(sys_get_temp_dir().'/http_cache'); + } + + protected function tearDown() + { + $this->store = null; + $this->request = null; + $this->response = null; + + HttpCacheTestCase::clearDirectory(sys_get_temp_dir().'/http_cache'); + } + + public function testReadsAnEmptyArrayWithReadWhenNothingCachedAtKey() + { + $this->assertEmpty($this->getStoreMetadata('/nothing')); + } + + public function testUnlockFileThatDoesExist() + { + $cacheKey = $this->storeSimpleEntry(); + $this->store->lock($this->request); + + $this->assertTrue($this->store->unlock($this->request)); + } + + public function testUnlockFileThatDoesNotExist() + { + $this->assertFalse($this->store->unlock($this->request)); + } + + public function testRemovesEntriesForKeyWithPurge() + { + $request = Request::create('/foo'); + $this->store->write($request, new Response('foo')); + + $metadata = $this->getStoreMetadata($request); + $this->assertNotEmpty($metadata); + + $this->assertTrue($this->store->purge('/foo')); + $this->assertEmpty($this->getStoreMetadata($request)); + + // cached content should be kept after purging + $path = $this->store->getPath($metadata[0][1]['x-content-digest'][0]); + $this->assertTrue(is_file($path)); + + $this->assertFalse($this->store->purge('/bar')); + } + + public function testStoresACacheEntry() + { + $cacheKey = $this->storeSimpleEntry(); + + $this->assertNotEmpty($this->getStoreMetadata($cacheKey)); + } + + public function testSetsTheXContentDigestResponseHeaderBeforeStoring() + { + $cacheKey = $this->storeSimpleEntry(); + $entries = $this->getStoreMetadata($cacheKey); + list($req, $res) = $entries[0]; + + $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $res['x-content-digest'][0]); + } + + public function testFindsAStoredEntryWithLookup() + { + $this->storeSimpleEntry(); + $response = $this->store->lookup($this->request); + + $this->assertNotNull($response); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); + } + + public function testDoesNotFindAnEntryWithLookupWhenNoneExists() + { + $request = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + + $this->assertNull($this->store->lookup($request)); + } + + public function testCanonizesUrlsForCacheKeys() + { + $this->storeSimpleEntry($path = '/test?x=y&p=q'); + $hitsReq = Request::create($path); + $missReq = Request::create('/test?p=x'); + + $this->assertNotNull($this->store->lookup($hitsReq)); + $this->assertNull($this->store->lookup($missReq)); + } + + public function testDoesNotFindAnEntryWithLookupWhenTheBodyDoesNotExist() + { + $this->storeSimpleEntry(); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $path = $this->getStorePath($this->response->headers->get('X-Content-Digest')); + @unlink($path); + $this->assertNull($this->store->lookup($this->request)); + } + + public function testRestoresResponseHeadersProperlyWithLookup() + { + $this->storeSimpleEntry(); + $response = $this->store->lookup($this->request); + + $this->assertEquals($response->headers->all(), array_merge(array('content-length' => 4, 'x-body-file' => array($this->getStorePath($response->headers->get('X-Content-Digest')))), $this->response->headers->all())); + } + + public function testRestoresResponseContentFromEntityStoreWithLookup() + { + $this->storeSimpleEntry(); + $response = $this->store->lookup($this->request); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test')), $response->getContent()); + } + + public function testInvalidatesMetaAndEntityStoreEntriesWithInvalidate() + { + $this->storeSimpleEntry(); + $this->store->invalidate($this->request); + $response = $this->store->lookup($this->request); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); + $this->assertFalse($response->isFresh()); + } + + public function testSucceedsQuietlyWhenInvalidateCalledWithNoMatchingEntries() + { + $req = Request::create('/test'); + $this->store->invalidate($req); + $this->assertNull($this->store->lookup($this->request)); + } + + public function testDoesNotReturnEntriesThatVaryWithLookup() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam')); + $res = new Response('test', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req1, $res); + + $this->assertNull($this->store->lookup($req2)); + } + + public function testDoesNotReturnEntriesThatSlightlyVaryWithLookup() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bam')); + $res = new Response('test', 200, array('Vary' => array('Foo', 'Bar'))); + $this->store->write($req1, $res); + + $this->assertNull($this->store->lookup($req2)); + } + + public function testStoresMultipleResponsesForEachVaryCombination() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $res1 = new Response('test 1', 200, array('Vary' => 'Foo Bar')); + $key = $this->store->write($req1, $res1); + + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam')); + $res2 = new Response('test 2', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req2, $res2); + + $req3 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom')); + $res3 = new Response('test 3', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req3, $res3); + + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 3')), $this->store->lookup($req3)->getContent()); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 2')), $this->store->lookup($req2)->getContent()); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent()); + + $this->assertCount(3, $this->getStoreMetadata($key)); + } + + public function testOverwritesNonVaryingResponseWithStore() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $res1 = new Response('test 1', 200, array('Vary' => 'Foo Bar')); + $key = $this->store->write($req1, $res1); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent()); + + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam')); + $res2 = new Response('test 2', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req2, $res2); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 2')), $this->store->lookup($req2)->getContent()); + + $req3 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $res3 = new Response('test 3', 200, array('Vary' => 'Foo Bar')); + $key = $this->store->write($req3, $res3); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 3')), $this->store->lookup($req3)->getContent()); + + $this->assertCount(2, $this->getStoreMetadata($key)); + } + + public function testLocking() + { + $req = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $this->assertTrue($this->store->lock($req)); + + $path = $this->store->lock($req); + $this->assertTrue($this->store->isLocked($req)); + + $this->store->unlock($req); + $this->assertFalse($this->store->isLocked($req)); + } + + protected function storeSimpleEntry($path = null, $headers = array()) + { + if (null === $path) { + $path = '/test'; + } + + $this->request = Request::create($path, 'get', array(), array(), array(), $headers); + $this->response = new Response('test', 200, array('Cache-Control' => 'max-age=420')); + + return $this->store->write($this->request, $this->response); + } + + protected function getStoreMetadata($key) + { + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('getMetadata'); + $m->setAccessible(true); + + if ($key instanceof Request) { + $m1 = $r->getMethod('getCacheKey'); + $m1->setAccessible(true); + $key = $m1->invoke($this->store, $key); + } + + return $m->invoke($this->store, $key); + } + + protected function getStorePath($key) + { + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('getPath'); + $m->setAccessible(true); + + return $m->invoke($this->store, $key); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php new file mode 100644 index 0000000..5546ba2 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestHttpKernel extends HttpKernel implements ControllerResolverInterface +{ + protected $body; + protected $status; + protected $headers; + protected $called = false; + protected $customizer; + protected $catch = false; + protected $backendRequest; + + public function __construct($body, $status, $headers, \Closure $customizer = null) + { + $this->body = $body; + $this->status = $status; + $this->headers = $headers; + $this->customizer = $customizer; + + parent::__construct(new EventDispatcher(), $this); + } + + public function getBackendRequest() + { + return $this->backendRequest; + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) + { + $this->catch = $catch; + $this->backendRequest = $request; + + return parent::handle($request, $type, $catch); + } + + public function isCatchingExceptions() + { + return $this->catch; + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + $this->called = true; + + $response = new Response($this->body, $this->status, $this->headers); + + if (null !== $customizer = $this->customizer) { + $customizer($request, $response); + } + + return $response; + } + + public function hasBeenCalled() + { + return $this->called; + } + + public function reset() + { + $this->called = false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestMultipleHttpKernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestMultipleHttpKernel.php new file mode 100644 index 0000000..5b5209e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestMultipleHttpKernel.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInterface +{ + protected $bodies = array(); + protected $statuses = array(); + protected $headers = array(); + protected $called = false; + protected $backendRequest; + + public function __construct($responses) + { + foreach ($responses as $response) { + $this->bodies[] = $response['body']; + $this->statuses[] = $response['status']; + $this->headers[] = $response['headers']; + } + + parent::__construct(new EventDispatcher(), $this); + } + + public function getBackendRequest() + { + return $this->backendRequest; + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) + { + $this->backendRequest = $request; + + return parent::handle($request, $type, $catch); + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + $this->called = true; + + $response = new Response(array_shift($this->bodies), array_shift($this->statuses), array_shift($this->headers)); + + return $response; + } + + public function hasBeenCalled() + { + return $this->called; + } + + public function reset() + { + $this->called = false; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php new file mode 100644 index 0000000..06f611e --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -0,0 +1,308 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class HttpKernelTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrue() + { + $kernel = new HttpKernel(new EventDispatcher(), $this->getResolver(function () { throw new \RuntimeException(); })); + + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + } + + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsFalseAndNoListenerIsRegistered() + { + $kernel = new HttpKernel(new EventDispatcher(), $this->getResolver(function () { throw new \RuntimeException(); })); + + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, false); + } + + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrueWithAHandlingListener() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + $event->setResponse(new Response($event->getException()->getMessage())); + }); + + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new \RuntimeException('foo'); })); + $response = $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertEquals('500', $response->getStatusCode()); + $this->assertEquals('foo', $response->getContent()); + } + + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrueWithANonHandlingListener() + { + $exception = new \RuntimeException(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + // should set a response, but does not + }); + + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () use ($exception) { throw $exception; })); + + try { + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + $this->fail('LogicException expected'); + } catch (\RuntimeException $e) { + $this->assertSame($exception, $e); + } + } + + public function testHandleExceptionWithARedirectionResponse() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + $event->setResponse(new RedirectResponse('/login', 301)); + }); + + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new AccessDeniedHttpException(); })); + $response = $kernel->handle(new Request()); + + $this->assertEquals('301', $response->getStatusCode()); + $this->assertEquals('/login', $response->headers->get('Location')); + } + + public function testHandleHttpException() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + $event->setResponse(new Response($event->getException()->getMessage())); + }); + + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new MethodNotAllowedHttpException(array('POST')); })); + $response = $kernel->handle(new Request()); + + $this->assertEquals('405', $response->getStatusCode()); + $this->assertEquals('POST', $response->headers->get('Allow')); + } + + /** + * @dataProvider getStatusCodes + */ + public function testHandleWhenAnExceptionIsHandledWithASpecificStatusCode($responseStatusCode, $expectedStatusCode) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) use ($responseStatusCode, $expectedStatusCode) { + $event->setResponse(new Response('', $responseStatusCode, array('X-Status-Code' => $expectedStatusCode))); + }); + + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new \RuntimeException(); })); + $response = $kernel->handle(new Request()); + + $this->assertEquals($expectedStatusCode, $response->getStatusCode()); + $this->assertFalse($response->headers->has('X-Status-Code')); + } + + public function getStatusCodes() + { + return array( + array(200, 404), + array(404, 200), + array(301, 200), + array(500, 200), + ); + } + + public function testHandleWhenAListenerReturnsAResponse() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::REQUEST, function ($event) { + $event->setResponse(new Response('hello')); + }); + + $kernel = new HttpKernel($dispatcher, $this->getResolver()); + + $this->assertEquals('hello', $kernel->handle(new Request())->getContent()); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public function testHandleWhenNoControllerIsFound() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(false)); + + $kernel->handle(new Request()); + } + + public function testHandleWhenTheControllerIsAClosure() + { + $response = new Response('foo'); + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () use ($response) { return $response; })); + + $this->assertSame($response, $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAnObjectWithInvoke() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(new Controller())); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAFunction() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver('Symfony\Component\HttpKernel\Tests\controller_func')); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAnArray() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(array(new Controller(), 'controller'))); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAStaticArray() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(array('Symfony\Component\HttpKernel\Tests\Controller', 'staticcontroller'))); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + /** + * @expectedException \LogicException + */ + public function testHandleWhenTheControllerDoesNotReturnAResponse() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { return 'foo'; })); + + $kernel->handle(new Request()); + } + + public function testHandleWhenTheControllerDoesNotReturnAResponseButAViewIsRegistered() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::VIEW, function ($event) { + $event->setResponse(new Response($event->getControllerResult())); + }); + $kernel = new HttpKernel($dispatcher, $this->getResolver(function () { return 'foo'; })); + + $this->assertEquals('foo', $kernel->handle(new Request())->getContent()); + } + + public function testHandleWithAResponseListener() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::RESPONSE, function ($event) { + $event->setResponse(new Response('foo')); + }); + $kernel = new HttpKernel($dispatcher, $this->getResolver()); + + $this->assertEquals('foo', $kernel->handle(new Request())->getContent()); + } + + public function testTerminate() + { + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver()); + $dispatcher->addListener(KernelEvents::TERMINATE, function ($event) use (&$called, &$capturedKernel, &$capturedRequest, &$capturedResponse) { + $called = true; + $capturedKernel = $event->getKernel(); + $capturedRequest = $event->getRequest(); + $capturedResponse = $event->getResponse(); + }); + + $kernel->terminate($request = Request::create('/'), $response = new Response()); + $this->assertTrue($called); + $this->assertEquals($kernel, $capturedKernel); + $this->assertEquals($request, $capturedRequest); + $this->assertEquals($response, $capturedResponse); + } + + public function testVerifyRequestStackPushPopDuringHandle() + { + $request = new Request(); + + $stack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array('push', 'pop')); + $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); + $stack->expects($this->at(1))->method('pop'); + + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(), $stack); + + $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST); + } + + protected function getResolver($controller = null) + { + if (null === $controller) { + $controller = function () { return new Response('Hello'); }; + } + + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $resolver->expects($this->any()) + ->method('getController') + ->will($this->returnValue($controller)); + $resolver->expects($this->any()) + ->method('getArguments') + ->will($this->returnValue(array())); + + return $resolver; + } + + protected function assertResponseEquals(Response $expected, Response $actual) + { + $expected->setDate($actual->getDate()); + $this->assertEquals($expected, $actual); + } +} + +class Controller +{ + public function __invoke() + { + return new Response('foo'); + } + + public function controller() + { + return new Response('foo'); + } + + public static function staticController() + { + return new Response('foo'); + } +} + +function controller_func() +{ + return new Response('foo'); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/KernelTest.php new file mode 100644 index 0000000..2b121c9 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -0,0 +1,806 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Config\EnvParametersResource; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForOverrideName; + +class KernelTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructor() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $this->assertEquals($env, $kernel->getEnvironment()); + $this->assertEquals($debug, $kernel->isDebug()); + $this->assertFalse($kernel->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $kernel->getStartTime()); + $this->assertNull($kernel->getContainer()); + } + + public function testClone() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $clone = clone $kernel; + + $this->assertEquals($env, $clone->getEnvironment()); + $this->assertEquals($debug, $clone->isDebug()); + $this->assertFalse($clone->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $clone->getStartTime()); + $this->assertNull($clone->getContainer()); + } + + public function testBootInitializesBundlesAndContainer() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->once()) + ->method('initializeBundles'); + $kernel->expects($this->once()) + ->method('initializeContainer'); + + $kernel->boot(); + } + + public function testBootSetsTheContainerToTheBundles() + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); + $bundle->expects($this->once()) + ->method('setContainer'); + + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'getBundles')); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + } + + public function testBootSetsTheBootedFlagToTrue() + { + // use test kernel to access isBooted() + $kernel = $this->getKernelForTest(array('initializeBundles', 'initializeContainer')); + $kernel->boot(); + + $this->assertTrue($kernel->isBooted()); + } + + public function testClassCacheIsLoaded() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->loadClassCache('name', '.extension'); + $kernel->expects($this->once()) + ->method('doLoadClassCache') + ->with('name', '.extension'); + + $kernel->boot(); + } + + public function testClassCacheIsNotLoadedByDefault() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + + $kernel->boot(); + } + + public function testClassCacheIsNotLoadedWhenKernelIsNotBooted() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->loadClassCache(); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + } + + public function testEnvParametersResourceIsAdded() + { + $container = new ContainerBuilder(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getContainerBuilder', 'prepareContainer', 'getCacheDir', 'getLogDir')) + ->getMock(); + $kernel->expects($this->any()) + ->method('getContainerBuilder') + ->will($this->returnValue($container)); + $kernel->expects($this->any()) + ->method('prepareContainer') + ->will($this->returnValue(null)); + $kernel->expects($this->any()) + ->method('getCacheDir') + ->will($this->returnValue(sys_get_temp_dir())); + $kernel->expects($this->any()) + ->method('getLogDir') + ->will($this->returnValue(sys_get_temp_dir())); + + $reflection = new \ReflectionClass(get_class($kernel)); + $method = $reflection->getMethod('buildContainer'); + $method->setAccessible(true); + $method->invoke($kernel); + + $found = false; + foreach ($container->getResources() as $resource) { + if ($resource instanceof EnvParametersResource) { + $found = true; + break; + } + } + + $this->assertTrue($found); + } + + public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->once()) + ->method('initializeBundles'); + + $kernel->boot(); + $kernel->boot(); + } + + public function testShutdownCallsShutdownOnAllBundles() + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); + $bundle->expects($this->once()) + ->method('shutdown'); + + $kernel = $this->getKernel(array(), array($bundle)); + + $kernel->boot(); + $kernel->shutdown(); + } + + public function testShutdownGivesNullContainerToAllBundles() + { + $bundle = $this->getMock('Symfony\Component\HttpKernel\Bundle\Bundle'); + $bundle->expects($this->at(3)) + ->method('setContainer') + ->with(null); + + $kernel = $this->getKernel(array('getBundles')); + $kernel->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + $kernel->shutdown(); + } + + public function testHandleCallsHandleOnHttpKernel() + { + $type = HttpKernelInterface::MASTER_REQUEST; + $catch = true; + $request = new Request(); + + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->getMock(); + $httpKernelMock + ->expects($this->once()) + ->method('handle') + ->with($request, $type, $catch); + + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->handle($request, $type, $catch); + } + + public function testHandleBootsTheKernel() + { + $type = HttpKernelInterface::MASTER_REQUEST; + $catch = true; + $request = new Request(); + + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->getMock(); + + $kernel = $this->getKernel(array('getHttpKernel', 'boot')); + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->expects($this->once()) + ->method('boot'); + + $kernel->handle($request, $type, $catch); + } + + public function testStripComments() + { + $source = <<<'EOF' +assertEquals($expected, $output); + } + + public function testGetRootDir() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures', realpath($kernel->getRootDir())); + } + + public function testGetName() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals('Fixtures', $kernel->getName()); + } + + public function testOverrideGetName() + { + $kernel = new KernelForOverrideName('test', true); + + $this->assertEquals('overridden', $kernel->getName()); + } + + public function testSerialize() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $expected = serialize(array($env, $debug)); + $this->assertEquals($expected, $kernel->serialize()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenNameIsNotValid() + { + $this->getKernel()->locateResource('Foo'); + } + + /** + * @expectedException \RuntimeException + */ + public function testLocateResourceThrowsExceptionWhenNameIsUnsafe() + { + $this->getKernel()->locateResource('@FooBundle/../bar'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenBundleDoesNotExist() + { + $this->getKernel()->locateResource('@FooBundle/config/routing.xml'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenResourceDoesNotExist() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ; + + $kernel->locateResource('@Bundle1Bundle/config/routing.xml'); + } + + public function testLocateResourceReturnsTheFirstThatMatches() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ; + + $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt', $kernel->locateResource('@Bundle1Bundle/foo.txt')); + } + + public function testLocateResourceReturnsTheFirstThatMatchesWithParent() + { + $parent = $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'); + $child = $this->getBundle(__DIR__.'/Fixtures/Bundle2Bundle'); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue(array($child, $parent))) + ; + + $this->assertEquals(__DIR__.'/Fixtures/Bundle2Bundle/foo.txt', $kernel->locateResource('@ParentAABundle/foo.txt')); + $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/bar.txt', $kernel->locateResource('@ParentAABundle/bar.txt')); + } + + public function testLocateResourceReturnsAllMatches() + { + $parent = $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'); + $child = $this->getBundle(__DIR__.'/Fixtures/Bundle2Bundle'); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($child, $parent))) + ; + + $this->assertEquals(array( + __DIR__.'/Fixtures/Bundle2Bundle/foo.txt', + __DIR__.'/Fixtures/Bundle1Bundle/foo.txt', ), + $kernel->locateResource('@Bundle1Bundle/foo.txt', null, false)); + } + + public function testLocateResourceReturnsAllMatchesBis() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array( + $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'), + $this->getBundle(__DIR__.'/Foobar'), + ))) + ; + + $this->assertEquals( + array(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt'), + $kernel->locateResource('@Bundle1Bundle/foo.txt', null, false) + ); + } + + public function testLocateResourceIgnoresDirOnNonResource() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/foo.txt', + $kernel->locateResource('@Bundle1Bundle/foo.txt', __DIR__.'/Fixtures') + ); + } + + public function testLocateResourceReturnsTheDirOneForResources() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle/foo.txt', + $kernel->locateResource('@FooBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources') + ); + } + + public function testLocateResourceReturnsTheDirOneForResourcesAndBundleOnes() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle')))) + ; + + $this->assertEquals(array( + __DIR__.'/Fixtures/Resources/Bundle1Bundle/foo.txt', + __DIR__.'/Fixtures/Bundle1Bundle/Resources/foo.txt', ), + $kernel->locateResource('@Bundle1Bundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources', false) + ); + } + + public function testLocateResourceOverrideBundleAndResourcesFolders() + { + $parent = $this->getBundle(__DIR__.'/Fixtures/BaseBundle', null, 'BaseBundle', 'BaseBundle'); + $child = $this->getBundle(__DIR__.'/Fixtures/ChildBundle', 'ParentBundle', 'ChildBundle', 'ChildBundle'); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(4)) + ->method('getBundle') + ->will($this->returnValue(array($child, $parent))) + ; + + $this->assertEquals(array( + __DIR__.'/Fixtures/Resources/ChildBundle/foo.txt', + __DIR__.'/Fixtures/ChildBundle/Resources/foo.txt', + __DIR__.'/Fixtures/BaseBundle/Resources/foo.txt', + ), + $kernel->locateResource('@BaseBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources', false) + ); + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/ChildBundle/foo.txt', + $kernel->locateResource('@BaseBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources') + ); + + try { + $kernel->locateResource('@BaseBundle/Resources/hide.txt', __DIR__.'/Fixtures/Resources', false); + $this->fail('Hidden resources should raise an exception when returning an array of matching paths'); + } catch (\RuntimeException $e) { + } + + try { + $kernel->locateResource('@BaseBundle/Resources/hide.txt', __DIR__.'/Fixtures/Resources', true); + $this->fail('Hidden resources should raise an exception when returning the first matching path'); + } catch (\RuntimeException $e) { + } + } + + public function testLocateResourceOnDirectories() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle/', + $kernel->locateResource('@FooBundle/Resources/', __DIR__.'/Fixtures/Resources') + ); + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle', + $kernel->locateResource('@FooBundle/Resources', __DIR__.'/Fixtures/Resources') + ); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/Resources/', + $kernel->locateResource('@Bundle1Bundle/Resources/') + ); + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/Resources', + $kernel->locateResource('@Bundle1Bundle/Resources') + ); + } + + public function testInitializeBundles() + { + $parent = $this->getBundle(null, null, 'ParentABundle'); + $child = $this->getBundle(null, 'ParentABundle', 'ChildABundle'); + + // use test kernel so we can access getBundleMap() + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $child))) + ; + $kernel->boot(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent), $map['ParentABundle']); + } + + public function testInitializeBundlesSupportInheritanceCascade() + { + $grandparent = $this->getBundle(null, null, 'GrandParentBBundle'); + $parent = $this->getBundle(null, 'GrandParentBBundle', 'ParentBBundle'); + $child = $this->getBundle(null, 'ParentBBundle', 'ChildBBundle'); + + // use test kernel so we can access getBundleMap() + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($grandparent, $parent, $child))) + ; + $kernel->boot(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentBBundle']); + $this->assertEquals(array($child, $parent), $map['ParentBBundle']); + $this->assertEquals(array($child), $map['ChildBBundle']); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Bundle "ChildCBundle" extends bundle "FooBar", which is not registered. + */ + public function testInitializeBundlesThrowsExceptionWhenAParentDoesNotExists() + { + $child = $this->getBundle(null, 'FooBar', 'ChildCBundle'); + $kernel = $this->getKernel(array(), array($child)); + $kernel->boot(); + } + + public function testInitializeBundlesSupportsArbitraryBundleRegistrationOrder() + { + $grandparent = $this->getBundle(null, null, 'GrandParentCBundle'); + $parent = $this->getBundle(null, 'GrandParentCBundle', 'ParentCBundle'); + $child = $this->getBundle(null, 'ParentCBundle', 'ChildCBundle'); + + // use test kernel so we can access getBundleMap() + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $grandparent, $child))) + ; + $kernel->boot(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentCBundle']); + $this->assertEquals(array($child, $parent), $map['ParentCBundle']); + $this->assertEquals(array($child), $map['ChildCBundle']); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Bundle "ParentCBundle" is directly extended by two bundles "ChildC2Bundle" and "ChildC1Bundle". + */ + public function testInitializeBundlesThrowsExceptionWhenABundleIsDirectlyExtendedByTwoBundles() + { + $parent = $this->getBundle(null, null, 'ParentCBundle'); + $child1 = $this->getBundle(null, 'ParentCBundle', 'ChildC1Bundle'); + $child2 = $this->getBundle(null, 'ParentCBundle', 'ChildC2Bundle'); + + $kernel = $this->getKernel(array(), array($parent, $child1, $child2)); + $kernel->boot(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Trying to register two bundles with the same name "DuplicateName" + */ + public function testInitializeBundleThrowsExceptionWhenRegisteringTwoBundlesWithTheSameName() + { + $fooBundle = $this->getBundle(null, null, 'FooBundle', 'DuplicateName'); + $barBundle = $this->getBundle(null, null, 'BarBundle', 'DuplicateName'); + + $kernel = $this->getKernel(array(), array($fooBundle, $barBundle)); + $kernel->boot(); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Bundle "CircularRefBundle" can not extend itself. + */ + public function testInitializeBundleThrowsExceptionWhenABundleExtendsItself() + { + $circularRef = $this->getBundle(null, 'CircularRefBundle', 'CircularRefBundle'); + + $kernel = $this->getKernel(array(), array($circularRef)); + $kernel->boot(); + } + + public function testTerminateReturnsSilentlyIfKernelIsNotBooted() + { + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->never()) + ->method('getHttpKernel'); + + $kernel->terminate(Request::create('/'), new Response()); + } + + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + // does not implement TerminableInterface + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface') + ->disableOriginalConstructor() + ->getMock(); + + $httpKernelMock + ->expects($this->never()) + ->method('terminate'); + + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->boot(); + $kernel->terminate(Request::create('/'), new Response()); + + // implements TerminableInterface + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->setMethods(array('terminate')) + ->getMock(); + + $httpKernelMock + ->expects($this->once()) + ->method('terminate'); + + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->exactly(2)) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->boot(); + $kernel->terminate(Request::create('/'), new Response()); + } + + /** + * Returns a mock for the BundleInterface. + * + * @return BundleInterface + */ + protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) + { + $bundle = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface') + ->setMethods(array('getPath', 'getParent', 'getName')) + ->disableOriginalConstructor() + ; + + if ($className) { + $bundle->setMockClassName($className); + } + + $bundle = $bundle->getMockForAbstractClass(); + + $bundle + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue(null === $bundleName ? get_class($bundle) : $bundleName)) + ; + + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($dir)) + ; + + $bundle + ->expects($this->any()) + ->method('getParent') + ->will($this->returnValue($parent)) + ; + + return $bundle; + } + + /** + * Returns a mock for the abstract kernel. + * + * @param array $methods Additional methods to mock (besides the abstract ones) + * @param array $bundles Bundles to register + * + * @return Kernel + */ + protected function getKernel(array $methods = array(), array $bundles = array()) + { + $methods[] = 'registerBundles'; + + $kernel = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Kernel') + ->setMethods($methods) + ->setConstructorArgs(array('test', false)) + ->getMockForAbstractClass() + ; + $kernel->expects($this->any()) + ->method('registerBundles') + ->will($this->returnValue($bundles)) + ; + $p = new \ReflectionProperty($kernel, 'rootDir'); + $p->setAccessible(true); + $p->setValue($kernel, __DIR__.'/Fixtures'); + + return $kernel; + } + + protected function getKernelForTest(array $methods = array()) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->setConstructorArgs(array('test', false)) + ->setMethods($methods) + ->getMock(); + $p = new \ReflectionProperty($kernel, 'rootDir'); + $p->setAccessible(true); + $p->setValue($kernel, __DIR__.'/Fixtures'); + + return $kernel; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Logger.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Logger.php new file mode 100644 index 0000000..63c70bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Logger.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Psr\Log\LoggerInterface; + +class Logger implements LoggerInterface +{ + protected $logs; + + public function __construct() + { + $this->clear(); + } + + public function getLogs($level = false) + { + return false === $level ? $this->logs : $this->logs[$level]; + } + + public function clear() + { + $this->logs = array( + 'emergency' => array(), + 'alert' => array(), + 'critical' => array(), + 'error' => array(), + 'warning' => array(), + 'notice' => array(), + 'info' => array(), + 'debug' => array(), + ); + } + + public function log($level, $message, array $context = array()) + { + $this->logs[$level][] = $message; + } + + public function emergency($message, array $context = array()) + { + $this->log('emergency', $message, $context); + } + + public function alert($message, array $context = array()) + { + $this->log('alert', $message, $context); + } + + public function critical($message, array $context = array()) + { + $this->log('critical', $message, $context); + } + + public function error($message, array $context = array()) + { + $this->log('error', $message, $context); + } + + public function warning($message, array $context = array()) + { + $this->log('warning', $message, $context); + } + + public function notice($message, array $context = array()) + { + $this->log('notice', $message, $context); + } + + public function info($message, array $context = array()) + { + $this->log('info', $message, $context); + } + + public function debug($message, array $context = array()) + { + $this->log('debug', $message, $context); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/FileProfilerStorageTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/FileProfilerStorageTest.php new file mode 100644 index 0000000..6f53de8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/FileProfilerStorageTest.php @@ -0,0 +1,335 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profile; + +class FileProfilerStorageTest extends \PHPUnit_Framework_TestCase +{ + private $tmpDir; + private $storage; + + protected function setUp() + { + $this->tmpDir = sys_get_temp_dir().'/sf2_profiler_file_storage'; + if (is_dir($this->tmpDir)) { + self::cleanDir(); + } + $this->storage = new FileProfilerStorage('file:'.$this->tmpDir); + $this->storage->purge(); + } + + protected function tearDown() + { + self::cleanDir(); + } + + public function testStore() + { + for ($i = 0; $i < 10; ++$i) { + $profile = new Profile('token_'.$i); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar'); + $profile->setMethod('GET'); + $this->storage->write($profile); + } + $this->assertCount(10, $this->storage->find('127.0.0.1', 'http://foo.bar', 20, 'GET'), '->write() stores data in the storage'); + } + + public function testChildren() + { + $parentProfile = new Profile('token_parent'); + $parentProfile->setIp('127.0.0.1'); + $parentProfile->setUrl('http://foo.bar/parent'); + + $childProfile = new Profile('token_child'); + $childProfile->setIp('127.0.0.1'); + $childProfile->setUrl('http://foo.bar/child'); + + $parentProfile->addChild($childProfile); + + $this->storage->write($parentProfile); + $this->storage->write($childProfile); + + // Load them from storage + $parentProfile = $this->storage->read('token_parent'); + $childProfile = $this->storage->read('token_child'); + + // Check child has link to parent + $this->assertNotNull($childProfile->getParent()); + $this->assertEquals($parentProfile->getToken(), $childProfile->getParentToken()); + + // Check parent has child + $children = $parentProfile->getChildren(); + $this->assertCount(1, $children); + $this->assertEquals($childProfile->getToken(), $children[0]->getToken()); + } + + public function testStoreSpecialCharsInUrl() + { + // The storage accepts special characters in URLs (Even though URLs are not + // supposed to contain them) + $profile = new Profile('simple_quote'); + $profile->setUrl('http://foo.bar/\''); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('simple_quote'), '->write() accepts single quotes in URL'); + + $profile = new Profile('double_quote'); + $profile->setUrl('http://foo.bar/"'); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('double_quote'), '->write() accepts double quotes in URL'); + + $profile = new Profile('backslash'); + $profile->setUrl('http://foo.bar/\\'); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('backslash'), '->write() accepts backslash in URL'); + + $profile = new Profile('comma'); + $profile->setUrl('http://foo.bar/,'); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('comma'), '->write() accepts comma in URL'); + } + + public function testStoreDuplicateToken() + { + $profile = new Profile('token'); + $profile->setUrl('http://example.com/'); + + $this->assertTrue($this->storage->write($profile), '->write() returns true when the token is unique'); + + $profile->setUrl('http://example.net/'); + + $this->assertTrue($this->storage->write($profile), '->write() returns true when the token is already present in the storage'); + $this->assertEquals('http://example.net/', $this->storage->read('token')->getUrl(), '->write() overwrites the current profile data'); + + $this->assertCount(1, $this->storage->find('', '', 1000, ''), '->find() does not return the same profile twice'); + } + + public function testRetrieveByIp() + { + $profile = new Profile('token'); + $profile->setIp('127.0.0.1'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertCount(1, $this->storage->find('127.0.0.1', '', 10, 'GET'), '->find() retrieve a record by IP'); + $this->assertCount(0, $this->storage->find('127.0.%.1', '', 10, 'GET'), '->find() does not interpret a "%" as a wildcard in the IP'); + $this->assertCount(0, $this->storage->find('127.0._.1', '', 10, 'GET'), '->find() does not interpret a "_" as a wildcard in the IP'); + } + + public function testRetrieveByUrl() + { + $profile = new Profile('simple_quote'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/\''); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('double_quote'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/"'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('backslash'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo\\bar/'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('percent'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/%'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('underscore'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/_'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('semicolon'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/;'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/\'', 10, 'GET'), '->find() accepts single quotes in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/"', 10, 'GET'), '->find() accepts double quotes in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo\\bar/', 10, 'GET'), '->find() accepts backslash in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/;', 10, 'GET'), '->find() accepts semicolon in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/%', 10, 'GET'), '->find() does not interpret a "%" as a wildcard in the URL'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/_', 10, 'GET'), '->find() does not interpret a "_" as a wildcard in the URL'); + } + + public function testStoreTime() + { + $dt = new \DateTime('now'); + $start = $dt->getTimestamp(); + + for ($i = 0; $i < 3; ++$i) { + $dt->modify('+1 minute'); + $profile = new Profile('time_'.$i); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar'); + $profile->setTime($dt->getTimestamp()); + $profile->setMethod('GET'); + $this->storage->write($profile); + } + + $records = $this->storage->find('', '', 3, 'GET', $start, time() + 3 * 60); + $this->assertCount(3, $records, '->find() returns all previously added records'); + $this->assertEquals($records[0]['token'], 'time_2', '->find() returns records ordered by time in descendant order'); + $this->assertEquals($records[1]['token'], 'time_1', '->find() returns records ordered by time in descendant order'); + $this->assertEquals($records[2]['token'], 'time_0', '->find() returns records ordered by time in descendant order'); + + $records = $this->storage->find('', '', 3, 'GET', $start, time() + 2 * 60); + $this->assertCount(2, $records, '->find() should return only first two of the previously added records'); + } + + public function testRetrieveByEmptyUrlAndIp() + { + for ($i = 0; $i < 5; ++$i) { + $profile = new Profile('token_'.$i); + $profile->setMethod('GET'); + $this->storage->write($profile); + } + $this->assertCount(5, $this->storage->find('', '', 10, 'GET'), '->find() returns all previously added records'); + $this->storage->purge(); + } + + public function testRetrieveByMethodAndLimit() + { + foreach (array('POST', 'GET') as $method) { + for ($i = 0; $i < 5; ++$i) { + $profile = new Profile('token_'.$i.$method); + $profile->setMethod($method); + $this->storage->write($profile); + } + } + + $this->assertCount(5, $this->storage->find('', '', 5, 'POST')); + + $this->storage->purge(); + } + + public function testPurge() + { + $profile = new Profile('token1'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.com/'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertTrue(false !== $this->storage->read('token1')); + $this->assertCount(1, $this->storage->find('127.0.0.1', '', 10, 'GET')); + + $profile = new Profile('token2'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.net/'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertTrue(false !== $this->storage->read('token2')); + $this->assertCount(2, $this->storage->find('127.0.0.1', '', 10, 'GET')); + + $this->storage->purge(); + + $this->assertEmpty($this->storage->read('token'), '->purge() removes all data stored by profiler'); + $this->assertCount(0, $this->storage->find('127.0.0.1', '', 10, 'GET'), '->purge() removes all items from index'); + } + + public function testDuplicates() + { + for ($i = 1; $i <= 5; ++$i) { + $profile = new Profile('foo'.$i); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.net/'); + $profile->setMethod('GET'); + + ///three duplicates + $this->storage->write($profile); + $this->storage->write($profile); + $this->storage->write($profile); + } + $this->assertCount(3, $this->storage->find('127.0.0.1', 'http://example.net/', 3, 'GET'), '->find() method returns incorrect number of entries'); + } + + public function testStatusCode() + { + $profile = new Profile('token1'); + $profile->setStatusCode(200); + $this->storage->write($profile); + + $profile = new Profile('token2'); + $profile->setStatusCode(404); + $this->storage->write($profile); + + $tokens = $this->storage->find('', '', 10, ''); + $this->assertCount(2, $tokens); + $this->assertContains($tokens[0]['status_code'], array(200, 404)); + $this->assertContains($tokens[1]['status_code'], array(200, 404)); + } + + public function testMultiRowIndexFile() + { + $iteration = 3; + for ($i = 0; $i < $iteration; ++$i) { + $profile = new Profile('token'.$i); + $profile->setIp('127.0.0.'.$i); + $profile->setUrl('http://foo.bar/'.$i); + + $this->storage->write($profile); + $this->storage->write($profile); + $this->storage->write($profile); + } + + $handle = fopen($this->tmpDir.'/index.csv', 'r'); + for ($i = 0; $i < $iteration; ++$i) { + $row = fgetcsv($handle); + $this->assertEquals('token'.$i, $row[0]); + $this->assertEquals('127.0.0.'.$i, $row[1]); + $this->assertEquals('http://foo.bar/'.$i, $row[3]); + } + $this->assertFalse(fgetcsv($handle)); + } + + public function testReadLineFromFile() + { + $r = new \ReflectionMethod($this->storage, 'readLineFromFile'); + + $r->setAccessible(true); + + $h = tmpfile(); + + fwrite($h, "line1\n\n\nline2\n"); + fseek($h, 0, SEEK_END); + + $this->assertEquals('line2', $r->invoke($this->storage, $h)); + $this->assertEquals('line1', $r->invoke($this->storage, $h)); + } + + protected function cleanDir() + { + $flags = \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveDirectoryIterator($this->tmpDir, $flags); + $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); + + foreach ($iterator as $file) { + if (is_file($file)) { + unlink($file); + } + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php new file mode 100644 index 0000000..6e56f8b --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/Profiler/ProfilerTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Profiler; + +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ProfilerTest extends \PHPUnit_Framework_TestCase +{ + private $tmp; + private $storage; + + public function testCollect() + { + $request = new Request(); + $request->query->set('foo', 'bar'); + $response = new Response('', 204); + $collector = new RequestDataCollector(); + + $profiler = new Profiler($this->storage); + $profiler->add($collector); + $profile = $profiler->collect($request, $response); + + $this->assertSame(204, $profile->getStatusCode()); + $this->assertSame('GET', $profile->getMethod()); + $this->assertEquals(array('foo' => 'bar'), $profiler->get('request')->getRequestQuery()->all()); + } + + public function testFindWorksWithDates() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, '7th April 2014', '9th April 2014')); + } + + public function testFindWorksWithTimestamps() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, '1396828800', '1397001600')); + } + + public function testFindWorksWithInvalidDates() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, 'some string', '')); + } + + protected function setUp() + { + $this->tmp = tempnam(sys_get_temp_dir(), 'sf2_profiler'); + if (file_exists($this->tmp)) { + @unlink($this->tmp); + } + + $this->storage = new FileProfilerStorage('file:'.$this->tmp); + $this->storage->purge(); + } + + protected function tearDown() + { + if (null !== $this->storage) { + $this->storage->purge(); + $this->storage = null; + + @unlink($this->tmp); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/TestHttpKernel.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/TestHttpKernel.php new file mode 100644 index 0000000..d526c4d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/TestHttpKernel.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestHttpKernel extends HttpKernel implements ControllerResolverInterface +{ + public function __construct() + { + parent::__construct(new EventDispatcher(), $this); + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + return new Response('Request: '.$request->getRequestUri()); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php new file mode 100644 index 0000000..6b1fd90 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\UriSigner; + +class UriSignerTest extends \PHPUnit_Framework_TestCase +{ + public function testSign() + { + $signer = new UriSigner('foobar'); + + $this->assertContains('?_hash=', $signer->sign('http://example.com/foo')); + $this->assertContains('&_hash=', $signer->sign('http://example.com/foo?foo=bar')); + } + + public function testCheck() + { + $signer = new UriSigner('foobar'); + + $this->assertFalse($signer->check('http://example.com/foo?_hash=foo')); + $this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo')); + $this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo&bar=foo')); + + $this->assertTrue($signer->check($signer->sign('http://example.com/foo'))); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar'))); + + $this->assertTrue($signer->sign('http://example.com/foo?foo=bar&bar=foo') === $signer->sign('http://example.com/foo?bar=foo&foo=bar')); + } + + public function testCheckWithDifferentArgSeparator() + { + $this->iniSet('arg_separator.output', '&'); + $signer = new UriSigner('foobar'); + + $this->assertSame( + 'http://example.com/foo?baz=bay&foo=bar&_hash=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D', + $signer->sign('http://example.com/foo?foo=bar&baz=bay') + ); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay'))); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/UriSigner.php b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/UriSigner.php new file mode 100644 index 0000000..6ddce87 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/UriSigner.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +/** + * Signs URIs. + * + * @author Fabien Potencier + */ +class UriSigner +{ + private $secret; + + /** + * Constructor. + * + * @param string $secret A secret + */ + public function __construct($secret) + { + $this->secret = $secret; + } + + /** + * Signs a URI. + * + * The given URI is signed by adding a _hash query string parameter + * which value depends on the URI and the secret. + * + * @param string $uri A URI to sign + * + * @return string The signed URI + */ + public function sign($uri) + { + $url = parse_url($uri); + if (isset($url['query'])) { + parse_str($url['query'], $params); + } else { + $params = array(); + } + + $uri = $this->buildUrl($url, $params); + + return $uri.(false === (strpos($uri, '?')) ? '?' : '&').'_hash='.$this->computeHash($uri); + } + + /** + * Checks that a URI contains the correct hash. + * + * The _hash query string parameter must be the last one + * (as it is generated that way by the sign() method, it should + * never be a problem). + * + * @param string $uri A signed URI + * + * @return bool True if the URI is signed correctly, false otherwise + */ + public function check($uri) + { + $url = parse_url($uri); + if (isset($url['query'])) { + parse_str($url['query'], $params); + } else { + $params = array(); + } + + if (empty($params['_hash'])) { + return false; + } + + $hash = urlencode($params['_hash']); + unset($params['_hash']); + + return $this->computeHash($this->buildUrl($url, $params)) === $hash; + } + + private function computeHash($uri) + { + return urlencode(base64_encode(hash_hmac('sha256', $uri, $this->secret, true))); + } + + private function buildUrl(array $url, array $params = array()) + { + ksort($params); + $url['query'] = http_build_query($params, '', '&'); + + $scheme = isset($url['scheme']) ? $url['scheme'].'://' : ''; + $host = isset($url['host']) ? $url['host'] : ''; + $port = isset($url['port']) ? ':'.$url['port'] : ''; + $user = isset($url['user']) ? $url['user'] : ''; + $pass = isset($url['pass']) ? ':'.$url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($url['path']) ? $url['path'] : ''; + $query = isset($url['query']) && $url['query'] ? '?'.$url['query'] : ''; + $fragment = isset($url['fragment']) ? '#'.$url['fragment'] : ''; + + return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/composer.json b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/composer.json new file mode 100644 index 0000000..95352d0 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/composer.json @@ -0,0 +1,66 @@ +{ + "name": "symfony/http-kernel", + "type": "library", + "description": "Symfony HttpKernel Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.5.9", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/http-foundation": "~2.8|~3.0", + "symfony/debug": "~2.8|~3.0", + "psr/log": "~1.0" + }, + "require-dev": { + "symfony/browser-kit": "~2.8|~3.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0", + "symfony/console": "~2.8|~3.0", + "symfony/css-selector": "~2.8|~3.0", + "symfony/dependency-injection": "~2.8|~3.0", + "symfony/dom-crawler": "~2.8|~3.0", + "symfony/expression-language": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0", + "symfony/routing": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0", + "symfony/templating": "~2.8|~3.0", + "symfony/translation": "~2.8|~3.0", + "symfony/var-dumper": "~2.8|~3.0" + }, + "conflict": { + "symfony/config": "<2.8" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/class-loader": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\HttpKernel\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/phpunit.xml.dist b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/phpunit.xml.dist new file mode 100644 index 0000000..17c4893 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/phpunit.xml.dist @@ -0,0 +1,38 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + + + + + + Symfony\Component\HttpFoundation + + + + + diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/CONTRIBUTING.md b/vendor/symfony/symfony/src/Symfony/Component/Intl/CONTRIBUTING.md new file mode 100644 index 0000000..971e0af --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/CONTRIBUTING.md @@ -0,0 +1,91 @@ +Contributing to the Intl component +================================== + +A very good way of contributing to the Intl component is by updating the +included data for the ICU version you have installed on your system. + +Preparation +----------- + +To prepare, you need to install the development dependencies of the component. + + $ cd /path/to/Symfony/Component/Intl + $ composer install + +Determining your ICU version +--------------------------- + +The ICU version installed in your PHP environment can be found by running +icu-version.php: + + $ php Resources/bin/icu-version.php + +Updating the ICU data +--------------------- + +To update the data files, run the update-icu-component.php script: + + $ php Resources/bin/update-icu-component.php + +The script needs the binaries "svn" and "make" to be available on your system. +It will download the latest version of the ICU sources for the ICU version +installed in your PHP environment. The script will then compile the "genrb" +binary and use it to compile the ICU data files to binaries. The binaries are +copied to the Resources/ directory of the Icu component found in the +vendor/symfony/icu/ directory. + +Updating the stub data +---------------------- + +In the previous step you updated the Icu component for the ICU version +installed on your system. If you are using the latest ICU version, you should +also create the stub data files which will be used by people who don't have +the intl extension installed. + +To update the stub files, run the update-stubs.php script: + + $ php Resources/bin/update-stubs.php + +The script will fail if you don't have the latest ICU version. If you want to +upgrade the ICU version, adjust the return value of the +`Intl::getIcuStubVersion()` before you run the script. + +The script creates copies of the binary resource bundles in the Icu component +and stores them in the Resources/ directory of the Intl component. The copies +are made for the locale "en" only and are stored in .php files, so that they +can be read even if the intl extension is not available. + +Creating a pull request +----------------------- + +You need to create up to two pull requests: + +* If you updated the Icu component, you need to push that change and create a + pull request in the `symfony/Icu` repository. Make sure to submit the pull + request to the correct master branch. If you updated the ICU data for version + 4.8, your pull request goes to branch `48-master`, for version 49 to + `49-master` and so on. + +* If you updated the stub files of the Intl component, you need to push that + change and create a pull request in the `symfony/symfony` repository. The + pull request should be based on the `master` branch. + +Combining .res files to a .dat-package +-------------------------------------- + +The individual *.res files can be combined into a single .dat-file. +Unfortunately, PHP's `ResourceBundle` class is currently not able to handle +.dat-files. + +Once it is, the following steps have to be followed to build the .dat-file: + +1. Package the resource bundles into a single file + + $ find . -name *.res | sed -e "s/\.\///g" > packagelist.txt + $ pkgdata -p region -T build -d . packagelist.txt + +2. Clean up + + $ rm -rf build packagelist.txt + +3. You can now move region.dat to replace the version bundled with Symfony. diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Collator/Collator.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Collator/Collator.php new file mode 100644 index 0000000..7482aaa --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Collator/Collator.php @@ -0,0 +1,297 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Collator; + +use Symfony\Component\Intl\Exception\MethodNotImplementedException; +use Symfony\Component\Intl\Exception\MethodArgumentValueNotImplementedException; +use Symfony\Component\Intl\Globals\IntlGlobals; +use Symfony\Component\Intl\Locale\Locale; + +/** + * Replacement for PHP's native {@link \Collator} class. + * + * The only methods currently supported in this class are: + * + * - {@link \__construct} + * - {@link create} + * - {@link asort} + * - {@link getErrorCode} + * - {@link getErrorMessage} + * - {@link getLocale} + * + * @author Igor Wiedler + * @author Bernhard Schussek + * + * @internal + */ +class Collator +{ + /* Attribute constants */ + const FRENCH_COLLATION = 0; + const ALTERNATE_HANDLING = 1; + const CASE_FIRST = 2; + const CASE_LEVEL = 3; + const NORMALIZATION_MODE = 4; + const STRENGTH = 5; + const HIRAGANA_QUATERNARY_MODE = 6; + const NUMERIC_COLLATION = 7; + + /* Attribute constants values */ + const DEFAULT_VALUE = -1; + + const PRIMARY = 0; + const SECONDARY = 1; + const TERTIARY = 2; + const DEFAULT_STRENGTH = 2; + const QUATERNARY = 3; + const IDENTICAL = 15; + + const OFF = 16; + const ON = 17; + + const SHIFTED = 20; + const NON_IGNORABLE = 21; + + const LOWER_FIRST = 24; + const UPPER_FIRST = 25; + + /* Sorting options */ + const SORT_REGULAR = 0; + const SORT_NUMERIC = 2; + const SORT_STRING = 1; + + /** + * Constructor. + * + * @param string $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en"). + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed + */ + public function __construct($locale) + { + if ('en' !== $locale && null !== $locale) { + throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported'); + } + } + + /** + * Static constructor. + * + * @param string $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en"). + * + * @return Collator + * + * @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed + */ + public static function create($locale) + { + return new self($locale); + } + + /** + * Sort array maintaining index association. + * + * @param array &$array Input array + * @param int $sortFlag Flags for sorting, can be one of the following: + * Collator::SORT_REGULAR - compare items normally (don't change types) + * Collator::SORT_NUMERIC - compare items numerically + * Collator::SORT_STRING - compare items as strings + * + * @return bool True on success or false on failure + */ + public function asort(&$array, $sortFlag = self::SORT_REGULAR) + { + $intlToPlainFlagMap = array( + self::SORT_REGULAR => \SORT_REGULAR, + self::SORT_NUMERIC => \SORT_NUMERIC, + self::SORT_STRING => \SORT_STRING, + ); + + $plainSortFlag = isset($intlToPlainFlagMap[$sortFlag]) ? $intlToPlainFlagMap[$sortFlag] : self::SORT_REGULAR; + + return asort($array, $plainSortFlag); + } + + /** + * Not supported. Compare two Unicode strings. + * + * @param string $str1 The first string to compare + * @param string $str2 The second string to compare + * + * @return bool|int Return the comparison result or false on failure: + * 1 if $str1 is greater than $str2 + * 0 if $str1 is equal than $str2 + * -1 if $str1 is less than $str2 + * + * @see http://www.php.net/manual/en/collator.compare.php + * + * @throws MethodNotImplementedException + */ + public function compare($str1, $str2) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Get a value of an integer collator attribute. + * + * @param int $attr An attribute specifier, one of the attribute constants + * + * @return bool|int The attribute value on success or false on error + * + * @see http://www.php.net/manual/en/collator.getattribute.php + * + * @throws MethodNotImplementedException + */ + public function getAttribute($attr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Returns collator's last error code. Always returns the U_ZERO_ERROR class constant value. + * + * @return int The error code from last collator call + */ + public function getErrorCode() + { + return IntlGlobals::U_ZERO_ERROR; + } + + /** + * Returns collator's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value. + * + * @return string The error message from last collator call + */ + public function getErrorMessage() + { + return 'U_ZERO_ERROR'; + } + + /** + * Returns the collator's locale. + * + * @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE) + * + * @return string The locale used to create the collator. Currently always + * returns "en". + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + return 'en'; + } + + /** + * Not supported. Get sorting key for a string. + * + * @param string $string The string to produce the key from + * + * @return string The collation key for $string + * + * @see http://www.php.net/manual/en/collator.getsortkey.php + * + * @throws MethodNotImplementedException + */ + public function getSortKey($string) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Get current collator's strength. + * + * @return bool|int The current collator's strength or false on failure + * + * @see http://www.php.net/manual/en/collator.getstrength.php + * + * @throws MethodNotImplementedException + */ + public function getStrength() + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set a collator's attribute. + * + * @param int $attr An attribute specifier, one of the attribute constants + * @param int $val The attribute value, one of the attribute value constants + * + * @return bool True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.setattribute.php + * + * @throws MethodNotImplementedException + */ + public function setAttribute($attr, $val) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Set the collator's strength. + * + * @param int $strength Strength to set, possible values: + * Collator::PRIMARY + * Collator::SECONDARY + * Collator::TERTIARY + * Collator::QUATERNARY + * Collator::IDENTICAL + * Collator::DEFAULT + * + * @return bool True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.setstrength.php + * + * @throws MethodNotImplementedException + */ + public function setStrength($strength) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Sort array using specified collator and sort keys. + * + * @param array &$arr Array of strings to sort + * + * @return bool True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.sortwithsortkeys.php + * + * @throws MethodNotImplementedException + */ + public function sortWithSortKeys(&$arr) + { + throw new MethodNotImplementedException(__METHOD__); + } + + /** + * Not supported. Sort array using specified collator. + * + * @param array &$arr Array of string to sort + * @param int $sortFlag Optional sorting type, one of the following: + * Collator::SORT_REGULAR + * Collator::SORT_NUMERIC + * Collator::SORT_STRING + * + * @return bool True on success or false on failure + * + * @see http://www.php.net/manual/en/collator.sort.php + * + * @throws MethodNotImplementedException + */ + public function sort(&$arr, $sortFlag = self::SORT_REGULAR) + { + throw new MethodNotImplementedException(__METHOD__); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Compiler/BundleCompilerInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Compiler/BundleCompilerInterface.php new file mode 100644 index 0000000..8c9d233 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Compiler/BundleCompilerInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Compiler; + +/** + * Compiles a resource bundle. + * + * @author Bernhard Schussek + * + * @internal + */ +interface BundleCompilerInterface +{ + /** + * Compiles a resource bundle at the given source to the given target + * directory. + * + * @param string $sourcePath + * @param string $targetDir + */ + public function compile($sourcePath, $targetDir); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php new file mode 100644 index 0000000..cf8ee1a --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Compiler/GenrbCompiler.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Compiler; + +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Compiles .txt resource bundles to binary .res files. + * + * @author Bernhard Schussek + * + * @internal + */ +class GenrbCompiler implements BundleCompilerInterface +{ + /** + * @var string The path to the "genrb" executable. + */ + private $genrb; + + /** + * Creates a new compiler based on the "genrb" executable. + * + * @param string $genrb Optional. The path to the "genrb" executable. + * @param string $envVars Optional. Environment variables to be loaded when + * running "genrb". + * + * @throws RuntimeException If the "genrb" cannot be found. + */ + public function __construct($genrb = 'genrb', $envVars = '') + { + exec('which '.$genrb, $output, $status); + + if (0 !== $status) { + throw new RuntimeException(sprintf( + 'The command "%s" is not installed', + $genrb + )); + } + + $this->genrb = ($envVars ? $envVars.' ' : '').$genrb; + } + + /** + * {@inheritdoc} + */ + public function compile($sourcePath, $targetDir) + { + if (is_dir($sourcePath)) { + $sourcePath .= '/*.txt'; + } + + exec($this->genrb.' --quiet -e UTF-8 -d '.$targetDir.' '.$sourcePath, $output, $status); + + if ($status !== 0) { + throw new RuntimeException(sprintf( + 'genrb failed with status %d while compiling %s to %s.', + $status, + $sourcePath, + $targetDir + )); + } + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php new file mode 100644 index 0000000..fede9cd --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BufferedBundleReader.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +use Symfony\Component\Intl\Data\Util\RingBuffer; + +/** + * @author Bernhard Schussek + * + * @internal + */ +class BufferedBundleReader implements BundleReaderInterface +{ + /** + * @var BundleReaderInterface + */ + private $reader; + + private $buffer; + + /** + * Buffers a given reader. + * + * @param BundleReaderInterface $reader The reader to buffer. + * @param int $bufferSize The number of entries to store + * in the buffer. + */ + public function __construct(BundleReaderInterface $reader, $bufferSize) + { + $this->reader = $reader; + $this->buffer = new RingBuffer($bufferSize); + } + + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + $hash = $path.'//'.$locale; + + if (!isset($this->buffer[$hash])) { + $this->buffer[$hash] = $this->reader->read($path, $locale); + } + + return $this->buffer[$hash]; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleEntryReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleEntryReader.php new file mode 100644 index 0000000..5f991bf --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleEntryReader.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +use Symfony\Component\Intl\Exception\MissingResourceException; +use Symfony\Component\Intl\Exception\OutOfBoundsException; +use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException; +use Symfony\Component\Intl\Locale; +use Symfony\Component\Intl\Data\Util\RecursiveArrayAccess; + +/** + * Default implementation of {@link BundleEntryReaderInterface}. + * + * @author Bernhard Schussek + * + * @see BundleEntryReaderInterface + * + * @internal + */ +class BundleEntryReader implements BundleEntryReaderInterface +{ + /** + * @var BundleReaderInterface + */ + private $reader; + + /** + * A mapping of locale aliases to locales. + * + * @var array + */ + private $localeAliases = array(); + + /** + * Creates an entry reader based on the given resource bundle reader. + * + * @param BundleReaderInterface $reader A resource bundle reader to use. + */ + public function __construct(BundleReaderInterface $reader) + { + $this->reader = $reader; + } + + /** + * Stores a mapping of locale aliases to locales. + * + * This mapping is used when reading entries and merging them with their + * fallback locales. If an entry is read for a locale alias (e.g. "mo") + * that points to a locale with a fallback locale ("ro_MD"), the reader + * can continue at the correct fallback locale ("ro"). + * + * @param array $localeAliases A mapping of locale aliases to locales + */ + public function setLocaleAliases($localeAliases) + { + $this->localeAliases = $localeAliases; + } + + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + return $this->reader->read($path, $locale); + } + + /** + * {@inheritdoc} + */ + public function readEntry($path, $locale, array $indices, $fallback = true) + { + $entry = null; + $isMultiValued = false; + $readSucceeded = false; + $exception = null; + $currentLocale = $locale; + $testedLocales = array(); + + while (null !== $currentLocale) { + // Resolve any aliases to their target locales + if (isset($this->localeAliases[$currentLocale])) { + $currentLocale = $this->localeAliases[$currentLocale]; + } + + try { + $data = $this->reader->read($path, $currentLocale); + $currentEntry = RecursiveArrayAccess::get($data, $indices); + $readSucceeded = true; + + $isCurrentTraversable = $currentEntry instanceof \Traversable; + $isCurrentMultiValued = $isCurrentTraversable || is_array($currentEntry); + + // Return immediately if fallback is disabled or we are dealing + // with a scalar non-null entry + if (!$fallback || (!$isCurrentMultiValued && null !== $currentEntry)) { + return $currentEntry; + } + + // ========================================================= + // Fallback is enabled, entry is either multi-valued or NULL + // ========================================================= + + // If entry is multi-valued, convert to array + if ($isCurrentTraversable) { + $currentEntry = iterator_to_array($currentEntry); + } + + // If previously read entry was multi-valued too, merge them + if ($isCurrentMultiValued && $isMultiValued) { + $currentEntry = array_merge($currentEntry, $entry); + } + + // Keep the previous entry if the current entry is NULL + if (null !== $currentEntry) { + $entry = $currentEntry; + } + + // If this or the previous entry was multi-valued, we are dealing + // with a merged, multi-valued entry now + $isMultiValued = $isMultiValued || $isCurrentMultiValued; + } catch (ResourceBundleNotFoundException $e) { + // Continue if there is a fallback locale for the current + // locale + $exception = $e; + } catch (OutOfBoundsException $e) { + // Remember exception and rethrow if we cannot find anything in + // the fallback locales either + $exception = $e; + } + + // Remember which locales we tried + $testedLocales[] = $currentLocale; + + // Check whether fallback is allowed + if (!$fallback) { + break; + } + + // Then determine fallback locale + $currentLocale = Locale::getFallback($currentLocale); + } + + // Multi-valued entry was merged + if ($isMultiValued) { + return $entry; + } + + // Entry is still NULL, but no read error occurred + if ($readSucceeded) { + return $entry; + } + + // Entry is still NULL, read error occurred. Throw an exception + // containing the detailed path and locale + $errorMessage = sprintf( + 'Couldn\'t read the indices [%s] for the locale "%s" in "%s".', + implode('][', $indices), + $locale, + $path + ); + + // Append fallback locales, if any + if (count($testedLocales) > 1) { + // Remove original locale + array_shift($testedLocales); + + $errorMessage .= sprintf( + ' The indices also couldn\'t be found for the fallback locale(s) "%s".', + implode('", "', $testedLocales) + ); + } + + throw new MissingResourceException($errorMessage, 0, $exception); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleEntryReaderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleEntryReaderInterface.php new file mode 100644 index 0000000..8a4ecd8 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleEntryReaderInterface.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +use Symfony\Component\Intl\Exception\MissingResourceException; + +/** + * Reads individual entries of a resource file. + * + * @author Bernhard Schussek + * + * @internal + */ +interface BundleEntryReaderInterface extends BundleReaderInterface +{ + /** + * Reads an entry from a resource bundle. + * + * An entry can be selected from the resource bundle by passing the path + * to that entry in the bundle. For example, if the bundle is structured + * like this: + * + * TopLevel + * NestedLevel + * Entry: Value + * + * Then the value can be read by calling: + * + * $reader->readEntry('...', 'en', array('TopLevel', 'NestedLevel', 'Entry')); + * + * @param string $path The path to the resource bundle. + * @param string $locale The locale to read. + * @param string[] $indices The indices to read from the bundle. + * @param bool $fallback Whether to merge the value with the value from + * the fallback locale (e.g. "en" for "en_GB"). + * Only applicable if the result is multivalued + * (i.e. array or \ArrayAccess) or cannot be found + * in the requested locale. + * + * @return mixed Returns an array or {@link \ArrayAccess} instance for + * complex data and a scalar value for simple data. + * + * @throws MissingResourceException If the indices cannot be accessed + */ + public function readEntry($path, $locale, array $indices, $fallback = true); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleReaderInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleReaderInterface.php new file mode 100644 index 0000000..7257b0d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/BundleReaderInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +/** + * Reads resource bundle files. + * + * @author Bernhard Schussek + * + * @internal + */ +interface BundleReaderInterface +{ + /** + * Reads a resource bundle. + * + * @param string $path The path to the resource bundle. + * @param string $locale The locale to read. + * + * @return mixed Returns an array or {@link \ArrayAccess} instance for + * complex data, a scalar value otherwise. + */ + public function read($path, $locale); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php new file mode 100644 index 0000000..9f800cc --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/IntlBundleReader.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException; +use Symfony\Component\Intl\Data\Util\ArrayAccessibleResourceBundle; + +/** + * Reads binary .res resource bundles. + * + * @author Bernhard Schussek + * + * @internal + */ +class IntlBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + // Point for future extension: Modify this class so that it works also + // if the \ResourceBundle class is not available. + try { + // Never enable fallback. We want to know if a bundle cannot be found + $bundle = new \ResourceBundle($locale, $path, false); + } catch (\Exception $e) { + // HHVM compatibility: constructor throws on invalid resource + $bundle = null; + } + + // The bundle is NULL if the path does not look like a resource bundle + // (i.e. contain a bunch of *.res files) + if (null === $bundle) { + throw new ResourceBundleNotFoundException(sprintf( + 'The resource bundle "%s/%s.res" could not be found.', + $path, + $locale + )); + } + + // Other possible errors are U_USING_FALLBACK_WARNING and U_ZERO_ERROR, + // which are OK for us. + return new ArrayAccessibleResourceBundle($bundle); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php new file mode 100644 index 0000000..84b20ab --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException; +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Reads .json resource bundles. + * + * @author Bernhard Schussek + * + * @internal + */ +class JsonBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + $fileName = $path.'/'.$locale.'.json'; + + if (!file_exists($fileName)) { + throw new ResourceBundleNotFoundException(sprintf( + 'The resource bundle "%s/%s.json" does not exist.', + $path, + $locale + )); + } + + if (!is_file($fileName)) { + throw new RuntimeException(sprintf( + 'The resource bundle "%s/%s.json" is not a file.', + $path, + $locale + )); + } + + $data = json_decode(file_get_contents($fileName), true); + + if (null === $data) { + throw new RuntimeException(sprintf( + 'The resource bundle "%s/%s.json" contains invalid JSON: %s', + $path, + $locale, + json_last_error_msg() + )); + } + + return $data; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/PhpBundleReader.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/PhpBundleReader.php new file mode 100644 index 0000000..57391ce --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Reader/PhpBundleReader.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Reader; + +use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException; +use Symfony\Component\Intl\Exception\RuntimeException; + +/** + * Reads .php resource bundles. + * + * @author Bernhard Schussek + * + * @internal + */ +class PhpBundleReader implements BundleReaderInterface +{ + /** + * {@inheritdoc} + */ + public function read($path, $locale) + { + $fileName = $path.'/'.$locale.'.php'; + + if (!file_exists($fileName)) { + throw new ResourceBundleNotFoundException(sprintf( + 'The resource bundle "%s/%s.php" does not exist.', + $path, + $locale + )); + } + + if (!is_file($fileName)) { + throw new RuntimeException(sprintf( + 'The resource bundle "%s/%s.php" is not a file.', + $path, + $locale + )); + } + + return include $fileName; + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/BundleWriterInterface.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/BundleWriterInterface.php new file mode 100644 index 0000000..e85376d --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/BundleWriterInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Writer; + +/** + * Writes resource bundle files. + * + * @author Bernhard Schussek + * + * @internal + */ +interface BundleWriterInterface +{ + /** + * Writes data to a resource bundle. + * + * @param string $path The path to the resource bundle. + * @param string $locale The locale to (over-)write. + * @param mixed $data The data to write. + */ + public function write($path, $locale, $data); +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/JsonBundleWriter.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/JsonBundleWriter.php new file mode 100644 index 0000000..f3df769 --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/JsonBundleWriter.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Writer; + +/** + * Writes .json resource bundles. + * + * @author Bernhard Schussek + * + * @internal + */ +class JsonBundleWriter implements BundleWriterInterface +{ + /** + * {@inheritdoc} + */ + public function write($path, $locale, $data) + { + if ($data instanceof \Traversable) { + $data = iterator_to_array($data); + } + + array_walk_recursive($data, function (&$value) { + if ($value instanceof \Traversable) { + $value = iterator_to_array($value); + } + }); + + $contents = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)."\n"; + + file_put_contents($path.'/'.$locale.'.json', $contents); + } +} diff --git a/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/PhpBundleWriter.php b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/PhpBundleWriter.php new file mode 100644 index 0000000..a29972c --- /dev/null +++ b/vendor/symfony/symfony/src/Symfony/Component/Intl/Data/Bundle/Writer/PhpBundleWriter.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Intl\Data\Bundle\Writer; + +/** + * Writes .php resource bundles. + * + * @author Bernhard Schussek + * + * @internal + */ +class PhpBundleWriter implements BundleWriterInterface +{ + /** + * {@inheritdoc} + */ + public function write($path, $locale, $data) + { + $template = <<